KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > junit > JUnitTask


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant.taskdefs.optional.junit;
20
21 import java.io.BufferedReader JavaDoc;
22 import java.io.BufferedWriter JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.FileReader JavaDoc;
26 import java.io.FileWriter JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.io.PrintWriter JavaDoc;
30 import java.lang.reflect.Constructor JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Collection JavaDoc;
33 import java.util.Enumeration JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Hashtable JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Properties JavaDoc;
40 import java.util.Vector JavaDoc;
41
42 import org.apache.tools.ant.AntClassLoader;
43 import org.apache.tools.ant.BuildException;
44 import org.apache.tools.ant.Project;
45 import org.apache.tools.ant.Task;
46 import org.apache.tools.ant.taskdefs.Execute;
47 import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
48 import org.apache.tools.ant.taskdefs.LogOutputStream;
49 import org.apache.tools.ant.taskdefs.PumpStreamHandler;
50 import org.apache.tools.ant.types.Assertions;
51 import org.apache.tools.ant.types.Commandline;
52 import org.apache.tools.ant.types.CommandlineJava;
53 import org.apache.tools.ant.types.EnumeratedAttribute;
54 import org.apache.tools.ant.types.Environment;
55 import org.apache.tools.ant.types.Path;
56 import org.apache.tools.ant.types.Permissions;
57 import org.apache.tools.ant.types.PropertySet;
58 import org.apache.tools.ant.util.FileUtils;
59 import org.apache.tools.ant.util.LoaderUtils;
60
61 /**
62  * Runs JUnit tests.
63  *
64  * <p> JUnit is a framework to create unit tests. It has been initially
65  * created by Erich Gamma and Kent Beck. JUnit can be found at <a
66  * HREF="http://www.junit.org">http://www.junit.org</a>.
67  *
68  * <p> <code>JUnitTask</code> can run a single specific
69  * <code>JUnitTest</code> using the <code>test</code> element.</p>
70  * For example, the following target <code><pre>
71  * &lt;target name="test-int-chars" depends="jar-test"&gt;
72  * &lt;echo message="testing international characters"/&gt;
73  * &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
74  * &lt;classpath refid="classpath"/&gt;
75  * &lt;formatter type="plain" usefile="false" /&gt;
76  * &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
77  * &lt;/junit&gt;
78  * &lt;/target&gt;
79  * </pre></code>
80  * <p>runs a single junit test
81  * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
82  * VM using the path with id <code>classpath</code> as classpath and
83  * presents the results formatted using the standard
84  * <code>plain</code> formatter on the command line.</p>
85  *
86  * <p> This task can also run batches of tests. The
87  * <code>batchtest</code> element creates a <code>BatchTest</code>
88  * based on a fileset. This allows, for example, all classes found in
89  * directory to be run as testcases.</p>
90  *
91  * <p>For example,</p><code><pre>
92  * &lt;target name="run-tests" depends="dump-info,compile-tests" if="junit.present"&gt;
93  * &lt;junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"&gt;
94  * &lt;jvmarg value="-classic"/&gt;
95  * &lt;classpath refid="tests-classpath"/&gt;
96  * &lt;sysproperty key="build.tests" value="${build.tests}"/&gt;
97  * &lt;formatter type="brief" usefile="false" /&gt;
98  * &lt;batchtest&gt;
99  * &lt;fileset dir="${tests.dir}"&gt;
100  * &lt;include name="**&#047;*Test*" /&gt;
101  * &lt;/fileset&gt;
102  * &lt;/batchtest&gt;
103  * &lt;/junit&gt;
104  * &lt;/target&gt;
105  * </pre></code>
106  * <p>this target finds any classes with a <code>test</code> directory
107  * anywhere in their path (under the top <code>${tests.dir}</code>, of
108  * course) and creates <code>JUnitTest</code>'s for each one.</p>
109  *
110  * <p> Of course, <code>&lt;junit&gt;</code> and
111  * <code>&lt;batch&gt;</code> elements can be combined for more
112  * complex tests. For an example, see the ant <code>build.xml</code>
113  * target <code>run-tests</code> (the second example is an edited
114  * version).</p>
115  *
116  * <p> To spawn a new Java VM to prevent interferences between
117  * different testcases, you need to enable <code>fork</code>. A
118  * number of attributes and elements allow you to set up how this JVM
119  * runs.
120  *
121  *
122  * @since Ant 1.2
123  *
124  * @see JUnitTest
125  * @see BatchTest
126  */

