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 &gt; 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]

Reply via email to