mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
JUnit: add support for TestNG annotations
This commit is contained in:
parent
e5c3d144e8
commit
71f87d79a5
|
@ -140,5 +140,10 @@ class DependencyClassSource implements ClassHolderSource {
|
|||
public boolean isStrict() {
|
||||
return strict;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submit(ClassHolder cls) {
|
||||
DependencyClassSource.this.submit(cls);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -58,13 +58,7 @@ class VirtualCallConsumer implements DependencyConsumer {
|
|||
knownTypes.set(type.index);
|
||||
|
||||
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("[")) {
|
||||
className = "java.lang.Object";
|
||||
}
|
||||
|
|
|
@ -28,4 +28,6 @@ public interface ClassHolderTransformerContext {
|
|||
boolean isObfuscated();
|
||||
|
||||
boolean isStrict();
|
||||
|
||||
void submit(ClassHolder cls);
|
||||
}
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -75,6 +75,7 @@
|
|||
<java-tests.version>11</java-tests.version>
|
||||
<rhino.version>1.7.11</rhino.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>
|
||||
<commons-cli.version>1.4</commons-cli.version>
|
||||
|
||||
|
@ -212,6 +213,11 @@
|
|||
<artifactId>rhino</artifactId>
|
||||
<version>${rhino.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>${testng.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
<artifactId>junit</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-tooling</artifactId>
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.io.OutputStreamWriter;
|
|||
import java.io.Writer;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -49,10 +50,6 @@ import java.util.function.Supplier;
|
|||
import java.util.stream.Stream;
|
||||
import junit.framework.TestCase;
|
||||
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.Runner;
|
||||
import org.junit.runner.manipulation.Filter;
|
||||
|
@ -74,11 +71,13 @@ import org.teavm.dependency.PreciseDependencyAnalyzer;
|
|||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||
import org.teavm.diagnostics.Problem;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.PreOptimizingClassHolderSource;
|
||||
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_AFTER = new MethodReference(JUNIT3_BASE_CLASS, "tearDown", ValueType.VOID);
|
||||
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 TESTNG_BEFORE = "org.testng.annotations.BeforeMethod";
|
||||
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 JS_RUNNER = "teavm.junit.js.runner";
|
||||
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) {
|
||||
if (!Modifier.isPublic(method.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TestCase.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return method.getName().startsWith("test") && method.getName().length() > 4
|
||||
&& Character.isUpperCase(method.getName().charAt(4));
|
||||
} else if (getClassAnnotation(method, TESTNG_TEST) != null) {
|
||||
return method.getName().startsWith("test_");
|
||||
} 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);
|
||||
notifier.fireTestStarted(description);
|
||||
|
||||
if (child.isAnnotationPresent(Ignore.class)) {
|
||||
if (isIgnored(child)) {
|
||||
notifier.fireTestIgnored(description);
|
||||
latch.countDown();
|
||||
return;
|
||||
|
@ -400,23 +411,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
boolean ran = false;
|
||||
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)) {
|
||||
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) {
|
||||
|
@ -459,7 +458,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
File outputPath = getOutputPathForClass();
|
||||
File outputPathForMethod = getOutputPath(child);
|
||||
MethodDescriptor descriptor = getDescriptor(child);
|
||||
MethodReference reference = new MethodReference(testClass.getName(), descriptor);
|
||||
MethodReference reference = new MethodReference(child.getDeclaringClass().getName(), descriptor);
|
||||
|
||||
File testFilePath = getOutputPath(child);
|
||||
testFilePath.mkdirs();
|
||||
|
@ -467,7 +466,8 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
Map<String, String> properties = new HashMap<>();
|
||||
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));
|
||||
runs.add(createTestRun(configuration, testPath, child, RunKind.JAVASCRIPT, reference.toString(),
|
||||
notifier, onSuccess));
|
||||
File htmlPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".html");
|
||||
properties.put("SCRIPT", "../" + testPath.getName());
|
||||
properties.put("IDENTIFIER", reference.toString());
|
||||
|
@ -480,12 +480,14 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
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));
|
||||
runs.add(createTestRun(configuration, testPath, child, RunKind.WASM, reference.toString(),
|
||||
notifier, onSuccess));
|
||||
}
|
||||
|
||||
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));
|
||||
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<>();
|
||||
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
|
||||
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) {
|
||||
runs.add(run);
|
||||
|
||||
|
@ -515,7 +517,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
|
||||
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) {
|
||||
runs.add(run);
|
||||
}
|
||||
|
@ -524,7 +526,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
||||
CompileResult compileResult = compileToWasm(singleTest(child), "test", configuration,
|
||||
outputPath);
|
||||
TestRun run = prepareRun(child, compileResult, notifier, RunKind.WASM, onSuccess);
|
||||
TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.WASM, onSuccess);
|
||||
if (run != null) {
|
||||
runs.add(run);
|
||||
}
|
||||
|
@ -539,119 +541,167 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
onSuccess.accept(true);
|
||||
}
|
||||
|
||||
private String[] getExpectedExceptions(MethodHolder method) {
|
||||
AnnotationHolder annot = method.getAnnotations().get(JUNIT4_TEST);
|
||||
if (annot == null) {
|
||||
return new String[0];
|
||||
}
|
||||
AnnotationValue expected = annot.getValue("expected");
|
||||
if (expected == null) {
|
||||
return new String[0];
|
||||
static String[] getExpectedExceptions(MethodReader method) {
|
||||
AnnotationReader annot = method.getAnnotations().get(JUNIT4_TEST);
|
||||
if (annot != null) {
|
||||
AnnotationValue expected = annot.getValue("expected");
|
||||
if (expected == null) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
ValueType result = expected.getJavaClass();
|
||||
return new String[] { ((ValueType.Object) result).getClassName() };
|
||||
}
|
||||
|
||||
ValueType result = expected.getJavaClass();
|
||||
return new String[] { ((ValueType.Object) result).getClassName() };
|
||||
annot = method.getAnnotations().get(TESTNG_TEST);
|
||||
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) {
|
||||
Description description = describeChild(child);
|
||||
Runner runner;
|
||||
private boolean runInJvm(Method testMethod, RunNotifier notifier, String[] expectedExceptions) {
|
||||
Description description = describeChild(testMethod);
|
||||
Object instance;
|
||||
try {
|
||||
instance = testClass.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
instance = testClass.getConstructor().newInstance();
|
||||
} 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));
|
||||
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 {
|
||||
runner = new JUnit3Runner(instance);
|
||||
((TestCase) instance).setName(child.getName());
|
||||
runner = new SimpleMethodRunner(instance, testMethod);
|
||||
}
|
||||
|
||||
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<>();
|
||||
Class<?> cls = instance.getClass();
|
||||
while (cls != null) {
|
||||
classes.add(cls);
|
||||
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);
|
||||
for (Class<?> c : classes) {
|
||||
for (Method method : c.getMethods()) {
|
||||
if (method.isAnnotationPresent(Before.class)) {
|
||||
try {
|
||||
method.invoke(instance);
|
||||
} catch (InvocationTargetException e) {
|
||||
notifier.fireTestFailure(new Failure(description, e.getTargetException()));
|
||||
} catch (IllegalAccessException e) {
|
||||
notifier.fireTestFailure(new Failure(description, e));
|
||||
}
|
||||
if (getAnnotation(method, JUNIT4_BEFORE) != null || getAnnotation(method, TESTNG_BEFORE) != null) {
|
||||
beforeMethods.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
boolean expectedCaught = false;
|
||||
try {
|
||||
runner.run();
|
||||
} catch (Throwable e) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
provider.setAccessible(true);
|
||||
data = provider.invoke(instance);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getTargetException();
|
||||
}
|
||||
|
||||
return new WithDataProviderRunner(runner, data, testMethod.getParameterTypes());
|
||||
}
|
||||
|
||||
interface Runner {
|
||||
void run() throws Throwable;
|
||||
void run(Object[] arguments) throws Throwable;
|
||||
}
|
||||
|
||||
static class JUnit4Runner implements Runner {
|
||||
static class SimpleMethodRunner implements Runner {
|
||||
Object instance;
|
||||
Method child;
|
||||
Method testMethod;
|
||||
|
||||
JUnit4Runner(Object instance, Method child) {
|
||||
SimpleMethodRunner(Object instance, Method testMethod) {
|
||||
this.instance = instance;
|
||||
this.child = child;
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
public void run(Object[] arguments) throws Throwable {
|
||||
try {
|
||||
child.invoke(instance);
|
||||
testMethod.invoke(instance, arguments);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getTargetException();
|
||||
}
|
||||
|
@ -659,20 +709,158 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
|
||||
static class JUnit3Runner implements Runner {
|
||||
Object instance;
|
||||
TestCase instance;
|
||||
Method testMethod;
|
||||
|
||||
JUnit3Runner(Object instance) {
|
||||
JUnit3Runner(TestCase instance, Method testMethod) {
|
||||
this.instance = instance;
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
((TestCase) instance).runBare();
|
||||
public void run(Object[] arguments) throws Throwable {
|
||||
instance.setName(testMethod.getName());
|
||||
instance.runBare();
|
||||
}
|
||||
}
|
||||
|
||||
private TestRun prepareRun(Method child, CompileResult result, RunNotifier notifier, RunKind kind,
|
||||
Consumer<Boolean> onComplete) {
|
||||
static class WithDataProviderRunner implements Runner {
|
||||
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);
|
||||
|
||||
if (!result.success) {
|
||||
|
@ -682,11 +870,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
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,
|
||||
Consumer<Boolean> onComplete) {
|
||||
private TestRun createTestRun(TeaVMTestConfiguration<?> configuration, File file, Method child, RunKind kind,
|
||||
String argument, RunNotifier notifier, Consumer<Boolean> onComplete) {
|
||||
Description description = describeChild(child);
|
||||
|
||||
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,
|
||||
argument, callback);
|
||||
return new TestRun(generateName(child.getName(), configuration), file.getParentFile(), child, description,
|
||||
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) {
|
||||
|
@ -854,6 +1050,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
vm.setProperties(properties);
|
||||
List<MethodReference> methodReferences = new ArrayList<>();
|
||||
for (Method method : methods) {
|
||||
if (isIgnored(method)) {
|
||||
continue;
|
||||
}
|
||||
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
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,
|
||||
Supplier<T> targetSupplier, String entryPoint, File path, String extension,
|
||||
CompilePostProcessor postBuild, boolean separateDir,
|
||||
|
@ -1096,7 +1320,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
Writer writer = new OutputStreamWriter(bufferedOutput)) {
|
||||
writer.write("[\n");
|
||||
boolean first = true;
|
||||
for (TestRun run : runsInCurrentClass) {
|
||||
for (TestRun run : runsInCurrentClass.toArray(new TestRun[0])) {
|
||||
if (!first) {
|
||||
writer.write(",\n");
|
||||
}
|
||||
|
@ -1114,6 +1338,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
writer.write(" \"argument\": ");
|
||||
writeJsonString(writer, run.getArgument());
|
||||
}
|
||||
writer.write(",\n");
|
||||
writer.write(" \"name\": ");
|
||||
writeJsonString(writer, run.getName());
|
||||
writer.write("\n }");
|
||||
}
|
||||
writer.write("\n]");
|
||||
|
|
|
@ -15,32 +15,46 @@
|
|||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class TestEntryPoint {
|
||||
private static Object testCase;
|
||||
|
||||
private TestEntryPoint() {
|
||||
}
|
||||
|
||||
public static void run(String name) throws Exception {
|
||||
before();
|
||||
try {
|
||||
launchTest(name);
|
||||
} finally {
|
||||
public static void run(String name) throws Throwable {
|
||||
List<Launcher> launchers = new ArrayList<>();
|
||||
testCase = createTestCase();
|
||||
launchers(name, launchers);
|
||||
for (Launcher launcher : launchers) {
|
||||
before();
|
||||
try {
|
||||
after();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
launcher.launch(testCase);
|
||||
} finally {
|
||||
try {
|
||||
after();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static native Object createTestCase();
|
||||
|
||||
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();
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
run(args.length == 1 ? args[0] : null);
|
||||
}
|
||||
|
||||
interface Launcher {
|
||||
void launch(Object testCase) throws Throwable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.JUNIT4_AFTER;
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
|
@ -34,12 +37,14 @@ import org.teavm.model.ClassHolderTransformerContext;
|
|||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.PhiEmitter;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
|
@ -47,6 +52,7 @@ import org.teavm.vm.spi.TeaVMPlugin;
|
|||
|
||||
abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||
private String testClassName;
|
||||
private int suffixGenerator;
|
||||
|
||||
TestEntryPointTransformer(String testClassName) {
|
||||
this.testClassName = testClassName;
|
||||
|
@ -60,18 +66,23 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
|
|||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
if (cls.getName().equals(TestEntryPoint.class.getName())) {
|
||||
suffixGenerator = 0;
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
switch (method.getName()) {
|
||||
case "launchTest":
|
||||
method.setProgram(generateLaunchProgram(method, context.getHierarchy()));
|
||||
case "createTestCase":
|
||||
generateCreateTestCaseProgram(method, context.getHierarchy());
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
break;
|
||||
case "launchers":
|
||||
generateLaunchProgram(method, context);
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
break;
|
||||
case "before":
|
||||
method.setProgram(generateBeforeProgram(method, context.getHierarchy()));
|
||||
generateBeforeProgram(method, context.getHierarchy());
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
break;
|
||||
case "after":
|
||||
method.setProgram(generateAfterProgram(method, context.getHierarchy()));
|
||||
generateAfterProgram(method, context.getHierarchy());
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
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);
|
||||
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);
|
||||
|
||||
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) {
|
||||
|
@ -97,11 +107,11 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
|
|||
Collections.reverse(classes);
|
||||
classes.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()));
|
||||
|
||||
pe.exit();
|
||||
return pe.getProgram();
|
||||
}
|
||||
|
||||
private Program generateAfterProgram(MethodHolder method, ClassHierarchy hierarchy) {
|
||||
|
@ -111,7 +121,8 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
|
|||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testClassName);
|
||||
classes.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()));
|
||||
|
||||
if (hierarchy.isSuperType(JUNIT3_BASE_CLASS, testClassName, false)) {
|
||||
|
@ -135,23 +146,181 @@ abstract class TestEntryPointTransformer implements ClassHolderTransformer, TeaV
|
|||
return result;
|
||||
}
|
||||
|
||||
protected abstract Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy);
|
||||
protected abstract void generateLaunchProgram(MethodHolder method, ClassHolderTransformerContext context);
|
||||
|
||||
protected final void generateSingleMethodLaunchProgram(MethodReference testMethod,
|
||||
ClassHierarchy hierarchy, ProgramEmitter pe) {
|
||||
pe.getField(TestEntryPoint.class, "testCase", Object.class)
|
||||
.cast(ValueType.object(testMethod.getClassName()))
|
||||
.invokeSpecial(testMethod);
|
||||
ClassHolderTransformerContext context, ProgramEmitter pe) {
|
||||
ClassHolder launcherClass = generateLauncherClass(testMethod, context.getHierarchy());
|
||||
context.submit(launcherClass);
|
||||
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);
|
||||
AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(JUNIT4_TEST);
|
||||
AnnotationValue throwsValue = testAnnotation != null ? testAnnotation.getValue("expected") : null;
|
||||
if (throwsValue != null) {
|
||||
String[] expectedExceptions = TeaVMTestRunner.getExpectedExceptions(testMethodReader);
|
||||
if (expectedExceptions.length != 0) {
|
||||
BasicBlock handler = pe.getProgram().createBasicBlock();
|
||||
TryCatchBlock tryCatch = new TryCatchBlock();
|
||||
tryCatch.setExceptionType(((ValueType.Object) throwsValue.getJavaClass()).getClassName());
|
||||
tryCatch.setHandler(handler);
|
||||
pe.getBlock().getTryCatchBlocks().add(tryCatch);
|
||||
|
||||
for (String exceptionType : expectedExceptions) {
|
||||
TryCatchBlock tryCatch = new TryCatchBlock();
|
||||
tryCatch.setExceptionType(exceptionType);
|
||||
tryCatch.setHandler(handler);
|
||||
pe.getBlock().getTryCatchBlocks().add(tryCatch);
|
||||
}
|
||||
|
||||
BasicBlock nextBlock = pe.getProgram().createBasicBlock();
|
||||
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.enter(handler);
|
||||
pe.exit();
|
||||
} else {
|
||||
pe.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
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 {
|
||||
|
@ -30,9 +29,8 @@ class TestEntryPointTransformerForSingleMethod extends TestEntryPointTransformer
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
generateSingleMethodLaunchProgram(testMethod, hierarchy, pe);
|
||||
return pe.getProgram();
|
||||
protected void generateLaunchProgram(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
generateSingleMethodLaunchProgram(testMethod, context, pe);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,9 @@
|
|||
package org.teavm.junit;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
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;
|
||||
|
@ -34,8 +33,8 @@ class TestEntryPointTransformerForWholeClass extends TestEntryPointTransformer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Program generateLaunchProgram(MethodHolder method, ClassHierarchy hierarchy) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, hierarchy);
|
||||
protected void generateLaunchProgram(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
ValueEmitter testName = pe.var(1, String.class);
|
||||
|
||||
for (MethodReference testMethod : testMethods) {
|
||||
|
@ -45,13 +44,11 @@ class TestEntryPointTransformerForWholeClass extends TestEntryPointTransformer {
|
|||
pe.enter(pe.getProgram().createBasicBlock());
|
||||
fork.setThen(pe.getBlock());
|
||||
|
||||
generateSingleMethodLaunchProgram(testMethod, hierarchy, pe);
|
||||
generateSingleMethodLaunchProgram(testMethod, context, pe);
|
||||
pe.enter(pe.getProgram().createBasicBlock());
|
||||
fork.setElse(pe.getBlock());
|
||||
}
|
||||
|
||||
pe.construct(IllegalArgumentException.class, pe.constant("Invalid test name")).raise();
|
||||
|
||||
return pe.getProgram();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Method;
|
|||
import org.junit.runner.Description;
|
||||
|
||||
class TestRun {
|
||||
private String name;
|
||||
private File baseDirectory;
|
||||
private Method method;
|
||||
private Description description;
|
||||
|
@ -28,8 +29,9 @@ class TestRun {
|
|||
private TestRunCallback callback;
|
||||
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) {
|
||||
this.name = name;
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.method = method;
|
||||
this.description = description;
|
||||
|
@ -39,6 +41,10 @@ class TestRun {
|
|||
this.callback = callback;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public File getBaseDirectory() {
|
||||
return baseDirectory;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user