Bug 522137: cleanup of AbstractScriptEngine + Unit tests

  removed unnecessary methods from class
  huge unittest refactoring

Change-Id: I02a5b6ad14c7f4e8d73f2924aa9f78779eebba3d
diff --git a/plugins/org.eclipse.ease.lang.groovy.interpreter/src/org/eclipse/ease/lang/groovy/interpreter/GroovyScriptEngine.java b/plugins/org.eclipse.ease.lang.groovy.interpreter/src/org/eclipse/ease/lang/groovy/interpreter/GroovyScriptEngine.java
index dcae69a..e904b7f 100644
--- a/plugins/org.eclipse.ease.lang.groovy.interpreter/src/org/eclipse/ease/lang/groovy/interpreter/GroovyScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.groovy.interpreter/src/org/eclipse/ease/lang/groovy/interpreter/GroovyScriptEngine.java
@@ -60,7 +60,7 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Exception {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Exception {
 		InputStreamReader reader = null;
 		Object result = null;
 		try {
diff --git a/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java b/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java
index e80a12c..92af027 100755
--- a/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.javascript.nashorn/src/org/eclipse/ease/lang/javascript/nashorn/NashornScriptEngine.java
@@ -90,7 +90,7 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Exception {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Exception {
 		return fEngine.eval(script.getCode());
 	}
 
diff --git a/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java b/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java
index 0fc5a63..1e21c90 100644
--- a/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.javascript.rhino/src/org/eclipse/ease/lang/javascript/rhino/RhinoScriptEngine.java
@@ -181,7 +181,7 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Throwable {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Throwable {
 		if (uiThread) {
 			// run in UI thread
 			final RunnableWithResult<Object> runnable = new RunnableWithResult<Object>() {
@@ -192,7 +192,7 @@
 					getContext().initStandardObjects(fScope);
 
 					// call execute again, now from correct thread
-					return internalExecute(script, reference, fileName);
+					return internalExecute(script, fileName);
 				}
 			};
 
@@ -202,10 +202,10 @@
 
 		} else
 			// run in engine thread
-			return internalExecute(script, reference, fileName);
+			return internalExecute(script, fileName);
 	}
 
-	private Object internalExecute(final Script script, final Object reference, final String fileName) throws Throwable {
+	private Object internalExecute(final Script script, final String fileName) throws Throwable {
 
 		// remove an eventually cached terminate request
 		((ObservingContextFactory) ContextFactory.getGlobal()).cancelTerminate(getContext());
diff --git a/plugins/org.eclipse.ease.lang.jvm.compiled/src/org/eclipse/ease/lang/jvm/compiled/JVMCompiledScriptEngine.java b/plugins/org.eclipse.ease.lang.jvm.compiled/src/org/eclipse/ease/lang/jvm/compiled/JVMCompiledScriptEngine.java
index 2524824..b5e926f 100644
--- a/plugins/org.eclipse.ease.lang.jvm.compiled/src/org/eclipse/ease/lang/jvm/compiled/JVMCompiledScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.jvm.compiled/src/org/eclipse/ease/lang/jvm/compiled/JVMCompiledScriptEngine.java
@@ -101,9 +101,9 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Exception {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Exception {
 
-		final Class<?> clazz = loadClass(reference);
+		final Class<?> clazz = loadClass(script.getFile());
 		if (clazz != null) {
 
 			final Method mainMethod = clazz.getMethod("main", String[].class);
@@ -121,8 +121,7 @@
 						// initialize method not available, to be ignored
 					}
 
-					final Object result = mainMethod.invoke(null, internalGetVariable("argv"));
-					return result;
+					return mainMethod.invoke(null, internalGetVariable("argv"));
 
 				} finally {
 					Thread.currentThread().setContextClassLoader(localClassLoader);
diff --git a/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/JythonScriptEngine.java b/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/JythonScriptEngine.java
index 0c1b59b..838c834 100644
--- a/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/JythonScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/JythonScriptEngine.java
@@ -139,7 +139,7 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Throwable {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Throwable {
 		if (uiThread) {
 			// run in UI thread
 			final RunnableWithResult<Object> runnable = new RunnableWithResult<Object>() {
@@ -148,7 +148,7 @@
 				public Object runWithTry() throws Throwable {
 
 					// call execute again, now from correct thread
-					return internalExecute(script, reference, fileName);
+					return internalExecute(script, fileName);
 				}
 			};
 
@@ -158,10 +158,10 @@
 
 		} else
 			// run in engine thread
-			return internalExecute(script, reference, fileName);
+			return internalExecute(script, fileName);
 	}
 
-	protected Object internalExecute(final Script script, final Object reference, final String fileName) throws Exception {
+	protected Object internalExecute(final Script script, final String fileName) throws Exception {
 		mResult = Py.None;
 
 		final PyObject code = Py.compile_command_flags(script.getCode(), "(none)", CompileMode.exec, new CompilerFlags(), true);
diff --git a/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/debugger/JythonDebuggerEngine.java b/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/debugger/JythonDebuggerEngine.java
index 1758fe9..29c6a6c 100644
--- a/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/debugger/JythonDebuggerEngine.java
+++ b/plugins/org.eclipse.ease.lang.python.jython/src/org/eclipse/ease/lang/python/jython/debugger/JythonDebuggerEngine.java
@@ -73,10 +73,10 @@
 		if (fDebugger != null) {
 			try {
 				// load python part of debugger
-				final InputStream stream = ResourceHelper.getResourceStream("org.eclipse.ease.lang.python", "pysrc/edb.py");
-
-				internalSetVariable(PythonDebugger.PYTHON_DEBUGGER_VARIABLE, Py.java2py(fDebugger));
-				super.internalExecute(new Script("Load Python debugger", stream), "edb.py", "Load Python Debugger");
+				try (final InputStream stream = ResourceHelper.getResourceStream("org.eclipse.ease.lang.python", "pysrc/edb.py")) {
+					internalSetVariable(PythonDebugger.PYTHON_DEBUGGER_VARIABLE, Py.java2py(fDebugger));
+					super.internalExecute(new Script("Load Python debugger", stream), "Load Python Debugger");
+				}
 
 			} catch (final Throwable e) {
 				throw new ScriptEngineException("Failed to load Python Debugger", e);
@@ -85,11 +85,11 @@
 	}
 
 	@Override
-	protected Object internalExecute(final Script script, final Object reference, final String fileName) throws Exception {
+	protected Object internalExecute(final Script script, final String fileName) throws Exception {
 		if (fDebugger != null) {
 			return fDebugger.execute(script);
 		}
-		return super.internalExecute(script, reference, fileName);
+		return super.internalExecute(script, fileName);
 	}
 
 	@Override
@@ -238,11 +238,6 @@
 
 	@Override
 	protected boolean acceptVariable(Object value) {
-		// if ((value != null) && ((value.getClass().getName().startsWith("org.mozilla.javascript.gen"))
-		// || (value.getClass().getName().startsWith("org.mozilla.javascript.Arguments"))))
-		// return false;
-
 		return true;
 	}
-
 }
diff --git a/plugins/org.eclipse.ease.lang.python.py4j/src/org/eclipse/ease/lang/python/py4j/internal/Py4jScriptEngine.java b/plugins/org.eclipse.ease.lang.python.py4j/src/org/eclipse/ease/lang/python/py4j/internal/Py4jScriptEngine.java
index f6389ee..563313a 100644
--- a/plugins/org.eclipse.ease.lang.python.py4j/src/org/eclipse/ease/lang/python/py4j/internal/Py4jScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.python.py4j/src/org/eclipse/ease/lang/python/py4j/internal/Py4jScriptEngine.java
@@ -216,7 +216,7 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Throwable {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Throwable {
 		if (uiThread) {
 			// run in UI thread
 			final RunnableWithResult<Object> runnable = new RunnableWithResult<Object>() {
diff --git a/plugins/org.eclipse.ease.lang.ruby.jruby/src/org/eclipse/ease/lang/ruby/jruby/JRubyScriptEngine.java b/plugins/org.eclipse.ease.lang.ruby.jruby/src/org/eclipse/ease/lang/ruby/jruby/JRubyScriptEngine.java
index aad017a..5c60b58 100644
--- a/plugins/org.eclipse.ease.lang.ruby.jruby/src/org/eclipse/ease/lang/ruby/jruby/JRubyScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.ruby.jruby/src/org/eclipse/ease/lang/ruby/jruby/JRubyScriptEngine.java
@@ -52,7 +52,7 @@
 	}
 
 	@Override
-	protected Object execute(final Script script, final Object reference, final String fileName, final boolean uiThread) throws Exception {
+	protected Object execute(final Script script, final String fileName, final boolean uiThread) throws Exception {
 		return fEngine.runScriptlet(script.getCodeStream(), fileName);
 	}
 
diff --git a/plugins/org.eclipse.ease.lang.scriptarchive/src/org/eclipse/ease/lang/scriptarchive/ArchiveEngine.java b/plugins/org.eclipse.ease.lang.scriptarchive/src/org/eclipse/ease/lang/scriptarchive/ArchiveEngine.java
index ceec272..05a2be5 100644
--- a/plugins/org.eclipse.ease.lang.scriptarchive/src/org/eclipse/ease/lang/scriptarchive/ArchiveEngine.java
+++ b/plugins/org.eclipse.ease.lang.scriptarchive/src/org/eclipse/ease/lang/scriptarchive/ArchiveEngine.java
@@ -269,7 +269,7 @@
 	}
 
 	@Override
-	protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {
+	protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {
 
 		final Object input = script.getCommand();
 		final Object mainScript = fManifest.get("Main-Script");
diff --git a/plugins/org.eclipse.ease.lang.unittest/src/org/eclipse/ease/lang/unittest/TestSuiteScriptEngine.java b/plugins/org.eclipse.ease.lang.unittest/src/org/eclipse/ease/lang/unittest/TestSuiteScriptEngine.java
index 7e1e021..d483d20 100644
--- a/plugins/org.eclipse.ease.lang.unittest/src/org/eclipse/ease/lang/unittest/TestSuiteScriptEngine.java
+++ b/plugins/org.eclipse.ease.lang.unittest/src/org/eclipse/ease/lang/unittest/TestSuiteScriptEngine.java
@@ -164,7 +164,7 @@
 	}
 
 	@Override
-	protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {
+	protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {
 
 		Object command = script.getCommand();
 
@@ -175,9 +175,9 @@
 				if (runtimeSuite != null)
 					command = runtimeSuite;
 				else
-					throw new Exception("Could not load suite from resource: " + reference);
+					throw new Exception("Could not load suite from resource: " + script.getFile());
 			} catch (final Exception e) {
-				throw new Exception("Invalid testsuite content detected in: " + reference, e);
+				throw new Exception("Invalid testsuite content detected in: " + script.getFile(), e);
 			}
 		}
 
diff --git a/plugins/org.eclipse.ease/src/org/eclipse/ease/AbstractScriptEngine.java b/plugins/org.eclipse.ease/src/org/eclipse/ease/AbstractScriptEngine.java
index 24ac687..3f5910f 100644
--- a/plugins/org.eclipse.ease/src/org/eclipse/ease/AbstractScriptEngine.java
+++ b/plugins/org.eclipse.ease/src/org/eclipse/ease/AbstractScriptEngine.java
@@ -121,9 +121,8 @@
 	 *            name of script engine job
 	 */
 	public AbstractScriptEngine(final String name) {
-		super("[EASE " + name + " Engine]");
+		super(String.format("[EASE %s Engine]", name));
 
-		// by default an engine shall be visible to the user. If the engine
 		setSystem(false);
 	}
 
@@ -188,7 +187,7 @@
 			else
 				notifyExecutionListeners(script, IExecutionListener.SCRIPT_INJECTION_START);
 
-			script.setResult(execute(script, script.getFile(), fStackTrace.get(0).getName(), uiThread));
+			script.setResult(execute(script, fStackTrace.get(0).getName(), uiThread));
 
 		} catch (final BreakException e) {
 			script.setResult(e.getCondition());
@@ -371,7 +370,6 @@
 
 	@Override
 	public void terminate() {
-
 		final IProgressMonitor monitor = getMonitor();
 		if ((monitor != null) && (!monitor.isCanceled()))
 			monitor.setCanceled(true);
@@ -379,18 +377,7 @@
 		terminateCurrent();
 
 		synchronized (this) {
-			notify();
-		}
-	}
-
-	/**
-	 * Check engine for cancellation request and terminate if indicated by the monitor.
-	 */
-	public void checkForCancellation() {
-		final IProgressMonitor monitor = getMonitor();
-		if ((monitor != null) && (monitor.isCanceled())) {
-			if (Thread.currentThread().equals(getThread()))
-				throw new ScriptEngineCancellationException();
+			notifyAll();
 		}
 	}
 
@@ -576,27 +563,6 @@
 		return Collections.unmodifiableMap(fBufferedVariables);
 	}
 
-	/**
-	 * Split a string with comma separated arguments.
-	 *
-	 * @param arguments
-	 *            comma separated arguments
-	 * @return trimmed list of arguments
-	 */
-	public static final String[] extractArguments(final String arguments) {
-		final ArrayList<String> args = new ArrayList<>();
-		if (arguments != null) {
-
-			final String[] tokens = arguments.split(",");
-			for (final String token : tokens) {
-				if (!token.trim().isEmpty())
-					args.add(token.trim());
-			}
-		}
-
-		return args.toArray(new String[args.size()]);
-	}
-
 	@Override
 	public void addSecurityCheck(ActionType type, ISecurityCheck check) {
 		if (!fSecurityChecks.containsKey(type))
@@ -606,13 +572,6 @@
 			fSecurityChecks.get(type).add(check);
 	}
 
-	@Override
-	public void removeSecurityCheck(ISecurityCheck check) {
-		for (final List<ISecurityCheck> entry : fSecurityChecks.values()) {
-			entry.remove(check);
-		}
-	}
-
 	protected List<Script> getScheduledScripts() {
 		return fScheduledScripts;
 	}
@@ -661,18 +620,17 @@
 	/**
 	 * Execute script code.
 	 *
+	 * @param script
+	 *            script to be executed
 	 * @param fileName
 	 *            name of file executed
 	 * @param uiThread
-	 * @param reader
-	 *            reader for script data to be executed
-	 * @param uiThread
 	 *            when set to <code>true</code> run code in UI thread
 	 * @return execution result
 	 * @throws Throwable
 	 *             any exception thrown during script execution
 	 */
-	protected abstract Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable;
+	protected abstract Object execute(Script script, String fileName, boolean uiThread) throws Throwable;
 
 	/**
 	 * Simple monitor to forward cancellation requests to the script engine.
diff --git a/plugins/org.eclipse.ease/src/org/eclipse/ease/IScriptEngine.java b/plugins/org.eclipse.ease/src/org/eclipse/ease/IScriptEngine.java
index 45a2fce..a67b472 100644
--- a/plugins/org.eclipse.ease/src/org/eclipse/ease/IScriptEngine.java
+++ b/plugins/org.eclipse.ease/src/org/eclipse/ease/IScriptEngine.java
@@ -220,14 +220,6 @@
 	void addSecurityCheck(ISecurityCheck.ActionType type, ISecurityCheck check);
 
 	/**
-	 * Remove registered security check for all actions. If the check was not registered at all nothing will happen.
-	 *
-	 * @param check
-	 *            check to be removed
-	 */
-	void removeSecurityCheck(ISecurityCheck check);
-
-	/**
 	 * Get the launch that was used to create this engine.
 	 *
 	 * @return launch or <code>null</code> in case this engine was created without launch configuration
diff --git a/plugins/org.eclipse.ease/src/org/eclipse/ease/ISecurityCheck.java b/plugins/org.eclipse.ease/src/org/eclipse/ease/ISecurityCheck.java
index 97de981..d4e2468 100644
--- a/plugins/org.eclipse.ease/src/org/eclipse/ease/ISecurityCheck.java
+++ b/plugins/org.eclipse.ease/src/org/eclipse/ease/ISecurityCheck.java
@@ -16,6 +16,7 @@
 /**
  * Interface used for security callbacks. Callbacks can be registered to append additional checks for certain script engine actions.
  */
+@FunctionalInterface
 public interface ISecurityCheck {
 
 	enum ActionType {
diff --git a/plugins/org.eclipse.ease/src/org/eclipse/ease/modules/EnvironmentModule.java b/plugins/org.eclipse.ease/src/org/eclipse/ease/modules/EnvironmentModule.java
index d0013d8..033549f 100644
--- a/plugins/org.eclipse.ease/src/org/eclipse/ease/modules/EnvironmentModule.java
+++ b/plugins/org.eclipse.ease/src/org/eclipse/ease/modules/EnvironmentModule.java
@@ -33,6 +33,7 @@
 import java.util.stream.Collectors;
 
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.ease.AbstractScriptEngine;
@@ -42,6 +43,7 @@
 import org.eclipse.ease.IScriptEngine;
 import org.eclipse.ease.Logger;
 import org.eclipse.ease.Script;
+import org.eclipse.ease.ScriptEngineCancellationException;
 import org.eclipse.ease.modules.ModuleDefinition.ModuleDependency;
 import org.eclipse.ease.modules.ModuleTracker.ModuleState;
 import org.eclipse.ease.service.ScriptService;
@@ -602,7 +604,7 @@
 
 	//
 	/**
-	 * Check if java callbacks are registered for a module method. This method get called on each module function invokation.
+	 * Check if java callbacks are registered for a module method. This method get called on each module function invocation.
 	 * <p>
 	 * ATTENTION: needed by dynamic script code, do not alter synopsis!
 	 * </p>
@@ -612,8 +614,9 @@
 	 * @return <code>true</code> when callbacks are registered
 	 */
 	public boolean hasMethodCallback(String methodToken) {
-		if (getScriptEngine() instanceof AbstractScriptEngine)
-			((AbstractScriptEngine) getScriptEngine()).checkForCancellation();
+		final IProgressMonitor monitor = getScriptEngine().getMonitor();
+		if ((monitor != null) && (monitor.isCanceled()))
+			throw new ScriptEngineCancellationException();
 
 		final Method method = fRegisteredMethods.get(methodToken);
 
diff --git a/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractReplScriptEngineTest.java b/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractReplScriptEngineTest.java
index 6679fc1..f3cf806 100644
--- a/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractReplScriptEngineTest.java
+++ b/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractReplScriptEngineTest.java
@@ -1,5 +1,6 @@
 package org.eclipse.ease;
 
+import static org.eclipse.ease.AbstractScriptEngineTest.INJECT_MARKER;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -20,7 +21,7 @@
 	private class MockedScriptEngine extends AbstractReplScriptEngine {
 
 		public MockedScriptEngine() {
-			super("Mocked Engine");
+			super("Mocked");
 		}
 
 		@Override
@@ -55,12 +56,15 @@
 		}
 
 		@Override
-		protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {
+		protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {
 			final String input = script.getCommand().toString();
 			if (input.contains(ERROR_MARKER))
 				throw new RuntimeException(input);
-			else
-				getOutputStream().write(input.getBytes());
+
+			if (input.contains(INJECT_MARKER))
+				inject("(injected code)", false);
+
+			getOutputStream().write(input.getBytes());
 
 			return input;
 		}
diff --git a/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractScriptEngineTest.java b/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractScriptEngineTest.java
index 086c581..2279df3 100644
--- a/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractScriptEngineTest.java
+++ b/tests/org.eclipse.ease.test/src/org/eclipse/ease/AbstractScriptEngineTest.java
@@ -1,85 +1,58 @@
+/*******************************************************************************

+ * Copyright (c) 2015 Christian Pontesegger and others.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v2.0

+ * which accompanies this distribution, and is available at

+ * https://www.eclipse.org/legal/epl-2.0/

+ *

+ * SPDX-License_Identifier: EPL-2.0

+ *

+ * Contributors:

+ *     Christian Pontesegger - initial API and implementation

+ *******************************************************************************/

 package org.eclipse.ease;

 

-import static org.junit.jupiter.api.Assertions.assertArrayEquals;

+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

 import static org.junit.jupiter.api.Assertions.assertEquals;

 import static org.junit.jupiter.api.Assertions.assertFalse;

-import static org.junit.jupiter.api.Assertions.assertNotNull;

+import static org.junit.jupiter.api.Assertions.assertNull;

 import static org.junit.jupiter.api.Assertions.assertThrows;

 import static org.junit.jupiter.api.Assertions.assertTrue;

+import static org.mockito.ArgumentMatchers.any;

+import static org.mockito.ArgumentMatchers.anyInt;

+import static org.mockito.ArgumentMatchers.eq;

+import static org.mockito.Mockito.atLeast;

+import static org.mockito.Mockito.mock;

+import static org.mockito.Mockito.never;

+import static org.mockito.Mockito.times;

+import static org.mockito.Mockito.verify;

+import static org.mockito.Mockito.when;

 

+import java.io.ByteArrayInputStream;

 import java.io.ByteArrayOutputStream;

 import java.io.IOException;

+import java.io.InputStream;

+import java.io.OutputStream;

 import java.net.URL;

-import java.util.Arrays;

+import java.util.HashMap;

 import java.util.Map;

 import java.util.concurrent.ExecutionException;

-import java.util.concurrent.TimeUnit;

 

+import org.eclipse.core.resources.IFile;

+import org.eclipse.core.runtime.Path;

 import org.eclipse.core.runtime.jobs.Job;

+import org.eclipse.debug.core.ILaunch;

+import org.eclipse.ease.ISecurityCheck.ActionType;

+import org.eclipse.ease.service.EngineDescription;

 import org.junit.jupiter.api.AfterEach;

 import org.junit.jupiter.api.BeforeEach;

+import org.junit.jupiter.api.DisplayName;

 import org.junit.jupiter.api.Test;

-import org.junit.jupiter.api.Timeout;

 

 public class AbstractScriptEngineTest {

-	protected static final int TEST_TIMEOUT = 3000;

-

-	protected static final String VALID_SAMPLE_CODE = "1";

 

 	protected static final CharSequence ERROR_MARKER = "ERROR";

-

-	private class MockedScriptEngine extends AbstractScriptEngine {

-

-		public MockedScriptEngine() {

-			super("Mocked Engine");

-		}

-

-		@Override

-		public void terminateCurrent() {

-		}

-

-		@Override

-		public void registerJar(URL url) {

-		}

-

-		@Override

-		protected Object internalGetVariable(String name) {

-			return null;

-		}

-

-		@Override

-		protected Map<String, Object> internalGetVariables() {

-			return null;

-		}

-

-		@Override

-		protected boolean internalHasVariable(String name) {

-			return false;

-		}

-

-		@Override

-		protected void internalSetVariable(String name, Object content) {

-		}

-

-		@Override

-		protected void setupEngine() throws ScriptEngineException {

-		}

-

-		@Override

-		protected void teardownEngine() throws ScriptEngineException {

-		}

-

-		@Override

-		protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {

-			final String input = script.getCommand().toString();

-			if (input.contains(ERROR_MARKER))

-				throw new RuntimeException(input);

-			else

-				getOutputStream().write(input.getBytes());

-

-			return input;

-		}

-	}

+	protected static final CharSequence INJECT_MARKER = "INJECT";

 

 	protected AbstractScriptEngine fTestEngine;

 

@@ -97,58 +70,511 @@
 	}

 

 	@Test

-	public void streamsAvailable() {

-		assertNotNull(fTestEngine.getOutputStream());

-		assertNotNull(fTestEngine.getErrorStream());

-		assertNotNull(fTestEngine.getInputStream());

+	@DisplayName("Constructor sets job name")

+	public void constructor_sets_job_name() {

+		assertEquals("[EASE Mocked Engine]", fTestEngine.getName());

 	}

 

 	@Test

-	public void setNotNullOutputStream() throws IOException {

-		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

-

-		fTestEngine.setOutputStream(bos);

-

-		assertNotNull(fTestEngine.getOutputStream());

-		fTestEngine.getOutputStream().print("test");

-		Arrays.equals("test".getBytes(), bos.toByteArray());

+	@DisplayName("getDescription() = null by default")

+	public void getDescription_returns_null_by_default() {

+		assertNull(fTestEngine.getDescription());

 	}

 

 	@Test

-	public void setNullOutputStream() throws IOException {

+	@DisplayName("getDescription() returns engine description")

+	public void getDescription_returns_engine_description() {

+		final EngineDescription description = mock(EngineDescription.class);

+		fTestEngine.setEngineDescription(description);

+		assertEquals(description, fTestEngine.getDescription());

+	}

+

+	@Test

+	@DisplayName("execute() schedules script")

+	public void execute_schedules_script() {

+		final ScriptResult result = fTestEngine.execute("foo");

+		assertFalse(result.isDone());

+	}

+

+	@Test

+	@DisplayName("execute() processes script when engine is started")

+	public void execute_processes_script_when_engine_is_started() throws ExecutionException {

+		final ScriptResult result = fTestEngine.execute("foo");

+		assertFalse(result.isDone());

+

+		fTestEngine.schedule();

+

+		assertEquals("foo", result.get());

+	}

+

+	@Test

+	@DisplayName("inject() executes code in the middle of a script")

+	public void inject_executes_code_in_the_middle_of_a_script() throws ExecutionException {

+		final ScriptResult result = fTestEngine.execute("start-" + INJECT_MARKER + "-done");

+		assertFalse(result.isDone());

+

+		final ByteArrayOutputStream out = new ByteArrayOutputStream();

+		fTestEngine.setOutputStream(out);

+		fTestEngine.schedule();

+

+		assertEquals("start-" + INJECT_MARKER + "-done", result.get());

+		assertEquals("(injected code)start-INJECT-done", out.toString());

+	}

+

+	@Test

+	@DisplayName("terminate() does nothing for non-launched engine")

+	public void terminate_does_nothing_for_non_launched_engine() {

+		fTestEngine.execute("not started");

+		fTestEngine.terminate();

+	}

+

+	@Test

+	@DisplayName("isFinished() = false for not started engine")

+	public void isFinished_equals_false_for_not_started_engine() {

+		assertFalse(fTestEngine.isFinished());

+	}

+

+	@Test

+	@DisplayName("isFinished() = true for finished engine")

+	public void isFinished_equals_true_for_finished_engine() throws ExecutionException {

+		final ScriptResult result = fTestEngine.execute("not started");

+		fTestEngine.schedule();

+		result.get();

+

+		// wait for job to terminate

+		while (fTestEngine.getState() != Job.NONE)

+			Thread.yield();

+

+		assertTrue(fTestEngine.isFinished());

+	}

+

+	@Test

+	@DisplayName("joinEngine() waits for engine to terminate")

+	public void joinEngine_waits_for_engine_to_terminate() throws InterruptedException {

+		fTestEngine.execute("not started");

+		fTestEngine.schedule(1000);

+		fTestEngine.joinEngine();

+		assertTrue(fTestEngine.isFinished());

+	}

+

+	@Test

+	@DisplayName("joinEngine(x) waits for x ms")

+	public void joinEngine_waits_for_x_ms() throws InterruptedException {

+		fTestEngine.execute("not started");

+		fTestEngine.schedule(1000);

+

+		fTestEngine.joinEngine(100);

+		assertFalse(fTestEngine.isFinished());

+	}

+

+	@Test

+	@DisplayName("getMonitor() = null for not started engine")

+	public void getMonitor_equals_null_for_not_started_engine() {

+		assertNull(fTestEngine.getMonitor());

+	}

+

+	@Test

+	@DisplayName("getMonitor() = null for terminated engine")

+	public void getMonitor_equals_null_for_terminated_engine() throws InterruptedException {

+		fTestEngine.execute("not started");

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		assertNull(fTestEngine.getMonitor());

+	}

+

+	@Test

+	@DisplayName("getMonitor() != null for running engine")

+	public void getMonitor_not_equals_null_for_running_engine() throws InterruptedException, ExecutionException {

+

+		final MockedScriptEngine engine = new MockedScriptEngine() {

+			@Override

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+				return getMonitor() != null;

+			}

+		};

+

+		final ScriptResult result = engine.execute("not started");

+		engine.schedule();

+		engine.joinEngine();

+

+		assertEquals(Boolean.TRUE, result.get());

+	}

+

+	@Test

+	@DisplayName("getInputStream() defaults to System.in")

+	public void getInputStream_defaults_to_system_in() {

+		assertEquals(System.in, fTestEngine.getInputStream());

+	}

+

+	@Test

+	@DisplayName("setInputStream() changes input stream")

+	public void setInputStream_changes_input_stream() {

+		final ByteArrayInputStream in = new ByteArrayInputStream("".getBytes());

+		fTestEngine.setInputStream(in);

+

+		assertEquals(in, fTestEngine.getInputStream());

+	}

+

+	@Test

+	@DisplayName("setInputStream(null) resets input stream")

+	public void setInputStream_to_null_resets_input_stream() {

+		final ByteArrayInputStream in = new ByteArrayInputStream("".getBytes());

+		fTestEngine.setInputStream(in);

+		fTestEngine.setInputStream(null);

+

+		assertEquals(System.in, fTestEngine.getInputStream());

+	}

+

+	@Test

+	@DisplayName("getOutputStream() defaults to System.out")

+	public void getOutputStream_defaults_to_system_in() {

+		assertEquals(System.out, fTestEngine.getOutputStream());

+	}

+

+	@Test

+	@DisplayName("setOutputStream() changes output stream")

+	public void setOutputStream_changes_output_stream() {

+		final ByteArrayOutputStream out = new ByteArrayOutputStream();

+		fTestEngine.setOutputStream(out);

+		fTestEngine.getOutputStream().print("out");

+

+		assertEquals("out", out.toString());

+	}

+

+	@Test

+	@DisplayName("setOutputStream(null) resets output stream")

+	public void setOutputStream_to_null_resets_output_stream() {

+		final ByteArrayOutputStream out = new ByteArrayOutputStream();

+		fTestEngine.setOutputStream(out);

 		fTestEngine.setOutputStream(null);

 

 		assertEquals(System.out, fTestEngine.getOutputStream());

 	}

 

 	@Test

-	public void setNotNullErrorStream() throws IOException {

-		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

-

-		assertNotNull(fTestEngine.getErrorStream());

-		fTestEngine.getErrorStream().print("test");

-		Arrays.equals("test".getBytes(), bos.toByteArray());

+	@DisplayName("getErrorStream() defaults to System.err")

+	public void getErrorStream_defaults_to_system_err() {

+		assertEquals(System.err, fTestEngine.getErrorStream());

 	}

 

 	@Test

-	public void setNullErrorStream() {

+	@DisplayName("setErrorStream() changes error stream")

+	public void setErrorStream_changes_error_stream() {

+		final ByteArrayOutputStream out = new ByteArrayOutputStream();

+		fTestEngine.setErrorStream(out);

+

+		fTestEngine.getErrorStream().print("err");

+		assertEquals("err", out.toString());

+	}

+

+	@Test

+	@DisplayName("setErrorStream(null) resets error stream")

+	public void setErrorStream_to_null_resets_error_stream() {

+		final ByteArrayOutputStream out = new ByteArrayOutputStream();

+		fTestEngine.setErrorStream(out);

 		fTestEngine.setErrorStream(null);

 

 		assertEquals(System.err, fTestEngine.getErrorStream());

 	}

 

 	@Test

+	@DisplayName("setCloseStreamsOnTerminate(true) closes streams")

+	public void setCloseStreamsOnTerminate_closes_streams() throws InterruptedException, IOException {

+		final OutputStream out = mock(OutputStream.class);

+		final OutputStream err = mock(OutputStream.class);

+		final InputStream in = mock(InputStream.class);

+

+		fTestEngine.setOutputStream(out);

+		fTestEngine.setErrorStream(err);

+		fTestEngine.setInputStream(in);

+		fTestEngine.setCloseStreamsOnTerminate(true);

+

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		verify(out, times(1)).close();

+		verify(err, times(1)).close();

+		verify(in, times(1)).close();

+	}

+

+	@Test

+	@DisplayName("setCloseStreamsOnTerminate(false) keeps streams open")

+	public void setCloseStreamsOnTerminate_keeps_streams_open() throws InterruptedException, IOException {

+		final OutputStream out = mock(OutputStream.class);

+		final OutputStream err = mock(OutputStream.class);

+		final InputStream in = mock(InputStream.class);

+

+		fTestEngine.setOutputStream(out);

+		fTestEngine.setErrorStream(err);

+		fTestEngine.setInputStream(in);

+		fTestEngine.setCloseStreamsOnTerminate(false);

+

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		verify(out, never()).close();

+		verify(err, never()).close();

+		verify(in, never()).close();

+	}

+

+	@Test

+	@DisplayName("streams are not closed by default on engine termination")

+	public void streams_are_not_closed_by_default_on_engine_termination() throws InterruptedException, IOException {

+		final OutputStream out = mock(OutputStream.class);

+		final OutputStream err = mock(OutputStream.class);

+		final InputStream in = mock(InputStream.class);

+

+		fTestEngine.setOutputStream(out);

+		fTestEngine.setErrorStream(err);

+		fTestEngine.setInputStream(in);

+

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		verify(out, never()).close();

+		verify(err, never()).close();

+		verify(in, never()).close();

+	}

+

+	@Test

+	@DisplayName("addExecutionListener() adds an engine listener")

+	public void addExecutionListener_adds_an_engine_listener() throws InterruptedException {

+		final IExecutionListener listener = mock(IExecutionListener.class);

+

+		fTestEngine.addExecutionListener(listener);

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		verify(listener, atLeast(1)).notify(eq(fTestEngine), any(), anyInt());

+	}

+

+	@Test

+	@DisplayName("removeExecutionListener() removes an engine listener")

+	public void removeExecutionListener_removes_an_engine_listener() throws InterruptedException {

+		final IExecutionListener listener = mock(IExecutionListener.class);

+

+		fTestEngine.addExecutionListener(listener);

+		fTestEngine.removeExecutionListener(listener);

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		verify(listener, never()).notify(any(), any(), anyInt());

+	}

+

+	@Test

+	@DisplayName("getStackTrace() returns empty trace for fresh engine")

+	public void getStackTrace_returns_empty_trace_for_fresh_engine() {

+		assertTrue(fTestEngine.getStackTrace().isEmpty());

+	}

+

+	@Test

+	@DisplayName("getStackTrace() returns empty trace for terminated engine")

+	public void getStackTrace_returns_empty_trace_for_terminated_engine() throws InterruptedException {

+		fTestEngine.execute("foo");

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		assertTrue(fTestEngine.getStackTrace().isEmpty());

+	}

+

+	@Test

+	@DisplayName("getStackTrace() returns stack of size 1 during execution")

+	public void getStackTrace_returns_stack_of_size_1_during_execution() throws ExecutionException {

+		final MockedScriptEngine engine = new MockedScriptEngine() {

+			@Override

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+				// TODO Auto-generated method stub

+				return getStackTrace().size();

+			}

+		};

+

+		final ScriptResult result = engine.execute("foo");

+		engine.schedule();

+

+		assertEquals(1, result.get());

+	}

+

+	@Test

+	@DisplayName("getExecutedFile() returns script resource")

+	public void getExecutedFile_returns_script_resource() throws ExecutionException {

+

+		final MockedScriptEngine engine = new MockedScriptEngine() {

+			@Override

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+				// TODO Auto-generated method stub

+				return getExecutedFile();

+			}

+		};

+

+		final IFile file = mock(IFile.class);

+		when(file.getFullPath()).thenReturn(new Path("mockedfile.js"));

+

+		final Script script = new Script(file) {

+			@Override

+			public String getCode() throws Exception {

+				return "code";

+			}

+		};

+

+		final ScriptResult result = engine.execute(script);

+		engine.schedule();

+

+		assertEquals(file, result.get());

+	}

+

+	@Test

+	@DisplayName("getExecutedFile() returns execution root file")

+	public void getExecutedFile_returns_execution_root_file() throws ExecutionException {

+

+		final MockedScriptEngine engine = new MockedScriptEngine() {

+			@Override

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+				// TODO Auto-generated method stub

+				return getExecutedFile();

+			}

+		};

+

+		final IFile file = mock(IFile.class);

+		engine.setExecutionRootFile(file);

+

+		final ScriptResult result = engine.execute(new Script("code"));

+		engine.schedule();

+

+		assertEquals(file, result.get());

+	}

+

+	@Test

+	@DisplayName("getExecutedFile() = null for dynamic code")

+	public void getExecutedFile_returns_null_for_dynamic_code() throws ExecutionException {

+

+		final MockedScriptEngine engine = new MockedScriptEngine() {

+			@Override

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+				// TODO Auto-generated method stub

+				return getExecutedFile();

+			}

+		};

+

+		final ScriptResult result = engine.execute(new Script("code"));

+		engine.schedule();

+

+		assertNull(result.get());

+	}

+

+	@Test

+	@DisplayName("hasVariable() = false when no variable is set")

+	public void hasVariable_is_false_when_no_variable_is_set() {

+		assertFalse(fTestEngine.hasVariable("foo"));

+	}

+

+	@Test

+	@DisplayName("hasVariable() = true when variable is set")

+	public void hasVariable_is_true_when_variable_is_set() {

+		fTestEngine.setVariable("foo", 42);

+

+		assertTrue(fTestEngine.hasVariable("foo"));

+	}

+

+	@Test

+	@DisplayName("getVariable() = null for non existing variable")

+	public void getVariable_is_null_for_non_existing_variable() {

+		assertNull(fTestEngine.getVariable("foo"));

+	}

+

+	@Test

+	@DisplayName("getVariable() returns buffered variable")

+	public void getVariable_returns_buffered_variable() {

+		fTestEngine.setVariable("foo", 42);

+

+		assertEquals(42, fTestEngine.getVariable("foo"));

+	}

+

+	@Test

+	@DisplayName("getVariables() returns all buffered variables")

+	public void getVariables_returns_all_buffered_variables() {

+		fTestEngine.setVariable("foo", 42);

+		fTestEngine.setVariable("bar", 84);

+

+		assertEquals(2, fTestEngine.getVariables().size());

+		assertEquals(42, fTestEngine.getVariables().get("foo"));

+		assertEquals(84, fTestEngine.getVariables().get("bar"));

+	}

+

+	@Test

+	@DisplayName("buffered variables are injected into the eninge")

+	public void buffered_variables_are_injected_into_the_engine() throws ExecutionException {

+

+		final MockedScriptEngine engine = new MockedScriptEngine() {

+			@Override

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+				// TODO Auto-generated method stub

+				return getVariable("foo");

+			}

+		};

+

+		engine.setVariable("foo", 42);

+		final ScriptResult result = engine.execute("code");

+		engine.schedule();

+

+		assertEquals(42, result.get());

+	}

+

+	@Test

+	@DisplayName("addSecurityCheck() adds passing check")

+	public void addSecurityCheck_adds_passing_check() throws InterruptedException {

+		final ISecurityCheck securityCheck = mock(ISecurityCheck.class);

+		when(securityCheck.doIt(any(), any())).thenReturn(true);

+		fTestEngine.addSecurityCheck(ActionType.INJECT_CODE, securityCheck);

+

+		final ScriptResult result = fTestEngine.execute("foo");

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		assertDoesNotThrow(() -> result.get());

+		verify(securityCheck, times(1)).doIt(any(), any());

+	}

+

+	@Test

+	@DisplayName("addSecurityCheck() adds failing check")

+	public void addSecurityCheck_adds_failing_check() throws InterruptedException {

+		fTestEngine.addSecurityCheck(ActionType.INJECT_CODE, (action, data) -> false);

+

+		final ScriptResult result = fTestEngine.execute("foo");

+		fTestEngine.schedule();

+		fTestEngine.joinEngine();

+

+		assertThrows(ScriptExecutionException.class, () -> result.get());

+	}

+

+	@Test

+	@DisplayName("getLaunch() = null by default")

+	public void getLaunch_is_null_by_default() {

+		assertNull(fTestEngine.getLaunch());

+	}

+

+	@Test

+	@DisplayName("getLaunch() returns previously set launch")

+	public void getLaunch_returns_previously_set_launch() {

+		final ILaunch launch = mock(ILaunch.class);

+

+		fTestEngine.setLaunch(launch);

+		assertEquals(launch, fTestEngine.getLaunch());

+	}

+

+	@Test

+	@DisplayName("instance extends Job.class")

 	public void isJob() {

 		assertTrue(fTestEngine instanceof Job);

 	}

 

 	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void executeValidCodeAndTerminate() throws ExecutionException, InterruptedException {

+	@DisplayName("execute valid code and terminate")

+	public void execute_valid_code_and_terminate() throws ExecutionException, InterruptedException {

 		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

 		fTestEngine.setOutputStream(bos);

 

-		final ScriptResult result1 = fTestEngine.execute(VALID_SAMPLE_CODE);

+		final ScriptResult result1 = fTestEngine.execute("1");

 		final ScriptResult result2 = fTestEngine.execute("2");

 		fTestEngine.schedule();

 

@@ -158,72 +584,41 @@
 		assertEquals("12", bos.toString());

 

 		assertTrue(result1.isDone());

-		assertEquals(VALID_SAMPLE_CODE, result1.get());

+		assertEquals("1", result1.get());

 

 		assertTrue(result2.isDone());

 		assertEquals("2", result2.get());

 	}

 

 	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void executeErrorCodeAndTerminate() throws InterruptedException, ExecutionException {

+	@DisplayName("execute errorous code and terminate")

+	public void execute_errorous_code_and_terminate() throws InterruptedException, ExecutionException {

 		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

 		fTestEngine.setOutputStream(bos);

 

-		final ScriptResult result1 = fTestEngine.execute(VALID_SAMPLE_CODE);

-		final ScriptResult result2 = fTestEngine.execute("ERROR");

+		final ScriptResult result1 = fTestEngine.execute("1");

+		final ScriptResult result2 = fTestEngine.execute(ERROR_MARKER);

 		fTestEngine.schedule();

 

 		fTestEngine.joinEngine();

 

 		assertTrue(fTestEngine.isFinished());

-		assertEquals(VALID_SAMPLE_CODE, bos.toString());

+		assertEquals("1", bos.toString());

 

 		assertTrue(result1.isDone());

-		assertEquals(VALID_SAMPLE_CODE, result1.get());

+		assertEquals("1", result1.get());

 

 		assertThrows(ScriptExecutionException.class, () -> result2.get());

 	}

 

 	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void executeSync() throws InterruptedException, ExecutionException {

-		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

-		fTestEngine.setOutputStream(bos);

-

-		final ScriptResult result1 = fTestEngine.execute(VALID_SAMPLE_CODE);

-		fTestEngine.schedule();

-

-		fTestEngine.joinEngine();

-

-		assertTrue(fTestEngine.isFinished());

-		assertEquals(VALID_SAMPLE_CODE, bos.toString());

-

-		assertTrue(result1.isDone());

-		assertEquals(VALID_SAMPLE_CODE, result1.get());

-	}

-

-	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void inject() throws ExecutionException {

-		assertEquals(VALID_SAMPLE_CODE, fTestEngine.inject(VALID_SAMPLE_CODE, false));

-	}

-

-	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void engineTerminatesWhenIdle() throws InterruptedException {

-		fTestEngine.schedule();

-		fTestEngine.joinEngine();

-	}

-

-	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void terminateViaTerminateMethod() throws InterruptedException {

+	@DisplayName("terminate() stops running engine")

+	public void terminate_stops_running_engine() throws InterruptedException {

 		final MockedScriptEngine engine = new MockedScriptEngine() {

 			@Override

-			protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

 				Thread.sleep(100);

-				return super.execute(script, reference, fileName, uiThread);

+				return super.execute(script, fileName, uiThread);

 			}

 		};

 

@@ -251,13 +646,13 @@
 	}

 

 	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void terminateViaMonitorCancellation() throws InterruptedException {

+	@DisplayName("terminate via monitor cancellation")

+	public void terminate_via_monitor_cancellation() throws InterruptedException {

 		final MockedScriptEngine engine = new MockedScriptEngine() {

 			@Override

-			protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {

+			protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

 				Thread.sleep(100);

-				return super.execute(script, reference, fileName, uiThread);

+				return super.execute(script, fileName, uiThread);

 			}

 		};

 

@@ -284,74 +679,62 @@
 		assertThrows(ScriptExecutionException.class, () -> result.get());

 	}

 

-	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void terminateViaMethodCallback() throws InterruptedException {

-		final MockedScriptEngine engine = new MockedScriptEngine() {

-			@Override

-			protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {

-				getMonitor().setCanceled(true);

-				checkForCancellation();

-				return super.execute(script, reference, fileName, uiThread);

-			}

-		};

+	public static class MockedScriptEngine extends AbstractScriptEngine {

 

-		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

-		engine.setOutputStream(bos);

+		private final Map<String, Object> fBufferedVariables = new HashMap<>();

 

-		final ScriptResult scriptResult = engine.execute(VALID_SAMPLE_CODE);

+		private MockedScriptEngine() {

+			super("Mocked");

+		}

 

-		engine.schedule();

-		engine.joinEngine();

+		@Override

+		public void terminateCurrent() {

+		}

 

-		assertTrue(bos.toString().isEmpty());

+		@Override

+		public void registerJar(URL url) {

+		}

 

-		final ScriptResult result = scriptResult;

-		assertTrue(result.isDone(), "result " + scriptResult.hashCode() + " is not ready");

-		assertThrows(ScriptExecutionException.class, () -> result.get());

-	}

+		@Override

+		protected Object internalGetVariable(String name) {

+			return fBufferedVariables.get(name);

+		}

 

-	@Test

-	@Timeout(value = 3, unit = TimeUnit.SECONDS)

-	public void terminateMultipleTimes() {

-		final MockedScriptEngine engine = new MockedScriptEngine() {

-			@Override

-			protected Object execute(Script script, Object reference, String fileName, boolean uiThread) throws Throwable {

-				Thread.sleep(300);

-				return super.execute(script, reference, fileName, uiThread);

-			}

-		};

+		@Override

+		protected Map<String, Object> internalGetVariables() {

+			return fBufferedVariables;

+		}

 

-		final ByteArrayOutputStream bos = new ByteArrayOutputStream();

-		engine.setOutputStream(bos);

+		@Override

+		protected boolean internalHasVariable(String name) {

+			return fBufferedVariables.containsKey(name);

+		}

 

-		for (int loop = 0; loop < 10; loop++)

-			engine.execute("Loop " + loop + "\n");

+		@Override

+		protected void internalSetVariable(String name, Object content) {

+			fBufferedVariables.put(name, content);

+		}

 

-		engine.schedule();

+		@Override

+		protected void setupEngine() throws ScriptEngineException {

+		}

 

-		// wait for engine to produce output

-		while (bos.toString().isEmpty())

-			Thread.yield();

+		@Override

+		protected void teardownEngine() throws ScriptEngineException {

+		}

 

-		while (engine.getState() != Job.NONE)

-			engine.terminate();

+		@Override

+		protected Object execute(Script script, String fileName, boolean uiThread) throws Throwable {

+			final String input = script.getCommand().toString();

+			if (input.contains(ERROR_MARKER))

+				throw new RuntimeException(input);

 

-		// this test is pass when it does not throw an Exception

-	}

+			if (input.contains(INJECT_MARKER))

+				inject("(injected code)", false);

 

-	@Test

-	public void extractEmptyArguments() {

-		assertEquals(0, AbstractScriptEngine.extractArguments(null).length);

-		assertEquals(0, AbstractScriptEngine.extractArguments("").length);

-		assertEquals(0, AbstractScriptEngine.extractArguments("    ").length);

-		assertEquals(0, AbstractScriptEngine.extractArguments("\t\t").length);

-	}

+			getOutputStream().write(input.getBytes());

 

-	@Test

-	public void extractArguments() {

-		assertArrayEquals(new String[] { "one" }, AbstractScriptEngine.extractArguments("one"));

-		assertArrayEquals(new String[] { "one with spaces" }, AbstractScriptEngine.extractArguments("one with spaces"));

-		assertArrayEquals(new String[] { "one", "and", "another" }, AbstractScriptEngine.extractArguments("one,and, another"));

+			return input;

+		}

 	}

 }