Adding JUnit JavaScript Support
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/.classpath b/bundles/org.eclipse.e4.languages.javascript.junit/.classpath
new file mode 100644
index 0000000..2fbb7a2
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/.project b/bundles/org.eclipse.e4.languages.javascript.junit/.project
new file mode 100644
index 0000000..d3d99e8
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.e4.languages.javascript.junit</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.e4.languages.javascript.junit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..2432afb
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Wed Apr 14 14:17:15 EDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.e4.languages.javascript.junit/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..b2d4e7c
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Wed Apr 07 17:49:08 EDT 2010
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.languages.javascript.junit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..82d401e
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: JUnit JavaScript Support
+Bundle-SymbolicName: org.eclipse.e4.languages.javascript.junit
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Import-Package: junit.framework,
+ org.mozilla.javascript
+Export-Package: org.eclipse.e4.languages.javascript.junit
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/build.properties b/bundles/org.eclipse.e4.languages.javascript.junit/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptAssertionFailedError.java b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptAssertionFailedError.java
new file mode 100644
index 0000000..fa69fab
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptAssertionFailedError.java
@@ -0,0 +1,47 @@
+package org.eclipse.e4.languages.javascript.junit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
+
+import org.mozilla.javascript.EvaluatorException;
+
+public class JavaScriptAssertionFailedError extends AssertionFailedError {
+
+ private static final long serialVersionUID = 8518724972493487259L;
+
+ public JavaScriptAssertionFailedError(AssertionFailedError e) {
+ super(e.getMessage());
+ initCause(e);
+ initStackTrace();
+ }
+
+ private void initStackTrace() {
+ EvaluatorException jsException = new EvaluatorException(null);
+ List targetTrace = new ArrayList();
+
+ StackTraceElement[] traceElements = jsException.getStackTrace();
+ for (int i = 0; i < traceElements.length; i++) {
+ StackTraceElement traceElement = traceElements[i];
+ if (!filter(traceElement))
+ targetTrace.add(new StackTraceElement("[JavaScript]", "", traceElement.getFileName(), traceElement.getLineNumber()));
+ }
+
+ setStackTrace((StackTraceElement[]) targetTrace.toArray(new StackTraceElement[targetTrace.size()]));
+ }
+
+ private boolean filter(StackTraceElement traceElement) {
+ if (!traceElement.getClassName().startsWith("org.mozilla.javascript"))
+ return true;
+ if (traceElement.getLineNumber() < 1)
+ return true;
+
+ if (traceElement.getFileName().endsWith(".java"))
+ return true;
+ if (traceElement.getFileName().startsWith("JavaScriptTestCase_"))
+ return true;
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptTestCase.java b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptTestCase.java
new file mode 100644
index 0000000..fb81ccc
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptTestCase.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.languages.javascript.junit;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.mozilla.javascript.BaseFunction;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+public class JavaScriptTestCase extends TestCase {
+
+ private Context context;
+ private ScriptableObject globalScope;
+
+ private String testCaseName;
+ private Collection scripts;
+ private ClassLoader applicationClassLoader;
+ private ClassLoader originalApplicationClassLoader;
+ private boolean superRunTest = false;
+ private static URL ASSERT_SCRIPT = JavaScriptTestCase.class.getResource("assert.js");
+
+ public JavaScriptTestCase() {
+ this(null, null, null, null);
+ }
+
+ public JavaScriptTestCase(String name, String testCaseName) {
+ this(name, testCaseName, null, null);
+ }
+
+ public JavaScriptTestCase(String name, String testCaseName, ClassLoader applicationClassLoader) {
+ this(name, testCaseName, null, applicationClassLoader);
+ }
+
+ public JavaScriptTestCase(String name, String testCaseName, Collection scripts, ClassLoader applicationClassLoader) {
+ super(name);
+ this.testCaseName = testCaseName;
+ this.scripts = scripts;
+ this.applicationClassLoader = applicationClassLoader;
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ context = Context.enter();
+ if (applicationClassLoader != null) {
+ originalApplicationClassLoader = context.getApplicationClassLoader();
+ context.setApplicationClassLoader(applicationClassLoader);
+ }
+ globalScope = context.initStandardObjects();
+ eval(readContents(ASSERT_SCRIPT), "JavaScriptTestCase_assert");
+ ScriptableObject assertScope = (ScriptableObject) ScriptableObject.getProperty(globalScope, "Assert");
+ ScriptableObject.putProperty(assertScope, "fail", new BaseFunction() { //$NON-NLS-1$
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+ try {
+ if (args.length > 0) {
+ fail(Context.toString(args[0]));
+ } else {
+ fail();
+ }
+ return null;
+ } catch (AssertionFailedError e) {
+ throw new JavaScriptAssertionFailedError(e);
+ }
+ }
+ });
+ evalScripts();
+ }
+
+ protected Scriptable createJavaScriptTestCaseInstance(String testName) {
+ Scriptable currentScope = getGlobalScope();
+ StringTokenizer tokenizer = new StringTokenizer(testCaseName, "."); //$NON-NLS-1$
+ while (true) {
+ String token = tokenizer.nextToken();
+ Object value = currentScope.get(token, currentScope);
+ if (!tokenizer.hasMoreTokens()) {
+ BaseFunction constructor = (BaseFunction) value;
+ Object[] arguments = (testName==null) ? new Object[0]: new Object[]{testName};
+ return constructor.construct(context, getGlobalScope(), arguments);
+ }
+ if (value instanceof Scriptable)
+ currentScope = (Scriptable) value;
+ else
+ throw new RuntimeException("Not Found: " + testCaseName + " in " + this.toString()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+
+ protected void tearDown() throws Exception {
+ if (applicationClassLoader != null) {
+ context.setApplicationClassLoader(originalApplicationClassLoader);
+ originalApplicationClassLoader = null;
+ }
+ globalScope = null;
+ context = null;
+ Context.exit();
+ super.tearDown();
+ }
+
+ public void useJavaTests() {
+ superRunTest = true;
+ }
+
+ public ScriptableObject getGlobalScope() {
+ return globalScope;
+ }
+
+ public Object eval(String source) {
+ return eval(source, null);
+ }
+
+ public Object eval(String source, String sourceName) {
+ if (sourceName == null) {
+ sourceName = "eval";
+ }
+ return context.evaluateString(globalScope, source, sourceName, 1, null);
+ }
+
+ protected void runTest() throws Throwable {
+ if (superRunTest || getName() == null) {
+ super.runTest();
+ return;
+ }
+ ScriptableObject testCaseInstance = (ScriptableObject) createJavaScriptTestCaseInstance(getName());
+ BaseFunction runMethod = (BaseFunction) ScriptableObject.getProperty(testCaseInstance, "run");
+ runMethod.call(context, globalScope, testCaseInstance, new Object[0]);
+ }
+
+ private void evalScripts() {
+ if (scripts == null)
+ return;
+ for (Iterator iterator = scripts.iterator(); iterator.hasNext();) {
+ Object script = iterator.next();
+ if (script instanceof String)
+ eval((String) script, null);
+ else if (script instanceof File) {
+ File scriptFile = (File) script;
+ eval(readContents(scriptFile), scriptFile.getAbsolutePath());
+ } else if (script instanceof URL) {
+ URL scriptURL = (URL) script;
+ eval(readContents(scriptURL), scriptURL.toExternalForm());
+ }
+ }
+ }
+
+ public static String readContents(File scriptFile) {
+ try {
+ return readContents(new FileInputStream(scriptFile));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String readContents(URL url) {
+ try {
+ return readContents(url.openStream());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String readContents(InputStream is) throws IOException {
+ Reader reader = new InputStreamReader(new BufferedInputStream(is));
+ try {
+ StringBuffer buffer = new StringBuffer();
+ int read = 0;
+ char[] cbuf = new char[1024];
+ while (-1 != (read = reader.read(cbuf))) {
+ buffer.append(cbuf, 0, read);
+ }
+ return buffer.toString();
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptTestSuite.java b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptTestSuite.java
new file mode 100644
index 0000000..bbccc7d
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/JavaScriptTestSuite.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.languages.javascript.junit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import junit.framework.TestSuite;
+
+import org.mozilla.javascript.BaseFunction;
+import org.mozilla.javascript.ScriptableObject;
+
+public class JavaScriptTestSuite extends TestSuite {
+
+ private Collection scripts;
+ private ClassLoader applicationClassLoader;
+
+ public JavaScriptTestSuite(String testCaseName, Collection scripts) {
+ this(testCaseName, scripts, null);
+ }
+
+ public JavaScriptTestSuite(String testCaseName, Collection scripts, ClassLoader applicationClassLoader) {
+ super(testCaseName);
+ this.scripts = scripts;
+ this.applicationClassLoader = applicationClassLoader;
+
+ try {
+ findTests();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void findTests() throws Exception {
+ JavaScriptTestCase testCase = new JavaScriptTestCase(null, getName(), scripts, applicationClassLoader);
+ testCase.setUp();
+ try {
+ ArrayList testFunctionNames = new ArrayList();
+ ScriptableObject testCaseInstance = (ScriptableObject) testCase.createJavaScriptTestCaseInstance(null);
+ Object[] ids = ScriptableObject.getPropertyIds(testCaseInstance);
+ for (int i = 0; i < ids.length; i++) {
+ Object id = ids[i];
+ Object value = null;
+ if (id instanceof String) {
+ String fieldName = (String) id;
+ if (!fieldName.startsWith("test"))
+ continue;
+ value = ScriptableObject.getProperty(testCaseInstance, fieldName);
+ if (value instanceof BaseFunction)
+ testFunctionNames.add(fieldName);
+ }
+ }
+ Collections.sort(testFunctionNames);
+ for (Iterator iterator = testFunctionNames.iterator(); iterator.hasNext();) {
+ String testFunctionName = (String) iterator.next();
+ addTest(new JavaScriptTestCase(testFunctionName, getName(), scripts, applicationClassLoader));
+ }
+ } finally {
+ testCase.tearDown();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/assert.js b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/assert.js
new file mode 100644
index 0000000..5b3de07
--- /dev/null
+++ b/bundles/org.eclipse.e4.languages.javascript.junit/src/org/eclipse/e4/languages/javascript/junit/assert.js
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others All rights reserved. This
+ * program and the accompanying materials are made available under the terms of
+ * the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+var Assert = {
+ assertTrue : function(message, asserted) {
+ if (arguments.length != 2) {
+ asserted = message;
+ message = null;
+ }
+ if (!asserted) {
+ Assert.fail(message ? message : 'assertTrue failed');
+ }
+ },
+ assertFalse : function(message, asserted) {
+ if (arguments.length != 2) {
+ asserted = message;
+ message = null;
+ }
+ if (asserted) {
+ Assert.fail(message ? message : 'assertFalse failed');
+ }
+ },
+ assertNull : function(message, asserted) {
+ if (arguments.length != 2) {
+ asserted = message;
+ message = null;
+ }
+ if (asserted !== null) {
+ Assert.fail(message ? message : 'assertNull failed: [' + asserted +']');
+ }
+ },
+ assertNotNull : function(message, asserted) {
+ if (arguments.length != 2) {
+ asserted = message;
+ message = null;
+ }
+ if (asserted === null) {
+ Assert.fail(message ? message : 'assertNotNull failed');
+ }
+ },
+ assertUndefined : function(message, asserted) {
+ if (arguments.length != 2) {
+ asserted = message;
+ message = null;
+ }
+ if (typeof asserted != 'undefined') {
+ Assert.fail(message ? message : 'assertUndefined failed: [' + asserted +']');
+ }
+ },
+ assertNotUndefined : function(message, asserted) {
+ if (arguments.length != 2) {
+ asserted = message;
+ message = null;
+ }
+ if (typeof asserted == 'undefined') {
+ Assert.fail(message ? message : 'assertNotUndefined failed');
+ }
+ },
+ assertSame : function(message, expected, value) {
+ if (arguments.length != 3) {
+ value = expected;
+ expected = message;
+ message = null;
+ }
+ if (expected === value)
+ return;
+ var expectedMessage = '- expected: [' + expected + '] but was [' + value + '].';
+ Assert.fail(message ? message : 'assertSame failed') + expectedMessage;
+ },
+ assertNotSame : function(message, expected, value) {
+ if (arguments.length != 3) {
+ value = expected;
+ expected = message;
+ message = null;
+ }
+ if (expected === value)
+ Assert.fail(message ? message : 'assertNotSame failed [' + value + '].');
+ },
+ assertEquals : function(message, expected, value) {
+ if (arguments.length != 3) {
+ value = expected;
+ expected = message;
+ message = null;
+ }
+ if (expected === value)
+ return;
+ if (expected !== null && typeof expected === 'object' && typeof expected.equals === 'function' && expected.equals(value))
+ return;
+ var expectedMessage = '- expected: [' + expected + '] but was [' + value + '].';
+ Assert.fail(message ? message : 'assertEquals failed') + expectedMessage;
+ },
+ assertNotEquals : function(message, expected, value) {
+ if (arguments.length != 3) {
+ value = expected;
+ expected = message;
+ message = null;
+ }
+ if (expected === value)
+ Assert.fail(message ? message : 'assertNotEquals failed [' + value + '].');
+ if (expected !== null && typeof expected === 'object' && typeof expected.equals === 'function' && expected.equals(value))
+ Assert.fail(message ? message : 'assertNotEquals failed [' + value + '].');
+ },
+ // For JSDT JUnit integration this method is over-ridden with a Java version
+ // with better stack support
+ fail : function(message) {
+ throw (message ? message : 'failed');
+ },
+ scopeAssert : function(scope) {
+ scope.fail = Assert.fail;
+ scope.assertNotEquals = Assert.assertNotEquals
+ scope.assertEquals = Assert.assertEquals;
+ scope.assertTrue = Assert.assertTrue;
+ scope.assertFalse = Assert.assertFalse;
+ scope.assertNotSame = Assert.assertNotSame;
+ scope.assertSame = Assert.assertSame;
+ scope.assertNull = Assert.assertNull;
+ scope.assertNotNull = Assert.assertNotNull;
+ scope.assertUndefined = Assert.assertUndefined;
+ scope.assertNotUndefined = Assert.assertNotUndefined;
+ }
+};
+Assert.scopeAssert(this);
+
+function TestCase(name) {
+ if (name)
+ this.setName(name);
+}
+TestCase.prototype = {
+ getName : function() {
+ return (this._testName) ? this._testName : null;
+ },
+ setName : function(name) {
+ this._testName = name;
+ },
+ getTestCaseName : function() {
+ return (this._testCaseName) ? this._testCaseName : null;
+ },
+ setTestCaseName : function(name) {
+ this._testCaseName = name;
+ },
+ setUp : function() {
+ },
+ tearDown : function() {
+ },
+ run : function() {
+ this.setUp();
+ try {
+ var testMethodName = this.getName();
+ if (!testMethodName) {
+ Assert.fail("Test method not set");
+ }
+ var testMethod = this[testMethodName];
+ if (!testMethod) {
+ Assert.fail("test method '" + testMethodName + "' not found in TestCase.");
+ }
+ if (!typeof testMethod === "function") {
+ Assert.fail("test method '" + testMethodName + "' is not a function.");
+ }
+ this[testMethodName]();
+ } finally {
+ this.tearDown();
+ }
+ }
+};
+TestCase.extend = function(testCaseName, protoObject) {
+ var F = function(testName) {
+ TestCase.call(this, testName);
+ };
+ var base = new TestCase();
+ base.setTestCaseName(testCaseName);
+ if (protoObject) {
+ for ( var prop in protoObject) {
+ base[prop] = protoObject[prop];
+ }
+ }
+ F.prototype = base;
+ return F;
+};