127 public class JUnitTask extends Task {
128
129     private static final String JavaDoc CLASSPATH = "CLASSPATH=";
130     private CommandlineJava commandline;
131     private Vector JavaDoc tests = new Vector JavaDoc();
132     private Vector JavaDoc batchTests = new Vector JavaDoc();
133     private Vector JavaDoc formatters = new Vector JavaDoc();
134     private File JavaDoc dir = null;
135
136     private Integer JavaDoc timeout = null;
137     private boolean summary = false;
138     private boolean reloading = true;
139     private String JavaDoc summaryValue = "";
140     private JUnitTaskMirror.JUnitTestRunnerMirror runner = null;
141
142     private boolean newEnvironment = false;
143     private Environment env = new Environment();
144
145     private boolean includeAntRuntime = true;
146     private Path antRuntimeClasses = null;
147
148     // Do we send output to System.out/.err in addition to the formatters?
149
private boolean showOutput = false;
150
151     // Do we send output to the formatters ?
152
private boolean outputToFormatters = true;
153
154     private File JavaDoc tmpDir;
155     private AntClassLoader classLoader = null;
156     private Permissions perm = null;
157     private ForkMode forkMode = new ForkMode("perTest");
158
159     private boolean splitJunit = false;
160     private JUnitTaskMirror delegate;
161
162     // Attributes for basetest
163
private boolean haltOnError = false;
164     private boolean haltOnFail = false;
165     private boolean filterTrace = true;
166     private boolean fork = false;
167     private String JavaDoc failureProperty;
168     private String JavaDoc errorProperty;
169
170     private static final int STRING_BUFFER_SIZE = 128;
171     /**
172      * @since Ant 1.7
173      */

174     public static final String JavaDoc TESTLISTENER_PREFIX =
175         "junit.framework.TestListener: ";
176
177     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
178
179     /**
180      * If true, force ant to re-classload all classes for each JUnit TestCase
181      *
182      * @param value force class reloading for each test case
183      */

184     public void setReloading(boolean value) {
185         reloading = value;
186     }
187
188     /**
189      * If true, smartly filter the stack frames of
190      * JUnit errors and failures before reporting them.
191      *
192      * <p>This property is applied on all BatchTest (batchtest) and
193      * JUnitTest (test) however it can possibly be overridden by their
194      * own properties.</p>
195      * @param value <tt>false</tt> if it should not filter, otherwise
196      * <tt>true<tt>
197      *
198      * @since Ant 1.5
199      */

200     public void setFiltertrace(boolean value) {
201         this.filterTrace = value;
202     }
203
204     /**
205      * If true, stop the build process when there is an error in a test.
206      * This property is applied on all BatchTest (batchtest) and JUnitTest
207      * (test) however it can possibly be overridden by their own
208      * properties.
209      * @param value <tt>true</tt> if it should halt, otherwise
210      * <tt>false</tt>
211      *
212      * @since Ant 1.2
213      */

214     public void setHaltonerror(boolean value) {
215         this.haltOnError = value;
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     public void setErrorProperty(String JavaDoc propertyName) {
230         this.errorProperty = propertyName;
231     }
232
233     /**
234      * If true, stop the build process if a test fails
235      * (errors are considered failures as well).
236      * This property is applied on all BatchTest (batchtest) and
237      * JUnitTest (test) however it can possibly be overridden by their
238      * own properties.
239      * @param value <tt>true</tt> if it should halt, otherwise
240      * <tt>false</tt>
241      *
242      * @since Ant 1.2
243      */

244     public void setHaltonfailure(boolean value) {
245         this.haltOnFail = value;
246     }
247
248     /**
249      * Property to set to "true" if there is a failure in a test.
250      *
251      * <p>This property is applied on all BatchTest (batchtest) and
252      * JUnitTest (test), however, it can possibly be overriden by
253      * their own properties.</p>
254      * @param propertyName the name of the property to set in the
255      * event of an failure.
256      *
257      * @since Ant 1.4
258      */

259     public void setFailureProperty(String JavaDoc propertyName) {
260         this.failureProperty = propertyName;
261     }
262
263     /**
264      * If true, JVM should be forked for each test.
265      *
266      * <p>It avoids interference between testcases and possibly avoids
267      * hanging the build. this property is applied on all BatchTest
268      * (batchtest) and JUnitTest (test) however it can possibly be
269      * overridden by their own properties.</p>
270      * @param value <tt>true</tt> if a JVM should be forked, otherwise
271      * <tt>false</tt>
272      * @see #setTimeout
273      *
274      * @since Ant 1.2
275      */

276     public void setFork(boolean value) {
277         this.fork = value;
278     }
279
280     /**
281      * Set the behavior when {@link #setFork fork} fork has been enabled.
282      *
283      * <p>Possible values are "once", "perTest" and "perBatch". If
284      * set to "once", only a single Java VM will be forked for all
285      * tests, with "perTest" (the default) each test will run in a
286      * fresh Java VM and "perBatch" will run all tests from the same
287      * &lt;batchtest&gt; in the same Java VM.</p>
288      *
289      * <p>This attribute will be ignored if tests run in the same VM
290      * as Ant.</p>
291      *
292      * <p>Only tests with the same configuration of haltonerror,
293      * haltonfailure, errorproperty, failureproperty and filtertrace
294      * can share a forked Java VM, so even if you set the value to
295      * "once", Ant may need to fork mutliple VMs.</p>
296      * @param mode the mode to use.
297      * @since Ant 1.6.2
298      */

299     public void setForkMode(ForkMode mode) {
300         this.forkMode = mode;
301     }
302
303     /**
304      * If true, print one-line statistics for each test, or "withOutAndErr"
305      * to also show standard output and error.
306      *
307      * Can take the values on, off, and withOutAndErr.
308      * @param value <tt>true</tt> to print a summary,
309      * <tt>withOutAndErr</tt> to include the test&apos;s output as
310      * well, <tt>false</tt> otherwise.
311      * @see SummaryJUnitResultFormatter
312      *
313      * @since Ant 1.2
314      */

315     public void setPrintsummary(SummaryAttribute value) {
316         summaryValue = value.getValue();
317         summary = value.asBoolean();
318     }
319
320     /**
321      * Print summary enumeration values.
322      */

323     public static class SummaryAttribute extends EnumeratedAttribute {
324         /**
325          * list the possible values
326          * @return array of allowed values
327          */

328         public String JavaDoc[] getValues() {
329             return new String JavaDoc[] {"true", "yes", "false", "no",
330                                  "on", "off", "withOutAndErr"};
331         }
332
333         /**
334          * gives the boolean equivalent of the authorized values
335          * @return boolean equivalent of the value
336          */

337         public boolean asBoolean() {
338             String JavaDoc v = getValue();
339             return "true".equals(v)
340                 || "on".equals(v)
341                 || "yes".equals(v)
342                 || "withOutAndErr".equals(v);
343         }
344     }
345
346     /**
347      * Set the timeout value (in milliseconds).
348      *
349      * <p>If the test is running for more than this value, the test
350      * will be canceled. (works only when in 'fork' mode).</p>
351      * @param value the maximum time (in milliseconds) allowed before
352      * declaring the test as 'timed-out'
353      * @see #setFork(boolean)
354      *
355      * @since Ant 1.2
356      */

357     public void setTimeout(Integer JavaDoc value) {
358         timeout = value;
359     }
360
361     /**
362      * Set the maximum memory to be used by all forked JVMs.
363      * @param max the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
364      * in the java command line options.
365      *
366      * @since Ant 1.2
367      */

368     public void setMaxmemory(String JavaDoc max) {
369         getCommandline().setMaxmemory(max);
370     }
371
372     /**
373      * The command used to invoke the Java Virtual Machine,
374      * default is 'java'. The command is resolved by
375      * java.lang.Runtime.exec(). Ignored if fork is disabled.
376      *
377      * @param value the new VM to use instead of <tt>java</tt>
378      * @see #setFork(boolean)
379      *
380      * @since Ant 1.2
381      */

382     public void setJvm(String JavaDoc value) {
383         getCommandline().setVm(value);
384     }
385
386     /**
387      * Adds a JVM argument; ignored if not forking.
388      *
389      * @return create a new JVM argument so that any argument can be
390      * passed to the JVM.
391      * @see #setFork(boolean)
392      *
393      * @since Ant 1.2
394      */

395     public Commandline.Argument createJvmarg() {
396         return getCommandline().createVmArgument();
397     }
398
399     /**
400      * The directory to invoke the VM in. Ignored if no JVM is forked.
401      * @param dir the directory to invoke the JVM from.
402      * @see #setFork(boolean)
403      *
404      * @since Ant 1.2
405      */

406     public void setDir(File JavaDoc dir) {
407         this.dir = dir;
408     }
409
410     /**
411      * Adds a system property that tests can access.
412      * This might be useful to tranfer Ant properties to the
413      * testcases when JVM forking is not enabled.
414      *
415      * @since Ant 1.3
416      * @deprecated since ant 1.6
417      * @param sysp environment variable to add
418      */

419     public void addSysproperty(Environment.Variable sysp) {
420
421         getCommandline().addSysproperty(sysp);
422     }
423
424     /**
425      * Adds a system property that tests can access.
426      * This might be useful to tranfer Ant properties to the
427      * testcases when JVM forking is not enabled.
428      * @param sysp new environment variable to add
429      * @since Ant 1.6
430      */

431     public void addConfiguredSysproperty(Environment.Variable sysp) {
432         // get a build exception if there is a missing key or value
433
// see bugzilla report 21684
434
String JavaDoc testString = sysp.getContent();
435         getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG);
436         getCommandline().addSysproperty(sysp);
437     }
438
439     /**
440      * Adds a set of properties that will be used as system properties
441      * that tests can access.
442      *
443      * This might be useful to tranfer Ant properties to the
444      * testcases when JVM forking is not enabled.
445      *
446      * @param sysp set of properties to be added
447      * @since Ant 1.6
448      */

449     public void addSyspropertyset(PropertySet sysp) {
450         getCommandline().addSyspropertyset(sysp);
451     }
452
453     /**
454      * Adds path to classpath used for tests.
455      *
456      * @return reference to the classpath in the embedded java command line
457      * @since Ant 1.2
458      */

459     public Path createClasspath() {
460         return getCommandline().createClasspath(getProject()).createPath();
461     }
462
463     /**
464      * Adds a path to the bootclasspath.
465      * @return reference to the bootclasspath in the embedded java command line
466      * @since Ant 1.6
467      */

468     public Path createBootclasspath() {
469         return getCommandline().createBootclasspath(getProject()).createPath();
470     }
471
472     /**
473      * Adds an environment variable; used when forking.
474      *
475      * <p>Will be ignored if we are not forking a new VM.</p>
476      * @param var environment variable to be added
477      * @since Ant 1.5
478      */

479     public void addEnv(Environment.Variable var) {
480         env.addVariable(var);
481     }
482
483     /**
484      * If true, use a new environment when forked.
485      *
486      * <p>Will be ignored if we are not forking a new VM.</p>
487      *
488      * @param newenv boolean indicating if setting a new environment is wished
489      * @since Ant 1.5
490      */

491     public void setNewenvironment(boolean newenv) {
492         newEnvironment = newenv;
493     }
494
495     /**
496      * Preset the attributes of the test
497      * before configuration in the build
498      * script.
499      * This allows attributes in the <junit> task
500      * be be defaults for the tests, but allows
501      * individual tests to override the defaults.
502      */

503     private void preConfigure(BaseTest test) {
504         test.setFiltertrace(filterTrace);
505         test.setHaltonerror(haltOnError);
506         if (errorProperty != null) {
507             test.setErrorProperty(errorProperty);
508         }
509         test.setHaltonfailure(haltOnFail);
510         if (failureProperty != null) {
511             test.setFailureProperty(failureProperty);
512         }
513         test.setFork(fork);
514     }
515
516     /**
517      * Add a new single testcase.
518      * @param test a new single testcase
519      * @see JUnitTest
520      *
521      * @since Ant 1.2
522      */

523     public void addTest(JUnitTest test) {
524         tests.addElement(test);
525         preConfigure(test);
526     }
527
528     /**
529      * Adds a set of tests based on pattern matching.
530      *
531      * @return a new instance of a batch test.
532      * @see BatchTest
533      *
534      * @since Ant 1.2
535      */

536     public BatchTest createBatchTest() {
537         BatchTest test = new BatchTest(getProject());
538         batchTests.addElement(test);
539         preConfigure(test);
540         return test;
541     }
542
543     /**
544      * Add a new formatter to all tests of this task.
545      *
546      * @param fe formatter element
547      * @since Ant 1.2
548      */

549     public void addFormatter(FormatterElement fe) {
550         formatters.addElement(fe);
551     }
552
553     /**
554      * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
555      *
556      * @param b include ant run time yes or no
557      * @since Ant 1.5
558      */

559     public void setIncludeantruntime(boolean b) {
560         includeAntRuntime = b;
561     }
562
563     /**
564      * If true, send any output generated by tests to Ant's logging system
565      * as well as to the formatters.
566      * By default only the formatters receive the output.
567      *
568      * <p>Output will always be passed to the formatters and not by
569      * shown by default. This option should for example be set for
570      * tests that are interactive and prompt the user to do
571      * something.</p>
572      *
573      * @param showOutput if true, send output to Ant's logging system too
574      * @since Ant 1.5
575      */

576     public void setShowOutput(boolean showOutput) {
577         this.showOutput = showOutput;
578     }
579
580     /**
581      * If true, send any output generated by tests to the formatters.
582      *
583      * @param outputToFormatters if true, send output to formatters (Default
584      * is true).
585      * @since Ant 1.7.0
586      */

587     public void setOutputToFormatters(boolean outputToFormatters) {
588         this.outputToFormatters = outputToFormatters;
589     }
590
591     /**
592      * Assertions to enable in this program (if fork=true)
593      * @since Ant 1.6
594      * @param asserts assertion set
595      */

596     public void addAssertions(Assertions asserts) {
597         if (getCommandline().getAssertions() != null) {
598             throw new BuildException("Only one assertion declaration is allowed");
599         }
600         getCommandline().setAssertions(asserts);
601     }
602
603     /**
604      * Sets the permissions for the application run inside the same JVM.
605      * @since Ant 1.6
606      * @return .
607      */

608     public Permissions createPermissions() {
609         if (perm == null) {
610             perm = new Permissions();
611         }
612         return perm;
613     }
614
615     /**
616      * If set, system properties will be copied to the cloned VM - as
617      * well as the bootclasspath unless you have explicitly specified
618      * a bootclaspath.
619      *
620      * <p>Doesn't have any effect unless fork is true.</p>
621      * @param cloneVm a <code>boolean</code> value.
622      * @since Ant 1.7
623      */

624     public void setCloneVm(boolean cloneVm) {
625         getCommandline().setCloneVm(cloneVm);
626     }
627
628     /**
629      * Creates a new JUnitRunner and enables fork of a new Java VM.
630      *
631      * @throws Exception under ??? circumstances
632      * @since Ant 1.2
633      */

634     public JUnitTask() throws Exception JavaDoc {
635         getCommandline()
636             .setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
637     }
638
639     /**
640      * Where Ant should place temporary files.
641      *
642      * @param tmpDir location where temporary files should go to
643      * @since Ant 1.6
644      */

645     public void setTempdir(File JavaDoc tmpDir) {
646         if (tmpDir != null) {
647             if (!tmpDir.exists() || !tmpDir.isDirectory()) {
648                 throw new BuildException(tmpDir.toString()
649                                          + " is not a valid temp directory");
650             }
651         }
652         this.tmpDir = tmpDir;
653     }
654
655     /**
656      * Adds the jars or directories containing Ant, this task and
657      * JUnit to the classpath - this should make the forked JVM work
658      * without having to specify them directly.
659      *
660      * @since Ant 1.4
661      */

662     public void init() {
663         antRuntimeClasses = new Path(getProject());
664         splitJunit = !addClasspathEntry("/junit/framework/TestCase.class");
665         addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class");
666         addClasspathEntry("/org/apache/tools/ant/Task.class");
667         addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
668     }
669
670     private static JUnitTaskMirror createMirror(JUnitTask task, ClassLoader JavaDoc loader) {
671         try {
672             loader.loadClass("junit.framework.Test"); // sanity check
673
} catch (ClassNotFoundException JavaDoc e) {
674             throw new BuildException(
675                     "The <classpath> for <junit> must include junit.jar "
676                     + "if not in Ant's own classpath",
677                     e, task.getLocation());
678         }
679         try {
680             Class JavaDoc c = loader.loadClass(JUnitTaskMirror.class.getName() + "Impl");
681             if (c.getClassLoader() != loader) {
682                 throw new BuildException("Overdelegating loader", task.getLocation());
683             }
684             Constructor JavaDoc cons = c.getConstructor(new Class JavaDoc[] {JUnitTask.class});
685             return (JUnitTaskMirror) cons.newInstance(new Object JavaDoc[] {task});
686         } catch (Exception JavaDoc e) {
687             throw new BuildException(e, task.getLocation());
688         }
689     }
690
691     private final class SplitLoader extends AntClassLoader {
692
693         public SplitLoader(ClassLoader JavaDoc parent, Path path) {
694             super(parent, getProject(), path, true);
695         }
696
697         // forceLoadClass is not convenient here since it would not
698
// properly deal with inner classes of these classes.
699
protected synchronized Class JavaDoc loadClass(String JavaDoc classname, boolean resolve)
700         throws ClassNotFoundException JavaDoc {
701             Class JavaDoc theClass = findLoadedClass(classname);
702             if (theClass != null) {
703                 return theClass;
704             }
705             if (isSplit(classname)) {
706                 theClass = findClass(classname);
707                 if (resolve) {
708                     resolveClass(theClass);
709                 }
710                 return theClass;
711             } else {
712                 return super.loadClass(classname, resolve);
713             }
714         }
715
716         private final String JavaDoc[] splitClasses = {
717             "BriefJUnitResultFormatter",
718             "JUnitResultFormatter",
719             "JUnitTaskMirrorImpl",
720             "JUnitTestRunner",
721             "JUnitVersionHelper",
722             "OutErrSummaryJUnitResultFormatter",
723             "PlainJUnitResultFormatter",
724             "SummaryJUnitResultFormatter",
725             "XMLJUnitResultFormatter",
726         };
727
728         private boolean isSplit(String JavaDoc classname) {
729             String JavaDoc simplename = classname.substring(classname.lastIndexOf('.') + 1);
730             for (int i = 0; i < splitClasses.length; i++) {
731                 if (simplename.equals(splitClasses[i])
732                         || simplename.startsWith(splitClasses[i] + '$')) {
733                     return true;
734                 }
735             }
736             return false;
737         }
738
739     }
740
741     /**
742      * Runs the testcase.
743      *
744      * @throws BuildException in case of test failures or errors
745      * @since Ant 1.2
746      */

747     public void execute() throws BuildException {
748         ClassLoader JavaDoc myLoader = JUnitTask.class.getClassLoader();
749         ClassLoader JavaDoc mirrorLoader;
750         if (splitJunit) {
751             Path path = new Path(getProject());
752             path.add(antRuntimeClasses);
753             path.add(getCommandline().getClasspath());
754             mirrorLoader = new SplitLoader(myLoader, path);
755         } else {
756             mirrorLoader = myLoader;
757         }
758         delegate = createMirror(this, mirrorLoader);
759
760         List JavaDoc testLists = new ArrayList JavaDoc();
761
762         boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST);
763         if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) {
764             testLists.addAll(executeOrQueue(getIndividualTests(),
765                                             forkPerTest));
766         } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */
767             final int count = batchTests.size();
768             for (int i = 0; i < count; i++) {
769                 BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
770                 testLists.addAll(executeOrQueue(batchtest.elements(), false));
771             }
772             testLists.addAll(executeOrQueue(tests.elements(), forkPerTest));
773         }
774
775         try {
776             Iterator JavaDoc iter = testLists.iterator();
777             while (iter.hasNext()) {
778                 List JavaDoc l = (List JavaDoc) iter.next();
779                 if (l.size() == 1) {
780                     execute((JUnitTest) l.get(0));
781                 } else {
782                     execute(l);
783                 }
784             }
785         } finally {
786             deleteClassLoader();
787             if (mirrorLoader instanceof SplitLoader) {
788                 ((SplitLoader) mirrorLoader).cleanup();
789             }
790             delegate = null;
791         }
792     }
793
794     /**
795      * Run the tests.
796      * @param arg one JunitTest
797      * @throws BuildException in case of test failures or errors
798      */

