Skip to content

Commit 41180ad

Browse files
committed
Vendored TestNGFramework compiles, ported to Java
1 parent dcd3659 commit 41180ad

File tree

7 files changed

+208
-0
lines changed

7 files changed

+208
-0
lines changed

build.sc

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ object client extends MillPublishModule{
7070
)
7171
}
7272

73+
74+
object testng extends MillPublishModule{
75+
def ivyDeps = Agg(
76+
ivy"org.scala-sbt:test-interface:1.0",
77+
ivy"org.testng:testng:6.11"
78+
)
79+
}
80+
7381
object core extends MillModule {
7482
def moduleDeps = Seq(moduledefs)
7583

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package mill.testng;
2+
3+
import org.scalatools.testing.Event;
4+
import org.testng.ITestResult;
5+
import org.testng.TestListenerAdapter;
6+
7+
import org.scalatools.testing.EventHandler;
8+
import org.scalatools.testing.Logger;
9+
10+
import java.util.HashMap;
11+
12+
public class EventRecorder extends TestListenerAdapter {
13+
private HashMap<String, java.util.List<Event>> basket = new HashMap<>();
14+
15+
String initKey(ITestResult result){
16+
String key = ResultEvent.classNameOf(result);
17+
if (!basket.containsKey(key)) basket.put(key, new java.util.ArrayList<Event>());
18+
return key;
19+
}
20+
public void onTestFailure(ITestResult result){
21+
String key = initKey(result);
22+
basket.get(key).add(ResultEvent.failure(result));
23+
}
24+
public void onTestSkipped(ITestResult result){
25+
String key = initKey(result);
26+
basket.get(key).add(ResultEvent.skipped(result));
27+
}
28+
public void onTestSuccess(ITestResult result){
29+
String key = initKey(result);
30+
basket.get(key).add(ResultEvent.success(result));
31+
}
32+
33+
void replayTo(EventHandler sbt, String className, Logger[] loggers){
34+
synchronized (basket){
35+
for(Event e: basket.remove(className)){
36+
sbt.handle(e);
37+
}
38+
}
39+
}
40+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
package mill.testng;
3+
4+
import org.scalatools.testing.Event;
5+
import org.scalatools.testing.Result;
6+
import org.testng.ITestResult;
7+
8+
public class ResultEvent implements Event {
9+
public Result result;
10+
public String testName;
11+
public String description;
12+
public Throwable error;
13+
14+
public ResultEvent(Result result, String testName, String description, Throwable error) {
15+
this.result = result;
16+
this.testName = testName;
17+
this.description = description;
18+
this.error = error;
19+
}
20+
21+
22+
public Result result(){ return result; }
23+
public String testName(){ return testName; }
24+
public String description(){ return description; }
25+
public Throwable error(){ return error; }
26+
27+
static ResultEvent failure(ITestResult result){ return event(Result.Failure, result); }
28+
static ResultEvent skipped(ITestResult result){ return event(Result.Skipped, result); }
29+
static ResultEvent success(ITestResult result){ return event(Result.Success, result); }
30+
31+
static ResultEvent event(Result result, ITestResult testNGResult) {
32+
return new ResultEvent(
33+
result,
34+
testNGResult.getName(),
35+
testNGResult.getName(),
36+
result != Result.Success ? testNGResult.getThrowable() : null
37+
);
38+
}
39+
static String classNameOf(ITestResult result){ return result.getTestClass().getName(); }
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package mill.testng;
2+
3+
4+
5+
import org.scalatools.testing.*;
6+
7+
8+
public class TestNGFramework implements Framework {
9+
public String name(){ return "TestNG"; }
10+
public Fingerprint[] tests(){
11+
return new Fingerprint[]{
12+
new Annotated("org.testng.annotations.Test")
13+
};
14+
}
15+
16+
public Runner testRunner(ClassLoader testClassLoader, Logger[] loggers){
17+
return new TestNGRunner(testClassLoader, loggers, sharedState);
18+
}
19+
20+
private TestRunState sharedState = new TestRunState();
21+
}
22+
23+
class Annotated implements AnnotatedFingerprint{
24+
String annotationName;
25+
public Annotated(String annotationName) {
26+
this.annotationName = annotationName;
27+
}
28+
public String annotationName(){return annotationName;}
29+
public boolean isModule(){return false;}
30+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package mill.testng;
2+
3+
4+
import org.scalatools.testing.EventHandler;
5+
import org.scalatools.testing.Logger;
6+
7+
import org.testng.CommandLineArgs;
8+
import org.testng.TestNG;
9+
10+
import com.beust.jcommander.JCommander;
11+
12+
public class TestNGInstance {
13+
Logger[] loggers;
14+
ConfigurableTestNG configurableTestNG = new ConfigurableTestNG();
15+
public TestNGInstance(Logger[] loggers){
16+
this.loggers = loggers;
17+
}
18+
TestNGInstance loadingClassesFrom(ClassLoader testClassLoader){
19+
configurableTestNG.addClassLoader(testClassLoader);
20+
return TestNGInstance.this;
21+
}
22+
TestNGInstance using(String[] testOptions){
23+
CommandLineArgs args = new CommandLineArgs();
24+
new JCommander(args, testOptions); // args is an output parameter of the constructor!
25+
configurableTestNG.configure(args);
26+
return TestNGInstance.this;
27+
}
28+
29+
TestNGInstance storingEventsIn(EventRecorder basket){
30+
configurableTestNG.addListener(basket);
31+
return TestNGInstance.this;
32+
}
33+
34+
static void start(TestNGInstance testNG){
35+
testNG.configurableTestNG.run();
36+
}
37+
static TestNGInstance loggingTo(Logger[] loggers){ return new TestNGInstance(loggers); }
38+
}
39+
40+
41+
class ConfigurableTestNG extends TestNG{ // the TestNG method we need is protected
42+
public void configure(CommandLineArgs args) { super.configure(args); }
43+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package mill.testng;
2+
3+
import org.scalatools.testing.Fingerprint;
4+
import org.scalatools.testing.Logger;
5+
import org.scalatools.testing.Runner2;
6+
import org.scalatools.testing.EventHandler;
7+
8+
public class TestNGRunner extends Runner2 {
9+
ClassLoader testClassLoader;
10+
Logger[] loggers;
11+
TestRunState state;
12+
public TestNGRunner(ClassLoader testClassLoader, Logger[] loggers, TestRunState state) {
13+
this.testClassLoader = testClassLoader;
14+
this.loggers = loggers;
15+
this.state = state;
16+
}
17+
public void run(String testClassname, Fingerprint fingerprint, EventHandler eventHandler, String[] testOptions) {
18+
if (TestRunState.permissionToExecute.tryAcquire()) {
19+
TestNGInstance.start(
20+
TestNGInstance.loggingTo(loggers)
21+
.loadingClassesFrom(testClassLoader)
22+
.using(testOptions)
23+
.storingEventsIn(state.recorder)
24+
);
25+
26+
state.testCompletion.countDown();
27+
}
28+
29+
try{
30+
state.testCompletion.await();
31+
}catch(InterruptedException e){
32+
throw new RuntimeException(e);
33+
}
34+
35+
state.recorder.replayTo(eventHandler, testClassname, loggers);
36+
}
37+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package mill.testng;
2+
3+
import java.util.concurrent.Semaphore;
4+
import java.util.concurrent.CountDownLatch;
5+
6+
public class TestRunState {
7+
public static Semaphore permissionToExecute = new Semaphore(1);
8+
public static CountDownLatch testCompletion = new CountDownLatch(1);
9+
public static EventRecorder recorder = new EventRecorder();
10+
}

0 commit comments

Comments
 (0)