Merge branch 'selenium'

This commit is contained in:
Alexey Andreev 2015-10-10 12:45:05 +03:00
commit 02d853adf7
87 changed files with 1941 additions and 680 deletions

13
.travis.yml Normal file
View File

@ -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

View File

@ -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?
--------------

View File

@ -45,6 +45,11 @@
<artifactId>hppc</artifactId>
<version>0.6.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<name>TeaVM core</name>

View File

@ -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) {

View File

@ -74,12 +74,11 @@
<execution>
<id>generate-javascript-tests</id>
<goals>
<goal>build-test-javascript</goal>
<goal>testCompile</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<minifying>false</minifying>
<outputDir>${project.build.directory}/javascript-test</outputDir>
<targetDirectory>${project.build.directory}/javascript-test</targetDirectory>
<debugInformationGenerated>true</debugInformationGenerated>
<sourceMapsGenerated>true</sourceMapsGenerated>
</configuration>
@ -87,13 +86,12 @@
<execution>
<id>generate-javascript-tck</id>
<goals>
<goal>build-test-javascript</goal>
<goal>testCompile</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<minifying>false</minifying>
<scanDependencies>true</scanDependencies>
<outputDir>${project.build.directory}/javascript-tck</outputDir>
<targetDirectory>${project.build.directory}/javascript-tck</targetDirectory>
<adapterClass>org.teavm.html4j.testing.KOTestAdapter</adapterClass>
<transformers>
<param>org.teavm.javascript.NullPointerExceptionTransformer</param>

View File

@ -21,6 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-jso-apis</artifactId>

View File

@ -21,6 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-jso</artifactId>

View File

@ -21,6 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-jso-impl</artifactId>

View File

@ -40,11 +40,6 @@
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

27
pom.xml
View File

@ -69,7 +69,8 @@
<html4j.version>1.2</html4j.version>
<jetty.version>9.2.1.v20140609</jetty.version>
<slf4j.version>1.7.7</slf4j.version>
<checker.version>1.9.3</checker.version>
<selenium.version>2.47.2</selenium.version>
<jackson.version>2.6.2</jackson.version>
</properties>
<modules>
@ -81,6 +82,7 @@
<module>html4j</module>
<module>samples</module>
<module>platform</module>
<module>tools/core</module>
<module>tools/cli</module>
<module>tools/maven</module>
<module>tools/chrome-rdp</module>
@ -166,13 +168,26 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!--
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>${checker.version}</version>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
-->
</dependencies>
</dependencyManagement>

View File

@ -57,9 +57,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>

View File

@ -27,13 +27,7 @@
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@ -111,9 +105,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>

View File

@ -42,12 +42,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@ -76,9 +70,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>

View File

@ -53,7 +53,7 @@
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@ -92,9 +92,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>

View File

@ -67,9 +67,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>

View File

@ -38,13 +38,7 @@
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@ -78,12 +72,11 @@
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>
<mainClass>org.teavm.samples.storage.Application</mainClass>
<runtime>SEPARATE</runtime>
<minifying>false</minifying>
<debugInformationGenerated>true</debugInformationGenerated>
<sourceMapsGenerated>true</sourceMapsGenerated>

View File

@ -38,13 +38,7 @@
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@ -76,9 +70,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>

View File

@ -27,6 +27,13 @@
<name>TeaVM tests</name>
<description>Project containing TeaVM tests, as it is impossible to test each module separately</description>
<properties>
<teavm.test.incremental>false</teavm.test.incremental>
<teavm.test.threads>1</teavm.test.threads>
<teavm.test.selenium></teavm.test.selenium>
<teavm.test.skip>true</teavm.test.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
@ -72,9 +79,8 @@
<execution>
<id>generate-javascript-tests</id>
<goals>
<goal>build-test-javascript</goal>
<goal>testCompile</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<minifying>false</minifying>
<scanDependencies>true</scanDependencies>
@ -89,6 +95,17 @@
<incremental>${teavm.test.incremental}</incremental>
</configuration>
</execution>
<execution>
<id>run-javascript-tests</id>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>${teavm.test.skip}</skip>
<numThreads>${teavm.test.threads}</numThreads>
<seleniumURL>${teavm.test.selenium}</seleniumURL>
</configuration>
</execution>
</executions>
</plugin>
<plugin>

View File

@ -21,6 +21,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-chrome-rdp</artifactId>

View File

@ -21,6 +21,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-cli</artifactId>
@ -30,7 +31,7 @@
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<artifactId>teavm-tooling</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -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")));

76
tools/core/pom.xml Normal file
View File

@ -0,0 +1,76 @@
<!--
Copyright 2014 Alexey Andreev.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-tooling</artifactId>
<packaging>bundle</packaging>
<name>TeaVM tooling core</name>
<description>TeaVM API that helps to create tooling</description>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>../../checkstyle.xml</configLocation>
<propertyExpansion>config_loc=${basedir}/../..</propertyExpansion>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>org.teavm.*</Export-Package>
<Bundle-SymbolicName>teavm-tooling</Bundle-SymbolicName>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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<ClassHolderTransformer> getTransformers();
void setLog(TeaVMToolLog log);
void setClassLoader(ClassLoader classLoader);
void addSourceFileProvider(SourceFileProvider sourceFileProvider);
}