799     protected void execute(JUnitTest arg) throws BuildException {
800         JUnitTest test = (JUnitTest) arg.clone();
801         // set the default values if not specified
802
//@todo should be moved to the test class instead.
803
if (test.getTodir() == null) {
804             test.setTodir(getProject().resolveFile("."));
805         }
806
807         if (test.getOutfile() == null) {
808             test.setOutfile("TEST-" + test.getName());
809         }
810
811         // execute the test and get the return code
812
TestResultHolder result = null;
813         if (!test.getFork()) {
814             result = executeInVM(test);
815         } else {
816             ExecuteWatchdog watchdog = createWatchdog();
817             result = executeAsForked(test, watchdog, null);
818             // null watchdog means no timeout, you'd better not check with null
819
}
820         actOnTestResult(result, test, "Test " + test.getName());
821     }
822
823     /**
824      * Execute a list of tests in a single forked Java VM.
825      * @param tests the list of tests to execute.
826      * @throws BuildException on error.
827      */

828     protected void execute(List JavaDoc tests) throws BuildException {
829         JUnitTest test = null;
830         // Create a temporary file to pass the test cases to run to
831
// the runner (one test case per line)
832
File JavaDoc casesFile = createTempPropertiesFile("junittestcases");
833         PrintWriter JavaDoc writer = null;
834         try {
835             writer =
836                 new PrintWriter JavaDoc(new BufferedWriter JavaDoc(new FileWriter JavaDoc(casesFile)));
837             Iterator JavaDoc iter = tests.iterator();
838             while (iter.hasNext()) {
839                 test = (JUnitTest) iter.next();
840                 writer.print(test.getName());
841                 if (test.getTodir() == null) {
842                     writer.print("," + getProject().resolveFile("."));
843                 } else {
844                     writer.print("," + test.getTodir());
845                 }
846
847                 if (test.getOutfile() == null) {
848                     writer.println("," + "TEST-" + test.getName());
849                 } else {
850                     writer.println("," + test.getOutfile());
851                 }
852             }
853             writer.flush();
854             writer.close();
855             writer = null;
856
857             // execute the test and get the return code
858
ExecuteWatchdog watchdog = createWatchdog();
859             TestResultHolder result =
860                 executeAsForked(test, watchdog, casesFile);
861             actOnTestResult(result, test, "Tests");
862         } catch (IOException JavaDoc e) {
863             log(e.toString(), Project.MSG_ERR);
864             throw new BuildException(e);
865         } finally {
866             if (writer != null) {
867                 writer.close();
868             }
869
870             try {
871                 casesFile.delete();
872             } catch (Exception JavaDoc e) {
873                 log(e.toString(), Project.MSG_ERR);
874             }
875         }
876     }
877
878     /**
879      * Execute a testcase by forking a new JVM. The command will block
880      * until it finishes. To know if the process was destroyed or not
881      * or whether the forked Java VM exited abnormally, use the
882      * attributes of the returned holder object.
883      * @param test the testcase to execute.
884      * @param watchdog the watchdog in charge of cancelling the test if it
885      * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
886      * the test could probably hang forever.
887      * @param casesFile list of test cases to execute. Can be <tt>null</tt>,
888      * in this case only one test is executed.
889      * @throws BuildException in case of error creating a temporary property file,
890      * or if the junit process can not be forked
891      */

