mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Improving JUnit test runner
This commit is contained in:
parent
d103306c3e
commit
c808f41a8f
|
@ -30,5 +30,46 @@
|
||||||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
||||||
<orderEntry type="module" module-name="teavm-tooling" scope="TEST" />
|
<orderEntry type="module" module-name="teavm-tooling" scope="TEST" />
|
||||||
<orderEntry type="module" module-name="teavm-junit" scope="TEST" />
|
<orderEntry type="module" module-name="teavm-junit" scope="TEST" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-java:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-chrome-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-firefox-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-ie-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-safari-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-support:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.webbitserver:webbit:0.4.14" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: io.netty:netty:3.5.2.Final" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-leg-rc:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-remote-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: cglib:cglib-nodep:2.1_3" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.seleniumhq.selenium:selenium-api:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.apache.httpcomponents:httpclient:4.4.1" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.apache.httpcomponents:httpcore:4.4.1" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: com.google.guava:guava:18.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.apache.commons:commons-exec:1.3" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: net.java.dev.jna:jna:4.1.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: net.java.dev.jna:jna-platform:4.1.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.6.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.6.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: com.fasterxml.jackson.core:jackson-core:2.6.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: net.sourceforge.htmlunit:htmlunit:2.18" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: xalan:xalan:2.7.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: xalan:serializer:2.7.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.apache.commons:commons-lang3:3.4" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.apache.httpcomponents:httpmime:4.5" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: commons-codec:commons-codec:1.10" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: net.sourceforge.htmlunit:htmlunit-core-js:2.17" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: xerces:xercesImpl:2.11.0" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: xml-apis:xml-apis:1.4.01" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: net.sourceforge.nekohtml:nekohtml:1.9.22" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: net.sourceforge.cssparser:cssparser:0.9.16" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.w3c.css:sac:1.3" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: commons-logging:commons-logging:1.2" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jetty:jetty-util:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jetty:jetty-io:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.2.12.v20150709" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<ClassHolderTransformer> transformers = new ArrayList<>();
|
|
||||||
private List<String> additionalScripts = new ArrayList<>();
|
|
||||||
private List<String> additionalScriptLocalPaths = new ArrayList<>();
|
|
||||||
private Properties properties = new Properties();
|
|
||||||
private List<String> 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<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
|
||||||
private MethodNodeCache astCache;
|
|
||||||
private ProgramCache programCache;
|
|
||||||
private SourceFilesCopier sourceFilesCopier;
|
|
||||||
private List<TestClassBuilder> 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<ClassHolderTransformer> getTransformers() {
|
|
||||||
return transformers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getAdditionalScripts() {
|
|
||||||
return additionalScripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Properties getProperties() {
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> 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<TestGroup> 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<TestCase> 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<String> 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('\"');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> expectedExceptions = new ArrayList<>();
|
|
||||||
private String runner;
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
public TestCase(
|
|
||||||
@JsonProperty("testMethod") String testMethod,
|
|
||||||
@JsonProperty("script") String testScript,
|
|
||||||
@JsonProperty("debugTable") String debugTable,
|
|
||||||
@JsonProperty("expectedExceptions") List<String> 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<String> getExpectedExceptions() {
|
|
||||||
return expectedExceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonGetter
|
|
||||||
public String getRunner() {
|
|
||||||
return runner;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<TestMethodBuilder> methods = new ArrayList<>();
|
|
||||||
|
|
||||||
public TestClassBuilder(String className) {
|
|
||||||
this.className = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TestMethodBuilder> getMethods() {
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<TestCase> testCases;
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
public TestGroup(@JsonProperty("className") String className,
|
|
||||||
@JsonProperty("testCases") List<TestCase> testCases) {
|
|
||||||
this.className = className;
|
|
||||||
this.testCases = Collections.unmodifiableList(new ArrayList<>(testCases));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonGetter("className")
|
|
||||||
public String getClassName() {
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonGetter("testCases")
|
|
||||||
public List<TestCase> getTestCases() {
|
|
||||||
return testCases;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String> expectedExceptions = new ArrayList<>();
|
|
||||||
private String runner;
|
|
||||||
|
|
||||||
public TestMethodBuilder(MethodReference method, String fileName, List<String> 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<String> getExpectedExceptions() {
|
|
||||||
return expectedExceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRunner() {
|
|
||||||
return runner;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<TestGroup> groups = new ArrayList<>();
|
|
||||||
|
|
||||||
@JsonCreator
|
|
||||||
public TestPlan(
|
|
||||||
@JsonProperty("runtimeScript") String runtimeScript,
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,6 +38,23 @@
|
||||||
<artifactId>teavm-tooling</artifactId>
|
<artifactId>teavm-tooling</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.seleniumhq.selenium</groupId>
|
||||||
|
<artifactId>selenium-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.seleniumhq.selenium</groupId>
|
||||||
|
<artifactId>selenium-remote-driver</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sourceforge.htmlunit</groupId>
|
||||||
|
<artifactId>htmlunit</artifactId>
|
||||||
|
<version>2.18</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2016 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.maven;
|
package org.teavm.junit;
|
||||||
|
|
||||||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||||
import com.gargoylesoftware.htmlunit.WebClient;
|
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.Function;
|
||||||
import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject;
|
import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject;
|
||||||
import org.apache.commons.io.IOUtils;
|
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 {
|
public class HtmlUnitRunStrategy implements TestRunStrategy {
|
||||||
private File directory;
|
|
||||||
|
|
||||||
public HtmlUnitRunStrategy(File directory) {
|
|
||||||
this.directory = directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeThread() {
|
public void beforeThread() {
|
||||||
}
|
}
|
||||||
|
@ -51,10 +39,10 @@ public class HtmlUnitRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)) {
|
try (WebClient webClient = new WebClient(BrowserVersion.CHROME)) {
|
||||||
HtmlPage page = webClient.getPage("about:blank");
|
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();
|
AsyncResult asyncResult = new AsyncResult();
|
||||||
Function function = (Function) page.executeJavaScript(readResource("teavm-htmlunit-adapter.js"))
|
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) };
|
Object[] args = new Object[] { new NativeJavaObject(function, asyncResult, AsyncResult.class) };
|
||||||
page.executeJavaScriptFunctionIfPossible(function, function, args, page);
|
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();
|
page.cleanUp();
|
||||||
for (WebWindow window : webClient.getWebWindows()) {
|
for (WebWindow window : webClient.getWebWindows()) {
|
||||||
window.getJobManager().removeAllJobs();
|
window.getJobManager().removeAllJobs();
|
||||||
|
@ -78,7 +66,7 @@ public class HtmlUnitRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String readResource(String resourceName) throws IOException {
|
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) {
|
if (input == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2016 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.maven;
|
package org.teavm.junit;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -23,27 +23,18 @@ import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
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.remote.DesiredCapabilities;
|
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
import org.openqa.selenium.remote.RemoteWebDriver;
|
||||||
import org.teavm.tooling.testing.TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public class SeleniumRunStrategy implements TestRunStrategy {
|
public class SeleniumRunStrategy implements TestRunStrategy {
|
||||||
private URL url;
|
private URL url;
|
||||||
private File directory;
|
|
||||||
private ThreadLocal<WebDriver> webDriver = new ThreadLocal<>();
|
private ThreadLocal<WebDriver> webDriver = new ThreadLocal<>();
|
||||||
private ThreadLocal<Integer> commandsSent = new ThreadLocal<>();
|
private ThreadLocal<Integer> commandsSent = new ThreadLocal<>();
|
||||||
|
|
||||||
public SeleniumRunStrategy(URL url, File directory) {
|
public SeleniumRunStrategy(URL url) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.directory = directory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,7 +52,7 @@ public class SeleniumRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException {
|
public String runTest(TestRun run) throws IOException {
|
||||||
commandsSent.set(commandsSent.get() + 1);
|
commandsSent.set(commandsSent.get() + 1);
|
||||||
if (commandsSent.get().equals(20)) {
|
if (commandsSent.get().equals(20)) {
|
||||||
commandsSent.set(0);
|
commandsSent.set(0);
|
||||||
|
@ -75,16 +66,16 @@ public class SeleniumRunStrategy implements TestRunStrategy {
|
||||||
try {
|
try {
|
||||||
return (String) js.executeAsyncScript(
|
return (String) js.executeAsyncScript(
|
||||||
readResource("teavm-selenium.js"),
|
readResource("teavm-selenium.js"),
|
||||||
readFile(new File(directory, runtimeScript)),
|
readFile(new File(run.getBaseDirectory(), "runtime.js")),
|
||||||
readFile(new File(directory, testCase.getTestScript())),
|
readFile(new File(run.getBaseDirectory(), "test.js")),
|
||||||
readResource("teavm-selenium-adapter.js"));
|
readResource("teavm-selenium-adapter.js"));
|
||||||
} catch (WebDriverException e) {
|
} catch (Throwable e) {
|
||||||
log.error("Error occured running test " + testCase.getTestMethod(), e);
|
run.getCallback().error(e);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<Object> errors = (List<Object>) js.executeScript("return window.jsErrors;");
|
List<Object> errors = (List<Object>) js.executeScript("return window.jsErrors;");
|
||||||
if (errors != null) {
|
if (errors != null) {
|
||||||
for (Object error : errors) {
|
for (Object error : errors) {
|
||||||
log.error(" -- additional error: " + error);
|
run.getCallback().error(new AssertionError(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -98,7 +89,7 @@ public class SeleniumRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String readResource(String resourceName) throws IOException {
|
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) {
|
if (input == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
|
@ -24,6 +24,8 @@ import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -32,12 +34,16 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
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 java.util.stream.Stream;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
|
import org.junit.runner.Runner;
|
||||||
import org.junit.runner.notification.Failure;
|
import org.junit.runner.notification.Failure;
|
||||||
import org.junit.runner.notification.RunNotifier;
|
import org.junit.runner.notification.RunNotifier;
|
||||||
import org.junit.runners.ParentRunner;
|
|
||||||
import org.junit.runners.model.InitializationError;
|
import org.junit.runners.model.InitializationError;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||||
|
@ -53,23 +59,32 @@ import org.teavm.parsing.ClasspathClassHolderSource;
|
||||||
import org.teavm.testing.JUnitTestAdapter;
|
import org.teavm.testing.JUnitTestAdapter;
|
||||||
import org.teavm.testing.TestAdapter;
|
import org.teavm.testing.TestAdapter;
|
||||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
import org.teavm.tooling.testing.TeaVMTestTool;
|
|
||||||
import org.teavm.vm.DirectoryBuildTarget;
|
import org.teavm.vm.DirectoryBuildTarget;
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
import org.teavm.vm.TeaVMBuilder;
|
import org.teavm.vm.TeaVMBuilder;
|
||||||
|
|
||||||
public class TeaVMTestRunner extends ParentRunner<Method> {
|
public class TeaVMTestRunner extends Runner {
|
||||||
private static final String PATH_PARAM = "teavm.junit.target";
|
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 ClassHolder classHolder;
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
private ClassHolderSource classSource;
|
private ClassHolderSource classSource;
|
||||||
|
private Description suiteDescription;
|
||||||
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
|
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
|
||||||
private File outputDir;
|
private File outputDir;
|
||||||
private TestAdapter testAdapter = new JUnitTestAdapter();
|
private TestAdapter testAdapter = new JUnitTestAdapter();
|
||||||
private Map<Method, Description> descriptions = new HashMap<>();
|
private Map<Method, Description> 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 {
|
public TeaVMTestRunner(Class<?> testClass) throws InitializationError {
|
||||||
super(testClass);
|
this.testClass = testClass;
|
||||||
classLoader = TeaVMTestRunner.class.getClassLoader();
|
classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||||
classSource = getClassSource(classLoader);
|
classSource = getClassSource(classLoader);
|
||||||
classHolder = classSource.get(testClass.getName());
|
classHolder = classSource.get(testClass.getName());
|
||||||
|
@ -77,12 +92,62 @@ public class TeaVMTestRunner extends ParentRunner<Method> {
|
||||||
if (outputPath != null) {
|
if (outputPath != null) {
|
||||||
outputDir = new File(outputPath);
|
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
|
@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<Method> 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<Method> getChildren() {
|
protected List<Method> getChildren() {
|
||||||
List<Method> children = new ArrayList<>();
|
List<Method> children = new ArrayList<>();
|
||||||
for (Method method : getTestClass().getJavaClass().getDeclaredMethods()) {
|
for (Method method : testClass.getDeclaredMethods()) {
|
||||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||||
if (testAdapter.acceptMethod(methodHolder)) {
|
if (testAdapter.acceptMethod(methodHolder)) {
|
||||||
children.add(method);
|
children.add(method);
|
||||||
|
@ -91,33 +156,32 @@ public class TeaVMTestRunner extends ParentRunner<Method> {
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Description describeChild(Method child) {
|
protected Description describeChild(Method child) {
|
||||||
return descriptions.computeIfAbsent(child, method -> Description.createTestDescription(
|
return descriptions.computeIfAbsent(child, method -> Description.createTestDescription(testClass,
|
||||||
getTestClass().getJavaClass(), method.getName()));
|
method.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void runChild(Method child, RunNotifier notifier) {
|
protected void runChild(Method child, RunNotifier notifier) {
|
||||||
notifier.fireTestStarted(describeChild(child));
|
notifier.fireTestStarted(describeChild(child));
|
||||||
|
|
||||||
boolean run = false;
|
boolean run = false;
|
||||||
boolean success = true;
|
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)) {
|
&& !child.getDeclaringClass().isAnnotationPresent(SkipJVM.class)) {
|
||||||
run = true;
|
run = true;
|
||||||
success &= runInJvm(child, notifier);
|
success = runInJvm(child, notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!run) {
|
if (success && outputDir != null) {
|
||||||
notifier.fireTestIgnored(describeChild(child));
|
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) {
|
private boolean runInJvm(Method child, RunNotifier notifier) {
|
||||||
|
@ -134,7 +198,7 @@ public class TeaVMTestRunner extends ParentRunner<Method> {
|
||||||
|
|
||||||
Object instance;
|
Object instance;
|
||||||
try {
|
try {
|
||||||
instance = getTestClass().getJavaClass().newInstance();
|
instance = testClass.newInstance();
|
||||||
} catch (InstantiationException | IllegalAccessException e) {
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||||
return false;
|
return false;
|
||||||
|
@ -184,9 +248,55 @@ public class TeaVMTestRunner extends ParentRunner<Method> {
|
||||||
return false;
|
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;
|
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 {
|
private CompileResult compileTest(Method method) throws IOException {
|
||||||
CompileResult result = new CompileResult();
|
CompileResult result = new CompileResult();
|
||||||
|
|
||||||
|
@ -254,7 +364,7 @@ public class TeaVMTestRunner extends ParentRunner<Method> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resourceToFile(String resource, File fileName) throws IOException {
|
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)) {
|
OutputStream output = new FileOutputStream(fileName)) {
|
||||||
IOUtils.copy(input, output);
|
IOUtils.copy(input, output);
|
||||||
}
|
}
|
||||||
|
|
58
tools/junit/src/main/java/org/teavm/junit/TestRun.java
Normal file
58
tools/junit/src/main/java/org/teavm/junit/TestRun.java
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2016 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.maven;
|
package org.teavm.junit;
|
||||||
|
|
||||||
/**
|
public interface TestRunCallback {
|
||||||
*
|
void complete();
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
void error(Throwable e);
|
||||||
public enum TestStatus {
|
|
||||||
PASSED,
|
|
||||||
ERROR,
|
|
||||||
EXCEPTION_NOT_THROWN
|
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2016 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.tooling.testing;
|
package org.teavm.junit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
public interface TestRunStrategy {
|
||||||
*
|
void beforeThread();
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
void afterThread();
|
||||||
public interface TeaVMTestToolListener {
|
|
||||||
void testGenerated(TestCase testCase);
|
String runTest(TestRun run) throws IOException;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2016 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.maven;
|
package org.teavm.junit;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
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.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 {
|
public class TestRunner {
|
||||||
private int numThreads = 1;
|
private int numThreads = 1;
|
||||||
private TestRunStrategy strategy;
|
private TestRunStrategy strategy;
|
||||||
private BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
|
private BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
|
||||||
private CountDownLatch latch;
|
private CountDownLatch latch;
|
||||||
private volatile boolean stopped;
|
private volatile boolean stopped;
|
||||||
private Log log;
|
|
||||||
private List<TestResult> report = new CopyOnWriteArrayList<>();
|
|
||||||
private ThreadLocal<List<TestResult>> localReport = new ThreadLocal<>();
|
|
||||||
|
|
||||||
public TestRunner(TestRunStrategy strategy) {
|
public TestRunner(TestRunStrategy strategy) {
|
||||||
this.strategy = strategy;
|
this.strategy = strategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLog(Log log) {
|
|
||||||
this.log = log;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumThreads() {
|
public int getNumThreads() {
|
||||||
return numThreads;
|
return numThreads;
|
||||||
}
|
}
|
||||||
|
@ -61,23 +45,11 @@ public class TestRunner {
|
||||||
this.numThreads = numThreads;
|
this.numThreads = numThreads;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(TestPlan testPlan) {
|
public void init() {
|
||||||
init();
|
|
||||||
for (TestGroup group : testPlan.getGroups()) {
|
|
||||||
for (TestCase testCase : group.getTestCases()) {
|
|
||||||
run(testPlan.getRuntimeScript(), testCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stop();
|
|
||||||
waitForCompletion();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
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(() -> {
|
||||||
strategy.beforeThread();
|
strategy.beforeThread();
|
||||||
localReport.set(new ArrayList<>());
|
|
||||||
while (!stopped || !taskQueue.isEmpty()) {
|
while (!stopped || !taskQueue.isEmpty()) {
|
||||||
Runnable task;
|
Runnable task;
|
||||||
try {
|
try {
|
||||||
|
@ -89,8 +61,6 @@ public class TestRunner {
|
||||||
task.run();
|
task.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
report.addAll(localReport.get());
|
|
||||||
localReport.remove();
|
|
||||||
strategy.afterThread();
|
strategy.afterThread();
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}).start();
|
}).start();
|
||||||
|
@ -101,29 +71,21 @@ public class TestRunner {
|
||||||
taskQueue.add(runnable);
|
taskQueue.add(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stop() {
|
public void stop() {
|
||||||
stopped = true;
|
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 {
|
try {
|
||||||
latch.await();
|
String result = strategy.runTest(run);
|
||||||
} 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);
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
log.info("Test failed: " + testCase.getTestMethod());
|
run.getCallback().complete();
|
||||||
localReport.get().add(TestResult.error(ref, null, null));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
@ -131,35 +93,19 @@ public class TestRunner {
|
||||||
String status = resultObject.get("status").asText();
|
String status = resultObject.get("status").asText();
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "ok":
|
case "ok":
|
||||||
if (testCase.getExpectedExceptions().isEmpty()) {
|
run.getCallback().complete();
|
||||||
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();
|
||||||
String exception = resultObject.get("exception").asText();
|
String exception = resultObject.get("exception").asText();
|
||||||
if (!testCase.getExpectedExceptions().contains(exception)) {
|
run.getCallback().error(new AssertionError(exception + "\n" + exception));
|
||||||
log.info("Test failed: " + testCase.getTestMethod());
|
run.getCallback().complete();
|
||||||
localReport.get().add(TestResult.error(ref, exception, stack));
|
|
||||||
} else {
|
|
||||||
log.info("Test passed: " + testCase.getTestMethod());
|
|
||||||
localReport.get().add(TestResult.passed(ref));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,37 +1,50 @@
|
||||||
var JUnitClient = {};
|
/*
|
||||||
JUnitClient.run = function() {
|
* Copyright 2016 Alexey Andreev.
|
||||||
$rt_startThread(function() {
|
*
|
||||||
var thread = $rt_nativeThread();
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
var instance;
|
* you may not use this file except in compliance with the License.
|
||||||
var ptr = 0;
|
* You may obtain a copy of the License at
|
||||||
var message;
|
*
|
||||||
if (thread.isResuming()) {
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
ptr = thread.pop();
|
*
|
||||||
instance = thread.pop();
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
}
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
loop: while (true) { switch (ptr) {
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
case 0:
|
* See the License for the specific language governing permissions and
|
||||||
try {
|
* limitations under the License.
|
||||||
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), "*");
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
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";
|
message.status = "exception";
|
||||||
var stack = e.stack;
|
var stack = e.stack;
|
||||||
if (e.$javaException && e.$javaException.constructor.$meta) {
|
if (e.$javaException && e.$javaException.constructor.$meta) {
|
||||||
|
|
|
@ -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 runtimeSource = arguments[0];
|
||||||
var testSource = arguments[1];
|
var testSource = arguments[1];
|
||||||
var adapterSource = arguments[2];
|
var adapterSource = arguments[2];
|
||||||
|
@ -11,7 +27,7 @@ window.jsErrors = [];
|
||||||
window.onerror = reportError;
|
window.onerror = reportError;
|
||||||
iframe.contentWindow.onerror = reportError;
|
iframe.contentWindow.onerror = reportError;
|
||||||
|
|
||||||
loadScripts([ runtimeSource, adapterSource, testSource ]);
|
loadScripts([ runtimeSource, testSource, adapterSource ]);
|
||||||
window.addEventListener("message", handleMessage);
|
window.addEventListener("message", handleMessage);
|
||||||
|
|
||||||
function handleMessage(event) {
|
function handleMessage(event) {
|
||||||
|
@ -20,16 +36,12 @@ function handleMessage(event) {
|
||||||
seleniumCallback(event.data)
|
seleniumCallback(event.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadScript(script, callback) {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadScripts(scripts) {
|
function loadScripts(scripts) {
|
||||||
for (var i = 0; i < scripts.length; ++i) {
|
for (var i = 0; i < scripts.length; ++i) {
|
||||||
var elem = doc.createElement("script");
|
var elem = doc.createElement("script");
|
||||||
elem.type = "text/javascript";
|
elem.type = "text/javascript";
|
||||||
doc.head.appendChild(elem);
|
doc.head.appendChild(elem);
|
||||||
elem.text = scripts[i]
|
elem.text = scripts[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function reportError(error, url, line) {
|
function reportError(error, url, line) {
|
||||||
|
|
|
@ -16,8 +16,50 @@
|
||||||
<orderEntry type="module" module-name="teavm-tooling" />
|
<orderEntry type="module" module-name="teavm-tooling" />
|
||||||
<orderEntry type="module" module-name="teavm-core" />
|
<orderEntry type="module" module-name="teavm-core" />
|
||||||
<orderEntry type="module" module-name="teavm-metaprogramming-api" />
|
<orderEntry type="module" module-name="teavm-metaprogramming-api" />
|
||||||
<orderEntry type="library" name="Maven: commons-io:commons-io:2.4" level="project" />
|
|
||||||
<orderEntry type="library" name="Maven: org.ow2.asm:asm-debug-all:5.0.4" level="project" />
|
<orderEntry type="library" name="Maven: org.ow2.asm:asm-debug-all:5.0.4" level="project" />
|
||||||
<orderEntry type="library" name="Maven: com.carrotsearch:hppc:0.6.1" level="project" />
|
<orderEntry type="library" name="Maven: com.carrotsearch:hppc:0.6.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-java:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-chrome-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-firefox-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-ie-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-safari-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-support:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.webbitserver:webbit:0.4.14" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: io.netty:netty:3.5.2.Final" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-leg-rc:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-remote-driver:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: cglib:cglib-nodep:2.1_3" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.3.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.seleniumhq.selenium:selenium-api:2.47.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.4.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: com.google.guava:guava:18.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.commons:commons-exec:1.3" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: net.java.dev.jna:jna:4.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: net.java.dev.jna:jna-platform:4.1.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.6.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.6.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.6.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: net.sourceforge.htmlunit:htmlunit:2.18" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: xalan:xalan:2.7.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: xalan:serializer:2.7.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.4" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpmime:4.5" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.10" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: net.sourceforge.htmlunit:htmlunit-core-js:2.17" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: xerces:xercesImpl:2.11.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: xml-apis:xml-apis:1.4.01" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: net.sourceforge.nekohtml:nekohtml:1.9.22" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: net.sourceforge.cssparser:cssparser:0.9.16" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.w3c.css:sac:1.3" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: commons-io:commons-io:2.4" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-util:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-io:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.2.12.v20150709" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.2.12.v20150709" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -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<String> 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<? extends TestAdapter> adapterCls = adapterClsRaw.asSubclass(TestAdapter.class);
|
|
||||||
Constructor<? extends TestAdapter> 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<JarEntry> 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<File> getAdditionalClassPath() {
|
|
||||||
return Arrays.asList(testFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isSupportedScope(String scope) {
|
|
||||||
return testScopes.contains(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void addSourceProviders(List<SourceFileProvider> providers) {
|
|
||||||
providers.add(new DirectorySourceFileProvider(sourceDirectory));
|
|
||||||
providers.add(new DirectorySourceFileProvider(testSourceDirectory));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<TestResult> results = new ArrayList<>();
|
|
||||||
|
|
||||||
public List<TestResult> getResults() {
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user