Test runner

This commit is contained in:
Alexey Andreev 2015-10-09 22:54:50 +03:00
parent d007b0c8ac
commit d29339f0b8
18 changed files with 196 additions and 55 deletions

View File

@ -45,6 +45,11 @@
<artifactId>hppc</artifactId> <artifactId>hppc</artifactId>
<version>0.6.1</version> <version>0.6.1</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
<name>TeaVM core</name> <name>TeaVM core</name>

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.model; package org.teavm.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Arrays; import java.util.Arrays;
/** /**
@ -132,6 +134,7 @@ public class MethodReference {
} }
@Override @Override
@JsonValue
public String toString() { public String toString() {
if (reprCache == null) { if (reprCache == null) {
reprCache = className + "." + name + signatureToString(); reprCache = className + "." + name + signatureToString();
@ -139,6 +142,7 @@ public class MethodReference {
return reprCache; return reprCache;
} }
@JsonCreator
public static MethodReference parse(String string) { public static MethodReference parse(String string) {
MethodReference reference = parseIfPossible(string); MethodReference reference = parseIfPossible(string);
if (reference == null) { if (reference == null) {

View File

@ -36,8 +36,6 @@
<module>video</module> <module>video</module>
<module>async</module> <module>async</module>
<module>kotlin</module> <module>kotlin</module>
<!--
<module>scala</module> <module>scala</module>
-->
</modules> </modules>
</project> </project>

View File

@ -27,6 +27,12 @@
<name>TeaVM tests</name> <name>TeaVM tests</name>
<description>Project containing TeaVM tests, as it is impossible to test each module separately</description> <description>Project containing TeaVM tests, as it is impossible to test each module separately</description>
<properties>
<teavm.test.incremental>false</teavm.test.incremental>
<teavm.test.threads>1</teavm.test.threads>
<teavm.test.selenium></teavm.test.selenium>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
@ -93,6 +99,10 @@
<goals> <goals>
<goal>test</goal> <goal>test</goal>
</goals> </goals>
<configuration>
<numThreads>${teavm.test.threads}</numThreads>
<seleniumURL>${teavm.test.selenium}</seleniumURL>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>

View File

@ -24,6 +24,7 @@
<relativePath>../..</relativePath> <relativePath>../..</relativePath>
</parent> </parent>
<artifactId>teavm-tooling</artifactId> <artifactId>teavm-tooling</artifactId>
<packaging>bundle</packaging>
<name>TeaVM tooling core</name> <name>TeaVM tooling core</name>
<description>TeaVM API that helps to create tooling</description> <description>TeaVM API that helps to create tooling</description>
@ -59,6 +60,17 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>org.teavm.*</Export-Package>
<Bundle-SymbolicName>teavm-tooling</Bundle-SymbolicName>
</instructions>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -71,5 +71,6 @@ Export-Package: org.teavm.cache,
org.teavm.resource, org.teavm.resource,
org.teavm.testing, org.teavm.testing,
org.teavm.tooling, org.teavm.tooling,
org.teavm.tooling.sources,
org.teavm.vm, org.teavm.vm,
org.teavm.vm.spi org.teavm.vm.spi

View File

@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for m2e
Bundle-SymbolicName: teavm-eclipse-m2e-plugin;singleton:=true Bundle-SymbolicName: teavm-eclipse-m2e-plugin;singleton:=true
Bundle-Version: 0.4.0.qualifier Bundle-Version: 0.4.0.qualifier
Bundle-Vendor: Alexey Andreev <konsoletyper@gmail.com> Bundle-Vendor: Alexey Andreev <konsoletyper@gmail.com>
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: teavm-eclipse-plugin;bundle-version="[0.4.0,0.5.0)", Require-Bundle: teavm-eclipse-plugin;bundle-version="[0.4.0,0.5.0)",
org.eclipse.m2e.core;bundle-version="[1.3,2)", org.eclipse.m2e.core;bundle-version="[1.3,2)",
org.eclipse.core.runtime;bundle-version="[3.8,4.0)", org.eclipse.core.runtime;bundle-version="[3.8,4.0)",

View File

@ -7,7 +7,7 @@
<artifactId>teavm-maven-plugin</artifactId> <artifactId>teavm-maven-plugin</artifactId>
<versionRange>0.4.0-SNAPSHOT</versionRange> <versionRange>0.4.0-SNAPSHOT</versionRange>
<goals> <goals>
<goal>build-javascript</goal> <goal>compile</goal>
</goals> </goals>
</pluginExecutionFilter> </pluginExecutionFilter>
<action> <action>

View File

@ -33,7 +33,7 @@ public class TeaVMProjectConfigurator extends AbstractProjectConfigurator {
private static final String TOOL_ID = "teavm-eclipse-m2e-plugin.tool"; 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_ARTIFACT_ID = "teavm-maven-plugin";
private static final String TEAVM_GROUP_ID = "org.teavm"; 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 int executionIdGenerator;
private Set<String> usedExecutionIds = new HashSet<>(); private Set<String> usedExecutionIds = new HashSet<>();
private IMaven maven; private IMaven maven;

View File

@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for Eclipse
Bundle-SymbolicName: teavm-eclipse-plugin;singleton:=true Bundle-SymbolicName: teavm-eclipse-plugin;singleton:=true
Bundle-Version: 0.4.0.qualifier Bundle-Version: 0.4.0.qualifier
Bundle-Vendor: Alexey Andreev <konsoletyper@gmail.com> Bundle-Vendor: Alexey Andreev <konsoletyper@gmail.com>
Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-Activator: org.teavm.eclipse.TeaVMEclipsePlugin Bundle-Activator: org.teavm.eclipse.TeaVMEclipsePlugin
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.8.0,4.0)", Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.8.0,4.0)",
org.eclipse.debug.core;bundle-version="[3.7.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.resource,
org.teavm.testing, org.teavm.testing,
org.teavm.tooling, org.teavm.tooling,
org.teavm.tooling.sources,
org.teavm.vm, org.teavm.vm,
org.teavm.vm.spi org.teavm.vm.spi

View File

@ -21,22 +21,62 @@ import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; 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.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.eclipse.core.resources.*; import org.eclipse.core.resources.IContainer;
import org.eclipse.core.runtime.*; 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.runtime.jobs.Job;
import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin; 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.CallGraph;
import org.teavm.callgraph.CallGraphNode; import org.teavm.callgraph.CallGraphNode;
import org.teavm.callgraph.CallSite; import org.teavm.callgraph.CallSite;
import org.teavm.diagnostics.Problem; import org.teavm.diagnostics.Problem;
import org.teavm.diagnostics.ProblemTextConsumer; import org.teavm.diagnostics.ProblemTextConsumer;
import org.teavm.model.*; import org.teavm.model.CallLocation;
import org.teavm.tooling.*; 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;
/** /**
* *

View File

@ -28,7 +28,6 @@ import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@ -82,12 +81,6 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo {
@Parameter @Parameter
private String[] additionalScripts; private String[] additionalScripts;
@Parameter
private Properties properties;
@Parameter
private boolean incremental;
private TeaVMTestTool tool = new TeaVMTestTool(); private TeaVMTestTool tool = new TeaVMTestTool();
@Override @Override

View File

@ -16,13 +16,17 @@
package org.teavm.maven; package org.teavm.maven;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader; import java.io.Reader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.List;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.MojoFailureException;
@ -40,15 +44,37 @@ public class RunTestsMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.directory}/javascript-test") @Parameter(defaultValue = "${project.build.directory}/javascript-test")
private File testDirectory; private File testDirectory;
@Parameter(defaultValue = "${project.build.directory}/teavm-test-report.json")
private File reportFile;
@Parameter @Parameter
private URL seleniumURL; private String seleniumURL;
@Parameter
private int numThreads = 1;
@Override @Override
public void execute() throws MojoExecutionException, MojoFailureException { 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(); SeleniumTestRunner runner = new SeleniumTestRunner();
runner.setLog(getLog()); runner.setLog(getLog());
runner.setDirectory(testDirectory); 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; TestPlan plan;
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
@ -63,23 +89,31 @@ public class RunTestsMojo extends AbstractMojo {
processReport(runner.getReport()); processReport(runner.getReport());
} }
private void processReport(List<TestResult> report) throws MojoExecutionException { private void processReport(TestReport report) throws MojoExecutionException, MojoFailureException {
if (report.isEmpty()) { if (report.getResults().isEmpty()) {
getLog().info("No tests ran"); getLog().info("No tests ran");
return; return;
} }
int failedTests = 0; int failedTests = 0;
for (TestResult result : report) { for (TestResult result : report.getResults()) {
if (result.getStatus() != TestStatus.PASSED) { if (result.getStatus() != TestStatus.PASSED) {
failedTests++; failedTests++;
} }
} }
if (failedTests > 0) { 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 { } 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);
} }
} }
} }

View File

@ -34,7 +34,8 @@ import org.apache.maven.plugin.logging.Log;
import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException; 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.model.MethodReference;
import org.teavm.tooling.testing.TestCase; import org.teavm.tooling.testing.TestCase;
import org.teavm.tooling.testing.TestGroup; import org.teavm.tooling.testing.TestGroup;
@ -99,7 +100,7 @@ public class SeleniumTestRunner {
latch = new CountDownLatch(numThreads); latch = new CountDownLatch(numThreads);
for (int i = 0; i < numThreads; ++i) { for (int i = 0; i < numThreads; ++i) {
new Thread(() -> { new Thread(() -> {
ChromeDriver driver = new ChromeDriver(); RemoteWebDriver driver = new RemoteWebDriver(DesiredCapabilities.chrome());
webDriver.set(driver); webDriver.set(driver);
localReport.set(new ArrayList<>()); localReport.set(new ArrayList<>());
while (!seleniumStopped || !seleniumTaskQueue.isEmpty()) { while (!seleniumStopped || !seleniumTaskQueue.isEmpty()) {
@ -157,13 +158,24 @@ public class SeleniumTestRunner {
MethodReference ref = MethodReference.parse(testCase.getTestMethod()); MethodReference ref = MethodReference.parse(testCase.getTestMethod());
switch (status) { switch (status) {
case "ok": case "ok":
log.info("Test passed: " + testCase.getTestMethod()); if (testCase.getExpectedExceptions().isEmpty()) {
localReport.get().add(TestResult.passed(ref)); 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; break;
case "exception": { case "exception": {
String stack = resultObject.get("stack").asText(); String stack = resultObject.get("stack").asText();
log.info("Test failed: " + testCase.getTestMethod()); String exception = resultObject.get("exception").asText();
localReport.get().add(TestResult.error(ref, stack)); 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; break;
} }
} }
@ -194,7 +206,9 @@ public class SeleniumTestRunner {
} }
} }
public List<TestResult> getReport() { public TestReport getReport() {
return new ArrayList<>(report); TestReport report = new TestReport();
report.getResults().addAll(this.report);
return report;
} }
} }

View File

@ -0,0 +1,16 @@
package org.teavm.maven;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public class TestReport {
private List<TestResult> results = new ArrayList<>();
public List<TestResult> getResults() {
return results;
}
}

View File

@ -15,6 +15,11 @@
*/ */
package org.teavm.maven; 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; import org.teavm.model.MethodReference;
/** /**
@ -24,30 +29,49 @@ import org.teavm.model.MethodReference;
public class TestResult { public class TestResult {
private MethodReference method; private MethodReference method;
private TestStatus status; private TestStatus status;
private String exception;
private String stack; 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.method = method;
this.status = status; this.status = status;
this.exception = exception;
this.stack = stack; this.stack = stack;
} }
public static TestResult passed(MethodReference method) { 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) { public static TestResult exceptionNotThrown(MethodReference method) {
return new TestResult(method, TestStatus.ERROR, stack); 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() { public MethodReference getMethod() {
return method; return method;
} }
@JsonGetter
public TestStatus getStatus() { public TestStatus getStatus() {
return status; return status;
} }
@JsonGetter
public String getException() {
return exception;
}
@JsonGetter
public String getStack() { public String getStack() {
return stack; return stack;
} }

View File

@ -21,5 +21,6 @@ package org.teavm.maven;
*/ */
public enum TestStatus { public enum TestStatus {
PASSED, PASSED,
ERROR ERROR,
EXCEPTION_NOT_THROWN
} }