892     private TestResultHolder executeAsForked(JUnitTest test,
893                                              ExecuteWatchdog watchdog,
894                                              File JavaDoc casesFile)
895         throws BuildException {
896
897         if (perm != null) {
898             log("Permissions ignored when running in forked mode!",
899                 Project.MSG_WARN);
900         }
901
902         CommandlineJava cmd = null;
903         try {
904             cmd = (CommandlineJava) (getCommandline().clone());
905         } catch (CloneNotSupportedException JavaDoc e) {
906             throw new BuildException("This shouldn't happen", e, getLocation());
907         }
908         cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
909         if (casesFile == null) {
910             cmd.createArgument().setValue(test.getName());
911         } else {
912             log("Running multiple tests in the same VM", Project.MSG_VERBOSE);
913             cmd.createArgument().setValue(Constants.TESTSFILE + casesFile);
914         }
915
916         cmd.createArgument().setValue(Constants.FILTERTRACE + test.getFiltertrace());
917         cmd.createArgument().setValue(Constants.HALT_ON_ERROR + test.getHaltonerror());
918         cmd.createArgument().setValue(Constants.HALT_ON_FAILURE
919                                       + test.getHaltonfailure());
920         if (includeAntRuntime) {
921             Vector JavaDoc v = Execute.getProcEnvironment();
922             Enumeration JavaDoc e = v.elements();
923             while (e.hasMoreElements()) {
924                 String JavaDoc s = (String JavaDoc) e.nextElement();
925                 if (s.startsWith(CLASSPATH)) {
926                     cmd.createClasspath(getProject()).createPath()
927                         .append(new Path(getProject(),
928                                          s.substring(CLASSPATH.length()
929                                                      )));
930                 }
931             }
932             log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH",
933                 Project.MSG_VERBOSE);
934             cmd.createClasspath(getProject()).createPath()
935                 .append(antRuntimeClasses);
936         }
937
938         if (summary) {
939             String JavaDoc prefix = "";
940             if ("withoutanderr".equalsIgnoreCase(summaryValue)) {
941                 prefix = "OutErr";
942             }
943             cmd.createArgument()
944                 .setValue(Constants.FORMATTER
945                           + "org.apache.tools.ant.taskdefs.optional.junit."
946                           + prefix + "SummaryJUnitResultFormatter");
947         }
948
949         cmd.createArgument().setValue(Constants.SHOWOUTPUT
950                                       + String.valueOf(showOutput));
951         cmd.createArgument().setValue(Constants.OUTPUT_TO_FORMATTERS
952                                       + String.valueOf(outputToFormatters));
953
954         cmd.createArgument().setValue(
955             Constants.LOGTESTLISTENEREVENTS + "true"); // #31885
956

