Add support for C backend in TeaVMTestRunner

This commit is contained in:
Alexey Andreev 2018-04-20 22:58:09 +03:00
parent e77997c93f
commit 4990dbe8e4
10 changed files with 354 additions and 85 deletions

View File

@ -157,7 +157,7 @@ public class TThrowable extends RuntimeException {
stream.println(TString.wrap(getClass().getName() + ": " + getMessage())); stream.println(TString.wrap(getClass().getName() + ": " + getMessage()));
if (stackTrace != null) { if (stackTrace != null) {
for (TStackTraceElement element : stackTrace) { for (TStackTraceElement element : stackTrace) {
stream.print(TString.wrap(" at ")); stream.print(TString.wrap(" at "));
stream.println(element); stream.println(element);
} }
} }

View File

@ -65,6 +65,7 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
@ -170,6 +171,7 @@ public class CTarget implements TeaVMTarget {
dependencyAnalyzer.linkClass("java.lang.String", null); dependencyAnalyzer.linkClass("java.lang.String", null);
dependencyAnalyzer.linkClass("java.lang.Class", null); dependencyAnalyzer.linkClass("java.lang.Class", null);
dependencyAnalyzer.linkField(new FieldReference("java.lang.String", "hashCode"), null);
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null); ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null);
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null); ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);

View File

@ -202,6 +202,9 @@ public class ExceptionHandlingShadowStackContributor {
String fileName = insn.getLocation() != null ? insn.getLocation().getFileName() : null; String fileName = insn.getLocation() != null ? insn.getLocation().getFileName() : null;
int lineNumber = insn.getLocation() != null ? insn.getLocation().getLine() : -1; int lineNumber = insn.getLocation() != null ? insn.getLocation().getLine() : -1;
if (fileName != null) {
fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
}
CallSiteLocation location = new CallSiteLocation(fileName, method.getClassName(), method.getName(), CallSiteLocation location = new CallSiteLocation(fileName, method.getClassName(), method.getName(),
lineNumber); lineNumber);
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location); CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), location);

View File