View File

@ -8,7 +8,7 @@
<packaging>war</packaging> <packaging>war</packaging>
<properties> <properties>
<java.version>1.7</java.version> <java.version>1.8</java.version>
<teavm.version>0.4.0-SNAPSHOT</teavm.version> <teavm.version>0.4.0-SNAPSHOT</teavm.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
@ -25,15 +25,7 @@
<!-- JavaScriptObjects (JSO) - a JavaScript binding for TeaVM --> <!-- JavaScriptObjects (JSO) - a JavaScript binding for TeaVM -->
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-jso</artifactId> <artifactId>teavm-jso-apis</artifactId>
<version>${teavm.version}</version>
<scope>provided</scope>
</dependency>
<!-- Different browser APIs for TeaVM in terms of JSO -->
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<version>${teavm.version}</version> <version>${teavm.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -80,9 +72,8 @@
<executions> <executions>
<execution> <execution>
<id>web-client</id> <id>web-client</id>
<phase>prepare-package</phase>
<goals> <goals>
<goal>build-javascript</goal> <goal>compile</goal>
</goals> </goals>
<configuration> <configuration>
<!-- Directory where TeaVM should put generated files. This configuration conforms to the settings <!-- Directory where TeaVM should put generated files. This configuration conforms to the settings
@ -92,9 +83,6 @@
<!-- Main class, containing static void main(String[]) --> <!-- Main class, containing static void main(String[]) -->
<mainClass>${package}.Client</mainClass> <mainClass>${package}.Client</mainClass>
<!-- How to attach runtime.js. Possible values are: SEPARATE, MERGED and NONE -->
<runtime>SEPARATE</runtime>
<!-- Whether TeaVM should produce minified JavaScript. Can reduce JavaScript file size more than <!-- Whether TeaVM should produce minified JavaScript. Can reduce JavaScript file size more than
two times --> two times -->
<minifying>true</minifying> <minifying>true</minifying>