View File

@ -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<ClassHolderTransformer> 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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
/**
*

View File

@ -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;
/**
*

View File

@ -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<String, List<MethodReference>> groupedMethods = new HashMap<>();
private Map<MethodReference, String> fileNames = new HashMap<>();
private List<MethodReference> 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<SourceFileProvider> sourceFileProviders = new ArrayList<>();
private MethodNodeCache astCache;
private ProgramCache programCache;
private SourceFilesCopier sourceFilesCopier;
private List<TestClassBuilder> 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<ClassHolderTransformer> 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<TestGroup> 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,35 +242,59 @@ public class TeaVMTestTool {
astCache = new InMemoryRegularMethodNodeCache();
programCache = new InMemoryProgramCache();
}
writeMetadata();
FiniteExecutor executor = new SimpleFiniteExecutor();
if (numThreads != 1) {
int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors();
ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads);
finalizer = () -> threadedExecutor.stop();
executor = threadedExecutor;
}
startTime = System.currentTimeMillis();
int methodsGenerated = writeMethods(executor, classSource);
if (sourceFilesCopied) {
sourceFilesCopier.copy(new File(new File(outputDir, "tests"), "src"));
}
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 {
if (finalizer != null) {
finalizer.run();
}
}
}
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 (String testClass : testClasses) {
Collection<MethodReference> methods = groupedMethods.get(testClass);
if (methods == null) {
continue;
}
for (TestClassBuilder testClass : testPlan) {
if (!first) {
allTestsWriter.append(",");
}
first = false;
allTestsWriter.append("\n { name : \"").append(testClass).append("\", methods : [");
allTestsWriter.append("\n { name : \"").append(testClass.getClassName())
.append("\", methods : [");
boolean firstMethod = true;
for (MethodReference methodRef : methods) {
String scriptName = "tests/" + fileNames.size() + ".js";
fileNames.put(methodRef, scriptName);
for (TestMethodBuilder testMethod : testClass.getMethods()) {
String scriptName = testMethod.getFileName();
if (!firstMethod) {
allTestsWriter.append(",");
}
firstMethod = false;
allTestsWriter.append("\n { name : \"" + methodRef.getName() + "\", script : \""
+ scriptName + "\", expected : [");
MethodHolder methodHolder = classSource.get(testClass).getMethod(
methodRef.getDescriptor());
allTestsWriter.append("\n { name : \"" + testMethod.getMethod().getName()
+ "\", script : \"" + scriptName + "\", expected : [");
boolean firstException = true;
for (String exception : adapter.getExpectedExceptions(methodHolder)) {
for (String exception : testMethod.getExpectedExceptions()) {
if (!firstException) {
allTestsWriter.append(", ");
}
@ -246,42 +314,29 @@ public class TeaVMTestTool {
}
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);
FiniteExecutor executor = new SimpleFiniteExecutor();
if (numThreads != 1) {
int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors();
final ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads);
finalizer = () -> threadedExecutor.stop();
executor = threadedExecutor;
}
for (final MethodReference method : testMethods) {
final ClassHolderSource builderClassSource = classSource;
for (TestClassBuilder testClass : testPlan) {
for (TestMethodBuilder testMethod : testClass.getMethods()) {
executor.execute(() -> {
log.debug("Building test for " + method);
log.debug("Building test for " + testMethod.getMethod());
try {
decompileClassesForTest(classLoader, new CopyClassHolderSource(builderClassSource), method,
fileNames.get(method));
decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource),
testMethod);
} catch (IOException e) {
log.error("Error generating JavaScript", e);
}
});
++methodsGenerated;
}
}
executor.complete();
if (sourceFilesCopied) {
sourceFilesCopier.copy(new File(new File(outputDir, "tests"), "src"));
}
log.info("Test files successfully generated for " + methodsGenerated + " method(s).");
} catch (IOException e) {
throw new TeaVMToolException("IO error occured generating JavaScript files", e);
} finally {
if (finalizer != null) {
finalizer.run();
}
}
return methodsGenerated;
}
private void resourceToFile(String resource, String fileName) throws IOException {
@ -292,19 +347,34 @@ public class TeaVMTestTool {
}
}
private void findTests(ClassHolder cls) {
private TestGroup findTests(ClassHolder cls) {
List<TestCase> 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<MethodReference> group = groupedMethods.get(cls.getName());
if (group == null) {
group = new ArrayList<>();
groupedMethods.put(cls.getName(), group);
String fileName = "tests/" + fileIndexGenerator++ + ".js";
List<String> 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(), "<init>", 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 {

View File

@ -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);
}

View File

@ -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<String> expectedExceptions = new ArrayList<>();
@JsonCreator
public TestCase(
@JsonProperty("testMethod") String testMethod,
@JsonProperty("script") String testScript,
@JsonProperty("debugTable") String debugTable,
@JsonProperty("expectedExceptions") List<String> 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<String> getExpectedExceptions() {
return expectedExceptions;
}
}

View File

@ -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<TestMethodBuilder> methods = new ArrayList<>();
public TestClassBuilder(String className) {
this.className = className;
}
public String getClassName() {
return className;
}
public List<TestMethodBuilder> getMethods() {
return methods;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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<TestCase> testCases;
@JsonCreator
public TestGroup(@JsonProperty("className") String className,
@JsonProperty("testCases") List<TestCase> testCases) {
this.className = className;
this.testCases = Collections.unmodifiableList(new ArrayList<>(testCases));
}
@JsonGetter("className")
public String getClassName() {
return className;
}
@JsonGetter("testCases")
public List<TestCase> getTestCases() {
return testCases;
}
}

View File

@ -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<String> expectedExceptions = new ArrayList<>();
public TestMethodBuilder(MethodReference method, String fileName, List<String> 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<String> getExpectedExceptions() {
return expectedExceptions;
}
}

View File

@ -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<TestGroup> groups = new ArrayList<>();
@JsonCreator
public TestPlan(
@JsonProperty("runtimeScript") String runtimeScript,
@JsonProperty("groups") List<TestGroup> groups) {
this.runtimeScript = runtimeScript;
this.groups = Collections.unmodifiableList(new ArrayList<>(groups));
}
@JsonGetter
public String getRuntimeScript() {
return runtimeScript;
}
@JsonGetter
public List<TestGroup> getGroups() {
return groups;
}
}

View File

Before

Width:  |  Height:  |  Size: 774 B

After

Width:  |  Height:  |  Size: 774 B

View File

@ -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

View File

@ -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,\

View File

@ -79,6 +79,11 @@
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-tooling</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-chrome-rdp</artifactId>

View File

@ -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 <konsoletyper@gmail.com>
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)",

View File

@ -7,7 +7,7 @@
<artifactId>teavm-maven-plugin</artifactId>
<versionRange>0.4.0-SNAPSHOT</versionRange>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
</pluginExecutionFilter>
<action>

View File

@ -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<String> usedExecutionIds = new HashSet<>();
private IMaven maven;

View File

@ -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 <konsoletyper@gmail.com>
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

View File

@ -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;
/**
*

View File

@ -21,6 +21,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-eclipse</artifactId>
<packaging>pom</packaging>

View File

@ -50,13 +50,30 @@
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<artifactId>teavm-tooling</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.18</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@ -91,6 +108,14 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>../../../checkstyle.xml</configLocation>
<propertyExpansion>config_loc=${basedir}/../../..</propertyExpansion>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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<MavenArtifactRepository> remoteRepositories;
@Parameter(readonly = true, defaultValue = "${plugin.artifacts}")
protected List<Artifact> pluginArtifacts;
@Parameter(defaultValue = "${project.build.outputDirectory}")
protected File classFiles;
@Parameter
protected List<String> 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<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
throws MojoExecutionException {
List<ClassHolderTransformer> 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<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> 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<URL> 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<File> 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<SourceFileProvider> getSourceFileProviders() {
MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup();
lookup.setMavenProject(project);
lookup.setRepositorySystem(repositorySystem);
lookup.setLocalRepository(localRepository);
lookup.setRemoteRepositories(remoteRepositories);
lookup.setPluginDependencies(pluginArtifacts);
return lookup.resolve();
}
}

View File

@ -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<MavenArtifactRepository> remoteRepositories;
@Parameter(readonly = true, defaultValue = "${plugin.artifacts}")
private List<Artifact> 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<String> 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<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
throws MojoExecutionException {
List<ClassHolderTransformer> 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<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> 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<URL> urls = new ArrayList<>();
StringBuilder classpath = new StringBuilder();
Set<String> 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);
}
}
}

View File

@ -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<String> 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<MavenArtifactRepository> remoteRepositories;
@Parameter(readonly = true, defaultValue = "${plugin.artifacts}")
private List<Artifact> 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<? extends TestAdapter> adapterCls = adapterClsRaw.asSubclass(TestAdapter.class);
Constructor<? extends TestAdapter> 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<URL> 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<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
throws MojoExecutionException {
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
if (transformers == null) {
return transformerInstances;
@Override
protected List<File> getAdditionalClassPath() {
return Arrays.asList(testFiles);
}
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<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> 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 boolean isSupportedScope(String scope) {
return testScopes.contains(scope);
}
}

View File

@ -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;
}
}
}
}

View File

@ -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));
}
}
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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> 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<Object> errors = (List<Object>) 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");
}
}
}

View File

@ -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<TestResult> results = new ArrayList<>();
public List<TestResult> getResults() {
return results;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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<Runnable> taskQueue = new LinkedBlockingQueue<>();
private CountDownLatch latch;
private volatile boolean stopped;
private Log log;
private List<TestResult> report = new CopyOnWriteArrayList<>();
private ThreadLocal<List<TestResult>> 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;
}
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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])
}

View File

@ -21,6 +21,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.4.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>teavm-maven</artifactId>

View File

@ -8,7 +8,7 @@
<packaging>war</packaging>
<properties>
<java.version>1.7</java.version>
<java.version>1.8</java.version>
<teavm.version>0.4.0-SNAPSHOT</teavm.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@ -25,15 +25,7 @@
<!-- JavaScriptObjects (JSO) - a JavaScript binding for TeaVM -->
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso</artifactId>
<version>${teavm.version}</version>
<scope>provided</scope>
</dependency>
<!-- Different browser APIs for TeaVM in terms of JSO -->
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-dom</artifactId>
<artifactId>teavm-jso-apis</artifactId>
<version>${teavm.version}</version>
<scope>provided</scope>
</dependency>
@ -80,9 +72,8 @@
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
<goal>compile</goal>
</goals>
<configuration>
<!-- Directory where TeaVM should put generated files. This configuration conforms to the settings
@ -92,9 +83,6 @@
<!-- Main class, containing static void main(String[]) -->
<mainClass>${package}.Client</mainClass>
<!-- How to attach runtime.js. Possible values are: SEPARATE, MERGED and NONE -->
<runtime>SEPARATE</runtime>
<!-- Whether TeaVM should produce minified JavaScript. Can reduce JavaScript file size more than
two times -->
<minifying>true</minifying>