diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..99a559dad --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: java +jdk: + - oraclejdk8 +cache: + directories: + - $HOME/.m2 +script: > + mvn test \ + -Dteavm.test.skip=false \ + -Dteavm.test.selenium="http://$SAUCE_USER_NAME:$SAUCE_ACCESS_KEY@ondemand.saucelabs.com:80/wd/hub" \ + -Dteavm.test.threads=2 +after_script: + - rm -rf $HOME/.m2/repository/org/teavm \ No newline at end of file diff --git a/README.md b/README.md index 38ff1e579..c17b93c53 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ TeaVM ===== +[![Build Status](https://travis-ci.org/konsoletyper/teavm.svg?branch=selenium)](https://travis-ci.org/konsoletyper/teavm) + What is TeaVM? -------------- diff --git a/core/pom.xml b/core/pom.xml index 798a6b5a8..98b1d0192 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -45,6 +45,11 @@ hppc 0.6.1 + + com.fasterxml.jackson.core + jackson-annotations + true + TeaVM core diff --git a/core/src/main/java/org/teavm/model/MethodReference.java b/core/src/main/java/org/teavm/model/MethodReference.java index a60d8b4ea..d5ed4a70f 100644 --- a/core/src/main/java/org/teavm/model/MethodReference.java +++ b/core/src/main/java/org/teavm/model/MethodReference.java @@ -15,6 +15,8 @@ */ package org.teavm.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; import java.util.Arrays; /** @@ -132,6 +134,7 @@ public class MethodReference { } @Override + @JsonValue public String toString() { if (reprCache == null) { reprCache = className + "." + name + signatureToString(); @@ -139,6 +142,7 @@ public class MethodReference { return reprCache; } + @JsonCreator public static MethodReference parse(String string) { MethodReference reference = parseIfPossible(string); if (reference == null) { diff --git a/html4j/pom.xml b/html4j/pom.xml index bffacb498..4626c9ea4 100644 --- a/html4j/pom.xml +++ b/html4j/pom.xml @@ -74,12 +74,11 @@ generate-javascript-tests - build-test-javascript + testCompile - process-test-classes false - ${project.build.directory}/javascript-test + ${project.build.directory}/javascript-test true true @@ -87,13 +86,12 @@ generate-javascript-tck - build-test-javascript + testCompile - process-test-classes false true - ${project.build.directory}/javascript-tck + ${project.build.directory}/javascript-tck org.teavm.html4j.testing.KOTestAdapter org.teavm.javascript.NullPointerExceptionTransformer diff --git a/jso/apis/pom.xml b/jso/apis/pom.xml index fe2ced996..4ff751af4 100644 --- a/jso/apis/pom.xml +++ b/jso/apis/pom.xml @@ -21,6 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-jso-apis diff --git a/jso/core/pom.xml b/jso/core/pom.xml index 89a011db5..f44fe2475 100644 --- a/jso/core/pom.xml +++ b/jso/core/pom.xml @@ -21,6 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-jso diff --git a/jso/impl/pom.xml b/jso/impl/pom.xml index f2c9e9bf3..c596a2929 100644 --- a/jso/impl/pom.xml +++ b/jso/impl/pom.xml @@ -21,6 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-jso-impl diff --git a/platform/pom.xml b/platform/pom.xml index 6299a95bc..d606b308a 100644 --- a/platform/pom.xml +++ b/platform/pom.xml @@ -40,11 +40,6 @@ teavm-jso-apis ${project.version} - - org.teavm - teavm-dom - ${project.version} - junit junit diff --git a/pom.xml b/pom.xml index e10fe10f4..21282c232 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,8 @@ 1.2 9.2.1.v20140609 1.7.7 - 1.9.3 + 2.47.2 + 2.6.2 @@ -81,6 +82,7 @@ html4j samples platform + tools/core tools/cli tools/maven tools/chrome-rdp @@ -166,13 +168,26 @@ slf4j-api ${slf4j.version} - diff --git a/samples/async/pom.xml b/samples/async/pom.xml index 001e04006..2ea775d90 100644 --- a/samples/async/pom.xml +++ b/samples/async/pom.xml @@ -57,9 +57,8 @@ web-client - prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm diff --git a/samples/benchmark/pom.xml b/samples/benchmark/pom.xml index ed0590921..4b1bfd80a 100644 --- a/samples/benchmark/pom.xml +++ b/samples/benchmark/pom.xml @@ -27,13 +27,7 @@ org.teavm - teavm-jso - ${project.version} - provided - - - org.teavm - teavm-dom + teavm-jso-apis ${project.version} provided @@ -111,9 +105,8 @@ web-client - prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm diff --git a/samples/hello/pom.xml b/samples/hello/pom.xml index ed8128e63..b07252129 100644 --- a/samples/hello/pom.xml +++ b/samples/hello/pom.xml @@ -42,12 +42,6 @@ ${project.version} provided - - org.teavm - teavm-dom - ${project.version} - provided - javax.servlet javax.servlet-api @@ -76,9 +70,8 @@ web-client - prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm diff --git a/samples/kotlin/pom.xml b/samples/kotlin/pom.xml index f9fcc0606..ce44c2989 100644 --- a/samples/kotlin/pom.xml +++ b/samples/kotlin/pom.xml @@ -53,7 +53,7 @@ org.teavm - teavm-dom + teavm-jso-apis ${project.version} provided @@ -92,9 +92,8 @@ web-client - prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm diff --git a/samples/scala/pom.xml b/samples/scala/pom.xml index e1c2c61a0..f0837e8cc 100644 --- a/samples/scala/pom.xml +++ b/samples/scala/pom.xml @@ -67,9 +67,8 @@ web-client - prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm diff --git a/samples/storage/pom.xml b/samples/storage/pom.xml index 70d0cc0bc..4bea360f1 100644 --- a/samples/storage/pom.xml +++ b/samples/storage/pom.xml @@ -38,13 +38,7 @@ org.teavm - teavm-jso - ${project.version} - provided - - - org.teavm - teavm-dom + teavm-jso-apis ${project.version} provided @@ -78,12 +72,11 @@ web-client prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm org.teavm.samples.storage.Application - SEPARATE false true true diff --git a/samples/video/pom.xml b/samples/video/pom.xml index 1f64bc021..02867ca12 100644 --- a/samples/video/pom.xml +++ b/samples/video/pom.xml @@ -38,13 +38,7 @@ org.teavm - teavm-jso - ${project.version} - provided - - - org.teavm - teavm-dom + teavm-jso-apis ${project.version} provided @@ -76,9 +70,8 @@ web-client - prepare-package - build-javascript + compile ${project.build.directory}/generated/js/teavm diff --git a/tests/pom.xml b/tests/pom.xml index f7a6d202d..002d0faef 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -27,6 +27,13 @@ TeaVM tests Project containing TeaVM tests, as it is impossible to test each module separately + + false + 1 + + true + + org.teavm @@ -72,9 +79,8 @@ generate-javascript-tests - build-test-javascript + testCompile - process-test-classes false true @@ -89,6 +95,17 @@ ${teavm.test.incremental} + + run-javascript-tests + + test + + + ${teavm.test.skip} + ${teavm.test.threads} + ${teavm.test.selenium} + + diff --git a/tools/chrome-rdp/pom.xml b/tools/chrome-rdp/pom.xml index cc17209eb..ab0e3875e 100644 --- a/tools/chrome-rdp/pom.xml +++ b/tools/chrome-rdp/pom.xml @@ -21,6 +21,7 @@ org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-chrome-rdp diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml index 7d1e838c8..51557637f 100644 --- a/tools/cli/pom.xml +++ b/tools/cli/pom.xml @@ -21,6 +21,7 @@ org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-cli @@ -30,7 +31,7 @@ org.teavm - teavm-core + teavm-tooling ${project.version} diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMTestRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMTestRunner.java index 32bf89b19..0a2a45328 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMTestRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMTestRunner.java @@ -19,11 +19,17 @@ import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; -import org.apache.commons.cli.*; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; import org.teavm.model.ClassHolderTransformer; import org.teavm.testing.TestAdapter; -import org.teavm.tooling.TeaVMTestTool; import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.testing.TeaVMTestTool; /** * @@ -84,7 +90,7 @@ public final class TeaVMTestRunner { } TeaVMTestTool tool = new TeaVMTestTool(); - tool.setOutputDir(new File(commandLine.getOptionValue("d", "."))); + tool.setTargetDirectory(new File(commandLine.getOptionValue("d", "."))); tool.setMinifying(commandLine.hasOption("m")); try { tool.setNumThreads(Integer.parseInt(commandLine.getOptionValue("t", "1"))); diff --git a/tools/core/pom.xml b/tools/core/pom.xml new file mode 100644 index 000000000..54c00a376 --- /dev/null +++ b/tools/core/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + + org.teavm + teavm + 0.4.0-SNAPSHOT + ../.. + + teavm-tooling + bundle + + TeaVM tooling core + TeaVM API that helps to create tooling + + + + org.teavm + teavm-core + ${project.version} + + + com.fasterxml.jackson.core + jackson-annotations + true + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ../../checkstyle.xml + config_loc=${basedir}/../.. + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + org.teavm.* + teavm-tooling + + + + + + \ No newline at end of file diff --git a/tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java new file mode 100644 index 000000000..b279ff85b --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java @@ -0,0 +1,50 @@ +/* + * 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.tooling; + +import java.io.File; +import java.util.List; +import java.util.Properties; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.tooling.sources.SourceFileProvider; + +/** + * + * @author Alexey Andreev + */ +public interface BaseTeaVMTool { + void setTargetDirectory(File targetDirectory); + + void setMinifying(boolean minifying); + + void setIncremental(boolean incremental); + + void setDebugInformationGenerated(boolean debugInformationGenerated); + + void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated); + + void setSourceFilesCopied(boolean sourceFilesCopied); + + Properties getProperties(); + + List getTransformers(); + + void setLog(TeaVMToolLog log); + + void setClassLoader(ClassLoader classLoader); + + void addSourceFileProvider(SourceFileProvider sourceFileProvider); +} diff --git a/core/src/main/java/org/teavm/tooling/ClassAlias.java b/tools/core/src/main/java/org/teavm/tooling/ClassAlias.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/ClassAlias.java rename to tools/core/src/main/java/org/teavm/tooling/ClassAlias.java diff --git a/core/src/main/java/org/teavm/tooling/EmptyTeaVMToolLog.java b/tools/core/src/main/java/org/teavm/tooling/EmptyTeaVMToolLog.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/EmptyTeaVMToolLog.java rename to tools/core/src/main/java/org/teavm/tooling/EmptyTeaVMToolLog.java diff --git a/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java b/tools/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/InstructionLocationReader.java rename to tools/core/src/main/java/org/teavm/tooling/InstructionLocationReader.java diff --git a/core/src/main/java/org/teavm/tooling/MethodAlias.java b/tools/core/src/main/java/org/teavm/tooling/MethodAlias.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/MethodAlias.java rename to tools/core/src/main/java/org/teavm/tooling/MethodAlias.java diff --git a/core/src/main/java/org/teavm/tooling/RuntimeCopyOperation.java b/tools/core/src/main/java/org/teavm/tooling/RuntimeCopyOperation.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/RuntimeCopyOperation.java rename to tools/core/src/main/java/org/teavm/tooling/RuntimeCopyOperation.java diff --git a/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java rename to tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java diff --git a/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java similarity index 98% rename from core/src/main/java/org/teavm/tooling/TeaVMTool.java rename to tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index ae767fb16..5938e2e83 100644 --- a/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -29,6 +29,8 @@ import org.teavm.diagnostics.ProblemProvider; import org.teavm.javascript.RenderingContext; import org.teavm.model.*; import org.teavm.parsing.ClasspathClassHolderSource; +import org.teavm.tooling.sources.SourceFileProvider; +import org.teavm.tooling.sources.SourceFilesCopier; import org.teavm.vm.*; import org.teavm.vm.spi.AbstractRendererListener; @@ -36,7 +38,7 @@ import org.teavm.vm.spi.AbstractRendererListener; * * @author Alexey Andreev */ -public class TeaVMTool { +public class TeaVMTool implements BaseTeaVMTool { private File targetDirectory = new File("."); private String targetFileName = "classes.js"; private boolean minifying = true; @@ -69,6 +71,7 @@ public class TeaVMTool { return targetDirectory; } + @Override public void setTargetDirectory(File targetDirectory) { this.targetDirectory = targetDirectory; } @@ -85,6 +88,7 @@ public class TeaVMTool { return minifying; } + @Override public void setMinifying(boolean minifying) { this.minifying = minifying; } @@ -93,6 +97,7 @@ public class TeaVMTool { return incremental; } + @Override public void setIncremental(boolean incremental) { this.incremental = incremental; } @@ -133,6 +138,7 @@ public class TeaVMTool { return debugInformationGenerated; } + @Override public void setDebugInformationGenerated(boolean debugInformationGenerated) { this.debugInformationGenerated = debugInformationGenerated; } @@ -149,6 +155,7 @@ public class TeaVMTool { return sourceMapsFileGenerated; } + @Override public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) { this.sourceMapsFileGenerated = sourceMapsFileGenerated; } @@ -157,14 +164,17 @@ public class TeaVMTool { return sourceFilesCopied; } + @Override public void setSourceFilesCopied(boolean sourceFilesCopied) { this.sourceFilesCopied = sourceFilesCopied; } + @Override public Properties getProperties() { return properties; } + @Override public List getTransformers() { return transformers; } @@ -181,6 +191,7 @@ public class TeaVMTool { return log; } + @Override public void setLog(TeaVMToolLog log) { this.log = log; } @@ -189,6 +200,7 @@ public class TeaVMTool { return classLoader; } + @Override public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @@ -245,6 +257,7 @@ public class TeaVMTool { return resources; } + @Override public void addSourceFileProvider(SourceFileProvider sourceFileProvider) { sourceFileProviders.add(sourceFileProvider); } diff --git a/core/src/main/java/org/teavm/tooling/TeaVMToolException.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMToolException.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/TeaVMToolException.java rename to tools/core/src/main/java/org/teavm/tooling/TeaVMToolException.java diff --git a/core/src/main/java/org/teavm/tooling/TeaVMToolLog.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMToolLog.java similarity index 100% rename from core/src/main/java/org/teavm/tooling/TeaVMToolLog.java rename to tools/core/src/main/java/org/teavm/tooling/TeaVMToolLog.java diff --git a/core/src/main/java/org/teavm/tooling/DirectorySourceFileProvider.java b/tools/core/src/main/java/org/teavm/tooling/sources/DirectorySourceFileProvider.java similarity index 97% rename from core/src/main/java/org/teavm/tooling/DirectorySourceFileProvider.java rename to tools/core/src/main/java/org/teavm/tooling/sources/DirectorySourceFileProvider.java index 21841bed8..aaf0e2abc 100644 --- a/core/src/main/java/org/teavm/tooling/DirectorySourceFileProvider.java +++ b/tools/core/src/main/java/org/teavm/tooling/sources/DirectorySourceFileProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.sources; import java.io.File; import java.io.FileInputStream; diff --git a/core/src/main/java/org/teavm/tooling/JarSourceFileProvider.java b/tools/core/src/main/java/org/teavm/tooling/sources/JarSourceFileProvider.java similarity index 98% rename from core/src/main/java/org/teavm/tooling/JarSourceFileProvider.java rename to tools/core/src/main/java/org/teavm/tooling/sources/JarSourceFileProvider.java index 88b747199..07269d76f 100644 --- a/core/src/main/java/org/teavm/tooling/JarSourceFileProvider.java +++ b/tools/core/src/main/java/org/teavm/tooling/sources/JarSourceFileProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.sources; import java.io.File; import java.io.IOException; diff --git a/core/src/main/java/org/teavm/tooling/ProgramSourceAggregator.java b/tools/core/src/main/java/org/teavm/tooling/sources/ProgramSourceAggregator.java similarity index 99% rename from core/src/main/java/org/teavm/tooling/ProgramSourceAggregator.java rename to tools/core/src/main/java/org/teavm/tooling/sources/ProgramSourceAggregator.java index fd8997642..b9e2170a0 100644 --- a/core/src/main/java/org/teavm/tooling/ProgramSourceAggregator.java +++ b/tools/core/src/main/java/org/teavm/tooling/sources/ProgramSourceAggregator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.sources; import java.util.List; import java.util.Set; diff --git a/core/src/main/java/org/teavm/tooling/SourceFileProvider.java b/tools/core/src/main/java/org/teavm/tooling/sources/SourceFileProvider.java similarity index 96% rename from core/src/main/java/org/teavm/tooling/SourceFileProvider.java rename to tools/core/src/main/java/org/teavm/tooling/sources/SourceFileProvider.java index c5567b036..c1dbebd9b 100644 --- a/core/src/main/java/org/teavm/tooling/SourceFileProvider.java +++ b/tools/core/src/main/java/org/teavm/tooling/sources/SourceFileProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.sources; import java.io.IOException; import java.io.InputStream; diff --git a/core/src/main/java/org/teavm/tooling/SourceFilesCopier.java b/tools/core/src/main/java/org/teavm/tooling/sources/SourceFilesCopier.java similarity index 96% rename from core/src/main/java/org/teavm/tooling/SourceFilesCopier.java rename to tools/core/src/main/java/org/teavm/tooling/sources/SourceFilesCopier.java index f400c20bf..746f38bdf 100644 --- a/core/src/main/java/org/teavm/tooling/SourceFilesCopier.java +++ b/tools/core/src/main/java/org/teavm/tooling/sources/SourceFilesCopier.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.sources; import java.io.*; import java.util.HashSet; @@ -23,6 +23,8 @@ import org.apache.commons.io.IOUtils; import org.teavm.model.ClassReader; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodReader; +import org.teavm.tooling.EmptyTeaVMToolLog; +import org.teavm.tooling.TeaVMToolLog; /** * diff --git a/core/src/main/java/org/teavm/tooling/ExceptionHelper.java b/tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java similarity index 95% rename from core/src/main/java/org/teavm/tooling/ExceptionHelper.java rename to tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java index ac53214c3..1977a6c7b 100644 --- a/core/src/main/java/org/teavm/tooling/ExceptionHelper.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/ExceptionHelper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.testing; /** * diff --git a/core/src/main/java/org/teavm/tooling/TeaVMTestTool.java b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java similarity index 64% rename from core/src/main/java/org/teavm/tooling/TeaVMTestTool.java rename to tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java index a2895b0eb..b65841aa6 100644 --- a/core/src/main/java/org/teavm/tooling/TeaVMTestTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestTool.java @@ -13,10 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.testing; -import java.io.*; -import java.util.*; +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.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.IOUtils; import org.teavm.common.FiniteExecutor; import org.teavm.common.SimpleFiniteExecutor; @@ -26,10 +35,26 @@ import org.teavm.debugging.information.DebugInformationBuilder; import org.teavm.javascript.EmptyRegularMethodNodeCache; import org.teavm.javascript.InMemoryRegularMethodNodeCache; import org.teavm.javascript.MethodNodeCache; -import org.teavm.model.*; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.CopyClassHolderSource; +import org.teavm.model.InMemoryProgramCache; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.PreOptimizingClassHolderSource; +import org.teavm.model.ProgramCache; +import org.teavm.model.ValueType; import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.testing.JUnitTestAdapter; import org.teavm.testing.TestAdapter; +import org.teavm.tooling.BaseTeaVMTool; +import org.teavm.tooling.EmptyTeaVMToolLog; +import org.teavm.tooling.TeaVMProblemRenderer; +import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.TeaVMToolLog; +import org.teavm.tooling.sources.SourceFileProvider; +import org.teavm.tooling.sources.SourceFilesCopier; import org.teavm.vm.DirectoryBuildTarget; import org.teavm.vm.TeaVM; import org.teavm.vm.TeaVMBuilder; @@ -38,10 +63,7 @@ import org.teavm.vm.TeaVMBuilder; * * @author Alexey Andreev */ -public class TeaVMTestTool { - private Map> groupedMethods = new HashMap<>(); - private Map fileNames = new HashMap<>(); - private List testMethods = new ArrayList<>(); +public class TeaVMTestTool implements BaseTeaVMTool { private File outputDir = new File("."); private boolean minifying = true; private int numThreads = 1; @@ -54,19 +76,25 @@ public class TeaVMTestTool { private ClassLoader classLoader = TeaVMTestTool.class.getClassLoader(); private TeaVMToolLog log = new EmptyTeaVMToolLog(); private boolean debugInformationGenerated; - private boolean sourceMapsGenerated; + private boolean sourceMapsFileGenerated; private boolean sourceFilesCopied; private boolean incremental; private List sourceFileProviders = new ArrayList<>(); private MethodNodeCache astCache; private ProgramCache programCache; private SourceFilesCopier sourceFilesCopier; + private List testPlan = new ArrayList<>(); + private int fileIndexGenerator; + private long startTime; + private int testCount; + private AtomicInteger testsBuilt = new AtomicInteger(); public File getOutputDir() { return outputDir; } - public void setOutputDir(File outputDir) { + @Override + public void setTargetDirectory(File outputDir) { this.outputDir = outputDir; } @@ -74,6 +102,7 @@ public class TeaVMTestTool { return minifying; } + @Override public void setMinifying(boolean minifying) { this.minifying = minifying; } @@ -94,6 +123,7 @@ public class TeaVMTestTool { this.adapter = adapter; } + @Override public List getTransformers() { return transformers; } @@ -102,6 +132,7 @@ public class TeaVMTestTool { return additionalScripts; } + @Override public Properties getProperties() { return properties; } @@ -114,6 +145,7 @@ public class TeaVMTestTool { return classLoader; } + @Override public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @@ -122,6 +154,7 @@ public class TeaVMTestTool { return log; } + @Override public void setLog(TeaVMToolLog log) { this.log = log; } @@ -130,6 +163,7 @@ public class TeaVMTestTool { return incremental; } + @Override public void setIncremental(boolean incremental) { this.incremental = incremental; } @@ -138,31 +172,36 @@ public class TeaVMTestTool { return debugInformationGenerated; } + @Override public void setDebugInformationGenerated(boolean debugInformationGenerated) { this.debugInformationGenerated = debugInformationGenerated; } - public boolean isSourceMapsGenerated() { - return sourceMapsGenerated; + public boolean isSourceMapsFileGenerated() { + return sourceMapsFileGenerated; } - public void setSourceMapsGenerated(boolean sourceMapsGenerated) { - this.sourceMapsGenerated = sourceMapsGenerated; + @Override + public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) { + this.sourceMapsFileGenerated = sourceMapsFileGenerated; } public boolean isSourceFilesCopied() { return sourceFilesCopied; } + @Override public void setSourceFilesCopied(boolean sourceFilesCopied) { this.sourceFilesCopied = sourceFilesCopied; } + @Override public void addSourceFileProvider(SourceFileProvider sourceFileProvider) { sourceFileProviders.add(sourceFileProvider); } - public void generate() throws TeaVMToolException { + public TestPlan generate() throws TeaVMToolException { + testsBuilt.set(0); Runnable finalizer = null; try { new File(outputDir, "tests").mkdirs(); @@ -184,12 +223,17 @@ public class TeaVMTestTool { if (incremental) { classSource = new PreOptimizingClassHolderSource(classSource); } + + List groups = new ArrayList<>(); for (String testClass : testClasses) { ClassHolder classHolder = classSource.get(testClass); if (classHolder == null) { throw new TeaVMToolException("Could not find class " + testClass); } - findTests(classHolder); + TestGroup group = findTests(classHolder); + if (group != null) { + groups.add(group); + } } includeAdditionalScripts(classLoader); @@ -198,83 +242,26 @@ public class TeaVMTestTool { astCache = new InMemoryRegularMethodNodeCache(); programCache = new InMemoryProgramCache(); } - 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 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"); - sourceFilesCopier = new SourceFilesCopier(sourceFileProviders); - sourceFilesCopier.setLog(log); + writeMetadata(); + FiniteExecutor executor = new SimpleFiniteExecutor(); if (numThreads != 1) { int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors(); - final ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads); + ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads); finalizer = () -> threadedExecutor.stop(); executor = threadedExecutor; } - for (final MethodReference method : testMethods) { - final ClassHolderSource builderClassSource = classSource; - executor.execute(() -> { - log.debug("Building test for " + method); - try { - decompileClassesForTest(classLoader, new CopyClassHolderSource(builderClassSource), method, - fileNames.get(method)); - } catch (IOException e) { - log.error("Error generating JavaScript", e); - } - }); - ++methodsGenerated; - } - executor.complete(); + startTime = System.currentTimeMillis(); + int methodsGenerated = writeMethods(executor, classSource); + if (sourceFilesCopied) { sourceFilesCopier.copy(new File(new File(outputDir, "tests"), "src")); } - log.info("Test files successfully generated for " + methodsGenerated + " method(s)."); + long timeSpent = System.currentTimeMillis() - startTime; + log.info("Test files successfully generated for " + methodsGenerated + " method(s) in " + + (timeSpent / 1000.0) + " seconds."); + + return new TestPlan("res/runtime.js", groups); } catch (IOException e) { throw new TeaVMToolException("IO error occured generating JavaScript files", e); } finally { @@ -284,6 +271,74 @@ public class TeaVMTestTool { } } + private void writeMetadata() throws IOException { + 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 (TestClassBuilder testClass : testPlan) { + if (!first) { + allTestsWriter.append(","); + } + first = false; + allTestsWriter.append("\n { name : \"").append(testClass.getClassName()) + .append("\", methods : ["); + boolean firstMethod = true; + for (TestMethodBuilder testMethod : testClass.getMethods()) { + String scriptName = testMethod.getFileName(); + if (!firstMethod) { + allTestsWriter.append(","); + } + firstMethod = false; + allTestsWriter.append("\n { name : \"" + testMethod.getMethod().getName() + + "\", script : \"" + scriptName + "\", expected : ["); + boolean firstException = true; + for (String exception : testMethod.getExpectedExceptions()) { + 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() {}); }"); + } + } + + private int writeMethods(FiniteExecutor executor, ClassHolderSource classSource) { + int methodsGenerated = 0; + log.info("Generating test files"); + sourceFilesCopier = new SourceFilesCopier(sourceFileProviders); + sourceFilesCopier.setLog(log); + for (TestClassBuilder testClass : testPlan) { + for (TestMethodBuilder testMethod : testClass.getMethods()) { + executor.execute(() -> { + log.debug("Building test for " + testMethod.getMethod()); + try { + decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), + testMethod); + } catch (IOException e) { + log.error("Error generating JavaScript", e); + } + }); + ++methodsGenerated; + } + } + executor.complete(); + return methodsGenerated; + } + 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))) { @@ -292,19 +347,34 @@ public class TeaVMTestTool { } } - private void findTests(ClassHolder cls) { + private TestGroup findTests(ClassHolder cls) { + List cases = new ArrayList<>(); + TestClassBuilder testClass = new TestClassBuilder(cls.getName()); for (MethodHolder method : cls.getMethods()) { if (adapter.acceptMethod(method)) { MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor()); - testMethods.add(ref); - List group = groupedMethods.get(cls.getName()); - if (group == null) { - group = new ArrayList<>(); - groupedMethods.put(cls.getName(), group); + String fileName = "tests/" + fileIndexGenerator++ + ".js"; + + List exceptions = new ArrayList<>(); + for (String exception : adapter.getExpectedExceptions(method)) { + exceptions.add(exception); } - group.add(ref); + + TestMethodBuilder testMethod = new TestMethodBuilder(ref, fileName, exceptions); + testClass.getMethods().add(testMethod); + + String debugTable = debugInformationGenerated ? testMethod.getFileName() + ".teavmdbg" : null; + cases.add(new TestCase(ref.toString(), testMethod.getFileName(), debugTable, + testMethod.getExpectedExceptions())); + ++testCount; } } + if (!testClass.getMethods().isEmpty()) { + testPlan.add(testClass); + return new TestGroup(cls.getName(), cases); + } else { + return null; + } } private void includeAdditionalScripts(ClassLoader classLoader) throws TeaVMToolException { @@ -332,7 +402,8 @@ public class TeaVMTestTool { } private void decompileClassesForTest(ClassLoader classLoader, ClassHolderSource classSource, - MethodReference methodRef, String targetName) throws IOException { + TestMethodBuilder testMethod) throws IOException { + String targetName = testMethod.getFileName(); TeaVM vm = new TeaVMBuilder() .setClassLoader(classLoader) .setClassSource(classSource) @@ -347,9 +418,11 @@ public class TeaVMTestTool { for (ClassHolderTransformer transformer : transformers) { vm.add(transformer); } - File file = new File(outputDir, targetName); - DebugInformationBuilder debugInfoBuilder = sourceMapsGenerated || debugInformationGenerated + + File file = new File(outputDir, testMethod.getFileName()); + DebugInformationBuilder debugInfoBuilder = sourceMapsFileGenerated || debugInformationGenerated ? new DebugInformationBuilder() : null; + MethodReference methodRef = testMethod.getMethod(); try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { MethodReference cons = new MethodReference(methodRef.getClassName(), "", ValueType.VOID); MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", @@ -362,7 +435,7 @@ public class TeaVMTestTool { vm.build(innerWriter, new DirectoryBuildTarget(outputDir)); innerWriter.append("\n"); innerWriter.append("\nJUnitClient.run();"); - if (sourceMapsGenerated) { + if (sourceMapsFileGenerated) { String sourceMapsFileName = targetName.substring(targetName.lastIndexOf('/') + 1) + ".map"; innerWriter.append("\n//# sourceMappingURL=").append(sourceMapsFileName); } @@ -376,13 +449,16 @@ public class TeaVMTestTool { } } } - if (sourceMapsGenerated) { + + if (debugInformationGenerated) { DebugInformation debugInfo = debugInfoBuilder.getDebugInformation(); - try (OutputStream debugInfoOut = new FileOutputStream(new File(outputDir, targetName + ".teavmdbg"))) { + File debugTableFile = new File(outputDir, targetName + ".teavmdbg"); + try (OutputStream debugInfoOut = new FileOutputStream(debugTableFile)) { debugInfo.write(debugInfoOut); } } - if (sourceMapsGenerated) { + + if (sourceMapsFileGenerated) { DebugInformation debugInfo = debugInfoBuilder.getDebugInformation(); String sourceMapsFileName = targetName + ".map"; try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream( @@ -393,6 +469,20 @@ public class TeaVMTestTool { if (sourceFilesCopied && vm.getWrittenClasses() != null) { sourceFilesCopier.addClasses(vm.getWrittenClasses()); } + + incrementCounter(); + } + + private void incrementCounter() { + int count = testsBuilt.incrementAndGet(); + if (count % 10 != 0) { + return; + } + + long timeSpent = System.currentTimeMillis() - startTime; + + getLog().info(count + " of " + testCount + " tests built in " + (timeSpent / 1000.0) + " seconds (" + + String.format("%.2f", (double) count / timeSpent * 1000.0) + " tests per second avg.)"); } private void escapeString(String string, Writer writer) throws IOException { diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java new file mode 100644 index 000000000..4460b2575 --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TeaVMTestToolListener.java @@ -0,0 +1,25 @@ +/* + * 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.tooling.testing; + + +/** + * + * @author Alexey Andreev + */ +public interface TeaVMTestToolListener { + void testGenerated(TestCase testCase); +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java new file mode 100644 index 000000000..5c0ae9e1b --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestCase.java @@ -0,0 +1,66 @@ +/* + * 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.tooling.testing; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +public class TestCase { + private String testMethod; + private String testScript; + private String debugTable; + private List expectedExceptions = new ArrayList<>(); + + @JsonCreator + public TestCase( + @JsonProperty("testMethod") String testMethod, + @JsonProperty("script") String testScript, + @JsonProperty("debugTable") String debugTable, + @JsonProperty("expectedExceptions") List expectedExceptions) { + this.testMethod = testMethod; + this.testScript = testScript; + this.debugTable = debugTable; + this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); + } + + @JsonGetter + public String getTestMethod() { + return testMethod; + } + + @JsonGetter("script") + public String getTestScript() { + return testScript; + } + + @JsonGetter + public String getDebugTable() { + return debugTable; + } + + @JsonGetter + public List getExpectedExceptions() { + return expectedExceptions; + } +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java new file mode 100644 index 000000000..d50e2b264 --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestClassBuilder.java @@ -0,0 +1,40 @@ +/* + * 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.tooling.testing; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +class TestClassBuilder { + private String className; + private List methods = new ArrayList<>(); + + public TestClassBuilder(String className) { + this.className = className; + } + + public String getClassName() { + return className; + } + + public List getMethods() { + return methods; + } +} diff --git a/core/src/main/java/org/teavm/tooling/TestExceptionDependency.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java similarity index 98% rename from core/src/main/java/org/teavm/tooling/TestExceptionDependency.java rename to tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java index 9fa8acd60..e388e80f6 100644 --- a/core/src/main/java/org/teavm/tooling/TestExceptionDependency.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionDependency.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.testing; import org.teavm.dependency.AbstractDependencyListener; import org.teavm.dependency.DependencyAgent; diff --git a/core/src/main/java/org/teavm/tooling/TestExceptionPlugin.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java similarity index 96% rename from core/src/main/java/org/teavm/tooling/TestExceptionPlugin.java rename to tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java index 2ec7136c6..19a2c04a1 100644 --- a/core/src/main/java/org/teavm/tooling/TestExceptionPlugin.java +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestExceptionPlugin.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.tooling; +package org.teavm.tooling.testing; import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMPlugin; diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java new file mode 100644 index 000000000..4ab9d5cbb --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestGroup.java @@ -0,0 +1,49 @@ +/* + * 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.tooling.testing; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +public class TestGroup { + private String className; + private List testCases; + + @JsonCreator + public TestGroup(@JsonProperty("className") String className, + @JsonProperty("testCases") List testCases) { + this.className = className; + this.testCases = Collections.unmodifiableList(new ArrayList<>(testCases)); + } + + @JsonGetter("className") + public String getClassName() { + return className; + } + + @JsonGetter("testCases") + public List getTestCases() { + return testCases; + } +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java new file mode 100644 index 000000000..3faa384d7 --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestMethodBuilder.java @@ -0,0 +1,49 @@ +/* + * 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.tooling.testing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +class TestMethodBuilder { + private MethodReference method; + private String fileName; + private List expectedExceptions = new ArrayList<>(); + + public TestMethodBuilder(MethodReference method, String fileName, List expectedExceptions) { + this.method = method; + this.fileName = fileName; + this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); + } + + public MethodReference getMethod() { + return method; + } + + public String getFileName() { + return fileName; + } + + public List getExpectedExceptions() { + return expectedExceptions; + } +} diff --git a/tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java b/tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java new file mode 100644 index 000000000..32ce4b364 --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/testing/TestPlan.java @@ -0,0 +1,50 @@ +/* + * 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.tooling.testing; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +public class TestPlan { + private String runtimeScript; + private List groups = new ArrayList<>(); + + @JsonCreator + public TestPlan( + @JsonProperty("runtimeScript") String runtimeScript, + @JsonProperty("groups") List groups) { + this.runtimeScript = runtimeScript; + this.groups = Collections.unmodifiableList(new ArrayList<>(groups)); + } + + @JsonGetter + public String getRuntimeScript() { + return runtimeScript; + } + + @JsonGetter + public List getGroups() { + return groups; + } +} diff --git a/core/src/main/resources/org/teavm/tooling/main.html b/tools/core/src/main/resources/org/teavm/tooling/main.html similarity index 100% rename from core/src/main/resources/org/teavm/tooling/main.html rename to tools/core/src/main/resources/org/teavm/tooling/main.html diff --git a/core/src/main/resources/org/teavm/tooling/test/junit.html b/tools/core/src/main/resources/org/teavm/tooling/test/junit.html similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/junit.html rename to tools/core/src/main/resources/org/teavm/tooling/test/junit.html diff --git a/core/src/main/resources/org/teavm/tooling/test/res/class_obj.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/class_obj.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/class_obj.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/class_obj.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/control-000-small.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/control-000-small.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/control-000-small.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/control-000-small.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/junit-support.js b/tools/core/src/main/resources/org/teavm/tooling/test/res/junit-support.js similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/junit-support.js rename to tools/core/src/main/resources/org/teavm/tooling/test/res/junit-support.js diff --git a/core/src/main/resources/org/teavm/tooling/test/res/junit.css b/tools/core/src/main/resources/org/teavm/tooling/test/res/junit.css similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/junit.css rename to tools/core/src/main/resources/org/teavm/tooling/test/res/junit.css diff --git a/core/src/main/resources/org/teavm/tooling/test/res/methpub_obj.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/methpub_obj.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/methpub_obj.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/methpub_obj.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/package_obj.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/package_obj.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/package_obj.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/package_obj.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/tick-small-red.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/tick-small-red.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/tick-small-red.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/tick-small-red.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/tick-small.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/tick-small.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/tick-small.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/tick-small.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/toggle-small-expand.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/toggle-small-expand.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/toggle-small-expand.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/toggle-small-expand.png diff --git a/core/src/main/resources/org/teavm/tooling/test/res/toggle-small.png b/tools/core/src/main/resources/org/teavm/tooling/test/res/toggle-small.png similarity index 100% rename from core/src/main/resources/org/teavm/tooling/test/res/toggle-small.png rename to tools/core/src/main/resources/org/teavm/tooling/test/res/toggle-small.png diff --git a/tools/eclipse/core-plugin/META-INF/MANIFEST.MF b/tools/eclipse/core-plugin/META-INF/MANIFEST.MF index 52513c7cb..b4bee0aa9 100644 --- a/tools/eclipse/core-plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/core-plugin/META-INF/MANIFEST.MF @@ -39,6 +39,7 @@ Bundle-ClassPath: ., lib/slf4j-api-1.7.7.jar, lib/teavm-chrome-rdp-0.4.0-SNAPSHOT.jar, lib/teavm-core-0.4.0-SNAPSHOT.jar, + lib/teavm-tooling-0.4.0-SNAPSHOT.jar, lib/websocket-api-9.2.1.v20140609.jar, lib/websocket-client-9.2.1.v20140609.jar, lib/websocket-common-9.2.1.v20140609.jar, @@ -70,5 +71,6 @@ Export-Package: org.teavm.cache, org.teavm.resource, org.teavm.testing, org.teavm.tooling, + org.teavm.tooling.sources, org.teavm.vm, org.teavm.vm.spi diff --git a/tools/eclipse/core-plugin/build.properties b/tools/eclipse/core-plugin/build.properties index ea8d50c54..95269dd03 100644 --- a/tools/eclipse/core-plugin/build.properties +++ b/tools/eclipse/core-plugin/build.properties @@ -36,6 +36,7 @@ bin.includes = META-INF/,\ lib/slf4j-api-1.7.7.jar,\ lib/teavm-chrome-rdp-0.4.0-SNAPSHOT.jar,\ lib/teavm-core-0.4.0-SNAPSHOT.jar,\ + lib/teavm-tooling-0.4.0-SNAPSHOT.jar,\ lib/websocket-api-9.2.1.v20140609.jar,\ lib/websocket-client-9.2.1.v20140609.jar,\ lib/websocket-common-9.2.1.v20140609.jar,\ diff --git a/tools/eclipse/core-plugin/dep-pom.xml b/tools/eclipse/core-plugin/dep-pom.xml index 41fe15f28..52dd1023d 100644 --- a/tools/eclipse/core-plugin/dep-pom.xml +++ b/tools/eclipse/core-plugin/dep-pom.xml @@ -79,6 +79,11 @@ teavm-core ${project.version} + + org.teavm + teavm-tooling + ${project.version} + org.teavm teavm-chrome-rdp diff --git a/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF b/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF index 062e541a9..1574d6301 100644 --- a/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/m2e-plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for m2e Bundle-SymbolicName: teavm-eclipse-m2e-plugin;singleton:=true Bundle-Version: 0.4.0.qualifier Bundle-Vendor: Alexey Andreev -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: teavm-eclipse-plugin;bundle-version="[0.4.0,0.5.0)", org.eclipse.m2e.core;bundle-version="[1.3,2)", org.eclipse.core.runtime;bundle-version="[3.8,4.0)", diff --git a/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml b/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml index 3ca52caff..e2fb85a6e 100644 --- a/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml +++ b/tools/eclipse/m2e-plugin/lifecycle-mapping-metadata.xml @@ -7,7 +7,7 @@ teavm-maven-plugin 0.4.0-SNAPSHOT - build-javascript + compile diff --git a/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java b/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java index 220d4ed0f..a8ad00c3a 100644 --- a/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java +++ b/tools/eclipse/m2e-plugin/src/main/java/org/teavm/eclipse/m2e/TeaVMProjectConfigurator.java @@ -33,7 +33,7 @@ public class TeaVMProjectConfigurator extends AbstractProjectConfigurator { private static final String TOOL_ID = "teavm-eclipse-m2e-plugin.tool"; private static final String TEAVM_ARTIFACT_ID = "teavm-maven-plugin"; private static final String TEAVM_GROUP_ID = "org.teavm"; - private static final String TEAVM_MAIN_GOAL = "build-javascript"; + private static final String TEAVM_MAIN_GOAL = "compile"; private int executionIdGenerator; private Set usedExecutionIds = new HashSet<>(); private IMaven maven; diff --git a/tools/eclipse/plugin/META-INF/MANIFEST.MF b/tools/eclipse/plugin/META-INF/MANIFEST.MF index 416ca1e23..4a219dbb4 100644 --- a/tools/eclipse/plugin/META-INF/MANIFEST.MF +++ b/tools/eclipse/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: TeaVM plugin for Eclipse Bundle-SymbolicName: teavm-eclipse-plugin;singleton:=true Bundle-Version: 0.4.0.qualifier Bundle-Vendor: Alexey Andreev -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Activator: org.teavm.eclipse.TeaVMEclipsePlugin Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.8.0,4.0)", org.eclipse.debug.core;bundle-version="[3.7.0,4.0)", @@ -48,5 +48,6 @@ Import-Package: org.teavm.cache, org.teavm.resource, org.teavm.testing, org.teavm.tooling, + org.teavm.tooling.sources, org.teavm.vm, org.teavm.vm.spi diff --git a/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java b/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java index 41c2184b7..a09363e27 100644 --- a/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java +++ b/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java @@ -21,22 +21,62 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.WeakHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.core.resources.*; -import org.eclipse.core.runtime.*; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; -import org.eclipse.jdt.core.*; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.Signature; import org.teavm.callgraph.CallGraph; import org.teavm.callgraph.CallGraphNode; import org.teavm.callgraph.CallSite; import org.teavm.diagnostics.Problem; import org.teavm.diagnostics.ProblemTextConsumer; -import org.teavm.model.*; -import org.teavm.tooling.*; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.FieldReference; +import org.teavm.model.InstructionLocation; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.tooling.ClassAlias; +import org.teavm.tooling.RuntimeCopyOperation; +import org.teavm.tooling.TeaVMTool; +import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.sources.DirectorySourceFileProvider; +import org.teavm.tooling.sources.JarSourceFileProvider; +import org.teavm.tooling.sources.SourceFileProvider; /** * diff --git a/tools/eclipse/pom.xml b/tools/eclipse/pom.xml index 9d5cccf51..024fdcba6 100644 --- a/tools/eclipse/pom.xml +++ b/tools/eclipse/pom.xml @@ -21,6 +21,7 @@ org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-eclipse pom diff --git a/tools/maven/plugin/pom.xml b/tools/maven/plugin/pom.xml index 143dd1213..2c7e153df 100644 --- a/tools/maven/plugin/pom.xml +++ b/tools/maven/plugin/pom.xml @@ -50,13 +50,30 @@ org.teavm - teavm-core + teavm-tooling ${project.version} commons-io commons-io + + org.seleniumhq.selenium + selenium-java + + + org.seleniumhq.selenium + selenium-remote-driver + + + com.fasterxml.jackson.core + jackson-databind + + + net.sourceforge.htmlunit + htmlunit + 2.18 + junit junit @@ -91,6 +108,14 @@ org.apache.maven.plugins maven-javadoc-plugin + + org.apache.maven.plugins + maven-checkstyle-plugin + + ../../../checkstyle.xml + config_loc=${basedir}/../../.. + + diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java new file mode 100644 index 000000000..cb43846e3 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java @@ -0,0 +1,219 @@ +/* + * 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.maven; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.repository.RepositorySystem; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.tooling.BaseTeaVMTool; +import org.teavm.tooling.sources.SourceFileProvider; + +/** + * + * @author Alexey Andreev + */ +public abstract class AbstractJavascriptMojo extends AbstractMojo { + @Component + protected MavenProject project; + + @Component + protected RepositorySystem repositorySystem; + + @Parameter(required = true, readonly = true, defaultValue = "${localRepository}") + protected MavenArtifactRepository localRepository; + + @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}") + protected List remoteRepositories; + + @Parameter(readonly = true, defaultValue = "${plugin.artifacts}") + protected List pluginArtifacts; + + @Parameter(defaultValue = "${project.build.outputDirectory}") + protected File classFiles; + + @Parameter + protected List compileScopes; + + @Parameter + protected boolean minifying = true; + + @Parameter + protected String mainClass; + + @Parameter + protected Properties properties; + + @Parameter + protected boolean debugInformationGenerated; + + @Parameter + protected boolean sourceMapsGenerated; + + @Parameter + protected boolean sourceFilesCopied; + + @Parameter + protected boolean incremental; + + @Parameter + protected String[] transformers; + + protected ClassLoader classLoader; + + protected abstract File getTargetDirectory(); + + protected final List instantiateTransformers(ClassLoader classLoader) + throws MojoExecutionException { + List transformerInstances = new ArrayList<>(); + if (transformers == null) { + return transformerInstances; + } + for (String transformerName : transformers) { + Class transformerRawType; + try { + transformerRawType = Class.forName(transformerName, true, classLoader); + } catch (ClassNotFoundException e) { + throw new MojoExecutionException("Transformer not found: " + transformerName, e); + } + if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) { + throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of " + + ClassHolderTransformer.class.getName()); + } + Class transformerType = transformerRawType.asSubclass( + ClassHolderTransformer.class); + Constructor ctor; + try { + ctor = transformerType.getConstructor(); + } catch (NoSuchMethodException e) { + throw new MojoExecutionException("Transformer " + transformerName + " has no default constructor"); + } + try { + ClassHolderTransformer transformer = ctor.newInstance(); + transformerInstances.add(transformer); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new MojoExecutionException("Error instantiating transformer " + transformerName, e); + } + } + return transformerInstances; + } + + protected void setupTool(BaseTeaVMTool tool) throws MojoExecutionException { + tool.setLog(new MavenTeaVMToolLog(getLog())); + try { + ClassLoader classLoader = prepareClassLoader(); + tool.setClassLoader(classLoader); + tool.setMinifying(minifying); + tool.setTargetDirectory(getTargetDirectory()); + tool.getTransformers().addAll(instantiateTransformers(classLoader)); + if (sourceFilesCopied) { + for (SourceFileProvider provider : getSourceFileProviders()) { + tool.addSourceFileProvider(provider); + } + } + if (properties != null) { + tool.getProperties().putAll(properties); + } + tool.setIncremental(incremental); + tool.setDebugInformationGenerated(debugInformationGenerated); + tool.setSourceMapsFileGenerated(sourceMapsGenerated); + tool.setSourceFilesCopied(sourceFilesCopied); + } catch (RuntimeException e) { + throw new MojoExecutionException("Unexpected error occured", e); + } + } + + protected final ClassLoader prepareClassLoader() throws MojoExecutionException { + try { + Log log = getLog(); + log.info("Preparing classpath for JavaScript generation"); + List urls = new ArrayList<>(); + StringBuilder classpath = new StringBuilder(); + for (Artifact artifact : project.getArtifacts()) { + if (!filterByScope(artifact)) { + continue; + } + File file = artifact.getFile(); + if (classpath.length() > 0) { + classpath.append(':'); + } + classpath.append(file.getPath()); + urls.add(file.toURI().toURL()); + } + if (classpath.length() > 0) { + classpath.append(':'); + } + classpath.append(classFiles.getPath()); + urls.add(classFiles.toURI().toURL()); + for (File additionalEntry : getAdditionalClassPath()) { + classpath.append(':').append(additionalEntry.getPath()); + urls.add(additionalEntry.toURI().toURL()); + } + log.info("Using the following classpath for JavaScript generation: " + classpath); + classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), + AbstractJavascriptMojo.class.getClassLoader()); + return classLoader; + } catch (MalformedURLException e) { + throw new MojoExecutionException("Error gathering classpath information", e); + } + } + + protected List getAdditionalClassPath() { + return Collections.emptyList(); + } + + protected boolean filterByScope(Artifact artifact) { + return compileScopes == null ? isSupportedScope(artifact.getScope()) + : compileScopes.contains(artifact.getScope()); + } + + protected boolean isSupportedScope(String scope) { + switch (scope) { + case Artifact.SCOPE_COMPILE: + case Artifact.SCOPE_PROVIDED: + case Artifact.SCOPE_SYSTEM: + return true; + default: + return false; + } + } + + protected final List getSourceFileProviders() { + MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup(); + lookup.setMavenProject(project); + lookup.setRepositorySystem(repositorySystem); + lookup.setLocalRepository(localRepository); + lookup.setRemoteRepositories(remoteRepositories); + lookup.setPluginDependencies(pluginArtifacts); + return lookup.resolve(); + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java index f4c43ec6a..511dd0cb1 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java @@ -16,96 +16,42 @@ package org.teavm.maven; import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.*; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.repository.MavenArtifactRepository; -import org.apache.maven.plugin.AbstractMojo; +import java.util.Arrays; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; 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.apache.maven.repository.RepositorySystem; -import org.teavm.model.ClassHolderTransformer; -import org.teavm.tooling.*; +import org.teavm.tooling.ClassAlias; +import org.teavm.tooling.MethodAlias; +import org.teavm.tooling.RuntimeCopyOperation; +import org.teavm.tooling.TeaVMTool; +import org.teavm.tooling.TeaVMToolException; /** * * @author Alexey Andreev */ -@Mojo(name = "build-javascript", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, - requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class BuildJavascriptMojo extends AbstractMojo { - @Component - private MavenProject project; - - @Component - private RepositorySystem repositorySystem; - - @Parameter(required = true, readonly = true, defaultValue = "${localRepository}") - private MavenArtifactRepository localRepository; - - @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}") - private List remoteRepositories; - - @Parameter(readonly = true, defaultValue = "${plugin.artifacts}") - private List pluginArtifacts; - +@Mojo(name = "compile", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, + requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, + defaultPhase = LifecyclePhase.PROCESS_CLASSES) +public class BuildJavascriptMojo extends AbstractJavascriptMojo { @Parameter(defaultValue = "${project.build.directory}/javascript") private File targetDirectory; - @Parameter(defaultValue = "${project.build.outputDirectory}") - private File classFiles; - - @Parameter - private List compileScopes; - @Parameter private String targetFileName = "classes.js"; - @Parameter - private boolean minifying = true; - @Parameter private String mainClass; - @Parameter - private RuntimeCopyOperation runtime = RuntimeCopyOperation.SEPARATE; - - @Parameter - private Properties properties; - @Parameter private boolean mainPageIncluded; @Parameter private boolean bytecodeLogging; - @Parameter - private boolean debugInformationGenerated; - - @Parameter - private boolean sourceMapsGenerated; - - @Parameter - private boolean sourceFilesCopied; - - @Parameter - private boolean incremental; - - @Parameter(defaultValue = "${project.build.directory}/teavm-cache") - private File cacheDirectory; - - @Parameter - private String[] transformers; - @Parameter private ClassAlias[] classAliases; @@ -115,144 +61,37 @@ public class BuildJavascriptMojo extends AbstractMojo { @Parameter private boolean stopOnErrors = true; + @Parameter + protected RuntimeCopyOperation runtime = RuntimeCopyOperation.SEPARATE; + + @Parameter(defaultValue = "${project.build.directory}/teavm-cache") + protected File cacheDirectory; + private TeaVMTool tool = new TeaVMTool(); - public void setProject(MavenProject project) { - this.project = project; - } - - public void setTargetDirectory(File targetDirectory) { - this.targetDirectory = targetDirectory; - } - - public void setTargetFileName(String targetFileName) { - this.targetFileName = targetFileName; - } - - public void setClassFiles(File classFiles) { - this.classFiles = classFiles; - } - - public void setMinifying(boolean minifying) { - this.minifying = minifying; - } - - public void setBytecodeLogging(boolean bytecodeLogging) { - this.bytecodeLogging = bytecodeLogging; - } - - public void setRuntimeCopy(RuntimeCopyOperation runtimeCopy) { - this.runtime = runtimeCopy; - } - - public void setMainPageIncluded(boolean mainPageIncluded) { - this.mainPageIncluded = mainPageIncluded; - } - - public String[] getTransformers() { - return transformers; - } - - public void setTransformers(String[] transformers) { - this.transformers = transformers; - } - - public void setProperties(Properties properties) { - this.properties = properties; - } - - public void setClassAliases(ClassAlias[] classAliases) { - this.classAliases = classAliases; - } - - public void setMethodAliases(MethodAlias[] methodAliases) { - this.methodAliases = methodAliases; - } - - public boolean isDebugInformationGenerated() { - return debugInformationGenerated; - } - - public void setDebugInformationGenerated(boolean debugInformationGenerated) { - this.debugInformationGenerated = debugInformationGenerated; - } - - public boolean isSourceMapsGenerated() { - return sourceMapsGenerated; - } - - public void setSourceMapsGenerated(boolean sourceMapsGenerated) { - this.sourceMapsGenerated = sourceMapsGenerated; - } - - public boolean isSourceFilesCopied() { - return sourceFilesCopied; - } - - public void setSourceFilesCopied(boolean sourceFilesCopied) { - this.sourceFilesCopied = sourceFilesCopied; - } - - public boolean isIncremental() { - return incremental; - } - - public void setIncremental(boolean incremental) { - this.incremental = incremental; - } - - public void setStopOnErrors(boolean stopOnErrors) { - this.stopOnErrors = stopOnErrors; - } - - public File getCacheDirectory() { - return cacheDirectory; - } - - public void setCacheDirectory(File cacheDirectory) { - this.cacheDirectory = cacheDirectory; + @Override + protected File getTargetDirectory() { + return targetDirectory; } @Override public void execute() throws MojoExecutionException { Log log = getLog(); + setupTool(tool); tool.setLog(new MavenTeaVMToolLog(log)); try { - ClassLoader classLoader = prepareClassLoader(); - tool.setClassLoader(classLoader); tool.setBytecodeLogging(bytecodeLogging); tool.setMainClass(mainClass); tool.setMainPageIncluded(mainPageIncluded); - tool.setMinifying(minifying); tool.setRuntime(runtime); - tool.setTargetDirectory(targetDirectory); tool.setTargetFileName(targetFileName); - tool.getTransformers().addAll(instantiateTransformers(classLoader)); - if (sourceFilesCopied) { - MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup(); - lookup.setMavenProject(project); - lookup.setRepositorySystem(repositorySystem); - lookup.setLocalRepository(localRepository); - lookup.setRemoteRepositories(remoteRepositories); - lookup.setPluginDependencies(pluginArtifacts); - for (SourceFileProvider provider : lookup.resolve()) { - tool.addSourceFileProvider(provider); - } - } if (classAliases != null) { tool.getClassAliases().addAll(Arrays.asList(classAliases)); } if (methodAliases != null) { tool.getMethodAliases().addAll(Arrays.asList(methodAliases)); } - if (properties != null) { - tool.getProperties().putAll(properties); - } tool.setCacheDirectory(cacheDirectory); - tool.setIncremental(incremental); - tool.setDebugInformationGenerated(debugInformationGenerated); - tool.setSourceMapsFileGenerated(sourceMapsGenerated); - tool.setSourceFilesCopied(sourceFilesCopied); tool.generate(); if (stopOnErrors && !tool.getProblemProvider().getSevereProblems().isEmpty()) { throw new MojoExecutionException("Build error"); @@ -263,75 +102,4 @@ public class BuildJavascriptMojo extends AbstractMojo { throw new MojoExecutionException("IO error occured", e); } } - - private List instantiateTransformers(ClassLoader classLoader) - throws MojoExecutionException { - List transformerInstances = new ArrayList<>(); - if (transformers == null) { - return transformerInstances; - } - for (String transformerName : transformers) { - Class transformerRawType; - try { - transformerRawType = Class.forName(transformerName, true, classLoader); - } catch (ClassNotFoundException e) { - throw new MojoExecutionException("Transformer not found: " + transformerName, e); - } - if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) { - throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of " + - ClassHolderTransformer.class.getName()); - } - Class transformerType = transformerRawType.asSubclass( - ClassHolderTransformer.class); - Constructor ctor; - try { - ctor = transformerType.getConstructor(); - } catch (NoSuchMethodException e) { - throw new MojoExecutionException("Transformer " + transformerName + " has no default constructor"); - } - try { - ClassHolderTransformer transformer = ctor.newInstance(); - transformerInstances.add(transformer); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new MojoExecutionException("Error instantiating transformer " + transformerName, e); - } - } - return transformerInstances; - } - - private ClassLoader prepareClassLoader() throws MojoExecutionException { - try { - Log log = getLog(); - log.info("Preparing classpath for JavaScript generation"); - List urls = new ArrayList<>(); - StringBuilder classpath = new StringBuilder(); - Set scopes; - if (compileScopes == null) { - scopes = new HashSet<>(Arrays.asList( - Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, Artifact.SCOPE_SYSTEM)); - } else { - scopes = new HashSet<>(compileScopes); - } - for (Artifact artifact : project.getArtifacts()) { - if (!scopes.contains(artifact.getScope())) { - continue; - } - File file = artifact.getFile(); - if (classpath.length() > 0) { - classpath.append(':'); - } - classpath.append(file.getPath()); - urls.add(file.toURI().toURL()); - } - if (classpath.length() > 0) { - classpath.append(':'); - } - classpath.append(classFiles.getPath()); - urls.add(classFiles.toURI().toURL()); - log.info("Using the following classpath for JavaScript generation: " + classpath); - return new URLClassLoader(urls.toArray(new URL[urls.size()]), BuildJavascriptMojo.class.getClassLoader()); - } catch (MalformedURLException e) { - throw new MojoExecutionException("Error gathering classpath information", e); - } - } } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java index a194b57ad..7b8618570 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java @@ -15,67 +15,50 @@ */ package org.teavm.maven; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.*; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.commons.io.FilenameUtils; import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.repository.MavenArtifactRepository; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; 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.apache.maven.repository.RepositorySystem; -import org.teavm.model.ClassHolderTransformer; import org.teavm.testing.JUnitTestAdapter; import org.teavm.testing.TestAdapter; -import org.teavm.tooling.SourceFileProvider; -import org.teavm.tooling.TeaVMTestTool; import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.testing.TeaVMTestTool; +import org.teavm.tooling.testing.TestPlan; /** * * @author Alexey Andreev */ -@Mojo(name = "build-test-javascript", requiresDependencyResolution = ResolutionScope.TEST, - requiresDependencyCollection = ResolutionScope.TEST) -public class BuildJavascriptTestMojo extends AbstractMojo { +@Mojo(name = "testCompile", requiresDependencyResolution = ResolutionScope.TEST, + requiresDependencyCollection = ResolutionScope.TEST, + defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES) +public class BuildJavascriptTestMojo extends AbstractJavascriptMojo { private static Set testScopes = new HashSet<>(Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_PROVIDED)); - - @Component - private MavenProject project; - - @Component - private RepositorySystem repositorySystem; - - @Parameter(required = true, readonly = true, defaultValue = "${localRepository}") - private MavenArtifactRepository localRepository; - - @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}") - private List remoteRepositories; - - @Parameter(readonly = true, defaultValue = "${plugin.artifacts}") - private List pluginArtifacts; - @Parameter(defaultValue = "${project.build.directory}/javascript-test") - private File outputDir; - - @Parameter(defaultValue = "${project.build.outputDirectory}") - private File classFiles; + private File targetDirectory; @Parameter(defaultValue = "${project.build.testOutputDirectory}") private File testFiles; @@ -86,9 +69,6 @@ public class BuildJavascriptTestMojo extends AbstractMojo { @Parameter private String[] excludeWildcards = new String[0]; - @Parameter - private boolean minifying = true; - @Parameter private boolean scanDependencies; @@ -98,153 +78,47 @@ public class BuildJavascriptTestMojo extends AbstractMojo { @Parameter private String adapterClass = JUnitTestAdapter.class.getName(); - @Parameter - private String[] transformers; - @Parameter private String[] additionalScripts; - @Parameter - private Properties properties; - - @Parameter - private boolean incremental; - - @Parameter - private boolean debugInformationGenerated; - - @Parameter - private boolean sourceMapsGenerated; - - @Parameter - private boolean sourceFilesCopied; - private TeaVMTestTool tool = new TeaVMTestTool(); - public void setProject(MavenProject project) { - this.project = project; - } - - public void setOutputDir(File outputDir) { - this.outputDir = outputDir; - } - - public void setClassFiles(File classFiles) { - this.classFiles = classFiles; - } - - public void setTestFiles(File testFiles) { - this.testFiles = testFiles; - } - - public void setMinifying(boolean minifying) { - this.minifying = minifying; - } - - public void setNumThreads(int numThreads) { - this.numThreads = numThreads; - } - - public void setAdapterClass(String adapterClass) { - this.adapterClass = adapterClass; - } - - public void setWildcards(String[] wildcards) { - this.wildcards = wildcards; - } - - public void setExcludeWildcards(String[] excludeWildcards) { - this.excludeWildcards = excludeWildcards; - } - - public String[] getTransformers() { - return transformers; - } - - public void setTransformers(String[] transformers) { - this.transformers = transformers; - } - - public void setProperties(Properties properties) { - this.properties = properties; - } - - public void setIncremental(boolean incremental) { - this.incremental = incremental; - } - - public boolean isDebugInformationGenerated() { - return debugInformationGenerated; - } - - public void setDebugInformationGenerated(boolean debugInformationGenerated) { - this.debugInformationGenerated = debugInformationGenerated; - } - - public boolean isSourceMapsGenerated() { - return sourceMapsGenerated; - } - - public void setSourceMapsGenerated(boolean sourceMapsGenerated) { - this.sourceMapsGenerated = sourceMapsGenerated; - } - - public boolean isSourceFilesCopied() { - return sourceFilesCopied; - } - - public void setSourceFilesCopied(boolean sourceFilesCopied) { - this.sourceFilesCopied = sourceFilesCopied; - } - @Override public void execute() throws MojoExecutionException, MojoFailureException { - if (System.getProperty("maven.test.skip", "false").equals("true") || - System.getProperty("skipTests") != null) { + if (System.getProperty("maven.test.skip", "false").equals("true") + || System.getProperty("skipTests") != null) { getLog().info("Tests build skipped as specified by system property"); return; } + + setupTool(tool); try { - final ClassLoader classLoader = prepareClassLoader(); getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'"); - tool.setClassLoader(classLoader); tool.setAdapter(createAdapter(classLoader)); findTestClasses(classLoader, testFiles, ""); if (scanDependencies) { findTestsInDependencies(classLoader); } - tool.getTransformers().addAll(instantiateTransformers(classLoader)); - tool.setLog(new MavenTeaVMToolLog(getLog())); - tool.setOutputDir(outputDir); tool.setNumThreads(numThreads); - tool.setMinifying(minifying); - tool.setIncremental(incremental); - tool.setDebugInformationGenerated(debugInformationGenerated); - tool.setSourceMapsGenerated(sourceMapsGenerated); - tool.setSourceFilesCopied(sourceFilesCopied); - if (sourceFilesCopied) { - MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup(); - lookup.setMavenProject(project); - lookup.setRepositorySystem(repositorySystem); - lookup.setLocalRepository(localRepository); - lookup.setRemoteRepositories(remoteRepositories); - lookup.setPluginDependencies(pluginArtifacts); - for (SourceFileProvider provider : lookup.resolve()) { - tool.addSourceFileProvider(provider); - } - } - if (properties != null) { - tool.getProperties().putAll(properties); - } if (additionalScripts != null) { tool.getAdditionalScripts().addAll(Arrays.asList(additionalScripts)); } - tool.generate(); + writePlan(tool.generate()); } catch (TeaVMToolException e) { throw new MojoFailureException("Error occured generating JavaScript files", e); } } + private void writePlan(TestPlan plan) throws MojoExecutionException { + File file = new File(targetDirectory, "plan.json"); + try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(writer, plan); + } catch (IOException e) { + throw new MojoExecutionException("Error writing test plan", e); + } + } + private TestAdapter createAdapter(ClassLoader classLoader) throws MojoExecutionException { Class adapterClsRaw; try { @@ -253,8 +127,8 @@ public class BuildJavascriptTestMojo extends AbstractMojo { throw new MojoExecutionException("Adapter not found: " + adapterClass, e); } if (!TestAdapter.class.isAssignableFrom(adapterClsRaw)) { - throw new MojoExecutionException("Adapter " + adapterClass + " does not implement " + - TestAdapter.class.getName()); + throw new MojoExecutionException("Adapter " + adapterClass + " does not implement " + + TestAdapter.class.getName()); } Class adapterCls = adapterClsRaw.asSubclass(TestAdapter.class); Constructor cons; @@ -272,38 +146,6 @@ public class BuildJavascriptTestMojo extends AbstractMojo { } } - private ClassLoader prepareClassLoader() throws MojoExecutionException { - try { - Log log = getLog(); - log.info("Preparing classpath for JavaScript test generation"); - List urls = new ArrayList<>(); - StringBuilder classpath = new StringBuilder(); - for (Artifact artifact : project.getArtifacts()) { - if (!testScopes.contains(artifact.getScope())) { - continue; - } - File file = artifact.getFile(); - if (classpath.length() > 0) { - classpath.append(':'); - } - classpath.append(file.getPath()); - urls.add(file.toURI().toURL()); - } - if (classpath.length() > 0) { - classpath.append(':'); - } - classpath.append(testFiles.getPath()); - urls.add(testFiles.toURI().toURL()); - classpath.append(':').append(classFiles.getPath()); - urls.add(classFiles.toURI().toURL()); - log.info("Using the following classpath for JavaScript test generation: " + classpath); - return new URLClassLoader(urls.toArray(new URL[urls.size()]), - BuildJavascriptTestMojo.class.getClassLoader()); - } catch (MalformedURLException e) { - throw new MojoExecutionException("Error gathering classpath information", e); - } - } - private void findTestsInDependencies(ClassLoader classLoader) throws MojoExecutionException { try { Log log = getLog(); @@ -384,39 +226,18 @@ public class BuildJavascriptTestMojo extends AbstractMojo { } } + @Override + protected File getTargetDirectory() { + return targetDirectory; + } - private List instantiateTransformers(ClassLoader classLoader) - throws MojoExecutionException { - List transformerInstances = new ArrayList<>(); - if (transformers == null) { - return transformerInstances; - } - for (String transformerName : transformers) { - Class transformerRawType; - try { - transformerRawType = Class.forName(transformerName, true, classLoader); - } catch (ClassNotFoundException e) { - throw new MojoExecutionException("Transformer not found: " + transformerName, e); - } - if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) { - throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of " + - ClassHolderTransformer.class.getName()); - } - Class transformerType = transformerRawType.asSubclass( - ClassHolderTransformer.class); - Constructor ctor; - try { - ctor = transformerType.getConstructor(); - } catch (NoSuchMethodException e) { - throw new MojoExecutionException("Transformer " + transformerName + " has no default constructor"); - } - try { - ClassHolderTransformer transformer = ctor.newInstance(); - transformerInstances.add(transformer); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new MojoExecutionException("Error instantiating transformer " + transformerName, e); - } - } - return transformerInstances; + @Override + protected List getAdditionalClassPath() { + return Arrays.asList(testFiles); + } + + @Override + protected boolean isSupportedScope(String scope) { + return testScopes.contains(scope); } } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java b/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java new file mode 100644 index 000000000..0b7b24a57 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java @@ -0,0 +1,107 @@ +/* + * 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.maven; + +import com.gargoylesoftware.htmlunit.BrowserVersion; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebWindow; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import net.sourceforge.htmlunit.corejs.javascript.Function; +import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject; +import org.apache.commons.io.IOUtils; +import org.apache.maven.plugin.logging.Log; +import org.teavm.tooling.testing.TestCase; + +/** + * + * @author Alexey Andreev + */ +public class HtmlUnitRunStrategy implements TestRunStrategy { + private File directory; + + public HtmlUnitRunStrategy(File directory) { + this.directory = directory; + } + + @Override + public void beforeThread() { + } + + @Override + public void afterThread() { + } + + @Override + public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException { + try (WebClient webClient = new WebClient(BrowserVersion.CHROME)) { + HtmlPage page = webClient.getPage("about:blank"); + page.executeJavaScript(readFile(new File(directory, runtimeScript))); + + AsyncResult asyncResult = new AsyncResult(); + Function function = (Function) page.executeJavaScript(readResource("teavm-htmlunit-adapter.js")) + .getJavaScriptResult(); + Object[] args = new Object[] { new NativeJavaObject(function, asyncResult, AsyncResult.class) }; + page.executeJavaScriptFunctionIfPossible(function, function, args, page); + + page.executeJavaScript(readFile(new File(directory, testCase.getTestScript()))); + page.cleanUp(); + for (WebWindow window : webClient.getWebWindows()) { + window.getJobManager().removeAllJobs(); + } + return (String) asyncResult.getResult(); + } + } + + private String readFile(File file) throws IOException { + try (InputStream input = new FileInputStream(file)) { + return IOUtils.toString(input, "UTF-8"); + } + } + + private String readResource(String resourceName) throws IOException { + try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resourceName)) { + if (input == null) { + return ""; + } + return IOUtils.toString(input, "UTF-8"); + } + } + + public class AsyncResult { + private CountDownLatch latch = new CountDownLatch(1); + private Object result; + + public void complete(Object result) { + this.result = result; + latch.countDown(); + } + + public Object getResult() { + try { + latch.await(5, TimeUnit.SECONDS); + return result; + } catch (InterruptedException e) { + return null; + } + } + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java index 42887a97b..3b6242f79 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java @@ -26,9 +26,9 @@ import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.project.MavenProject; import org.apache.maven.repository.RepositorySystem; -import org.teavm.tooling.DirectorySourceFileProvider; -import org.teavm.tooling.JarSourceFileProvider; -import org.teavm.tooling.SourceFileProvider; +import org.teavm.tooling.sources.DirectorySourceFileProvider; +import org.teavm.tooling.sources.JarSourceFileProvider; +import org.teavm.tooling.sources.SourceFileProvider; /** * @@ -85,7 +85,12 @@ public class MavenSourceFileProviderLookup { ArtifactResolutionResult result = repositorySystem.resolve(request); for (Artifact resolvedArtifact : result.getArtifacts()) { if (resolvedArtifact.getFile() != null) { - providers.add(new JarSourceFileProvider(resolvedArtifact.getFile())); + File file = resolvedArtifact.getFile(); + if (!file.isDirectory()) { + providers.add(new JarSourceFileProvider(file)); + } else { + providers.add(new DirectorySourceFileProvider(file)); + } } } } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java index 862f5c605..1208d4a25 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java @@ -1,3 +1,18 @@ +/* + * 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.maven; import org.apache.maven.plugin.logging.Log; diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java new file mode 100644 index 000000000..2c0b82354 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java @@ -0,0 +1,128 @@ +/* + * 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.maven; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.teavm.tooling.testing.TestPlan; + +/** + * + * @author Alexey Andreev + */ +@Mojo(name = "test", defaultPhase = LifecyclePhase.TEST) +public class RunTestsMojo extends AbstractMojo { + @Parameter(defaultValue = "${project.build.directory}/javascript-test") + private File testDirectory; + + @Parameter(defaultValue = "${project.build.directory}/teavm-test-report.json") + private File reportFile; + + @Parameter + private String seleniumURL; + + @Parameter + private int numThreads = 1; + + @Parameter + private boolean skip; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (skip) { + getLog().info("Tests run skipped as specified by skip property"); + return; + } + + if (System.getProperty("maven.test.skip", "false").equals("true") + || System.getProperty("skipTests") != null) { + getLog().info("Tests run skipped as specified by system property"); + return; + } + + TestRunner runner = new TestRunner(pickStrategy()); + runner.setLog(getLog()); + runner.setNumThreads(numThreads); + + TestPlan plan; + ObjectMapper mapper = new ObjectMapper(); + File file = new File(testDirectory, "plan.json"); + try (Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) { + plan = mapper.readValue(reader, TestPlan.class); + } catch (IOException e) { + throw new MojoExecutionException("Error reading test plan", e); + } + + runner.run(plan); + processReport(runner.getReport()); + } + + private TestRunStrategy pickStrategy() throws MojoFailureException { + if (seleniumURL != null) { + try { + return new SeleniumRunStrategy(new URL(seleniumURL), testDirectory); + } catch (MalformedURLException e) { + throw new MojoFailureException("Can't parse URL: " + seleniumURL, e); + } + } else { + return new HtmlUnitRunStrategy(testDirectory); + } + } + + private void processReport(TestReport report) throws MojoExecutionException, MojoFailureException { + if (report.getResults().isEmpty()) { + getLog().info("No tests ran"); + return; + } + + int failedTests = 0; + for (TestResult result : report.getResults()) { + if (result.getStatus() != TestStatus.PASSED) { + failedTests++; + } + } + + if (failedTests > 0) { + throw new MojoExecutionException(failedTests + " of " + report.getResults().size() + " test(s) failed"); + } else { + getLog().info("All of " + report.getResults().size() + " tests successfully passed"); + } + + ObjectMapper mapper = new ObjectMapper(); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + try (Writer writer = new OutputStreamWriter(new FileOutputStream(reportFile), "UTF-8")) { + mapper.writeValue(writer, report); + } catch (IOException e) { + throw new MojoFailureException("Error writing test report", e); + } + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java b/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java new file mode 100644 index 000000000..1bb827c43 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java @@ -0,0 +1,95 @@ +/* + * 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.maven; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.commons.io.IOUtils; +import org.apache.maven.plugin.logging.Log; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.teavm.tooling.testing.TestCase; + +/** + * + * @author Alexey Andreev + */ +public class SeleniumRunStrategy implements TestRunStrategy { + private URL url; + private File directory; + private ThreadLocal webDriver = new ThreadLocal<>(); + + public SeleniumRunStrategy(URL url, File directory) { + this.url = url; + this.directory = directory; + } + + @Override + public void beforeThread() { + RemoteWebDriver driver = new RemoteWebDriver(url, DesiredCapabilities.chrome()); + webDriver.set(driver); + } + + @Override + public void afterThread() { + webDriver.get().close(); + webDriver.remove(); + } + + @Override + public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException { + webDriver.get().manage().timeouts().setScriptTimeout(2, TimeUnit.SECONDS); + JavascriptExecutor js = (JavascriptExecutor) webDriver.get(); + try { + return (String) js.executeAsyncScript( + readResource("teavm-selenium.js"), + readFile(new File(directory, runtimeScript)), + readFile(new File(directory, testCase.getTestScript())), + readResource("teavm-selenium-adapter.js")); + } catch (WebDriverException e) { + log.error("Error occured running test " + testCase.getTestMethod(), e); + @SuppressWarnings("unchecked") + List errors = (List) js.executeScript("return window.jsErrors;"); + for (Object error : errors) { + log.error(" -- additional error: " + error); + } + return null; + } + } + + private String readFile(File file) throws IOException { + try (InputStream input = new FileInputStream(file)) { + return IOUtils.toString(input, "UTF-8"); + } + } + + private String readResource(String resourceName) throws IOException { + try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resourceName)) { + if (input == null) { + return ""; + } + return IOUtils.toString(input, "UTF-8"); + } + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java new file mode 100644 index 000000000..c6e57d502 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestReport.java @@ -0,0 +1,31 @@ +/* + * 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.maven; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +public class TestReport { + private List results = new ArrayList<>(); + + public List getResults() { + return results; + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java new file mode 100644 index 000000000..9728857e6 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestResult.java @@ -0,0 +1,78 @@ +/* + * 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.maven; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class TestResult { + private MethodReference method; + private TestStatus status; + private String exception; + private String stack; + + @JsonCreator + TestResult( + @JsonProperty("method") MethodReference method, + @JsonProperty("status") TestStatus status, + @JsonInclude(Include.NON_NULL) @JsonProperty("exception") String exception, + @JsonInclude(Include.NON_NULL) @JsonProperty("stack") String stack) { + this.method = method; + this.status = status; + this.exception = exception; + this.stack = stack; + } + + public static TestResult passed(MethodReference method) { + return new TestResult(method, TestStatus.PASSED, null, null); + } + + public static TestResult exceptionNotThrown(MethodReference method) { + return new TestResult(method, TestStatus.EXCEPTION_NOT_THROWN, null, null); + } + + public static TestResult error(MethodReference method, String exception, String stack) { + return new TestResult(method, TestStatus.ERROR, exception, stack); + } + + @JsonGetter + public MethodReference getMethod() { + return method; + } + + @JsonGetter + public TestStatus getStatus() { + return status; + } + + @JsonGetter + public String getException() { + return exception; + } + + @JsonGetter + public String getStack() { + return stack; + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java new file mode 100644 index 000000000..8c1bcc745 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunStrategy.java @@ -0,0 +1,32 @@ +/* + * 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.maven; + +import java.io.IOException; +import org.apache.maven.plugin.logging.Log; +import org.teavm.tooling.testing.TestCase; + +/** + * + * @author Alexey Andreev + */ +public interface TestRunStrategy { + void beforeThread(); + + void afterThread(); + + String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException; +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunner.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunner.java new file mode 100644 index 000000000..3594cfec6 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestRunner.java @@ -0,0 +1,164 @@ +/* + * 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.maven; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import org.apache.maven.plugin.logging.Log; +import org.teavm.model.MethodReference; +import org.teavm.tooling.testing.TestCase; +import org.teavm.tooling.testing.TestGroup; +import org.teavm.tooling.testing.TestPlan; + +/** + * + * @author Alexey Andreev + */ +public class TestRunner { + private int numThreads = 1; + private TestRunStrategy strategy; + private BlockingQueue taskQueue = new LinkedBlockingQueue<>(); + private CountDownLatch latch; + private volatile boolean stopped; + private Log log; + private List report = new CopyOnWriteArrayList<>(); + private ThreadLocal> localReport = new ThreadLocal<>(); + + public TestRunner(TestRunStrategy strategy) { + this.strategy = strategy; + } + + public void setLog(Log log) { + this.log = log; + } + + public int getNumThreads() { + return numThreads; + } + + public void setNumThreads(int numThreads) { + this.numThreads = numThreads; + } + + public void run(TestPlan testPlan) { + initSelenium(); + for (TestGroup group : testPlan.getGroups()) { + for (TestCase testCase : group.getTestCases()) { + run(testPlan.getRuntimeScript(), testCase); + } + } + stopSelenium(); + waitForCompletion(); + } + + private void initSelenium() { + latch = new CountDownLatch(numThreads); + for (int i = 0; i < numThreads; ++i) { + new Thread(() -> { + strategy.beforeThread(); + localReport.set(new ArrayList<>()); + while (!stopped || !taskQueue.isEmpty()) { + Runnable task; + try { + task = taskQueue.poll(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + break; + } + if (task != null) { + task.run(); + } + } + report.addAll(localReport.get()); + localReport.remove(); + strategy.afterThread(); + latch.countDown(); + }).start(); + } + } + + private void addTask(Runnable runnable) { + taskQueue.add(runnable); + } + + private void stopSelenium() { + stopped = true; + } + + private void waitForCompletion() { + try { + latch.await(); + } catch (InterruptedException e) { + return; + } + } + + private void run(String runtimeScript, TestCase testCase) { + addTask(() -> runImpl(runtimeScript, testCase)); + } + + private void runImpl(String runtimeScript, TestCase testCase) { + MethodReference ref = MethodReference.parse(testCase.getTestMethod()); + try { + String result = strategy.runTest(log, runtimeScript, testCase); + if (result == null) { + log.info("Test failed: " + testCase.getTestMethod()); + localReport.get().add(TestResult.error(ref, null, null)); + } + ObjectMapper mapper = new ObjectMapper(); + ObjectNode resultObject = (ObjectNode) mapper.readTree(result); + String status = resultObject.get("status").asText(); + switch (status) { + case "ok": + if (testCase.getExpectedExceptions().isEmpty()) { + log.info("Test passed: " + testCase.getTestMethod()); + localReport.get().add(TestResult.passed(ref)); + } else { + log.info("Test failed: " + testCase.getTestMethod()); + localReport.get().add(TestResult.exceptionNotThrown(ref)); + } + break; + case "exception": { + String stack = resultObject.get("stack").asText(); + String exception = resultObject.get("exception").asText(); + if (!testCase.getExpectedExceptions().contains(exception)) { + log.info("Test failed: " + testCase.getTestMethod()); + localReport.get().add(TestResult.error(ref, exception, stack)); + } else { + log.info("Test passed: " + testCase.getTestMethod()); + localReport.get().add(TestResult.passed(ref)); + } + break; + } + } + } catch (IOException e) { + log.error(e); + } + } + + public TestReport getReport() { + TestReport report = new TestReport(); + report.getResults().addAll(this.report); + return report; + } +} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java new file mode 100644 index 000000000..366859a58 --- /dev/null +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TestStatus.java @@ -0,0 +1,26 @@ +/* + * 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.maven; + +/** + * + * @author Alexey Andreev + */ +public enum TestStatus { + PASSED, + ERROR, + EXCEPTION_NOT_THROWN +} diff --git a/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js b/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js new file mode 100644 index 000000000..93d549474 --- /dev/null +++ b/tools/maven/plugin/src/main/resources/teavm-htmlunit-adapter.js @@ -0,0 +1,65 @@ +function(callback) { + var JUnitClient = {} + JUnitClient.run = function() { + $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: + instance = new TestClass(); + ptr = 1; + case 1: + try { + initInstance(instance); + } catch (e) { + message = {}; + JUnitClient.makeErrorMessage(message, e); + break loop; + } + if (thread.isSuspending()) { + thread.push(instance); + thread.push(ptr); + return; + } + ptr = 2; + case 2: + try { + runTest(instance); + } catch (e) { + message = {}; + JUnitClient.makeErrorMessage(message, e); + break loop; + } + if (thread.isSuspending()) { + thread.push(instance); + thread.push(ptr); + return; + } + message = {}; + message.status = "ok"; + break loop; + }} + callback.complete(JSON.stringify(message)); + }) + } + + JUnitClient.makeErrorMessage = function(message, e) { + message.status = "exception"; + var stack = e.stack; + if (e.$javaException && e.$javaException.constructor.$meta) { + message.exception = e.$javaException.constructor.$meta.name; + message.stack = e.$javaException.constructor.$meta.name + ": "; + var exceptionMessage = extractException(e.$javaException); + message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; + } + message.stack += "\n" + stack; + } + + window.JUnitClient = JUnitClient; +} \ No newline at end of file diff --git a/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js b/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js new file mode 100644 index 000000000..91cf0cf83 --- /dev/null +++ b/tools/maven/plugin/src/main/resources/teavm-selenium-adapter.js @@ -0,0 +1,61 @@ +var JUnitClient = {} +JUnitClient.run = function() { + $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: + instance = new TestClass(); + ptr = 1; + case 1: + try { + initInstance(instance); + } catch (e) { + message = {}; + JUnitClient.makeErrorMessage(message, e); + break loop; + } + if (thread.isSuspending()) { + thread.push(instance); + thread.push(ptr); + return; + } + ptr = 2; + case 2: + try { + runTest(instance); + } catch (e) { + message = {}; + JUnitClient.makeErrorMessage(message, e); + break loop; + } + if (thread.isSuspending()) { + thread.push(instance); + thread.push(ptr); + return; + } + message = {}; + message.status = "ok"; + break loop; + }} + window.parent.postMessage(JSON.stringify(message), "*"); + }) +} + +JUnitClient.makeErrorMessage = function(message, e) { + message.status = "exception"; + var stack = e.stack; + if (e.$javaException && e.$javaException.constructor.$meta) { + message.exception = e.$javaException.constructor.$meta.name; + message.stack = e.$javaException.constructor.$meta.name + ": "; + var exceptionMessage = extractException(e.$javaException); + message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; + } + message.stack += "\n" + stack; +} \ No newline at end of file diff --git a/tools/maven/plugin/src/main/resources/teavm-selenium.js b/tools/maven/plugin/src/main/resources/teavm-selenium.js new file mode 100644 index 000000000..bbf451b11 --- /dev/null +++ b/tools/maven/plugin/src/main/resources/teavm-selenium.js @@ -0,0 +1,43 @@ +var runtimeSource = arguments[0] +var testSource = arguments[1] +var adapterSource = arguments[2] +var seleniumCallback = arguments[arguments.length - 1] + +var iframe = document.createElement("iframe") +document.body.appendChild(iframe) +var doc = iframe.contentDocument + +window.jsErrors = [] +window.onerror = reportError +iframe.contentWindow.onerror = reportError + +loadScripts([ runtimeSource, adapterSource, testSource ]) +window.addEventListener("message", handleMessage) + +function handleMessage(event) { + window.removeEventListener("message", handleMessage) + document.body.removeChild(iframe) + seleniumCallback(event.data) +} + +function loadScript(script, callback) { + callback() +} + +function loadScripts(scripts) { + for (var i = 0; i < scripts.length; ++i) { + var elem = doc.createElement("script") + elem.type = "text/javascript" + doc.head.appendChild(elem) + elem.text = scripts[i] + } +} +function reportError(error, url, line) { + window.jsErrors.push(error + " at " + line) +} +function report(error) { + window.jsErrors.push(error) +} +function globalEval(window, arg) { + eval.apply(window, [arg]) +} \ No newline at end of file diff --git a/tools/maven/pom.xml b/tools/maven/pom.xml index 84507fd9b..f20bff3e7 100644 --- a/tools/maven/pom.xml +++ b/tools/maven/pom.xml @@ -21,6 +21,7 @@ org.teavm teavm 0.4.0-SNAPSHOT + ../.. teavm-maven diff --git a/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml b/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml index 0a265a56c..5a2eebaa2 100644 --- a/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml +++ b/tools/maven/webapp/src/main/resources/archetype-resources/pom.xml @@ -8,7 +8,7 @@ war - 1.7 + 1.8 0.4.0-SNAPSHOT UTF-8 @@ -25,15 +25,7 @@ org.teavm - teavm-jso - ${teavm.version} - provided - - - - - org.teavm - teavm-dom + teavm-jso-apis ${teavm.version} provided @@ -80,9 +72,8 @@ web-client - prepare-package - build-javascript + compile ${package}.Client - - SEPARATE - true