JUnit: add support for TestNG annotations

This commit is contained in:
Alexey Andreev 2021-03-16 20:18:58 +03:00
parent e5c3d144e8
commit 71f87d79a5
11 changed files with 593 additions and 173 deletions

View File

@ -140,5 +140,10 @@ class DependencyClassSource implements ClassHolderSource {
public boolean isStrict() { public boolean isStrict() {
return strict; return strict;
} }
@Override
public void submit(ClassHolder cls) {
DependencyClassSource.this.submit(cls);
}
}; };
} }

View File

@ -58,13 +58,7 @@ class VirtualCallConsumer implements DependencyConsumer {
knownTypes.set(type.index); knownTypes.set(type.index);
String className = type.getName(); String className = type.getName();
/*
if (DependencyAnalyzer.shouldLog) {
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
+ "Target class is " + className);
}
*/
if (className.startsWith("[")) { if (className.startsWith("[")) {
className = "java.lang.Object"; className = "java.lang.Object";
} }

View File

@ -28,4 +28,6 @@ public interface ClassHolderTransformerContext {
boolean isObfuscated(); boolean isObfuscated();
boolean isStrict(); boolean isStrict();
void submit(ClassHolder cls);
} }

View File

@ -75,6 +75,7 @@
<java-tests.version>11</java-tests.version> <java-tests.version>11</java-tests.version>
<rhino.version>1.7.11</rhino.version> <rhino.version>1.7.11</rhino.version>
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version> <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
<testng.version>7.1.0</testng.version>
<junit.version>4.13.2</junit.version> <junit.version>4.13.2</junit.version>
<commons-cli.version>1.4</commons-cli.version> <commons-cli.version>1.4</commons-cli.version>
@ -212,6 +213,11 @@
<artifactId>rhino</artifactId> <artifactId>rhino</artifactId>
<version>${rhino.version}</version> <version>${rhino.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
</dependency>
<dependency> <dependency>
<groupId>commons-cli</groupId> <groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId> <artifactId>commons-cli</artifactId>

View File

@ -35,6 +35,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-tooling</artifactId> <artifactId>teavm-tooling</artifactId>

View File

@ -28,6 +28,7 @@ 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.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -49,10 +50,6 @@ import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.Description; import org.junit.runner.Description;
import org.junit.runner.Runner; import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.Filter;
@ -74,11 +71,13 @@ import org.teavm.dependency.PreciseDependencyAnalyzer;
import org.teavm.diagnostics.DefaultProblemTextConsumer; import org.teavm.diagnostics.DefaultProblemTextConsumer;
import org.teavm.diagnostics.Problem; import org.teavm.diagnostics.Problem;
import org.teavm.model.AnnotationHolder; import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue; import org.teavm.model.AnnotationValue;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.PreOptimizingClassHolderSource; import org.teavm.model.PreOptimizingClassHolderSource;
import org.teavm.model.ReferenceCache; import org.teavm.model.ReferenceCache;
@ -96,8 +95,14 @@ public class TeaVMTestRunner extends Runner implements Filterable {
static final MethodReference JUNIT3_BEFORE = new MethodReference(JUNIT3_BASE_CLASS, "setUp", ValueType.VOID); static final MethodReference JUNIT3_BEFORE = new MethodReference(JUNIT3_BASE_CLASS, "setUp", ValueType.VOID);
static final MethodReference JUNIT3_AFTER = new MethodReference(JUNIT3_BASE_CLASS, "tearDown", ValueType.VOID); static final MethodReference JUNIT3_AFTER = new MethodReference(JUNIT3_BASE_CLASS, "tearDown", ValueType.VOID);
static final String JUNIT4_TEST = "org.junit.Test"; static final String JUNIT4_TEST = "org.junit.Test";
static final String JUNIT4_IGNORE = "org.junit.Ignore";
static final String TESTNG_TEST = "org.testng.annotations.Test";
static final String TESTNG_IGNORE = "org.testng.annotations.Ignore";
static final String JUNIT4_BEFORE = "org.junit.Before"; static final String JUNIT4_BEFORE = "org.junit.Before";
static final String TESTNG_BEFORE = "org.testng.annotations.BeforeMethod";
static final String JUNIT4_AFTER = "org.junit.After"; static final String JUNIT4_AFTER = "org.junit.After";
static final String TESTNG_AFTER = "org.testng.annotations.AfterMethod";
static final String TESTNG_PROVIDER = "org.testng.annotations.DataProvider";
private static final String PATH_PARAM = "teavm.junit.target"; private static final String PATH_PARAM = "teavm.junit.target";
private static final String JS_RUNNER = "teavm.junit.js.runner"; private static final String JS_RUNNER = "teavm.junit.js.runner";
private static final String THREAD_COUNT = "teavm.junit.threads"; private static final String THREAD_COUNT = "teavm.junit.threads";
@ -335,11 +340,17 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
private boolean isTestMethod(Method method) { private boolean isTestMethod(Method method) {
if (!Modifier.isPublic(method.getModifiers())) {
return false;
}
if (TestCase.class.isAssignableFrom(method.getDeclaringClass())) { if (TestCase.class.isAssignableFrom(method.getDeclaringClass())) {
return method.getName().startsWith("test") && method.getName().length() > 4 return method.getName().startsWith("test") && method.getName().length() > 4
&& Character.isUpperCase(method.getName().charAt(4)); && Character.isUpperCase(method.getName().charAt(4));
} else if (getClassAnnotation(method, TESTNG_TEST) != null) {
return method.getName().startsWith("test_");
} else { } else {
return method.isAnnotationPresent(Test.class); return getAnnotation(method, JUNIT4_TEST) != null || getAnnotation(method, TESTNG_TEST) != null;
} }
} }
@ -391,7 +402,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
Description description = describeChild(child); Description description = describeChild(child);
notifier.fireTestStarted(description); notifier.fireTestStarted(description);
if (child.isAnnotationPresent(Ignore.class)) { if (isIgnored(child)) {
notifier.fireTestIgnored(description); notifier.fireTestIgnored(description);
latch.countDown(); latch.countDown();
return; return;
@ -400,23 +411,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
boolean ran = false; boolean ran = false;
boolean success = true; boolean success = true;
ClassHolder classHolder = classSource.get(child.getDeclaringClass().getName());
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(child));
Set<Class<?>> expectedExceptions = new HashSet<>();
for (String exceptionName : getExpectedExceptions(methodHolder)) {
try {
expectedExceptions.add(Class.forName(exceptionName, false, classLoader));
} catch (ClassNotFoundException e) {
notifier.fireTestFailure(new Failure(description, e));
notifier.fireTestFinished(description);
latch.countDown();
return;
}
}
if (!child.isAnnotationPresent(SkipJVM.class) && !testClass.isAnnotationPresent(SkipJVM.class)) { if (!child.isAnnotationPresent(SkipJVM.class) && !testClass.isAnnotationPresent(SkipJVM.class)) {
ran = true; ran = true;
success = runInJvm(child, notifier, expectedExceptions); ClassHolder classHolder = classSource.get(child.getDeclaringClass().getName());
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(child));
success = runInJvm(child, notifier, getExpectedExceptions(methodHolder));
} }
if (success && outputDir != null) { if (success && outputDir != null) {
@ -459,7 +458,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
File outputPath = getOutputPathForClass(); File outputPath = getOutputPathForClass();
File outputPathForMethod = getOutputPath(child); File outputPathForMethod = getOutputPath(child);
MethodDescriptor descriptor = getDescriptor(child); MethodDescriptor descriptor = getDescriptor(child);
MethodReference reference = new MethodReference(testClass.getName(), descriptor); MethodReference reference = new MethodReference(child.getDeclaringClass().getName(), descriptor);
File testFilePath = getOutputPath(child); File testFilePath = getOutputPath(child);
testFilePath.mkdirs(); testFilePath.mkdirs();
@ -467,7 +466,8 @@ public class TeaVMTestRunner extends Runner implements Filterable {
Map<String, String> properties = new HashMap<>(); Map<String, String> properties = new HashMap<>();
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) { for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".js"); File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".js");
runs.add(createTestRun(testPath, child, RunKind.JAVASCRIPT, reference.toString(), notifier, onSuccess)); runs.add(createTestRun(configuration, testPath, child, RunKind.JAVASCRIPT, reference.toString(),
notifier, onSuccess));
File htmlPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".html"); File htmlPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".html");
properties.put("SCRIPT", "../" + testPath.getName()); properties.put("SCRIPT", "../" + testPath.getName());
properties.put("IDENTIFIER", reference.toString()); properties.put("IDENTIFIER", reference.toString());
@ -480,12 +480,14 @@ public class TeaVMTestRunner extends Runner implements Filterable {
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) { for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".wasm"); File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".wasm");
runs.add(createTestRun(testPath, child, RunKind.WASM, reference.toString(), notifier, onSuccess)); runs.add(createTestRun(configuration, testPath, child, RunKind.WASM, reference.toString(),
notifier, onSuccess));
} }
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) { for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), true, ".c"); File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), true, ".c");
runs.add(createTestRun(testPath, child, RunKind.C, reference.toString(), notifier, onSuccess)); runs.add(createTestRun(configuration, testPath, child, RunKind.C, reference.toString(),
notifier, onSuccess));
} }
} }
@ -496,7 +498,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
Map<String, String> properties = new HashMap<>(); Map<String, String> properties = new HashMap<>();
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) { for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
CompileResult compileResult = compileToJs(singleTest(child), "test", configuration, outputPath); CompileResult compileResult = compileToJs(singleTest(child), "test", configuration, outputPath);
TestRun run = prepareRun(child, compileResult, notifier, RunKind.JAVASCRIPT, onSuccess); TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.JAVASCRIPT, onSuccess);
if (run != null) { if (run != null) {
runs.add(run); runs.add(run);
@ -515,7 +517,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) { for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
CompileResult compileResult = compileToC(singleTest(child), "test", configuration, outputPath); CompileResult compileResult = compileToC(singleTest(child), "test", configuration, outputPath);
TestRun run = prepareRun(child, compileResult, notifier, RunKind.C, onSuccess); TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.C, onSuccess);
if (run != null) { if (run != null) {
runs.add(run); runs.add(run);
} }
@ -524,7 +526,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) { for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
CompileResult compileResult = compileToWasm(singleTest(child), "test", configuration, CompileResult compileResult = compileToWasm(singleTest(child), "test", configuration,
outputPath); outputPath);
TestRun run = prepareRun(child, compileResult, notifier, RunKind.WASM, onSuccess); TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.WASM, onSuccess);
if (run != null) { if (run != null) {
runs.add(run); runs.add(run);
} }
@ -539,119 +541,167 @@ public class TeaVMTestRunner extends Runner implements Filterable {
onSuccess.accept(true); onSuccess.accept(true);
} }
private String[] getExpectedExceptions(MethodHolder method) { static String[] getExpectedExceptions(MethodReader method) {
AnnotationHolder annot = method.getAnnotations().get(JUNIT4_TEST); AnnotationReader annot = method.getAnnotations().get(JUNIT4_TEST);
if (annot == null) { if (annot != null) {
return new String[0]; AnnotationValue expected = annot.getValue("expected");
} if (expected == null) {
AnnotationValue expected = annot.getValue("expected"); return new String[0];
if (expected == null) { }
return new String[0];
ValueType result = expected.getJavaClass();
return new String[] { ((ValueType.Object) result).getClassName() };
} }
ValueType result = expected.getJavaClass(); annot = method.getAnnotations().get(TESTNG_TEST);
return new String[] { ((ValueType.Object) result).getClassName() }; if (annot != null) {
AnnotationValue expected = annot.getValue("expectedExceptions");
if (expected == null) {
return new String[0];
}
List<AnnotationValue> list = expected.getList();
String[] result = new String[list.size()];
for (int i = 0; i < list.size(); ++i) {
result[i] = ((ValueType.Object) list.get(i).getJavaClass()).getClassName();
}
return result;
}
return new String[0];
} }
private boolean runInJvm(Method child, RunNotifier notifier, Set<Class<?>> expectedExceptions) { private boolean runInJvm(Method testMethod, RunNotifier notifier, String[] expectedExceptions) {
Description description = describeChild(child); Description description = describeChild(testMethod);
Runner runner;
Object instance; Object instance;
try { try {
instance = testClass.newInstance(); instance = testClass.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
notifier.fireTestFailure(new Failure(description, e));
return false;
} catch (InvocationTargetException e) {
notifier.fireTestFailure(new Failure(description, e.getTargetException()));
return false;
}
Runner runner;
try {
runner = prepareJvmRunner(instance, testMethod, expectedExceptions);
} catch (Throwable e) {
notifier.fireTestFailure(new Failure(description, e)); notifier.fireTestFailure(new Failure(description, e));
return false; return false;
} }
if (!TestCase.class.isAssignableFrom(testClass)) {
runner = new JUnit4Runner(instance, child); try {
runner.run(new Object[0]);
return true;
} catch (Throwable e) {
notifier.fireTestFailure(new Failure(description, e));
return false;
}
}
private Runner prepareJvmRunner(Object instance, Method testMethod, String[] expectedExceptions) throws Throwable {
Runner runner;
if (TestCase.class.isAssignableFrom(testClass)) {
runner = new JUnit3Runner((TestCase) instance, testMethod);
} else { } else {
runner = new JUnit3Runner(instance); runner = new SimpleMethodRunner(instance, testMethod);
((TestCase) instance).setName(child.getName());
} }
if (expectedExceptions.length > 0) {
runner = new WithExpectedExceptionRunner(runner, expectedExceptions);
}
runner = wrapWithBeforeAndAfter(runner, instance);
runner = wrapWithDataProvider(runner, instance, testMethod);
return runner;
}
private Runner wrapWithBeforeAndAfter(Runner runner, Object instance) {
List<Class<?>> classes = new ArrayList<>(); List<Class<?>> classes = new ArrayList<>();
Class<?> cls = instance.getClass(); Class<?> cls = instance.getClass();
while (cls != null) { while (cls != null) {
classes.add(cls); classes.add(cls);
cls = cls.getSuperclass(); cls = cls.getSuperclass();
} }
List<Method> afterMethods = new ArrayList<>();
for (Class<?> c : classes) {
for (Method method : c.getMethods()) {
if (getAnnotation(method, JUNIT4_AFTER) != null || getAnnotation(method, TESTNG_AFTER) != null) {
afterMethods.add(method);
}
}
}
List<Method> beforeMethods = new ArrayList<>();
Collections.reverse(classes); Collections.reverse(classes);
for (Class<?> c : classes) { for (Class<?> c : classes) {
for (Method method : c.getMethods()) { for (Method method : c.getMethods()) {
if (method.isAnnotationPresent(Before.class)) { if (getAnnotation(method, JUNIT4_BEFORE) != null || getAnnotation(method, TESTNG_BEFORE) != null) {
try { beforeMethods.add(method);
method.invoke(instance);
} catch (InvocationTargetException e) {
notifier.fireTestFailure(new Failure(description, e.getTargetException()));
} catch (IllegalAccessException e) {
notifier.fireTestFailure(new Failure(description, e));
}
} }
} }
} }
if (beforeMethods.isEmpty() && afterMethods.isEmpty()) {
return runner;
}
return new WithBeforeAndAfterRunner(runner, instance, beforeMethods.toArray(new Method[0]),
afterMethods.toArray(new Method[0]));
}
private Runner wrapWithDataProvider(Runner runner, Object instance, Method testMethod) throws Throwable {
AnnotationHolder annot = getAnnotation(testMethod, TESTNG_TEST);
if (annot == null) {
return runner;
}
String providerName = annot.getValue("dataProvider").getString();
if (providerName.isEmpty()) {
return runner;
}
Method provider = null;
for (Method method : testMethod.getDeclaringClass().getDeclaredMethods()) {
AnnotationHolder providerAnnot = getAnnotation(method, TESTNG_PROVIDER);
if (providerAnnot != null && providerAnnot.getValue("name").getString().equals(providerName)) {
provider = method;
break;
}
}
Object data;
try { try {
boolean expectedCaught = false; provider.setAccessible(true);
try { data = provider.invoke(instance);
runner.run(); } catch (InvocationTargetException e) {
} catch (Throwable e) { throw e.getTargetException();
boolean wasExpected = false;
for (Class<?> expected : expectedExceptions) {
if (expected.isInstance(e)) {
expectedCaught = true;
wasExpected = true;
}
}
if (!wasExpected) {
notifier.fireTestFailure(new Failure(description, e));
return false;
}
return false;
}
if (!expectedCaught && !expectedExceptions.isEmpty()) {
notifier.fireTestAssumptionFailed(new Failure(description,
new AssertionError("Expected exception was not thrown")));
return false;
}
return true;
} finally {
Collections.reverse(classes);
for (Class<?> c : classes) {
for (Method method : c.getMethods()) {
if (method.isAnnotationPresent(After.class)) {
try {
method.invoke(instance);
} catch (InvocationTargetException e) {
notifier.fireTestFailure(new Failure(description, e.getTargetException()));
} catch (IllegalAccessException e) {
notifier.fireTestFailure(new Failure(description, e));
}
}
}
}
} }
return new WithDataProviderRunner(runner, data, testMethod.getParameterTypes());
} }
interface Runner { interface Runner {
void run() throws Throwable; void run(Object[] arguments) throws Throwable;
} }
static class JUnit4Runner implements Runner { static class SimpleMethodRunner implements Runner {
Object instance; Object instance;
Method child; Method testMethod;
JUnit4Runner(Object instance, Method child) { SimpleMethodRunner(Object instance, Method testMethod) {
this.instance = instance; this.instance = instance;
this.child = child; this.testMethod = testMethod;
} }
@Override @Override
public void run() throws Throwable { public void run(Object[] arguments) throws Throwable {
try { try {
child.invoke(instance); testMethod.invoke(instance, arguments);
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw e.getTargetException(); throw e.getTargetException();
} }
@ -659,20 +709,158 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
static class JUnit3Runner implements Runner { static class JUnit3Runner implements Runner {
Object instance; TestCase instance;
Method testMethod;
JUnit3Runner(Object instance) { JUnit3Runner(TestCase instance, Method testMethod) {
this.instance = instance; this.instance = instance;
this.testMethod = testMethod;
} }
@Override @Override
public void run() throws Throwable { public void run(Object[] arguments) throws Throwable {
((TestCase) instance).runBare(); instance.setName(testMethod.getName());
instance.runBare();
} }
} }
private TestRun prepareRun(Method child, CompileResult result, RunNotifier notifier, RunKind kind, static class WithDataProviderRunner implements Runner {
Consumer<Boolean> onComplete) { Runner underlyingRunner;
Object data;
Class<?>[] types;
WithDataProviderRunner(Runner underlyingRunner, Object data, Class<?>[] types) {
this.underlyingRunner = underlyingRunner;
this.data = data;
this.types = types;
}
@Override
public void run(Object[] arguments) throws Throwable {
if (arguments.length > 0) {
throw new IllegalArgumentException("Expected 0 arguments");
}
if (data instanceof Iterator) {
runWithIteratorData((Iterator<?>) data);
} else {
runWithArrayData((Object[][]) data);
}
}
private void runWithArrayData(Object[][] data) throws Throwable {
for (int i = 0; i < data.length; ++i) {
runWithDataRow(data[i]);
}
}
private void runWithIteratorData(Iterator<?> data) throws Throwable {
while (data.hasNext()) {
runWithDataRow((Object[]) data.next());
}
}
private void runWithDataRow(Object[] dataRow) throws Throwable {
Object[] args = dataRow.clone();
for (int j = 0; j < args.length; ++j) {
args[j] = convert(args[j], types[j]);
}
underlyingRunner.run(args);
}
private Object convert(Object value, Class<?> type) {
if (type == byte.class) {
value = ((Number) value).byteValue();
} else if (type == short.class) {
value = ((Number) value).shortValue();
} else if (type == int.class) {
value = ((Number) value).intValue();
} else if (type == long.class) {
value = ((Number) value).longValue();
} else if (type == float.class) {
value = ((Number) value).floatValue();
} else if (type == double.class) {
value = ((Number) value).doubleValue();
}
return value;
}
}
static class WithExpectedExceptionRunner implements Runner {
private Runner underlyingRunner;
private String[] expectedExceptions;
WithExpectedExceptionRunner(Runner underlyingRunner, String[] expectedExceptions) {
this.underlyingRunner = underlyingRunner;
this.expectedExceptions = expectedExceptions;
}
@Override
public void run(Object[] arguments) throws Throwable {
boolean caught = false;
try {
underlyingRunner.run(arguments);
} catch (Exception e) {
for (String expected : expectedExceptions) {
if (isSubtype(e.getClass(), expected)) {
caught = true;
break;
}
}
if (!caught) {
throw e;
}
}
if (!caught) {
throw new AssertionError("Expected exception not thrown");
}
}
private boolean isSubtype(Class<?> cls, String superType) {
while (cls != Throwable.class) {
if (cls.getName().equals(superType)) {
return true;
}
cls = cls.getSuperclass();
}
return false;
}
}
static class WithBeforeAndAfterRunner implements Runner {
private Runner underlyingRunner;
private Object instance;
private Method[] beforeMethods;
private Method[] afterMethods;
WithBeforeAndAfterRunner(Runner underlyingRunner, Object instance, Method[] beforeMethods,
Method[] afterMethods) {
this.underlyingRunner = underlyingRunner;
this.instance = instance;
this.beforeMethods = beforeMethods;
this.afterMethods = afterMethods;
}
@Override
public void run(Object[] arguments) throws Throwable {
for (Method method : beforeMethods) {
try {
method.invoke(instance);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
try {
underlyingRunner.run(arguments);
} finally {
for (Method method : afterMethods) {
method.invoke(instance);
}
}
}
}
private TestRun prepareRun(TeaVMTestConfiguration<?> configuration, Method child, CompileResult result,
RunNotifier notifier, RunKind kind, Consumer<Boolean> onComplete) {
Description description = describeChild(child); Description description = describeChild(child);
if (!result.success) { if (!result.success) {
@ -682,11 +870,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
return null; return null;
} }
return createTestRun(result.file, child, kind, null, notifier, onComplete); return createTestRun(configuration, result.file, child, kind, null, notifier, onComplete);
} }
private TestRun createTestRun(File file, Method child, RunKind kind, String argument, RunNotifier notifier, private TestRun createTestRun(TeaVMTestConfiguration<?> configuration, File file, Method child, RunKind kind,
Consumer<Boolean> onComplete) { String argument, RunNotifier notifier, Consumer<Boolean> onComplete) {
Description description = describeChild(child); Description description = describeChild(child);
TestRunCallback callback = new TestRunCallback() { TestRunCallback callback = new TestRunCallback() {
@ -702,8 +890,16 @@ public class TeaVMTestRunner extends Runner implements Filterable {
} }
}; };
return new TestRun(file.getParentFile(), child, description, file.getName(), kind, return new TestRun(generateName(child.getName(), configuration), file.getParentFile(), child, description,
argument, callback); file.getName(), kind, argument, callback);
}
private String generateName(String baseName, TeaVMTestConfiguration<?> configuration) {
String suffix = configuration.getSuffix();
if (!suffix.isEmpty()) {
baseName = baseName + " (" + suffix + ")";
}
return baseName;
} }
private Failure createFailure(Description description, CompileResult result) { private Failure createFailure(Description description, CompileResult result) {
@ -854,6 +1050,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
vm.setProperties(properties); vm.setProperties(properties);
List<MethodReference> methodReferences = new ArrayList<>(); List<MethodReference> methodReferences = new ArrayList<>();
for (Method method : methods) { for (Method method : methods) {
if (isIgnored(method)) {
continue;
}
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName()); ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method)); MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
methodReferences.add(methodHolder.getReference()); methodReferences.add(methodHolder.getReference());
@ -862,6 +1061,31 @@ public class TeaVMTestRunner extends Runner implements Filterable {
}; };
} }
private boolean isIgnored(Method method) {
return getAnnotation(method, JUNIT4_IGNORE) != null || getAnnotation(method, TESTNG_IGNORE) != null;
}
private AnnotationHolder getAnnotation(Method method, String name) {
ClassHolder cls = classSource.get(method.getDeclaringClass().getName());
if (cls == null) {
return null;
}
MethodDescriptor descriptor = getDescriptor(method);
MethodHolder methodHolder = cls.getMethod(descriptor);
if (methodHolder == null) {
return null;
}
return methodHolder.getAnnotations().get(name);
}
private AnnotationHolder getClassAnnotation(Method method, String name) {
ClassHolder cls = classSource.get(method.getDeclaringClass().getName());
if (cls == null) {
return null;
}
return cls.getAnnotations().get(name);
}
private <T extends TeaVMTarget> CompileResult compile(TeaVMTestConfiguration<T> configuration, private <T extends TeaVMTarget> CompileResult compile(TeaVMTestConfiguration<T> configuration,
Supplier<T> targetSupplier, String entryPoint, File path, String extension, Supplier<T> targetSupplier, String entryPoint, File path, String extension,
CompilePostProcessor postBuild, boolean separateDir, CompilePostProcessor postBuild, boolean separateDir,
@ -1096,7 +1320,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
Writer writer = new OutputStreamWriter(bufferedOutput)) { Writer writer = new OutputStreamWriter(bufferedOutput)) {
writer.write("[\n"); writer.write("[\n");
boolean first = true; boolean first = true;
for (TestRun run : runsInCurrentClass) { for (TestRun run : runsInCurrentClass.toArray(new TestRun[0])) {
if (!first) { if (!first) {
writer.write(",\n"); writer.write(",\n");
} }
@ -1114,6 +1338,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
writer.write(" \"argument\": "); writer.write(" \"argument\": ");
writeJsonString(writer, run.getArgument()); writeJsonString(writer, run.getArgument());
} }
writer.write(",\n");
writer.write(" \"name\": ");
writeJsonString(writer, run.getName());
writer.write("\n }"); writer.write("\n }");
} }
writer.write("\n]"); writer.write("\n]");

View File

@ -15,32 +15,46 @@
*/ */
package org.teavm.junit; package org.teavm.junit;
import java.util.ArrayList;
import java.util.List;
final class TestEntryPoint { final class TestEntryPoint {
private static Object testCase; private static Object testCase;
private TestEntryPoint() { private TestEntryPoint() {
} }
public static void run(String name) throws Exception { public static void run(String name) throws Throwable {
before(); List<Launcher> launchers = new ArrayList<>();
try { testCase = createTestCase();
launchTest(name); launchers(name, launchers);
} finally { for (Launcher launcher : launchers) {
before();
try { try {
after(); launcher.launch(testCase);
} catch (Throwable e) { } finally {
e.printStackTrace(); try {
after();
} catch (Throwable e) {
e.printStackTrace();
}
} }
} }
} }
private static native Object createTestCase();
private static native void before(); private static native void before();
private static native void launchTest(String name) throws Exception; private static native void launchers(String name, List<Launcher> result) throws Throwable;
private static native void after(); private static native void after();
public static void main(String[] args) throws Throwable { public static void main(String[] args) throws Throwable {
run(args.length == 1 ? args[0] : null); run(args.length == 1 ? args[0] : null);
} }
interface Launcher {
void launch(Object testCase) throws Throwable;
}
} }

View File

@ -20,7 +20,10 @@ import static org.teavm.junit.TeaVMTestRunner.JUNIT3_BASE_CLASS;
import static org.teavm.junit.TeaVMTestRunner.JUNIT3_BEFORE; import static org.teavm.junit.TeaVMTestRunner.JUNIT3_BEFORE;
import static org.teavm.junit.TeaVMTestRunner.JUNIT4_AFTER; import static org.teavm.junit.TeaVMTestRunner.JUNIT4_AFTER;
import static org.teavm.junit.TeaVMTestRunner.JUNIT4_BEFORE; import static org.teavm.junit.TeaVMTestRunner.JUNIT4_BEFORE;
import static org.teavm.junit.TeaVMTestRunner.JUNIT4_TEST; import static org.teavm.junit.TeaVMTestRunner.TESTNG_AFTER;
import static org.teavm.junit.TeaVMTestRunner.TESTNG_BEFORE;
import static org.teavm.junit.TeaVMTestRunner.TESTNG_PROVIDER;
import static org.teavm.junit.TeaVMTestRunner.TESTNG_TEST;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -34,12 +37,14 @@ import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock; import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.emit.PhiEmitter;
import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.emit.ValueEmitter; import org.teavm.model.emit.ValueEmitter;
import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMHost;
@ -47,6 +52,7 @@ import org.teavm.vm.spi.TeaVMPlugin;
abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin { abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
private String testClassName; private String testClassName;
private int suffixGenerator;
TestEntryPointTransformer(String testClassName) { TestEntryPointTransformer(String testClassName) {
this.testClassName = testClassName; this.testClassName = testClassName;
@ -60,18 +66,23 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
@Override @Override
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
if (cls.getName().equals(TestEntryPoint.class.getName())) { if (cls.getName().equals(TestEntryPoint.class.getName())) {
suffixGenerator = 0;
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
switch (method.getName()) { switch (method.getName()) {
case "launchTest": case "createTestCase":
method.setProgram(generateLaunchProgram(method, context.getHierarchy())); generateCreateTestCaseProgram(method, context.getHierarchy());
method.getModifiers().remove(ElementModifier.NATIVE);
break;
case "launchers":
generateLaunchProgram(method, context);
method.getModifiers().remove(ElementModifier.NATIVE); method.getModifiers().remove(ElementModifier.NATIVE);
break; break;
case "before": case "before":
method.setProgram(generateBeforeProgram(method, context.getHierarchy())); generateBeforeProgram(method, context.getHierarchy());
method.getModifiers().remove(ElementModifier.NATIVE); method.getModifiers().remove(ElementModifier.NATIVE);
break; break;
case "after": case "after":
method.setProgram(generateAfterProgram(method, context.getHierarchy())); generateAfterProgram(method, context.getHierarchy());
method.getModifiers().remove(ElementModifier.NATIVE); method.getModifiers().remove(ElementModifier.NATIVE);
break; break;
} }
@ -79,14 +90,13 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
} }
} }
private Program generateBeforeProgram(MethodHolder method, ClassHierarchy hierarchy) { private void generateCreateTestCaseProgram(MethodHolder method, ClassHierarchy hierarchy) {
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
pe.construct(testClassName).cast(Object.class).returnValue();
}
private void generateBeforeProgram(MethodHolder method, ClassHierarchy hierarchy) {
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy); ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
ValueEmitter testCaseInitVar = pe.getField(TestEntryPoint.class, "testCase", Object.class);
pe.when(testCaseInitVar.isNull())
.thenDo(() -> {
pe.setField(TestEntryPoint.class, "testCase",
pe.construct(testClassName).cast(Object.class));
});
ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class); ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class);
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) { if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) {
@ -97,11 +107,11 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
Collections.reverse(classes); Collections.reverse(classes);
classes.stream() classes.stream()
.flatMap(cls -> cls.getMethods().stream()) .flatMap(cls -> cls.getMethods().stream())
.filter(m -> m.getAnnotations().get(JUNIT4_BEFORE) != null) .filter(m -> m.getAnnotations().get(JUNIT4_BEFORE) != null
|| m.getAnnotations().get(TESTNG_BEFORE) != null)
.forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference())); .forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference()));
pe.exit(); pe.exit();
return pe.getProgram();
} }
private Program generateAfterProgram(MethodHolder method, ClassHierarchy hierarchy) { private Program generateAfterProgram(MethodHolder method, ClassHierarchy hierarchy) {
@ -111,7 +121,8 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testClassName); List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testClassName);
classes.stream() classes.stream()
.flatMap(cls -> cls.getMethods().stream()) .flatMap(cls -> cls.getMethods().stream())
.filter(m -> m.getAnnotations().get(JUNIT4_AFTER) != null) .filter(m -> m.getAnnotations().get(JUNIT4_AFTER) != null
|| m.getAnnotations().get(TESTNG_AFTER) != null)
.forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference())); .forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference()));
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) { if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) {
@ -135,23 +146,181 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
return result; return result;
} }
protected abstract Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy); protected abstract void generateLaunchProgram(MethodHolder method, ClassHolderTransformerContext context);
protected final void generateSingleMethodLaunchProgram(MethodReference testMethod, protected final void generateSingleMethodLaunchProgram(MethodReference testMethod,
ClassHierarchy hierarchy, ProgramEmitter pe) { ClassHolderTransformerContext context, ProgramEmitter pe) {
pe.getField(TestEntryPoint.class, "testCase", Object.class) ClassHolder launcherClass = generateLauncherClass(testMethod, context.getHierarchy());
.cast(ValueType.object(testMethod.getClassName())) context.submit(launcherClass);
.invokeSpecial(testMethod); ValueEmitter list = pe.var(2, List.class);
MethodReader testMethodReader = context.getHierarchy().getClassSource().resolve(testMethod);
AnnotationReader testNgAnnot = testMethodReader.getAnnotations().get(TESTNG_TEST);
if (testNgAnnot != null) {
AnnotationValue dataProviderValue = testNgAnnot.getValue("dataProvider");
if (dataProviderValue != null) {
generateAddLaunchersWithProvider(testMethodReader, context.getHierarchy(), pe, list,
dataProviderValue.getString(), launcherClass.getName());
return;
}
}
list.invokeVirtual("add", boolean.class, pe.construct(launcherClass.getName()).cast(Object.class));
pe.exit();
}
private void generateAddLaunchersWithProvider(MethodReader testMethodReader, ClassHierarchy hierarchy,
ProgramEmitter pe, ValueEmitter list, String providerName, String launcherClassName) {
ClassReader owningClass = hierarchy.getClassSource().get(testMethodReader.getOwnerName());
MethodReader providerMethod = null;
for (MethodReader method : owningClass.getMethods()) {
AnnotationReader annot = method.getAnnotations().get(TESTNG_PROVIDER);
if (annot != null && annot.getValue("name").getString().equals(providerName)) {
providerMethod = method;
break;
}
}
ValueEmitter data = pe.getField(TestEntryPoint.class, "testCase", Object.class)
.cast(ValueType.object(testMethodReader.getOwnerName()))
.invokeSpecial(providerMethod.getReference());
if (data.getType() instanceof ValueType.Array) {
generateAddLaunchersWithProviderArray(testMethodReader, pe, list, data, launcherClassName);
} else {
generateAddLaunchersWithProviderIterator(testMethodReader, pe, list, data, launcherClassName);
}
}
private void generateAddLaunchersWithProviderArray(MethodReader testMethodReader, ProgramEmitter pe,
ValueEmitter list, ValueEmitter data, String launcherClassName) {
ValueEmitter size = data.arrayLength();
BasicBlock loopHead = pe.getProgram().createBasicBlock();
BasicBlock loopBody = pe.getProgram().createBasicBlock();
BasicBlock loopExit = pe.getProgram().createBasicBlock();
PhiEmitter index = pe.phi(int.class, loopHead);
pe.constant(0).propagateTo(index);
pe.jump(loopHead);
pe.enter(loopHead);
pe.when(index.getValue().isLessThan(size))
.thenDo(() -> pe.jump(loopBody))
.elseDo(() -> pe.jump(loopExit));
pe.enter(loopBody);
ValueEmitter dataRow = data.getElement(index.getValue());
generateAddLauncherWithData(testMethodReader, pe, list, dataRow, launcherClassName);
index.getValue().add(1).propagateTo(index);
pe.jump(loopHead);
pe.enter(loopExit);
pe.exit();
}
private void generateAddLaunchersWithProviderIterator(MethodReader testMethodReader, ProgramEmitter pe,
ValueEmitter list, ValueEmitter data, String launcherClassName) {
BasicBlock loopHead = pe.getProgram().createBasicBlock();
BasicBlock loopBody = pe.getProgram().createBasicBlock();
BasicBlock loopExit = pe.getProgram().createBasicBlock();
pe.jump(loopHead);
pe.enter(loopHead);
pe.when(data.invokeVirtual("hasNext", boolean.class).isTrue())
.thenDo(() -> pe.jump(loopBody))
.elseDo(() -> pe.jump(loopExit));
pe.enter(loopBody);
ValueEmitter dataRow = data.invokeVirtual("next", Object.class).cast(Object[].class);
generateAddLauncherWithData(testMethodReader, pe, list, dataRow, launcherClassName);
pe.jump(loopHead);
pe.enter(loopExit);
pe.exit();
}
private void generateAddLauncherWithData(MethodReader testMethodReader, ProgramEmitter pe, ValueEmitter list,
ValueEmitter dataRow, String launcherClassName) {
List<ValueEmitter> arguments = new ArrayList<>();
for (int i = 0; i < testMethodReader.parameterCount(); ++i) {
ValueType type = testMethodReader.parameterType(i);
arguments.add(convertArgument(dataRow.getElement(i), type));
}
list.invokeVirtual("add", boolean.class, pe.construct(launcherClassName,
arguments.toArray(new ValueEmitter[0])).cast(Object.class));
}
private ValueEmitter convertArgument(ValueEmitter value, ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
return value.cast(Boolean.class).invokeVirtual("booleanValue", boolean.class);
case CHARACTER:
return value.cast(Character.class).invokeVirtual("charValue", char.class);
case BYTE:
return value.cast(Number.class).invokeVirtual("byteValue", byte.class);
case SHORT:
return value.cast(Number.class).invokeVirtual("shortValue", byte.class);
case INTEGER:
return value.cast(Number.class).invokeVirtual("intValue", int.class);
case LONG:
return value.cast(Number.class).invokeVirtual("longValue", long.class);
case FLOAT:
return value.cast(Number.class).invokeVirtual("floatValue", float.class);
case DOUBLE:
return value.cast(Number.class).invokeVirtual("doubleValue", double.class);
}
}
return value.cast(type);
}
private ClassHolder generateLauncherClass(MethodReference testMethod, ClassHierarchy hierarchy) {
ClassHolder cls = new ClassHolder(TestEntryPoint.Launcher.class.getName() + "Impl" + suffixGenerator++);
cls.setParent("java.lang.Object");
cls.getInterfaces().add(TestEntryPoint.Launcher.class.getName());
MethodHolder constructor = new MethodHolder("<init>", testMethod.getSignature());
cls.addMethod(constructor);
ProgramEmitter pe = ProgramEmitter.create(constructor, hierarchy);
pe.invoke(Object.class, "<init>", void.class);
ValueEmitter self = pe.var(0, ValueType.object(cls.getName()));
for (int i = 0; i < testMethod.parameterCount(); ++i) {
FieldHolder paramField = new FieldHolder("param_" + i);
paramField.setType(testMethod.parameterType(i));
cls.addField(paramField);
self.setField(paramField.getName(), pe.var(i + 1, testMethod.parameterType(i)));
}
pe.exit();
MethodHolder launchMethod = new MethodHolder("launch", ValueType.parse(Object.class), ValueType.VOID);
cls.addMethod(launchMethod);
pe = ProgramEmitter.create(launchMethod, hierarchy);
List<ValueEmitter> arguments = new ArrayList<>();
self = pe.var(0, ValueType.object(cls.getName()));
for (int i = 0; i < testMethod.parameterCount(); ++i) {
arguments.add(self.getField("param_" + i, testMethod.parameterType(i)));
}
generateRunMethodOnce(testMethod, hierarchy, pe, pe.var(1, Object.class), arguments);
pe.exit();
return cls;
}
private void generateRunMethodOnce(MethodReference testMethod, ClassHierarchy hierarchy, ProgramEmitter pe,
ValueEmitter testCase, List<ValueEmitter> arguments) {
testCase.cast(ValueType.object(testMethod.getClassName()))
.invokeSpecial(testMethod, arguments.toArray(new ValueEmitter[0]));
MethodReader testMethodReader = hierarchy.getClassSource().resolve(testMethod); MethodReader testMethodReader = hierarchy.getClassSource().resolve(testMethod);
AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(JUNIT4_TEST); String[] expectedExceptions = TeaVMTestRunner.getExpectedExceptions(testMethodReader);
AnnotationValue throwsValue = testAnnotation != null ? testAnnotation.getValue("expected") : null; if (expectedExceptions.length != 0) {
if (throwsValue != null) {
BasicBlock handler = pe.getProgram().createBasicBlock(); BasicBlock handler = pe.getProgram().createBasicBlock();
TryCatchBlock tryCatch = new TryCatchBlock();
tryCatch.setExceptionType(((ValueType.Object) throwsValue.getJavaClass()).getClassName()); for (String exceptionType : expectedExceptions) {
tryCatch.setHandler(handler); TryCatchBlock tryCatch = new TryCatchBlock();
pe.getBlock().getTryCatchBlocks().add(tryCatch); tryCatch.setExceptionType(exceptionType);
tryCatch.setHandler(handler);
pe.getBlock().getTryCatchBlocks().add(tryCatch);
}
BasicBlock nextBlock = pe.getProgram().createBasicBlock(); BasicBlock nextBlock = pe.getProgram().createBasicBlock();
pe.jump(nextBlock); pe.jump(nextBlock);
@ -159,9 +328,6 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
pe.construct(AssertionError.class, pe.constant("Expected exception not thrown")).raise(); pe.construct(AssertionError.class, pe.constant("Expected exception not thrown")).raise();
pe.enter(handler); pe.enter(handler);
pe.exit();
} else {
pe.exit();
} }
} }
} }

