The reason behind why your solution is not killing the JVM that was spawned is perhaps because you are invoking cmd.exe
and from within that is where you are probably spawning the JVM. So when you invoke destroyProcess()
I believe its cmd.exe
that is getting killed but not java.exe
.
You should try changing your command line to something like below:
java -cp D:\MyProject\Utilities*;D:\MyProject\bin org.testng.TestNG D:\MyProject\testng.xml
Here's a solution that does not make use of Apache commons executor for doing this, but manages everything within a single JVM and also provides for a way to retrieve the output and error output from TestNG.
This solution makes use of the TestNG APIs.
The main test runner, that uses TestNG to run tests, in a different thread looks like below
import org.testng.TestNG;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SimpleTestRunner {
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.err.println("***Main Thread running in Thread [" + Thread.currentThread().getId() + "]***");
ExecutorService service = Executors.newCachedThreadPool();
WorkerThread thread = new WorkerThread(SampleTestClass.class);
List<Future<ExecutionResults>> allResults = service.invokeAll(Collections.singletonList(thread));
service.shutdown();
ExecutionResults result = allResults.get(0).get();
System.err.println("******Printing the TestNG output******");
System.err.println(result);
System.err.println("**************************************");
}
public static class WorkerThread implements Callable<ExecutionResults> {
private Class<?>[] classes;
WorkerThread(Class<?>... classes) {
this.classes = classes;
}
@Override
public ExecutionResults call() throws Exception {
System.err.println("***Worker Thread running in Thread [" + Thread.currentThread().getId() + "]***");
TestNG testNG = new TestNG();
ExecutionResults results;
testNG.setVerbose(2);
ConsoleCapturer capturer = new ConsoleCapturer();
testNG.setTestClasses(classes);
try {
capturer.start();
testNG.run();
} finally {
ConsoleCapturer.CapturedData data = capturer.stop();
results = new ExecutionResults(data, testNG.getStatus());
}
return results;
}
}
public static class ExecutionResults {
private ConsoleCapturer.CapturedData data;
private int status;
public ExecutionResults(ConsoleCapturer.CapturedData data, int status) {
this.data = data;
this.status = status;
}
public ConsoleCapturer.CapturedData getData() {
return data;
}
public int getStatus() {
return status;
}
@Override
public String toString() {
return "ExecutionResults{" +
"data=" + getData() +
", status=" + getStatus() +
'}';
}
}
}
The utility class that re-directs all output and error contents to a thread, so that they can be redirected to anywhere, looks like below :
This class is mostly borrowed code from the solution Redirect console output to string in java with some improvisations.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
/**
* This class is an improvisation of the solution provided in https://stackoverflow.com/a/30665299/679824
*/
public class ConsoleCapturer {
private ByteArrayOutputStream baosOutput, baosError;
private PrintStream previousOut, previousError;
private boolean capturing;
public void start() {
if (capturing) {
return;
}
capturing = true;
previousOut = System.out;
previousError = System.err;
baosOutput = new ByteArrayOutputStream();
baosError = new ByteArrayOutputStream();
System.setOut(new PrintStream(new OutputStreamCombiner(previousOut, baosOutput)));
System.setErr(new PrintStream(new OutputStreamCombiner(previousError, baosError)));
}
public CapturedData stop() {
if (!capturing) {
return new CapturedData();
}
System.setOut(previousOut);
System.setErr(previousError);
String output = baosOutput.toString();
String error = baosError.toString();
try {
baosOutput.close();
baosError.close();
} catch (IOException e) {
e.printStackTrace();
}
baosOutput = null;
previousOut = null;
capturing = false;
return new CapturedData(output, error);
}
private static class OutputStreamCombiner extends OutputStream {
private OutputStream[] outputStreams;
OutputStreamCombiner(OutputStream... outputStreams) {
this.outputStreams = outputStreams;
}
public void write(int b) throws IOException {
for (OutputStream os : outputStreams) {
os.write(b);
}
}
public void flush() throws IOException {
for (OutputStream os : outputStreams) {
os.flush();
}
}
public void close() throws IOException {
for (OutputStream os : outputStreams) {
os.close();
}
}
}
public static class CapturedData {
private String output;
private String error;
CapturedData() {
this("", "");
}
public CapturedData(String output, String error) {
this.output = output;
this.error = error;
}
public String getError() {
return error;
}
public String getOutput() {
return output;
}
@Override
public String toString() {
return "CapturedData{" +
"output='" + getOutput() + '\'' +
", error='" + getError() + '\'' +
'}';
}
}
}
The test class used looks like below
import org.testng.annotations.Test;
public class SampleTestClass {
@Test
public void testMethod() {
System.err.println("This goes into the error console");
System.out.println("This goes into the console");
}
}
The output looks like below
***Main Thread running in Thread [1]***
***Worker Thread running in Thread [11]***
This goes into the console
This goes into the error console
PASSED: testMethod
===============================================
Command line test
Tests run: 1, Failures: 0, Skips: 0
===============================================
===============================================
Command line suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
******Printing the TestNG output******
ExecutionResults{data=CapturedData{output='This goes into the console
PASSED: testMethod
===============================================
Command line test
Tests run: 1, Failures: 0, Skips: 0
===============================================
===============================================
Command line suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
', error='This goes into the error console
'}, status=0}
**************************************
Process finished with exit code 0