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>
<version>0.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<name>TeaVM core</name>

View File

@ -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) {

View File

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

View File

@ -27,6 +27,12 @@
<name>TeaVM tests</name>
<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>
<dependency>
<groupId>org.teavm</groupId>
@ -93,6 +99,10 @@
<goals>
<goal>test</goal>
</goals>
<configuration>
<numThreads>${teavm.test.threads}</numThreads>
<seleniumURL>${teavm.test.selenium}</seleniumURL>
</configuration>
</execution>
</executions>
</plugin>

View File

@ -24,6 +24,7 @@
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-tooling</artifactId>
<packaging>bundle</packaging>
<name>TeaVM tooling core</name>
<description>TeaVM API that helps to create tooling</description>
@ -59,6 +60,17 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</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>
</build>
</project>

View File

@ -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

View File

@ -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 <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)",
org.eclipse.m2e.core;bundle-version="[1.3,2)",
org.eclipse.core.runtime;bundle-version="[3.8,4.0)",

View File

@ -7,7 +7,7 @@
<artifactId>teavm-maven-plugin</artifactId>
<versionRange>0.4.0-SNAPSHOT</versionRange>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
</pluginExecutionFilter>
<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 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<String> usedExecutionIds = new HashSet<>();
private IMaven maven;

View File

@ -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 <konsoletyper@gmail.com>
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

View File

@ -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;
/**
*

View File

@ -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

View File

@ -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<TestResult> 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);
}
}
}

View File

@ -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<TestResult> getReport() {
return new ArrayList<>(report);
public TestReport getReport() {
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;
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;
}

View File

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

View File

@ -8,7 +8,7 @@
<packaging>war</packaging>
<properties>
<java.version>1.7</java.version>
<java.version>1.8</java.version>
<teavm.version>0.4.0-SNAPSHOT</teavm.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@ -25,15 +25,7 @@
<!-- JavaScriptObjects (JSO) - a JavaScript binding for TeaVM -->
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso</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>
<artifactId>teavm-jso-apis</artifactId>
<version>${teavm.version}</version>
<scope>provided</scope>
</dependency>
@ -80,9 +72,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<!-- Directory where TeaVM should put generated files. This configuration conforms to the settings
@ -92,9 +83,6 @@
<!-- Main class, containing static void main(String[]) -->
<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
two times -->
<minifying>true</minifying>