957         StringBuffer JavaDoc formatterArg = new StringBuffer JavaDoc(STRING_BUFFER_SIZE);
958         final FormatterElement[] feArray = mergeFormatters(test);
959         for (int i = 0; i < feArray.length; i++) {
960             FormatterElement fe = feArray[i];
961             if (fe.shouldUse(this)) {
962                 formatterArg.append(Constants.FORMATTER);
963                 formatterArg.append(fe.getClassname());
964                 File JavaDoc outFile = getOutput(fe, test);
965                 if (outFile != null) {
966                     formatterArg.append(",");
967                     formatterArg.append(outFile);
968                 }
969                 cmd.createArgument().setValue(formatterArg.toString());
970                 formatterArg = new StringBuffer JavaDoc();
971             }
972         }
973
974         File JavaDoc vmWatcher = createTempPropertiesFile("junitvmwatcher");
975         cmd.createArgument().setValue(Constants.CRASHFILE
976                                       + vmWatcher.getAbsolutePath());
977         File JavaDoc propsFile = createTempPropertiesFile("junit");
978         cmd.createArgument().setValue(Constants.PROPSFILE
979                                       + propsFile.getAbsolutePath());
980         Hashtable JavaDoc p = getProject().getProperties();
981         Properties JavaDoc props = new Properties JavaDoc();
982         for (Enumeration JavaDoc e = p.keys(); e.hasMoreElements();) {
983             Object JavaDoc key = e.nextElement();
984             props.put(key, p.get(key));
985         }
986         try {
987             FileOutputStream JavaDoc outstream = new FileOutputStream JavaDoc(propsFile);
988             props.store(outstream, "Ant JUnitTask generated properties file");
989             outstream.close();
990         } catch (java.io.IOException JavaDoc e) {
991             propsFile.delete();
992             throw new BuildException("Error creating temporary properties "
993                                      + "file.", e, getLocation());
994         }
995
996         Execute execute = new Execute(
997             new JUnitLogStreamHandler(
998                 this,
999                 Project.MSG_INFO,
1000                Project.MSG_WARN),
1001            watchdog);
1002        execute.setCommandline(cmd.getCommandline());
1003        execute.setAntRun(getProject());
1004        if (dir != null) {
1005            execute.setWorkingDirectory(dir);
1006        }
1007
1008        String JavaDoc[] environment = env.getVariables();
1009        if (environment != null) {
1010            for (int i = 0; i < environment.length; i++) {
1011                log("Setting environment variable: " + environment[i],
1012                    Project.MSG_VERBOSE);
1013            }
1014        }
1015        execute.setNewenvironment(newEnvironment);
1016        execute.setEnvironment(environment);
1017
1018        log(cmd.describeCommand(), Project.MSG_VERBOSE);
1019        TestResultHolder result = new TestResultHolder();
1020        try {
1021            result.exitCode = execute.execute();
1022        } catch (IOException JavaDoc e) {
1023            throw new BuildException("Process fork failed.", e, getLocation());
1024        } finally {
1025            String JavaDoc vmCrashString = "unknown";
1026            BufferedReader JavaDoc br = null;
1027            try {
1028                br = new BufferedReader JavaDoc(new FileReader JavaDoc(vmWatcher));
1029                vmCrashString = br.readLine();
1030            } catch (Exception JavaDoc e) {
1031                e.printStackTrace();
1032                // ignored.
1033
} finally {
1034                FileUtils.close(br);
1035            }
1036            if (watchdog != null && watchdog.killedProcess()) {
1037                result.timedOut = true;
1038                logTimeout(feArray, test, vmCrashString);
1039            } else if (!Constants.TERMINATED_SUCCESSFULLY.equals(vmCrashString)) {
1040                result.crashed = true;
1041                logVmCrash(feArray, test, vmCrashString);
1042            }
1043            vmWatcher.delete();
1044
1045            if (!propsFile.delete()) {
1046                throw new BuildException("Could not delete temporary "
1047                                         + "properties file.");
1048            }
1049        }
1050
1051        return result;
1052    }
1053
1054    /**
1055     * Create a temporary file to pass the properties to a new process.
1056     * Will auto-delete on (graceful) exit.
1057     * The file will be in the project basedir unless tmpDir declares
1058     * something else.
1059     * @param prefix
1060     * @return created file
1061     */

1062    private File JavaDoc createTempPropertiesFile(String JavaDoc prefix) {
1063        File JavaDoc propsFile =
1064            FILE_UTILS.createTempFile(prefix, ".properties",
1065                tmpDir != null ? tmpDir : getProject().getBaseDir(), true);
1066        return propsFile;
1067    }
1068
1069
1070    /**
1071     * Pass output sent to System.out to the TestRunner so it can
1072     * collect ot for the formatters.
1073     *
1074     * @param output output coming from System.out
1075     * @since Ant 1.5
1076     */

1077    protected void handleOutput(String JavaDoc output) {
1078        if (output.startsWith(TESTLISTENER_PREFIX)) {
1079            log(output, Project.MSG_VERBOSE);
1080        } else if (runner != null) {
1081            if (outputToFormatters) {
1082                runner.handleOutput(output);
1083            }
1084            if (showOutput) {
1085                super.handleOutput(output);
1086            }
1087        } else {
1088            super.handleOutput(output);
1089        }
1090    }
1091
1092    /**
1093     * Handle an input request by this task.
1094     * @see Task#handleInput(byte[], int, int)
1095     * This implementation delegates to a runner if it
1096     * present.
1097     * @param buffer the buffer into which data is to be read.
1098     * @param offset the offset into the buffer at which data is stored.
1099     * @param length the amount of data to read.
1100     *
1101     * @return the number of bytes read.
1102     * @exception IOException if the data cannot be read.
1103     *
1104     * @since Ant 1.6
1105     */

1106    protected int handleInput(byte[] buffer, int offset, int length)
1107        throws IOException JavaDoc {
1108        if (runner != null) {
1109            return runner.handleInput(buffer, offset, length);
1110        } else {
1111            return super.handleInput(buffer, offset, length);
1112        }
1113    }
1114
1115
1116    /**
1117     * Pass output sent to System.out to the TestRunner so it can
1118     * collect ot for the formatters.
1119     *
1120     * @param output output coming from System.out
1121     * @since Ant 1.5.2
1122     */

1123    protected void handleFlush(String JavaDoc output) {
1124        if (runner != null) {
1125            runner.handleFlush(output);
1126            if (showOutput) {
1127                super.handleFlush(output);
1128            }
1129        } else {
1130            super.handleFlush(output);
1131        }
1132    }
1133
1134    /**
1135     * Pass output sent to System.err to the TestRunner so it can
1136     * collect it for the formatters.
1137     *
1138     * @param output output coming from System.err
1139     * @since Ant 1.5
1140     */

1141    public void handleErrorOutput(String JavaDoc output) {
1142        if (runner != null) {
1143            runner.handleErrorOutput(output);
1144            if (showOutput) {
1145                super.handleErrorOutput(output);
1146            }
1147        } else {
1148            super.handleErrorOutput(output);
1149        }
1150    }
1151
1152
1153    /**
1154     * Pass output sent to System.err to the TestRunner so it can
1155     * collect it for the formatters.
1156     *
1157     * @param output coming from System.err
1158     * @since Ant 1.5.2
1159     */

