|
|
@@ -172,11 +172,15 @@ public class JUnitTask extends Task { |
|
|
|
/** A boolean on whether to get the forked path for ant classes */ |
|
|
|
private boolean forkedPathChecked = false; |
|
|
|
|
|
|
|
/* set when a test fails/errs with haltonfailure/haltonerror and >1 thread to stop other threads */ |
|
|
|
private volatile BuildException caughtBuildException = null; |
|
|
|
|
|
|
|
// Attributes for basetest |
|
|
|
private boolean haltOnError = false; |
|
|
|
private boolean haltOnFail = false; |
|
|
|
private boolean filterTrace = true; |
|
|
|
private boolean fork = false; |
|
|
|
private int threads = 1; |
|
|
|
private String failureProperty; |
|
|
|
private String errorProperty; |
|
|
|
|
|
|
@@ -319,6 +323,22 @@ public class JUnitTask extends Task { |
|
|
|
this.forkMode = mode; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Set the number of test threads to be used for parallel test |
|
|
|
* execution. The default is 1, which is the same behavior as |
|
|
|
* before parallel test execution was possible. |
|
|
|
* |
|
|
|
* <p>This attribute will be ignored if tests run in the same VM |
|
|
|
* as Ant.</p> |
|
|
|
* |
|
|
|
* @since Ant 1.9.4 |
|
|
|
*/ |
|
|
|
public void setThreads(int threads) { |
|
|
|
if (threads >= 0) { |
|
|
|
this.threads = threads; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* If true, print one-line statistics for each test, or "withOutAndErr" |
|
|
|
* to also show standard output and error. |
|
|
@@ -798,6 +818,10 @@ public class JUnitTask extends Task { |
|
|
|
setupJUnitDelegate(); |
|
|
|
|
|
|
|
List testLists = new ArrayList(); |
|
|
|
/* parallel test execution is only supported for multi-process execution */ |
|
|
|
int threads = ((!fork) || (forkMode.getValue().equals(ForkMode.ONCE)) |
|
|
|
? 1 |
|
|
|
: this.threads); |
|
|
|
|
|
|
|
boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST); |
|
|
|
if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) { |
|
|
@@ -813,29 +837,172 @@ public class JUnitTask extends Task { |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
Iterator iter = testLists.iterator(); |
|
|
|
while (iter.hasNext()) { |
|
|
|
List l = (List) iter.next(); |
|
|
|
if (l.size() == 1) { |
|
|
|
execute((JUnitTest) l.get(0)); |
|
|
|
} else { |
|
|
|
execute(l); |
|
|
|
} |
|
|
|
} |
|
|
|
/* prior to parallel the code in 'oneJunitThread' used to be here. */ |
|
|
|
runTestsInThreads(testLists, threads); |
|
|
|
} finally { |
|
|
|
cleanup(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* When the list of tests is established, an array of threads is created to pick the |
|
|
|
* tests off the list one at a time and execute them until the list is empty. Tests are |
|
|
|
* not assigned to threads until the thread is available. |
|
|
|
* |
|
|
|
* This class is the runnable thread subroutine that takes care of passing the shared |
|
|
|
* list iterator and the handle back to the main class to the test execution subroutine |
|
|
|
* code 'runTestsInThreads'. One object is created for each thread and each one gets |
|
|
|
* a unique thread id that can be useful for tracing test starts and stops. |
|
|
|
* |
|
|
|
* Because the threads are picking tests off the same list, it is the list *iterator* |
|
|
|
* that must be shared, not the list itself - and the iterator must have a thread-safe |
|
|
|
* ability to pop the list - hence the synchronized 'getNextTest'. |
|
|
|
*/ |
|
|
|
private class JunitTestThread implements Runnable { |
|
|
|
|
|
|
|
JunitTestThread(JUnitTask master, Iterator iterator, int id) { |
|
|
|
this.masterTask = master; |
|
|
|
this.iterator = iterator; |
|
|
|
this.id = id; |
|
|
|
} |
|
|
|
|
|
|
|
public void run() { |
|
|
|
try { |
|
|
|
masterTask.oneJunitThread(iterator, id); |
|
|
|
} catch (BuildException b) { |
|
|
|
/* saved to rethrow in main thread to be like single-threaded case */ |
|
|
|
caughtBuildException = b; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private JUnitTask masterTask; |
|
|
|
private Iterator iterator; |
|
|
|
private int id; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Because the threads are picking tests off the same list, it is the list *iterator* |
|
|
|
* that must be shared, not the list itself - and the iterator must have a thread-safe |
|
|
|
* ability to pop the list - hence the synchronized 'getNextTest'. We can't have two |
|
|
|
* threads get the same test, or two threads simultaneously pop the list so that a test |
|
|
|
* gets skipped! |
|
|
|
*/ |
|
|
|
private List getNextTest(Iterator iter) { |
|
|
|
synchronized(iter) { |
|
|
|
if (iter.hasNext()) { |
|
|
|
return (List) iter.next(); |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* This code loops keeps executing the next test or test bunch (depending on fork mode) |
|
|
|
* on the list of test cases until none are left. Basically this body of code used to |
|
|
|
* be in the execute routine above; now, several copies (one for each test thread) execute |
|
|
|
* simultaneously. The while loop was modified to call the new thread-safe atomic list |
|
|
|
* popping subroutine and the logging messages were added. |
|
|
|
* |
|
|
|
* If one thread aborts due to a BuildException (haltOnError, haltOnFailure, or any other |
|
|
|
* fatal reason, no new tests/batches will be started but the running threads will be |
|
|
|
* permitted to complete. Additional tests may start in already-running batch-test threads. |
|
|
|
*/ |
|
|
|
private void oneJunitThread(Iterator iter, int threadId) { |
|
|
|
|
|
|
|
List l; |
|
|
|
log("Starting test thread " + threadId, Project.MSG_VERBOSE); |
|
|
|
while ((caughtBuildException == null) && ((l = getNextTest(iter)) != null)) { |
|
|
|
log("Running test " + l.get(0).toString() + "(" + l.size() + ") in thread " + threadId, Project.MSG_VERBOSE); |
|
|
|
if (l.size() == 1) { |
|
|
|
execute((JUnitTest) l.get(0), threadId); |
|
|
|
} else { |
|
|
|
execute(l, threadId); |
|
|
|
} |
|
|
|
} |
|
|
|
log("Ending test thread " + threadId, Project.MSG_VERBOSE); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void runTestsInThreads(List testList, int numThreads) { |
|
|
|
|
|
|
|
Iterator iter = testList.iterator(); |
|
|
|
|
|
|
|
if (numThreads == 1) { |
|
|
|
/* with just one thread just run the test - don't create any threads */ |
|
|
|
oneJunitThread(iter, 0); |
|
|
|
} |
|
|
|
else { |
|
|
|
Thread threads[] = new Thread[numThreads]; |
|
|
|
int i; |
|
|
|
boolean exceptionOccurred; |
|
|
|
|
|
|
|
/* Need to split apart tests, which are still grouped in batches */ |
|
|
|
/* is there a simpler Java mechanism to do this? */ |
|
|
|
/* I assume we don't want to do this with "per batch" forking. */ |
|
|
|
List newlist = new ArrayList(); |
|
|
|
if (forkMode.getValue().equals(ForkMode.PER_TEST)) { |
|
|
|
Iterator i1 = testList.iterator(); |
|
|
|
while (i1.hasNext()) { |
|
|
|
List l = (List) i1.next(); |
|
|
|
if (l.size() == 1) { |
|
|
|
newlist.add(l); |
|
|
|
} else { |
|
|
|
Iterator i2 = l.iterator(); |
|
|
|
while (i2.hasNext()) { |
|
|
|
List tmpSingleton = new ArrayList(); |
|
|
|
tmpSingleton.add(i2.next()); |
|
|
|
newlist.add(tmpSingleton); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
newlist = testList; |
|
|
|
} |
|
|
|
iter = newlist.iterator(); |
|
|
|
|
|
|
|
/* create 1 thread using the passthrough class, and let each thread start */ |
|
|
|
for (i = 0; i < numThreads; i++) { |
|
|
|
threads[i] = new Thread(new JunitTestThread(this, iter, i+1)); |
|
|
|
threads[i].start(); |
|
|
|
} |
|
|
|
|
|
|
|
/* wait for all of the threads to complete. Not sure if the exception can actually occur in this use case. */ |
|
|
|
do { |
|
|
|
exceptionOccurred = false; |
|
|
|
|
|
|
|
try { |
|
|
|
for (i = 0; i < numThreads; i++) { |
|
|
|
threads[i].join(); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (InterruptedException e) { |
|
|
|
exceptionOccurred = true; |
|
|
|
} |
|
|
|
} while (exceptionOccurred); |
|
|
|
|
|
|
|
/* an exception occurred in one of the threads - usually a haltOnError/Failure. |
|
|
|
throw the exception again so it behaves like the single-thread case */ |
|
|
|
if (caughtBuildException != null) { |
|
|
|
throw new BuildException(caughtBuildException); |
|
|
|
} |
|
|
|
|
|
|
|
/* all threads are completed - that's all there is to do. */ |
|
|
|
/* control will flow back to the test cleanup call and then execute is done. */ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Run the tests. |
|
|
|
* @param arg one JUnitTest |
|
|
|
* @param thread Identifies which thread is test running in (0 for single-threaded runs) |
|
|
|
* @throws BuildException in case of test failures or errors |
|
|
|
*/ |
|
|
|
protected void execute(JUnitTest arg) throws BuildException { |
|
|
|
protected void execute(JUnitTest arg, int thread) throws BuildException { |
|
|
|
validateTestName(arg.getName()); |
|
|
|
|
|
|
|
JUnitTest test = (JUnitTest) arg.clone(); |
|
|
|
test.setThread(thread); |
|
|
|
|
|
|
|
// set the default values if not specified |
|
|
|
//@todo should be moved to the test class instead. |
|
|
|
if (test.getTodir() == null) { |
|
|
@@ -858,6 +1025,15 @@ public class JUnitTask extends Task { |
|
|
|
actOnTestResult(result, test, "Test " + test.getName()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Run the tests. |
|
|
|
* @param arg one JUnitTest |
|
|
|
* @throws BuildException in case of test failures or errors |
|
|
|
*/ |
|
|
|
protected void execute(JUnitTest arg) throws BuildException { |
|
|
|
execute(arg, 0); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Throws a <code>BuildException</code> if the given test name is invalid. |
|
|
|
* Validity is defined as not <code>null</code>, not empty, and not the |
|
|
@@ -875,9 +1051,10 @@ public class JUnitTask extends Task { |
|
|
|
/** |
|
|
|
* Execute a list of tests in a single forked Java VM. |
|
|
|
* @param testList the list of tests to execute. |
|
|
|
* @param thread Identifies which thread is test running in (0 for single-threaded runs) |
|
|
|
* @throws BuildException on error. |
|
|
|
*/ |
|
|
|
protected void execute(List testList) throws BuildException { |
|
|
|
protected void execute(List testList, int thread) throws BuildException { |
|
|
|
JUnitTest test = null; |
|
|
|
// Create a temporary file to pass the test cases to run to |
|
|
|
// the runner (one test case per line) |
|
|
@@ -894,6 +1071,7 @@ public class JUnitTask extends Task { |
|
|
|
Iterator iter = testList.iterator(); |
|
|
|
while (iter.hasNext()) { |
|
|
|
test = (JUnitTest) iter.next(); |
|
|
|
test.setThread(thread); |
|
|
|
printDual(writer, logWriter, test.getName()); |
|
|
|
if (test.getMethods() != null) { |
|
|
|
printDual(writer, logWriter, ":" + test.getMethodsString().replace(',', '+')); |
|
|
@@ -935,6 +1113,15 @@ public class JUnitTask extends Task { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Execute a list of tests in a single forked Java VM. |
|
|
|
* @param testList the list of tests to execute. |
|
|
|
* @throws BuildException on error. |
|
|
|
*/ |
|
|
|
protected void execute(List testList) throws BuildException { |
|
|
|
execute(testList, 0); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Execute a testcase by forking a new JVM. The command will block |
|
|
|
* until it finishes. To know if the process was destroyed or not |
|
|
@@ -991,6 +1178,8 @@ public class JUnitTask extends Task { |
|
|
|
+ String.valueOf(outputToFormatters)); |
|
|
|
cmd.createArgument().setValue(Constants.LOG_FAILED_TESTS |
|
|
|
+ String.valueOf(logFailedTests)); |
|
|
|
cmd.createArgument().setValue(Constants.THREADID |
|
|
|
+ String.valueOf(test.getThread())); |
|
|
|
|
|
|
|
// #31885 |
|
|
|
cmd.createArgument().setValue(Constants.LOGTESTLISTENEREVENTS |
|
|
@@ -1900,8 +2089,10 @@ public class JUnitTask extends Task { |
|
|
|
while (testList.hasMoreElements()) { |
|
|
|
JUnitTest test = (JUnitTest) testList.nextElement(); |
|
|
|
if (test.shouldRun(getProject())) { |
|
|
|
if (runIndividual || !test.getFork()) { |
|
|
|
execute(test); |
|
|
|
/* with multi-threaded runs need to defer execution of even */ |
|
|
|
/* individual tests so the threads can pick tests off the queue. */ |
|
|
|
if ((runIndividual || !test.getFork()) && (threads == 1)) { |
|
|
|
execute(test, 0); |
|
|
|
} else { |
|
|
|
ForkedTestConfiguration c = |
|
|
|
new ForkedTestConfiguration(test); |
|
|
|