@@ -683,19 +683,15 @@ public class JUnitTask extends Task {
}
// execute the test and get the return code
int exitValue = JUnitTestRunner.ERRORS;
boolean wasKilled = false;
TestResultHolder result = null;
if (!test.getFork()) {
exitValue = executeInVM(test);
result = executeInVM(test);
} else {
ExecuteWatchdog watchdog = createWatchdog();
exitValue = executeAsForked(test, watchdog, null);
result = executeAsForked(test, watchdog, null);
// null watchdog means no timeout, you'd better not check with null
if (watchdog != null) {
wasKilled = watchdog.killedProcess();
}
}
actOnTestResult(exitValue, wasKilled , test, "Test " + test.getName());
actOnTestResult(result, test, "Test " + test.getName());
}
/**
@@ -731,16 +727,10 @@ public class JUnitTask extends Task {
writer = null;
// execute the test and get the return code
int exitValue = JUnitTestRunner.ERRORS;
boolean wasKilled = false;
ExecuteWatchdog watchdog = createWatchdog();
exitValue = executeAsForked(test, watchdog, casesFile);
// null watchdog means no timeout, you'd better not check
// with null
if (watchdog != null) {
wasKilled = watchdog.killedProcess();
}
actOnTestResult(exitValue, wasKilled, test, "Tests");
TestResultHolder result =
executeAsForked(test, watchdog, casesFile);
actOnTestResult(result, test, "Tests");
} catch(IOException e) {
log(e.toString(), Project.MSG_ERR);
throw new BuildException(e);
@@ -758,18 +748,22 @@ public class JUnitTask extends Task {
}
/**
* Execute a testcase by forking a new JVM. The command will block until
* it finishes. To know if the process was destroyed or not, use the
* <tt>killedProcess()</tt> method of the watchdog class.
* Execute a testcase by forking a new JVM. The command will block
* until it finishes. To know if the process was destroyed or not
* or whether the forked Java VM exited abnormally, use the
* attributes of the returned holder object.
* @param test the testcase to execute.
* @param watchdog the watchdog in charge of cancelling the test if it
* exceeds a certain amount of time. Can be <tt>null</tt>, in this case
* the test could probably hang forever.
* @param ForkedVMState will hold information about the forked
* VM's sanity.
* @throws BuildException in case of error creating a temporary property file,
* or if the junit process can not be forked
*/
private int executeAsForked(JUnitTest test, ExecuteWatchdog watchdog,
File casesFile)
private TestResultHolder executeAsForked(JUnitTest test,
ExecuteWatchdog watchdog,
File casesFile)
throws BuildException {
if (perm != null) {
@@ -836,6 +830,12 @@ public class JUnitTask extends Task {
}
}
File vmWatcher = createTempPropertiesFile("junitvmwatcher");
formatterArg.append("formatter=");
formatterArg.append(ForkedVMWatcher.class.getName());
formatterArg.append(",");
formatterArg.append(vmWatcher);
cmd.createArgument().setValue(formatterArg.toString());
File propsFile = createTempPropertiesFile("junit");
cmd.createArgument().setValue("propsfile="
@@ -877,15 +877,20 @@ public class JUnitTask extends Task {
execute.setEnvironment(environment);
log(cmd.describeCommand(), Project.MSG_VERBOSE);
int retVal ;
TestResultHolder result = new TestResultHolder() ;
try {
retVal = execute.execute();
result.exitCode = execute.execute();
} catch (IOException e) {
throw new BuildException("Process fork failed.", e, getLocation());
} finally {
if (watchdog != null && watchdog.killedProcess()) {
result.timedOut = true;
logTimeout(feArray, test);
} else if (vmWatcher.length() == 0) {
result.crashed = true;
logVmCrash(feArray, test);
}
vmWatcher.delete();
if (!propsFile.delete()) {
throw new BuildException("Could not delete temporary "
@@ -893,7 +898,7 @@ public class JUnitTask extends Task {
}
}
return retVal ;
return result ;
}
/**
@@ -1010,7 +1015,7 @@ public class JUnitTask extends Task {
* @param arg one JUnitTest
* @throws BuildException under unspecified circumstances
*/
private int executeInVM(JUnitTest arg) throws BuildException {
private TestResultHolder executeInVM(JUnitTest arg) throws BuildException {
JUnitTest test = (JUnitTest) arg.clone();
test.setProperties(getProject().getProperties());
if (dir != null) {
@@ -1072,7 +1077,9 @@ public class JUnitTask extends Task {
}
runner.run();
return runner.getRetCode();
TestResultHolder result = new TestResultHolder();
result.exitCode = runner.getRetCode();
return result;
} finally {
if (sysProperties != null) {
sysProperties.restoreSystem();
@@ -1211,6 +1218,28 @@ public class JUnitTask extends Task {
*/
private void logTimeout(FormatterElement[] feArray, JUnitTest test) {
logVmExit(feArray, test, "Timeout occurred");
}
/**
* Take care that some output is produced in report files if the
* forked machine exited before the test suite finished but the
* reason is not a timeout.
*
* @since Ant 1.7
*/
private void logVmCrash(FormatterElement[] feArray, JUnitTest test) {
logVmExit(feArray, test, "forked Java VM exited abnormally");
}
/**
* Take care that some output is produced in report files if the
* forked machine existed before the test suite finished
*
* @since Ant 1.7
*/
private void logVmExit(FormatterElement[] feArray, JUnitTest test,
String message) {
createClassLoader();
test.setCounts(1, 0, 1);
test.setProperties(getProject().getProperties());
@@ -1221,7 +1250,7 @@ public class JUnitTask extends Task {
if (outFile != null && formatter != null) {
try {
OutputStream out = new FileOutputStream(outFile);
addTimeout(test, formatter, out );
addVmExit(test, formatter, out, message );
} catch (IOException e) {
// ignore
}
@@ -1230,31 +1259,31 @@ public class JUnitTask extends Task {
if (summary) {
SummaryJUnitResultFormatter f = new SummaryJUnitResultFormatter();
f.setWithOutAndErr("withoutanderr".equalsIgnoreCase(summaryValue));
addTimeout(test, f, getDefaultOutput() );
addVmExit(test, f, getDefaultOutput(), message );
}
}
/**
* Adds the actual timeout to the formatter.
* Only used from the logTimeou t method.
* @since Ant 1.6
* Adds the actual error message to the formatter.
* Only used from the logVmExi t method.
* @since Ant 1.7
*/
private void addTimeou t(JUnitTest test, JUnitResultFormatter formatter,
OutputStream out) {
private void addVmExi t(JUnitTest test, JUnitResultFormatter formatter,
OutputStream out, final String message ) {
formatter.setOutput(out);
formatter.startTestSuite(test);
//the trick to integrating test output to the formatter, is to
//create a special test class that asserts a timout occurred,
//create a special test class that asserts an error
//and tell the formatter that it raised.
Test t = new Test() {
public int countTestCases() { return 1; }
public void run(TestResult r) {
throw new AssertionFailedError("Timeout occurred" );
throw new AssertionFailedError(message );
}
};
formatter.startTest(t);
formatter.addError(t, new AssertionFailedError("Timeout occurred" ));
formatter.addError(t, new AssertionFailedError(message ));
formatter.endTestSuite(test);
}
@@ -1445,22 +1474,25 @@ public class JUnitTask extends Task {
*
* @since Ant 1.6.2
*/
protected void actOnTestResult(int exitValue, boolean wasKilled,
JUnitTest test, String name) {
protected void actOnTestResult(TestResultHolder result,JUnitTest test,
String name) {
// if there is an error/failure and that it should halt, stop
// everything otherwise just log a statement
boolean fatal = result.timedOut || result.crashed;
boolean errorOccurredHere =
exitValue == JUnitTestRunner.ERRORS || wasKilled ;
result.exitCode == JUnitTestRunner.ERRORS || fatal ;
boolean failureOccurredHere =
exitValue != JUnitTestRunner.SUCCESS || wasKilled ;
result.exitCode != JUnitTestRunner.SUCCESS || fatal ;
if (errorOccurredHere || failureOccurredHere) {
if ((errorOccurredHere && test.getHaltonerror())
|| (failureOccurredHere && test.getHaltonfailure())) {
throw new BuildException(name + " failed"
+ (wasKilled ? " (timeout)" : ""), getLocation());
+ (result.timedOut ? " (timeout)" : "")
+ (result.crashed ? " (crashed)" : ""), getLocation());
} else {
log(name + " FAILED"
+ (wasKilled ? " (timeout)" : ""), Project.MSG_ERR);
+ (result.timedOut ? " (timeout)" : "")
+ (result.crashed ? " (crashed)" : ""), Project.MSG_ERR);
if (errorOccurredHere && test.getErrorProperty() != null) {
getProject().setNewProperty(test.getErrorProperty(), "true");
}
@@ -1471,4 +1503,9 @@ public class JUnitTask extends Task {
}
}
private class TestResultHolder {
public int exitCode = JUnitTestRunner.ERRORS;
public boolean timedOut = false;
public boolean crashed = false;
}
}