bodewig 2004/09/30 02:42:16
Modified: src/main/org/apache/tools/ant/taskdefs/optional/junit
JUnitTask.java JUnitTestRunner.java
Added: src/main/org/apache/tools/ant/taskdefs/optional/junit
ForkedVMWatcher.java
Log:
Add a simple TestListener to each forked VM that does nothing but
write a single byte to a file.
If that file is empty when the test runner finishes, the VM has exited
and we should consider the tests as failed.
PR: 30333
Revision Changes Path
1.101 +81 -44
ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
Index: JUnitTask.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java,v
retrieving revision 1.100
retrieving revision 1.101
diff -u -r1.100 -r1.101
--- JUnitTask.java 27 Jul 2004 14:04:09 -0000 1.100
+++ JUnitTask.java 30 Sep 2004 09:42:16 -0000 1.101
@@ -683,19 +683,15 @@
}
// 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 @@
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 @@
}
/**
- * 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 @@
}
}
+ 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 @@
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 @@
}
}
- return retVal;
+ return result;
}
/**
@@ -1010,7 +1015,7 @@
* @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 @@
}
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 @@
*/
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 @@
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 @@
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 logTimeout method.
- * @since Ant 1.6
+ * Adds the actual error message to the formatter.
+ * Only used from the logVmExit method.
+ * @since Ant 1.7
*/
- private void addTimeout(JUnitTest test, JUnitResultFormatter formatter,
- OutputStream out) {
+ private void addVmExit(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 @@
*
* @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 @@
}
}
+ private class TestResultHolder {
+ public int exitCode = JUnitTestRunner.ERRORS;
+ public boolean timedOut = false;
+ public boolean crashed = false;
+ }
}
1.50 +1 -1
ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
Index: JUnitTestRunner.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -r1.49 -r1.50
--- JUnitTestRunner.java 3 Aug 2004 23:16:16 -0000 1.49
+++ JUnitTestRunner.java 30 Sep 2004 09:42:16 -0000 1.50
@@ -244,7 +244,7 @@
if (exception != null) { // had an exception in the constructor
for (int i = 0; i < formatters.size(); i++) {
((TestListener) formatters.elementAt(i)).addError(null,
- exception);
+ exception);
}
junitTest.setCounts(1, 0, 1);
junitTest.setRunTime(0);
1.1
ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/ForkedVMWatcher.java
Index: ForkedVMWatcher.java
===================================================================
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant.taskdefs.optional.junit;
import java.io.IOException;
import java.io.OutputStream;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import org.apache.tools.ant.BuildException;
/**
* writes a single 0 byte to the given output stream in endTestSuite.
*/
public class ForkedVMWatcher implements JUnitResultFormatter {
/**
* OutputStream to write to.
*/
private OutputStream out;
/**
* Empty
*/
public ForkedVMWatcher() {
}
/**
* Empty
*/
public void startTestSuite(JUnitTest suite) {
}
/**
* Empty
*/
public void startTest(Test t) {
}
/**
* Empty
*/
public void endTest(Test test) {
}
/**
* Empty
*/
public void addFailure(Test test, Throwable t) {
}
/**
* Interface TestListener for JUnit > 3.4.
*
* <p>A Test failed.
*/
public void addFailure(Test test, AssertionFailedError t) {
addFailure(test, (Throwable) t);
}
/**
* Empty
*/
public void addError(Test test, Throwable t) {
}
/**
* Empty
*/
public void setSystemOutput(String out) {
}
/**
* Empty
*/
public void setSystemError(String err) {
}
public void setOutput(OutputStream out) {
this.out = out;
}
/**
* The whole testsuite ended.
*/
public void endTestSuite(JUnitTest suite) throws BuildException {
try {
out.write(0);
out.flush();
} catch (IOException ioex) {
throw new BuildException("Unable to write output", ioex);
} finally {
if (out != System.out && out != System.err) {
try {
out.close();
} catch (IOException e) {
// ignore
}
}
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]