@ -0,0 +1,118 @@
/*
* Copyright 2018 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.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class CRunner {
private String compilerCommand;
public CRunner(String compilerCommand) {
this.compilerCommand = compilerCommand;
}
public void run(TestRun run) {
try {
File inputFile = new File(run.getBaseDirectory(), run.getFileName());
String exeName = run.getFileName();
if (exeName.endsWith(".c")) {
exeName = exeName.substring(0, exeName.length() - 2);
}
if (System.getProperty("os.name").toLowerCase().contains("win")) {
exeName += ".exe";
} else {
exeName += ".out";
}
File outputFile = new File(run.getBaseDirectory(), exeName);
List<String> compilerOutput = new ArrayList<>();
boolean compilerSuccess = runCompiler(inputFile, outputFile, compilerOutput);
if (!compilerSuccess) {
run.getCallback().error(new RuntimeException("C compiler error:\n" + mergeLines(compilerOutput)));
return;
}
writeLines(compilerOutput);
List<String> runtimeOutput = new ArrayList<>();
outputFile.setExecutable(true);
runProcess(new ProcessBuilder(outputFile.getPath()).start(), runtimeOutput);
if (!runtimeOutput.isEmpty() && runtimeOutput.get(runtimeOutput.size() - 1).equals("SUCCESS")) {
writeLines(runtimeOutput.subList(0, runtimeOutput.size() - 1));
run.getCallback().complete();
} else {
run.getCallback().error(new RuntimeException("Test failed:\n" + mergeLines(runtimeOutput)));
}
} catch (Exception e) {
run.getCallback().error(e);
return;
}
}
private String mergeLines(List<String> lines) {
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line).append('\n');
}
return sb.toString();
}
private void writeLines(List<String> lines) {
for (String line : lines) {
System.out.println(line);
}
}
private boolean runCompiler(File inputFile, File outputFile, List<String> output) throws Exception {
String[] parts = compilerCommand.split(" +");
for (int i = 0; i < parts.length; ++i) {
switch (parts[i]) {
case "@IN":
parts[i] = inputFile.getPath();
break;
case "@OUT":
parts[i] = outputFile.getPath();
break;
}
}
return runProcess(new ProcessBuilder(parts).start(), output);
}
private boolean runProcess(Process process, List<String> output) throws Exception {
BufferedReader stdin = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while (process.isAlive()) {
String line = stderr.readLine();
if (line == null) {
break;
}
output.add(line);
}
while (process.isAlive()) {
String line = stdin.readLine();
if (line == null) {
break;
}
output.add(line);
}
return process.waitFor() == 0;
}
}

View File

@ -54,7 +54,7 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
} }
try { try {
page.set(webClient.get().<HtmlPage>getPage("about:blank")); page.set(webClient.get().getPage("about:blank"));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -0,0 +1,21 @@
/*
* Copyright 2018 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;
enum RunKind {
JAVASCRIPT,
C
}

View File

@ -15,18 +15,20 @@
*/ */
package org.teavm.junit; package org.teavm.junit;
import org.teavm.backend.c.CTarget;
import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.vm.TeaVM; import org.teavm.vm.TeaVM;
import org.teavm.vm.TeaVMOptimizationLevel; import org.teavm.vm.TeaVMOptimizationLevel;
import org.teavm.vm.TeaVMTarget;
interface TeaVMTestConfiguration { interface TeaVMTestConfiguration<T extends TeaVMTarget> {
String getSuffix(); String getSuffix();
void apply(TeaVM vm); void apply(TeaVM vm);
void apply(JavaScriptTarget target); void apply(T target);
TeaVMTestConfiguration DEFAULT = new TeaVMTestConfiguration() { TeaVMTestConfiguration<JavaScriptTarget> JS_DEFAULT = new TeaVMTestConfiguration<JavaScriptTarget>() {
@Override @Override
public String getSuffix() { public String getSuffix() {
return ""; return "";
@ -43,7 +45,7 @@ interface TeaVMTestConfiguration {
} }
}; };
TeaVMTestConfiguration OPTIMIZED = new TeaVMTestConfiguration() { TeaVMTestConfiguration<JavaScriptTarget> JS_OPTIMIZED = new TeaVMTestConfiguration<JavaScriptTarget>() {
@Override @Override
public String getSuffix() { public String getSuffix() {
return "optimized"; return "optimized";
@ -60,7 +62,7 @@ interface TeaVMTestConfiguration {
} }
}; };
TeaVMTestConfiguration MINIFIED = new TeaVMTestConfiguration() { TeaVMTestConfiguration<JavaScriptTarget> JS_MINIFIED = new TeaVMTestConfiguration<JavaScriptTarget>() {
@Override @Override
public String getSuffix() { public String getSuffix() {
return "min"; return "min";
@ -76,4 +78,37 @@ interface TeaVMTestConfiguration {
target.setMinifying(true); target.setMinifying(true);
} }
}; };
TeaVMTestConfiguration<CTarget> C_DEFAULT = new TeaVMTestConfiguration<CTarget>() {
@Override
public String getSuffix() {
return "";
}
@Override
public void apply(TeaVM vm) {
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
}
@Override
public void apply(CTarget target) {
}
};
TeaVMTestConfiguration<CTarget> C_OPTIMIZED = new TeaVMTestConfiguration<CTarget>() {
@Override
public String getSuffix() {
return "";
}
@Override
public void apply(TeaVM vm) {
vm.setOptimizationLevel(TeaVMOptimizationLevel.FULL);
}
@Override
public void apply(CTarget target) {
}
};
} }

View File

@ -39,6 +39,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
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;
@ -49,6 +50,7 @@ import org.junit.runner.manipulation.NoTestsRemainException;
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.model.InitializationError; import org.junit.runners.model.InitializationError;
import org.teavm.backend.c.CTarget;
import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.callgraph.CallGraph; import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.DefaultProblemTextConsumer; import org.teavm.diagnostics.DefaultProblemTextConsumer;
@ -67,12 +69,18 @@ import org.teavm.tooling.TeaVMProblemRenderer;
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;
import org.teavm.vm.TeaVMTarget;
public class TeaVMTestRunner extends Runner implements Filterable { public class TeaVMTestRunner extends Runner implements Filterable {
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 JS_RUNNER = "teavm.junit.js.runner";
private static final String THREAD_COUNT = "teavm.junit.js.threads"; private static final String THREAD_COUNT = "teavm.junit.js.threads";
private static final String SELENIUM_URL = "teavm.junit.js.selenium.url"; private static final String SELENIUM_URL = "teavm.junit.js.selenium.url";
private static final String C_ENABLED = "teavm.junit.c";
private static final String C_COMPILER = "teavm.junit.c-compiler";
private static final String MINIFIED = "teavm.junit.minified";
private static final String OPTIMIZED = "teavm.junit.optimized";
private static final int stopTimeout = 15000; private static final int stopTimeout = 15000;
private Class<?> testClass; private Class<?> testClass;
private ClassHolder classHolder; private ClassHolder classHolder;
@ -82,12 +90,13 @@ public class TeaVMTestRunner extends Runner implements Filterable {
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 TestRunStrategy jsRunStrategy;
private static volatile TestRunner runner; private static volatile TestRunner runner;
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private static volatile ScheduledFuture<?> cleanupFuture; private static volatile ScheduledFuture<?> cleanupFuture;
private CountDownLatch latch; private CountDownLatch latch;
private List<Method> filteredChildren; private List<Method> filteredChildren;
private CRunner cRunner;
static { static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
@ -111,27 +120,32 @@ public class TeaVMTestRunner extends Runner implements Filterable {
outputDir = new File(outputPath); outputDir = new File(outputPath);
} }
String runStrategyName = System.getProperty(RUNNER); String runStrategyName = System.getProperty(JS_RUNNER);
if (runStrategyName != null) { if (runStrategyName != null) {
switch (runStrategyName) { switch (runStrategyName) {
case "selenium": case "selenium":
try { try {
runStrategy = new SeleniumRunStrategy(new URL(System.getProperty(SELENIUM_URL))); jsRunStrategy = new SeleniumRunStrategy(new URL(System.getProperty(SELENIUM_URL)));
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
throw new InitializationError(e); throw new InitializationError(e);
} }
break; break;
case "htmlunit": case "htmlunit":
runStrategy = new HtmlUnitRunStrategy(); jsRunStrategy = new HtmlUnitRunStrategy();
break; break;
case "": case "":
case "none": case "none":
runStrategy = null; jsRunStrategy = null;
break; break;
default: default:
throw new InitializationError("Unknown run strategy: " + runStrategyName); throw new InitializationError("Unknown run strategy: " + runStrategyName);
} }
} }
String cCommand = System.getProperty(C_COMPILER);
if (cCommand != null) {
cRunner = new CRunner(cCommand);
}
} }
@Override @Override
@ -217,7 +231,6 @@ public class TeaVMTestRunner extends Runner implements Filterable {
Description description = describeChild(child); Description description = describeChild(child);
if (success && outputDir != null) { if (success && outputDir != null) {
List<TeaVMTestConfiguration> configurations = getConfigurations();
int[] configurationIndex = new int[] { 0 }; int[] configurationIndex = new int[] { 0 };
List<Consumer<Boolean>> onSuccess = new ArrayList<>(); List<Consumer<Boolean>> onSuccess = new ArrayList<>();
@ -231,18 +244,30 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
}); });
for (TeaVMTestConfiguration configuration : configurations) { try {
try { File outputPath = getOutputPath(child);
TestRun run = compileByTeaVM(child, notifier, configuration, onSuccess.get(0)); copyJsFilesTo(outputPath);
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
TestRun run = compile(child, notifier, RunKind.JAVASCRIPT,
m -> compileToJs(m, configuration, outputPath), onSuccess.get(0));
if (run != null) { if (run != null) {
runs.add(run); runs.add(run);
} }
} catch (Throwable e) {
notifier.fireTestFailure(new Failure(description, e));
notifier.fireTestFinished(description);
latch.countDown();
return;
} }
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
TestRun run = compile(child, notifier, RunKind.C,
m -> compileToC(m, configuration, outputPath), onSuccess.get(0));
if (run != null) {
runs.add(run);
}
}
} catch (Throwable e) {
notifier.fireTestFailure(new Failure(description, e));
notifier.fireTestFinished(description);
latch.countDown();
return;
} }
onSuccess.get(0).accept(true); onSuccess.get(0).accept(true);
@ -293,13 +318,13 @@ public class TeaVMTestRunner extends Runner implements Filterable {
return true; return true;
} }
private TestRun compileByTeaVM(Method child, RunNotifier notifier, private TestRun compile(Method child, RunNotifier notifier, RunKind kind,
TeaVMTestConfiguration configuration, Consumer<Boolean> onComplete) { CompileFunction compiler, Consumer<Boolean> onComplete) {
Description description = describeChild(child); Description description = describeChild(child);
CompileResult compileResult; CompileResult compileResult;
try { try {
compileResult = compileTest(child, configuration); compileResult = compiler.compile(child);
} catch (Exception e) { } catch (Exception e) {
notifier.fireTestFailure(new Failure(description, e)); notifier.fireTestFailure(new Failure(description, e));
notifier.fireTestFinished(description); notifier.fireTestFinished(description);
@ -308,12 +333,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
if (!compileResult.success) { if (!compileResult.success) {
notifier.fireTestFailure(new Failure(description, notifier.fireTestFailure(new Failure(description, new AssertionError(compileResult.errorMessage)));
new AssertionError(compileResult.errorMessage)));
return null; return null;
} }
if (runStrategy == null) { if (jsRunStrategy == null) {
return null; return null;
} }
@ -330,36 +354,59 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
}; };
return new TestRun(compileResult.file.getParentFile(), child, return new TestRun(compileResult.file.getParentFile(), child, description, compileResult.file.getName(),
new MethodReference(testClass.getName(), getDescriptor(child)), kind, callback);
description, compileResult.file.getName(), callback);
} }
private void submitRun(TestRun run) { private void submitRun(TestRun run) {
synchronized (TeaVMTestRunner.class) { synchronized (TeaVMTestRunner.class) {
if (runStrategy == null) { switch (run.getKind()) {
return; case JAVASCRIPT:
submitJavaScriptRun(run);
break;
case C:
submitCRun(run);
break;
default:
run.getCallback().complete();
break;
} }
if (runner == null) {
runner = new TestRunner(runStrategy);
try {
runner.setNumThreads(Integer.parseInt(System.getProperty(THREAD_COUNT, "1")));
} catch (NumberFormatException e) {
runner.setNumThreads(1);
}
runner.init();
}
runner.run(run);
if (cleanupFuture != null) {
cleanupFuture.cancel(false);
cleanupFuture = null;
}
cleanupFuture = executor.schedule(TeaVMTestRunner::cleanupRunner, stopTimeout, TimeUnit.MILLISECONDS);
} }
} }
private void submitJavaScriptRun(TestRun run) {
if (jsRunStrategy == null) {
run.getCallback().complete();
return;
}
if (runner == null) {
runner = new TestRunner(jsRunStrategy);
try {
runner.setNumThreads(Integer.parseInt(System.getProperty(THREAD_COUNT, "1")));
} catch (NumberFormatException e) {
runner.setNumThreads(1);
}
runner.init();
}
runner.run(run);
if (cleanupFuture != null) {
cleanupFuture.cancel(false);
cleanupFuture = null;
}
cleanupFuture = executor.schedule(TeaVMTestRunner::cleanupRunner, stopTimeout, TimeUnit.MILLISECONDS);
}
private void submitCRun(TestRun run) {
if (cRunner == null) {
run.getCallback().complete();
return;
}
cRunner.run(run);
}
private static void cleanupRunner() { private static void cleanupRunner() {
synchronized (TeaVMTestRunner.class) { synchronized (TeaVMTestRunner.class) {
cleanupFuture = null; cleanupFuture = null;
@ -368,13 +415,39 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
} }
private CompileResult compileTest(Method method, TeaVMTestConfiguration configuration) throws IOException { private File getOutputPath(Method method) {
CompileResult result = new CompileResult();
File path = outputDir; File path = outputDir;
path = new File(path, method.getDeclaringClass().getName().replace('.', '/')); path = new File(path, method.getDeclaringClass().getName().replace('.', '/'));
path = new File(path, method.getName()); path = new File(path, method.getName());
path.mkdirs(); path.mkdirs();
return path;
}
private void copyJsFilesTo(File path) throws IOException {
resourceToFile("org/teavm/backend/javascript/runtime.js", new File(path, "runtime.js"));
resourceToFile("teavm-run-test.html", new File(path, "run-test.html"));
}
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
File path) {
return compileTest(method, configuration, JavaScriptTarget::new, vm -> {
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);
}, path, ".js");
}
private CompileResult compileToC(Method method, TeaVMTestConfiguration<CTarget> configuration,
File path) {
return compileTest(method, configuration, CTarget::new, vm -> {
vm.entryPoint("main", new MethodReference(TestEntryPoint.class, "main", String[].class, void.class));
}, path, ".c");
}
private <T extends TeaVMTarget> CompileResult compileTest(Method method, TeaVMTestConfiguration<T> configuration,
Supplier<T> targetSupplier, Consumer<TeaVM> preBuild, File path, String extension) {
CompileResult result = new CompileResult();
StringBuilder simpleName = new StringBuilder(); StringBuilder simpleName = new StringBuilder();
simpleName.append("test"); simpleName.append("test");
@ -382,23 +455,20 @@ public class TeaVMTestRunner extends Runner implements Filterable {
if (!suffix.isEmpty()) { if (!suffix.isEmpty()) {
simpleName.append('-').append(suffix); simpleName.append('-').append(suffix);
} }
simpleName.append(".js"); simpleName.append(extension);
File outputFile = new File(path, simpleName.toString()); File outputFile = new File(path, simpleName.toString());
result.file = outputFile; result.file = outputFile;
resourceToFile("org/teavm/backend/javascript/runtime.js", new File(path, "runtime.js"));
resourceToFile("teavm-run-test.html", new File(path, "run-test.html"));
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader(); ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
ClassHolderSource classSource = getClassSource(classLoader); ClassHolderSource classSource = getClassSource(classLoader);
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method)); MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
Class<?> runnerType = testAdapter.getRunner(methodHolder); Class<?> runnerType = testAdapter.getRunner(methodHolder);
JavaScriptTarget jsTarget = new JavaScriptTarget(); T target = targetSupplier.get();
configuration.apply(jsTarget); configuration.apply(target);
TeaVM vm = new TeaVMBuilder(jsTarget) TeaVM vm = new TeaVMBuilder(target)
.setClassLoader(classLoader) .setClassLoader(classLoader)
.setClassSource(classSource) .setClassSource(classSource)
.build(); .build();
@ -414,10 +484,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
new TestExceptionPlugin().install(vm); new TestExceptionPlugin().install(vm);
new TestEntryPointTransformer(runnerType.getName(), methodHolder.getReference()).install(vm); new TestEntryPointTransformer(runnerType.getName(), methodHolder.getReference()).install(vm);
MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", preBuild.accept(vm);
Throwable.class, String.class);
vm.entryPoint("runTest", new MethodReference(TestEntryPoint.class, "run", void.class)).async();
vm.entryPoint("extractException", exceptionMsg);
vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName()); vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName());
if (!vm.getProblemProvider().getProblems().isEmpty()) { if (!vm.getProblemProvider().getProblems().isEmpty()) {
result.success = false; result.success = false;
@ -427,14 +494,25 @@ public class TeaVMTestRunner extends Runner implements Filterable {
return result; return result;
} }
private List<TeaVMTestConfiguration> getConfigurations() { private List<TeaVMTestConfiguration<JavaScriptTarget>> getJavaScriptConfigurations() {
List<TeaVMTestConfiguration> configurations = new ArrayList<>(); List<TeaVMTestConfiguration<JavaScriptTarget>> configurations = new ArrayList<>();
configurations.add(TeaVMTestConfiguration.DEFAULT); configurations.add(TeaVMTestConfiguration.JS_DEFAULT);
if (Boolean.parseBoolean(System.getProperty("teavm.junit.minified", "false"))) { if (Boolean.getBoolean(MINIFIED)) {
configurations.add(TeaVMTestConfiguration.MINIFIED); configurations.add(TeaVMTestConfiguration.JS_MINIFIED);
} }
if (Boolean.parseBoolean(System.getProperty("teavm.junit.optimized", "false"))) { if (Boolean.getBoolean(OPTIMIZED)) {
configurations.add(TeaVMTestConfiguration.OPTIMIZED); configurations.add(TeaVMTestConfiguration.JS_OPTIMIZED);
}
return configurations;
}
private List<TeaVMTestConfiguration<CTarget>> getCConfigurations() {
List<TeaVMTestConfiguration<CTarget>> configurations = new ArrayList<>();
if (Boolean.getBoolean(C_ENABLED)) {
configurations.add(TeaVMTestConfiguration.C_DEFAULT);
if (Boolean.getBoolean(OPTIMIZED)) {
configurations.add(TeaVMTestConfiguration.C_OPTIMIZED);
}
} }
return configurations; return configurations;
} }
@ -501,4 +579,8 @@ public class TeaVMTestRunner extends Runner implements Filterable {
String errorMessage; String errorMessage;
File file; File file;
} }
interface CompileFunction {
CompileResult compile(Method method);
}
} }

