mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
Support running JUnit3 tests and improve support of JUnit4
This commit is contained in:
parent
cd7a702c31
commit
6d2815bc5c
|
@ -15,7 +15,6 @@
|
|||
<option value="-Dteavm.build.all=false" />
|
||||
<option value="-Pwith-cli" />
|
||||
<option value="-Dmaven.javadoc.skip=true" />
|
||||
<option value="-Dmaven.source.skip=true" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="pomFileName" />
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>provided</scope>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.testing;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import org.junit.Test;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class JUnitTestAdapter implements TestAdapter {
|
||||
@Override
|
||||
public boolean acceptClass(Class<?> cls) {
|
||||
for (Method method : cls.getDeclaredMethods()) {
|
||||
for (Annotation annot : method.getAnnotations()) {
|
||||
if (annot.annotationType().getName().equals(Test.class.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptMethod(MethodReader method) {
|
||||
return method.getAnnotations().get(Test.class.getName()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> getExpectedExceptions(MethodReader method) {
|
||||
AnnotationReader annot = method.getAnnotations().get(Test.class.getName());
|
||||
AnnotationValue expectedAnnot = annot.getValue("expected");
|
||||
if (expectedAnnot != null) {
|
||||
String className = ((ValueType.Object) expectedAnnot.getJavaClass()).getClassName();
|
||||
return Collections.singletonList(className);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends TestRunner> getRunner(MethodReader method) {
|
||||
return SimpleTestRunner.class;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.testing;
|
||||
|
||||
public interface TestRunner {
|
||||
void run(TestLauncher launcher) throws Throwable;
|
||||
}
|
|
@ -17,11 +17,12 @@ package org.teavm.vm;
|
|||
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||
|
||||
public class TeaVMBuilder {
|
||||
TeaVMTarget target;
|
||||
ClassHolderSource classSource;
|
||||
ClassReaderSource classSource;
|
||||
ClassLoader classLoader;
|
||||
|
||||
public TeaVMBuilder(TeaVMTarget target) {
|
||||
|
@ -30,7 +31,7 @@ public class TeaVMBuilder {
|
|||
classSource = !isBootstrap() ? new ClasspathClassHolderSource(classLoader) : name -> null;
|
||||
}
|
||||
|
||||
public ClassHolderSource getClassSource() {
|
||||
public ClassReaderSource getClassSource() {
|
||||
return classSource;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,6 @@
|
|||
<dependency>
|
||||
<groupId>org.netbeans.html</groupId>
|
||||
<artifactId>net.java.html.boot</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.netbeans.html</groupId>
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.html4j.testing;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.testing.TestAdapter;
|
||||
import org.teavm.testing.TestRunner;
|
||||
|
||||
public class KOTestAdapter implements TestAdapter {
|
||||
static final String KO_TEST_CLASS = "org.netbeans.html.json.tck.KOTest";
|
||||
|
||||
@Override
|
||||
public boolean acceptClass(Class<?> cls) {
|
||||
for (Method method : cls.getDeclaredMethods()) {
|
||||
for (Annotation annot : method.getAnnotations()) {
|
||||
if (annot.annotationType().getName().equals(KO_TEST_CLASS)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptMethod(MethodReader method) {
|
||||
return method.getAnnotations().get(KO_TEST_CLASS) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> getExpectedExceptions(MethodReader method) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends TestRunner> getRunner(MethodReader method) {
|
||||
return KOTestRunner.class;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.html4j.testing;
|
||||
|
||||
import org.teavm.testing.TestLauncher;
|
||||
import org.teavm.testing.TestRunner;
|
||||
|
||||
public class KOTestRunner implements TestRunner {
|
||||
@Override
|
||||
public void run(TestLauncher launcher) throws Throwable {
|
||||
int repeatCount = 0;
|
||||
while (true) {
|
||||
try {
|
||||
launcher.launch();
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
if (++repeatCount == 10) {
|
||||
throw e;
|
||||
}
|
||||
Thread.sleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,6 +83,21 @@
|
|||
<version>0.7.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-util</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mozilla</groupId>
|
||||
<artifactId>rhino</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,11 +13,21 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.testing;
|
||||
package org.teavm.tests;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public abstract class JUnit3BaseTest extends TestCase {
|
||||
String a;
|
||||
String b;
|
||||
|
||||
public class SimpleTestRunner implements TestRunner {
|
||||
@Override
|
||||
public void run(TestLauncher launcher) throws Throwable {
|
||||
launcher.launch();
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
a = "start";
|
||||
}
|
||||
|
||||
public void testFoo() {
|
||||
assertEquals("start", a);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2014 Alexey Andreev.
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,16 +13,20 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.testing;
|
||||
package org.teavm.tests;
|
||||
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
public interface TestAdapter {
|
||||
boolean acceptClass(Class<?> cls);
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class JUnit3DerivedTest extends JUnit3BaseTest {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
b = "derived";
|
||||
}
|
||||
|
||||
boolean acceptMethod(MethodReader method);
|
||||
|
||||
Iterable<String> getExpectedExceptions(MethodReader method);
|
||||
|
||||
Class<? extends TestRunner> getRunner(MethodReader method);
|
||||
public void testBar() {
|
||||
assertEquals("derived", b);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2017 Jaroslav Tulach.
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,17 +13,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.html4j.testing;
|
||||
package org.teavm.tests;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jaroslav Tulach
|
||||
*/
|
||||
public class KOTestAdapterTest {
|
||||
public abstract class JUnitBaseTest {
|
||||
String a;
|
||||
String b;
|
||||
|
||||
@Before
|
||||
public void startBase() {
|
||||
a = "start";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyKOTestClassNameIsCorrect() throws ClassNotFoundException {
|
||||
Class.forName(KOTestAdapter.KO_TEST_CLASS);
|
||||
public void foo() {
|
||||
assertEquals("start", a);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,8 +13,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.testing;
|
||||
package org.teavm.tests;
|
||||
|
||||
public interface TestLauncher {
|
||||
void launch() throws Throwable;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class JUnitDerivedTest extends JUnitBaseTest {
|
||||
@Before
|
||||
public void startDerived() {
|
||||
b = "derived";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bar() {
|
||||
assertEquals("derived", b);
|
||||
}
|
||||
}
|
|
@ -15,17 +15,21 @@
|
|||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -41,7 +45,12 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.function.Consumer;
|
||||
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;
|
||||
|
@ -54,17 +63,20 @@ import org.teavm.backend.c.CTarget;
|
|||
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||
import org.teavm.backend.wasm.WasmTarget;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.debugging.information.DebugInformation;
|
||||
import org.teavm.debugging.information.DebugInformationBuilder;
|
||||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||
import org.teavm.diagnostics.Problem;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
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.MethodReference;
|
||||
import org.teavm.model.PreOptimizingClassHolderSource;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||
import org.teavm.testing.JUnitTestAdapter;
|
||||
import org.teavm.testing.TestAdapter;
|
||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||
import org.teavm.vm.DirectoryBuildTarget;
|
||||
import org.teavm.vm.TeaVM;
|
||||
|
@ -72,6 +84,12 @@ import org.teavm.vm.TeaVMBuilder;
|
|||
import org.teavm.vm.TeaVMTarget;
|
||||
|
||||
public class TeaVMTestRunner extends Runner implements Filterable {
|
||||
static final String JUNIT3_BASE_CLASS = "junit.framework.TestCase";
|
||||
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_BEFORE = "org.junit.Before";
|
||||
static final String JUNIT4_AFTER = "org.junit.After";
|
||||
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.js.threads";
|
||||
|
@ -85,12 +103,11 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
private static final int stopTimeout = 15000;
|
||||
private Class<?> testClass;
|
||||
private ClassHolder classHolder;
|
||||
private ClassHolderSource classSource;
|
||||
private ClassLoader classLoader;
|
||||
private Description suiteDescription;
|
||||
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
|
||||
private File outputDir;
|
||||
private TestAdapter testAdapter = new JUnitTestAdapter();
|
||||
private Map<Method, Description> descriptions = new HashMap<>();
|
||||
private static Map<RunKind, RunnerKindInfo> runners = new HashMap<>();
|
||||
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
|
||||
|
@ -124,8 +141,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
public TeaVMTestRunner(Class<?> testClass) throws InitializationError {
|
||||
this.testClass = testClass;
|
||||
classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||
ClassHolderSource classSource = getClassSource(classLoader);
|
||||
classHolder = classSource.get(testClass.getName());
|
||||
classSource = getClassSource(classLoader);
|
||||
String outputPath = System.getProperty(PATH_PARAM);
|
||||
if (outputPath != null) {
|
||||
outputDir = new File(outputPath);
|
||||
|
@ -166,7 +182,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
if (suiteDescription == null) {
|
||||
suiteDescription = Description.createSuiteDescription(testClass);
|
||||
for (Method child : getFilteredChildren()) {
|
||||
suiteDescription.getChildren().add(describeChild(child));
|
||||
suiteDescription.addChild(describeChild(child));
|
||||
}
|
||||
}
|
||||
return suiteDescription;
|
||||
|
@ -196,15 +212,29 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
private List<Method> getChildren() {
|
||||
List<Method> children = new ArrayList<>();
|
||||
for (Method method : testClass.getDeclaredMethods()) {
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
if (testAdapter.acceptMethod(methodHolder)) {
|
||||
children.add(method);
|
||||
Class<?> cls = testClass;
|
||||
Set<String> foundMethods = new HashSet<>();
|
||||
while (cls != Object.class && !cls.getName().equals(JUNIT3_BASE_CLASS)) {
|
||||
for (Method method : cls.getDeclaredMethods()) {
|
||||
if (foundMethods.add(method.getName()) && isTestMethod(method)) {
|
||||
children.add(method);
|
||||
}
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
private boolean isTestMethod(Method method) {
|
||||
if (TestCase.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return method.getName().startsWith("test") && method.getName().length() > 4
|
||||
&& Character.isUpperCase(method.getName().charAt(4));
|
||||
} else {
|
||||
return method.isAnnotationPresent(Test.class);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Method> getFilteredChildren() {
|
||||
if (filteredChildren == null) {
|
||||
filteredChildren = getChildren();
|
||||
|
@ -218,31 +248,39 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
|
||||
private void runChild(Method child, RunNotifier notifier) {
|
||||
notifier.fireTestStarted(describeChild(child));
|
||||
Description description = describeChild(child);
|
||||
notifier.fireTestStarted(description);
|
||||
|
||||
if (child.isAnnotationPresent(Ignore.class)) {
|
||||
notifier.fireTestIgnored(description);
|
||||
latch.countDown();
|
||||
return;
|
||||
}
|
||||
|
||||
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 : testAdapter.getExpectedExceptions(methodHolder)) {
|
||||
for (String exceptionName : getExpectedExceptions(methodHolder)) {
|
||||
try {
|
||||
expectedExceptions.add(Class.forName(exceptionName, false, classLoader));
|
||||
} catch (ClassNotFoundException e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
notifier.fireTestFinished(describeChild(child));
|
||||
notifier.fireTestFailure(new Failure(description, e));
|
||||
notifier.fireTestFinished(description);
|
||||
latch.countDown();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!child.isAnnotationPresent(SkipJVM.class)
|
||||
&& !child.getDeclaringClass().isAnnotationPresent(SkipJVM.class)) {
|
||||
&& !testClass.isAnnotationPresent(SkipJVM.class)) {
|
||||
ran = true;
|
||||
success = runInJvm(child, notifier, expectedExceptions);
|
||||
}
|
||||
|
||||
Description description = describeChild(child);
|
||||
|
||||
if (success && outputDir != null) {
|
||||
int[] configurationIndex = new int[] { 0 };
|
||||
List<Consumer<Boolean>> onSuccess = new ArrayList<>();
|
||||
|
@ -302,42 +340,136 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
ValueType result = expected.getJavaClass();
|
||||
return new String[] { ((ValueType.Object) result).getClassName() };
|
||||
}
|
||||
|
||||
private boolean runInJvm(Method child, RunNotifier notifier, Set<Class<?>> expectedExceptions) {
|
||||
Description description = describeChild(child);
|
||||
Runner runner;
|
||||
Object instance;
|
||||
try {
|
||||
instance = testClass.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
notifier.fireTestFailure(new Failure(description, e));
|
||||
return false;
|
||||
}
|
||||
if (!TestCase.class.isAssignableFrom(testClass)) {
|
||||
runner = new JUnit4Runner(instance, child);
|
||||
} else {
|
||||
runner = new JUnit3Runner(instance);
|
||||
((TestCase) instance).setName(child.getName());
|
||||
}
|
||||
|
||||
boolean expectedCaught = false;
|
||||
try {
|
||||
child.invoke(instance);
|
||||
} catch (IllegalAccessException e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
return false;
|
||||
} catch (InvocationTargetException e) {
|
||||
boolean wasExpected = false;
|
||||
for (Class<?> expected : expectedExceptions) {
|
||||
if (expected.isInstance(e.getTargetException())) {
|
||||
expectedCaught = true;
|
||||
wasExpected = true;
|
||||
List<Class<?>> classes = new ArrayList<>();
|
||||
Class<?> cls = instance.getClass();
|
||||
while (cls != null) {
|
||||
classes.add(cls);
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
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 (!wasExpected) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e.getTargetException()));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Runner {
|
||||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
class JUnit4Runner implements Runner {
|
||||
Object instance;
|
||||
Method child;
|
||||
|
||||
JUnit4Runner(Object instance, Method child) {
|
||||
this.instance = instance;
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
if (!expectedCaught && !expectedExceptions.isEmpty()) {
|
||||
notifier.fireTestAssumptionFailed(new Failure(describeChild(child),
|
||||
new AssertionError("Expected exception was not thrown")));
|
||||
return false;
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
try {
|
||||
child.invoke(instance);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getTargetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JUnit3Runner implements Runner {
|
||||
Object instance;
|
||||
|
||||
JUnit3Runner(Object instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
return true;
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
((TestCase) instance).runBare();
|
||||
}
|
||||
}
|
||||
|
||||
private TestRun compile(Method child, RunNotifier notifier, RunKind kind,
|
||||
|
@ -416,7 +548,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
private File getOutputPath(Method method) {
|
||||
File path = outputDir;
|
||||
path = new File(path, method.getDeclaringClass().getName().replace('.', '/'));
|
||||
path = new File(path, testClass.getName().replace('.', '/'));
|
||||
path = new File(path, method.getName());
|
||||
path.mkdirs();
|
||||
return path;
|
||||
|
@ -430,27 +562,46 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
|
||||
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
|
||||
File path) {
|
||||
return compileTest(method, configuration, JavaScriptTarget::new, vm -> {
|
||||
vm.entryPoint(TestEntryPoint.class.getName());
|
||||
}, path, ".js");
|
||||
DebugInformationBuilder debugEmitter = new DebugInformationBuilder();
|
||||
Supplier<JavaScriptTarget> targetSupplier = () -> {
|
||||
JavaScriptTarget target = new JavaScriptTarget();
|
||||
target.setDebugEmitter(debugEmitter);
|
||||
return target;
|
||||
};
|
||||
CompilePostProcessor postBuild = (vm, file) -> {
|
||||
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
||||
File sourceMapsFile = new File(file.getPath() + ".map");
|
||||
try {
|
||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file, true), UTF_8)) {
|
||||
writer.write("\n//# sourceMappingURL=");
|
||||
writer.write(sourceMapsFile.getName());
|
||||
}
|
||||
|
||||
try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream(sourceMapsFile), UTF_8)) {
|
||||
debugInfo.writeAsSourceMaps(sourceMapsOut, "src", file.getPath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
return compileTest(method, configuration, targetSupplier, TestEntryPoint.class.getName(), path, ".js",
|
||||
postBuild);
|
||||
}
|
||||
|
||||
private CompileResult compileToC(Method method, TeaVMTestConfiguration<CTarget> configuration,
|
||||
File path) {
|
||||
return compileTest(method, configuration, CTarget::new, vm -> {
|
||||
vm.entryPoint(TestNativeEntryPoint.class.getName());
|
||||
}, path, ".c");
|
||||
return compileTest(method, configuration, CTarget::new, TestNativeEntryPoint.class.getName(), path, ".c", null);
|
||||
}
|
||||
|
||||
private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration,
|
||||
File path) {
|
||||
return compileTest(method, configuration, WasmTarget::new, vm -> {
|
||||
vm.entryPoint(TestNativeEntryPoint.class.getName());
|
||||
}, path, ".wasm");
|
||||
return compileTest(method, configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path,
|
||||
".wasm", null);
|
||||
}
|
||||
|
||||
private <T extends TeaVMTarget> CompileResult compileTest(Method method, TeaVMTestConfiguration<T> configuration,
|
||||
Supplier<T> targetSupplier, Consumer<TeaVM> preBuild, File path, String extension) {
|
||||
Supplier<T> targetSupplier, String entryPoint, File path, String extension,
|
||||
CompilePostProcessor postBuild) {
|
||||
CompileResult result = new CompileResult();
|
||||
|
||||
StringBuilder simpleName = new StringBuilder();
|
||||
|
@ -464,10 +615,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
result.file = outputFile;
|
||||
|
||||
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||
ClassHolderSource classSource = getClassSource(classLoader);
|
||||
|
||||
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
Class<?> runnerType = testAdapter.getRunner(methodHolder);
|
||||
|
||||
T target = targetSupplier.get();
|
||||
configuration.apply(target);
|
||||
|
@ -486,18 +636,26 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
vm.installPlugins();
|
||||
|
||||
new TestExceptionPlugin().install(vm);
|
||||
new TestEntryPointTransformer(runnerType.getName(), methodHolder.getReference()).install(vm);
|
||||
new TestEntryPointTransformer(methodHolder.getReference(), testClass.getName()).install(vm);
|
||||
|
||||
preBuild.accept(vm);
|
||||
vm.entryPoint(entryPoint);
|
||||
vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName());
|
||||
if (!vm.getProblemProvider().getProblems().isEmpty()) {
|
||||
result.success = false;
|
||||
result.errorMessage = buildErrorMessage(vm);
|
||||
} else {
|
||||
if (postBuild != null) {
|
||||
postBuild.process(vm, outputFile);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
interface CompilePostProcessor {
|
||||
void process(TeaVM vm, File targetFile);
|
||||
}
|
||||
|
||||
private List<TeaVMTestConfiguration<JavaScriptTarget>> getJavaScriptConfigurations() {
|
||||
List<TeaVMTestConfiguration<JavaScriptTarget>> configurations = new ArrayList<>();
|
||||
if (Boolean.parseBoolean(System.getProperty(JS_ENABLED, "true"))) {
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.teavm.testing.TestRunner;
|
||||
|
||||
final class TestEntryPoint {
|
||||
private static Object testCase;
|
||||
|
||||
|
@ -24,14 +22,23 @@ final class TestEntryPoint {
|
|||
}
|
||||
|
||||
public static void run() throws Throwable {
|
||||
createRunner().run(() -> launchTest());
|
||||
before();
|
||||
try {
|
||||
launchTest();
|
||||
} finally {
|
||||
try {
|
||||
after();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static native TestRunner createRunner();
|
||||
private static native void before();
|
||||
|
||||
private static native void launchTest();
|
||||
|
||||
private static native boolean isExpectedException(Class<?> cls);
|
||||
private static native void after();
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
run();
|
||||
|
|
|
@ -15,13 +15,22 @@
|
|||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.teavm.junit.TeaVMTestRunner.JUNIT3_AFTER;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodHolder;
|
||||
|
@ -36,12 +45,12 @@ import org.teavm.vm.spi.TeaVMHost;
|
|||
import org.teavm.vm.spi.TeaVMPlugin;
|
||||
|
||||
class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||
private String runnerClassName;
|
||||
private MethodReference testMethod;
|
||||
private String testClassName;
|
||||
|
||||
TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) {
|
||||
this.runnerClassName = runnerClassName;
|
||||
TestEntryPointTransformer(MethodReference testMethod, String testClassName) {
|
||||
this.testMethod = testMethod;
|
||||
this.testClassName = testClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,38 +62,89 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
|||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
if (cls.getName().equals(TestEntryPoint.class.getName())) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getName().equals("createRunner")) {
|
||||
method.setProgram(generateRunnerProgram(method, innerSource));
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
} else if (method.getName().equals("launchTest")) {
|
||||
method.setProgram(generateLaunchProgram(method, innerSource));
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
switch (method.getName()) {
|
||||
case "launchTest":
|
||||
method.setProgram(generateLaunchProgram(method, innerSource));
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
break;
|
||||
case "before":
|
||||
method.setProgram(generateBeforeProgram(method, innerSource));
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
break;
|
||||
case "after":
|
||||
method.setProgram(generateAfterProgram(method, innerSource));
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Program generateRunnerProgram(MethodHolder method, ClassReaderSource innerSource) {
|
||||
private Program generateBeforeProgram(MethodHolder method, ClassReaderSource innerSource) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, innerSource);
|
||||
pe.construct(runnerClassName).returnValue();
|
||||
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 (innerSource.isSuperType(JUNIT3_BASE_CLASS, testMethod.getClassName()).orElse(false)) {
|
||||
testCaseVar.cast(ValueType.object(JUNIT3_BASE_CLASS)).invokeVirtual(JUNIT3_BEFORE);
|
||||
}
|
||||
|
||||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testMethod.getClassName());
|
||||
Collections.reverse(classes);
|
||||
classes.stream()
|
||||
.flatMap(cls -> cls.getMethods().stream())
|
||||
.filter(m -> m.getAnnotations().get(JUNIT4_BEFORE) != null)
|
||||
.forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference()));
|
||||
|
||||
pe.exit();
|
||||
return pe.getProgram();
|
||||
}
|
||||
|
||||
private Program generateAfterProgram(MethodHolder method, ClassReaderSource innerSource) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, innerSource);
|
||||
ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class);
|
||||
|
||||
List<ClassReader> classes = collectSuperClasses(pe.getClassSource(), testMethod.getClassName());
|
||||
classes.stream()
|
||||
.flatMap(cls -> cls.getMethods().stream())
|
||||
.filter(m -> m.getAnnotations().get(JUNIT4_AFTER) != null)
|
||||
.forEach(m -> testCaseVar.cast(ValueType.object(m.getOwnerName())).invokeVirtual(m.getReference()));
|
||||
|
||||
if (innerSource.isSuperType(JUNIT3_BASE_CLASS, testMethod.getClassName()).orElse(false)) {
|
||||
testCaseVar.cast(ValueType.object(JUNIT3_BASE_CLASS)).invokeVirtual(JUNIT3_AFTER);
|
||||
}
|
||||
|
||||
pe.exit();
|
||||
return pe.getProgram();
|
||||
}
|
||||
|
||||
private List<ClassReader> collectSuperClasses(ClassReaderSource classSource, String className) {
|
||||
List<ClassReader> result = new ArrayList<>();
|
||||
while (className != null && !className.equals(JUNIT3_BASE_CLASS)) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null) {
|
||||
break;
|
||||
}
|
||||
result.add(cls);
|
||||
className = cls.getParent();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Program generateLaunchProgram(MethodHolder method, ClassReaderSource innerSource) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, innerSource);
|
||||
ValueEmitter testCaseVar = pe.getField(TestEntryPoint.class, "testCase", Object.class);
|
||||
pe.when(testCaseVar.isNull())
|
||||
.thenDo(() -> {
|
||||
pe.setField(TestEntryPoint.class, "testCase",
|
||||
pe.construct(testMethod.getClassName()).cast(Object.class));
|
||||
});
|
||||
pe.getField(TestEntryPoint.class, "testCase", Object.class)
|
||||
.cast(ValueType.object(testMethod.getClassName()))
|
||||
.invokeSpecial(testMethod);
|
||||
|
||||
MethodReader testMethodReader = innerSource.resolve(testMethod);
|
||||
AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(Test.class.getName());
|
||||
AnnotationValue throwsValue = testAnnotation.getValue("expected");
|
||||
AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(JUNIT4_TEST);
|
||||
AnnotationValue throwsValue = testAnnotation != null ? testAnnotation.getValue("expected") : null;
|
||||
if (throwsValue != null) {
|
||||
BasicBlock handler = pe.getProgram().createBasicBlock();
|
||||
TryCatchBlock tryCatch = new TryCatchBlock();
|
||||
|
|
Loading…
Reference in New Issue
Block a user