1160    public void handleErrorFlush(String JavaDoc output) {
1161        if (runner != null) {
1162            runner.handleErrorFlush(output);
1163            if (showOutput) {
1164                super.handleErrorFlush(output);
1165            }
1166        } else {
1167            super.handleErrorFlush(output);
1168        }
1169    }
1170
1171    // in VM is not very nice since it could probably hang the
1172
// whole build. IMHO this method should be avoided and it would be best
1173
// to remove it in future versions. TBD. (SBa)
1174

1175    /**
1176     * Execute inside VM.
1177     * @param arg one JUnitTest
1178     * @throws BuildException under unspecified circumstances
1179     */

1180    private TestResultHolder executeInVM(JUnitTest arg) throws BuildException {
1181        JUnitTest test = (JUnitTest) arg.clone();
1182        test.setProperties(getProject().getProperties());
1183        if (dir != null) {
1184            log("dir attribute ignored if running in the same VM",
1185                Project.MSG_WARN);
1186        }
1187
1188        if (newEnvironment || null != env.getVariables()) {
1189            log("Changes to environment variables are ignored if running in "
1190                + "the same VM.", Project.MSG_WARN);
1191        }
1192
1193        if (getCommandline().getBootclasspath() != null) {
1194            log("bootclasspath is ignored if running in the same VM.",
1195                Project.MSG_WARN);
1196        }
1197
1198        CommandlineJava.SysProperties sysProperties =
1199                getCommandline().getSystemProperties();
1200        if (sysProperties != null) {
1201            sysProperties.setSystem();
1202        }
1203
1204        try {
1205            log("Using System properties " + System.getProperties(),
1206                Project.MSG_VERBOSE);
1207            if (splitJunit) {
1208                classLoader = (AntClassLoader) delegate.getClass().getClassLoader();
1209            } else {
1210                createClassLoader();
1211            }
1212            if (classLoader != null) {
1213                classLoader.setThreadContextLoader();
1214            }
1215            runner = delegate.newJUnitTestRunner(test, test.getHaltonerror(),
1216                                         test.getFiltertrace(),
1217                                         test.getHaltonfailure(), false,
1218                                         true, classLoader);
1219            if (summary) {
1220
1221                JUnitTaskMirror.SummaryJUnitResultFormatterMirror f =
1222                    delegate.newSummaryJUnitResultFormatter();
1223                f.setWithOutAndErr("withoutanderr"
1224                                   .equalsIgnoreCase(summaryValue));
1225                f.setOutput(getDefaultOutput());
1226                runner.addFormatter(f);
1227            }
1228
1229            runner.setPermissions(perm);
1230
1231            final FormatterElement[] feArray = mergeFormatters(test);
1232            for (int i = 0; i < feArray.length; i++) {
1233                FormatterElement fe = feArray[i];
1234                if (fe.shouldUse(this)) {
1235                    File JavaDoc outFile = getOutput(fe, test);
1236                    if (outFile != null) {
1237                        fe.setOutfile(outFile);
1238                    } else {
1239                        fe.setOutput(getDefaultOutput());
1240                    }
1241                    runner.addFormatter(fe.createFormatter(classLoader));
1242                }
1243            }
1244
1245            runner.run();
1246            TestResultHolder result = new TestResultHolder();
1247            result.exitCode = runner.getRetCode();
1248            return result;
1249        } finally {
1250            if (sysProperties != null) {
1251                sysProperties.restoreSystem();
1252            }
1253            if (classLoader != null) {
1254                classLoader.resetThreadContextLoader();
1255            }
1256        }
1257    }
1258
1259    /**
1260     * @return <tt>null</tt> if there is a timeout value, otherwise the
1261     * watchdog instance.
1262     *
1263     * @throws BuildException under unspecified circumstances
1264     * @since Ant 1.2
1265     */

1266    protected ExecuteWatchdog createWatchdog() throws BuildException {
1267        if (timeout == null) {
1268            return null;
1269        }
1270        return new ExecuteWatchdog((long) timeout.intValue());
1271    }
1272
1273    /**
1274     * Get the default output for a formatter.
1275     *
1276     * @return default output stream for a formatter
1277     * @since Ant 1.3
1278     */

1279    protected OutputStream JavaDoc getDefaultOutput() {
1280        return new LogOutputStream(this, Project.MSG_INFO);
1281    }
1282
1283    /**
1284     * Merge all individual tests from the batchtest with all individual tests
1285     * and return an enumeration over all <tt>JUnitTest</tt>.
1286     *
1287     * @return enumeration over individual tests
1288     * @since Ant 1.3
1289     */

1290    protected Enumeration JavaDoc getIndividualTests() {
1291        final int count = batchTests.size();
1292        final Enumeration JavaDoc[] enums = new Enumeration JavaDoc[ count + 1];
1293        for (int i = 0; i < count; i++) {
1294            BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
1295            enums[i] = batchtest.elements();
1296        }
1297        enums[enums.length - 1] = tests.elements();
1298        return Enumerations.fromCompound(enums);
1299    }
1300
1301    /**
1302     * return an enumeration listing each test, then each batchtest
1303     * @return enumeration
1304     * @since Ant 1.3
1305     */

1306    protected Enumeration JavaDoc allTests() {
1307        Enumeration JavaDoc[] enums = {tests.elements(), batchTests.elements()};
1308        return Enumerations.fromCompound(enums);
1309    }
1310
1311    /**
1312     * @param test junit test
1313     * @return array of FormatterElement
1314     * @since Ant 1.3
1315     */

1316    private FormatterElement[] mergeFormatters(JUnitTest test) {
1317        Vector JavaDoc feVector = (Vector JavaDoc) formatters.clone();
1318        test.addFormattersTo(feVector);
1319        FormatterElement[] feArray = new FormatterElement[feVector.size()];
1320        feVector.copyInto(feArray);
1321        return feArray;
1322    }
1323
1324    /**
1325     * If the formatter sends output to a file, return that file.
1326     * null otherwise.
1327     * @param fe formatter element
1328     * @param test one JUnit test
1329     * @return file reference
1330     * @since Ant 1.3
1331     */

1332    protected File JavaDoc getOutput(FormatterElement fe, JUnitTest test) {
1333        if (fe.getUseFile()) {
1334            String JavaDoc base = test.getOutfile();
1335            if (base == null) {
1336                base = JUnitTaskMirror.JUnitTestRunnerMirror.IGNORED_FILE_NAME;
1337            }
1338            String JavaDoc filename = base + fe.getExtension();
1339            File JavaDoc destFile = new File JavaDoc(test.getTodir(), filename);
1340            String JavaDoc absFilename = destFile.getAbsolutePath();
1341            return getProject().resolveFile(absFilename);
1342        }
1343        return null;
1344    }
1345
1346    /**
1347     * Search for the given resource and add the directory or archive
1348     * that contains it to the classpath.
1349     *
1350     * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
1351     * getResource doesn't contain the name of the archive.</p>
1352     *
1353     * @param resource resource that one wants to lookup
1354     * @return true if something was in fact added
1355     * @since Ant 1.4
1356     */