View File

@ -32,4 +32,13 @@ final class TestEntryPoint {
private static native void launchTest(); private static native void launchTest();
private static native boolean isExpectedException(Class<?> cls); private static native boolean isExpectedException(Class<?> cls);
public static void main(String[] args) throws Exception {
try {
run();
System.out.println("SUCCESS");
} catch (Throwable e) {
e.printStackTrace(System.out);
}
}
} }

View File

@ -18,24 +18,23 @@ package org.teavm.junit;
import java.io.File; import java.io.File;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.junit.runner.Description; import org.junit.runner.Description;
import org.teavm.model.MethodReference;
class TestRun { class TestRun {
private File baseDirectory; private File baseDirectory;
private Method method; private Method method;
private MethodReference reference;
private Description description; private Description description;
private TestRunCallback callback;
private String fileName; private String fileName;
private RunKind kind;
private TestRunCallback callback;
TestRun(File baseDirectory, Method method, MethodReference reference, Description description, String fileName, TestRun(File baseDirectory, Method method, Description description, String fileName, RunKind kind,
TestRunCallback callback) { TestRunCallback callback) {
this.baseDirectory = baseDirectory; this.baseDirectory = baseDirectory;
this.method = method; this.method = method;
this.reference = reference;
this.description = description; this.description = description;
this.callback = callback;
this.fileName = fileName; this.fileName = fileName;
this.kind = kind;
this.callback = callback;
} }
public File getBaseDirectory() { public File getBaseDirectory() {
@ -46,19 +45,19 @@ class TestRun {
return method; return method;
} }
public MethodReference getReference() {
return reference;
}
public Description getDescription() { public Description getDescription() {
return description; return description;
} }
public TestRunCallback getCallback() {
return callback;
}
public String getFileName() { public String getFileName() {
return fileName; return fileName;
} }
public RunKind getKind() {
return kind;
}
public TestRunCallback getCallback() {
return callback;
}
} }