TeaVM test runner as a separate mojo

This commit is contained in:
Alexey Andreev 2015-10-09 18:30:31 +03:00
parent 5522f55c68
commit d007b0c8ac
7 changed files with 99 additions and 48 deletions

View File

@ -86,9 +86,14 @@
<java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available>
</properties>
<incremental>${teavm.test.incremental}</incremental>
<seleniumURL>http://127.0.0.1:4444/wd/hub</seleniumURL>
</configuration>
</execution>
<execution>
<id>run-javascript-tests</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>

View File

@ -364,7 +364,8 @@ public class TeaVMTestTool implements BaseTeaVMTool {
testClass.getMethods().add(testMethod);
String debugTable = debugInformationGenerated ? testMethod.getFileName() + ".teavmdbg" : null;
cases.add(new TestCase(ref, testMethod.getFileName(), debugTable, testMethod.getExpectedExceptions()));
cases.add(new TestCase(ref.toString(), testMethod.getFileName(), debugTable,
testMethod.getExpectedExceptions()));
++testCount;
}
}

View File

@ -21,21 +21,20 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class TestCase {
private MethodReference testMethod;
private String testMethod;
private String testScript;
private String debugTable;
private List<String> expectedExceptions = new ArrayList<>();
@JsonCreator
public TestCase(
@JsonProperty("testMethod") MethodReference testMethod,
@JsonProperty("testMethod") String testMethod,
@JsonProperty("script") String testScript,
@JsonProperty("debugTable") String debugTable,
@JsonProperty("expectedExceptions") List<String> expectedExceptions) {
@ -46,7 +45,7 @@ public class TestCase {
}
@JsonGetter
public MethodReference getTestMethod() {
public String getTestMethod() {
return testMethod;
}

View File

@ -16,6 +16,7 @@
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;
@ -32,15 +33,17 @@ public class TestPlan {
@JsonCreator
public TestPlan(
@JsonProperty("runtimeScript") String runtimeScript,
@JsonProperty("groupList") List<TestGroup> groups) {
@JsonProperty("groups") List<TestGroup> groups) {
this.runtimeScript = runtimeScript;
this.groups = Collections.unmodifiableList(new ArrayList<>(groups));
}
@JsonGetter
public String getRuntimeScript() {
return runtimeScript;
}
@JsonGetter
public List<TestGroup> getGroups() {
return groups;
}

View File

@ -15,12 +15,15 @@
*/
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.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
@ -42,6 +45,7 @@ import org.teavm.testing.JUnitTestAdapter;
import org.teavm.testing.TestAdapter;
import org.teavm.tooling.TeaVMToolException;
import org.teavm.tooling.testing.TeaVMTestTool;
import org.teavm.tooling.testing.TestPlan;
/**
*
@ -84,11 +88,6 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo {
@Parameter
private boolean incremental;
@Parameter
private URL seleniumURL;
private SeleniumTestRunner seleniumRunner;
private TeaVMTestTool tool = new TeaVMTestTool();
@Override
@ -111,29 +110,19 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo {
if (additionalScripts != null) {
tool.getAdditionalScripts().addAll(Arrays.asList(additionalScripts));
}
tool.generate();
writePlan(tool.generate());
} catch (TeaVMToolException e) {
throw new MojoFailureException("Error occured generating JavaScript files", e);
}
}
private void processReport(List<TestResult> report) throws MojoExecutionException {
if (report.isEmpty()) {
getLog().info("No tests ran");
return;
}
int failedTests = 0;
for (TestResult result : report) {
if (result.getStatus() != TestStatus.PASSED) {
failedTests++;
}
}
if (failedTests > 0) {
throw new MojoExecutionException(failedTests + " of " + report.size() + " test(s) failed");
} else {
getLog().info("All of " + report.size() + " tests successfully passed");
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);
}
}

View File

@ -15,17 +15,71 @@
*/
package org.teavm.maven;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
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;
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 = "run-tests", defaultPhase = LifecyclePhase.TEST)
public class RunTestsMojo {
@Parameter(defaultValue = "${project.build.directory}/javascript-tests")
private File targetDirectory;
@Mojo(name = "test", defaultPhase = LifecyclePhase.TEST)
public class RunTestsMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.directory}/javascript-test")
private File testDirectory;
@Parameter
private URL seleniumURL;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
SeleniumTestRunner runner = new SeleniumTestRunner();
runner.setLog(getLog());
runner.setDirectory(testDirectory);
runner.setUrl(seleniumURL);
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 void processReport(List<TestResult> report) throws MojoExecutionException {
if (report.isEmpty()) {
getLog().info("No tests ran");
return;
}
int failedTests = 0;
for (TestResult result : report) {
if (result.getStatus() != TestStatus.PASSED) {
failedTests++;
}
}
if (failedTests > 0) {
throw new MojoExecutionException(failedTests + " of " + report.size() + " test(s) failed");
} else {
getLog().info("All of " + report.size() + " tests successfully passed");
}
}
}

View File

@ -35,6 +35,7 @@ import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.chrome.ChromeDriver;
import org.teavm.model.MethodReference;
import org.teavm.tooling.testing.TestCase;
import org.teavm.tooling.testing.TestGroup;
import org.teavm.tooling.testing.TestPlan;
@ -48,7 +49,7 @@ public class SeleniumTestRunner {
private int numThreads = 1;
private ThreadLocal<WebDriver> webDriver = new ThreadLocal<>();
private BlockingQueue<Runnable> seleniumTaskQueue = new LinkedBlockingQueue<>();
private CountDownLatch latch = new CountDownLatch(1);
private CountDownLatch latch;
private volatile boolean seleniumStopped = false;
private Log log;
private List<TestResult> report = new CopyOnWriteArrayList<>();
@ -95,15 +96,16 @@ public class SeleniumTestRunner {
}
private void initSelenium() {
latch = new CountDownLatch(numThreads);
for (int i = 0; i < numThreads; ++i) {
new Thread(() -> {
ChromeDriver driver = new ChromeDriver();
webDriver.set(driver);
localReport.set(new ArrayList<>());
while (!seleniumStopped) {
while (!seleniumStopped || !seleniumTaskQueue.isEmpty()) {
Runnable task;
try {
task = seleniumTaskQueue.poll(1, TimeUnit.SECONDS);
task = seleniumTaskQueue.poll(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
break;
}
@ -113,22 +115,19 @@ public class SeleniumTestRunner {
}
report.addAll(localReport.get());
localReport.remove();
webDriver.get().close();
webDriver.remove();
latch.countDown();
}).start();
}
}
private void addSeleniumTask(Runnable runnable) {
if (url != null) {
seleniumTaskQueue.add(runnable);
}
seleniumTaskQueue.add(runnable);
}
private void stopSelenium() {
addSeleniumTask(() -> {
seleniumStopped = true;
latch.countDown();
});
seleniumStopped = true;
}
private void waitForCompletion() {
@ -145,7 +144,7 @@ public class SeleniumTestRunner {
private void runImpl(String runtimeScript, TestCase testCase) {
webDriver.get().manage().timeouts().setScriptTimeout(2, TimeUnit.SECONDS);
JavascriptExecutor js = (JavascriptExecutor) webDriver;
JavascriptExecutor js = (JavascriptExecutor) webDriver.get();
try {
String result = (String) js.executeAsyncScript(
readResource("teavm-selenium.js"),
@ -155,15 +154,16 @@ public class SeleniumTestRunner {
ObjectMapper mapper = new ObjectMapper();
ObjectNode resultObject = (ObjectNode) mapper.readTree(result);
String status = resultObject.get("status").asText();
MethodReference ref = MethodReference.parse(testCase.getTestMethod());
switch (status) {
case "ok":
log.info("Test passed: " + testCase.getTestMethod());
localReport.get().add(TestResult.passed(testCase.getTestMethod()));
localReport.get().add(TestResult.passed(ref));
break;
case "exception": {
String stack = resultObject.get("stack").asText();
log.info("Test failed: " + testCase.getTestMethod());
localReport.get().add(TestResult.error(testCase.getTestMethod(), stack));
localReport.get().add(TestResult.error(ref, stack));
break;
}
}