mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Improving JUnit test runner
This commit is contained in:
parent
2b3cba69bc
commit
d103306c3e
|
@ -20,6 +20,13 @@
|
|||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="HTML">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="DEFAULT_COMPILER" value="Eclipse" />
|
||||
<excludeFromCompile>
|
||||
<directory url="file://$PROJECT_DIR$/tools/maven/webapp/src/main/resources/archetype-resources" includeSubdirectories="true" />
|
||||
</excludeFromCompile>
|
||||
|
|
|
@ -71,13 +71,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
@ -83,43 +83,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-maven-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-javascript-tests</id>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minifying>false</minifying>
|
||||
<scanDependencies>true</scanDependencies>
|
||||
<wildcards>
|
||||
<wildcard>org.teavm.classlib.**.*Test</wildcard>
|
||||
<wildcard>org.teavm.jso.**.*Test</wildcard>
|
||||
<wildcard>org.teavm.platform.metadata.*Test</wildcard>
|
||||
</wildcards>
|
||||
<properties>
|
||||
<java.util.Locale.available>en, en_US, en_GB, ru, ru_RU</java.util.Locale.available>
|
||||
</properties>
|
||||
<incremental>${teavm.test.incremental}</incremental>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>run-javascript-tests</id>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<skip>${teavm.test.skip}</skip>
|
||||
<numThreads>${teavm.test.threads}</numThreads>
|
||||
<seleniumURL>${teavm.test.selenium}</seleniumURL>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
|
|
|
@ -23,9 +23,12 @@ import java.text.ParseException;
|
|||
import java.util.Locale;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMProperties;
|
||||
import org.teavm.junit.TeaVMProperty;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
||||
public class DecimalFormatParseTest {
|
||||
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
|
||||
|
||||
|
|
|
@ -25,9 +25,12 @@ import java.util.Currency;
|
|||
import java.util.Locale;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMProperties;
|
||||
import org.teavm.junit.TeaVMProperty;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
||||
public class DecimalFormatTest {
|
||||
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.Locale;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
|
@ -32,7 +33,7 @@ public class NumberFormatTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
@SkipJVM
|
||||
public void formatsPercent() {
|
||||
NumberFormat format = NumberFormat.getPercentInstance(new Locale("en", "US"));
|
||||
assertEquals("12,345,679%", format.format(123456.789123));
|
||||
|
|
|
@ -21,9 +21,13 @@ import java.util.Locale;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMProperties;
|
||||
import org.teavm.junit.TeaVMProperty;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
||||
public class CurrencyTest {
|
||||
@Test
|
||||
public void findsByCode() {
|
||||
|
@ -46,8 +50,7 @@ public class CurrencyTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// It seems that JDK can't translate currency names into Russian
|
||||
@SkipJVM
|
||||
public void getsDisplayName() {
|
||||
Locale russian = new Locale("ru");
|
||||
Locale english = new Locale("en");
|
||||
|
@ -64,8 +67,7 @@ public class CurrencyTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// It seems that JDK does not know about currency symbols
|
||||
@SkipJVM
|
||||
public void getsSymbol() {
|
||||
Locale russian = new Locale("ru");
|
||||
Locale english = new Locale("en");
|
||||
|
|
|
@ -19,9 +19,12 @@ import static org.junit.Assert.*;
|
|||
import java.util.Locale;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMProperties;
|
||||
import org.teavm.junit.TeaVMProperty;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
||||
public class LocaleTest {
|
||||
@Test
|
||||
public void availableLocalesFound() {
|
||||
|
|
|
@ -15,14 +15,6 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.util;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class TestServiceImpl implements TestService {
|
||||
private int counter;
|
||||
|
||||
|
|
|
@ -22,9 +22,11 @@ import org.teavm.jso.JSBody;
|
|||
import org.teavm.jso.JSMethod;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class AnnotationsTest {
|
||||
@Test
|
||||
public void staticBodyWorks() {
|
||||
|
|
|
@ -22,9 +22,11 @@ import org.teavm.jso.JSBody;
|
|||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.jso.core.JSString;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class ConversionTest {
|
||||
@Test
|
||||
public void convertsPrimitivesToJavaScript() {
|
||||
|
|
|
@ -21,9 +21,11 @@ import org.junit.runner.RunWith;
|
|||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSFunctor;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class FunctorTest {
|
||||
@Test
|
||||
public void functorPassed() {
|
||||
|
|
|
@ -20,13 +20,11 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class ImplementationTest {
|
||||
@Test
|
||||
public void respectsPrecedence() {
|
||||
|
@ -35,10 +33,10 @@ public class ImplementationTest {
|
|||
}
|
||||
|
||||
@JSBody(params = { "a", "b" }, script = "return a + b;")
|
||||
static final native int add(int a, int b);
|
||||
static native int add(int a, int b);
|
||||
|
||||
@JSBody(params = { "a", "b" }, script = "return a * b;")
|
||||
static final native int mul(int a, int b);
|
||||
static native int mul(int a, int b);
|
||||
|
||||
@Test
|
||||
public void inliningUsageCounterWorksProperly() {
|
||||
|
|
|
@ -20,9 +20,11 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class JavaInvocationTest {
|
||||
@Test
|
||||
public void callStaticMethod() {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.platform.metadata;
|
|||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
/**
|
||||
|
@ -25,6 +26,7 @@ import org.teavm.junit.TeaVMTestRunner;
|
|||
* @author Alexey Andreev
|
||||
*/
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipJVM
|
||||
public class MetadataGeneratorTest {
|
||||
@MetadataProvider(TestResourceGenerator.class)
|
||||
private native TestResource getNull();
|
||||
|
|
|
@ -22,17 +22,13 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.teavm.diagnostics.Problem;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.vm.TeaVM;
|
||||
import org.teavm.vm.TeaVMBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class JSOTest {
|
||||
@Test
|
||||
public void reportsAboutWrongParameterOfJSBody() {
|
||||
|
|
|
@ -55,7 +55,7 @@ public final class TeaVMProblemRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private static void renderCallStack(CallGraph cg, CallLocation location, StringBuilder sb) {
|
||||
public static void renderCallStack(CallGraph cg, CallLocation location, StringBuilder sb) {
|
||||
if (location == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public final class TeaVMProblemRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private static void renderCallLocation(MethodReference method, InstructionLocation location, StringBuilder sb) {
|
||||
public static void renderCallLocation(MethodReference method, InstructionLocation location, StringBuilder sb) {
|
||||
if (method != null) {
|
||||
sb.append(method.getClassName() + "." + method.getName());
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
final class ExceptionHelper {
|
||||
private ExceptionHelper() {
|
||||
}
|
||||
|
||||
public static String showException(Throwable e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
26
tools/junit/src/main/java/org/teavm/junit/SkipJVM.java
Normal file
26
tools/junit/src/main/java/org/teavm/junit/SkipJVM.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface SkipJVM {
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface TeaVMProperties {
|
||||
TeaVMProperty[] value();
|
||||
}
|
22
tools/junit/src/main/java/org/teavm/junit/TeaVMProperty.java
Normal file
22
tools/junit/src/main/java/org/teavm/junit/TeaVMProperty.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
public @interface TeaVMProperty {
|
||||
String key();
|
||||
|
||||
String value();
|
||||
}
|
|
@ -16,72 +16,258 @@
|
|||
package org.teavm.junit;
|
||||
|
||||
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 org.junit.Test;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
import org.junit.runners.ParentRunner;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||
import org.teavm.diagnostics.Problem;
|
||||
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.tooling.testing.TeaVMTestTool;
|
||||
import org.teavm.vm.DirectoryBuildTarget;
|
||||
import org.teavm.vm.TeaVM;
|
||||
import org.teavm.vm.TeaVMBuilder;
|
||||
|
||||
public class TeaVMTestRunner extends Runner {
|
||||
public class TeaVMTestRunner extends ParentRunner<Method> {
|
||||
private static final String PATH_PARAM = "teavm.junit.target";
|
||||
private Class<?> testClass;
|
||||
private Description description;
|
||||
private ClassHolder classHolder;
|
||||
private ClassLoader classLoader;
|
||||
private ClassHolderSource classSource;
|
||||
private static Map<ClassLoader, ClassHolderSource> classSources = new WeakHashMap<>();
|
||||
private File outputDir;
|
||||
private TestAdapter testAdapter = new JUnitTestAdapter();
|
||||
private Map<Method, Description> descriptions = new HashMap<>();
|
||||
|
||||
public TeaVMTestRunner(Class<?> testClass) {
|
||||
this.testClass = testClass;
|
||||
public TeaVMTestRunner(Class<?> testClass) throws InitializationError {
|
||||
super(testClass);
|
||||
classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||
classSource = getClassSource(classLoader);
|
||||
classHolder = classSource.get(testClass.getName());
|
||||
String outputPath = System.getProperty(PATH_PARAM);
|
||||
if (outputPath != null) {
|
||||
outputDir = new File(outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
if (description == null) {
|
||||
description = Description.createSuiteDescription(testClass);
|
||||
for (Method method : testClass.getMethods()) {
|
||||
if (method.getParameterCount() == 0 && method.getReturnType() == void.class
|
||||
&& method.isAnnotationPresent(Test.class)) {
|
||||
Description testDescription = Description.createTestDescription(testClass, method.getName());
|
||||
description.addChild(testDescription);
|
||||
protected List<Method> getChildren() {
|
||||
List<Method> children = new ArrayList<>();
|
||||
for (Method method : getTestClass().getJavaClass().getDeclaredMethods()) {
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
if (testAdapter.acceptMethod(methodHolder)) {
|
||||
children.add(method);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Description describeChild(Method child) {
|
||||
return descriptions.computeIfAbsent(child, method -> Description.createTestDescription(
|
||||
getTestClass().getJavaClass(), method.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runChild(Method child, RunNotifier notifier) {
|
||||
notifier.fireTestStarted(describeChild(child));
|
||||
|
||||
boolean run = false;
|
||||
boolean success = true;
|
||||
if (outputDir != null) {
|
||||
run = true;
|
||||
success &= runInTeaVM(child, notifier);
|
||||
}
|
||||
|
||||
if (success && !child.isAnnotationPresent(SkipJVM.class)
|
||||
&& !child.getDeclaringClass().isAnnotationPresent(SkipJVM.class)) {
|
||||
run = true;
|
||||
success &= runInJvm(child, notifier);
|
||||
}
|
||||
|
||||
if (!run) {
|
||||
notifier.fireTestIgnored(describeChild(child));
|
||||
}
|
||||
notifier.fireTestFinished(describeChild(child));
|
||||
}
|
||||
|
||||
private boolean runInJvm(Method child, RunNotifier notifier) {
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(child));
|
||||
Set<Class<?>> expectedExceptions = new HashSet<>();
|
||||
for (String exceptionName : testAdapter.getExpectedExceptions(methodHolder)) {
|
||||
try {
|
||||
expectedExceptions.add(Class.forName(exceptionName, false, classLoader));
|
||||
} catch (ClassNotFoundException e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Object instance;
|
||||
try {
|
||||
instance = getTestClass().getJavaClass().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (!wasExpected) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e.getTargetException()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return description;
|
||||
|
||||
if (!expectedCaught && !expectedExceptions.isEmpty()) {
|
||||
notifier.fireTestAssumptionFailed(new Failure(describeChild(child),
|
||||
new AssertionError("Expected exception was not thrown")));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(RunNotifier notifier) {
|
||||
Description description = getDescription();
|
||||
|
||||
notifier.fireTestStarted(description);
|
||||
String targetPath = System.getProperty(PATH_PARAM);
|
||||
if (targetPath == null) {
|
||||
for (Description testDescription : description.getChildren()) {
|
||||
notifier.fireTestIgnored(testDescription);
|
||||
}
|
||||
notifier.fireTestIgnored(description);
|
||||
notifier.fireTestFinished(description);
|
||||
return;
|
||||
}
|
||||
|
||||
TeaVMTestTool tool = new TeaVMTestTool();
|
||||
tool.setTargetDirectory(new File(targetPath, testClass.getName()));
|
||||
tool.setMinifying(false);
|
||||
tool.getTestClasses().add(testClass.getName());
|
||||
boolean success = true;
|
||||
private boolean runInTeaVM(Method child, RunNotifier notifier) {
|
||||
CompileResult compileResult;
|
||||
try {
|
||||
tool.generate();
|
||||
} catch (Exception e) {
|
||||
notifier.fireTestFailure(new Failure(description, e));
|
||||
success = false;
|
||||
compileResult = compileTest(child);
|
||||
} catch (IOException e) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
for (Description testDescription : description.getChildren()) {
|
||||
notifier.fireTestStarted(testDescription);
|
||||
notifier.fireTestFinished(testDescription);
|
||||
if (!compileResult.success) {
|
||||
notifier.fireTestFailure(new Failure(describeChild(child),
|
||||
new AssertionError(compileResult.errorMessage)));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private CompileResult compileTest(Method method) throws IOException {
|
||||
CompileResult result = new CompileResult();
|
||||
|
||||
File path = outputDir;
|
||||
path = new File(path, method.getDeclaringClass().getName().replace('.', '/'));
|
||||
path = new File(path, method.getName());
|
||||
path.mkdirs();
|
||||
File outputFile = new File(path, "test.js");
|
||||
result.file = outputFile;
|
||||
|
||||
resourceToFile("org/teavm/javascript/runtime.js", new File(path, "runtime.js"));
|
||||
resourceToFile("teavm-run-test.html", new File(path, "run-test.html"));
|
||||
|
||||
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||
ClassHolderSource classSource = getClassSource(classLoader);
|
||||
|
||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||
Class<?> runnerType = testAdapter.getRunner(methodHolder);
|
||||
|
||||
TeaVM vm = new TeaVMBuilder()
|
||||
.setClassLoader(classLoader)
|
||||
.setClassSource(classSource)
|
||||
.build();
|
||||
vm.setIncremental(false);
|
||||
vm.setMinifying(false);
|
||||
vm.installPlugins();
|
||||
|
||||
new TestExceptionPlugin().install(vm);
|
||||
new TestEntryPointTransformer(runnerType.getName(), methodHolder.getReference()).install(vm);
|
||||
|
||||
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8")) {
|
||||
MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException",
|
||||
Throwable.class, String.class);
|
||||
vm.entryPoint("runTest", new MethodReference(TestEntryPoint.class, "run", void.class)).async();
|
||||
vm.entryPoint("extractException", exceptionMsg);
|
||||
vm.build(innerWriter, new DirectoryBuildTarget(outputDir));
|
||||
if (!vm.getProblemProvider().getProblems().isEmpty()) {
|
||||
result.success = false;
|
||||
result.errorMessage = buildErrorMessage(vm);
|
||||
}
|
||||
}
|
||||
|
||||
notifier.fireTestFinished(description);
|
||||
return result;
|
||||
}
|
||||
|
||||
private MethodDescriptor getDescriptor(Method method) {
|
||||
ValueType[] signature = Stream.concat(Arrays.stream(method.getParameterTypes()).map(ValueType::parse),
|
||||
Stream.of(ValueType.parse(method.getReturnType())))
|
||||
.toArray(ValueType[]::new);
|
||||
return new MethodDescriptor(method.getName(), signature);
|
||||
}
|
||||
|
||||
private String buildErrorMessage(TeaVM vm) {
|
||||
CallGraph cg = vm.getDependencyInfo().getCallGraph();
|
||||
DefaultProblemTextConsumer consumer = new DefaultProblemTextConsumer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Problem problem : vm.getProblemProvider().getProblems()) {
|
||||
consumer.clear();
|
||||
problem.render(consumer);
|
||||
sb.append(consumer.getText());
|
||||
TeaVMProblemRenderer.renderCallStack(cg, problem.getLocation(), sb);
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void resourceToFile(String resource, File fileName) throws IOException {
|
||||
try (InputStream input = TeaVMTestTool.class.getClassLoader().getResourceAsStream(resource);
|
||||
OutputStream output = new FileOutputStream(fileName)) {
|
||||
IOUtils.copy(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassHolderSource getClassSource(ClassLoader classLoader) {
|
||||
return classSources.computeIfAbsent(classLoader, cl -> new PreOptimizingClassHolderSource(
|
||||
new ClasspathClassHolderSource(classLoader)));
|
||||
}
|
||||
|
||||
static class CompileResult {
|
||||
boolean success = true;
|
||||
String errorMessage;
|
||||
File file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.teavm.testing.TestRunner;
|
||||
|
||||
final class TestEntryPoint {
|
||||
private static Object testCase;
|
||||
|
||||
private TestEntryPoint() {
|
||||
}
|
||||
|
||||
public static void run() throws Throwable {
|
||||
createRunner().run(() -> launchTest());
|
||||
}
|
||||
|
||||
private static native TestRunner createRunner();
|
||||
|
||||
private static native void launchTest();
|
||||
|
||||
private static native boolean isExpectedException(Class<?> cls);
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
import org.teavm.vm.spi.TeaVMPlugin;
|
||||
|
||||
class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
|
||||
private String runnerClassName;
|
||||
private MethodReference testMethod;
|
||||
|
||||
public TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) {
|
||||
this.runnerClassName = runnerClassName;
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install(TeaVMHost host) {
|
||||
host.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Program generateRunnerProgram(MethodHolder method, ClassReaderSource innerSource) {
|
||||
ProgramEmitter pe = ProgramEmitter.create(method, innerSource);
|
||||
pe.construct(runnerClassName).returnValue();
|
||||
return pe.getProgram();
|
||||
}
|
||||
|
||||
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);
|
||||
pe.exit();
|
||||
return pe.getProgram();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
class TestExceptionDependency extends AbstractDependencyListener {
|
||||
private MethodReference getMessageRef = new MethodReference(ExceptionHelper.class, "showException",
|
||||
Throwable.class, String.class);
|
||||
private DependencyNode allClasses;
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
allClasses = agent.createNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
||||
if (isException(agent.getClassSource(), className)) {
|
||||
allClasses.propagate(agent.getType(className));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isException(ClassReaderSource classSource, String className) {
|
||||
while (className != null) {
|
||||
if (className.equals("java.lang.Throwable")) {
|
||||
return true;
|
||||
}
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null) {
|
||||
return false;
|
||||
}
|
||||
className = cls.getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (method.getReference().equals(getMessageRef)) {
|
||||
allClasses.connect(method.getVariable(1));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.junit;
|
||||
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
import org.teavm.vm.spi.TeaVMPlugin;
|
||||
|
||||
class TestExceptionPlugin implements TeaVMPlugin {
|
||||
@Override
|
||||
public void install(TeaVMHost host) {
|
||||
host.add(new TestExceptionDependency());
|
||||
}
|
||||
}
|
53
tools/junit/src/main/resources/teavm-run-test.html
Normal file
53
tools/junit/src/main/resources/teavm-run-test.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TeaVM JUnit test</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="runtime.js"></script>
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
<script type="text/javascript">
|
||||
$rt_startThread(function() {
|
||||
var thread = $rt_nativeThread();
|
||||
var instance;
|
||||
var ptr = 0;
|
||||
var message;
|
||||
if (thread.isResuming()) {
|
||||
ptr = thread.pop();
|
||||
instance = thread.pop();
|
||||
}
|
||||
loop: while (true) {
|
||||
switch (ptr) {
|
||||
case 0:
|
||||
try {
|
||||
runTest();
|
||||
} catch (e) {
|
||||
message = buildErrorMessage(e);
|
||||
break loop;
|
||||
}
|
||||
if (thread.isSuspending()) {
|
||||
thread.push(instance);
|
||||
thread.push(ptr);
|
||||
return;
|
||||
}
|
||||
message = "OK";
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
document.body.appendChild(document.createTextNode(message));
|
||||
});
|
||||
|
||||
function buildErrorMessage(e) {
|
||||
var stack = e.stack;
|
||||
if (e.$javaException && e.$javaException.constructor.$meta) {
|
||||
stack = e.$javaException.constructor.$meta.name + ": ";
|
||||
var exceptionMessage = extractException(e.$javaException);
|
||||
stack += exceptionMessage ? $rt_ustr(exceptionMessage) : "";
|
||||
}
|
||||
stack += "\n" + stack;
|
||||
return stack;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user