Bring back ScriptLaunchingTests as it's needed.

Change-Id: I39e5bdf0d793e340b6b0fd12bff6d67784fb77e1
diff --git a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/launching/ScriptLaunchingTests.java b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/launching/ScriptLaunchingTests.java
new file mode 100644
index 0000000..7a97787
--- /dev/null
+++ b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/launching/ScriptLaunchingTests.java
@@ -0,0 +1,673 @@
+package org.eclipse.dltk.core.tests.launching;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchDelegate;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.Launch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStreamMonitor;
+import org.eclipse.debug.core.model.IStreamsProxy;
+import org.eclipse.dltk.core.IScriptProject;
+import org.eclipse.dltk.core.environment.EnvironmentManager;
+import org.eclipse.dltk.core.environment.IFileHandle;
+import org.eclipse.dltk.core.tests.model.AbstractModelTests;
+import org.eclipse.dltk.debug.core.ExtendedDebugEventDetails;
+import org.eclipse.dltk.debug.core.model.IScriptLineBreakpoint;
+import org.eclipse.dltk.debug.core.model.IScriptStackFrame;
+import org.eclipse.dltk.debug.core.model.IScriptThread;
+import org.eclipse.dltk.internal.debug.core.model.ScriptDebugTarget;
+import org.eclipse.dltk.internal.debug.core.model.ScriptLineBreakpoint;
+import org.eclipse.dltk.internal.debug.core.model.ScriptThread;
+import org.eclipse.dltk.launching.IInterpreterInstall;
+import org.eclipse.dltk.launching.IInterpreterInstallType;
+import org.eclipse.dltk.launching.InterpreterConfig;
+import org.eclipse.dltk.launching.InterpreterSearcher;
+import org.eclipse.dltk.launching.ScriptLaunchConfigurationConstants;
+
+public abstract class ScriptLaunchingTests extends AbstractModelTests {
+
+	protected IScriptProject scriptProject;
+
+	protected IInterpreterInstall[] interpreterInstalls;
+
+	public ScriptLaunchingTests(String name) {
+		super(name);
+	}
+
+	// Configuration
+	@Override
+	public void setUpSuite() throws Exception {
+		super.setUpSuite();
+		scriptProject = setUpScriptProject(getProjectName(), getBundleName());
+
+		final IProject project = scriptProject.getProject();
+		IProjectDescription description = project.getDescription();
+		description.setNatureIds(new String[] { getNatureId() });
+		project.setDescription(description, null);
+
+		if (!hasPredefinedInterpreters()) {
+			interpreterInstalls = searchInstalls(getNatureId());
+		} else {
+			interpreterInstalls = getPredefinedInterpreterInstalls();
+		}
+	}
+
+	/**
+	 * Should return predefined interpreters. Used with hasPredefinedInterpreters
+	 * method as true.
+	 *
+	 * @return Not null array of interpreter installs.
+	 */
+	protected IInterpreterInstall[] getPredefinedInterpreterInstalls() {
+		return new IInterpreterInstall[0];
+	}
+
+	@Override
+	public void tearDownSuite() throws Exception {
+		deleteProject(getProjectName());
+		super.tearDownSuite();
+	}
+
+	// Helper methods
+	protected ILaunchConfiguration createTestLaunchConfiguration(final String natureId, final String projectName,
+			final String script, final String arguments) {
+		return new ILaunchConfiguration() {
+			@Override
+			public boolean contentsEqual(ILaunchConfiguration configuration) {
+				return false;
+			}
+
+			@Override
+			public ILaunchConfigurationWorkingCopy copy(String name) throws CoreException {
+				return null;
+			}
+
+			@Override
+			public void delete() throws CoreException {
+
+			}
+
+			@Override
+			public boolean exists() {
+				return false;
+			}
+
+			@Override
+			public boolean getAttribute(String attributeName, boolean defaultValue) throws CoreException {
+				if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_DEFAULT_BUILDPATH)) {
+					return true;
+				} else if (attributeName.equals(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES)) {
+					return true;
+				}
+
+				return defaultValue;
+			}
+
+			@Override
+			public int getAttribute(String attributeName, int defaultValue) throws CoreException {
+				if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_DLTK_DBGP_WAITING_TIMEOUT)) {
+					return 10000;
+				}
+				return defaultValue;
+			}
+
+			@Override
+			public List<String> getAttribute(String attributeName, List<String> defaultValue) throws CoreException {
+				return defaultValue;
+			}
+
+			@Override
+			public Set<String> getAttribute(String attributeName, Set<String> defaultValue) throws CoreException {
+				return defaultValue;
+			}
+
+			@Override
+			public Map<String, String> getAttribute(String attributeName, Map<String, String> defaultValue)
+					throws CoreException {
+				if (attributeName.equals(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES)) {
+					Map<String, String> env = new HashMap<>();
+					configureEnvironment(env);
+					return env;
+				}
+				return defaultValue;
+			}
+
+			@Override
+			public String getAttribute(String attributeName, String defaultValue) throws CoreException {
+
+				if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_MAIN_SCRIPT_NAME)) {
+					return script;
+				} else if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_PROJECT_NAME)) {
+					return projectName;
+				} else if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY)) {
+					return null;
+				} else if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_SCRIPT_ARGUMENTS)) {
+					return arguments;
+				} else if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_INTERPRETER_ARGUMENTS)) {
+					return "";
+				} else if (attributeName.equals(ScriptLaunchConfigurationConstants.ATTR_SCRIPT_NATURE)) {
+					return natureId;
+				}
+				return defaultValue;
+			}
+
+			@Override
+			public Map<String, Object> getAttributes() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public String getCategory() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public IFile getFile() {
+				return ScriptLaunchingTests.this.getFile(projectName + '/' + script);
+			}
+
+			@Override
+			public IPath getLocation() {
+				return null;
+			}
+
+			@Override
+			public IResource[] getMappedResources() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public String getMemento() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public Set<String> getModes() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public String getName() {
+				return null;
+			}
+
+			@Override
+			public ILaunchDelegate getPreferredDelegate(Set<String> modes) throws CoreException {
+				return null;
+			}
+
+			@Override
+			public ILaunchConfigurationType getType() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public ILaunchConfigurationWorkingCopy getWorkingCopy() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public boolean isLocal() {
+				return false;
+			}
+
+			@Override
+			public boolean isMigrationCandidate() throws CoreException {
+				return false;
+			}
+
+			@Override
+			public boolean isReadOnly() {
+				return false;
+			}
+
+			@Override
+			public boolean isWorkingCopy() {
+				return false;
+			}
+
+			@Override
+			public ILaunch launch(String mode, IProgressMonitor monitor) throws CoreException {
+				return null;
+			}
+
+			@Override
+			public ILaunch launch(String mode, IProgressMonitor monitor, boolean build) throws CoreException {
+				return null;
+			}
+
+			@Override
+			public ILaunch launch(String mode, IProgressMonitor monitor, boolean build, boolean register)
+					throws CoreException {
+				return null;
+			}
+
+			@Override
+			public void migrate() throws CoreException {
+
+			}
+
+			@Override
+			public boolean supportsMode(String mode) throws CoreException {
+				return false;
+			}
+
+			@Override
+			public <T> T getAdapter(Class<T> adapter) {
+				return null;
+			}
+
+			@Override
+			public boolean hasAttribute(String attributeName) throws CoreException {
+				return false;
+			}
+
+			@Override
+			public void delete(int flag) throws CoreException {
+
+			}
+
+			@Override
+			public ILaunchConfiguration getPrototype() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public boolean isAttributeModified(String attribute) throws CoreException {
+				return false;
+			}
+
+			@Override
+			public boolean isPrototype() {
+				return false;
+			}
+
+			@Override
+			public Collection<ILaunchConfiguration> getPrototypeChildren() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public int getKind() throws CoreException {
+				// TODO Auto-generated method stub
+				return 0;
+			}
+
+			@Override
+			public Set<String> getPrototypeVisibleAttributes() throws CoreException {
+				return null;
+			}
+
+			@Override
+			public void setPrototypeAttributeVisibility(String attribute, boolean visible) throws CoreException {
+			}
+		};
+	}
+
+	protected void configureEnvironment(Map<?, ?> env) {
+	}
+
+	public IInterpreterInstall[] searchInstalls(String natureId) {
+		final List<IInterpreterInstall> installs = new ArrayList<>();
+		final InterpreterSearcher searcher = new InterpreterSearcher();
+
+		searcher.search(EnvironmentManager.getLocalEnvironment(), natureId, null, 1, null);
+
+		if (searcher.hasResults()) {
+			IFileHandle[] files = searcher.getFoundFiles();
+			IInterpreterInstallType[] types = searcher.getFoundInstallTypes();
+
+			for (int i = 0; i < files.length; ++i) {
+				final IFileHandle file = files[i];
+				final IInterpreterInstallType type = types[i];
+
+				// // Skip useless interpreters
+				// if (!isInterpreterRequired(file))
+				// continue;
+
+				String installId = getNatureId() + "_" + Integer.toString(i);
+				IInterpreterInstall install = type.findInterpreterInstall(installId);
+
+				if (install == null)
+					install = type.createInterpreterInstall(installId);
+
+				install.setName(file.toString());
+				install.setInstallLocation(file);
+				install.setLibraryLocations(null);
+				install.setEnvironmentVariables(null);
+				installs.add(install);
+			}
+		}
+
+		return installs.toArray(new IInterpreterInstall[installs.size()]);
+	}
+
+	public void internalTestRequiredInterpreterAvailable(String name) {
+		assertTrue("Interpreter " + name + " not available", isInterpreterAvailable(name));
+	}
+
+	private boolean isInterpreterAvailable(String interpreterName) {
+		for (int i = 0; i < interpreterInstalls.length; i++) {
+			IFileHandle installLocation = interpreterInstalls[i].getInstallLocation();
+			if (isRequiredInstall(interpreterName, installLocation)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean isRequiredInstall(String interpreterName, IFileHandle installLocation) {
+		String executableName = installLocation.getName();
+		if (executableName.startsWith(interpreterName)) {
+			return true;
+		}
+		return false;
+	}
+
+	public final static int SKIP_STDOUT_TEST = 1;
+
+	protected void internalTestRun(String name) throws Exception {
+		internalTestRun(name, 0);
+	}
+
+	protected void internalTestRun(String name, int flags) throws Exception {
+		if (interpreterInstalls.length == 0) {
+			fail("No interperters found for nature " + getNatureId());
+		}
+
+		for (int i = 0; i < interpreterInstalls.length; ++i) {
+			final IInterpreterInstall install = interpreterInstalls[i];
+			if (!isRequiredInstall(name, install.getInstallLocation())) {
+				continue;
+			}
+			// System.out.println("Interpreter install location (run): "
+			// + install.getInstallLocation().toString());
+
+			// InterpretersUpdater updater = new InterpretersUpdater();
+			// updater.updateInterpreterSettings(getNatureId(),
+			// interpreterInstalls, install);
+
+			final long time = System.currentTimeMillis();
+			final String stdoutTest = Long.toString(time) + "_stdout";
+			final String stderrTest = Long.toString(time) + "_stderr";
+
+			final ILaunchConfiguration configuration = createLaunchConfiguration(stdoutTest + " " + stderrTest);
+
+			final ILaunch launch = new Launch(configuration, ILaunchManager.RUN_MODE, null);
+
+			startLaunch(launch, install);
+
+			IProcess[] processes = launch.getProcesses();
+			assertEquals(1, processes.length);
+
+			final IProcess process = processes[0];
+			final IStreamsProxy proxy = process.getStreamsProxy();
+			assertNotNull(proxy);
+
+			final IStreamMonitor outputMonitor = proxy.getOutputStreamMonitor();
+			assertNotNull(outputMonitor);
+
+			final IStreamMonitor errorMonitor = proxy.getErrorStreamMonitor();
+			assertNotNull(errorMonitor);
+
+			while (!process.isTerminated()) {
+				Thread.sleep(20);
+			}
+
+			assertTrue(process.isTerminated());
+
+			final int exitValue = process.getExitValue();
+			assertEquals(0, exitValue);
+
+			if ((flags & SKIP_STDOUT_TEST) == 0) {
+				final String output = outputMonitor.getContents();
+				assertEquals(stdoutTest, output);
+			}
+
+			final String error = errorMonitor.getContents();
+			assertEquals(stderrTest, error);
+			return;
+		}
+		assertTrue("Requied interpreter are't found" + name, false);
+	}
+
+	public static class DebugEventStats implements IDebugEventSetListener {
+		private int suspendCount;
+		private int resumeCount;
+		private int beforeSuspendCount;
+		private int beforeResumeCount;
+		private int beforeCodeLoaded;
+		private int beforeVmStarted;
+
+		public DebugEventStats() {
+			this.suspendCount = 0;
+			this.resumeCount = 0;
+		}
+
+		@Override
+		public void handleDebugEvents(DebugEvent[] events) {
+			for (int i = 0; i < events.length; ++i) {
+				DebugEvent event = events[i];
+
+				final int kind = event.getKind();
+				switch (kind) {
+				case DebugEvent.RESUME:
+					resumeCount += 1;
+					break;
+				case DebugEvent.SUSPEND:
+					suspendCount += 1;
+					try {
+						final Object source = event.getSource();
+						if (source instanceof IScriptStackFrame) {
+							((IScriptStackFrame) source).resume();
+						} else if (source instanceof IScriptThread) {
+							((IScriptThread) source).resume();
+						}
+					} catch (DebugException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+
+					break;
+				case DebugEvent.CREATE:
+					break;
+				case DebugEvent.TERMINATE:
+					break;
+				case DebugEvent.CHANGE:
+					break;
+				case DebugEvent.MODEL_SPECIFIC:
+					handleExtendedEvent(event);
+					break;
+				}
+			}
+		}
+
+		private void handleExtendedEvent(DebugEvent event) {
+			int extendedType = event.getDetail();
+			switch (extendedType) {
+			case ExtendedDebugEventDetails.BEFORE_VM_STARTED:
+				handleBeforeVmStarted((InterpreterConfig) event.getSource());
+				break;
+			case ExtendedDebugEventDetails.BEFORE_CODE_LOADED:
+				handleBeforeCodeLoaded((ScriptDebugTarget) event.getSource());
+				break;
+			case ExtendedDebugEventDetails.BEFORE_RESUME:
+				handleBeforeResume((ScriptThread) event.getSource());
+				break;
+			case ExtendedDebugEventDetails.BEFORE_SUSPEND:
+				handleBeforeSuspend((ScriptThread) event.getSource());
+				break;
+			}
+
+		}
+
+		private void handleBeforeSuspend(ScriptThread source) {
+			beforeSuspendCount++;
+		}
+
+		private void handleBeforeResume(ScriptThread source) {
+			beforeResumeCount++;
+		}
+
+		private void handleBeforeCodeLoaded(ScriptDebugTarget source) {
+			beforeCodeLoaded++;
+		}
+
+		private void handleBeforeVmStarted(InterpreterConfig source) {
+			beforeVmStarted++;
+		}
+
+		public void reset() {
+			suspendCount = 0;
+			resumeCount = 0;
+			beforeSuspendCount = 0;
+			beforeResumeCount = 0;
+			beforeCodeLoaded = 0;
+			beforeVmStarted = 0;
+		}
+
+		public int getSuspendCount() {
+			return suspendCount;
+		}
+
+		public int getResumeCount() {
+			return resumeCount;
+		}
+
+		public int getBeforeSuspendCount() {
+			return beforeSuspendCount;
+		}
+
+		public int getBeforeResumeCount() {
+			return beforeResumeCount;
+		}
+
+		public int getBeforeCodeLoaded() {
+			return beforeCodeLoaded;
+		}
+
+		public int getBeforeVmStarted() {
+			return beforeVmStarted;
+		}
+	}
+
+	public DebugEventStats internalTestDebug(String name) throws Exception {
+		if (interpreterInstalls.length == 0) {
+			fail("No interperters found for nature " + getNatureId());
+		}
+
+		// Debug
+		for (int i = 0; i < interpreterInstalls.length; ++i) {
+			final IInterpreterInstall install = interpreterInstalls[i];
+			if (!isRequiredInstall(name, install.getInstallLocation())) {
+				continue;
+			}
+
+			DebugEventStats stats = new DebugEventStats();
+
+			DebugPlugin.getDefault().addDebugEventListener(stats);
+			// System.out.println("Interperter install location (debug): "
+			// + install.getInstallLocation());
+
+			// final InterpretersUpdater updater = new InterpretersUpdater();
+			// updater.updateInterpreterSettings(getNatureId(),
+			// interpreterInstalls, install);
+
+			stats.reset();
+
+			final ILaunch launch = new Launch(createLaunchConfiguration(""), ILaunchManager.DEBUG_MODE, null);
+
+			// Setting breakpoint
+			IFile file = launch.getLaunchConfiguration().getFile();
+			IScriptLineBreakpoint b = new ScriptLineBreakpoint(getDebugModelId(), file, file.getLocation(), 1, -1, -1,
+					true);
+
+			try {
+				startLaunch(launch, install);
+
+				IProcess[] processes = launch.getProcesses();
+				assertEquals(1, processes.length);
+
+				final IProcess process = processes[0];
+				while (!process.isTerminated()) {
+					Thread.sleep(200);
+				}
+
+				assertTrue(process.isTerminated());
+				final int exitValue = process.getExitValue();
+				assertEquals(0, exitValue);
+			} finally {
+				b.delete();
+
+			}
+			DebugPlugin.getDefault().removeDebugEventListener(stats);
+			return stats;
+		}
+
+		fail("Requied interpreter \"" + name + "\" is not found");
+		return null;
+	}
+
+	protected abstract String getProjectName();
+
+	protected abstract String getBundleName();
+
+	protected abstract String getNatureId();
+
+	protected abstract String getDebugModelId();
+
+	protected abstract void startLaunch(ILaunch launch, final IInterpreterInstall install) throws CoreException;
+
+	protected abstract ILaunchConfiguration createLaunchConfiguration(String arguments);
+
+	protected boolean hasPredefinedInterpreters() {
+		return false;
+	}
+
+	protected IInterpreterInstall createInstall(String path, String id, IInterpreterInstallType type) {
+		IFileHandle file = EnvironmentManager.getLocalEnvironment().getFile(new Path(path));
+		if (!file.exists()) {
+			return null;
+		}
+		IInterpreterInstall install = type.findInterpreterInstall(id);
+
+		if (install == null)
+			install = type.createInterpreterInstall(id);
+
+		install.setName("");
+		install.setInstallLocation(file);
+		install.setLibraryLocations(null);
+		install.setEnvironmentVariables(null);
+		return install;
+	}
+
+	protected void createAddInstall(List<IInterpreterInstall> installs, String path, String id,
+			IInterpreterInstallType type) {
+		IInterpreterInstall install = createInstall(path, id, type);
+		if (install != null) {
+			installs.add(install);
+		}
+	}
+}