View File

@ -15,10 +15,9 @@
*/ */
package org.teavm.junit; package org.teavm.junit;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.emit.ProgramEmitter;
class TestEntryPointTransformerForSingleMethod extends TestEntryPointTransformer { class TestEntryPointTransformerForSingleMethod extends TestEntryPointTransformer {
@ -30,9 +29,8 @@ class TestEntryPointTransformerForSingleMethod extends TestEntryPointTransformer
} }
@Override @Override
protected Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) { protected void generateLaunchProgram(MethodHolder method, ClassHolderTransformerContext context) {
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy); ProgramEmitter pe = ProgramEmitter.create(method, context.getHierarchy());
generateSingleMethodLaunchProgram(testMethod, hierarchy, pe); generateSingleMethodLaunchProgram(testMethod, context, pe);
return pe.getProgram();
} }
} }

View File

@ -16,10 +16,9 @@
package org.teavm.junit; package org.teavm.junit;
import java.util.List; import java.util.List;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.emit.ForkEmitter; import org.teavm.model.emit.ForkEmitter;
import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.emit.ValueEmitter; import org.teavm.model.emit.ValueEmitter;
@ -34,8 +33,8 @@ class TestEntryPointTransformerForWholeClass extends TestEntryPointTransformer {
} }
@Override @Override
protected Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) { protected void generateLaunchProgram(MethodHolder method, ClassHolderTransformerContext context) {
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy); ProgramEmitter pe = ProgramEmitter.create(method, context.getHierarchy());
ValueEmitter testName = pe.var(1, String.class); ValueEmitter testName = pe.var(1, String.class);
for (MethodReference testMethod : testMethods) { for (MethodReference testMethod : testMethods) {
@ -45,13 +44,11 @@ class TestEntryPointTransformerForWholeClass extends TestEntryPointTransformer {
pe.enter(pe.getProgram().createBasicBlock()); pe.enter(pe.getProgram().createBasicBlock());
fork.setThen(pe.getBlock()); fork.setThen(pe.getBlock());
generateSingleMethodLaunchProgram(testMethod, hierarchy, pe); generateSingleMethodLaunchProgram(testMethod, context, pe);
pe.enter(pe.getProgram().createBasicBlock()); pe.enter(pe.getProgram().createBasicBlock());
fork.setElse(pe.getBlock()); fork.setElse(pe.getBlock());
} }
pe.construct(IllegalArgumentException.class, pe.constant("Invalid test name")).raise(); pe.construct(IllegalArgumentException.class, pe.constant("Invalid test name")).raise();
return pe.getProgram();
} }
} }

View File

@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import org.junit.runner.Description; import org.junit.runner.Description;
class TestRun { class TestRun {
private String name;
private File baseDirectory; private File baseDirectory;
private Method method; private Method method;
private Description description; private Description description;
@ -28,8 +29,9 @@ class TestRun {
private TestRunCallback callback; private TestRunCallback callback;
private String argument; private String argument;
TestRun(File baseDirectory, Method method, Description description, String fileName, RunKind kind, TestRun(String name, File baseDirectory, Method method, Description description, String fileName, RunKind kind,
String argument, TestRunCallback callback) { String argument, TestRunCallback callback) {
this.name = name;
this.baseDirectory = baseDirectory; this.baseDirectory = baseDirectory;
this.method = method; this.method = method;
this.description = description; this.description = description;
@ -39,6 +41,10 @@ class TestRun {
this.callback = callback; this.callback = callback;
} }
public String getName() {
return name;
}
public File getBaseDirectory() { public File getBaseDirectory() {
return baseDirectory; return baseDirectory;
} }