diff options
-rwxr-xr-x | build.sc | 8 | ||||
-rw-r--r-- | testng/src/mill/testng/EventRecorder.java | 40 | ||||
-rw-r--r-- | testng/src/mill/testng/ResultEvent.java | 40 | ||||
-rw-r--r-- | testng/src/mill/testng/TestNGFramework.java | 30 | ||||
-rw-r--r-- | testng/src/mill/testng/TestNGInstance.java | 43 | ||||
-rw-r--r-- | testng/src/mill/testng/TestNGRunner.java | 37 | ||||
-rw-r--r-- | testng/src/mill/testng/TestRunState.java | 10 |
7 files changed, 208 insertions, 0 deletions
@@ -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 |