Moves test generation logic from teavm-maven-plugin to teavm-core
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.tooling;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class EmptyTeaVMToolLog implements TeaVMToolLog {
|
||||
@Override
|
||||
public void info(String text) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String text) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String text) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String text) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String text, Throwable e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String text, Throwable e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String text, Throwable e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String text, Throwable e) {
|
||||
}
|
||||
}
|
359
teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java
Normal file
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* 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.tooling;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.teavm.common.FiniteExecutor;
|
||||
import org.teavm.common.SimpleFiniteExecutor;
|
||||
import org.teavm.common.ThreadPoolFiniteExecutor;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||
import org.teavm.testing.JUnitTestAdapter;
|
||||
import org.teavm.testing.TestAdapter;
|
||||
import org.teavm.vm.DirectoryBuildTarget;
|
||||
import org.teavm.vm.TeaVM;
|
||||
import org.teavm.vm.TeaVMBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class TeaVMTestTool {
|
||||
private Map<String, List<MethodReference>> groupedMethods = new HashMap<>();
|
||||
private Map<MethodReference, String> fileNames = new HashMap<>();
|
||||
private List<MethodReference> testMethods = new ArrayList<>();
|
||||
private File outputDir = new File(".");
|
||||
private boolean minifying = true;
|
||||
private int numThreads = 1;
|
||||
private TestAdapter adapter = new JUnitTestAdapter();
|
||||
private List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||
private List<String> additionalScripts = new ArrayList<>();
|
||||
private List<String> additionalScriptLocalPaths = new ArrayList<>();
|
||||
private Properties properties = new Properties();
|
||||
private List<String> testClasses = new ArrayList<>();
|
||||
private ClassLoader classLoader = TeaVMTestTool.class.getClassLoader();
|
||||
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||
|
||||
public File getOutputDir() {
|
||||
return outputDir;
|
||||
}
|
||||
|
||||
public void setOutputDir(File outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
public boolean isMinifying() {
|
||||
return minifying;
|
||||
}
|
||||
|
||||
public void setMinifying(boolean minifying) {
|
||||
this.minifying = minifying;
|
||||
}
|
||||
|
||||
public int getNumThreads() {
|
||||
return numThreads;
|
||||
}
|
||||
|
||||
public void setNumThreads(int numThreads) {
|
||||
this.numThreads = numThreads;
|
||||
}
|
||||
|
||||
public TestAdapter getAdapter() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public void setAdapter(TestAdapter adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
return transformers;
|
||||
}
|
||||
|
||||
public List<String> getAdditionalScripts() {
|
||||
return additionalScripts;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public List<String> getTestClasses() {
|
||||
return testClasses;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
public void setClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public TeaVMToolLog getLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
public void setLog(TeaVMToolLog log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public void generate() throws TeaVMToolException {
|
||||
Runnable finalizer = null;
|
||||
try {
|
||||
new File(outputDir, "tests").mkdirs();
|
||||
new File(outputDir, "res").mkdirs();
|
||||
resourceToFile("org/teavm/javascript/runtime.js", "res/runtime.js");
|
||||
String prefix = "org/teavm/tooling/test";
|
||||
resourceToFile(prefix + "/res/junit-support.js", "res/junit-support.js");
|
||||
resourceToFile(prefix + "/res/junit.css", "res/junit.css");
|
||||
resourceToFile(prefix + "/res/class_obj.png", "res/class_obj.png");
|
||||
resourceToFile(prefix + "/res/control-000-small.png", "res/control-000-small.png");
|
||||
resourceToFile(prefix + "/res/methpub_obj.png", "res/methpub_obj.png");
|
||||
resourceToFile(prefix + "/res/package_obj.png", "res/package_obj.png");
|
||||
resourceToFile(prefix + "/res/tick-small-red.png", "res/tick-small-red.png");
|
||||
resourceToFile(prefix + "/res/tick-small.png", "res/tick-small.png");
|
||||
resourceToFile(prefix + "/res/toggle-small-expand.png", "res/toggle-small-expand.png");
|
||||
resourceToFile(prefix + "/res/toggle-small.png", "res/toggle-small.png");
|
||||
resourceToFile(prefix + "/junit.html", "junit.html");
|
||||
final ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader);
|
||||
for (String testClass : testClasses) {
|
||||
ClassHolder classHolder = classSource.get(testClass);
|
||||
if (classHolder == null) {
|
||||
throw new TeaVMToolException("Could not find class " + testClass);
|
||||
}
|
||||
findTests(classHolder);
|
||||
}
|
||||
|
||||
includeAdditionalScripts(classLoader);
|
||||
File allTestsFile = new File(outputDir, "tests/all.js");
|
||||
try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) {
|
||||
allTestsWriter.write("prepare = function() {\n");
|
||||
allTestsWriter.write(" return new JUnitServer(document.body).readTests([");
|
||||
boolean first = true;
|
||||
for (String testClass : testClasses) {
|
||||
Collection<MethodReference> methods = groupedMethods.get(testClass);
|
||||
if (methods == null) {
|
||||
continue;
|
||||
}
|
||||
if (!first) {
|
||||
allTestsWriter.append(",");
|
||||
}
|
||||
first = false;
|
||||
allTestsWriter.append("\n { name : \"").append(testClass).append("\", methods : [");
|
||||
boolean firstMethod = true;
|
||||
for (MethodReference methodRef : methods) {
|
||||
String scriptName = "tests/" + fileNames.size() + ".js";
|
||||
fileNames.put(methodRef, scriptName);
|
||||
if (!firstMethod) {
|
||||
allTestsWriter.append(",");
|
||||
}
|
||||
firstMethod = false;
|
||||
allTestsWriter.append("\n { name : \"" + methodRef.getName() + "\", script : \"" +
|
||||
scriptName + "\", expected : [");
|
||||
MethodHolder methodHolder = classSource.get(testClass).getMethod(
|
||||
methodRef.getDescriptor());
|
||||
boolean firstException = true;
|
||||
for (String exception : adapter.getExpectedExceptions(methodHolder)) {
|
||||
if (!firstException) {
|
||||
allTestsWriter.append(", ");
|
||||
}
|
||||
firstException = false;
|
||||
allTestsWriter.append("\"" + exception + "\"");
|
||||
}
|
||||
allTestsWriter.append("], additionalScripts : [");
|
||||
for (int i = 0; i < additionalScriptLocalPaths.size(); ++i) {
|
||||
if (i > 0) {
|
||||
allTestsWriter.append(", ");
|
||||
}
|
||||
escapeString(additionalScriptLocalPaths.get(i), allTestsWriter);
|
||||
}
|
||||
allTestsWriter.append("] }");
|
||||
}
|
||||
allTestsWriter.append("] }");
|
||||
}
|
||||
allTestsWriter.write("], function() {}); }");
|
||||
}
|
||||
int methodsGenerated = 0;
|
||||
log.info("Generating test files");
|
||||
FiniteExecutor executor = new SimpleFiniteExecutor();
|
||||
if (numThreads != 1) {
|
||||
int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors();
|
||||
final ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads);
|
||||
finalizer = new Runnable() {
|
||||
@Override public void run() {
|
||||
threadedExecutor.stop();
|
||||
}
|
||||
};
|
||||
executor = threadedExecutor;
|
||||
}
|
||||
for (final MethodReference method : testMethods) {
|
||||
executor.execute(new Runnable() {
|
||||
@Override public void run() {
|
||||
log.debug("Building test for " + method);
|
||||
try {
|
||||
decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), method,
|
||||
fileNames.get(method), new SimpleFiniteExecutor());
|
||||
} catch (IOException e) {
|
||||
log.error("Error generating JavaScript", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
++methodsGenerated;
|
||||
}
|
||||
executor.complete();
|
||||
log.info("Test files successfully generated for " + methodsGenerated + " method(s).");
|
||||
} catch (IOException e) {
|
||||
throw new TeaVMToolException("IO error occured generating JavaScript files", e);
|
||||
} finally {
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resourceToFile(String resource, String fileName) throws IOException {
|
||||
try (InputStream input = TeaVMTestTool.class.getClassLoader().getResourceAsStream(resource)) {
|
||||
try (OutputStream output = new FileOutputStream(new File(outputDir, fileName))) {
|
||||
IOUtils.copy(input, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findTests(ClassHolder cls) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (adapter.acceptMethod(method)) {
|
||||
MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor());
|
||||
testMethods.add(ref);
|
||||
List<MethodReference> group = groupedMethods.get(cls.getName());
|
||||
if (group == null) {
|
||||
group = new ArrayList<>();
|
||||
groupedMethods.put(cls.getName(), group);
|
||||
}
|
||||
group.add(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void includeAdditionalScripts(ClassLoader classLoader) throws TeaVMToolException {
|
||||
if (additionalScripts == null) {
|
||||
return;
|
||||
}
|
||||
for (String script : additionalScripts) {
|
||||
String simpleName = script.substring(script.lastIndexOf('/') + 1);
|
||||
additionalScriptLocalPaths.add("tests/" + simpleName);
|
||||
if (classLoader.getResource(script) == null) {
|
||||
throw new TeaVMToolException("Additional script " + script + " was not found");
|
||||
}
|
||||
File file = new File(outputDir, "tests/" + simpleName);
|
||||
try (InputStream in = classLoader.getResourceAsStream(script)) {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
try(OutputStream out = new FileOutputStream(file)) {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new TeaVMToolException("Error copying additional script " + script, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void decompileClassesForTest(ClassLoader classLoader, ClassHolderSource classSource,
|
||||
MethodReference methodRef, String targetName, FiniteExecutor executor) throws IOException {
|
||||
TeaVM vm = new TeaVMBuilder()
|
||||
.setClassLoader(classLoader)
|
||||
.setClassSource(classSource)
|
||||
.setExecutor(executor)
|
||||
.build();
|
||||
vm.setProperties(properties);
|
||||
vm.setMinifying(minifying);
|
||||
vm.installPlugins();
|
||||
new TestExceptionPlugin().install(vm);
|
||||
for (ClassHolderTransformer transformer : transformers) {
|
||||
vm.add(transformer);
|
||||
}
|
||||
File file = new File(outputDir, targetName);
|
||||
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
|
||||
MethodReference cons = new MethodReference(methodRef.getClassName(),
|
||||
new MethodDescriptor("<init>", ValueType.VOID));
|
||||
MethodReference exceptionMsg = new MethodReference("java.lang.Throwable", "getMessage",
|
||||
ValueType.object("java.lang.String"));
|
||||
vm.entryPoint("initInstance", cons);
|
||||
vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName());
|
||||
vm.entryPoint("extractException", exceptionMsg);
|
||||
vm.exportType("TestClass", cons.getClassName());
|
||||
vm.build(innerWriter, new DirectoryBuildTarget(outputDir));
|
||||
if (!vm.hasMissingItems()) {
|
||||
innerWriter.append("\n");
|
||||
innerWriter.append("\nJUnitClient.run();");
|
||||
innerWriter.close();
|
||||
} else {
|
||||
innerWriter.append("JUnitClient.reportError(\n");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
vm.showMissingItems(sb);
|
||||
escapeStringLiteral(sb.toString(), innerWriter);
|
||||
innerWriter.append(");");
|
||||
log.warning("Error building test " + methodRef);
|
||||
log.warning(sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void escapeStringLiteral(String text, Writer writer) throws IOException {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
int next = text.indexOf('\n', index);
|
||||
if (next < 0) {
|
||||
break;
|
||||
}
|
||||
escapeString(text.substring(index, next + 1), writer);
|
||||
writer.append(" +\n");
|
||||
index = next + 1;
|
||||
}
|
||||
escapeString(text.substring(index), writer);
|
||||
}
|
||||
|
||||
private void escapeString(String string, Writer writer) throws IOException {
|
||||
writer.append('\"');
|
||||
for (int i = 0; i < string.length(); ++i) {
|
||||
char c = string.charAt(i);
|
||||
switch (c) {
|
||||
case '"':
|
||||
writer.append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
writer.append("\\\\");
|
||||
break;
|
||||
case '\n':
|
||||
writer.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
writer.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
writer.append("\\t");
|
||||
break;
|
||||
default:
|
||||
writer.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
writer.append('\"');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.tooling;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class TeaVMToolException extends Exception {
|
||||
private static final long serialVersionUID = 579149191624783241L;
|
||||
|
||||
public TeaVMToolException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TeaVMToolException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TeaVMToolException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
38
teavm-core/src/main/java/org/teavm/tooling/TeaVMToolLog.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.tooling;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public interface TeaVMToolLog {
|
||||
void info(String text);
|
||||
|
||||
void debug(String text);
|
||||
|
||||
void warning(String text);
|
||||
|
||||
void error(String text);
|
||||
|
||||
void info(String text, Throwable e);
|
||||
|
||||
void debug(String text, Throwable e);
|
||||
|
||||
void warning(String text, Throwable e);
|
||||
|
||||
void error(String text, Throwable e);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.tooling;
|
||||
|
||||
import org.teavm.dependency.*;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class TestExceptionDependency implements DependencyListener {
|
||||
private MethodReference getMessageRef = new MethodReference("java.lang.Throwable", "getMessage",
|
||||
ValueType.object("java.lang.String"));
|
||||
private DependencyNode allClasses;
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
allClasses = agent.createNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classAchieved(DependencyAgent agent, String className) {
|
||||
if (isException(agent.getClassSource(), className)) {
|
||||
allClasses.propagate(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 methodAchieved(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getReference().equals(getMessageRef)) {
|
||||
allClasses.connect(method.getVariable(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldAchieved(DependencyAgent agent, FieldDependency field) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.tooling;
|
||||
|
||||
import org.teavm.vm.spi.TeaVMHost;
|
||||
import org.teavm.vm.spi.TeaVMPlugin;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class TestExceptionPlugin implements TeaVMPlugin {
|
||||
@Override
|
||||
public void install(TeaVMHost host) {
|
||||
host.add(new TestExceptionDependency());
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 774 B |
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 325 B |
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package org.teavm.maven;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
|
@ -25,7 +26,6 @@ import java.util.*;
|
|||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
|
@ -36,16 +36,11 @@ import org.apache.maven.plugins.annotations.Mojo;
|
|||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.teavm.common.FiniteExecutor;
|
||||
import org.teavm.common.SimpleFiniteExecutor;
|
||||
import org.teavm.common.ThreadPoolFiniteExecutor;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.testing.JUnitTestAdapter;
|
||||
import org.teavm.testing.TestAdapter;
|
||||
import org.teavm.vm.DirectoryBuildTarget;
|
||||
import org.teavm.vm.TeaVM;
|
||||
import org.teavm.vm.TeaVMBuilder;
|
||||
import org.teavm.tooling.TeaVMTestTool;
|
||||
import org.teavm.tooling.TeaVMToolException;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -57,11 +52,6 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
private static Set<String> testScopes = new HashSet<>(Arrays.asList(
|
||||
Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME,
|
||||
Artifact.SCOPE_PROVIDED));
|
||||
private Map<String, List<MethodReference>> groupedMethods = new HashMap<>();
|
||||
private Map<MethodReference, String> fileNames = new HashMap<>();
|
||||
private List<MethodReference> testMethods = new ArrayList<>();
|
||||
private List<String> testClasses = new ArrayList<>();
|
||||
private TestAdapter adapter;
|
||||
|
||||
@Component
|
||||
private MavenProject project;
|
||||
|
@ -93,16 +83,14 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
@Parameter
|
||||
private String[] transformers;
|
||||
|
||||
private List<ClassHolderTransformer> transformerInstances;
|
||||
|
||||
@Parameter
|
||||
private String[] additionalScripts;
|
||||
|
||||
private List<String> additionalScriptLocalPaths = new ArrayList<>();
|
||||
|
||||
@Parameter
|
||||
private Properties properties;
|
||||
|
||||
private TeaVMTestTool tool = new TeaVMTestTool();
|
||||
|
||||
public void setProject(MavenProject project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
@ -154,124 +142,33 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
getLog().info("Tests build skipped as specified by system property");
|
||||
return;
|
||||
}
|
||||
Runnable finalizer = null;
|
||||
try {
|
||||
final ClassLoader classLoader = prepareClassLoader();
|
||||
createAdapter(classLoader);
|
||||
getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'");
|
||||
tool.setClassLoader(classLoader);
|
||||
tool.setAdapter(createAdapter(classLoader));
|
||||
findTestClasses(classLoader, testFiles, "");
|
||||
if (scanDependencies) {
|
||||
findTestsInDependencies(classLoader);
|
||||
}
|
||||
final Log log = getLog();
|
||||
new File(outputDir, "tests").mkdirs();
|
||||
new File(outputDir, "res").mkdirs();
|
||||
resourceToFile("org/teavm/javascript/runtime.js", "res/runtime.js");
|
||||
resourceToFile("org/teavm/maven/res/junit-support.js", "res/junit-support.js");
|
||||
resourceToFile("org/teavm/maven/res/junit.css", "res/junit.css");
|
||||
resourceToFile("org/teavm/maven/res/class_obj.png", "res/class_obj.png");
|
||||
resourceToFile("org/teavm/maven/res/control-000-small.png", "res/control-000-small.png");
|
||||
resourceToFile("org/teavm/maven/res/methpub_obj.png", "res/methpub_obj.png");
|
||||
resourceToFile("org/teavm/maven/res/package_obj.png", "res/package_obj.png");
|
||||
resourceToFile("org/teavm/maven/res/tick-small-red.png", "res/tick-small-red.png");
|
||||
resourceToFile("org/teavm/maven/res/tick-small.png", "res/tick-small.png");
|
||||
resourceToFile("org/teavm/maven/res/toggle-small-expand.png", "res/toggle-small-expand.png");
|
||||
resourceToFile("org/teavm/maven/res/toggle-small.png", "res/toggle-small.png");
|
||||
resourceToFile("org/teavm/maven/junit.html", "junit.html");
|
||||
final ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader);
|
||||
for (String testClass : testClasses) {
|
||||
ClassHolder classHolder = classSource.get(testClass);
|
||||
if (classHolder == null) {
|
||||
throw new MojoFailureException("Could not find class " + testClass);
|
||||
tool.getTransformers().addAll(instantiateTransformers(classLoader));
|
||||
tool.setLog(new MavenTeaVMToolLog(getLog()));
|
||||
tool.setOutputDir(outputDir);
|
||||
tool.setNumThreads(numThreads);
|
||||
tool.setMinifying(minifying);
|
||||
if (properties != null) {
|
||||
tool.getProperties().putAll(properties);
|
||||
}
|
||||
findTests(classHolder);
|
||||
}
|
||||
|
||||
transformerInstances = instantiateTransformers(classLoader);
|
||||
includeAdditionalScripts(classLoader);
|
||||
File allTestsFile = new File(outputDir, "tests/all.js");
|
||||
try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) {
|
||||
allTestsWriter.write("prepare = function() {\n");
|
||||
allTestsWriter.write(" return new JUnitServer(document.body).readTests([");
|
||||
boolean first = true;
|
||||
for (String testClass : testClasses) {
|
||||
if (!first) {
|
||||
allTestsWriter.append(",");
|
||||
}
|
||||
first = false;
|
||||
allTestsWriter.append("\n { name : \"").append(testClass).append("\", methods : [");
|
||||
boolean firstMethod = true;
|
||||
for (MethodReference methodRef : groupedMethods.get(testClass)) {
|
||||
String scriptName = "tests/" + fileNames.size() + ".js";
|
||||
fileNames.put(methodRef, scriptName);
|
||||
if (!firstMethod) {
|
||||
allTestsWriter.append(",");
|
||||
}
|
||||
firstMethod = false;
|
||||
allTestsWriter.append("\n { name : \"" + methodRef.getName() + "\", script : \"" +
|
||||
scriptName + "\", expected : [");
|
||||
MethodHolder methodHolder = classSource.get(testClass).getMethod(
|
||||
methodRef.getDescriptor());
|
||||
boolean firstException = true;
|
||||
for (String exception : adapter.getExpectedExceptions(methodHolder)) {
|
||||
if (!firstException) {
|
||||
allTestsWriter.append(", ");
|
||||
}
|
||||
firstException = false;
|
||||
allTestsWriter.append("\"" + exception + "\"");
|
||||
}
|
||||
allTestsWriter.append("], additionalScripts : [");
|
||||
for (int i = 0; i < additionalScriptLocalPaths.size(); ++i) {
|
||||
if (i > 0) {
|
||||
allTestsWriter.append(", ");
|
||||
}
|
||||
escapeString(additionalScriptLocalPaths.get(i), allTestsWriter);
|
||||
}
|
||||
allTestsWriter.append("] }");
|
||||
}
|
||||
allTestsWriter.append("] }");
|
||||
}
|
||||
allTestsWriter.write("], function() {}); }");
|
||||
}
|
||||
int methodsGenerated = 0;
|
||||
log.info("Generating test files");
|
||||
FiniteExecutor executor = new SimpleFiniteExecutor();
|
||||
if (numThreads != 1) {
|
||||
int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors();
|
||||
final ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads);
|
||||
finalizer = new Runnable() {
|
||||
@Override public void run() {
|
||||
threadedExecutor.stop();
|
||||
}
|
||||
};
|
||||
executor = threadedExecutor;
|
||||
}
|
||||
for (final MethodReference method : testMethods) {
|
||||
executor.execute(new Runnable() {
|
||||
@Override public void run() {
|
||||
log.debug("Building test for " + method);
|
||||
try {
|
||||
decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), method,
|
||||
fileNames.get(method), new SimpleFiniteExecutor());
|
||||
} catch (IOException e) {
|
||||
log.error("Error generating JavaScript", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
++methodsGenerated;
|
||||
}
|
||||
executor.complete();
|
||||
log.info("Test files successfully generated for " + methodsGenerated + " method(s).");
|
||||
} catch (IOException e) {
|
||||
throw new MojoFailureException("IO error occured generating JavaScript files", e);
|
||||
} finally {
|
||||
if (finalizer != null) {
|
||||
finalizer.run();
|
||||
if (additionalScripts != null) {
|
||||
tool.getAdditionalScripts().addAll(Arrays.asList(additionalScripts));
|
||||
}
|
||||
tool.generate();
|
||||
} catch (TeaVMToolException e) {
|
||||
throw new MojoFailureException("Error occured generating JavaScript files", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createAdapter(ClassLoader classLoader) throws MojoExecutionException {
|
||||
private TestAdapter createAdapter(ClassLoader classLoader) throws MojoExecutionException {
|
||||
Class<?> adapterClsRaw;
|
||||
try {
|
||||
adapterClsRaw = Class.forName(adapterClass, true, classLoader);
|
||||
|
@ -290,7 +187,7 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
throw new MojoExecutionException("No default constructor found for test adapter " + adapterClass, e);
|
||||
}
|
||||
try {
|
||||
adapter = cons.newInstance();
|
||||
return cons.newInstance();
|
||||
} catch (IllegalAccessException | InstantiationException e) {
|
||||
throw new MojoExecutionException("Error creating test adapter", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
|
@ -330,89 +227,6 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
}
|
||||
}
|
||||
|
||||
private void decompileClassesForTest(ClassLoader classLoader, ClassHolderSource classSource,
|
||||
MethodReference methodRef, String targetName, FiniteExecutor executor) throws IOException {
|
||||
TeaVM vm = new TeaVMBuilder()
|
||||
.setClassLoader(classLoader)
|
||||
.setClassSource(classSource)
|
||||
.setExecutor(executor)
|
||||
.build();
|
||||
vm.setProperties(properties);
|
||||
vm.setMinifying(minifying);
|
||||
vm.installPlugins();
|
||||
new TestExceptionPlugin().install(vm);
|
||||
for (ClassHolderTransformer transformer : transformerInstances) {
|
||||
vm.add(transformer);
|
||||
}
|
||||
File file = new File(outputDir, targetName);
|
||||
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
|
||||
MethodReference cons = new MethodReference(methodRef.getClassName(),
|
||||
new MethodDescriptor("<init>", ValueType.VOID));
|
||||
MethodReference exceptionMsg = new MethodReference("java.lang.Throwable", "getMessage",
|
||||
ValueType.object("java.lang.String"));
|
||||
vm.entryPoint("initInstance", cons);
|
||||
vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName());
|
||||
vm.entryPoint("extractException", exceptionMsg);
|
||||
vm.exportType("TestClass", cons.getClassName());
|
||||
vm.build(innerWriter, new DirectoryBuildTarget(outputDir));
|
||||
if (!vm.hasMissingItems()) {
|
||||
innerWriter.append("\n");
|
||||
innerWriter.append("\nJUnitClient.run();");
|
||||
innerWriter.close();
|
||||
} else {
|
||||
innerWriter.append("JUnitClient.reportError(\n");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
vm.showMissingItems(sb);
|
||||
escapeStringLiteral(sb.toString(), innerWriter);
|
||||
innerWriter.append(");");
|
||||
getLog().warn("Error building test " + methodRef);
|
||||
getLog().warn(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void escapeStringLiteral(String text, Writer writer) throws IOException {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
int next = text.indexOf('\n', index);
|
||||
if (next < 0) {
|
||||
break;
|
||||
}
|
||||
escapeString(text.substring(index, next + 1), writer);
|
||||
writer.append(" +\n");
|
||||
index = next + 1;
|
||||
}
|
||||
escapeString(text.substring(index), writer);
|
||||
}
|
||||
|
||||
private void escapeString(String string, Writer writer) throws IOException {
|
||||
writer.append('\"');
|
||||
for (int i = 0; i < string.length(); ++i) {
|
||||
char c = string.charAt(i);
|
||||
switch (c) {
|
||||
case '"':
|
||||
writer.append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
writer.append("\\\\");
|
||||
break;
|
||||
case '\n':
|
||||
writer.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
writer.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
writer.append("\\t");
|
||||
break;
|
||||
default:
|
||||
writer.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
writer.append('\"');
|
||||
}
|
||||
|
||||
private void findTestsInDependencies(ClassLoader classLoader) throws MojoExecutionException {
|
||||
try {
|
||||
Log log = getLog();
|
||||
|
@ -477,8 +291,8 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
}
|
||||
try {
|
||||
Class<?> candidate = Class.forName(className, true, classLoader);
|
||||
if (adapter.acceptClass(candidate)) {
|
||||
testClasses.add(candidate.getName());
|
||||
if (tool.getAdapter().acceptClass(candidate)) {
|
||||
tool.getTestClasses().add(candidate.getName());
|
||||
getLog().info("Test class detected: " + candidate.getName());
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
|
@ -486,28 +300,6 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
}
|
||||
}
|
||||
|
||||
private void findTests(ClassHolder cls) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (adapter.acceptMethod(method)) {
|
||||
MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor());
|
||||
testMethods.add(ref);
|
||||
List<MethodReference> group = groupedMethods.get(cls.getName());
|
||||
if (group == null) {
|
||||
group = new ArrayList<>();
|
||||
groupedMethods.put(cls.getName(), group);
|
||||
}
|
||||
group.add(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resourceToFile(String resource, String fileName) throws IOException {
|
||||
try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resource)) {
|
||||
try (OutputStream output = new FileOutputStream(new File(outputDir, fileName))) {
|
||||
IOUtils.copy(input, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
|
||||
throws MojoExecutionException {
|
||||
|
@ -543,29 +335,4 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
|
|||
}
|
||||
return transformerInstances;
|
||||
}
|
||||
|
||||
private void includeAdditionalScripts(ClassLoader classLoader) throws MojoExecutionException {
|
||||
if (additionalScripts == null) {
|
||||
return;
|
||||
}
|
||||
for (String script : additionalScripts) {
|
||||
String simpleName = script.substring(script.lastIndexOf('/') + 1);
|
||||
additionalScriptLocalPaths.add("tests/" + simpleName);
|
||||
if (classLoader.getResource(script) == null) {
|
||||
throw new MojoExecutionException("Additional script " + script + " was not found");
|
||||
}
|
||||
File file = new File(outputDir, "tests/" + simpleName);
|
||||
try (InputStream in = classLoader.getResourceAsStream(script)) {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
try(OutputStream out = new FileOutputStream(file)) {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MojoExecutionException("Error copying additional script " + script, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package org.teavm.maven;
|
||||
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
import org.teavm.tooling.TeaVMToolLog;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class MavenTeaVMToolLog implements TeaVMToolLog {
|
||||
private Log log;
|
||||
|
||||
public MavenTeaVMToolLog(Log log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String text) {
|
||||
log.info(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String text) {
|
||||
log.debug(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String text) {
|
||||
log.warn(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String text) {
|
||||
log.error(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String text, Throwable e) {
|
||||
log.info(text, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String text, Throwable e) {
|
||||
log.debug(text, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String text, Throwable e) {
|
||||
log.warn(text, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String text, Throwable e) {
|
||||
log.error(text, e);
|
||||
}
|
||||
}
|