From d29339f0b8ee4d04d4e6763c93b24e68bad9956d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 9 Oct 2015 22:54:50 +0300 Subject: [PATCH] Test runner --- core/pom.xml | 5 ++ .../java/org/teavm/model/MethodReference.java | 4 ++ samples/pom.xml | 2 - tests/pom.xml | 10 ++++ tools/core/pom.xml | 12 +++++ .../eclipse/core-plugin/META-INF/MANIFEST.MF | 1 + tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF | 2 +- .../m2e-plugin/lifecycle-mapping-metadata.xml | 2 +- .../eclipse/m2e/TeaVMProjectConfigurator.java | 2 +- tools/eclipse/plugin/META-INF/MANIFEST.MF | 3 +- .../teavm/eclipse/TeaVMProjectBuilder.java | 52 ++++++++++++++++--- .../teavm/maven/BuildJavascriptTestMojo.java | 7 --- .../java/org/teavm/maven/RunTestsMojo.java | 50 +++++++++++++++--- .../org/teavm/maven/SeleniumTestRunner.java | 30 ++++++++--- .../main/java/org/teavm/maven/TestReport.java | 16 ++++++ .../main/java/org/teavm/maven/TestResult.java | 32 ++++++++++-- .../main/java/org/teavm/maven/TestStatus.java | 3 +- .../resources/archetype-resources/pom.xml | 18 ++----- 18 files changed, 196 insertions(+), 55 deletions(-) create mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java diff --git a/core/pom.xml b/core/pom.xml index 798a6b5a8..98b1d0192 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -45,6 +45,11 @@ hppc 0.6.1 + + com.fasterxml.jackson.core + jackson-annotations + true + TeaVM core diff --git a/core/src/main/java/org/teavm/model/MethodReference.java b/core/src/main/java/org/teavm/model/MethodReference.java index a60d8b4ea..d5ed4a70f 100644 --- a/core/src/main/java/org/teavm/model/MethodReference.java +++ b/core/src/main/java/org/teavm/model/MethodReference.java @@ -15,6 +15,8 @@ */ package org.teavm.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import java.util.Arrays; /** @@ -132,6 +134,7 @@ public class MethodReference { } @Override + @JsonValue public String toString() { if (reprCache == null) { reprCache = className + "." + name + signatureToString(); @@ -139,6 +142,7 @@ public class MethodReference { return reprCache; } + @JsonCreator public static MethodReference parse(String string) { MethodReference reference = parseIfPossible(string); if (reference == null) { diff --git a/samples/pom.xml b/samples/pom.xml index 60292acad..fea836574 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -36,8 +36,6 @@ video async kotlin - \ No newline at end of file diff --git a/tests/pom.xml b/tests/pom.xml index 28955d1bd..17b57350d 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -27,6 +27,12 @@ TeaVM tests Project containing TeaVM tests, as it is impossible to test each module separately + + false + 1 + + + org.teavm @@ -93,6 +99,10 @@ test + + ${teavm.test.threads} + ${teavm.test.selenium} + diff --git a/tools/core/pom.xml b/tools/core/pom.xml index 7abaacfc8..54c00a376 100644 --- a/tools/core/pom.xml +++ b/tools/core/pom.xml @@ -24,6 +24,7 @@ ../.. teavm-tooling + bundle TeaVM tooling core TeaVM API that helps to create tooling @@ -59,6 +60,17 @@ org.apache.maven.plugins maven-javadoc-plugin + + org.apache.felix + maven-bundle-plugin + true + + + org.teavm.* + teavm-tooling + + + \ No newline at end of file diff --git a/tools/eclipse/core-plugin/META-INF/MANIFEST.MF b/tools/eclipse/core-plugin/META-INF/MANIFEST.MF index a0133cc02..b4bee0aa9 100644 --- a/tools/eclipse/core-plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/core-plugin/META-INF/MANIFEST.MF @@ -71,5 +71,6 @@ Export-Package: org.teavm.cache, org.teavm.resource, org.teavm.testing, org.teavm.tooling, + org.teavm.tooling.sources, org.teavm.vm, org.teavm.vm.spi diff --git a/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF b/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF index 062e541a9..1574d6301 100644 --- a/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for m2e Bundle-SymbolicName: teavm-eclipse-m2e-plugin;singleton:=true Bundle-Version: 0.4.0.qualifier Bundle-Vendor: Alexey Andreev -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: teavm-eclipse-plugin;bundle-version="[0.4.0,0.5.0)", org.eclipse.m2e.core;bundle-version="[1.3,2)", org.eclipse.core.runtime;bundle-version="[3.8,4.0)", diff --git a/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml b/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml index 3ca52caff..e2fb85a6e 100644 --- a/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml +++ b/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml @@ -7,7 +7,7 @@ teavm-maven-plugin 0.4.0-SNAPSHOT - build-javascript + compile diff --git a/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java b/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java index 220d4ed0f..a8ad00c3a 100644 --- a/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java +++ b/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java @@ -33,7 +33,7 @@ public class TeaVMProjectConfigurator extends AbstractProjectConfigurator { private static final String TOOL_ID = "teavm-eclipse-m2e-plugin.tool"; private static final String TEAVM_ARTIFACT_ID = "teavm-maven-plugin"; private static final String TEAVM_GROUP_ID = "org.teavm"; - private static final String TEAVM_MAIN_GOAL = "build-javascript"; + private static final String TEAVM_MAIN_GOAL = "compile"; private int executionIdGenerator; private Set usedExecutionIds = new HashSet<>(); private IMaven maven; diff --git a/tools/eclipse/plugin/META-INF/MANIFEST.MF b/tools/eclipse/plugin/META-INF/MANIFEST.MF index 416ca1e23..4a219dbb4 100644 --- a/tools/eclipse/plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for Eclipse Bundle-SymbolicName: teavm-eclipse-plugin;singleton:=true Bundle-Version: 0.4.0.qualifier Bundle-Vendor: Alexey Andreev -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Activator: org.teavm.eclipse.TeaVMEclipsePlugin Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.8.0,4.0)", org.eclipse.debug.core;bundle-version="[3.7.0,4.0)", @@ -48,5 +48,6 @@ Import-Package: org.teavm.cache, org.teavm.resource, org.teavm.testing, org.teavm.tooling, + org.teavm.tooling.sources, org.teavm.vm, org.teavm.vm.spi diff --git a/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java b/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java index 41c2184b7..a09363e27 100644 --- a/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java +++ b/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java @@ -21,22 +21,62 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.WeakHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.core.resources.*; -import org.eclipse.core.runtime.*; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; -import org.eclipse.jdt.core.*; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.Signature; import org.teavm.callgraph.CallGraph; import org.teavm.callgraph.CallGraphNode; import org.teavm.callgraph.CallSite; import org.teavm.diagnostics.Problem; import org.teavm.diagnostics.ProblemTextConsumer; -import org.teavm.model.*; -import org.teavm.tooling.*; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.FieldReference; +import org.teavm.model.InstructionLocation; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.tooling.ClassAlias; +import org.teavm.tooling.RuntimeCopyOperation; +import org.teavm.tooling.TeaVMTool; +import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.sources.DirectorySourceFileProvider; +import org.teavm.tooling.sources.JarSourceFileProvider; +import org.teavm.tooling.sources.SourceFileProvider; /** * 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 index de0617ebd..5beb6c135 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.HashSet; import java.util.List; -import java.util.Properties; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -82,12 +81,6 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo { @Parameter private String[] additionalScripts; - @Parameter - private Properties properties; - - @Parameter - private boolean incremental; - private TeaVMTestTool tool = new TeaVMTestTool(); @Override 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 index f89a3d176..8bfd3857e 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java @@ -16,13 +16,17 @@ 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 java.util.List; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -40,15 +44,37 @@ 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 URL seleniumURL; + private String seleniumURL; + + @Parameter + private int numThreads = 1; @Override public void execute() throws MojoExecutionException, MojoFailureException { + if (seleniumURL == null || seleniumURL.isEmpty()) { + getLog().info("Tests build skipped as selenium URL was not specified"); + return; + } + + if (System.getProperty("maven.test.skip", "false").equals("true") || + System.getProperty("skipTests") != null) { + getLog().info("Tests build skipped as specified by system property"); + return; + } + SeleniumTestRunner runner = new SeleniumTestRunner(); runner.setLog(getLog()); runner.setDirectory(testDirectory); - runner.setUrl(seleniumURL); + runner.setNumThreads(numThreads); + try { + runner.setUrl(new URL(seleniumURL)); + } catch (MalformedURLException e) { + throw new MojoFailureException("Can't parse URL: " + seleniumURL, e); + } TestPlan plan; ObjectMapper mapper = new ObjectMapper(); @@ -63,23 +89,31 @@ public class RunTestsMojo extends AbstractMojo { processReport(runner.getReport()); } - private void processReport(List report) throws MojoExecutionException { - if (report.isEmpty()) { + 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) { + for (TestResult result : report.getResults()) { if (result.getStatus() != TestStatus.PASSED) { failedTests++; } } if (failedTests > 0) { - throw new MojoExecutionException(failedTests + " of " + report.size() + " test(s) failed"); + throw new MojoExecutionException(failedTests + " of " + report.getResults().size() + " test(s) failed"); } else { - getLog().info("All of " + report.size() + " tests successfully passed"); + getLog().info("All of " + report.getResults().size() + " tests successfully passed"); + } + + 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); } } } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumTestRunner.java b/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumTestRunner.java index 836cfd405..b90e7da2c 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumTestRunner.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumTestRunner.java @@ -34,7 +34,8 @@ 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.chrome.ChromeDriver; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.remote.RemoteWebDriver; import org.teavm.model.MethodReference; import org.teavm.tooling.testing.TestCase; import org.teavm.tooling.testing.TestGroup; @@ -99,7 +100,7 @@ public class SeleniumTestRunner { latch = new CountDownLatch(numThreads); for (int i = 0; i < numThreads; ++i) { new Thread(() -> { - ChromeDriver driver = new ChromeDriver(); + RemoteWebDriver driver = new RemoteWebDriver(DesiredCapabilities.chrome()); webDriver.set(driver); localReport.set(new ArrayList<>()); while (!seleniumStopped || !seleniumTaskQueue.isEmpty()) { @@ -157,13 +158,24 @@ public class SeleniumTestRunner { MethodReference ref = MethodReference.parse(testCase.getTestMethod()); switch (status) { case "ok": - log.info("Test passed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.passed(ref)); + 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)); + } break; case "exception": { String stack = resultObject.get("stack").asText(); - log.info("Test failed: " + testCase.getTestMethod()); - localReport.get().add(TestResult.error(ref, stack)); + 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)); + } break; } } @@ -194,7 +206,9 @@ public class SeleniumTestRunner { } } - public List getReport() { - return new ArrayList<>(report); + public TestReport getReport() { + TestReport report = new TestReport(); + report.getResults().addAll(this.report); + return report; } } 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 new file mode 100644 index 000000000..d8fab47b1 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java @@ -0,0 +1,16 @@ +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 index 1151f2c85..9728857e6 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java @@ -15,6 +15,11 @@ */ 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; /** @@ -24,30 +29,49 @@ import org.teavm.model.MethodReference; public class TestResult { private MethodReference method; private TestStatus status; + private String exception; private String stack; - private TestResult(MethodReference method, TestStatus status, 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); + return new TestResult(method, TestStatus.PASSED, null, null); } - public static TestResult error(MethodReference method, String stack) { - return new TestResult(method, TestStatus.ERROR, stack); + 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/TestStatus.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java index 8bdbaecd2..366859a58 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java @@ -21,5 +21,6 @@ package org.teavm.maven; */ public enum TestStatus { PASSED, - ERROR + ERROR, + EXCEPTION_NOT_THROWN } diff --git a/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml b/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml index 0a265a56c..5a2eebaa2 100644 --- a/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml +++ b/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml @@ -8,7 +8,7 @@ war - 1.7 + 1.8 0.4.0-SNAPSHOT UTF-8 @@ -25,15 +25,7 @@ org.teavm - teavm-jso - ${teavm.version} - provided - - - - - org.teavm - teavm-dom + teavm-jso-apis ${teavm.version} provided @@ -80,9 +72,8 @@ web-client - prepare-package - build-javascript + compile ${package}.Client - - SEPARATE - true