diff --git a/.travis.yml b/.travis.yml
index c892b40dc..92edf3e8c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,4 +5,9 @@ cache:
directories:
- $HOME/.m2
after_script:
- - rm -rf $HOME/.m2/repository/org/teavm
\ No newline at end of file
+ - rm -rf $HOME/.m2/repository/org/teavm
+script:
+ - mvn test \
+ - -Pteavm.test.skip=false \
+ - -Pteavm.test.selenium="http://$SAUCE_USER_NAME:$SAUCE_ACCESS_KEY@ondemand.saucelabs.com:80/wd/hub" \
+ - -Pteavm.test.threads=2
\ No newline at end of file
diff --git a/tests/pom.xml b/tests/pom.xml
index 17b57350d..002d0faef 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -31,6 +31,7 @@
false
1
+ true
@@ -100,6 +101,7 @@
test
+ ${teavm.test.skip}
${teavm.test.threads}
${teavm.test.selenium}
diff --git a/tools/maven/plugin/pom.xml b/tools/maven/plugin/pom.xml
index 34fe8454c..2c7e153df 100644
--- a/tools/maven/plugin/pom.xml
+++ b/tools/maven/plugin/pom.xml
@@ -69,6 +69,11 @@
com.fasterxml.jackson.core
jackson-databind
+
+ net.sourceforge.htmlunit
+ htmlunit
+ 2.18
+
junit
junit
@@ -103,6 +108,14 @@
org.apache.maven.plugins
maven-javadoc-plugin
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ ../../../checkstyle.xml
+ config_loc=${basedir}/../../..
+
+
diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java
index 6446e3441..cb43846e3 100644
--- a/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java
+++ b/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractJavascriptMojo.java
@@ -106,8 +106,8 @@ public abstract class AbstractJavascriptMojo extends AbstractMojo {
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());
+ throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of "
+ + ClassHolderTransformer.class.getName());
}
Class extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java
index 5beb6c135..7b8618570 100644
--- a/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java
+++ b/tools/maven/plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java
@@ -85,8 +85,8 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo {
@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;
}
@@ -127,8 +127,8 @@ public class BuildJavascriptTestMojo extends AbstractJavascriptMojo {
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;
diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java b/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java
new file mode 100644
index 000000000..0b7b24a57
--- /dev/null
+++ b/tools/maven/plugin/src/main/java/org/teavm/maven/HtmlUnitRunStrategy.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.maven;
+
+import com.gargoylesoftware.htmlunit.BrowserVersion;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.WebWindow;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import net.sourceforge.htmlunit.corejs.javascript.Function;
+import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject;
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.plugin.logging.Log;
+import org.teavm.tooling.testing.TestCase;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class HtmlUnitRunStrategy implements TestRunStrategy {
+ private File directory;
+
+ public HtmlUnitRunStrategy(File directory) {
+ this.directory = directory;
+ }
+
+ @Override
+ public void beforeThread() {
+ }
+
+ @Override
+ public void afterThread() {
+ }
+
+ @Override
+ public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException {
+ try (WebClient webClient = new WebClient(BrowserVersion.CHROME)) {
+ HtmlPage page = webClient.getPage("about:blank");
+ page.executeJavaScript(readFile(new File(directory, runtimeScript)));
+
+ AsyncResult asyncResult = new AsyncResult();
+ Function function = (Function) page.executeJavaScript(readResource("teavm-htmlunit-adapter.js"))
+ .getJavaScriptResult();
+ Object[] args = new Object[] { new NativeJavaObject(function, asyncResult, AsyncResult.class) };
+ page.executeJavaScriptFunctionIfPossible(function, function, args, page);
+
+ page.executeJavaScript(readFile(new File(directory, testCase.getTestScript())));
+ page.cleanUp();
+ for (WebWindow window : webClient.getWebWindows()) {
+ window.getJobManager().removeAllJobs();
+ }
+ return (String) asyncResult.getResult();
+ }
+ }
+
+ private String readFile(File file) throws IOException {
+ try (InputStream input = new FileInputStream(file)) {
+ return IOUtils.toString(input, "UTF-8");
+ }
+ }
+
+ private String readResource(String resourceName) throws IOException {
+ try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resourceName)) {
+ if (input == null) {
+ return "";
+ }
+ return IOUtils.toString(input, "UTF-8");
+ }
+ }
+
+ public class AsyncResult {
+ private CountDownLatch latch = new CountDownLatch(1);
+ private Object result;
+
+ public void complete(Object result) {
+ this.result = result;
+ latch.countDown();
+ }
+
+ public Object getResult() {
+ try {
+ latch.await(5, TimeUnit.SECONDS);
+ return result;
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ }
+}
diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java
index 862f5c605..1208d4a25 100644
--- a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java
+++ b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenTeaVMToolLog.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2015 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.teavm.maven;
import org.apache.maven.plugin.logging.Log;
diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java
index 8bfd3857e..2c0b82354 100644
--- a/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java
+++ b/tools/maven/plugin/src/main/java/org/teavm/maven/RunTestsMojo.java
@@ -53,28 +53,25 @@ public class RunTestsMojo extends AbstractMojo {
@Parameter
private int numThreads = 1;
+ @Parameter
+ private boolean skip;
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
- if (seleniumURL == null || seleniumURL.isEmpty()) {
- getLog().info("Tests build skipped as selenium URL was not specified");
+ 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 build skipped as specified by system property");
+ if (System.getProperty("maven.test.skip", "false").equals("true")
+ || System.getProperty("skipTests") != null) {
+ getLog().info("Tests run skipped as specified by system property");
return;
}
- SeleniumTestRunner runner = new SeleniumTestRunner();
+ TestRunner runner = new TestRunner(pickStrategy());
runner.setLog(getLog());
- runner.setDirectory(testDirectory);
runner.setNumThreads(numThreads);
- try {
- runner.setUrl(new URL(seleniumURL));
- } catch (MalformedURLException e) {
- throw new MojoFailureException("Can't parse URL: " + seleniumURL, e);
- }
TestPlan plan;
ObjectMapper mapper = new ObjectMapper();
@@ -89,6 +86,18 @@ public class RunTestsMojo extends AbstractMojo {
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");
diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java b/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java
new file mode 100644
index 000000000..1bb827c43
--- /dev/null
+++ b/tools/maven/plugin/src/main/java/org/teavm/maven/SeleniumRunStrategy.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.maven;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.plugin.logging.Log;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebDriverException;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.remote.RemoteWebDriver;
+import org.teavm.tooling.testing.TestCase;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class SeleniumRunStrategy implements TestRunStrategy {
+ private URL url;
+ private File directory;
+ private ThreadLocal webDriver = new ThreadLocal<>();
+
+ public SeleniumRunStrategy(URL url, File directory) {
+ this.url = url;
+ this.directory = directory;
+ }
+
+ @Override
+ public void beforeThread() {
+ RemoteWebDriver driver = new RemoteWebDriver(url, DesiredCapabilities.chrome());
+ webDriver.set(driver);
+ }
+
+ @Override
+ public void afterThread() {
+ webDriver.get().close();
+ webDriver.remove();
+ }
+
+ @Override
+ public String runTest(Log log, String runtimeScript, TestCase testCase) throws IOException {
+ webDriver.get().manage().timeouts().setScriptTimeout(2, TimeUnit.SECONDS);
+ JavascriptExecutor js = (JavascriptExecutor) webDriver.get();
+ try {
+ return (String) js.executeAsyncScript(
+ readResource("teavm-selenium.js"),
+ readFile(new File(directory, runtimeScript)),
+ readFile(new File(directory, testCase.getTestScript())),
+ readResource("teavm-selenium-adapter.js"));
+ } catch (WebDriverException e) {
+ log.error("Error occured running test " + testCase.getTestMethod(), e);
+ @SuppressWarnings("unchecked")
+ List