// Process test classes and resources.
long start = System.currentTimeMillis();
final TestsCollection testCollection = processTestResources();
final EventBus aggregatedBus = new EventBus("aggregated");
final TestsSummaryEventListener summaryListener = new TestsSummaryEventListener();
aggregatedBus.register(summaryListener);
for (Object o : listeners) {
if (o instanceof ProjectComponent) {
((ProjectComponent) o).setProject(getProject());
}
if (o instanceof AggregatedEventListener) {
((AggregatedEventListener) o).setOuter(this);
}
aggregatedBus.register(o);
}
if (testCollection.testClasses.isEmpty()) {
aggregatedBus.post(new AggregatedQuitEvent());
} else {
start = System.currentTimeMillis();
// Check if we allow duplicate suite names. Some reports (ANT compatible XML
// reports) will have a problem with duplicate suite names, for example.
if (uniqueSuiteNames) {
testCollection.onlyUniqueSuiteNames();
}
final int jvmCount = determineForkedJvmCount(testCollection);
final List<ForkedJvmInfo> slaveInfos = Lists.newArrayList();
for (int jvmid = 0; jvmid < jvmCount; jvmid++) {
final ForkedJvmInfo slaveInfo = new ForkedJvmInfo(jvmid, jvmCount);
slaveInfos.add(slaveInfo);
}
if (jvmCount > 1 && uniqueSuiteNames && testCollection.hasReplicatedSuites()) {
throw new BuildException(String.format(Locale.ENGLISH,
"There are test suites that request JVM replication and the number of forked JVMs %d is larger than 1. Run on a single JVM.",
jvmCount));
}
// Prepare a pool of suites dynamically dispatched to slaves as they become idle.
final Deque<String> stealingQueue =
new ArrayDeque<String>(loadBalanceSuites(slaveInfos, testCollection, balancers));
aggregatedBus.register(new Object() {
@Subscribe
public void onSlaveIdle(SlaveIdle slave) {
if (stealingQueue.isEmpty()) {
slave.finished();
} else {
String suiteName = stealingQueue.pop();
slave.newSuite(suiteName);
}
}
});
// Check for filtering expressions.
@SuppressWarnings("unchecked")
Vector<Variable> vv = getCommandline().getSystemProperties().getVariablesVector();
for (Variable v : vv) {
if (SysGlobals.SYSPROP_TESTFILTER().equals(v.getKey())) {
try {
Node root = new FilterExpressionParser().parse(v.getValue());
log("Parsed test filtering expression: " + root.toExpression(), Project.MSG_INFO);
} catch (Exception e) {
log("Could not parse filtering expression: " + v.getValue(), Project.MSG_WARN);
}
}
}
// Create callables for the executor.
final List<Callable<Void>> slaves = Lists.newArrayList();
for (int slave = 0; slave < jvmCount; slave++) {
final ForkedJvmInfo slaveInfo = slaveInfos.get(slave);
slaves.add(new Callable<Void>() {
@Override
public Void call() throws Exception {
executeSlave(slaveInfo, aggregatedBus);
return null;
}
});
}
ExecutorService executor = Executors.newCachedThreadPool();
aggregatedBus.post(new AggregatedStartEvent(slaves.size(),
// TODO: this doesn't account for replicated suites.
testCollection.testClasses.size()));
try {
List<Future<Void>> all = executor.invokeAll(slaves);
executor.shutdown();
for (int i = 0; i < slaves.size(); i++) {
Future<Void> f = all.get(i);
try {
f.get();
} catch (ExecutionException e) {
slaveInfos.get(i).executionError = e.getCause();
}
}
} catch (InterruptedException e) {
log("Master interrupted? Weird.", Project.MSG_ERR);
}
aggregatedBus.post(new AggregatedQuitEvent());
for (ForkedJvmInfo si : slaveInfos) {
if (si.start > 0 && si.end > 0) {
log(String.format(Locale.ENGLISH, "JVM J%d: %8.2f .. %8.2f = %8.2fs",
si.id,