1357    protected boolean addClasspathEntry(String JavaDoc resource) {
1358        /*
1359         * pre Ant 1.6 this method used to call getClass().getResource
1360         * while Ant 1.6 will call ClassLoader.getResource().
1361         *
1362         * The difference is that Class.getResource expects a leading
1363         * slash for "absolute" resources and will strip it before
1364         * delegating to ClassLoader.getResource - so we now have to
1365         * emulate Class's behavior.
1366         */

1367        if (resource.startsWith("/")) {
1368            resource = resource.substring(1);
1369        } else {
1370            resource = "org/apache/tools/ant/taskdefs/optional/junit/"
1371                + resource;
1372        }
1373
1374        File JavaDoc f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
1375                                               resource);
1376        if (f != null) {
1377            log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
1378            antRuntimeClasses.createPath().setLocation(f);
1379            return true;
1380        } else {
1381            log("Couldn\'t find " + resource, Project.MSG_DEBUG);
1382            return false;
1383        }
1384    }
1385
1386    /**
1387     * Take care that some output is produced in report files if the
1388     * watchdog kills the test.
1389     *
1390     * @since Ant 1.5.2
1391     */

1392
1393    private void logTimeout(FormatterElement[] feArray, JUnitTest test, String JavaDoc testCase) {
1394        logVmExit(
1395            feArray, test,
1396            "Timeout occurred. Please note the time in the report does"
1397            + " not reflect the time until the timeout.",
1398            testCase);
1399    }
1400
1401    /**
1402     * Take care that some output is produced in report files if the
1403     * forked machine exited before the test suite finished but the
1404     * reason is not a timeout.
1405     *
1406     * @since Ant 1.7
1407     */

1408    private void logVmCrash(FormatterElement[] feArray, JUnitTest test, String JavaDoc testCase) {
1409        logVmExit(
1410            feArray, test,
1411            "Forked Java VM exited abnormally. Please note the time in the report"
1412            + " does not reflect the time until the VM exit.",
1413            testCase);
1414    }
1415
1416    /**
1417     * Take care that some output is produced in report files if the
1418     * forked machine terminated before the test suite finished
1419     *
1420     * @since Ant 1.7
1421     */

1422    private void logVmExit(FormatterElement[] feArray, JUnitTest test,
1423                           String JavaDoc message, String JavaDoc testCase) {
1424        try {
1425            log("Using System properties " + System.getProperties(),
1426                Project.MSG_VERBOSE);
1427            if (splitJunit) {
1428                classLoader = (AntClassLoader) delegate.getClass().getClassLoader();
1429            } else {
1430                createClassLoader();
1431            }
1432            if (classLoader != null) {
1433                classLoader.setThreadContextLoader();
1434            }
1435
1436            test.setCounts(1, 0, 1);
1437            test.setProperties(getProject().getProperties());
1438            for (int i = 0; i < feArray.length; i++) {
1439                FormatterElement fe = feArray[i];
1440                File JavaDoc outFile = getOutput(fe, test);
1441                JUnitTaskMirror.JUnitResultFormatterMirror formatter =
1442                    fe.createFormatter(classLoader);
1443                if (outFile != null && formatter != null) {
1444                    try {
1445                        OutputStream JavaDoc out = new FileOutputStream JavaDoc(outFile);
1446                        delegate.addVmExit(test, formatter, out, message, testCase);
1447                    } catch (IOException JavaDoc e) {
1448                        // ignore
1449
}
1450                }
1451            }
1452            if (summary) {
1453                JUnitTaskMirror.SummaryJUnitResultFormatterMirror f =
1454                    delegate.newSummaryJUnitResultFormatter();
1455                f.setWithOutAndErr("withoutanderr".equalsIgnoreCase(summaryValue));
1456                delegate.addVmExit(test, f, getDefaultOutput(), message, testCase);
1457            }
1458        } finally {
1459            if (classLoader != null) {
1460                classLoader.resetThreadContextLoader();
1461            }
1462        }
1463    }
1464
1465    /**
1466     * Creates and configures an AntClassLoader instance from the
1467     * nested classpath element.
1468     *
1469     * @since Ant 1.6
1470     */

1471    private void createClassLoader() {
1472        Path userClasspath = getCommandline().getClasspath();
1473        if (userClasspath != null) {
1474            if (reloading || classLoader == null) {
1475                deleteClassLoader();
1476                Path classpath = (Path) userClasspath.clone();
1477                if (includeAntRuntime) {
1478                    log("Implicitly adding " + antRuntimeClasses
1479                        + " to CLASSPATH", Project.MSG_VERBOSE);
1480                    classpath.append(antRuntimeClasses);
1481                }
1482                classLoader = getProject().createClassLoader(classpath);
1483                if (getClass().getClassLoader() != null
1484                    && getClass().getClassLoader() != Project.class.getClassLoader()) {
1485                    classLoader.setParent(getClass().getClassLoader());
1486                }
1487                classLoader.setParentFirst(false);
1488                classLoader.addJavaLibraries();
1489                log("Using CLASSPATH " + classLoader.getClasspath(),
1490                    Project.MSG_VERBOSE);
1491                // make sure the test will be accepted as a TestCase
1492
classLoader.addSystemPackageRoot("junit");
1493                // make sure the test annotation are accepted
1494
classLoader.addSystemPackageRoot("org.junit");
1495                // will cause trouble in JDK 1.1 if omitted
1496
classLoader.addSystemPackageRoot("org.apache.tools.ant");
1497            }
1498        }
1499    }
1500
1501    /**
1502     * Removes a classloader if needed.
1503     * @since Ant 1.7
1504     */

1505    private void deleteClassLoader() {
1506        if (classLoader != null) {
1507            classLoader.cleanup();
1508            classLoader = null;
1509        }
1510    }
1511
1512    /**
1513     * Get the command line used to run the tests.
1514     * @return the command line.
1515     * @since Ant 1.6.2
1516     */

1517    protected CommandlineJava getCommandline() {
1518        if (commandline == null) {
1519            commandline = new CommandlineJava();
1520        }
1521        return commandline;
1522    }
1523
1524    /**
1525     * Forked test support
1526     * @since Ant 1.6.2
1527     */

1528    private static final class ForkedTestConfiguration {
1529        private boolean filterTrace;
1530        private boolean haltOnError;
1531        private boolean haltOnFailure;
1532        private String JavaDoc errorProperty;
1533        private String JavaDoc failureProperty;
1534
1535        /**
1536         * constructor for forked test configuration
1537         * @param filterTrace
1538         * @param haltOnError
1539         * @param haltOnFailure
1540         * @param errorProperty
1541         * @param failureProperty
1542         */

1543        ForkedTestConfiguration(boolean filterTrace, boolean haltOnError,
1544                                boolean haltOnFailure, String JavaDoc errorProperty,
1545                                String JavaDoc failureProperty) {
1546            this.filterTrace = filterTrace;
1547            this.haltOnError = haltOnError;
1548            this.haltOnFailure = haltOnFailure;
1549            this.errorProperty = errorProperty;
1550            this.failureProperty = failureProperty;
1551        }
1552
1553        /**
1554         * configure from a test; sets member variables to attributes of the test
1555         * @param test
1556         */

1557        ForkedTestConfiguration(JUnitTest test) {
1558            this(test.getFiltertrace(),
1559                    test.getHaltonerror(),
1560                    test.getHaltonfailure(),
1561                    test.getErrorProperty(),
1562                    test.getFailureProperty());
1563        }
1564
1565        /**
1566         * equality test checks all the member variables
1567         * @param other
1568         * @return true if everything is equal
1569         */

1570        public boolean equals(Object JavaDoc other) {
1571            if (other == null
1572                || other.getClass() != ForkedTestConfiguration.class) {
1573                return false;
1574            }
1575            ForkedTestConfiguration o = (ForkedTestConfiguration) other;
1576            return filterTrace == o.filterTrace
1577                && haltOnError == o.haltOnError
1578                && haltOnFailure == o.haltOnFailure
1579                && ((errorProperty == null && o.errorProperty == null)
1580                    ||
1581                    (errorProperty != null
1582                     && errorProperty.equals(o.errorProperty)))
1583                && ((failureProperty == null && o.failureProperty == null)
1584                    ||
1585                    (failureProperty != null
1586                     && failureProperty.equals(o.failureProperty)));
1587        }
1588
1589        /**
1590         * hashcode is based only on the boolean members, and returns a value
1591         * in the range 0-7.
1592         * @return hash code value
1593         */

1594        public int hashCode() {
1595            return (filterTrace ? 1 : 0)
1596                + (haltOnError ? 2 : 0)
1597                + (haltOnFailure ? 4 : 0);
1598        }
1599    }
1600
1601    /**
1602     * These are the different forking options
1603     * @since 1.6.2
1604     */

