mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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="-Dteavm.build.all=false" />
|
||||||
<option value="-Pwith-cli" />
|
<option value="-Pwith-cli" />
|
||||||
<option value="-Dmaven.javadoc.skip=true" />
|
<option value="-Dmaven.javadoc.skip=true" />
|
||||||
<option value="-Dmaven.source.skip=true" />
|
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
<option name="pomFileName" />
|
<option name="pomFileName" />
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.teavm</groupId>
|
<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.interop.PlatformMarker;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||||
|
|
||||||
public class TeaVMBuilder {
|
public class TeaVMBuilder {
|
||||||
TeaVMTarget target;
|
TeaVMTarget target;
|
||||||
ClassHolderSource classSource;
|
ClassReaderSource classSource;
|
||||||
ClassLoader classLoader;
|
ClassLoader classLoader;
|
||||||
|
|
||||||
public TeaVMBuilder(TeaVMTarget target) {
|
public TeaVMBuilder(TeaVMTarget target) {
|
||||||
|
@ -30,7 +31,7 @@ public class TeaVMBuilder {
|
||||||
classSource = !isBootstrap() ? new ClasspathClassHolderSource(classLoader) : name -> null;
|
classSource = !isBootstrap() ? new ClasspathClassHolderSource(classLoader) : name -> null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassHolderSource getClassSource() {
|
public ClassReaderSource getClassSource() {
|
||||||
return classSource;
|
return classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,6 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.netbeans.html</groupId>
|
<groupId>org.netbeans.html</groupId>
|
||||||
<artifactId>net.java.html.boot</artifactId>
|
<artifactId>net.java.html.boot</artifactId>
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.ow2.asm</groupId>
|
|
||||||
<artifactId>asm</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.netbeans.html</groupId>
|
<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>
|
<version>0.7.3</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Alexey Andreev.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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
|
@Override
|
||||||
public void run(TestLauncher launcher) throws Throwable {
|
protected void setUp() throws Exception {
|
||||||
launcher.launch();
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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 {
|
@RunWith(TeaVMTestRunner.class)
|
||||||
boolean acceptClass(Class<?> cls);
|
public class JUnit3DerivedTest extends JUnit3BaseTest {
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
b = "derived";
|
||||||
|
}
|
||||||
|
|
||||||
boolean acceptMethod(MethodReader method);
|
public void testBar() {
|
||||||
|
assertEquals("derived", b);
|
||||||
Iterable<String> getExpectedExceptions(MethodReader method);
|
}
|
||||||
|
|
||||||
Class<? extends TestRunner> getRunner(MethodReader method);
|
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2017 Jaroslav Tulach.
|
* Copyright 2018 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
public abstract class JUnitBaseTest {
|
||||||
*
|
String a;
|
||||||
* @author Jaroslav Tulach
|
String b;
|
||||||
*/
|
|
||||||
public class KOTestAdapterTest {
|
@Before
|
||||||
|
public void startBase() {
|
||||||
|
a = "start";
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void verifyKOTestClassNameIsCorrect() throws ClassNotFoundException {
|
public void foo() {
|
||||||
Class.forName(KOTestAdapter.KO_TEST_CLASS);
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.testing;
|
package org.teavm.tests;
|
||||||
|
|
||||||
public interface TestLauncher {
|
import static org.junit.Assert.assertEquals;
|
||||||
void launch() throws Throwable;
|
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;
|
package org.teavm.junit;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
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.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -41,7 +45,12 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
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;
|
||||||
|
@ -54,17 +63,20 @@ import org.teavm.backend.c.CTarget;
|
||||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
import org.teavm.backend.wasm.WasmTarget;
|
import org.teavm.backend.wasm.WasmTarget;
|
||||||
import org.teavm.callgraph.CallGraph;
|
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.DefaultProblemTextConsumer;
|
||||||
import org.teavm.diagnostics.Problem;
|
import org.teavm.diagnostics.Problem;
|
||||||
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
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.MethodReference;
|
||||||
import org.teavm.model.PreOptimizingClassHolderSource;
|
import org.teavm.model.PreOptimizingClassHolderSource;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||||
import org.teavm.testing.JUnitTestAdapter;
|
|
||||||
import org.teavm.testing.TestAdapter;
|
|
||||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
import org.teavm.vm.DirectoryBuildTarget;
|
import org.teavm.vm.DirectoryBuildTarget;
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
|
@ -72,6 +84,12 @@ import org.teavm.vm.TeaVMBuilder;
|
||||||
import org.teavm.vm.TeaVMTarget;
|
import org.teavm.vm.TeaVMTarget;
|
||||||
|
|
||||||
public class TeaVMTestRunner extends Runner implements Filterable {
|
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 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.js.threads";
|
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 static final int stopTimeout = 15000;
|
||||||
private Class<?> testClass;
|
private Class<?> testClass;
|
||||||
private ClassHolder classHolder;
|
private ClassHolderSource classSource;
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
private Description suiteDescription;
|
private Description suiteDescription;
|
||||||
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
|
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
|
||||||
private File outputDir;
|
private File outputDir;
|
||||||
private TestAdapter testAdapter = new JUnitTestAdapter();
|
|
||||||
private Map<Method, Description> descriptions = new HashMap<>();
|
private Map<Method, Description> descriptions = new HashMap<>();
|
||||||
private static Map<RunKind, RunnerKindInfo> runners = new HashMap<>();
|
private static Map<RunKind, RunnerKindInfo> runners = new HashMap<>();
|
||||||
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
|
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
|
||||||
|
@ -124,8 +141,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
public TeaVMTestRunner(Class<?> testClass) throws InitializationError {
|
public TeaVMTestRunner(Class<?> testClass) throws InitializationError {
|
||||||
this.testClass = testClass;
|
this.testClass = testClass;
|
||||||
classLoader = TeaVMTestRunner.class.getClassLoader();
|
classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||||
ClassHolderSource classSource = getClassSource(classLoader);
|
classSource = getClassSource(classLoader);
|
||||||
classHolder = classSource.get(testClass.getName());
|
|
||||||
String outputPath = System.getProperty(PATH_PARAM);
|
String outputPath = System.getProperty(PATH_PARAM);
|
||||||
if (outputPath != null) {
|
if (outputPath != null) {
|
||||||
outputDir = new File(outputPath);
|
outputDir = new File(outputPath);
|
||||||
|
@ -166,7 +182,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
if (suiteDescription == null) {
|
if (suiteDescription == null) {
|
||||||
suiteDescription = Description.createSuiteDescription(testClass);
|
suiteDescription = Description.createSuiteDescription(testClass);
|
||||||
for (Method child : getFilteredChildren()) {
|
for (Method child : getFilteredChildren()) {
|
||||||
suiteDescription.getChildren().add(describeChild(child));
|
suiteDescription.addChild(describeChild(child));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return suiteDescription;
|
return suiteDescription;
|
||||||
|
@ -196,15 +212,29 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
|
|
||||||
private List<Method> getChildren() {
|
private List<Method> getChildren() {
|
||||||
List<Method> children = new ArrayList<>();
|
List<Method> children = new ArrayList<>();
|
||||||
for (Method method : testClass.getDeclaredMethods()) {
|
Class<?> cls = testClass;
|
||||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
Set<String> foundMethods = new HashSet<>();
|
||||||
if (testAdapter.acceptMethod(methodHolder)) {
|
while (cls != Object.class && !cls.getName().equals(JUNIT3_BASE_CLASS)) {
|
||||||
children.add(method);
|
for (Method method : cls.getDeclaredMethods()) {
|
||||||
|
if (foundMethods.add(method.getName()) && isTestMethod(method)) {
|
||||||
|
children.add(method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cls = cls.getSuperclass();
|
||||||
}
|
}
|
||||||
|
|
||||||
return children;
|
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() {
|
private List<Method> getFilteredChildren() {
|
||||||
if (filteredChildren == null) {
|
if (filteredChildren == null) {
|
||||||
filteredChildren = getChildren();
|
filteredChildren = getChildren();
|
||||||
|
@ -218,31 +248,39 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runChild(Method child, RunNotifier notifier) {
|
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 ran = false;
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
|
|
||||||
|
ClassHolder classHolder = classSource.get(child.getDeclaringClass().getName());
|
||||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(child));
|
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(child));
|
||||||
Set<Class<?>> expectedExceptions = new HashSet<>();
|
Set<Class<?>> expectedExceptions = new HashSet<>();
|
||||||
for (String exceptionName : testAdapter.getExpectedExceptions(methodHolder)) {
|
for (String exceptionName : getExpectedExceptions(methodHolder)) {
|
||||||
try {
|
try {
|
||||||
expectedExceptions.add(Class.forName(exceptionName, false, classLoader));
|
expectedExceptions.add(Class.forName(exceptionName, false, classLoader));
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
notifier.fireTestFailure(new Failure(description, e));
|
||||||
notifier.fireTestFinished(describeChild(child));
|
notifier.fireTestFinished(description);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!child.isAnnotationPresent(SkipJVM.class)
|
if (!child.isAnnotationPresent(SkipJVM.class)
|
||||||
&& !child.getDeclaringClass().isAnnotationPresent(SkipJVM.class)) {
|
&& !testClass.isAnnotationPresent(SkipJVM.class)) {
|
||||||
ran = true;
|
ran = true;
|
||||||
success = runInJvm(child, notifier, expectedExceptions);
|
success = runInJvm(child, notifier, expectedExceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Description description = describeChild(child);
|
|
||||||
if (success && outputDir != null) {
|
if (success && outputDir != null) {
|
||||||
int[] configurationIndex = new int[] { 0 };
|
int[] configurationIndex = new int[] { 0 };
|
||||||
List<Consumer<Boolean>> onSuccess = new ArrayList<>();
|
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) {
|
private boolean runInJvm(Method child, RunNotifier notifier, Set<Class<?>> expectedExceptions) {
|
||||||
|
Description description = describeChild(child);
|
||||||
|
Runner runner;
|
||||||
Object instance;
|
Object instance;
|
||||||
try {
|
try {
|
||||||
instance = testClass.newInstance();
|
instance = testClass.newInstance();
|
||||||
} catch (InstantiationException | IllegalAccessException e) {
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
notifier.fireTestFailure(new Failure(description, e));
|
||||||
return false;
|
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;
|
List<Class<?>> classes = new ArrayList<>();
|
||||||
try {
|
Class<?> cls = instance.getClass();
|
||||||
child.invoke(instance);
|
while (cls != null) {
|
||||||
} catch (IllegalAccessException e) {
|
classes.add(cls);
|
||||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
cls = cls.getSuperclass();
|
||||||
return false;
|
}
|
||||||
} catch (InvocationTargetException e) {
|
Collections.reverse(classes);
|
||||||
boolean wasExpected = false;
|
for (Class<?> c : classes) {
|
||||||
for (Class<?> expected : expectedExceptions) {
|
for (Method method : c.getMethods()) {
|
||||||
if (expected.isInstance(e.getTargetException())) {
|
if (method.isAnnotationPresent(Before.class)) {
|
||||||
expectedCaught = true;
|
try {
|
||||||
wasExpected = true;
|
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;
|
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()) {
|
@Override
|
||||||
notifier.fireTestAssumptionFailed(new Failure(describeChild(child),
|
public void run() throws Throwable {
|
||||||
new AssertionError("Expected exception was not thrown")));
|
try {
|
||||||
return false;
|
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,
|
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) {
|
private File getOutputPath(Method method) {
|
||||||
File path = outputDir;
|
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 = new File(path, method.getName());
|
||||||
path.mkdirs();
|
path.mkdirs();
|
||||||
return path;
|
return path;
|
||||||
|
@ -430,27 +562,46 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
|
|
||||||
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
|
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
|
||||||
File path) {
|
File path) {
|
||||||
return compileTest(method, configuration, JavaScriptTarget::new, vm -> {
|
DebugInformationBuilder debugEmitter = new DebugInformationBuilder();
|
||||||
vm.entryPoint(TestEntryPoint.class.getName());
|
Supplier<JavaScriptTarget> targetSupplier = () -> {
|
||||||
}, path, ".js");
|
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,
|
private CompileResult compileToC(Method method, TeaVMTestConfiguration<CTarget> configuration,
|
||||||
File path) {
|
File path) {
|
||||||
return compileTest(method, configuration, CTarget::new, vm -> {
|
return compileTest(method, configuration, CTarget::new, TestNativeEntryPoint.class.getName(), path, ".c", null);
|
||||||
vm.entryPoint(TestNativeEntryPoint.class.getName());
|
|
||||||
}, path, ".c");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration,
|
private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration,
|
||||||
File path) {
|
File path) {
|
||||||
return compileTest(method, configuration, WasmTarget::new, vm -> {
|
return compileTest(method, configuration, WasmTarget::new, TestNativeEntryPoint.class.getName(), path,
|
||||||
vm.entryPoint(TestNativeEntryPoint.class.getName());
|
".wasm", null);
|
||||||
}, path, ".wasm");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends TeaVMTarget> CompileResult compileTest(Method method, TeaVMTestConfiguration<T> configuration,
|
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();
|
CompileResult result = new CompileResult();
|
||||||
|
|
||||||
StringBuilder simpleName = new StringBuilder();
|
StringBuilder simpleName = new StringBuilder();
|
||||||
|
@ -464,10 +615,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
result.file = outputFile;
|
result.file = outputFile;
|
||||||
|
|
||||||
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||||
ClassHolderSource classSource = getClassSource(classLoader);
|
|
||||||
|
|
||||||
|
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||||
Class<?> runnerType = testAdapter.getRunner(methodHolder);
|
|
||||||
|
|
||||||
T target = targetSupplier.get();
|
T target = targetSupplier.get();
|
||||||
configuration.apply(target);
|
configuration.apply(target);
|
||||||
|
@ -486,18 +636,26 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
vm.installPlugins();
|
vm.installPlugins();
|
||||||
|
|
||||||
new TestExceptionPlugin().install(vm);
|
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());
|
vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName());
|
||||||
if (!vm.getProblemProvider().getProblems().isEmpty()) {
|
if (!vm.getProblemProvider().getProblems().isEmpty()) {
|
||||||
result.success = false;
|
result.success = false;
|
||||||
result.errorMessage = buildErrorMessage(vm);
|
result.errorMessage = buildErrorMessage(vm);
|
||||||
|
} else {
|
||||||
|
if (postBuild != null) {
|
||||||
|
postBuild.process(vm, outputFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CompilePostProcessor {
|
||||||
|
void process(TeaVM vm, File targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
private List<TeaVMTestConfiguration<JavaScriptTarget>> getJavaScriptConfigurations() {
|
private List<TeaVMTestConfiguration<JavaScriptTarget>> getJavaScriptConfigurations() {
|
||||||
List<TeaVMTestConfiguration<JavaScriptTarget>> configurations = new ArrayList<>();
|
List<TeaVMTestConfiguration<JavaScriptTarget>> configurations = new ArrayList<>();
|
||||||
if (Boolean.parseBoolean(System.getProperty(JS_ENABLED, "true"))) {
|
if (Boolean.parseBoolean(System.getProperty(JS_ENABLED, "true"))) {
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.junit;
|
package org.teavm.junit;
|
||||||
|
|
||||||
import org.teavm.testing.TestRunner;
|
|
||||||
|
|
||||||
final class TestEntryPoint {
|
final class TestEntryPoint {
|
||||||
private static Object testCase;
|
private static Object testCase;
|
||||||
|
|
||||||
|
@ -24,14 +22,23 @@ final class TestEntryPoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void run() throws Throwable {
|
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 void launchTest();
|
||||||
|
|
||||||
private static native boolean isExpectedException(Class<?> cls);
|
private static native void after();
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
run();
|
run();
|
||||||
|
|
|
@ -15,13 +15,22 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.junit;
|
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.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
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.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
@ -36,12 +45,12 @@ import org.teavm.vm.spi.TeaVMHost;
|
||||||
import org.teavm.vm.spi.TeaVMPlugin;
|
import org.teavm.vm.spi.TeaVMPlugin;
|
||||||
|
|
||||||
class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||||
private String runnerClassName;
|
|
||||||
private MethodReference testMethod;
|
private MethodReference testMethod;
|
||||||
|
private String testClassName;
|
||||||
|
|
||||||
TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) {
|
TestEntryPointTransformer(MethodReference testMethod, String testClassName) {
|
||||||
this.runnerClassName = runnerClassName;
|
|
||||||
this.testMethod = testMethod;
|
this.testMethod = testMethod;
|
||||||
|
this.testClassName = testClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,38 +62,89 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||||
if (cls.getName().equals(TestEntryPoint.class.getName())) {
|
if (cls.getName().equals(TestEntryPoint.class.getName())) {
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getName().equals("createRunner")) {
|
switch (method.getName()) {
|
||||||
method.setProgram(generateRunnerProgram(method, innerSource));
|
case "launchTest":
|
||||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
method.setProgram(generateLaunchProgram(method, innerSource));
|
||||||
} else if (method.getName().equals("launchTest")) {
|
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||||
method.setProgram(generateLaunchProgram(method, innerSource));
|
break;
|
||||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
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);
|
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();
|
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) {
|
private Program generateLaunchProgram(MethodHolder method, ClassReaderSource innerSource) {
|
||||||
ProgramEmitter pe = ProgramEmitter.create(method, 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)
|
pe.getField(TestEntryPoint.class, "testCase", Object.class)
|
||||||
.cast(ValueType.object(testMethod.getClassName()))
|
.cast(ValueType.object(testMethod.getClassName()))
|
||||||
.invokeSpecial(testMethod);
|
.invokeSpecial(testMethod);
|
||||||
|
|
||||||
MethodReader testMethodReader = innerSource.resolve(testMethod);
|
MethodReader testMethodReader = innerSource.resolve(testMethod);
|
||||||
AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(Test.class.getName());
|
AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(JUNIT4_TEST);
|
||||||
AnnotationValue throwsValue = testAnnotation.getValue("expected");
|
AnnotationValue throwsValue = testAnnotation != null ? testAnnotation.getValue("expected") : null;
|
||||||
if (throwsValue != null) {
|
if (throwsValue != null) {
|
||||||
BasicBlock handler = pe.getProgram().createBasicBlock();
|
BasicBlock handler = pe.getProgram().createBasicBlock();
|
||||||
TryCatchBlock tryCatch = new TryCatchBlock();
|
TryCatchBlock tryCatch = new TryCatchBlock();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user