summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sc8
-rw-r--r--testng/src/mill/testng/EventRecorder.java40
-rw-r--r--testng/src/mill/testng/ResultEvent.java40
-rw-r--r--testng/src/mill/testng/TestNGFramework.java30
-rw-r--r--testng/src/mill/testng/TestNGInstance.java43
-rw-r--r--testng/src/mill/testng/TestNGRunner.java37
-rw-r--r--testng/src/mill/testng/TestRunState.java10
7 files changed, 208 insertions, 0 deletions
diff --git a/build.sc b/build.sc
index 1376690b..55da37ec 100755
--- a/build.sc
+++ b/build.sc
@@ -70,6 +70,14 @@ object client extends MillPublishModule{
)
}
+
+object testng extends MillPublishModule{
+ def ivyDeps = Agg(
+ ivy"org.scala-sbt:test-interface:1.0",
+ ivy"org.testng:testng:6.11"
+ )
+}
+
object core extends MillModule {
def moduleDeps = Seq(moduledefs)
diff --git a/testng/src/mill/testng/EventRecorder.java b/testng/src/mill/testng/EventRecorder.java
new file mode 100644
index 00000000..3400bf6d
--- /dev/null
+++ b/testng/src/mill/testng/EventRecorder.java
@@ -0,0 +1,40 @@
+package mill.testng;
+
+import org.scalatools.testing.Event;
+import org.testng.ITestResult;
+import org.testng.TestListenerAdapter;
+
+import org.scalatools.testing.EventHandler;
+import org.scalatools.testing.Logger;
+
+import java.util.HashMap;
+
+public class EventRecorder extends TestListenerAdapter {
+ private HashMap<String, java.util.List<Event>> basket = new HashMap<>();
+
+ String initKey(ITestResult result){
+ String key = ResultEvent.classNameOf(result);
+ if (!basket.containsKey(key)) basket.put(key, new java.util.ArrayList<Event>());
+ return key;
+ }
+ public void onTestFailure(ITestResult result){
+ String key = initKey(result);
+ basket.get(key).add(ResultEvent.failure(result));
+ }
+ public void onTestSkipped(ITestResult result){
+ String key = initKey(result);
+ basket.get(key).add(ResultEvent.skipped(result));
+ }
+ public void onTestSuccess(ITestResult result){
+ String key = initKey(result);
+ basket.get(key).add(ResultEvent.success(result));
+ }
+
+ void replayTo(EventHandler sbt, String className, Logger[] loggers){
+ synchronized (basket){
+ for(Event e: basket.remove(className)){
+ sbt.handle(e);
+ }
+ }
+ }
+}
diff --git a/testng/src/mill/testng/ResultEvent.java b/testng/src/mill/testng/ResultEvent.java
new file mode 100644
index 00000000..7c943a17
--- /dev/null
+++ b/testng/src/mill/testng/ResultEvent.java
@@ -0,0 +1,40 @@
+
+package mill.testng;
+
+import org.scalatools.testing.Event;
+import org.scalatools.testing.Result;
+import org.testng.ITestResult;
+
+public class ResultEvent implements Event {
+ public Result result;
+ public String testName;
+ public String description;
+ public Throwable error;
+
+ public ResultEvent(Result result, String testName, String description, Throwable error) {
+ this.result = result;
+ this.testName = testName;
+ this.description = description;
+ this.error = error;
+ }
+
+
+ public Result result(){ return result; }
+ public String testName(){ return testName; }
+ public String description(){ return description; }
+ public Throwable error(){ return error; }
+
+ static ResultEvent failure(ITestResult result){ return event(Result.Failure, result); }
+ static ResultEvent skipped(ITestResult result){ return event(Result.Skipped, result); }
+ static ResultEvent success(ITestResult result){ return event(Result.Success, result); }
+
+ static ResultEvent event(Result result, ITestResult testNGResult) {
+ return new ResultEvent(
+ result,
+ testNGResult.getName(),
+ testNGResult.getName(),
+ result != Result.Success ? testNGResult.getThrowable() : null
+ );
+ }
+ static String classNameOf(ITestResult result){ return result.getTestClass().getName(); }
+} \ No newline at end of file
diff --git a/testng/src/mill/testng/TestNGFramework.java b/testng/src/mill/testng/TestNGFramework.java
new file mode 100644
index 00000000..bc278caa
--- /dev/null
+++ b/testng/src/mill/testng/TestNGFramework.java
@@ -0,0 +1,30 @@
+package mill.testng;
+
+
+
+import org.scalatools.testing.*;
+
+
+public class TestNGFramework implements Framework {
+ public String name(){ return "TestNG"; }
+ public Fingerprint[] tests(){
+ return new Fingerprint[]{
+ new Annotated("org.testng.annotations.Test")
+ };
+ }
+
+ public Runner testRunner(ClassLoader testClassLoader, Logger[] loggers){
+ return new TestNGRunner(testClassLoader, loggers, sharedState);
+ }
+
+ private TestRunState sharedState = new TestRunState();
+}
+
+class Annotated implements AnnotatedFingerprint{
+ String annotationName;
+ public Annotated(String annotationName) {
+ this.annotationName = annotationName;
+ }
+ public String annotationName(){return annotationName;}
+ public boolean isModule(){return false;}
+} \ No newline at end of file
diff --git a/testng/src/mill/testng/TestNGInstance.java b/testng/src/mill/testng/TestNGInstance.java
new file mode 100644
index 00000000..f1e52870
--- /dev/null
+++ b/testng/src/mill/testng/TestNGInstance.java
@@ -0,0 +1,43 @@
+package mill.testng;
+
+
+import org.scalatools.testing.EventHandler;
+import org.scalatools.testing.Logger;
+
+import org.testng.CommandLineArgs;
+import org.testng.TestNG;
+
+import com.beust.jcommander.JCommander;
+
+public class TestNGInstance {
+ Logger[] loggers;
+ ConfigurableTestNG configurableTestNG = new ConfigurableTestNG();
+ public TestNGInstance(Logger[] loggers){
+ this.loggers = loggers;
+ }
+ TestNGInstance loadingClassesFrom(ClassLoader testClassLoader){
+ configurableTestNG.addClassLoader(testClassLoader);
+ return TestNGInstance.this;
+ }
+ TestNGInstance using(String[] testOptions){
+ CommandLineArgs args = new CommandLineArgs();
+ new JCommander(args, testOptions); // args is an output parameter of the constructor!
+ configurableTestNG.configure(args);
+ return TestNGInstance.this;
+ }
+
+ TestNGInstance storingEventsIn(EventRecorder basket){
+ configurableTestNG.addListener(basket);
+ return TestNGInstance.this;
+ }
+
+ static void start(TestNGInstance testNG){
+ testNG.configurableTestNG.run();
+ }
+ static TestNGInstance loggingTo(Logger[] loggers){ return new TestNGInstance(loggers); }
+}
+
+
+class ConfigurableTestNG extends TestNG{ // the TestNG method we need is protected
+ public void configure(CommandLineArgs args) { super.configure(args); }
+}
diff --git a/testng/src/mill/testng/TestNGRunner.java b/testng/src/mill/testng/TestNGRunner.java
new file mode 100644
index 00000000..d59c60da
--- /dev/null
+++ b/testng/src/mill/testng/TestNGRunner.java
@@ -0,0 +1,37 @@
+package mill.testng;
+
+import org.scalatools.testing.Fingerprint;
+import org.scalatools.testing.Logger;
+import org.scalatools.testing.Runner2;
+import org.scalatools.testing.EventHandler;
+
+public class TestNGRunner extends Runner2 {
+ ClassLoader testClassLoader;
+ Logger[] loggers;
+ TestRunState state;
+ public TestNGRunner(ClassLoader testClassLoader, Logger[] loggers, TestRunState state) {
+ this.testClassLoader = testClassLoader;
+ this.loggers = loggers;
+ this.state = state;
+ }
+ public void run(String testClassname, Fingerprint fingerprint, EventHandler eventHandler, String[] testOptions) {
+ if (TestRunState.permissionToExecute.tryAcquire()) {
+ TestNGInstance.start(
+ TestNGInstance.loggingTo(loggers)
+ .loadingClassesFrom(testClassLoader)
+ .using(testOptions)
+ .storingEventsIn(state.recorder)
+ );
+
+ state.testCompletion.countDown();
+ }
+
+ try{
+ state.testCompletion.await();
+ }catch(InterruptedException e){
+ throw new RuntimeException(e);
+ }
+
+ state.recorder.replayTo(eventHandler, testClassname, loggers);
+ }
+}
diff --git a/testng/src/mill/testng/TestRunState.java b/testng/src/mill/testng/TestRunState.java
new file mode 100644
index 00000000..3edb2a4d
--- /dev/null
+++ b/testng/src/mill/testng/TestRunState.java
@@ -0,0 +1,10 @@
+package mill.testng;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.CountDownLatch;
+
+public class TestRunState {
+ public static Semaphore permissionToExecute = new Semaphore(1);
+ public static CountDownLatch testCompletion = new CountDownLatch(1);
+ public static EventRecorder recorder = new EventRecorder();
+} \ No newline at end of file