Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 1,031   Methods: 45
NCLOC: 502   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
JUnitTask.java 37.5% 47.5% 53.3% 45.5%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.taskdefs.optional.junit;
 56   
 
 57   
 import java.io.File;
 58   
 import java.io.FileOutputStream;
 59   
 import java.io.IOException;
 60   
 import java.io.OutputStream;
 61   
 import java.net.URL;
 62   
 import java.util.Enumeration;
 63   
 import java.util.Hashtable;
 64   
 import java.util.Properties;
 65   
 import java.util.Vector;
 66   
 import org.apache.tools.ant.AntClassLoader;
 67   
 import org.apache.tools.ant.BuildException;
 68   
 import org.apache.tools.ant.Project;
 69   
 import org.apache.tools.ant.Task;
 70   
 import org.apache.tools.ant.taskdefs.Execute;
 71   
 import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
 72   
 import org.apache.tools.ant.taskdefs.LogOutputStream;
 73   
 import org.apache.tools.ant.taskdefs.LogStreamHandler;
 74   
 import org.apache.tools.ant.types.Commandline;
 75   
 import org.apache.tools.ant.types.CommandlineJava;
 76   
 import org.apache.tools.ant.types.EnumeratedAttribute;
 77   
 import org.apache.tools.ant.types.Environment;
 78   
 import org.apache.tools.ant.types.Path;
 79   
 import org.apache.tools.ant.util.FileUtils;
 80   
 import org.apache.tools.ant.util.LoaderUtils;
 81   
 import junit.framework.AssertionFailedError;
 82   
 import junit.framework.Test;
 83   
 import junit.framework.TestResult;
 84   
 
 85   
 /**
 86   
  * Runs JUnit tests.
 87   
  *
 88   
  * <p> JUnit is a framework to create unit test. It has been initially
 89   
  * created by Erich Gamma and Kent Beck.  JUnit can be found at <a
 90   
  * href="http://www.junit.org">http://www.junit.org</a>.
 91   
  *
 92   
  * <p> <code>JUnitTask</code> can run a single specific
 93   
  * <code>JUnitTest</code> using the <code>test</code> element.</p>
 94   
  * For example, the following target <code><pre>
 95   
  *   &lt;target name="test-int-chars" depends="jar-test"&gt;
 96   
  *       &lt;echo message="testing international characters"/&gt;
 97   
  *       &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
 98   
  *           &lt;classpath refid="classpath"/&gt;
 99   
  *           &lt;formatter type="plain" usefile="false" /&gt;
 100   
  *           &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
 101   
  *       &lt;/junit&gt;
 102   
  *   &lt;/target&gt;
 103   
  * </pre></code>
 104   
  * <p>runs a single junit test
 105   
  * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
 106   
  * VM using the path with id <code>classpath</code> as classpath and
 107   
  * presents the results formatted using the standard
 108   
  * <code>plain</code> formatter on the command line.</p>
 109   
  *
 110   
  * <p> This task can also run batches of tests.  The
 111   
  * <code>batchtest</code> element creates a <code>BatchTest</code>
 112   
  * based on a fileset.  This allows, for example, all classes found in
 113   
  * directory to be run as testcases.</p>
 114   
  *
 115   
  * <p>For example,</p><code><pre>
 116   
  * &lt;target name="run-tests" depends="dump-info,compile-tests" if="junit.present"&gt;
 117   
  *   &lt;junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"&gt;
 118   
  *     &lt;jvmarg value="-classic"/&gt;
 119   
  *     &lt;classpath refid="tests-classpath"/&gt;
 120   
  *     &lt;sysproperty key="build.tests" value="${build.tests}"/&gt;
 121   
  *     &lt;formatter type="brief" usefile="false" /&gt;
 122   
  *     &lt;batchtest&gt;
 123   
  *       &lt;fileset dir="${tests.dir}"&gt;
 124   
  *         &lt;include name="**&#047;*Test*" /&gt;
 125   
  *       &lt;/fileset&gt;
 126   
  *     &lt;/batchtest&gt;
 127   
  *   &lt;/junit&gt;
 128   
  * &lt;/target&gt;
 129   
  * </pre></code>
 130   
  * <p>this target finds any classes with a <code>test</code> directory
 131   
  * anywhere in their path (under the top <code>${tests.dir}</code>, of
 132   
  * course) and creates <code>JUnitTest</code>'s for each one.</p>
 133   
  *
 134   
  * <p> Of course, <code>&lt;junit&gt;</code> and
 135   
  * <code>&lt;batch&gt;</code> elements can be combined for more
 136   
  * complex tests. For an example, see the ant <code>build.xml</code>
 137   
  * target <code>run-tests</code> (the second example is an edited
 138   
  * version).</p>
 139   
  *
 140   
  * <p> To spawn a new Java VM to prevent interferences between
 141   
  * different testcases, you need to enable <code>fork</code>.  A
 142   
  * number of attributes and elements allow you to set up how this JVM
 143   
  * runs.
 144   
  * @author Thomas Haas
 145   
  * @author Stefan Bodewig
 146   
  * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a>
 147   
  * @author <a href="mailto:Gerrit.Riessen@web.de">Gerrit Riessen</a>
 148   
  * @author <a href="mailto:ehatcher@apache.org">Erik Hatcher</a>
 149   
  * @author <a href="mailto:martijn@kruithof.xs4all.nl">Martijn Kruithof></a>
 150   
  *
 151   
  * @version $Revision: 1.61 $
 152   
  *
 153   
  * @since Ant 1.2
 154   
  *
 155   
  * @see JUnitTest
 156   
  * @see BatchTest
 157   
  */
 158   
 public class JUnitTask extends Task {
 159   
 
 160   
     private CommandlineJava commandline = new CommandlineJava();
 161   
     private Vector tests = new Vector();
 162   
     private Vector batchTests = new Vector();
 163   
     private Vector formatters = new Vector();
 164   
     private File dir = null;
 165   
 
 166   
     private Integer timeout = null;
 167   
     private boolean summary = false;
 168   
     private String summaryValue = "";
 169   
     private JUnitTestRunner runner = null;
 170   
 
 171   
     private boolean newEnvironment = false;
 172   
     private Environment env = new Environment();
 173   
 
 174   
     private boolean includeAntRuntime = true;
 175   
     private Path antRuntimeClasses = null;
 176   
 
 177   
     private boolean showOutput = false;
 178   
     private File tmpDir;
 179   
 
 180   
     /**
 181   
      * If true, smartly filter the stack frames of
 182   
      * JUnit errors and failures before reporting them.
 183   
      *
 184   
      * <p>This property is applied on all BatchTest (batchtest) and
 185   
      * JUnitTest (test) however it can possibly be overridden by their
 186   
      * own properties.</p>
 187   
      * @param value <tt>false</tt> if it should not filter, otherwise
 188   
      * <tt>true<tt>
 189   
      *
 190   
      * @since Ant 1.5
 191   
      */
 192  1
     public void setFiltertrace(boolean value) {
 193  1
         Enumeration enum = allTests();
 194  1
         while (enum.hasMoreElements()) {
 195  1
             BaseTest test = (BaseTest) enum.nextElement();
 196  1
             test.setFiltertrace(value);
 197   
         }
 198   
     }
 199   
 
 200   
     /**
 201   
      * If true, stop the build process when there is an error in a test.
 202   
      * This property is applied on all BatchTest (batchtest) and JUnitTest
 203   
      * (test) however it can possibly be overridden by their own
 204   
      * properties.
 205   
      * @param value <tt>true</tt> if it should halt, otherwise
 206   
      * <tt>false</tt>
 207   
      *
 208   
      * @since Ant 1.2
 209   
      */
 210  0
     public void setHaltonerror(boolean value) {
 211  0
         Enumeration enum = allTests();
 212  0
         while (enum.hasMoreElements()) {
 213  0
             BaseTest test = (BaseTest) enum.nextElement();
 214  0
             test.setHaltonerror(value);
 215   
         }
 216   
     }
 217   
 
 218   
     /**
 219   
      * Property to set to "true" if there is a error in a test.
 220   
      *
 221   
      * <p>This property is applied on all BatchTest (batchtest) and
 222   
      * JUnitTest (test), however, it can possibly be overriden by
 223   
      * their own properties.</p>
 224   
      * @param propertyName the name of the property to set in the
 225   
      * event of an error.
 226   
      *
 227   
      * @since Ant 1.4
 228   
      */
 229  0
     public void setErrorProperty(String propertyName) {
 230  0
         Enumeration enum = allTests();
 231  0
         while (enum.hasMoreElements()) {
 232  0
             BaseTest test = (BaseTest) enum.nextElement();
 233  0
             test.setErrorProperty(propertyName);
 234   
         }
 235   
     }
 236   
 
 237   
     /**
 238   
      * If true, stop the build process if a test fails
 239   
      * (errors are considered failures as well).
 240   
      * This property is applied on all BatchTest (batchtest) and
 241   
      * JUnitTest (test) however it can possibly be overridden by their
 242   
      * own properties.
 243   
      * @param value <tt>true</tt> if it should halt, otherwise
 244   
      * <tt>false</tt>
 245   
      *
 246   
      * @since Ant 1.2
 247   
      */
 248  1
     public void setHaltonfailure(boolean value) {
 249  1
         Enumeration enum = allTests();
 250  1
         while (enum.hasMoreElements()) {
 251  1
             BaseTest test = (BaseTest) enum.nextElement();
 252  1
             test.setHaltonfailure(value);
 253   
         }
 254   
     }
 255   
 
 256   
     /**
 257   
      * Property to set to "true" if there is a failure in a test.
 258   
      *
 259   
      * <p>This property is applied on all BatchTest (batchtest) and
 260   
      * JUnitTest (test), however, it can possibly be overriden by
 261   
      * their own properties.</p>
 262   
      * @param propertyName the name of the property to set in the
 263   
      * event of an failure.
 264   
      *
 265   
      * @since Ant 1.4
 266   
      */
 267  1
     public void setFailureProperty(String propertyName) {
 268  1
         Enumeration enum = allTests();
 269  1
         while (enum.hasMoreElements()) {
 270  1
             BaseTest test = (BaseTest) enum.nextElement();
 271  1
             test.setFailureProperty(propertyName);
 272   
         }
 273   
     }
 274   
 
 275   
     /**
 276   
      * If true, JVM should be forked for each test.
 277   
      *
 278   
      * <p>It avoids interference between testcases and possibly avoids
 279   
      * hanging the build.  this property is applied on all BatchTest
 280   
      * (batchtest) and JUnitTest (test) however it can possibly be
 281   
      * overridden by their own properties.</p>
 282   
      * @param value <tt>true</tt> if a JVM should be forked, otherwise
 283   
      * <tt>false</tt>
 284   
      * @see #setTimeout
 285   
      *
 286   
      * @since Ant 1.2
 287   
      */
 288  1
     public void setFork(boolean value) {
 289  1
         Enumeration enum = allTests();
 290  1
         while (enum.hasMoreElements()) {
 291  1
             BaseTest test = (BaseTest) enum.nextElement();
 292  1
             test.setFork(value);
 293   
         }
 294   
     }
 295   
 
 296   
     /**
 297   
      * If true, print one-line statistics for each test, or "withOutAndErr"
 298   
      * to also show standard output and error.
 299   
      *
 300   
      * Can take the values on, off, and withOutAndErr.
 301   
      * @param value <tt>true</tt> to print a summary,
 302   
      * <tt>withOutAndErr</tt> to include the test&apos;s output as
 303   
      * well, <tt>false</tt> otherwise.
 304   
      * @see SummaryJUnitResultFormatter
 305   
      *
 306   
      * @since Ant 1.2
 307   
      */
 308  1
     public void setPrintsummary(SummaryAttribute value) {
 309  1
         summaryValue = value.getValue();
 310  1
         summary = value.asBoolean();
 311   
     }
 312   
 
 313   
     /**
 314   
      * Print summary enumeration values.
 315   
      */
 316   
     public static class SummaryAttribute extends EnumeratedAttribute {
 317  1
         public String[] getValues() {
 318  1
             return new String[] {"true", "yes", "false", "no",
 319   
                                  "on", "off", "withOutAndErr"};
 320   
         }
 321   
 
 322  1
         public boolean asBoolean() {
 323  1
             String value = getValue();
 324  1
             return "true".equals(value)
 325   
                 || "on".equals(value)
 326   
                 || "yes".equals(value)
 327   
                 || "withOutAndErr".equals(value);
 328   
         }
 329   
     }
 330   
 
 331   
     /**
 332   
      * Set the timeout value (in milliseconds).
 333   
      *
 334   
      * <p>If the test is running for more than this value, the test
 335   
      * will be canceled. (works only when in 'fork' mode).</p>
 336   
      * @param value the maximum time (in milliseconds) allowed before
 337   
      * declaring the test as 'timed-out'
 338   
      * @see #setFork(boolean)
 339   
      *
 340   
      * @since Ant 1.2
 341   
      */
 342  0
     public void setTimeout(Integer value) {
 343  0
         timeout = value;
 344   
     }
 345   
 
 346   
     /**
 347   
      * Set the maximum memory to be used by all forked JVMs.
 348   
      * @param   max     the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
 349   
      *                  in the java command line options.
 350   
      *
 351   
      * @since Ant 1.2
 352   
      */
 353  0
     public void setMaxmemory(String max) {
 354  0
         commandline.setMaxmemory(max);
 355   
     }
 356   
 
 357   
     /**
 358   
      * The command used to invoke the Java Virtual Machine,
 359   
      * default is 'java'. The command is resolved by
 360   
      * java.lang.Runtime.exec(). Ignored if fork is disabled.
 361   
      *
 362   
      * @param   value   the new VM to use instead of <tt>java</tt>
 363   
      * @see #setFork(boolean)
 364   
      *
 365   
      * @since Ant 1.2
 366   
      */
 367  0
     public void setJvm(String value) {
 368  0
         commandline.setVm(value);
 369   
     }
 370   
 
 371   
     /**
 372   
      * Adds a JVM argument; ignored if not forking.
 373   
      *
 374   
      * @return create a new JVM argument so that any argument can be
 375   
      * passed to the JVM.
 376   
      * @see #setFork(boolean)
 377   
      *
 378   
      * @since Ant 1.2
 379   
      */
 380  0
     public Commandline.Argument createJvmarg() {
 381  0
         return commandline.createVmArgument();
 382   
     }
 383   
 
 384   
     /**
 385   
      * The directory to invoke the VM in. Ignored if no JVM is forked.
 386   
      * @param   dir     the directory to invoke the JVM from.
 387   
      * @see #setFork(boolean)
 388   
      *
 389   
      * @since Ant 1.2
 390   
      */
 391  0
     public void setDir(File dir) {
 392  0
         this.dir = dir;
 393   
     }
 394   
 
 395   
     /**
 396   
      * Adds a system property that tests can access.
 397   
      * This might be useful to tranfer Ant properties to the
 398   
      * testcases when JVM forking is not enabled.
 399   
      *
 400   
      * @since Ant 1.3
 401   
      */
 402  3
     public void addSysproperty(Environment.Variable sysp) {
 403  3
         commandline.addSysproperty(sysp);
 404   
     }
 405   
 
 406   
     /**
 407   
      * Adds path to classpath used for tests.
 408   
      *
 409   
      * @since Ant 1.2
 410   
      */
 411  2
     public Path createClasspath() {
 412  2
         return commandline.createClasspath(getProject()).createPath();
 413   
     }
 414   
 
 415   
     /**
 416   
      * Adds an environment variable; used when forking.
 417   
      *
 418   
      * <p>Will be ignored if we are not forking a new VM.</p>
 419   
      *
 420   
      * @since Ant 1.5
 421   
      */
 422  0
     public void addEnv(Environment.Variable var) {
 423  0
         env.addVariable(var);
 424   
     }
 425   
 
 426   
     /**
 427   
      * If true, use a new environment when forked.
 428   
      *
 429   
      * <p>Will be ignored if we are not forking a new VM.</p>
 430   
      *
 431   
      * @since Ant 1.5
 432   
      */
 433  0
     public void setNewenvironment(boolean newenv) {
 434  0
         newEnvironment = newenv;
 435   
     }
 436   
 
 437   
     /**
 438   
      * Add a new single testcase.
 439   
      * @param   test    a new single testcase
 440   
      * @see JUnitTest
 441   
      *
 442   
      * @since Ant 1.2
 443   
      */
 444  0
     public void addTest(JUnitTest test) {
 445  0
         tests.addElement(test);
 446   
     }
 447   
 
 448   
     /**
 449   
      * Adds a set of tests based on pattern matching.
 450   
      *
 451   
      * @return  a new instance of a batch test.
 452   
      * @see BatchTest
 453   
      *
 454   
      * @since Ant 1.2
 455   
      */
 456  1
     public BatchTest createBatchTest() {
 457  1
         BatchTest test = new BatchTest(getProject());
 458  1
         batchTests.addElement(test);
 459  1
         return test;
 460   
     }
 461   
 
 462   
     /**
 463   
      * Add a new formatter to all tests of this task.
 464   
      *
 465   
      * @since Ant 1.2
 466   
      */
 467  1
     public void addFormatter(FormatterElement fe) {
 468  1
         formatters.addElement(fe);
 469   
     }
 470   
 
 471   
     /**
 472   
      * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
 473   
      *
 474   
      * @since Ant 1.5
 475   
      */
 476  0
     public void setIncludeantruntime(boolean b) {
 477  0
         includeAntRuntime = b;
 478   
     }
 479   
 
 480   
     /**
 481   
      * If true, send any output generated by tests to Ant's logging system
 482   
      * as well as to the formatters.
 483   
      * By default only the formatters receive the output.
 484   
      *
 485   
      * <p>Output will always be passed to the formatters and not by
 486   
      * shown by default.  This option should for example be set for
 487   
      * tests that are interactive and prompt the user to do
 488   
      * something.</p>
 489   
      *
 490   
      * @since Ant 1.5
 491   
      */
 492  0
     public void setShowOutput(boolean showOutput) {
 493  0
         this.showOutput = showOutput;
 494   
     }
 495   
 
 496   
     /**
 497   
      * Creates a new JUnitRunner and enables fork of a new Java VM.
 498   
      *
 499   
      * @since Ant 1.2
 500   
      */
 501  1
     public JUnitTask() throws Exception {
 502  1
         commandline
 503   
             .setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
 504   
     }
 505   
 
 506   
     /**
 507   
      * Where Ant should place temporary files.
 508   
      *
 509   
      * @since Ant 1.6
 510   
      */
 511  0
     public void setTempdir(File tmpDir) {
 512  0
         this.tmpDir = tmpDir;
 513   
     }
 514   
 
 515   
     /**
 516   
      * Adds the jars or directories containing Ant, this task and
 517   
      * JUnit to the classpath - this should make the forked JVM work
 518   
      * without having to specify them directly.
 519   
      *
 520   
      * @since Ant 1.4
 521   
      */
 522  1
     public void init() {
 523  1
         antRuntimeClasses = new Path(getProject());
 524  1
         addClasspathEntry("/junit/framework/TestCase.class");
 525  1
         addClasspathEntry("/org/apache/tools/ant/Task.class");
 526  1
         addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
 527   
     }
 528   
 
 529   
     /**
 530   
      * Runs the testcase.
 531   
      *
 532   
      * @since Ant 1.2
 533   
      */
 534  1
     public void execute() throws BuildException {
 535  1
         Enumeration list = getIndividualTests();
 536  1
         while (list.hasMoreElements()) {
 537  136
             JUnitTest test = (JUnitTest) list.nextElement();
 538  136
             if (test.shouldRun(getProject())) {
 539  136
                 execute(test);
 540   
             }
 541   
         }
 542   
     }
 543   
 
 544   
     /**
 545   
      * Run the tests.
 546   
      */
 547  136
     protected void execute(JUnitTest arg) throws BuildException {
 548  136
         JUnitTest test = (JUnitTest) arg.clone();
 549   
         // set the default values if not specified
 550   
         //@todo should be moved to the test class instead.
 551  136
         if (test.getTodir() == null) {
 552  136
             test.setTodir(getProject().resolveFile("."));
 553   
         }
 554   
 
 555  136
         if (test.getOutfile() == null) {
 556  136
             test.setOutfile("TEST-" + test.getName());
 557   
         }
 558   
 
 559   
         // execute the test and get the return code
 560  136
         int exitValue = JUnitTestRunner.ERRORS;
 561  136
         boolean wasKilled = false;
 562  136
         if (!test.getFork()) {
 563  136
             exitValue = executeInVM(test);
 564   
         } else {
 565  0
             ExecuteWatchdog watchdog = createWatchdog();
 566  0
             exitValue = executeAsForked(test, watchdog);
 567   
             // null watchdog means no timeout, you'd better not check with null
 568  0
             if (watchdog != null) {
 569  0
                 wasKilled = watchdog.killedProcess();
 570   
             }
 571   
         }
 572   
 
 573   
         // if there is an error/failure and that it should halt, stop
 574   
         // everything otherwise just log a statement
 575  136
         boolean errorOccurredHere = exitValue == JUnitTestRunner.ERRORS;
 576  136
         boolean failureOccurredHere = exitValue != JUnitTestRunner.SUCCESS;
 577  136
         if (errorOccurredHere || failureOccurredHere) {
 578  2
             if ((errorOccurredHere && test.getHaltonerror())
 579   
                 || (failureOccurredHere && test.getHaltonfailure())) {
 580  0
                 throw new BuildException("Test " + test.getName() + " failed"
 581   
                     + (wasKilled ? " (timeout)" : ""), getLocation());
 582   
             } else {
 583  2
                 log("TEST " + test.getName() + " FAILED"
 584   
                     + (wasKilled ? " (timeout)" : ""), Project.MSG_ERR);
 585  2
                 if (errorOccurredHere && test.getErrorProperty() != null) {
 586  0
                     getProject().setNewProperty(test.getErrorProperty(), "true");
 587   
                 }
 588  2
                 if (failureOccurredHere && test.getFailureProperty() != null) {
 589  2
                     getProject().setNewProperty(test.getFailureProperty(), "true");
 590   
                 }
 591   
             }
 592   
         }
 593   
     }
 594   
 
 595   
     /**
 596   
      * Execute a testcase by forking a new JVM. The command will block until
 597   
      * it finishes. To know if the process was destroyed or not, use the
 598   
      * <tt>killedProcess()</tt> method of the watchdog class.
 599   
      * @param  test       the testcase to execute.
 600   
      * @param  watchdog   the watchdog in charge of cancelling the test if it
 601   
      * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
 602   
      * the test could probably hang forever.
 603   
      */
 604  0
     private int executeAsForked(JUnitTest test, ExecuteWatchdog watchdog)
 605   
         throws BuildException {
 606   
 
 607  0
         CommandlineJava cmd = (CommandlineJava) commandline.clone();
 608   
 
 609  0
         cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
 610  0
         cmd.createArgument().setValue(test.getName());
 611  0
         cmd.createArgument().setValue("filtertrace=" + test.getFiltertrace());
 612  0
         cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror());
 613  0
         cmd.createArgument().setValue("haltOnFailure="
 614   
                                       + test.getHaltonfailure());
 615  0
         if (includeAntRuntime) {
 616  0
             log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH",
 617   
                 Project.MSG_VERBOSE);
 618  0
             cmd.createClasspath(getProject()).createPath()
 619   
                 .append(antRuntimeClasses);
 620   
         }
 621   
 
 622  0
         if (summary) {
 623  0
             log("Running " + test.getName(), Project.MSG_INFO);
 624  0
             cmd.createArgument()
 625   
                 .setValue("formatter=org.apache.tools.ant.taskdefs.optional.junit.SummaryJUnitResultFormatter");
 626   
         }
 627   
 
 628  0
         cmd.createArgument().setValue("showoutput="
 629   
                                       + String.valueOf(showOutput));
 630   
 
 631  0
         StringBuffer formatterArg = new StringBuffer(128);
 632  0
         final FormatterElement[] feArray = mergeFormatters(test);
 633  0
         for (int i = 0; i < feArray.length; i++) {
 634  0
             FormatterElement fe = feArray[i];
 635  0
             formatterArg.append("formatter=");
 636  0
             formatterArg.append(fe.getClassname());
 637  0
             File outFile = getOutput(fe, test);
 638  0
             if (outFile != null) {
 639  0
                 formatterArg.append(",");
 640  0
                 formatterArg.append(outFile);
 641   
             }
 642  0
             cmd.createArgument().setValue(formatterArg.toString());
 643  0
             formatterArg = new StringBuffer();
 644   
         }
 645   
 
 646   
         // Create a temporary file to pass the Ant properties to the
 647   
         // forked test
 648  0
         File propsFile =
 649   
             FileUtils.newFileUtils().createTempFile("junit", ".properties",
 650   
                                                     tmpDir != null ? tmpDir :
 651   
                                                     getProject().getBaseDir());
 652  0
         cmd.createArgument().setValue("propsfile="
 653   
                                       + propsFile.getAbsolutePath());
 654  0
         Hashtable p = getProject().getProperties();
 655  0
         Properties props = new Properties();
 656  0
         for (Enumeration enum = p.keys(); enum.hasMoreElements();) {
 657  0
             Object key = enum.nextElement();
 658  0
             props.put(key, p.get(key));
 659   
         }
 660  0
         try {
 661  0
             FileOutputStream outstream = new FileOutputStream(propsFile);
 662  0
             props.save(outstream, "Ant JUnitTask generated properties file");
 663  0
             outstream.close();
 664   
         } catch (java.io.IOException e) {
 665  0
             propsFile.delete();
 666  0
             throw new BuildException("Error creating temporary properties "
 667   
                                      + "file.", e, getLocation());
 668   
         }
 669   
 
 670  0
         Execute execute = new Execute(new LogStreamHandler(this,
 671   
                                                            Project.MSG_INFO,
 672   
                                                            Project.MSG_WARN),
 673   
                                       watchdog);
 674  0
         execute.setCommandline(cmd.getCommandline());
 675  0
         execute.setAntRun(getProject());
 676  0
         if (dir != null) {
 677  0
             execute.setWorkingDirectory(dir);
 678   
         }
 679   
 
 680  0
         String[] environment = env.getVariables();
 681  0
         if (environment != null) {
 682  0
             for (int i = 0; i < environment.length; i++) {
 683  0
                 log("Setting environment variable: " + environment[i],
 684   
                     Project.MSG_VERBOSE);
 685   
             }
 686   
         }
 687  0
         execute.setNewenvironment(newEnvironment);
 688  0
         execute.setEnvironment(environment);
 689   
 
 690  0
         log(cmd.describeCommand(), Project.MSG_VERBOSE);
 691  0
         int retVal;
 692  0
         try {
 693  0
             retVal = execute.execute();
 694   
         } catch (IOException e) {
 695  0
             throw new BuildException("Process fork failed.", e, getLocation());
 696   
         } finally {
 697  0
             if (watchdog != null && watchdog.killedProcess()) {
 698  0
                 logTimeout(feArray, test);
 699   
             }
 700   
 
 701  0
             if (!propsFile.delete()) {
 702  0
                 throw new BuildException("Could not delete temporary "
 703   
                                          + "properties file.");
 704   
             }
 705   
         }
 706   
 
 707  0
         return retVal;
 708   
     }
 709   
 
 710   
 
 711   
     /**
 712   
      * Pass output sent to System.out to the TestRunner so it can
 713   
      * collect ot for the formatters.
 714   
      *
 715   
      * @since Ant 1.5
 716   
      */
 717  191
     protected void handleOutput(String line) {
 718  191
         if (runner != null) {
 719  191
             runner.handleOutput(line);
 720  191
             if (showOutput) {
 721  0
                 super.handleOutput(line);
 722   
             }
 723   
         } else {
 724  0
             super.handleOutput(line);
 725   
         }
 726   
     }
 727   
 
 728   
     /**
 729   
      * @see Task#handleInput(byte[], int, int)
 730   
      * 
 731   
      * @since Ant 1.6
 732   
      */
 733  0
     protected int handleInput(byte[] buffer, int offset, int length) 
 734   
         throws IOException {
 735  0
         if (runner != null) {
 736  0
             return runner.handleInput(buffer, offset, length);
 737   
         } else {
 738  0
             return super.handleInput(buffer, offset, length);
 739   
         }
 740   
     }
 741   
     
 742   
     
 743   
     /**
 744   
      * Pass output sent to System.out to the TestRunner so it can
 745   
      * collect ot for the formatters.
 746   
      *
 747   
      * @since Ant 1.5.2
 748   
      */
 749  0
     protected void handleFlush(String line) {
 750  0
         if (runner != null) {
 751  0
             runner.handleFlush(line);
 752  0
             if (showOutput) {
 753  0
                 super.handleFlush(line);
 754   
             }
 755   
         } else {
 756  0
             super.handleFlush(line);
 757   
         }
 758   
     }
 759   
 
 760   
     /**
 761   
      * Pass output sent to System.err to the TestRunner so it can
 762   
      * collect ot for the formatters.
 763   
      *
 764   
      * @since Ant 1.5
 765   
      */
 766  28
     public void handleErrorOutput(String line) {
 767  28
         if (runner != null) {
 768  28
             runner.handleErrorOutput(line);
 769  28
             if (showOutput) {
 770  0
                 super.handleErrorOutput(line);
 771   
             }
 772   
         } else {
 773  0
             super.handleErrorOutput(line);
 774   
         }
 775   
     }
 776   
 
 777   
 
 778   
     /**
 779   
      * Pass output sent to System.err to the TestRunner so it can
 780   
      * collect ot for the formatters.
 781   
      *
 782   
      * @since Ant 1.5.2
 783   
      */
 784  0
     public void handleErrorFlush(String line) {
 785  0
         if (runner != null) {
 786  0
             runner.handleErrorFlush(line);
 787  0
             if (showOutput) {
 788  0
                 super.handleErrorFlush(line);
 789   
             }
 790   
         } else {
 791  0
             super.handleErrorFlush(line);
 792   
         }
 793   
     }
 794   
 
 795   
     // in VM is not very nice since it could probably hang the
 796   
     // whole build. IMHO this method should be avoided and it would be best
 797   
     // to remove it in future versions. TBD. (SBa)
 798   
 
 799   
     /**
 800   
      * Execute inside VM.
 801   
      */
 802  136
     private int executeInVM(JUnitTest arg) throws BuildException {
 803  136
         JUnitTest test = (JUnitTest) arg.clone();
 804  136
         test.setProperties(getProject().getProperties());
 805  136
         if (dir != null) {
 806  0
             log("dir attribute ignored if running in the same VM",
 807   
                 Project.MSG_WARN);
 808   
         }
 809   
 
 810  136
         if (newEnvironment || null != env.getVariables()) {
 811  0
             log("Changes to environment variables are ignored if running in "
 812   
                 + "the same VM.", Project.MSG_WARN);
 813   
         }
 814   
 
 815  136
         CommandlineJava.SysProperties sysProperties =
 816   
             commandline.getSystemProperties();
 817  136
         if (sysProperties != null) {
 818  136
             sysProperties.setSystem();
 819   
         }
 820  136
         AntClassLoader cl = null;
 821  136
         try {
 822  136
             log("Using System properties " + System.getProperties(),
 823   
                 Project.MSG_VERBOSE);
 824  136
             Path userClasspath = commandline.getClasspath();
 825  136
             Path classpath = userClasspath == null
 826   
                                               ? null
 827   
                                               : (Path) userClasspath.clone();
 828  136
             if (classpath != null) {
 829  136
                 if (includeAntRuntime) {
 830  136
                     log("Implicitly adding " + antRuntimeClasses
 831   
                         + " to CLASSPATH", Project.MSG_VERBOSE);
 832  136
                     classpath.append(antRuntimeClasses);
 833   
                 }
 834   
 
 835  136
                 cl = getProject().createClassLoader(classpath);
 836  136
                 cl.setParentFirst(false);
 837  136
                 cl.addJavaLibraries();
 838  136
                 log("Using CLASSPATH " + cl.getClasspath(),
 839   
                     Project.MSG_VERBOSE);
 840   
 
 841   
                 // make sure the test will be accepted as a TestCase
 842  136
                 cl.addSystemPackageRoot("junit");
 843   
                 // will cause trouble in JDK 1.1 if omitted
 844  136
                 cl.addSystemPackageRoot("org.apache.tools.ant");
 845  136
                 cl.setThreadContextLoader();
 846   
             }
 847  136
             runner = new JUnitTestRunner(test, test.getHaltonerror(),
 848   
                                          test.getFiltertrace(),
 849   
                                          test.getHaltonfailure(), cl);
 850  136
             if (summary) {
 851  0
                 log("Running " + test.getName(), Project.MSG_INFO);
 852   
 
 853  0
                 SummaryJUnitResultFormatter f =
 854   
                     new SummaryJUnitResultFormatter();
 855  0
                 f.setWithOutAndErr("withoutanderr"
 856   
                                    .equalsIgnoreCase(summaryValue));
 857  0
                 f.setOutput(getDefaultOutput());
 858  0
                 runner.addFormatter(f);
 859   
             }
 860   
 
 861  136
             final FormatterElement[] feArray = mergeFormatters(test);
 862  136
             for (int i = 0; i < feArray.length; i++) {
 863  136
                 FormatterElement fe = feArray[i];
 864  136
                 File outFile = getOutput(fe, test);
 865  136
                 if (outFile != null) {
 866  0
                     fe.setOutfile(outFile);
 867   
                 } else {
 868  136
                     fe.setOutput(getDefaultOutput());
 869   
                 }
 870  136
                 runner.addFormatter(fe.createFormatter());
 871   
             }
 872   
 
 873  136
             runner.run();
 874  136
             return runner.getRetCode();
 875   
         } finally{
 876  136
             if (sysProperties != null) {
 877  136
                 sysProperties.restoreSystem();
 878   
             }
 879  136
             if (cl != null) {
 880  136
                 cl.resetThreadContextLoader();
 881   
             }
 882   
         }
 883   
     }
 884   
 
 885   
     /**
 886   
      * @return <tt>null</tt> if there is a timeout value, otherwise the
 887   
      * watchdog instance.
 888   
      *
 889   
      * @since Ant 1.2
 890   
      */
 891  0
     protected ExecuteWatchdog createWatchdog() throws BuildException {
 892  0
         if (timeout == null){
 893  0
             return null;
 894   
         }
 895  0
         return new ExecuteWatchdog(timeout.intValue());
 896   
     }
 897   
 
 898   
     /**
 899   
      * Get the default output for a formatter.
 900   
      *
 901   
      * @since Ant 1.3
 902   
      */
 903  136
     protected OutputStream getDefaultOutput(){
 904  136
         return new LogOutputStream(this, Project.MSG_INFO);
 905   
     }
 906   
 
 907   
     /**
 908   
      * Merge all individual tests from the batchtest with all individual tests
 909   
      * and return an enumeration over all <tt>JUnitTest</tt>.
 910   
      *
 911   
      * @since Ant 1.3
 912   
      */
 913  1
     protected Enumeration getIndividualTests(){
 914  1
         final int count = batchTests.size();
 915  1
         final Enumeration[] enums = new Enumeration[ count + 1];
 916  1
         for (int i = 0; i < count; i++) {
 917  1
             BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
 918  1
             enums[i] = batchtest.elements();
 919   
         }
 920  1
         enums[enums.length - 1] = tests.elements();
 921  1
         return Enumerations.fromCompound(enums);
 922   
     }
 923   
 
 924   
     /**
 925   
      * @since Ant 1.3
 926   
      */
 927  4
     protected Enumeration allTests() {
 928  4
         Enumeration[] enums = { tests.elements(), batchTests.elements() };
 929  4
         return Enumerations.fromCompound(enums);
 930   
     }
 931   
 
 932   
     /**
 933   
      * @since Ant 1.3
 934   
      */
 935  136
     private FormatterElement[] mergeFormatters(JUnitTest test){
 936  136
         Vector feVector = (Vector) formatters.clone();
 937  136
         test.addFormattersTo(feVector);
 938  136
         FormatterElement[] feArray = new FormatterElement[feVector.size()];
 939  136
         feVector.copyInto(feArray);
 940  136
         return feArray;
 941   
     }
 942   
 
 943   
     /**
 944   
      * If the formatter sends output to a file, return that file.
 945   
      * null otherwise.
 946   
      *
 947   
      * @since Ant 1.3
 948   
      */
 949  136
     protected File getOutput(FormatterElement fe, JUnitTest test){
 950  136
         if (fe.getUseFile()) {
 951  0
             String filename = test.getOutfile() + fe.getExtension();
 952  0
             File destFile = new File(test.getTodir(), filename);
 953  0
             String absFilename = destFile.getAbsolutePath();
 954  0
             return getProject().resolveFile(absFilename);
 955   
         }
 956  136
         return null;
 957   
     }
 958   
 
 959   
     /**
 960   
      * Search for the given resource and add the directory or archive
 961   
      * that contains it to the classpath.
 962   
      *
 963   
      * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
 964   
      * getResource doesn't contain the name of the archive.</p>
 965   
      *
 966   
      * @since Ant 1.4
 967   
      */
 968  3
     protected void addClasspathEntry(String resource) {
 969   
         /* 
 970   
          * pre Ant 1.6 this method used to call getClass().getResource
 971   
          * while Ant 1.6 will call ClassLoader.getResource().
 972   
          *
 973   
          * The difference is that Class.getResource expects a leading
 974   
          * slash for "absolute" resources and will strip it before
 975   
          * delegating to ClassLoader.getResource - so we now have to
 976   
          * emulate Class's behavior.
 977   
          */
 978  3
         if (resource.startsWith("/")) {
 979  3
             resource = resource.substring(1);
 980   
         } else {
 981  0
             resource = "org/apache/tools/ant/taskdefs/optional/junit/"
 982   
                 + resource;
 983   
         }
 984   
         
 985  3
         File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
 986   
                                                resource);
 987  3
         if (f != null) {
 988  3
             log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
 989  3
             antRuntimeClasses.createPath().setLocation(f);
 990   
         } else {
 991  0
             log("Couldn\'t find " + resource, Project.MSG_DEBUG);
 992   
         }
 993   
     }
 994   
 
 995   
     /**
 996   
      * Take care that some output is produced in report files if the
 997   
      * watchdog kills the test.
 998   
      *
 999   
      * @since Ant 1.5.2
 1000   
      */
 1001   
 
 1002  0
     private void logTimeout(FormatterElement[] feArray, JUnitTest test) {
 1003  0
         for (int i = 0; i < feArray.length; i++) {
 1004  0
             FormatterElement fe = feArray[i];
 1005  0
             File outFile = getOutput(fe, test);
 1006  0
             JUnitResultFormatter formatter = fe.createFormatter();
 1007  0
             if (outFile != null && formatter != null) {
 1008  0
                 try {
 1009  0
                     OutputStream out = new FileOutputStream(outFile);
 1010  0
                     formatter.setOutput(out);
 1011  0
                     formatter.startTestSuite(test);
 1012  0
                     test.setCounts(0,0,1);
 1013  0
                     Test t = new Test() {
 1014  0
                         public int countTestCases() { return 0; }
 1015  0
                         public void run(TestResult r) {
 1016  0
                             throw new AssertionFailedError("Timeout occurred");
 1017   
                         }
 1018   
                     };
 1019  0
                     formatter.startTest(t);
 1020  0
                     formatter
 1021   
                         .addError(t,
 1022   
                                   new AssertionFailedError("Timeout occurred"));
 1023   
 
 1024  0
                     formatter.endTestSuite(test);
 1025   
                 } catch (IOException e) {}
 1026   
             }
 1027   
         }
 1028   
     }
 1029   
 
 1030   
 }
 1031