mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add per-class compilation when running tests (requires @WholeClassCompilation
annotation)
This commit is contained in:
parent
150a613709
commit
95426e2159
|
@ -19,18 +19,20 @@
|
|||
window.addEventListener("message", event => {
|
||||
let request = event.data;
|
||||
switch (request.type) {
|
||||
case "js":
|
||||
appendFiles(request.files, 0, () => {
|
||||
launchTest(response => {
|
||||
case "JAVASCRIPT":
|
||||
appendFiles([request.file], 0, () => {
|
||||
launchTest(request.argument, response => {
|
||||
event.source.postMessage(response, "*");
|
||||
});
|
||||
}, error => {
|
||||
event.source.postMessage({ status: "failed", errorMessage: error }, "*");
|
||||
});
|
||||
break;
|
||||
case "wasm":
|
||||
appendFiles(request.files.filter(f => f.endsWith(".js")), 0, () => {
|
||||
launchWasmTest(request.files.filter(f => f.endsWith(".wasm"))[0], response => {
|
||||
|
||||
case "WASM":
|
||||
const runtimeFile = request.file + "-runtime.js";
|
||||
appendFiles([runtimeFile], 0, () => {
|
||||
launchWasmTest(request.file, equest.argument, response => {
|
||||
event.source.postMessage(response, "*");
|
||||
});
|
||||
}, error => {
|
||||
|
@ -57,8 +59,8 @@ function appendFiles(files, index, callback, errorCallback) {
|
|||
}
|
||||
}
|
||||
|
||||
function launchTest(callback) {
|
||||
main([], result => {
|
||||
function launchTest(argument, callback) {
|
||||
main(argument ? [argument] : [], result => {
|
||||
if (result instanceof Error) {
|
||||
callback({
|
||||
status: "failed",
|
||||
|
@ -81,7 +83,7 @@ function launchTest(callback) {
|
|||
}
|
||||
}
|
||||
|
||||
function launchWasmTest(path, callback) {
|
||||
function launchWasmTest(path, argument, callback) {
|
||||
var output = [];
|
||||
var outputBuffer = "";
|
||||
|
||||
|
|
|
@ -19,16 +19,6 @@ import * as fs from "./promise-fs.js";
|
|||
import * as http from "http";
|
||||
import {server as WebSocketServer} from "websocket";
|
||||
|
||||
const TEST_FILE_NAME = "test.js";
|
||||
const WASM_RUNTIME_FILE_NAME = "test.wasm-runtime.js";
|
||||
const TEST_FILES = [
|
||||
{ file: TEST_FILE_NAME, name: "simple", type: "js" },
|
||||
{ file: "test-min.js", name: "minified", type: "js" },
|
||||
{ file: "test-optimized.js", name: "optimized", type: "js" },
|
||||
{ file: "test.wasm", name: "wasm", type: "wasm" },
|
||||
{ file: "test-optimized.wasm", name: "wasm-optimized", type: "wasm" }
|
||||
];
|
||||
const SERVER_PREFIX = "http://localhost:9090/";
|
||||
let totalTests = 0;
|
||||
|
||||
class TestSuite {
|
||||
|
@ -39,10 +29,10 @@ class TestSuite {
|
|||
}
|
||||
}
|
||||
class TestCase {
|
||||
constructor(type, name, files) {
|
||||
constructor(type, file, argument) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.files = files;
|
||||
this.file = file;
|
||||
this.argument = argument
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +44,7 @@ if (rootDir.endsWith("/")) {
|
|||
async function runAll() {
|
||||
const rootSuite = new TestSuite("root");
|
||||
console.log("Searching tests");
|
||||
await walkDir("", "root", rootSuite);
|
||||
await walkDir("", rootSuite);
|
||||
|
||||
console.log("Running tests");
|
||||
|
||||
|
@ -119,38 +109,29 @@ async function serveFile(path, response) {
|
|||
}
|
||||
}
|
||||
|
||||
async function walkDir(path, name, suite) {
|
||||
async function walkDir(path, suite) {
|
||||
const files = await fs.readdir(rootDir + "/" + path);
|
||||
if (files.includes(WASM_RUNTIME_FILE_NAME) || files.includes("test.js")) {
|
||||
for (const { file: fileName, name: profileName, type: type } of TEST_FILES) {
|
||||
if (files.includes(fileName)) {
|
||||
switch (type) {
|
||||
case "js":
|
||||
suite.testCases.push(new TestCase(
|
||||
"js", name + " " + profileName,
|
||||
[SERVER_PREFIX + path + "/" + fileName]));
|
||||
break;
|
||||
case "wasm":
|
||||
suite.testCases.push(new TestCase(
|
||||
"wasm", name + " " + profileName,
|
||||
[SERVER_PREFIX + path + "/" + WASM_RUNTIME_FILE_NAME,
|
||||
SERVER_PREFIX + path + "/" + fileName]));
|
||||
break;
|
||||
}
|
||||
if (files.includes("tests.json")) {
|
||||
const descriptor = JSON.parse(await fs.readFile(`${rootDir}/${path}/tests.json`));
|
||||
for (const { baseDir, fileName, kind, argument } of descriptor) {
|
||||
switch (kind) {
|
||||
case "JAVASCRIPT":
|
||||
case "WASM":
|
||||
suite.testCases.push(new TestCase(kind, `${baseDir}/${fileName}`, argument));
|
||||
totalTests++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (files) {
|
||||
const childSuite = new TestSuite(name);
|
||||
suite.testSuites.push(childSuite);
|
||||
await Promise.all(files.map(async file => {
|
||||
const filePath = path + "/" + file;
|
||||
const stat = await fs.stat(rootDir + "/" + filePath);
|
||||
if (stat.isDirectory()) {
|
||||
await walkDir(filePath, file, childSuite);
|
||||
const childSuite = new TestSuite(file);
|
||||
suite.testSuites.push(childSuite);
|
||||
await walkDir(filePath, childSuite);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class TestRunner {
|
||||
|
@ -182,11 +163,15 @@ class TestRunner {
|
|||
const startTime = new Date().getTime();
|
||||
let request = { id: this.requestIdGen++ };
|
||||
request.tests = suite.testCases.map(testCase => {
|
||||
return {
|
||||
const result = {
|
||||
type: testCase.type,
|
||||
name: testCase.name,
|
||||
files: testCase.files
|
||||
file: testCase.file
|
||||
};
|
||||
if (testCase.argument) {
|
||||
result.argument = testCase.argument;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
this.testsRun += suite.testCases.length;
|
||||
|
||||
|
|
|
@ -20,11 +20,16 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
class CRunStrategy implements TestRunStrategy {
|
||||
private String compilerCommand;
|
||||
private ConcurrentMap<String, Compilation> compilationMap = new ConcurrentHashMap<>();
|
||||
|
||||
CRunStrategy(String compilerCommand) {
|
||||
this.compilerCommand = compilerCommand;
|
||||
|
@ -47,18 +52,21 @@ class CRunStrategy implements TestRunStrategy {
|
|||
}
|
||||
|
||||
File outputFile = new File(run.getBaseDirectory(), exeName);
|
||||
List<String> compilerOutput = new ArrayList<>();
|
||||
boolean compilerSuccess = runCompiler(run.getBaseDirectory(), compilerOutput);
|
||||
boolean compilerSuccess = compile(run.getBaseDirectory());
|
||||
if (!compilerSuccess) {
|
||||
run.getCallback().error(new RuntimeException("C compiler error:\n" + mergeLines(compilerOutput)));
|
||||
run.getCallback().error(new RuntimeException("C compiler error"));
|
||||
return;
|
||||
}
|
||||
writeLines(compilerOutput);
|
||||
|
||||
List<String> runtimeOutput = new ArrayList<>();
|
||||
List<String> stdout = new ArrayList<>();
|
||||
outputFile.setExecutable(true);
|
||||
runProcess(new ProcessBuilder(outputFile.getPath()).start(), runtimeOutput, stdout);
|
||||
List<String> runCommand = new ArrayList<>();
|
||||
runCommand.add(outputFile.getPath());
|
||||
if (run.getArgument() != null) {
|
||||
runCommand.add(run.getArgument());
|
||||
}
|
||||
runProcess(new ProcessBuilder(runCommand.toArray(new String[0])).start(), runtimeOutput, stdout);
|
||||
if (!stdout.isEmpty() && stdout.get(stdout.size() - 1).equals("SUCCESS")) {
|
||||
writeLines(runtimeOutput);
|
||||
run.getCallback().complete();
|
||||
|
@ -84,6 +92,24 @@ class CRunStrategy implements TestRunStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean compile(File inputDir) throws IOException, InterruptedException {
|
||||
Compilation compilation = compilationMap.computeIfAbsent(inputDir.getPath(), k -> new Compilation());
|
||||
synchronized (compilation) {
|
||||
if (!compilation.started) {
|
||||
compilation.started = true;
|
||||
compilation.success = doCompile(inputDir);
|
||||
}
|
||||
}
|
||||
return compilation.success;
|
||||
}
|
||||
|
||||
private boolean doCompile(File inputDir) throws IOException, InterruptedException {
|
||||
List<String> compilerOutput = new ArrayList<>();
|
||||
boolean compilerSuccess = runCompiler(inputDir, compilerOutput);
|
||||
writeLines(compilerOutput);
|
||||
return compilerSuccess;
|
||||
}
|
||||
|
||||
private boolean runCompiler(File inputDir, List<String> output)
|
||||
throws IOException, InterruptedException {
|
||||
String command = new File(compilerCommand).getAbsolutePath();
|
||||
|
@ -133,4 +159,9 @@ class CRunStrategy implements TestRunStrategy {
|
|||
output.addAll(lines);
|
||||
return result;
|
||||
}
|
||||
|
||||
static class Compilation {
|
||||
volatile boolean started;
|
||||
volatile boolean success;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.BaseFunction;
|
||||
|
@ -64,7 +65,6 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
HtmlPage pageRef = page.get();
|
||||
|
||||
pageRef.executeJavaScript(readFile(new File(run.getBaseDirectory(), run.getFileName())));
|
||||
boolean decodeStack = Boolean.parseBoolean(System.getProperty(TeaVMTestRunner.JS_DECODE_STACK, "true"));
|
||||
File debugFile = decodeStack ? new File(run.getBaseDirectory(), run.getFileName() + ".teavmdbg") : null;
|
||||
|
@ -74,6 +74,7 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
Function function = (Function) page.get().executeJavaScript(readResource("teavm-htmlunit-adapter.js"))
|
||||
.getJavaScriptResult();
|
||||
Object[] args = new Object[] {
|
||||
run.getArgument(),
|
||||
decodeStack ? createStackDecoderFunction(resultParser) : null,
|
||||
new NativeJavaObject(function, asyncResult, AsyncResult.class)
|
||||
};
|
||||
|
@ -161,7 +162,7 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
|
||||
private String readFile(File file) throws IOException {
|
||||
try (InputStream input = new FileInputStream(file)) {
|
||||
return IOUtils.toString(input, "UTF-8");
|
||||
return IOUtils.toString(input, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,11 +171,11 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
if (input == null) {
|
||||
return "";
|
||||
}
|
||||
return IOUtils.toString(input, "UTF-8");
|
||||
return IOUtils.toString(input, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
public class AsyncResult {
|
||||
public static class AsyncResult {
|
||||
private CountDownLatch latch = new CountDownLatch(1);
|
||||
private Object result;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.junit;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -109,6 +110,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
private static final int stopTimeout = 15000;
|
||||
private Class<?> testClass;
|
||||
private boolean isWholeClassCompilation;
|
||||
private ClassHolderSource classSource;
|
||||
private ClassLoader classLoader;
|
||||
private Description suiteDescription;
|
||||
|
@ -120,6 +122,8 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
private CountDownLatch latch;
|
||||
private List<Method> filteredChildren;
|
||||
private ReferenceCache referenceCache = new ReferenceCache();
|
||||
private boolean classCompilationOk;
|
||||
private List<TestRun> runsInCurrentClass = new ArrayList<>();
|
||||
|
||||
static class RunnerKindInfo {
|
||||
volatile TestRunner runner;
|
||||
|
@ -193,10 +197,17 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
latch = new CountDownLatch(children.size());
|
||||
|
||||
notifier.fireTestStarted(getDescription());
|
||||
isWholeClassCompilation = testClass.isAnnotationPresent(WholeClassCompilation.class);
|
||||
if (isWholeClassCompilation) {
|
||||
classCompilationOk = compileWholeClass(children, notifier);
|
||||
}
|
||||
for (Method child : children) {
|
||||
runChild(child, notifier);
|
||||
}
|
||||
|
||||
writeRunsDescriptor();
|
||||
runsInCurrentClass.clear();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
if (latch.await(1000, TimeUnit.MILLISECONDS)) {
|
||||
|
@ -246,6 +257,38 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
method.getName()));
|
||||
}
|
||||
|
||||
private boolean compileWholeClass(List<Method> children, RunNotifier notifier) {
|
||||
File outputPath = getOutputPathForClass();
|
||||
boolean hasErrors = false;
|
||||
Description description = getDescription();
|
||||
|
||||
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
|
||||
CompileResult result = compileToJs(wholeClass(children), "classTest", configuration, outputPath);
|
||||
if (!result.success) {
|
||||
hasErrors = true;
|
||||
notifier.fireTestFailure(createFailure(description, result));
|
||||
}
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
|
||||
CompileResult result = compileToC(wholeClass(children), "classTest", configuration, outputPath);
|
||||
if (!result.success) {
|
||||
hasErrors = true;
|
||||
notifier.fireTestFailure(createFailure(description, result));
|
||||
}
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
||||
CompileResult result = compileToWasm(wholeClass(children), "classTest", configuration, outputPath);
|
||||
if (!result.success) {
|
||||
hasErrors = true;
|
||||
notifier.fireTestFailure(createFailure(description, result));
|
||||
}
|
||||
}
|
||||
|
||||
return !hasErrors;
|
||||
}
|
||||
|
||||
private void runChild(Method child, RunNotifier notifier) {
|
||||
Description description = describeChild(child);
|
||||
notifier.fireTestStarted(description);
|
||||
|
@ -273,62 +316,37 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
}
|
||||
|
||||
if (!child.isAnnotationPresent(SkipJVM.class)
|
||||
&& !testClass.isAnnotationPresent(SkipJVM.class)) {
|
||||
if (!child.isAnnotationPresent(SkipJVM.class) && !testClass.isAnnotationPresent(SkipJVM.class)) {
|
||||
ran = true;
|
||||
success = runInJvm(child, notifier, expectedExceptions);
|
||||
}
|
||||
|
||||
if (success && outputDir != null) {
|
||||
int[] configurationIndex = new int[] { 0 };
|
||||
List<Consumer<Boolean>> onSuccess = new ArrayList<>();
|
||||
|
||||
List<TestRun> runs = new ArrayList<>();
|
||||
onSuccess.add(runSuccess -> {
|
||||
Consumer<Boolean> onSuccess = runSuccess -> {
|
||||
if (runSuccess && configurationIndex[0] < runs.size()) {
|
||||
submitRun(runs.get(configurationIndex[0]++));
|
||||
} else {
|
||||
notifier.fireTestFinished(description);
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
File outputPath = getOutputPath(child);
|
||||
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) {
|
||||
runs.add(run);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
||||
TestRun run = compile(child, notifier, RunKind.WASM,
|
||||
m -> compileToWasm(m, configuration, outputPath), onSuccess.get(0));
|
||||
if (run != null) {
|
||||
runs.add(run);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
notifier.fireTestFailure(new Failure(description, e));
|
||||
if (isWholeClassCompilation) {
|
||||
if (!classCompilationOk) {
|
||||
notifier.fireTestFinished(description);
|
||||
notifier.fireTestFailure(new Failure(description,
|
||||
new AssertionError("Could not compile test class")));
|
||||
latch.countDown();
|
||||
return;
|
||||
} else {
|
||||
runTestsFromWholeClass(child, notifier, runs, onSuccess);
|
||||
onSuccess.accept(true);
|
||||
}
|
||||
} else {
|
||||
runCompiledTest(child, notifier, runs, onSuccess);
|
||||
}
|
||||
|
||||
onSuccess.get(0).accept(true);
|
||||
} else {
|
||||
if (!ran) {
|
||||
notifier.fireTestIgnored(description);
|
||||
|
@ -338,6 +356,81 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
}
|
||||
|
||||
private void runTestsFromWholeClass(Method child, RunNotifier notifier, List<TestRun> runs,
|
||||
Consumer<Boolean> onSuccess) {
|
||||
File outputPath = getOutputPathForClass();
|
||||
MethodDescriptor descriptor = getDescriptor(child);
|
||||
MethodReference reference = new MethodReference(testClass.getName(), descriptor);
|
||||
|
||||
File testFilePath = getOutputPath(child);
|
||||
testFilePath.mkdirs();
|
||||
|
||||
boolean hasJsOrWasm = false;
|
||||
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
|
||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".js");
|
||||
runs.add(createTestRun(testPath, child, RunKind.JAVASCRIPT, reference.toString(), notifier, onSuccess));
|
||||
hasJsOrWasm = true;
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".wasm");
|
||||
runs.add(createTestRun(testPath, child, RunKind.WASM, reference.toString(), notifier, onSuccess));
|
||||
hasJsOrWasm = true;
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
|
||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), true, ".c");
|
||||
runs.add(createTestRun(testPath, child, RunKind.C, reference.toString(), notifier, onSuccess));
|
||||
}
|
||||
|
||||
if (hasJsOrWasm) {
|
||||
try {
|
||||
copyJsFilesTo(testFilePath);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runCompiledTest(Method child, RunNotifier notifier, List<TestRun> runs, Consumer<Boolean> onSuccess) {
|
||||
try {
|
||||
File outputPath = getOutputPath(child);
|
||||
copyJsFilesTo(outputPath);
|
||||
|
||||
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
|
||||
CompileResult compileResult = compileToJs(singleTest(child), "test", configuration, outputPath);
|
||||
TestRun run = prepareRun(child, compileResult, notifier, RunKind.JAVASCRIPT, onSuccess);
|
||||
if (run != null) {
|
||||
runs.add(run);
|
||||
}
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
|
||||
CompileResult compileResult = compileToC(singleTest(child), "test", configuration, outputPath);
|
||||
TestRun run = prepareRun(child, compileResult, notifier, RunKind.C, onSuccess);
|
||||
if (run != null) {
|
||||
runs.add(run);
|
||||
}
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
||||
CompileResult compileResult = compileToWasm(singleTest(child), "test", configuration,
|
||||
outputPath);
|
||||
TestRun run = prepareRun(child, compileResult, notifier, RunKind.WASM, onSuccess);
|
||||
if (run != null) {
|
||||
runs.add(run);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
notifier.fireTestFinished(describeChild(child));
|
||||
latch.countDown();
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess.accept(true);
|
||||
}
|
||||
|
||||
private String[] getExpectedExceptions(MethodHolder method) {
|
||||
AnnotationHolder annot = method.getAnnotations().get(JUNIT4_TEST);
|
||||
if (annot == null) {
|
||||
|
@ -438,7 +531,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
class JUnit4Runner implements Runner {
|
||||
static class JUnit4Runner implements Runner {
|
||||
Object instance;
|
||||
Method child;
|
||||
|
||||
|
@ -457,7 +550,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
}
|
||||
|
||||
class JUnit3Runner implements Runner {
|
||||
static class JUnit3Runner implements Runner {
|
||||
Object instance;
|
||||
|
||||
JUnit3Runner(Object instance) {
|
||||
|
@ -470,25 +563,24 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
}
|
||||
|
||||
private TestRun compile(Method child, RunNotifier notifier, RunKind kind,
|
||||
CompileFunction compiler, Consumer<Boolean> onComplete) {
|
||||
private TestRun prepareRun(Method child, CompileResult result, RunNotifier notifier, RunKind kind,
|
||||
Consumer<Boolean> onComplete) {
|
||||
Description description = describeChild(child);
|
||||
|
||||
CompileResult compileResult;
|
||||
try {
|
||||
compileResult = compiler.compile(child);
|
||||
} catch (Exception e) {
|
||||
notifier.fireTestFailure(new Failure(description, e));
|
||||
if (!result.success) {
|
||||
notifier.fireTestFailure(createFailure(description, result));
|
||||
notifier.fireTestFinished(description);
|
||||
latch.countDown();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!compileResult.success) {
|
||||
notifier.fireTestFailure(new Failure(description, new AssertionError(compileResult.errorMessage)));
|
||||
return null;
|
||||
return createTestRun(result.file, child, kind, null, notifier, onComplete);
|
||||
}
|
||||
|
||||
private TestRun createTestRun(File file, Method child, RunKind kind, String argument, RunNotifier notifier,
|
||||
Consumer<Boolean> onComplete) {
|
||||
Description description = describeChild(child);
|
||||
|
||||
TestRunCallback callback = new TestRunCallback() {
|
||||
@Override
|
||||
public void complete() {
|
||||
|
@ -502,12 +594,21 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
};
|
||||
|
||||
return new TestRun(compileResult.file.getParentFile(), child, description, compileResult.file.getName(),
|
||||
kind, callback);
|
||||
return new TestRun(file.getParentFile(), child, description, file.getName(), kind,
|
||||
argument, callback);
|
||||
}
|
||||
|
||||
private Failure createFailure(Description description, CompileResult result) {
|
||||
Throwable throwable = result.throwable;
|
||||
if (throwable == null) {
|
||||
throwable = new AssertionError(result.errorMessage);
|
||||
}
|
||||
return new Failure(description, throwable);
|
||||
}
|
||||
|
||||
private void submitRun(TestRun run) {
|
||||
synchronized (TeaVMTestRunner.class) {
|
||||
runsInCurrentClass.add(run);
|
||||
RunnerKindInfo info = runners.get(run.getKind());
|
||||
|
||||
if (info.strategy == null) {
|
||||
|
@ -552,14 +653,20 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
return path;
|
||||
}
|
||||
|
||||
private File getOutputPathForClass() {
|
||||
File path = outputDir;
|
||||
path = new File(path, testClass.getName().replace('.', '/'));
|
||||
path.mkdirs();
|
||||
return path;
|
||||
}
|
||||
|
||||
private void copyJsFilesTo(File path) throws IOException {
|
||||
resourceToFile("org/teavm/backend/wasm/wasm-runtime.js", new File(path, "test.wasm-runtime.js"));
|
||||
resourceToFile("teavm-run-test.html", new File(path, "run-test.html"));
|
||||
resourceToFile("teavm-run-test-wasm.html", new File(path, "run-test-wasm.html"));
|
||||
}
|
||||
|
||||
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
|
||||
File path) {
|
||||
private CompileResult compileToJs(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||
TeaVMTestConfiguration<JavaScriptTarget> configuration, File path) {
|
||||
boolean decodeStack = Boolean.parseBoolean(System.getProperty(JS_DECODE_STACK, "true"));
|
||||
DebugInformationBuilder debugEmitter = new DebugInformationBuilder(new ReferenceCache());
|
||||
Supplier<JavaScriptTarget> targetSupplier = () -> {
|
||||
|
@ -595,12 +702,12 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
};
|
||||
}
|
||||
return compileTest(method, configuration, targetSupplier, TestEntryPoint.class.getName(), path, ".js",
|
||||
postBuild, false);
|
||||
return compile(configuration, targetSupplier, TestEntryPoint.class.getName(), path, ".js",
|
||||
postBuild, false, additionalProcessing, baseName);
|
||||
}
|
||||
|
||||
private CompileResult compileToC(Method method, TeaVMTestConfiguration<CTarget> configuration,
|
||||
File path) {
|
||||
private CompileResult compileToC(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||
TeaVMTestConfiguration<CTarget> configuration, File path) {
|
||||
CompilePostProcessor postBuild = (vm, file) -> {
|
||||
try {
|
||||
resourceToFile("teavm-CMakeLists.txt", new File(file.getParent(), "CMakeLists.txt"));
|
||||
|
@ -608,8 +715,8 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
return compileTest(method, configuration, this::createCTarget, TestNativeEntryPoint.class.getName(), path, ".c",
|
||||
postBuild, true);
|
||||
return compile(configuration, this::createCTarget, TestNativeEntryPoint.class.getName(), path, ".c",
|
||||
postBuild, true, additionalProcessing, baseName);
|
||||
}
|
||||
|
||||
private CTarget createCTarget() {
|
||||
|
@ -618,39 +725,50 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
return cTarget;
|
||||
}
|
||||
|
||||
private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration,
|
||||
File path) {
|
||||
return compileTest(method, configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path,
|
||||
".wasm", null, false);
|
||||
private CompileResult compileToWasm(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||
TeaVMTestConfiguration<WasmTarget> configuration, File path) {
|
||||
return compile(configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path,
|
||||
".wasm", null, false, additionalProcessing, baseName);
|
||||
}
|
||||
|
||||
private <T extends TeaVMTarget> CompileResult compileTest(Method method, TeaVMTestConfiguration<T> configuration,
|
||||
private Consumer<TeaVM> singleTest(Method method) {
|
||||
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
|
||||
return vm -> {
|
||||
Properties properties = new Properties();
|
||||
applyProperties(method.getDeclaringClass(), properties);
|
||||
vm.setProperties(properties);
|
||||
new TestEntryPointTransformerForSingleMethod(methodHolder.getReference(), testClass.getName()).install(vm);
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<TeaVM> wholeClass(List<Method> methods) {
|
||||
return vm -> {
|
||||
Properties properties = new Properties();
|
||||
applyProperties(testClass, properties);
|
||||
vm.setProperties(properties);
|
||||
List<MethodReference> methodReferences = new ArrayList<>();
|
||||
for (Method method : methods) {
|
||||
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
methodReferences.add(methodHolder.getReference());
|
||||
}
|
||||
new TestEntryPointTransformerForWholeClass(methodReferences, testClass.getName()).install(vm);
|
||||
};
|
||||
}
|
||||
|
||||
private <T extends TeaVMTarget> CompileResult compile(TeaVMTestConfiguration<T> configuration,
|
||||
Supplier<T> targetSupplier, String entryPoint, File path, String extension,
|
||||
CompilePostProcessor postBuild, boolean separateDir) {
|
||||
CompilePostProcessor postBuild, boolean separateDir,
|
||||
Consumer<TeaVM> additionalProcessing, String baseName) {
|
||||
CompileResult result = new CompileResult();
|
||||
|
||||
StringBuilder simpleName = new StringBuilder();
|
||||
simpleName.append("test");
|
||||
String suffix = configuration.getSuffix();
|
||||
if (!suffix.isEmpty()) {
|
||||
if (!separateDir) {
|
||||
simpleName.append('-').append(suffix);
|
||||
}
|
||||
}
|
||||
File outputFile;
|
||||
if (separateDir) {
|
||||
outputFile = new File(new File(path, simpleName.toString()), "test" + extension);
|
||||
} else {
|
||||
simpleName.append(extension);
|
||||
outputFile = new File(path, simpleName.toString());
|
||||
}
|
||||
File outputFile = getOutputFile(path, baseName, configuration.getSuffix(), separateDir, extension);
|
||||
result.file = outputFile;
|
||||
|
||||
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||
|
||||
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
|
||||
T target = targetSupplier.get();
|
||||
configuration.apply(target);
|
||||
|
||||
|
@ -660,6 +778,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
dependencyAnalyzerFactory = FastDependencyAnalyzer::new;
|
||||
}
|
||||
|
||||
try {
|
||||
TeaVM vm = new TeaVMBuilder(target)
|
||||
.setClassLoader(classLoader)
|
||||
.setClassSource(classSource)
|
||||
|
@ -667,15 +786,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
.setDependencyAnalyzerFactory(dependencyAnalyzerFactory)
|
||||
.build();
|
||||
|
||||
Properties properties = new Properties();
|
||||
applyProperties(method.getDeclaringClass(), properties);
|
||||
vm.setProperties(properties);
|
||||
|
||||
configuration.apply(vm);
|
||||
additionalProcessing.accept(vm);
|
||||
vm.installPlugins();
|
||||
|
||||
new TestExceptionPlugin().install(vm);
|
||||
new TestEntryPointTransformer(methodHolder.getReference(), testClass.getName()).install(vm);
|
||||
|
||||
vm.entryPoint(entryPoint);
|
||||
|
||||
|
@ -697,6 +812,31 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
result = new CompileResult();
|
||||
result.success = false;
|
||||
result.throwable = e;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private File getOutputFile(File path, String baseName, String suffix, boolean separateDir, String extension) {
|
||||
StringBuilder simpleName = new StringBuilder();
|
||||
simpleName.append(baseName);
|
||||
if (!suffix.isEmpty()) {
|
||||
if (!separateDir) {
|
||||
simpleName.append('-').append(suffix);
|
||||
}
|
||||
}
|
||||
File outputFile;
|
||||
if (separateDir) {
|
||||
outputFile = new File(new File(path, simpleName.toString()), "test" + extension);
|
||||
} else {
|
||||
simpleName.append(extension);
|
||||
outputFile = new File(path, simpleName.toString());
|
||||
}
|
||||
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
interface CompilePostProcessor {
|
||||
|
@ -796,13 +936,92 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
}
|
||||
|
||||
private void writeRunsDescriptor() {
|
||||
if (runsInCurrentClass.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File outputDir = getOutputPathForClass();
|
||||
outputDir.mkdirs();
|
||||
File descriptorFile = new File(outputDir, "tests.json");
|
||||
try (OutputStream output = new FileOutputStream(descriptorFile);
|
||||
OutputStream bufferedOutput = new BufferedOutputStream(output);
|
||||
Writer writer = new OutputStreamWriter(bufferedOutput)) {
|
||||
writer.write("[\n");
|
||||
boolean first = true;
|
||||
for (TestRun run : runsInCurrentClass) {
|
||||
if (!first) {
|
||||
writer.write(",\n");
|
||||
}
|
||||
first = false;
|
||||
writer.write(" {\n");
|
||||
writer.write(" \"baseDir\": ");
|
||||
writeJsonString(writer, run.getBaseDirectory().getAbsolutePath().replace('\\', '/'));
|
||||
writer.write(",\n");
|
||||
writer.write(" \"fileName\": ");
|
||||
writeJsonString(writer, run.getFileName());
|
||||
writer.write(",\n");
|
||||
writer.write(" \"kind\": \"" + run.getKind().name() + "\"");
|
||||
if (run.getArgument() != null) {
|
||||
writer.write(",\n");
|
||||
writer.write(" \"argument\": ");
|
||||
writeJsonString(writer, run.getArgument());
|
||||
}
|
||||
writer.write("\n }");
|
||||
}
|
||||
writer.write("\n]");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeJsonString(Writer writer, String s) throws IOException {
|
||||
writer.write('"');
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '"':
|
||||
writer.write("\\\"");
|
||||
case '\\':
|
||||
writer.write("\\\\");
|
||||
break;
|
||||
case '\r':
|
||||
writer.write("\\r");
|
||||
break;
|
||||
case '\n':
|
||||
writer.write("\\n");
|
||||
break;
|
||||
case '\t':
|
||||
writer.write("\\t");
|
||||
break;
|
||||
case '\f':
|
||||
writer.write("\\f");
|
||||
break;
|
||||
case '\b':
|
||||
writer.write("\\b");
|
||||
break;
|
||||
default:
|
||||
if (c < ' ') {
|
||||
writer.write("\\u00");
|
||||
writer.write(hex(c / 16));
|
||||
writer.write(hex(c % 16));
|
||||
} else {
|
||||
writer.write(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
private static char hex(int digit) {
|
||||
return (char) (digit < 10 ? '0' + digit : 'A' + digit - 10);
|
||||
}
|
||||
|
||||
static class CompileResult {
|
||||
boolean success = true;
|
||||
String errorMessage;
|
||||
File file;
|
||||
}
|
||||
|
||||
interface CompileFunction {
|
||||
CompileResult compile(Method method);
|
||||
Throwable throwable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ final class TestEntryPoint {
|
|||
private TestEntryPoint() {
|
||||
}
|
||||
|
||||
public static void run() throws Exception {
|
||||
public static void run(String name) throws Exception {
|
||||
before();
|
||||
try {
|
||||
launchTest();
|
||||
launchTest(name);
|
||||
} finally {
|
||||
try {
|
||||
after();
|
||||
|
@ -36,11 +36,11 @@ final class TestEntryPoint {
|
|||
|
||||
private static native void before();
|
||||
|
||||
private static native void launchTest() throws Exception;
|
||||
private static native void launchTest(String name) throws Exception;
|
||||
|
||||
private static native void after();
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
run();
|
||||
run(args.length == 1 ? args[0] : null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,12 +45,10 @@ import org.teavm.model.emit.ValueEmitter;
|
|||
import org.teavm.vm.spi.TeaVMHost;
|
||||
import org.teavm.vm.spi.TeaVMPlugin;
|
||||
|
||||
class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||
private MethodReference testMethod;
|
||||
abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||
private String testClassName;
|
||||
|
||||
TestEntryPointTransformer(MethodReference testMethod, String testClassName) {
|
||||
this.testMethod = testMethod;
|
||||
TestEntryPointTransformer(String testClassName) {
|
||||
this.testClassName = testClassName;
|
||||
}
|
||||
|
||||
|
@ -91,11 +89,11 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
|||
});
|
||||
ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class);
|
||||
|
||||
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testMethod.getClassName(), false)) {
|
||||
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) {
|
||||
testCaseVar.cast(ValueType.object(JUNIT3_BASE_CLASS)).invokeVirtual(JUNIT3_BEFORE);
|
||||
}
|
||||
|
||||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testMethod.getClassName());
|
||||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testClassName);
|
||||
Collections.reverse(classes);
|
||||
classes.stream()
|
||||
.flatMap(cls -> cls.getMethods().stream())
|
||||
|
@ -110,13 +108,13 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
|||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class);
|
||||
|
||||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testMethod.getClassName());
|
||||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testClassName);
|
||||
classes.stream()
|
||||
.flatMap(cls -> cls.getMethods().stream())
|
||||
.filter(m -> m.getAnnotations().get(JUNIT4_AFTER) != null)
|
||||
.forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference()));
|
||||
|
||||
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testMethod.getClassName(), false)) {
|
||||
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) {
|
||||
testCaseVar.cast(ValueType.object(JUNIT3_BASE_CLASS)).invokeVirtual(JUNIT3_AFTER);
|
||||
}
|
||||
|
||||
|
@ -137,8 +135,10 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
|||
return result;
|
||||
}
|
||||
|
||||
private Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
protected abstract Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy);
|
||||
|
||||
protected final void generateSingleMethodLaunchProgram(MethodReference testMethod,
|
||||
ClassHierarchy hierarchy, ProgramEmitter pe) {
|
||||
pe.getField(TestEntryPoint.class, "testCase", Object.class)
|
||||
.cast(ValueType.object(testMethod.getClassName()))
|
||||
.invokeSpecial(testMethod);
|
||||
|
@ -163,6 +163,5 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
|||
} else {
|
||||
pe.exit();
|
||||
}
|
||||
return pe.getProgram();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
|
||||
class TestEntryPointTransformerForSingleMethod extends TestEntryPointTransformer {
|
||||
private MethodReference testMethod;
|
||||
|
||||
TestEntryPointTransformerForSingleMethod(MethodReference testMethod, String testClassName) {
|
||||
super(testClassName);
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
generateSingleMethodLaunchProgram(testMethod, hierarchy, pe);
|
||||
return pe.getProgram();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.util.List;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.emit.ForkEmitter;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.model.instructions.BranchingCondition;
|
||||
|
||||
class TestEntryPointTransformerForWholeClass extends TestEntryPointTransformer {
|
||||
private List<MethodReference> testMethods;
|
||||
|
||||
TestEntryPointTransformerForWholeClass(List<MethodReference> testMethods, String testClassName) {
|
||||
super(testClassName);
|
||||
this.testMethods = testMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
ValueEmitter testName = pe.var(1, String.class);
|
||||
|
||||
for (MethodReference testMethod : testMethods) {
|
||||
ValueEmitter isTest = testName.invokeSpecial("equals", boolean.class,
|
||||
pe.constant(testMethod.toString()).cast(Object.class));
|
||||
ForkEmitter fork = isTest.fork(BranchingCondition.NOT_EQUAL);
|
||||
pe.enter(pe.getProgram().createBasicBlock());
|
||||
fork.setThen(pe.getBlock());
|
||||
|
||||
generateSingleMethodLaunchProgram(testMethod, hierarchy, pe);
|
||||
pe.enter(pe.getProgram().createBasicBlock());
|
||||
fork.setElse(pe.getBlock());
|
||||
}
|
||||
|
||||
pe.construct(IllegalArgumentException.class, pe.constant("Invalid test name")).raise();
|
||||
|
||||
return pe.getProgram();
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ final class TestNativeEntryPoint {
|
|||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
TestEntryPoint.run();
|
||||
TestEntryPoint.run(args.length > 0 ? args[0] : null);
|
||||
new PrintStream(StdoutOutputStream.INSTANCE).println("SUCCESS");
|
||||
} catch (Throwable e) {
|
||||
PrintStream out = new PrintStream(StderrOutputStream.INSTANCE);
|
||||
|
|
|
@ -26,14 +26,16 @@ class TestRun {
|
|||
private String fileName;
|
||||
private RunKind kind;
|
||||
private TestRunCallback callback;
|
||||
private String argument;
|
||||
|
||||
TestRun(File baseDirectory, Method method, Description description, String fileName, RunKind kind,
|
||||
TestRunCallback callback) {
|
||||
String argument, TestRunCallback callback) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.method = method;
|
||||
this.description = description;
|
||||
this.fileName = fileName;
|
||||
this.kind = kind;
|
||||
this.argument = argument;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
|
@ -57,6 +59,10 @@ class TestRun {
|
|||
return kind;
|
||||
}
|
||||
|
||||
public String getArgument() {
|
||||
return argument;
|
||||
}
|
||||
|
||||
public TestRunCallback getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2020 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.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface WholeClassCompilation {
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
var $rt_decodeStack;
|
||||
|
||||
function runMain(stackDecoder, callback) {
|
||||
function runMain(argument, stackDecoder, callback) {
|
||||
$rt_decodeStack = stackDecoder;
|
||||
main([], function(result) {
|
||||
main(argument !== null ? [argument] : [], function(result) {
|
||||
var message = {};
|
||||
if (result instanceof Error) {
|
||||
makeErrorMessage(message, result);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="classTest.js"></script>
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
<script type="text/javascript">
|
||||
main([], function(result) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user