1605    public static final class ForkMode extends EnumeratedAttribute {
1606
1607        /**
1608         * fork once only
1609         */

1610        public static final String JavaDoc ONCE = "once";
1611        /**
1612         * fork once per test class
1613         */

1614        public static final String JavaDoc PER_TEST = "perTest";
1615        /**
1616         * fork once per batch of tests
1617         */

1618        public static final String JavaDoc PER_BATCH = "perBatch";
1619
1620        /** No arg constructor. */
1621        public ForkMode() {
1622            super();
1623        }
1624
1625        /**
1626         * Constructor using a value.
1627         * @param value the value to use - once, perTest or perBatch.
1628         */

1629        public ForkMode(String JavaDoc value) {
1630            super();
1631            setValue(value);
1632        }
1633
1634        /** {@inheritDoc}. */
1635        public String JavaDoc[] getValues() {
1636            return new String JavaDoc[] {ONCE, PER_TEST, PER_BATCH};
1637        }
1638    }
1639
1640    /**
1641     * Executes all tests that don't need to be forked (or all tests
1642     * if the runIndividual argument is true. Returns a collection of
1643     * lists of tests that share the same VM configuration and haven't
1644     * been executed yet.
1645     * @param testList the list of tests to be executed or queued.
1646     * @param runIndividual if true execute each test individually.
1647     * @return a list of tasks to be executed.
1648     * @since 1.6.2
1649     */

1650    protected Collection JavaDoc executeOrQueue(Enumeration JavaDoc testList,
1651                                        boolean runIndividual) {
1652        Map JavaDoc testConfigurations = new HashMap JavaDoc();
1653        while (testList.hasMoreElements()) {
1654            JUnitTest test = (JUnitTest) testList.nextElement();
1655            if (test.shouldRun(getProject())) {
1656                if (runIndividual || !test.getFork()) {
1657                    execute(test);
1658                } else {
1659                    ForkedTestConfiguration c =
1660                        new ForkedTestConfiguration(test);
1661                    List JavaDoc l = (List JavaDoc) testConfigurations.get(c);
1662                    if (l == null) {
1663                        l = new ArrayList JavaDoc();
1664                        testConfigurations.put(c, l);
1665                    }
1666                    l.add(test);
1667                }
1668            }
1669        }
1670        return testConfigurations.values();
1671    }
1672
1673    /**
1674     * Logs information about failed tests, potentially stops
1675     * processing (by throwing a BuildException) if a failure/error
1676     * occurred or sets a property.
1677     * @param exitValue the exitValue of the test.
1678     * @param wasKilled if true, the test had been killed.
1679     * @param test the test in question.
1680     * @param name the name of the test.
1681     * @since Ant 1.6.2
1682     */

1683    protected void actOnTestResult(int exitValue, boolean wasKilled,
1684                                   JUnitTest test, String JavaDoc name) {
1685        TestResultHolder t = new TestResultHolder();
1686        t.exitCode = exitValue;
1687        t.timedOut = wasKilled;
1688        actOnTestResult(t, test, name);
1689    }
1690
1691    /**
1692     * Logs information about failed tests, potentially stops
1693     * processing (by throwing a BuildException) if a failure/error
1694     * occurred or sets a property.
1695     * @param result the result of the test.
1696     * @param test the test in question.
1697     * @param name the name of the test.
1698     * @since Ant 1.7
1699     */

1700    protected void actOnTestResult(TestResultHolder result, JUnitTest test,
1701                                   String JavaDoc name) {
1702        // if there is an error/failure and that it should halt, stop
1703
// everything otherwise just log a statement
1704
boolean fatal = result.timedOut || result.crashed;
1705        boolean errorOccurredHere =
1706            result.exitCode == JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS || fatal;
1707        boolean failureOccurredHere =
1708            result.exitCode != JUnitTaskMirror.JUnitTestRunnerMirror.SUCCESS || fatal;
1709        if (errorOccurredHere || failureOccurredHere) {
1710            if ((errorOccurredHere && test.getHaltonerror())
1711                || (failureOccurredHere && test.getHaltonfailure())) {
1712                throw new BuildException(name + " failed"
1713                    + (result.timedOut ? " (timeout)" : "")
1714                    + (result.crashed ? " (crashed)" : ""), getLocation());
1715            } else {
1716                log(name + " FAILED"
1717                    + (result.timedOut ? " (timeout)" : "")
1718                    + (result.crashed ? " (crashed)" : ""), Project.MSG_ERR);
1719                if (errorOccurredHere && test.getErrorProperty() != null) {
1720                    getProject().setNewProperty(test.getErrorProperty(), "true");
1721                }
1722                if (failureOccurredHere && test.getFailureProperty() != null) {
1723                    getProject().setNewProperty(test.getFailureProperty(), "true");
1724                }
1725            }
1726        }
1727    }
1728
1729    /**
1730     * A value class that contains thee result of a test.
1731     */

1732    protected class TestResultHolder {
1733        // CheckStyle:VisibilityModifier OFF - bc
1734
/** the exit code of the test. */
1735        public int exitCode = JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS;
1736        /** true if the test timed out */
1737        public boolean timedOut = false;
1738        /** true if the test crashed */
1739        public boolean crashed = false;
1740        // CheckStyle:VisibilityModifier ON
1741
}
1742
1743    /**
1744     * A stream handler for handling the junit task.
1745     * @since Ant 1.7
1746     */

1747    protected static class JUnitLogOutputStream extends LogOutputStream {
1748        private Task task; // local copy since LogOutputStream.task is private
1749

1750        /**
1751         * Constructor.
1752         * @param task the task being logged.
1753         * @param level the log level used to log data written to this stream.
1754         */

1755        public JUnitLogOutputStream(Task task, int level) {
1756            super(task, level);
1757            this.task = task;
1758        }
1759
1760        /**
1761         * Logs a line.
1762         * If the line starts with junit.framework.TestListener: set the level
1763         * to MSG_VERBOSE.
1764         * @param line the line to log.
1765         * @param level the logging level to use.
1766         */

1767        protected void processLine(String JavaDoc line, int level) {
1768            if (line.startsWith(TESTLISTENER_PREFIX)) {
1769                task.log(line, Project.MSG_VERBOSE);
1770            } else {
1771                super.processLine(line, level);
1772            }
1773        }
1774    }
1775
1776    /**
1777     * A log stream handler for junit.
1778     * @since Ant 1.7
1779     */

1780    protected static class JUnitLogStreamHandler extends PumpStreamHandler {
1781        /**
1782         * Constructor.
1783         * @param task the task to log.
1784         * @param outlevel the level to use for standard output.
1785         * @param errlevel the level to use for error output.
1786         */

1787        public JUnitLogStreamHandler(Task task, int outlevel, int errlevel) {
1788            super(new JUnitLogOutputStream(task, outlevel),
1789                  new LogOutputStream(task, errlevel));
1790        }
1791    }
1792}
1793
Popular Tags