Return-Path: Delivered-To: apmail-ant-dev-archive@www.apache.org Received: (qmail 25532 invoked from network); 30 Sep 2004 09:42:22 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 30 Sep 2004 09:42:22 -0000 Received: (qmail 15460 invoked by uid 500); 30 Sep 2004 09:42:20 -0000 Delivered-To: apmail-ant-dev-archive@ant.apache.org Received: (qmail 15230 invoked by uid 500); 30 Sep 2004 09:42:18 -0000 Mailing-List: contact dev-help@ant.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Ant Developers List" Reply-To: "Ant Developers List" Delivered-To: mailing list dev@ant.apache.org Received: (qmail 15216 invoked by uid 500); 30 Sep 2004 09:42:18 -0000 Received: (qmail 15212 invoked by uid 99); 30 Sep 2004 09:42:18 -0000 X-ASF-Spam-Status: No, hits=-10.0 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Thu, 30 Sep 2004 02:42:17 -0700 Received: (qmail 25480 invoked by uid 1146); 30 Sep 2004 09:42:16 -0000 Date: 30 Sep 2004 09:42:16 -0000 Message-ID: <20040930094216.25479.qmail@minotaur.apache.org> From: bodewig@apache.org To: ant-cvs@apache.org Subject: cvs commit: ant/src/main/org/apache/tools/ant/taskdefs/optional/junit ForkedVMWatcher.java JUnitTask.java JUnitTestRunner.java X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N 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 - * killedProcess() 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 null, 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. * *

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: dev-unsubscribe@ant.apache.org For additional commands, e-mail: dev-help@ant.apache.org