From c808f41a8f9cb122c27350506835e4fd80d7e733 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 23 Feb 2016 23:27:50 +0300 Subject: [PATCH] Improving JUnit test runner --- tests/teavm-tests.iml | 41 ++ .../tooling/testing/ExceptionHelper.java | 29 - .../teavm/tooling/testing/TeaVMTestTool.java | 516 ------------------ .../org/teavm/tooling/testing/TestCase.java | 74 --- .../tooling/testing/TestClassBuilder.java | 40 -- .../teavm/tooling/testing/TestEntryPoint.java | 37 -- .../testing/TestEntryPointTransformer.java | 85 --- .../testing/TestExceptionDependency.java | 68 --- .../tooling/testing/TestExceptionPlugin.java | 30 - .../org/teavm/tooling/testing/TestGroup.java | 49 -- .../tooling/testing/TestMethodBuilder.java | 56 -- .../org/teavm/tooling/testing/TestPlan.java | 50 -- tools/junit/pom.xml | 17 + .../org/teavm/junit}/HtmlUnitRunStrategy.java | 24 +- .../org/teavm/junit}/SeleniumRunStrategy.java | 29 +- .../java/org/teavm/junit/TeaVMTestRunner.java | 150 ++++- .../main/java/org/teavm/junit/TestRun.java | 58 ++ .../org/teavm/junit/TestRunCallback.java} | 16 +- .../org/teavm/junit/TestRunStrategy.java} | 17 +- .../java/org/teavm/junit}/TestRunner.java | 94 +--- .../main/resources/teavm-selenium-adapter.js | 79 +-- .../src/main/resources/teavm-selenium.js | 24 +- tools/junit/teavm-junit.iml | 44 +- .../teavm/maven/BuildJavascriptTestMojo.java | 260 --------- .../java/org/teavm/maven/RunTestsMojo.java | 128 ----- .../teavm/maven/TestExceptionDependency.java | 69 --- .../org/teavm/maven/TestExceptionPlugin.java | 30 - .../main/java/org/teavm/maven/TestReport.java | 31 -- .../main/java/org/teavm/maven/TestResult.java | 78 --- .../java/org/teavm/maven/TestRunStrategy.java | 32 -- 30 files changed, 404 insertions(+), 1851 deletions(-) delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java delete mode 100644 tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java rename tools/{maven/plugin/src/main/java/org/teavm/maven => junit/src/main/java/org/teavm/junit}/HtmlUnitRunStrategy.java (81%) rename tools/{maven/plugin/src/main/java/org/teavm/maven => junit/src/main/java/org/teavm/junit}/SeleniumRunStrategy.java (76%) create mode 100644 tools/junit/src/main/java/org/teavm/junit/TestRun.java rename tools/{maven/plugin/src/main/java/org/teavm/maven/TestStatus.java => junit/src/main/java/org/teavm/junit/TestRunCallback.java} (77%) rename tools/{core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java => junit/src/main/java/org/teavm/junit/TestRunStrategy.java} (71%) rename tools/{maven/plugin/src/main/java/org/teavm/maven => junit/src/main/java/org/teavm/junit}/TestRunner.java (50%) delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionPlugin.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java diff --git a/tests/teavm-tests.iml b/tests/teavm-tests.iml index ee1b398d9..874da67d8 100644 --- a/tests/teavm-tests.iml +++ b/tests/teavm-tests.iml @@ -30,5 +30,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java b/tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java deleted file mode 100644 index 1977a6c7b..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -/** - * - * @author Alexey Andreev - */ -final class ExceptionHelper { - private ExceptionHelper() { - } - - public static String showException(Throwable e) { - return e.getMessage(); - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java deleted file mode 100644 index 2f45a4431..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.atomic.AtomicInteger; -import org.apache.commons.io.IOUtils; -import org.teavm.common.FiniteExecutor; -import org.teavm.common.SimpleFiniteExecutor; -import org.teavm.common.ThreadPoolFiniteExecutor; -import org.teavm.debugging.information.DebugInformation; -import org.teavm.debugging.information.DebugInformationBuilder; -import org.teavm.javascript.EmptyRegularMethodNodeCache; -import org.teavm.javascript.InMemoryRegularMethodNodeCache; -import org.teavm.javascript.MethodNodeCache; -import org.teavm.model.ClassHolder; -import org.teavm.model.ClassHolderSource; -import org.teavm.model.ClassHolderTransformer; -import org.teavm.model.CopyClassHolderSource; -import org.teavm.model.InMemoryProgramCache; -import org.teavm.model.MethodHolder; -import org.teavm.model.MethodReference; -import org.teavm.model.PreOptimizingClassHolderSource; -import org.teavm.model.ProgramCache; -import org.teavm.parsing.ClasspathClassHolderSource; -import org.teavm.testing.JUnitTestAdapter; -import org.teavm.testing.TestAdapter; -import org.teavm.tooling.BaseTeaVMTool; -import org.teavm.tooling.EmptyTeaVMToolLog; -import org.teavm.tooling.TeaVMProblemRenderer; -import org.teavm.tooling.TeaVMToolException; -import org.teavm.tooling.TeaVMToolLog; -import org.teavm.tooling.sources.SourceFileProvider; -import org.teavm.tooling.sources.SourceFilesCopier; -import org.teavm.vm.DirectoryBuildTarget; -import org.teavm.vm.TeaVM; -import org.teavm.vm.TeaVMBuilder; - -/** - * - * @author Alexey Andreev - */ -public class TeaVMTestTool implements BaseTeaVMTool { - private File outputDir = new File("."); - private boolean minifying = true; - private int numThreads = 1; - private TestAdapter adapter = new JUnitTestAdapter(); - private List transformers = new ArrayList<>(); - private List additionalScripts = new ArrayList<>(); - private List additionalScriptLocalPaths = new ArrayList<>(); - private Properties properties = new Properties(); - private List testClasses = new ArrayList<>(); - private ClassLoader classLoader = TeaVMTestTool.class.getClassLoader(); - private TeaVMToolLog log = new EmptyTeaVMToolLog(); - private boolean debugInformationGenerated; - private boolean sourceMapsFileGenerated; - private boolean sourceFilesCopied; - private boolean incremental; - private List sourceFileProviders = new ArrayList<>(); - private MethodNodeCache astCache; - private ProgramCache programCache; - private SourceFilesCopier sourceFilesCopier; - private List testPlan = new ArrayList<>(); - private int fileIndexGenerator; - private long startTime; - private int testCount; - private AtomicInteger testsBuilt = new AtomicInteger(); - - public File getOutputDir() { - return outputDir; - } - - @Override - public void setTargetDirectory(File outputDir) { - this.outputDir = outputDir; - } - - public boolean isMinifying() { - return minifying; - } - - @Override - public void setMinifying(boolean minifying) { - this.minifying = minifying; - } - - public int getNumThreads() { - return numThreads; - } - - public void setNumThreads(int numThreads) { - this.numThreads = numThreads; - } - - public TestAdapter getAdapter() { - return adapter; - } - - public void setAdapter(TestAdapter adapter) { - this.adapter = adapter; - } - - @Override - public List getTransformers() { - return transformers; - } - - public List getAdditionalScripts() { - return additionalScripts; - } - - @Override - public Properties getProperties() { - return properties; - } - - public List getTestClasses() { - return testClasses; - } - - public ClassLoader getClassLoader() { - return classLoader; - } - - @Override - public void setClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public TeaVMToolLog getLog() { - return log; - } - - @Override - public void setLog(TeaVMToolLog log) { - this.log = log; - } - - public boolean isIncremental() { - return incremental; - } - - @Override - public void setIncremental(boolean incremental) { - this.incremental = incremental; - } - - public boolean isDebugInformationGenerated() { - return debugInformationGenerated; - } - - @Override - public void setDebugInformationGenerated(boolean debugInformationGenerated) { - this.debugInformationGenerated = debugInformationGenerated; - } - - public boolean isSourceMapsFileGenerated() { - return sourceMapsFileGenerated; - } - - @Override - public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) { - this.sourceMapsFileGenerated = sourceMapsFileGenerated; - } - - public boolean isSourceFilesCopied() { - return sourceFilesCopied; - } - - @Override - public void setSourceFilesCopied(boolean sourceFilesCopied) { - this.sourceFilesCopied = sourceFilesCopied; - } - - @Override - public void addSourceFileProvider(SourceFileProvider sourceFileProvider) { - sourceFileProviders.add(sourceFileProvider); - } - - public TestPlan generate() throws TeaVMToolException { - testsBuilt.set(0); - Runnable finalizer = null; - try { - new File(outputDir, "tests").mkdirs(); - new File(outputDir, "res").mkdirs(); - resourceToFile("org/teavm/javascript/runtime.js", "res/runtime.js"); - String prefix = "org/teavm/tooling/test"; - resourceToFile(prefix + "/res/junit-support.js", "res/junit-support.js"); - resourceToFile(prefix + "/res/junit-client.js", "res/junit-client.js"); - resourceToFile(prefix + "/res/junit.css", "res/junit.css"); - resourceToFile(prefix + "/res/class_obj.png", "res/class_obj.png"); - resourceToFile(prefix + "/res/control-000-small.png", "res/control-000-small.png"); - resourceToFile(prefix + "/res/methpub_obj.png", "res/methpub_obj.png"); - resourceToFile(prefix + "/res/package_obj.png", "res/package_obj.png"); - resourceToFile(prefix + "/res/tick-small-red.png", "res/tick-small-red.png"); - resourceToFile(prefix + "/res/tick-small.png", "res/tick-small.png"); - resourceToFile(prefix + "/res/toggle-small-expand.png", "res/toggle-small-expand.png"); - resourceToFile(prefix + "/res/toggle-small.png", "res/toggle-small.png"); - resourceToFile(prefix + "/junit.html", "junit.html"); - resourceToFile(prefix + "/junit-client.html", "junit-client.html"); - ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader); - if (incremental) { - classSource = new PreOptimizingClassHolderSource(classSource); - } - - List groups = new ArrayList<>(); - for (String testClass : testClasses) { - ClassHolder classHolder = classSource.get(testClass); - if (classHolder == null) { - throw new TeaVMToolException("Could not find class " + testClass); - } - TestGroup group = findTests(classHolder); - if (group != null) { - groups.add(group); - } - } - - includeAdditionalScripts(classLoader); - astCache = new EmptyRegularMethodNodeCache(); - if (incremental) { - astCache = new InMemoryRegularMethodNodeCache(); - programCache = new InMemoryProgramCache(); - } - writeMetadata(); - - FiniteExecutor executor = new SimpleFiniteExecutor(); - if (numThreads != 1) { - int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors(); - ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads); - finalizer = threadedExecutor::stop; - executor = threadedExecutor; - } - startTime = System.currentTimeMillis(); - int methodsGenerated = writeMethods(executor, classSource); - - if (sourceFilesCopied) { - sourceFilesCopier.copy(new File(new File(outputDir, "tests"), "src")); - } - long timeSpent = System.currentTimeMillis() - startTime; - log.info("Test files successfully generated for " + methodsGenerated + " method(s) in " - + (timeSpent / 1000.0) + " seconds."); - - return new TestPlan("res/runtime.js", groups); - } catch (IOException e) { - throw new TeaVMToolException("IO error occured generating JavaScript files", e); - } finally { - if (finalizer != null) { - finalizer.run(); - } - } - } - - private void writeMetadata() throws IOException { - File allTestsFile = new File(outputDir, "tests/all.js"); - try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { - allTestsWriter.write("prepare = function() {\n"); - allTestsWriter.write(" return new JUnitServer(document.body).readTests(["); - boolean first = true; - for (TestClassBuilder testClass : testPlan) { - if (!first) { - allTestsWriter.append(","); - } - first = false; - allTestsWriter.append("\n { name : \"").append(testClass.getClassName()) - .append("\", methods : ["); - boolean firstMethod = true; - for (TestMethodBuilder testMethod : testClass.getMethods()) { - String scriptName = testMethod.getFileName(); - if (!firstMethod) { - allTestsWriter.append(","); - } - firstMethod = false; - allTestsWriter.append("\n { name : \"" + testMethod.getMethod().getName() - + "\", script : \"" + scriptName + "\", expected : ["); - boolean firstException = true; - for (String exception : testMethod.getExpectedExceptions()) { - if (!firstException) { - allTestsWriter.append(", "); - } - firstException = false; - allTestsWriter.append("\"" + exception + "\""); - } - allTestsWriter.append("], additionalScripts : ["); - for (int i = 0; i < additionalScriptLocalPaths.size(); ++i) { - if (i > 0) { - allTestsWriter.append(", "); - } - escapeString(additionalScriptLocalPaths.get(i), allTestsWriter); - } - allTestsWriter.append("] }"); - } - allTestsWriter.append("] }"); - } - allTestsWriter.write("], function() {}); }"); - } - } - - private int writeMethods(FiniteExecutor executor, ClassHolderSource classSource) { - int methodsGenerated = 0; - log.info("Generating test files"); - sourceFilesCopier = new SourceFilesCopier(sourceFileProviders); - sourceFilesCopier.setLog(log); - for (TestClassBuilder testClass : testPlan) { - for (TestMethodBuilder testMethod : testClass.getMethods()) { - executor.execute(() -> { - log.debug("Building test for " + testMethod.getMethod()); - try { - decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), - testMethod); - } catch (IOException e) { - log.error("Error generating JavaScript", e); - } - }); - ++methodsGenerated; - } - } - executor.complete(); - return methodsGenerated; - } - - private void resourceToFile(String resource, String fileName) throws IOException { - try (InputStream input = TeaVMTestTool.class.getClassLoader().getResourceAsStream(resource)) { - try (OutputStream output = new FileOutputStream(new File(outputDir, fileName))) { - IOUtils.copy(input, output); - } - } - } - - private TestGroup findTests(ClassHolder cls) { - List cases = new ArrayList<>(); - TestClassBuilder testClass = new TestClassBuilder(cls.getName()); - for (MethodHolder method : cls.getMethods()) { - if (adapter.acceptMethod(method)) { - MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor()); - String fileName = "tests/" + fileIndexGenerator++ + ".js"; - - List exceptions = new ArrayList<>(); - for (String exception : adapter.getExpectedExceptions(method)) { - exceptions.add(exception); - } - - String runner = adapter.getRunner(method).getName(); - - TestMethodBuilder testMethod = new TestMethodBuilder(ref, fileName, exceptions, runner); - testClass.getMethods().add(testMethod); - - String debugTable = debugInformationGenerated ? testMethod.getFileName() + ".teavmdbg" : null; - cases.add(new TestCase(ref.toString(), testMethod.getFileName(), debugTable, - testMethod.getExpectedExceptions(), runner)); - ++testCount; - } - } - if (!testClass.getMethods().isEmpty()) { - testPlan.add(testClass); - return new TestGroup(cls.getName(), cases); - } else { - return null; - } - } - - private void includeAdditionalScripts(ClassLoader classLoader) throws TeaVMToolException { - if (additionalScripts == null) { - return; - } - for (String script : additionalScripts) { - String simpleName = script.substring(script.lastIndexOf('/') + 1); - additionalScriptLocalPaths.add("tests/" + simpleName); - if (classLoader.getResource(script) == null) { - throw new TeaVMToolException("Additional script " + script + " was not found"); - } - File file = new File(outputDir, "tests/" + simpleName); - try (InputStream in = classLoader.getResourceAsStream(script)) { - if (!file.exists()) { - file.createNewFile(); - } - try (OutputStream out = new FileOutputStream(file)) { - IOUtils.copy(in, out); - } - } catch (IOException e) { - throw new TeaVMToolException("Error copying additional script " + script, e); - } - } - } - - private void decompileClassesForTest(ClassLoader classLoader, ClassHolderSource classSource, - TestMethodBuilder testMethod) throws IOException { - String targetName = testMethod.getFileName(); - TeaVM vm = new TeaVMBuilder() - .setClassLoader(classLoader) - .setClassSource(classSource) - .build(); - vm.setIncremental(incremental); - vm.setAstCache(astCache); - vm.setProgramCache(programCache); - vm.setProperties(properties); - vm.setMinifying(minifying); - vm.installPlugins(); - new TestExceptionPlugin().install(vm); - new TestEntryPointTransformer(testMethod.getRunner(), testMethod.getMethod()).install(vm); - for (ClassHolderTransformer transformer : transformers) { - vm.add(transformer); - } - - File file = new File(outputDir, testMethod.getFileName()); - DebugInformationBuilder debugInfoBuilder = sourceMapsFileGenerated || debugInformationGenerated - ? new DebugInformationBuilder() : null; - MethodReference methodRef = testMethod.getMethod(); - try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { - MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", - Throwable.class, String.class); - vm.entryPoint("runTest", new MethodReference(TestEntryPoint.class, "run", void.class)).async(); - vm.entryPoint("extractException", exceptionMsg); - vm.setDebugEmitter(debugInfoBuilder); - vm.build(innerWriter, new DirectoryBuildTarget(outputDir)); - innerWriter.append("\n"); - innerWriter.append("\nJUnitClient.run();"); - if (sourceMapsFileGenerated) { - String sourceMapsFileName = targetName.substring(targetName.lastIndexOf('/') + 1) + ".map"; - innerWriter.append("\n//# sourceMappingURL=").append(sourceMapsFileName); - } - if (!vm.getProblemProvider().getProblems().isEmpty()) { - if (vm.getProblemProvider().getSevereProblems().isEmpty()) { - log.warning("Test built with warnings: " + methodRef); - TeaVMProblemRenderer.describeProblems(vm, log); - } else { - log.warning("Test built with errors: " + methodRef); - TeaVMProblemRenderer.describeProblems(vm, log); - } - } - } - - if (debugInformationGenerated) { - DebugInformation debugInfo = debugInfoBuilder.getDebugInformation(); - File debugTableFile = new File(outputDir, targetName + ".teavmdbg"); - try (OutputStream debugInfoOut = new FileOutputStream(debugTableFile)) { - debugInfo.write(debugInfoOut); - } - } - - if (sourceMapsFileGenerated) { - DebugInformation debugInfo = debugInfoBuilder.getDebugInformation(); - String sourceMapsFileName = targetName + ".map"; - try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream( - new File(outputDir, sourceMapsFileName)), "UTF-8")) { - debugInfo.writeAsSourceMaps(sourceMapsOut, "src", targetName); - } - } - if (sourceFilesCopied && vm.getWrittenClasses() != null) { - sourceFilesCopier.addClasses(vm.getWrittenClasses()); - } - - incrementCounter(); - } - - private void incrementCounter() { - int count = testsBuilt.incrementAndGet(); - if (count % 10 != 0) { - return; - } - - long timeSpent = System.currentTimeMillis() - startTime; - - getLog().info(count + " of " + testCount + " tests built in " + (timeSpent / 1000.0) + " seconds (" - + String.format("%.2f", (double) count / timeSpent * 1000.0) + " tests per second avg.)"); - } - - private void escapeString(String string, Writer writer) throws IOException { - writer.append('\"'); - for (int i = 0; i < string.length(); ++i) { - char c = string.charAt(i); - switch (c) { - case '"': - writer.append("\\\""); - break; - case '\\': - writer.append("\\\\"); - break; - case '\n': - writer.append("\\n"); - break; - case '\r': - writer.append("\\r"); - break; - case '\t': - writer.append("\\t"); - break; - default: - writer.append(c); - break; - } - } - writer.append('\"'); - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java deleted file mode 100644 index 825b46752..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * - * @author Alexey Andreev - */ -public class TestCase { - private String testMethod; - private String testScript; - private String debugTable; - private List expectedExceptions = new ArrayList<>(); - private String runner; - - @JsonCreator - public TestCase( - @JsonProperty("testMethod") String testMethod, - @JsonProperty("script") String testScript, - @JsonProperty("debugTable") String debugTable, - @JsonProperty("expectedExceptions") List expectedExceptions, - @JsonProperty("runner") String runner) { - this.testMethod = testMethod; - this.testScript = testScript; - this.debugTable = debugTable; - this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); - this.runner = runner; - } - - @JsonGetter - public String getTestMethod() { - return testMethod; - } - - @JsonGetter("script") - public String getTestScript() { - return testScript; - } - - @JsonGetter - public String getDebugTable() { - return debugTable; - } - - @JsonGetter - public List getExpectedExceptions() { - return expectedExceptions; - } - - @JsonGetter - public String getRunner() { - return runner; - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java deleted file mode 100644 index d50e2b264..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author Alexey Andreev - */ -class TestClassBuilder { - private String className; - private List methods = new ArrayList<>(); - - public TestClassBuilder(String className) { - this.className = className; - } - - public String getClassName() { - return className; - } - - public List getMethods() { - return methods; - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java deleted file mode 100644 index 5208d78ab..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPoint.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import org.teavm.testing.TestRunner; - -/** - * - * @author Alexey Andreev - */ -final class TestEntryPoint { - private static Object testCase; - - private TestEntryPoint() { - } - - public static void run() throws Throwable { - createRunner().run(() -> launchTest()); - } - - private static native TestRunner createRunner(); - - private static native void launchTest(); -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java deleted file mode 100644 index 914c60844..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestEntryPointTransformer.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import org.teavm.diagnostics.Diagnostics; -import org.teavm.model.ClassHolder; -import org.teavm.model.ClassHolderTransformer; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.ElementModifier; -import org.teavm.model.MethodHolder; -import org.teavm.model.MethodReference; -import org.teavm.model.Program; -import org.teavm.model.ValueType; -import org.teavm.model.emit.ProgramEmitter; -import org.teavm.model.emit.ValueEmitter; -import org.teavm.vm.spi.TeaVMHost; -import org.teavm.vm.spi.TeaVMPlugin; - -/** - * - * @author Alexey Andreev - */ -class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin { - private String runnerClassName; - private MethodReference testMethod; - - public TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) { - this.runnerClassName = runnerClassName; - this.testMethod = testMethod; - } - - @Override - public void install(TeaVMHost host) { - host.add(this); - } - - @Override - public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { - if (cls.getName().equals(TestEntryPoint.class.getName())) { - for (MethodHolder method : cls.getMethods()) { - if (method.getName().equals("createRunner")) { - method.setProgram(generateRunnerProgram(method, innerSource)); - method.getModifiers().remove(ElementModifier.NATIVE); - } else if (method.getName().equals("launchTest")) { - method.setProgram(generateLaunchProgram(method, innerSource)); - method.getModifiers().remove(ElementModifier.NATIVE); - } - } - } - } - - private Program generateRunnerProgram(MethodHolder method, ClassReaderSource innerSource) { - ProgramEmitter pe = ProgramEmitter.create(method, innerSource); - pe.construct(runnerClassName).returnValue(); - return pe.getProgram(); - } - - private Program generateLaunchProgram(MethodHolder method, ClassReaderSource innerSource) { - ProgramEmitter pe = ProgramEmitter.create(method, innerSource); - ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class); - pe.when(testCaseVar.isNull()) - .thenDo(() -> { - pe.setField(TestEntryPoint.class, "testCase", - pe.construct(testMethod.getClassName()).cast(Object.class)); - }); - pe.getField(TestEntryPoint.class, "testCase", Object.class) - .cast(ValueType.object(testMethod.getClassName())) - .invokeSpecial(testMethod); - pe.exit(); - return pe.getProgram(); - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java deleted file mode 100644 index e388e80f6..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import org.teavm.dependency.AbstractDependencyListener; -import org.teavm.dependency.DependencyAgent; -import org.teavm.dependency.DependencyNode; -import org.teavm.dependency.MethodDependency; -import org.teavm.model.CallLocation; -import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.MethodReference; - -/** - * - * @author Alexey Andreev - */ -class TestExceptionDependency extends AbstractDependencyListener { - private MethodReference getMessageRef = new MethodReference(ExceptionHelper.class, "showException", - Throwable.class, String.class); - private DependencyNode allClasses; - - @Override - public void started(DependencyAgent agent) { - allClasses = agent.createNode(); - } - - @Override - public void classReached(DependencyAgent agent, String className, CallLocation location) { - if (isException(agent.getClassSource(), className)) { - allClasses.propagate(agent.getType(className)); - } - } - - private boolean isException(ClassReaderSource classSource, String className) { - while (className != null) { - if (className.equals("java.lang.Throwable")) { - return true; - } - ClassReader cls = classSource.get(className); - if (cls == null) { - return false; - } - className = cls.getParent(); - } - return false; - } - - @Override - public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { - if (method.getReference().equals(getMessageRef)) { - allClasses.connect(method.getVariable(1)); - } - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java deleted file mode 100644 index 19a2c04a1..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import org.teavm.vm.spi.TeaVMHost; -import org.teavm.vm.spi.TeaVMPlugin; - -/** - * - * @author Alexey Andreev - */ -class TestExceptionPlugin implements TeaVMPlugin { - @Override - public void install(TeaVMHost host) { - host.add(new TestExceptionDependency()); - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java deleted file mode 100644 index 4ab9d5cbb..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * - * @author Alexey Andreev - */ -public class TestGroup { - private String className; - private List testCases; - - @JsonCreator - public TestGroup(@JsonProperty("className") String className, - @JsonProperty("testCases") List testCases) { - this.className = className; - this.testCases = Collections.unmodifiableList(new ArrayList<>(testCases)); - } - - @JsonGetter("className") - public String getClassName() { - return className; - } - - @JsonGetter("testCases") - public List getTestCases() { - return testCases; - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java deleted file mode 100644 index 9f81a41f6..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.teavm.model.MethodReference; - -/** - * - * @author Alexey Andreev - */ -class TestMethodBuilder { - private MethodReference method; - private String fileName; - private List expectedExceptions = new ArrayList<>(); - private String runner; - - public TestMethodBuilder(MethodReference method, String fileName, List expectedExceptions, - String runner) { - this.method = method; - this.fileName = fileName; - this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); - this.runner = runner; - } - - public MethodReference getMethod() { - return method; - } - - public String getFileName() { - return fileName; - } - - public List getExpectedExceptions() { - return expectedExceptions; - } - - public String getRunner() { - return runner; - } -} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java deleted file mode 100644 index 32ce4b364..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.tooling.testing; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * - * @author Alexey Andreev - */ -public class TestPlan { - private String runtimeScript; - private List groups = new ArrayList<>(); - - @JsonCreator - public TestPlan( - @JsonProperty("runtimeScript") String runtimeScript, - @JsonProperty("groups") List groups) { - this.runtimeScript = runtimeScript; - this.groups = Collections.unmodifiableList(new ArrayList<>(groups)); - } - - @JsonGetter - public String getRuntimeScript() { - return runtimeScript; - } - - @JsonGetter - public List getGroups() { - return groups; - } -} diff --git a/tools/junit/pom.xml b/tools/junit/pom.xml index d4959089d..e22e24331 100644 --- a/tools/junit/pom.xml +++ b/tools/junit/pom.xml @@ -38,6 +38,23 @@ teavm-tooling ${project.version} + + org.seleniumhq.selenium + selenium-java + + + org.seleniumhq.selenium + selenium-remote-driver + + + com.fasterxml.jackson.core + jackson-databind + + + net.sourceforge.htmlunit + htmlunit + 2.18 + diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java b/tools/junit/src/main/java/org/teavm/junit/HtmlUnitRunStrategy.java similarity index 81% rename from tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java rename to tools/junit/src/main/java/org/teavm/junit/HtmlUnitRunStrategy.java index 0b7b24a57..7265ff1d3 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java +++ b/tools/junit/src/main/java/org/teavm/junit/HtmlUnitRunStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Alexey Andreev. + * Copyright 2016 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.maven; +package org.teavm.junit; import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.WebClient; @@ -28,20 +28,8 @@ import java.util.concurrent.TimeUnit; import net.sourceforge.htmlunit.corejs.javascript.Function; import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject; import org.apache.commons.io.IOUtils; -import org.apache.maven.plugin.logging.Log; -import org.teavm.tooling.testing.TestCase; -/** - * - * @author Alexey Andreev - */ public class HtmlUnitRunStrategy implements TestRunStrategy { - private File directory; - - public HtmlUnitRunStrategy(File directory) { - this.directory = directory; - } - @Override public void beforeThread() { } @@ -51,10 +39,10 @@ public class HtmlUnitRunStrategy implements TestRunStrategy { } @Override - public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException { + public String runTest(TestRun run) throws IOException { try (WebClient webClient = new WebClient(BrowserVersion.CHROME)) { HtmlPage page = webClient.getPage("about:blank"); - page.executeJavaScript(readFile(new File(directory, runtimeScript))); + page.executeJavaScript(readFile(new File(run.getBaseDirectory(), "runtime.js"))); AsyncResult asyncResult = new AsyncResult(); Function function = (Function) page.executeJavaScript(readResource("teavm-htmlunit-adapter.js")) @@ -62,7 +50,7 @@ public class HtmlUnitRunStrategy implements TestRunStrategy { Object[] args = new Object[] { new NativeJavaObject(function, asyncResult, AsyncResult.class) }; page.executeJavaScriptFunctionIfPossible(function, function, args, page); - page.executeJavaScript(readFile(new File(directory, testCase.getTestScript()))); + page.executeJavaScript(readFile(new File(run.getBaseDirectory(), "test.js"))); page.cleanUp(); for (WebWindow window : webClient.getWebWindows()) { window.getJobManager().removeAllJobs(); @@ -78,7 +66,7 @@ public class HtmlUnitRunStrategy implements TestRunStrategy { } private String readResource(String resourceName) throws IOException { - try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resourceName)) { + try (InputStream input = HtmlUnitRunStrategy.class.getClassLoader().getResourceAsStream(resourceName)) { if (input == null) { return ""; } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java b/tools/junit/src/main/java/org/teavm/junit/SeleniumRunStrategy.java similarity index 76% rename from tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java rename to tools/junit/src/main/java/org/teavm/junit/SeleniumRunStrategy.java index fdaeac6ed..6c3f7d8cb 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java +++ b/tools/junit/src/main/java/org/teavm/junit/SeleniumRunStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Alexey Andreev. + * Copyright 2016 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.maven; +package org.teavm.junit; import java.io.File; import java.io.FileInputStream; @@ -23,27 +23,18 @@ import java.net.URL; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; -import org.apache.maven.plugin.logging.Log; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; -import org.teavm.tooling.testing.TestCase; -/** - * - * @author Alexey Andreev - */ public class SeleniumRunStrategy implements TestRunStrategy { private URL url; - private File directory; private ThreadLocal webDriver = new ThreadLocal<>(); private ThreadLocal commandsSent = new ThreadLocal<>(); - public SeleniumRunStrategy(URL url, File directory) { + public SeleniumRunStrategy(URL url) { this.url = url; - this.directory = directory; } @Override @@ -61,7 +52,7 @@ public class SeleniumRunStrategy implements TestRunStrategy { } @Override - public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException { + public String runTest(TestRun run) throws IOException { commandsSent.set(commandsSent.get() + 1); if (commandsSent.get().equals(20)) { commandsSent.set(0); @@ -75,16 +66,16 @@ public class SeleniumRunStrategy implements TestRunStrategy { try { return (String) js.executeAsyncScript( readResource("teavm-selenium.js"), - readFile(new File(directory, runtimeScript)), - readFile(new File(directory, testCase.getTestScript())), + readFile(new File(run.getBaseDirectory(), "runtime.js")), + readFile(new File(run.getBaseDirectory(), "test.js")), readResource("teavm-selenium-adapter.js")); - } catch (WebDriverException e) { - log.error("Error occured running test " + testCase.getTestMethod(), e); + } catch (Throwable e) { + run.getCallback().error(e); @SuppressWarnings("unchecked") List errors = (List) js.executeScript("return window.jsErrors;"); if (errors != null) { for (Object error : errors) { - log.error(" -- additional error: " + error); + run.getCallback().error(new AssertionError(error)); } } return null; @@ -98,7 +89,7 @@ public class SeleniumRunStrategy implements TestRunStrategy { } private String readResource(String resourceName) throws IOException { - try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resourceName)) { + try (InputStream input = SeleniumRunStrategy.class.getClassLoader().getResourceAsStream(resourceName)) { if (input == null) { return ""; } diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java index 66983c754..7c5e5a5d1 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java @@ -24,6 +24,8 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -32,12 +34,16 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import org.apache.commons.io.IOUtils; import org.junit.runner.Description; +import org.junit.runner.Runner; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; -import org.junit.runners.ParentRunner; import org.junit.runners.model.InitializationError; import org.teavm.callgraph.CallGraph; import org.teavm.diagnostics.DefaultProblemTextConsumer; @@ -53,23 +59,32 @@ import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.testing.JUnitTestAdapter; import org.teavm.testing.TestAdapter; import org.teavm.tooling.TeaVMProblemRenderer; -import org.teavm.tooling.testing.TeaVMTestTool; import org.teavm.vm.DirectoryBuildTarget; import org.teavm.vm.TeaVM; import org.teavm.vm.TeaVMBuilder; -public class TeaVMTestRunner extends ParentRunner { +public class TeaVMTestRunner extends Runner { private static final String PATH_PARAM = "teavm.junit.target"; + private static final String RUNNER = "teavm.junit.js.runner"; + private static final String SELENIUM_URL = "teavm.junit.js.selenium.url"; + private static final int stopTimeout = 15000; + private Class testClass; private ClassHolder classHolder; private ClassLoader classLoader; private ClassHolderSource classSource; + private Description suiteDescription; private static Map classSources = new WeakHashMap<>(); private File outputDir; private TestAdapter testAdapter = new JUnitTestAdapter(); private Map descriptions = new HashMap<>(); + private TestRunStrategy runStrategy; + private static volatile TestRunner runner; + private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); + private static volatile ScheduledFuture cleanupFuture; + private CountDownLatch latch; public TeaVMTestRunner(Class testClass) throws InitializationError { - super(testClass); + this.testClass = testClass; classLoader = TeaVMTestRunner.class.getClassLoader(); classSource = getClassSource(classLoader); classHolder = classSource.get(testClass.getName()); @@ -77,12 +92,62 @@ public class TeaVMTestRunner extends ParentRunner { if (outputPath != null) { outputDir = new File(outputPath); } + + String runStrategyName = System.getProperty(RUNNER); + if (runStrategyName != null) { + switch (runStrategyName) { + case "selenium": + try { + runStrategy = new SeleniumRunStrategy(new URL(System.getProperty(SELENIUM_URL))); + } catch (MalformedURLException e) { + throw new InitializationError(e); + } + break; + case "htmlunit": + runStrategy = new HtmlUnitRunStrategy(); + break; + default: + throw new InitializationError("Unknown run strategy: " + runStrategyName); + } + } } @Override + public Description getDescription() { + if (suiteDescription == null) { + suiteDescription = Description.createSuiteDescription(testClass); + for (Method child : getChildren()) { + suiteDescription.getChildren().add(describeChild(child)); + } + } + return suiteDescription; + } + + @Override + public void run(RunNotifier notifier) { + List children = getChildren(); + latch = new CountDownLatch(children.size()); + + notifier.fireTestStarted(getDescription()); + for (Method child : children) { + runChild(child, notifier); + } + + while (true) { + try { + if (latch.await(1000, TimeUnit.MILLISECONDS)) { + break; + } + } catch (InterruptedException e) { + break; + } + } + notifier.fireTestFinished(getDescription()); + } + protected List getChildren() { List children = new ArrayList<>(); - for (Method method : getTestClass().getJavaClass().getDeclaredMethods()) { + for (Method method : testClass.getDeclaredMethods()) { MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method)); if (testAdapter.acceptMethod(methodHolder)) { children.add(method); @@ -91,33 +156,32 @@ public class TeaVMTestRunner extends ParentRunner { return children; } - @Override protected Description describeChild(Method child) { - return descriptions.computeIfAbsent(child, method -> Description.createTestDescription( - getTestClass().getJavaClass(), method.getName())); + return descriptions.computeIfAbsent(child, method -> Description.createTestDescription(testClass, + method.getName())); } - @Override protected void runChild(Method child, RunNotifier notifier) { notifier.fireTestStarted(describeChild(child)); boolean run = false; boolean success = true; - if (outputDir != null) { - run = true; - success &= runInTeaVM(child, notifier); - } - if (success && !child.isAnnotationPresent(SkipJVM.class) + if (!child.isAnnotationPresent(SkipJVM.class) && !child.getDeclaringClass().isAnnotationPresent(SkipJVM.class)) { run = true; - success &= runInJvm(child, notifier); + success = runInJvm(child, notifier); } - if (!run) { - notifier.fireTestIgnored(describeChild(child)); + if (success && outputDir != null) { + runInTeaVM(child, notifier); + } else { + if (!run) { + notifier.fireTestIgnored(describeChild(child)); + } + notifier.fireTestFinished(describeChild(child)); + latch.countDown(); } - notifier.fireTestFinished(describeChild(child)); } private boolean runInJvm(Method child, RunNotifier notifier) { @@ -134,7 +198,7 @@ public class TeaVMTestRunner extends ParentRunner { Object instance; try { - instance = getTestClass().getJavaClass().newInstance(); + instance = testClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { notifier.fireTestFailure(new Failure(describeChild(child), e)); return false; @@ -184,9 +248,55 @@ public class TeaVMTestRunner extends ParentRunner { return false; } + Description description = describeChild(child); + TestRunCallback callback = new TestRunCallback() { + @Override + public void complete() { + notifier.fireTestFinished(description); + latch.countDown(); + } + + @Override + public void error(Throwable e) { + notifier.fireTestFailure(new Failure(description, e)); + } + }; + + TestRun run = new TestRun(compileResult.file.getParentFile(), child, + new MethodReference(testClass.getName(), getDescriptor(child)), + description, callback); + submitRun(run); return true; } + private void submitRun(TestRun run) { + synchronized (TeaVMTestRunner.class) { + if (runStrategy == null) { + return; + } + + if (runner == null) { + runner = new TestRunner(runStrategy); + runner.init(); + } + runner.run(run); + + if (cleanupFuture != null) { + cleanupFuture.cancel(false); + cleanupFuture = null; + } + cleanupFuture = executor.schedule(TeaVMTestRunner::cleanupRunner, stopTimeout, TimeUnit.MILLISECONDS); + } + } + + private static void cleanupRunner() { + synchronized (TeaVMTestRunner.class) { + cleanupFuture = null; + runner.stop(); + runner = null; + } + } + private CompileResult compileTest(Method method) throws IOException { CompileResult result = new CompileResult(); @@ -254,7 +364,7 @@ public class TeaVMTestRunner extends ParentRunner { } private void resourceToFile(String resource, File fileName) throws IOException { - try (InputStream input = TeaVMTestTool.class.getClassLoader().getResourceAsStream(resource); + try (InputStream input = TeaVMTestRunner.class.getClassLoader().getResourceAsStream(resource); OutputStream output = new FileOutputStream(fileName)) { IOUtils.copy(input, output); } diff --git a/tools/junit/src/main/java/org/teavm/junit/TestRun.java b/tools/junit/src/main/java/org/teavm/junit/TestRun.java new file mode 100644 index 000000000..c03eb5a68 --- /dev/null +++ b/tools/junit/src/main/java/org/teavm/junit/TestRun.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.junit; + +import java.io.File; +import java.lang.reflect.Method; +import org.junit.runner.Description; +import org.teavm.model.MethodReference; + +public class TestRun { + private File baseDirectory; + private Method method; + private MethodReference reference; + private Description description; + private TestRunCallback callback; + + public TestRun(File baseDirectory, Method method, MethodReference reference, Description description, + TestRunCallback callback) { + this.baseDirectory = baseDirectory; + this.method = method; + this.reference = reference; + this.description = description; + this.callback = callback; + } + + public File getBaseDirectory() { + return baseDirectory; + } + + public Method getMethod() { + return method; + } + + public MethodReference getReference() { + return reference; + } + + public Description getDescription() { + return description; + } + + public TestRunCallback getCallback() { + return callback; + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java b/tools/junit/src/main/java/org/teavm/junit/TestRunCallback.java similarity index 77% rename from tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java rename to tools/junit/src/main/java/org/teavm/junit/TestRunCallback.java index 366859a58..bf53f0606 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestRunCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Alexey Andreev. + * Copyright 2016 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.maven; +package org.teavm.junit; -/** - * - * @author Alexey Andreev - */ -public enum TestStatus { - PASSED, - ERROR, - EXCEPTION_NOT_THROWN +public interface TestRunCallback { + void complete(); + + void error(Throwable e); } diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java b/tools/junit/src/main/java/org/teavm/junit/TestRunStrategy.java similarity index 71% rename from tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java rename to tools/junit/src/main/java/org/teavm/junit/TestRunStrategy.java index 4460b2575..fd9d4af96 100644 --- a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestRunStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Alexey Andreev. + * Copyright 2016 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling.testing; +package org.teavm.junit; +import java.io.IOException; -/** - * - * @author Alexey Andreev - */ -public interface TeaVMTestToolListener { - void testGenerated(TestCase testCase); +public interface TestRunStrategy { + void beforeThread(); + + void afterThread(); + + String runTest(TestRun run) throws IOException; } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TestRunner.java similarity index 50% rename from tools/maven/plugin/src/main/java/org/teavm/maven/TestRunner.java rename to tools/junit/src/main/java/org/teavm/junit/TestRunner.java index ca823534e..4286ddc5b 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunner.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Alexey Andreev. + * Copyright 2016 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,46 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.maven; +package org.teavm.junit; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.apache.maven.plugin.logging.Log; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; import org.teavm.model.MethodReference; -import org.teavm.tooling.testing.TestCase; -import org.teavm.tooling.testing.TestGroup; -import org.teavm.tooling.testing.TestPlan; -/** - * - * @author Alexey Andreev - */ public class TestRunner { private int numThreads = 1; private TestRunStrategy strategy; private BlockingQueue taskQueue = new LinkedBlockingQueue<>(); private CountDownLatch latch; private volatile boolean stopped; - private Log log; - private List report = new CopyOnWriteArrayList<>(); - private ThreadLocal> localReport = new ThreadLocal<>(); public TestRunner(TestRunStrategy strategy) { this.strategy = strategy; } - public void setLog(Log log) { - this.log = log; - } - public int getNumThreads() { return numThreads; } @@ -61,23 +45,11 @@ public class TestRunner { this.numThreads = numThreads; } - public void run(TestPlan testPlan) { - init(); - for (TestGroup group : testPlan.getGroups()) { - for (TestCase testCase : group.getTestCases()) { - run(testPlan.getRuntimeScript(), testCase); - } - } - stop(); - waitForCompletion(); - } - - private void init() { + public void init() { latch = new CountDownLatch(numThreads); for (int i = 0; i < numThreads; ++i) { new Thread(() -> { strategy.beforeThread(); - localReport.set(new ArrayList<>()); while (!stopped || !taskQueue.isEmpty()) { Runnable task; try { @@ -89,8 +61,6 @@ public class TestRunner { task.run(); } } - report.addAll(localReport.get()); - localReport.remove(); strategy.afterThread(); latch.countDown(); }).start(); @@ -101,29 +71,21 @@ public class TestRunner { taskQueue.add(runnable); } - private void stop() { + public void stop() { stopped = true; + taskQueue.add(null); } - private void waitForCompletion() { + public void run(TestRun run) { + addTask(() -> runImpl(run)); + } + + private void runImpl(TestRun run) { + MethodReference ref = run.getReference(); try { - latch.await(); - } catch (InterruptedException e) { - return; - } - } - - private void run(String runtimeScript, TestCase testCase) { - addTask(() -> runImpl(runtimeScript, testCase)); - } - - private void runImpl(String runtimeScript, TestCase testCase) { - MethodReference ref = MethodReference.parse(testCase.getTestMethod()); - try { - String result = strategy.runTest(log, runtimeScript, testCase); + String result = strategy.runTest(run); if (result == null) { - log.info("Test failed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.error(ref, null, null)); + run.getCallback().complete(); return; } ObjectMapper mapper = new ObjectMapper(); @@ -131,35 +93,19 @@ public class TestRunner { String status = resultObject.get("status").asText(); switch (status) { case "ok": - if (testCase.getExpectedExceptions().isEmpty()) { - log.info("Test passed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.passed(ref)); - } else { - log.info("Test failed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.exceptionNotThrown(ref)); - } + run.getCallback().complete(); break; case "exception": { String stack = resultObject.get("stack").asText(); String exception = resultObject.get("exception").asText(); - if (!testCase.getExpectedExceptions().contains(exception)) { - log.info("Test failed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.error(ref, exception, stack)); - } else { - log.info("Test passed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.passed(ref)); - } + run.getCallback().error(new AssertionError(exception + "\n" + exception)); + run.getCallback().complete(); break; } } } catch (IOException e) { - log.error(e); + run.getCallback().error(e); + run.getCallback().complete(); } } - - public TestReport getReport() { - TestReport report = new TestReport(); - report.getResults().addAll(this.report); - return report; - } } diff --git a/tools/junit/src/main/resources/teavm-selenium-adapter.js b/tools/junit/src/main/resources/teavm-selenium-adapter.js index 5957c22b4..f122df3df 100644 --- a/tools/junit/src/main/resources/teavm-selenium-adapter.js +++ b/tools/junit/src/main/resources/teavm-selenium-adapter.js @@ -1,37 +1,50 @@ -var JUnitClient = {}; -JUnitClient.run = function() { - $rt_startThread(function() { - var thread = $rt_nativeThread(); - var instance; - var ptr = 0; - var message; - if (thread.isResuming()) { - ptr = thread.pop(); - instance = thread.pop(); - } - loop: while (true) { switch (ptr) { - case 0: - try { - runTest(); - } catch (e) { - message = {}; - JUnitClient.makeErrorMessage(message, e); - break loop; - } - if (thread.isSuspending()) { - thread.push(instance); - thread.push(ptr); - return; - } - message = {}; - message.status = "ok"; - break loop; - }} - window.parent.postMessage(JSON.stringify(message), "*"); - }) -}; +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -JUnitClient.makeErrorMessage = function(message, e) { +$rt_startThread(function() { + var thread = $rt_nativeThread(); + var instance; + var ptr = 0; + var message; + if (thread.isResuming()) { + ptr = thread.pop(); + instance = thread.pop(); + } + loop: while (true) { switch (ptr) { + case 0: + try { + runTest(); + } catch (e) { + message = {}; + JUnitClient.makeErrorMessage(message, e); + break loop; + } + if (thread.isSuspending()) { + thread.push(instance); + thread.push(ptr); + return; + } + message = {}; + message.status = "ok"; + break loop; + }} + window.parent.postMessage(JSON.stringify(message), "*"); +}); + +function makeErrorMessage(message, e) { message.status = "exception"; var stack = e.stack; if (e.$javaException && e.$javaException.constructor.$meta) { diff --git a/tools/junit/src/main/resources/teavm-selenium.js b/tools/junit/src/main/resources/teavm-selenium.js index 469974bd4..94395da4f 100644 --- a/tools/junit/src/main/resources/teavm-selenium.js +++ b/tools/junit/src/main/resources/teavm-selenium.js @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + var runtimeSource = arguments[0]; var testSource = arguments[1]; var adapterSource = arguments[2]; @@ -11,7 +27,7 @@ window.jsErrors = []; window.onerror = reportError; iframe.contentWindow.onerror = reportError; -loadScripts([ runtimeSource, adapterSource, testSource ]); +loadScripts([ runtimeSource, testSource, adapterSource ]); window.addEventListener("message", handleMessage); function handleMessage(event) { @@ -20,16 +36,12 @@ function handleMessage(event) { seleniumCallback(event.data) } -function loadScript(script, callback) { - callback() -} - function loadScripts(scripts) { for (var i = 0; i < scripts.length; ++i) { var elem = doc.createElement("script"); elem.type = "text/javascript"; doc.head.appendChild(elem); - elem.text = scripts[i] + elem.text = scripts[i]; } } function reportError(error, url, line) { diff --git a/tools/junit/teavm-junit.iml b/tools/junit/teavm-junit.iml index 70adf675b..579ab3b8a 100644 --- a/tools/junit/teavm-junit.iml +++ b/tools/junit/teavm-junit.iml @@ -16,8 +16,50 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java deleted file mode 100644 index ccc865863..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2013 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import org.apache.commons.io.FilenameUtils; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.teavm.testing.JUnitTestAdapter; -import org.teavm.testing.TestAdapter; -import org.teavm.tooling.TeaVMToolException; -import org.teavm.tooling.sources.DirectorySourceFileProvider; -import org.teavm.tooling.sources.SourceFileProvider; -import org.teavm.tooling.testing.TeaVMTestTool; -import org.teavm.tooling.testing.TestPlan; - -/** - * - * @author Alexey Andreev - */ -@Mojo(name = "testCompile", requiresDependencyResolution = ResolutionScope.TEST, - requiresDependencyCollection = ResolutionScope.TEST, - defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES) -public class BuildJavascriptTestMojo extends AbstractJavascriptMojo { - private static Set testScopes = new HashSet<>(Arrays.asList( - Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME, - Artifact.SCOPE_PROVIDED)); - @Parameter(defaultValue = "${project.build.directory}/javascript-test") - private File targetDirectory; - - @Parameter(defaultValue = "${project.build.sourceDirectory}") - private File sourceDirectory; - - @Parameter(defaultValue = "${project.build.testSourceDirectory}") - private File testSourceDirectory; - - @Parameter(defaultValue = "${project.build.testOutputDirectory}") - private File testFiles; - - @Parameter - private String[] wildcards = { "**.*Test", "**.*UnitTest" }; - - @Parameter - private String[] excludeWildcards = new String[0]; - - @Parameter - private boolean scanDependencies; - - @Parameter - private int numThreads = 1; - - @Parameter - private String adapterClass = JUnitTestAdapter.class.getName(); - - @Parameter - private String[] additionalScripts; - - private TeaVMTestTool tool = new TeaVMTestTool(); - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - if (System.getProperty("maven.test.skip", "false").equals("true") - || System.getProperty("skipTests") != null) { - getLog().info("Tests build skipped as specified by system property"); - return; - } - - setupTool(tool); - try { - if (!testFiles.isDirectory()) { - throw new MojoFailureException("Directory with tests doesn't exist: " + testFiles); - } - getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'"); - tool.setAdapter(createAdapter(classLoader)); - findTestClasses(classLoader, testFiles, ""); - if (scanDependencies) { - findTestsInDependencies(classLoader); - } - tool.setNumThreads(numThreads); - if (additionalScripts != null) { - tool.getAdditionalScripts().addAll(Arrays.asList(additionalScripts)); - } - writePlan(tool.generate()); - } catch (TeaVMToolException e) { - throw new MojoFailureException("Error occured generating JavaScript files", e); - } - } - - private void writePlan(TestPlan plan) throws MojoExecutionException { - File file = new File(targetDirectory, "plan.json"); - try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(writer, plan); - } catch (IOException e) { - throw new MojoExecutionException("Error writing test plan", e); - } - } - - private TestAdapter createAdapter(ClassLoader classLoader) throws MojoExecutionException { - Class adapterClsRaw; - try { - adapterClsRaw = Class.forName(adapterClass, true, classLoader); - } catch (ClassNotFoundException e) { - throw new MojoExecutionException("Adapter not found: " + adapterClass, e); - } - if (!TestAdapter.class.isAssignableFrom(adapterClsRaw)) { - throw new MojoExecutionException("Adapter " + adapterClass + " does not implement " - + TestAdapter.class.getName()); - } - Class adapterCls = adapterClsRaw.asSubclass(TestAdapter.class); - Constructor cons; - try { - cons = adapterCls.getConstructor(); - } catch (NoSuchMethodException e) { - throw new MojoExecutionException("No default constructor found for test adapter " + adapterClass, e); - } - try { - return cons.newInstance(); - } catch (IllegalAccessException | InstantiationException e) { - throw new MojoExecutionException("Error creating test adapter", e); - } catch (InvocationTargetException e) { - throw new MojoExecutionException("Error creating test adapter", e.getTargetException()); - } - } - - private void findTestsInDependencies(ClassLoader classLoader) throws MojoExecutionException { - try { - Log log = getLog(); - log.info("Scanning dependencies for tests"); - for (Artifact artifact : project.getArtifacts()) { - if (!testScopes.contains(artifact.getScope())) { - continue; - } - File file = artifact.getFile(); - if (file.isDirectory()) { - findTestClasses(classLoader, file, ""); - } else if (file.getName().endsWith(".jar")) { - findTestClassesInJar(classLoader, new JarFile(file)); - } - } - } catch (MalformedURLException e) { - throw new MojoExecutionException("Error gathering classpath information", e); - } catch (IOException e) { - throw new MojoExecutionException("Error scanning dependencies for tests", e); - } - } - - private void findTestClasses(ClassLoader classLoader, File folder, String prefix) { - for (File file : folder.listFiles()) { - if (file.isDirectory()) { - String newPrefix = prefix.isEmpty() ? file.getName() : prefix + "." + file.getName(); - findTestClasses(classLoader, file, newPrefix); - } else if (file.getName().endsWith(".class")) { - String className = file.getName().substring(0, file.getName().length() - ".class".length()); - if (!prefix.isEmpty()) { - className = prefix + "." + className; - } - addCandidate(classLoader, className); - } - } - } - - private void findTestClassesInJar(ClassLoader classLoader, JarFile jarFile) { - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - if (entry.isDirectory() || !entry.getName().endsWith(".class")) { - continue; - } - String className = entry.getName().substring(0, entry.getName().length() - ".class".length()) - .replace('/', '.'); - addCandidate(classLoader, className); - } - } - - private void addCandidate(ClassLoader classLoader, String className) { - boolean matches = false; - String simpleName = className.replace('.', '/'); - for (String wildcard : wildcards) { - if (FilenameUtils.wildcardMatch(simpleName, wildcard.replace('.', '/'))) { - matches = true; - break; - } - } - if (!matches) { - return; - } - - for (String wildcard : excludeWildcards) { - if (FilenameUtils.wildcardMatch(simpleName, wildcard.replace('.', '/'))) { - return; - } - } - - try { - Class candidate = Class.forName(className, true, classLoader); - if (tool.getAdapter().acceptClass(candidate)) { - tool.getTestClasses().add(candidate.getName()); - getLog().info("Test class detected: " + candidate.getName()); - } - } catch (ClassNotFoundException e) { - getLog().info("Could not load class `" + className + "' to search for tests"); - } - } - - @Override - protected File getTargetDirectory() { - return targetDirectory; - } - - @Override - protected List getAdditionalClassPath() { - return Arrays.asList(testFiles); - } - - @Override - protected boolean isSupportedScope(String scope) { - return testScopes.contains(scope); - } - - @Override - protected void addSourceProviders(List providers) { - providers.add(new DirectorySourceFileProvider(sourceDirectory)); - providers.add(new DirectorySourceFileProvider(testSourceDirectory)); - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java deleted file mode 100644 index 982f87cd3..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; -import java.net.MalformedURLException; -import java.net.URL; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.teavm.tooling.testing.TestPlan; - -/** - * - * @author Alexey Andreev - */ -@Mojo(name = "test", defaultPhase = LifecyclePhase.TEST) -public class RunTestsMojo extends AbstractMojo { - @Parameter(defaultValue = "${project.build.directory}/javascript-test") - private File testDirectory; - - @Parameter(defaultValue = "${project.build.directory}/teavm-test-report.json") - private File reportFile; - - @Parameter - private String seleniumURL; - - @Parameter - private int numThreads = 1; - - @Parameter - private boolean skip; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - if (skip) { - getLog().info("Tests run skipped as specified by skip property"); - return; - } - - if (System.getProperty("maven.test.skip", "false").equals("true") - || System.getProperty("skipTests") != null) { - getLog().info("Tests run skipped as specified by system property"); - return; - } - - TestRunner runner = new TestRunner(pickStrategy()); - runner.setLog(getLog()); - runner.setNumThreads(numThreads); - - TestPlan plan; - ObjectMapper mapper = new ObjectMapper(); - File file = new File(testDirectory, "plan.json"); - try (Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) { - plan = mapper.readValue(reader, TestPlan.class); - } catch (IOException e) { - throw new MojoExecutionException("Error reading test plan", e); - } - - runner.run(plan); - processReport(runner.getReport()); - } - - private TestRunStrategy pickStrategy() throws MojoFailureException { - if (seleniumURL != null) { - try { - return new SeleniumRunStrategy(new URL(seleniumURL), testDirectory); - } catch (MalformedURLException e) { - throw new MojoFailureException("Can't parse URL: " + seleniumURL, e); - } - } else { - return new HtmlUnitRunStrategy(testDirectory); - } - } - - private void processReport(TestReport report) throws MojoExecutionException, MojoFailureException { - if (report.getResults().isEmpty()) { - getLog().info("No tests ran"); - return; - } - - int failedTests = 0; - for (TestResult result : report.getResults()) { - if (result.getStatus() != TestStatus.PASSED) { - failedTests++; - } - } - - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.INDENT_OUTPUT); - try (Writer writer = new OutputStreamWriter(new FileOutputStream(reportFile), "UTF-8")) { - mapper.writeValue(writer, report); - } catch (IOException e) { - throw new MojoFailureException("Error writing test report", e); - } - - if (failedTests > 0) { - throw new MojoExecutionException(failedTests + " of " + report.getResults().size() + " test(s) failed"); - } else { - getLog().info("All of " + report.getResults().size() + " tests successfully passed"); - } - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java deleted file mode 100644 index 673a6e92e..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import org.teavm.dependency.AbstractDependencyListener; -import org.teavm.dependency.DependencyAgent; -import org.teavm.dependency.DependencyNode; -import org.teavm.dependency.MethodDependency; -import org.teavm.model.CallLocation; -import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; - -/** - * - * @author Alexey Andreev - */ -class TestExceptionDependency extends AbstractDependencyListener { - private MethodReference getMessageRef = new MethodReference("java.lang.Throwable", "getMessage", - ValueType.object("java.lang.String")); - private DependencyNode allClasses; - - @Override - public void started(DependencyAgent agent) { - allClasses = agent.createNode(); - } - - @Override - public void classReached(DependencyAgent agent, String className, CallLocation location) { - if (isException(agent.getClassSource(), className)) { - allClasses.propagate(agent.getType(className)); - } - } - - private boolean isException(ClassReaderSource classSource, String className) { - while (className != null) { - if (className.equals("java.lang.Throwable")) { - return true; - } - ClassReader cls = classSource.get(className); - if (cls == null) { - return false; - } - className = cls.getParent(); - } - return false; - } - - @Override - public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) { - if (method.getReference().equals(getMessageRef)) { - allClasses.connect(method.getVariable(0)); - } - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionPlugin.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionPlugin.java deleted file mode 100644 index 1edf66116..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestExceptionPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import org.teavm.vm.spi.TeaVMHost; -import org.teavm.vm.spi.TeaVMPlugin; - -/** - * - * @author Alexey Andreev - */ -public class TestExceptionPlugin implements TeaVMPlugin { - @Override - public void install(TeaVMHost host) { - host.add(new TestExceptionDependency()); - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java deleted file mode 100644 index c6e57d502..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author Alexey Andreev - */ -public class TestReport { - private List results = new ArrayList<>(); - - public List getResults() { - return results; - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java deleted file mode 100644 index 9728857e6..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.teavm.model.MethodReference; - -/** - * - * @author Alexey Andreev - */ -public class TestResult { - private MethodReference method; - private TestStatus status; - private String exception; - private String stack; - - @JsonCreator - TestResult( - @JsonProperty("method") MethodReference method, - @JsonProperty("status") TestStatus status, - @JsonInclude(Include.NON_NULL) @JsonProperty("exception") String exception, - @JsonInclude(Include.NON_NULL) @JsonProperty("stack") String stack) { - this.method = method; - this.status = status; - this.exception = exception; - this.stack = stack; - } - - public static TestResult passed(MethodReference method) { - return new TestResult(method, TestStatus.PASSED, null, null); - } - - public static TestResult exceptionNotThrown(MethodReference method) { - return new TestResult(method, TestStatus.EXCEPTION_NOT_THROWN, null, null); - } - - public static TestResult error(MethodReference method, String exception, String stack) { - return new TestResult(method, TestStatus.ERROR, exception, stack); - } - - @JsonGetter - public MethodReference getMethod() { - return method; - } - - @JsonGetter - public TestStatus getStatus() { - return status; - } - - @JsonGetter - public String getException() { - return exception; - } - - @JsonGetter - public String getStack() { - return stack; - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java deleted file mode 100644 index 8c1bcc745..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.maven; - -import java.io.IOException; -import org.apache.maven.plugin.logging.Log; -import org.teavm.tooling.testing.TestCase; - -/** - * - * @author Alexey Andreev - */ -public interface TestRunStrategy { - void beforeThread(); - - void afterThread(); - - String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException; -}