source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 51.6 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs.optional.junit;
19
20import java.io.BufferedWriter;
21import java.io.File;
22import java.io.FileOutputStream;
23import java.io.FileWriter;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.io.PrintWriter;
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Enumeration;
30import java.util.HashMap;
31import java.util.Hashtable;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35import java.util.Properties;
36import java.util.Vector;
37import org.apache.tools.ant.AntClassLoader;
38import org.apache.tools.ant.BuildException;
39import org.apache.tools.ant.Project;
40import org.apache.tools.ant.Task;
41import org.apache.tools.ant.taskdefs.Execute;
42import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
43import org.apache.tools.ant.taskdefs.LogOutputStream;
44import org.apache.tools.ant.taskdefs.LogStreamHandler;
45import org.apache.tools.ant.types.Assertions;
46import org.apache.tools.ant.types.Commandline;
47import org.apache.tools.ant.types.CommandlineJava;
48import org.apache.tools.ant.types.EnumeratedAttribute;
49import org.apache.tools.ant.types.Environment;
50import org.apache.tools.ant.types.Path;
51import org.apache.tools.ant.types.Permissions;
52import org.apache.tools.ant.types.PropertySet;
53import org.apache.tools.ant.util.FileUtils;
54import org.apache.tools.ant.util.LoaderUtils;
55import junit.framework.AssertionFailedError;
56import junit.framework.Test;
57import junit.framework.TestResult;
58
59/**
60 * Runs JUnit tests.
61 *
62 * <p> JUnit is a framework to create unit test. It has been initially
63 * created by Erich Gamma and Kent Beck. JUnit can be found at <a
64 * href="http://www.junit.org">http://www.junit.org</a>.
65 *
66 * <p> <code>JUnitTask</code> can run a single specific
67 * <code>JUnitTest</code> using the <code>test</code> element.</p>
68 * For example, the following target <code><pre>
69 * &lt;target name="test-int-chars" depends="jar-test"&gt;
70 * &lt;echo message="testing international characters"/&gt;
71 * &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
72 * &lt;classpath refid="classpath"/&gt;
73 * &lt;formatter type="plain" usefile="false" /&gt;
74 * &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
75 * &lt;/junit&gt;
76 * &lt;/target&gt;
77 * </pre></code>
78 * <p>runs a single junit test
79 * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
80 * VM using the path with id <code>classpath</code> as classpath and
81 * presents the results formatted using the standard
82 * <code>plain</code> formatter on the command line.</p>
83 *
84 * <p> This task can also run batches of tests. The
85 * <code>batchtest</code> element creates a <code>BatchTest</code>
86 * based on a fileset. This allows, for example, all classes found in
87 * directory to be run as testcases.</p>
88 *
89 * <p>For example,</p><code><pre>
90 * &lt;target name="run-tests" depends="dump-info,compile-tests" if="junit.present"&gt;
91 * &lt;junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"&gt;
92 * &lt;jvmarg value="-classic"/&gt;
93 * &lt;classpath refid="tests-classpath"/&gt;
94 * &lt;sysproperty key="build.tests" value="${build.tests}"/&gt;
95 * &lt;formatter type="brief" usefile="false" /&gt;
96 * &lt;batchtest&gt;
97 * &lt;fileset dir="${tests.dir}"&gt;
98 * &lt;include name="**&#047;*Test*" /&gt;
99 * &lt;/fileset&gt;
100 * &lt;/batchtest&gt;
101 * &lt;/junit&gt;
102 * &lt;/target&gt;
103 * </pre></code>
104 * <p>this target finds any classes with a <code>test</code> directory
105 * anywhere in their path (under the top <code>${tests.dir}</code>, of
106 * course) and creates <code>JUnitTest</code>'s for each one.</p>
107 *
108 * <p> Of course, <code>&lt;junit&gt;</code> and
109 * <code>&lt;batch&gt;</code> elements can be combined for more
110 * complex tests. For an example, see the ant <code>build.xml</code>
111 * target <code>run-tests</code> (the second example is an edited
112 * version).</p>
113 *
114 * <p> To spawn a new Java VM to prevent interferences between
115 * different testcases, you need to enable <code>fork</code>. A
116 * number of attributes and elements allow you to set up how this JVM
117 * runs.
118 *
119 *
120 * @since Ant 1.2
121 *
122 * @see JUnitTest
123 * @see BatchTest
124 */
125public class JUnitTask extends Task {
126
127 private CommandlineJava commandline;
128 private Vector tests = new Vector();
129 private Vector batchTests = new Vector();
130 private Vector formatters = new Vector();
131 private File dir = null;
132
133 private Integer timeout = null;
134 private boolean summary = false;
135 private boolean reloading = true;
136 private String summaryValue = "";
137 private JUnitTestRunner runner = null;
138
139 private boolean newEnvironment = false;
140 private Environment env = new Environment();
141
142 private boolean includeAntRuntime = true;
143 private Path antRuntimeClasses = null;
144
145 private boolean showOutput = false;
146 private File tmpDir;
147 private AntClassLoader classLoader = null;
148 private Permissions perm = null;
149 private ForkMode forkMode = new ForkMode("perTest");
150
151 private static final int STRING_BUFFER_SIZE = 128;
152
153 /**
154 * If true, force ant to re-classload all classes for each JUnit TestCase
155 *
156 * @param value force class reloading for each test case
157 */
158 public void setReloading(boolean value) {
159 reloading = value;
160 }
161
162 /**
163 * If true, smartly filter the stack frames of
164 * JUnit errors and failures before reporting them.
165 *
166 * <p>This property is applied on all BatchTest (batchtest) and
167 * JUnitTest (test) however it can possibly be overridden by their
168 * own properties.</p>
169 * @param value <tt>false</tt> if it should not filter, otherwise
170 * <tt>true<tt>
171 *
172 * @since Ant 1.5
173 */
174 public void setFiltertrace(boolean value) {
175 Enumeration e = allTests();
176 while (e.hasMoreElements()) {
177 BaseTest test = (BaseTest) e.nextElement();
178 test.setFiltertrace(value);
179 }
180 }
181
182 /**
183 * If true, stop the build process when there is an error in a test.
184 * This property is applied on all BatchTest (batchtest) and JUnitTest
185 * (test) however it can possibly be overridden by their own
186 * properties.
187 * @param value <tt>true</tt> if it should halt, otherwise
188 * <tt>false</tt>
189 *
190 * @since Ant 1.2
191 */
192 public void setHaltonerror(boolean value) {
193 Enumeration e = allTests();
194 while (e.hasMoreElements()) {
195 BaseTest test = (BaseTest) e.nextElement();
196 test.setHaltonerror(value);
197 }
198 }
199
200 /**
201 * Property to set to "true" if there is a error in a test.
202 *
203 * <p>This property is applied on all BatchTest (batchtest) and
204 * JUnitTest (test), however, it can possibly be overriden by
205 * their own properties.</p>
206 * @param propertyName the name of the property to set in the
207 * event of an error.
208 *
209 * @since Ant 1.4
210 */
211 public void setErrorProperty(String propertyName) {
212 Enumeration e = allTests();
213 while (e.hasMoreElements()) {
214 BaseTest test = (BaseTest) e.nextElement();
215 test.setErrorProperty(propertyName);
216 }
217 }
218
219 /**
220 * If true, stop the build process if a test fails
221 * (errors are considered failures as well).
222 * This property is applied on all BatchTest (batchtest) and
223 * JUnitTest (test) however it can possibly be overridden by their
224 * own properties.
225 * @param value <tt>true</tt> if it should halt, otherwise
226 * <tt>false</tt>
227 *
228 * @since Ant 1.2
229 */
230 public void setHaltonfailure(boolean value) {
231 Enumeration e = allTests();
232 while (e.hasMoreElements()) {
233 BaseTest test = (BaseTest) e.nextElement();
234 test.setHaltonfailure(value);
235 }
236 }
237
238 /**
239 * Property to set to "true" if there is a failure in a test.
240 *
241 * <p>This property is applied on all BatchTest (batchtest) and
242 * JUnitTest (test), however, it can possibly be overriden by
243 * their own properties.</p>
244 * @param propertyName the name of the property to set in the
245 * event of an failure.
246 *
247 * @since Ant 1.4
248 */
249 public void setFailureProperty(String propertyName) {
250 Enumeration e = allTests();
251 while (e.hasMoreElements()) {
252 BaseTest test = (BaseTest) e.nextElement();
253 test.setFailureProperty(propertyName);
254 }
255 }
256
257 /**
258 * If true, JVM should be forked for each test.
259 *
260 * <p>It avoids interference between testcases and possibly avoids
261 * hanging the build. this property is applied on all BatchTest
262 * (batchtest) and JUnitTest (test) however it can possibly be
263 * overridden by their own properties.</p>
264 * @param value <tt>true</tt> if a JVM should be forked, otherwise
265 * <tt>false</tt>
266 * @see #setTimeout
267 *
268 * @since Ant 1.2
269 */
270 public void setFork(boolean value) {
271 Enumeration e = allTests();
272 while (e.hasMoreElements()) {
273 BaseTest test = (BaseTest) e.nextElement();
274 test.setFork(value);
275 }
276 }
277
278 /**
279 * Set the behavior when {@link #setFork fork} fork has been enabled.
280 *
281 * <p>Possible values are "once", "perTest" and "perBatch". If
282 * set to "once", only a single Java VM will be forked for all
283 * tests, with "perTest" (the default) each test will run in a
284 * fresh Java VM and "perBatch" will run all tests from the same
285 * &lt;batchtest&gt; in the same Java VM.</p>
286 *
287 * <p>This attribute will be ignored if tests run in the same VM
288 * as Ant.</p>
289 *
290 * <p>Only tests with the same configuration of haltonerror,
291 * haltonfailure, errorproperty, failureproperty and filtertrace
292 * can share a forked Java VM, so even if you set the value to
293 * "once", Ant may need to fork mutliple VMs.</p>
294 *
295 * @since Ant 1.6.2
296 */
297 public void setForkMode(ForkMode mode) {
298 this.forkMode = mode;
299 }
300
301 /**
302 * If true, print one-line statistics for each test, or "withOutAndErr"
303 * to also show standard output and error.
304 *
305 * Can take the values on, off, and withOutAndErr.
306 * @param value <tt>true</tt> to print a summary,
307 * <tt>withOutAndErr</tt> to include the test&apos;s output as
308 * well, <tt>false</tt> otherwise.
309 * @see SummaryJUnitResultFormatter
310 *
311 * @since Ant 1.2
312 */
313 public void setPrintsummary(SummaryAttribute value) {
314 summaryValue = value.getValue();
315 summary = value.asBoolean();
316 }
317
318 /**
319 * Print summary enumeration values.
320 */
321 public static class SummaryAttribute extends EnumeratedAttribute {
322 /**
323 * list the possible values
324 * @return array of allowed values
325 */
326 public String[] getValues() {
327 return new String[] {"true", "yes", "false", "no",
328 "on", "off", "withOutAndErr"};
329 }
330
331 /**
332 * gives the boolean equivalent of the authorized values
333 * @return boolean equivalent of the value
334 */
335 public boolean asBoolean() {
336 String value = getValue();
337 return "true".equals(value)
338 || "on".equals(value)
339 || "yes".equals(value)
340 || "withOutAndErr".equals(value);
341 }
342 }
343
344 /**
345 * Set the timeout value (in milliseconds).
346 *
347 * <p>If the test is running for more than this value, the test
348 * will be canceled. (works only when in 'fork' mode).</p>
349 * @param value the maximum time (in milliseconds) allowed before
350 * declaring the test as 'timed-out'
351 * @see #setFork(boolean)
352 *
353 * @since Ant 1.2
354 */
355 public void setTimeout(Integer value) {
356 timeout = value;
357 }
358
359 /**
360 * Set the maximum memory to be used by all forked JVMs.
361 * @param max the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
362 * in the java command line options.
363 *
364 * @since Ant 1.2
365 */
366 public void setMaxmemory(String max) {
367 getCommandline().setMaxmemory(max);
368 }
369
370 /**
371 * The command used to invoke the Java Virtual Machine,
372 * default is 'java'. The command is resolved by
373 * java.lang.Runtime.exec(). Ignored if fork is disabled.
374 *
375 * @param value the new VM to use instead of <tt>java</tt>
376 * @see #setFork(boolean)
377 *
378 * @since Ant 1.2
379 */
380 public void setJvm(String value) {
381 getCommandline().setVm(value);
382 }
383
384 /**
385 * Adds a JVM argument; ignored if not forking.
386 *
387 * @return create a new JVM argument so that any argument can be
388 * passed to the JVM.
389 * @see #setFork(boolean)
390 *
391 * @since Ant 1.2
392 */
393 public Commandline.Argument createJvmarg() {
394 return getCommandline().createVmArgument();
395 }
396
397 /**
398 * The directory to invoke the VM in. Ignored if no JVM is forked.
399 * @param dir the directory to invoke the JVM from.
400 * @see #setFork(boolean)
401 *
402 * @since Ant 1.2
403 */
404 public void setDir(File dir) {
405 this.dir = dir;
406 }
407
408 /**
409 * Adds a system property that tests can access.
410 * This might be useful to tranfer Ant properties to the
411 * testcases when JVM forking is not enabled.
412 *
413 * @since Ant 1.3
414 * @deprecated since ant 1.6
415 * @param sysp environment variable to add
416 */
417 public void addSysproperty(Environment.Variable sysp) {
418
419 getCommandline().addSysproperty(sysp);
420 }
421
422 /**
423 * Adds a system property that tests can access.
424 * This might be useful to tranfer Ant properties to the
425 * testcases when JVM forking is not enabled.
426 * @param sysp new environment variable to add
427 * @since Ant 1.6
428 */
429 public void addConfiguredSysproperty(Environment.Variable sysp) {
430 // get a build exception if there is a missing key or value
431 // see bugzilla report 21684
432 String testString = sysp.getContent();
433 getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG);
434 getCommandline().addSysproperty(sysp);
435 }
436
437 /**
438 * Adds a set of properties that will be used as system properties
439 * that tests can access.
440 *
441 * This might be useful to tranfer Ant properties to the
442 * testcases when JVM forking is not enabled.
443 *
444 * @param sysp set of properties to be added
445 * @since Ant 1.6
446 */
447 public void addSyspropertyset(PropertySet sysp) {
448 getCommandline().addSyspropertyset(sysp);
449 }
450
451 /**
452 * Adds path to classpath used for tests.
453 *
454 * @return reference to the classpath in the embedded java command line
455 * @since Ant 1.2
456 */
457 public Path createClasspath() {
458 return getCommandline().createClasspath(getProject()).createPath();
459 }
460
461 /**
462 * Adds a path to the bootclasspath.
463 * @return reference to the bootclasspath in the embedded java command line
464 * @since Ant 1.6
465 */
466 public Path createBootclasspath() {
467 return getCommandline().createBootclasspath(getProject()).createPath();
468 }
469
470 /**
471 * Adds an environment variable; used when forking.
472 *
473 * <p>Will be ignored if we are not forking a new VM.</p>
474 * @param var environment variable to be added
475 * @since Ant 1.5
476 */
477 public void addEnv(Environment.Variable var) {
478 env.addVariable(var);
479 }
480
481 /**
482 * If true, use a new environment when forked.
483 *
484 * <p>Will be ignored if we are not forking a new VM.</p>
485 *
486 * @param newenv boolean indicating if setting a new environment is wished
487 * @since Ant 1.5
488 */
489 public void setNewenvironment(boolean newenv) {
490 newEnvironment = newenv;
491 }
492
493 /**
494 * Add a new single testcase.
495 * @param test a new single testcase
496 * @see JUnitTest
497 *
498 * @since Ant 1.2
499 */
500 public void addTest(JUnitTest test) {
501 tests.addElement(test);
502 }
503
504 /**
505 * Adds a set of tests based on pattern matching.
506 *
507 * @return a new instance of a batch test.
508 * @see BatchTest
509 *
510 * @since Ant 1.2
511 */
512 public BatchTest createBatchTest() {
513 BatchTest test = new BatchTest(getProject());
514 batchTests.addElement(test);
515 return test;
516 }
517
518 /**
519 * Add a new formatter to all tests of this task.
520 *
521 * @param fe formatter element
522 * @since Ant 1.2
523 */
524 public void addFormatter(FormatterElement fe) {
525 formatters.addElement(fe);
526 }
527
528 /**
529 * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
530 *
531 * @param b include ant run time yes or no
532 * @since Ant 1.5
533 */
534 public void setIncludeantruntime(boolean b) {
535 includeAntRuntime = b;
536 }
537
538 /**
539 * If true, send any output generated by tests to Ant's logging system
540 * as well as to the formatters.
541 * By default only the formatters receive the output.
542 *
543 * <p>Output will always be passed to the formatters and not by
544 * shown by default. This option should for example be set for
545 * tests that are interactive and prompt the user to do
546 * something.</p>
547 *
548 * @param showOutput if true, send output to Ant's logging system too
549 * @since Ant 1.5
550 */
551 public void setShowOutput(boolean showOutput) {
552 this.showOutput = showOutput;
553 }
554
555 /**
556 * Assertions to enable in this program (if fork=true)
557 * @since Ant 1.6
558 * @param asserts assertion set
559 */
560 public void addAssertions(Assertions asserts) {
561 if (getCommandline().getAssertions() != null) {
562 throw new BuildException("Only one assertion declaration is allowed");
563 }
564 getCommandline().setAssertions(asserts);
565 }
566
567 /**
568 * Sets the permissions for the application run inside the same JVM.
569 * @since Ant 1.6
570 * @return .
571 */
572 public Permissions createPermissions() {
573 if (perm == null) {
574 perm = new Permissions();
575 }
576 return perm;
577 }
578
579 /**
580 * Creates a new JUnitRunner and enables fork of a new Java VM.
581 *
582 * @throws Exception under ??? circumstances
583 * @since Ant 1.2
584 */
585 public JUnitTask() throws Exception {
586 getCommandline()
587 .setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
588 }
589
590 /**
591 * Where Ant should place temporary files.
592 *
593 * @param tmpDir location where temporary files should go to
594 * @since Ant 1.6
595 */
596 public void setTempdir(File tmpDir) {
597 if (tmpDir!=null) {
598 if (!tmpDir.exists() || !tmpDir.isDirectory()) {
599 throw new BuildException(tmpDir.toString()
600 +" is not a valid temp directory");
601 }
602 }
603 this.tmpDir = tmpDir;
604 }
605
606 /**
607 * Adds the jars or directories containing Ant, this task and
608 * JUnit to the classpath - this should make the forked JVM work
609 * without having to specify them directly.
610 *
611 * @since Ant 1.4
612 */
613 public void init() {
614 antRuntimeClasses = new Path(getProject());
615 addClasspathEntry("/junit/framework/TestCase.class");
616 addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class");
617 addClasspathEntry("/org/apache/tools/ant/Task.class");
618 addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
619 }
620
621 /**
622 * Runs the testcase.
623 *
624 * @throws BuildException in case of test failures or errors
625 * @since Ant 1.2
626 */
627 public void execute() throws BuildException {
628 List testLists = new ArrayList();
629
630 boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST);
631 if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) {
632 testLists.addAll(executeOrQueue(getIndividualTests(),
633 forkPerTest));
634 } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */
635 final int count = batchTests.size();
636 for (int i = 0; i < count; i++) {
637 BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
638 testLists.addAll(executeOrQueue(batchtest.elements(), false));
639 }
640 testLists.addAll(executeOrQueue(tests.elements(), forkPerTest));
641 }
642
643 try {
644 Iterator iter = testLists.iterator();
645 while (iter.hasNext()) {
646 List l = (List) iter.next();
647 if (l.size() == 1) {
648 execute((JUnitTest) l.get(0));
649 } else {
650 execute(l);
651 }
652 }
653 } finally {
654 if (classLoader != null && reloading) {
655 classLoader.cleanup();
656 }
657 classLoader = null;
658 }
659 }
660
661 /**
662 * Run the tests.
663 * @param arg one JunitTest
664 * @throws BuildException in case of test failures or errors
665 */
666 protected void execute(JUnitTest arg) throws BuildException {
667 JUnitTest test = (JUnitTest) arg.clone();
668 // set the default values if not specified
669 //@todo should be moved to the test class instead.
670 if (test.getTodir() == null) {
671 test.setTodir(getProject().resolveFile("."));
672 }
673
674 if (test.getOutfile() == null) {
675 test.setOutfile("TEST-" + test.getName());
676 }
677
678 // execute the test and get the return code
679 int exitValue = JUnitTestRunner.ERRORS;
680 boolean wasKilled = false;
681 if (!test.getFork()) {
682 exitValue = executeInVM(test);
683 } else {
684 ExecuteWatchdog watchdog = createWatchdog();
685 exitValue = executeAsForked(test, watchdog, null);
686 // null watchdog means no timeout, you'd better not check with null
687 if (watchdog != null) {
688 wasKilled = watchdog.killedProcess();
689 }
690 }
691 actOnTestResult(exitValue, wasKilled, test, "Test " + test.getName());
692 }
693
694 /**
695 * Execute a list of tests in a single forked Java VM.
696 */
697 protected void execute(List tests) throws BuildException {
698 JUnitTest test = null;
699 // Create a temporary file to pass the test cases to run to
700 // the runner (one test case per line)
701 File casesFile = createTempPropertiesFile("junittestcases");
702 PrintWriter writer = null;
703 try {
704 writer =
705 new PrintWriter(new BufferedWriter(new FileWriter(casesFile)));
706 Iterator iter = tests.iterator();
707 while (iter.hasNext()) {
708 test = (JUnitTest) iter.next();
709 writer.print(test.getName());
710 if (test.getTodir() == null) {
711 writer.print("," + getProject().resolveFile("."));
712 } else {
713 writer.print("," + test.getTodir());
714 }
715
716 if (test.getOutfile() == null) {
717 writer.println("," + "TEST-" + test.getName());
718 } else {
719 writer.println("," + test.getOutfile());
720 }
721 }
722 writer.flush();
723 writer.close();
724 writer = null;
725
726 // execute the test and get the return code
727 int exitValue = JUnitTestRunner.ERRORS;
728 boolean wasKilled = false;
729 ExecuteWatchdog watchdog = createWatchdog();
730 exitValue = executeAsForked(test, watchdog, casesFile);
731 // null watchdog means no timeout, you'd better not check
732 // with null
733 if (watchdog != null) {
734 wasKilled = watchdog.killedProcess();
735 }
736 actOnTestResult(exitValue, wasKilled, test, "Tests");
737 } catch(IOException e) {
738 log(e.toString(), Project.MSG_ERR);
739 throw new BuildException(e);
740 } finally {
741 if (writer != null) {
742 writer.close();
743 }
744
745 try {
746 casesFile.delete();
747 } catch (Exception e) {
748 log(e.toString(), Project.MSG_ERR);
749 }
750 }
751 }
752
753 /**
754 * Execute a testcase by forking a new JVM. The command will block until
755 * it finishes. To know if the process was destroyed or not, use the
756 * <tt>killedProcess()</tt> method of the watchdog class.
757 * @param test the testcase to execute.
758 * @param watchdog the watchdog in charge of cancelling the test if it
759 * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
760 * the test could probably hang forever.
761 * @throws BuildException in case of error creating a temporary property file,
762 * or if the junit process can not be forked
763 */
764 private int executeAsForked(JUnitTest test, ExecuteWatchdog watchdog,
765 File casesFile)
766 throws BuildException {
767
768 if (perm != null) {
769 log("Permissions ignored when running in forked mode!",
770 Project.MSG_WARN);
771 }
772
773 CommandlineJava cmd = null;
774 try {
775 cmd = (CommandlineJava) getCommandline().clone();
776 } catch (CloneNotSupportedException e) {
777 throw new BuildException(e);
778 }
779
780 cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
781 if (casesFile == null) {
782 cmd.createArgument().setValue(test.getName());
783 } else {
784 log("Running multiple tests in the same VM", Project.MSG_VERBOSE);
785 cmd.createArgument().setValue("testsfile=" + casesFile);
786 }
787
788 cmd.createArgument().setValue("filtertrace=" + test.getFiltertrace());
789 cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror());
790 cmd.createArgument().setValue("haltOnFailure="
791 + test.getHaltonfailure());
792 if (includeAntRuntime) {
793 Vector v = Execute.getProcEnvironment();
794 Enumeration e = v.elements();
795 while (e.hasMoreElements()) {
796 String s = (String) e.nextElement();
797 if (s.startsWith("CLASSPATH=")) {
798 cmd.createClasspath(getProject()).createPath()
799 .append(new Path(getProject(),
800 s.substring(10 // "CLASSPATH=".length()
801 )));
802 }
803 }
804 log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH",
805 Project.MSG_VERBOSE);
806 cmd.createClasspath(getProject()).createPath()
807 .append(antRuntimeClasses);
808 }
809
810 if (summary) {
811 log("Running " + test.getName(), Project.MSG_INFO);
812 cmd.createArgument()
813 .setValue("formatter"
814 + "=org.apache.tools.ant.taskdefs.optional.junit.SummaryJUnitResultFormatter");
815 }
816
817 cmd.createArgument().setValue("showoutput="
818 + String.valueOf(showOutput));
819
820 StringBuffer formatterArg = new StringBuffer(STRING_BUFFER_SIZE);
821 final FormatterElement[] feArray = mergeFormatters(test);
822 for (int i = 0; i < feArray.length; i++) {
823 FormatterElement fe = feArray[i];
824 if (fe.shouldUse(this)) {
825 formatterArg.append("formatter=");
826 formatterArg.append(fe.getClassname());
827 File outFile = getOutput(fe, test);
828 if (outFile != null) {
829 formatterArg.append(",");
830 formatterArg.append(outFile);
831 }
832 cmd.createArgument().setValue(formatterArg.toString());
833 formatterArg = new StringBuffer();
834 }
835 }
836
837
838 File propsFile = createTempPropertiesFile("junit");
839 cmd.createArgument().setValue("propsfile="
840 + propsFile.getAbsolutePath());
841 Hashtable p = getProject().getProperties();
842 Properties props = new Properties();
843 for (Enumeration e = p.keys(); e.hasMoreElements();) {
844 Object key = e.nextElement();
845 props.put(key, p.get(key));
846 }
847 try {
848 FileOutputStream outstream = new FileOutputStream(propsFile);
849 props.store(outstream, "Ant JUnitTask generated properties file");
850 outstream.close();
851 } catch (java.io.IOException e) {
852 propsFile.delete();
853 throw new BuildException("Error creating temporary properties "
854 + "file.", e, getLocation());
855 }
856
857 Execute execute = new Execute(new LogStreamHandler(this,
858 Project.MSG_INFO,
859 Project.MSG_WARN),
860 watchdog);
861 execute.setCommandline(cmd.getCommandline());
862 execute.setAntRun(getProject());
863 if (dir != null) {
864 execute.setWorkingDirectory(dir);
865 }
866
867 String[] environment = env.getVariables();
868 if (environment != null) {
869 for (int i = 0; i < environment.length; i++) {
870 log("Setting environment variable: " + environment[i],
871 Project.MSG_VERBOSE);
872 }
873 }
874 execute.setNewenvironment(newEnvironment);
875 execute.setEnvironment(environment);
876
877 log(cmd.describeCommand(), Project.MSG_VERBOSE);
878 int retVal;
879 try {
880 retVal = execute.execute();
881 } catch (IOException e) {
882 throw new BuildException("Process fork failed.", e, getLocation());
883 } finally {
884 if (watchdog != null && watchdog.killedProcess()) {
885 logTimeout(feArray, test);
886 }
887
888 if (!propsFile.delete()) {
889 throw new BuildException("Could not delete temporary "
890 + "properties file.");
891 }
892 }
893
894 return retVal;
895 }
896
897 /**
898 * Create a temporary file to pass the properties to a new process.
899 * Will auto-delete on (graceful) exit.
900 * The file will be in the project basedir unless tmpDir declares
901 * something else.
902 * @param prefix
903 * @return
904 */
905 private File createTempPropertiesFile(String prefix) {
906 File propsFile =
907 FileUtils.newFileUtils().createTempFile(prefix, ".properties",
908 tmpDir != null ? tmpDir : getProject().getBaseDir());
909 propsFile.deleteOnExit();
910 return propsFile;
911 }
912
913
914 /**
915 * Pass output sent to System.out to the TestRunner so it can
916 * collect ot for the formatters.
917 *
918 * @param output output coming from System.out
919 * @since Ant 1.5
920 */
921 protected void handleOutput(String output) {
922 if (runner != null) {
923 runner.handleOutput(output);
924 if (showOutput) {
925 super.handleOutput(output);
926 }
927 } else {
928 super.handleOutput(output);
929 }
930 }
931
932 /**
933 * @see Task#handleInput(byte[], int, int)
934 *
935 * @since Ant 1.6
936 */
937 protected int handleInput(byte[] buffer, int offset, int length)
938 throws IOException {
939 if (runner != null) {
940 return runner.handleInput(buffer, offset, length);
941 } else {
942 return super.handleInput(buffer, offset, length);
943 }
944 }
945
946
947 /**
948 * Pass output sent to System.out to the TestRunner so it can
949 * collect ot for the formatters.
950 *
951 * @param output output coming from System.out
952 * @since Ant 1.5.2
953 */
954 protected void handleFlush(String output) {
955 if (runner != null) {
956 runner.handleFlush(output);
957 if (showOutput) {
958 super.handleFlush(output);
959 }
960 } else {
961 super.handleFlush(output);
962 }
963 }
964
965 /**
966 * Pass output sent to System.err to the TestRunner so it can
967 * collect it for the formatters.
968 *
969 * @param output output coming from System.err
970 * @since Ant 1.5
971 */
972 public void handleErrorOutput(String output) {
973 if (runner != null) {
974 runner.handleErrorOutput(output);
975 if (showOutput) {
976 super.handleErrorOutput(output);
977 }
978 } else {
979 super.handleErrorOutput(output);
980 }
981 }
982
983
984 /**
985 * Pass output sent to System.err to the TestRunner so it can
986 * collect it for the formatters.
987 *
988 * @param output coming from System.err
989 * @since Ant 1.5.2
990 */
991 public void handleErrorFlush(String output) {
992 if (runner != null) {
993 runner.handleErrorFlush(output);
994 if (showOutput) {
995 super.handleErrorFlush(output);
996 }
997 } else {
998 super.handleErrorFlush(output);
999 }
1000 }
1001
1002 // in VM is not very nice since it could probably hang the
1003 // whole build. IMHO this method should be avoided and it would be best
1004 // to remove it in future versions. TBD. (SBa)
1005
1006 /**
1007 * Execute inside VM.
1008 * @param arg one JUnitTest
1009 * @throws BuildException under unspecified circumstances
1010 */
1011 private int executeInVM(JUnitTest arg) throws BuildException {
1012 JUnitTest test = (JUnitTest) arg.clone();
1013 test.setProperties(getProject().getProperties());
1014 if (dir != null) {
1015 log("dir attribute ignored if running in the same VM",
1016 Project.MSG_WARN);
1017 }
1018
1019 if (newEnvironment || null != env.getVariables()) {
1020 log("Changes to environment variables are ignored if running in "
1021 + "the same VM.", Project.MSG_WARN);
1022 }
1023
1024 if (getCommandline().getBootclasspath() != null) {
1025 log("bootclasspath is ignored if running in the same VM.",
1026 Project.MSG_WARN);
1027 }
1028
1029 CommandlineJava.SysProperties sysProperties =
1030 getCommandline().getSystemProperties();
1031 if (sysProperties != null) {
1032 sysProperties.setSystem();
1033 }
1034
1035 try {
1036 log("Using System properties " + System.getProperties(),
1037 Project.MSG_VERBOSE);
1038 createClassLoader();
1039 if (classLoader != null) {
1040 classLoader.setThreadContextLoader();
1041 }
1042 runner = new JUnitTestRunner(test, test.getHaltonerror(),
1043 test.getFiltertrace(),
1044 test.getHaltonfailure(), classLoader);
1045 if (summary) {
1046 log("Running " + test.getName(), Project.MSG_INFO);
1047
1048 SummaryJUnitResultFormatter f =
1049 new SummaryJUnitResultFormatter();
1050 f.setWithOutAndErr("withoutanderr"
1051 .equalsIgnoreCase(summaryValue));
1052 f.setOutput(getDefaultOutput());
1053 runner.addFormatter(f);
1054 }
1055
1056 runner.setPermissions(perm);
1057
1058 final FormatterElement[] feArray = mergeFormatters(test);
1059 for (int i = 0; i < feArray.length; i++) {
1060 FormatterElement fe = feArray[i];
1061 if (fe.shouldUse(this)) {
1062 File outFile = getOutput(fe, test);
1063 if (outFile != null) {
1064 fe.setOutfile(outFile);
1065 } else {
1066 fe.setOutput(getDefaultOutput());
1067 }
1068 runner.addFormatter(fe.createFormatter(classLoader));
1069 }
1070 }
1071
1072 runner.run();
1073 return runner.getRetCode();
1074 } finally {
1075 if (sysProperties != null) {
1076 sysProperties.restoreSystem();
1077 }
1078 if (classLoader != null) {
1079 classLoader.resetThreadContextLoader();
1080 if (!reloading) {
1081 classLoader.cleanup();
1082 }
1083 }
1084 }
1085 }
1086
1087 /**
1088 * @return <tt>null</tt> if there is a timeout value, otherwise the
1089 * watchdog instance.
1090 *
1091 * @throws BuildException under unspecified circumstances
1092 * @since Ant 1.2
1093 */
1094 protected ExecuteWatchdog createWatchdog() throws BuildException {
1095 if (timeout == null) {
1096 return null;
1097 }
1098 return new ExecuteWatchdog((long) timeout.intValue());
1099 }
1100
1101 /**
1102 * Get the default output for a formatter.
1103 *
1104 * @return default output stream for a formatter
1105 * @since Ant 1.3
1106 */
1107 protected OutputStream getDefaultOutput() {
1108 return new LogOutputStream(this, Project.MSG_INFO);
1109 }
1110
1111 /**
1112 * Merge all individual tests from the batchtest with all individual tests
1113 * and return an enumeration over all <tt>JUnitTest</tt>.
1114 *
1115 * @return enumeration over individual tests
1116 * @since Ant 1.3
1117 */
1118 protected Enumeration getIndividualTests() {
1119 final int count = batchTests.size();
1120 final Enumeration[] enums = new Enumeration[ count + 1];
1121 for (int i = 0; i < count; i++) {
1122 BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
1123 enums[i] = batchtest.elements();
1124 }
1125 enums[enums.length - 1] = tests.elements();
1126 return Enumerations.fromCompound(enums);
1127 }
1128
1129 /**
1130 * return an enumeration listing each test, then each batchtest
1131 * @return enumeration
1132 * @since Ant 1.3
1133 */
1134 protected Enumeration allTests() {
1135 Enumeration[] enums = {tests.elements(), batchTests.elements()};
1136 return Enumerations.fromCompound(enums);
1137 }
1138
1139 /**
1140 * @param test junit test
1141 * @return array of FormatterElement
1142 * @since Ant 1.3
1143 */
1144 private FormatterElement[] mergeFormatters(JUnitTest test) {
1145 Vector feVector = (Vector) formatters.clone();
1146 test.addFormattersTo(feVector);
1147 FormatterElement[] feArray = new FormatterElement[feVector.size()];
1148 feVector.copyInto(feArray);
1149 return feArray;
1150 }
1151
1152 /**
1153 * If the formatter sends output to a file, return that file.
1154 * null otherwise.
1155 * @param fe formatter element
1156 * @param test one JUnit test
1157 * @return file reference
1158 * @since Ant 1.3
1159 */
1160 protected File getOutput(FormatterElement fe, JUnitTest test) {
1161 if (fe.getUseFile()) {
1162 String base = test.getOutfile();
1163 if (base == null) {
1164 base = JUnitTestRunner.IGNORED_FILE_NAME;
1165 }
1166 String filename = base + fe.getExtension();
1167 File destFile = new File(test.getTodir(), filename);
1168 String absFilename = destFile.getAbsolutePath();
1169 return getProject().resolveFile(absFilename);
1170 }
1171 return null;
1172 }
1173
1174 /**
1175 * Search for the given resource and add the directory or archive
1176 * that contains it to the classpath.
1177 *
1178 * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
1179 * getResource doesn't contain the name of the archive.</p>
1180 *
1181 * @param resource resource that one wants to lookup
1182 * @since Ant 1.4
1183 */
1184 protected void addClasspathEntry(String resource) {
1185 /*
1186 * pre Ant 1.6 this method used to call getClass().getResource
1187 * while Ant 1.6 will call ClassLoader.getResource().
1188 *
1189 * The difference is that Class.getResource expects a leading
1190 * slash for "absolute" resources and will strip it before
1191 * delegating to ClassLoader.getResource - so we now have to
1192 * emulate Class's behavior.
1193 */
1194 if (resource.startsWith("/")) {
1195 resource = resource.substring(1);
1196 } else {
1197 resource = "org/apache/tools/ant/taskdefs/optional/junit/"
1198 + resource;
1199 }
1200
1201 File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
1202 resource);
1203 if (f != null) {
1204 log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
1205 antRuntimeClasses.createPath().setLocation(f);
1206 } else {
1207 log("Couldn\'t find " + resource, Project.MSG_DEBUG);
1208 }
1209 }
1210
1211 /**
1212 * Take care that some output is produced in report files if the
1213 * watchdog kills the test.
1214 *
1215 * @since Ant 1.5.2
1216 */
1217
1218 private void logTimeout(FormatterElement[] feArray, JUnitTest test) {
1219 createClassLoader();
1220 test.setCounts(1, 0, 1);
1221 test.setProperties(getProject().getProperties());
1222 for (int i = 0; i < feArray.length; i++) {
1223 FormatterElement fe = feArray[i];
1224 File outFile = getOutput(fe, test);
1225 JUnitResultFormatter formatter = fe.createFormatter(classLoader);
1226 if (outFile != null && formatter != null) {
1227 try {
1228 OutputStream out = new FileOutputStream(outFile);
1229 addTimeout(test, formatter, out);
1230 } catch (IOException e) {
1231 // ignore
1232 }
1233 }
1234 }
1235 if (summary) {
1236 SummaryJUnitResultFormatter f = new SummaryJUnitResultFormatter();
1237 f.setWithOutAndErr("withoutanderr".equalsIgnoreCase(summaryValue));
1238 addTimeout(test, f, getDefaultOutput());
1239 }
1240 }
1241
1242 /**
1243 * Adds the actual timeout to the formatter.
1244 * Only used from the logTimeout method.
1245 * @since Ant 1.6
1246 */
1247 private void addTimeout(JUnitTest test, JUnitResultFormatter formatter,
1248 OutputStream out) {
1249 formatter.setOutput(out);
1250 formatter.startTestSuite(test);
1251
1252 //the trick to integrating test output to the formatter, is to
1253 //create a special test class that asserts a timout occurred,
1254 //and tell the formatter that it raised.
1255 Test t = new Test() {
1256 public int countTestCases() { return 1; }
1257 public void run(TestResult r) {
1258 throw new AssertionFailedError("Timeout occurred");
1259 }
1260 };
1261 formatter.startTest(t);
1262 formatter.addError(t, new AssertionFailedError("Timeout occurred"));
1263 formatter.endTestSuite(test);
1264 }
1265
1266 /**
1267 * Creates and configures an AntClassLoader instance from the
1268 * nested classpath element.
1269 *
1270 * @since Ant 1.6
1271 */
1272 private void createClassLoader() {
1273 Path userClasspath = getCommandline().getClasspath();
1274 if (userClasspath != null) {
1275 if (reloading || classLoader == null) {
1276 Path classpath = (Path) userClasspath.clone();
1277 if (includeAntRuntime) {
1278 log("Implicitly adding " + antRuntimeClasses
1279 + " to CLASSPATH", Project.MSG_VERBOSE);
1280 classpath.append(antRuntimeClasses);
1281 }
1282 classLoader = getProject().createClassLoader(classpath);
1283 if (getClass().getClassLoader() != null
1284 && getClass().getClassLoader() != Project.class.getClassLoader()) {
1285 classLoader.setParent(getClass().getClassLoader());
1286 }
1287 classLoader.setParentFirst(false);
1288 classLoader.addJavaLibraries();
1289 log("Using CLASSPATH " + classLoader.getClasspath(),
1290 Project.MSG_VERBOSE);
1291 // make sure the test will be accepted as a TestCase
1292 classLoader.addSystemPackageRoot("junit");
1293 // will cause trouble in JDK 1.1 if omitted
1294 classLoader.addSystemPackageRoot("org.apache.tools.ant");
1295 }
1296 }
1297 }
1298
1299 /**
1300 * @since Ant 1.6.2
1301 */
1302 protected CommandlineJava getCommandline() {
1303 if (commandline == null) {
1304 commandline = new CommandlineJava();
1305 }
1306 return commandline;
1307 }
1308
1309 /**
1310 * Forked test support
1311 * @since Ant 1.6.2
1312 */
1313 private final class ForkedTestConfiguration {
1314 private boolean filterTrace;
1315 private boolean haltOnError;
1316 private boolean haltOnFailure;
1317 private String errorProperty;
1318 private String failureProperty;
1319
1320 /**
1321 * constructor for forked test configuration
1322 * @param filterTrace
1323 * @param haltOnError
1324 * @param haltOnFailure
1325 * @param errorProperty
1326 * @param failureProperty
1327 */
1328 ForkedTestConfiguration(boolean filterTrace, boolean haltOnError,
1329 boolean haltOnFailure, String errorProperty,
1330 String failureProperty) {
1331 this.filterTrace = filterTrace;
1332 this.haltOnError = haltOnError;
1333 this.haltOnFailure = haltOnFailure;
1334 this.errorProperty = errorProperty;
1335 this.failureProperty = failureProperty;
1336 }
1337
1338 /**
1339 * configure from a test; sets member variables to attributes of the test
1340 * @param test
1341 */
1342 ForkedTestConfiguration(JUnitTest test) {
1343 this(test.getFiltertrace(),
1344 test.getHaltonerror(),
1345 test.getHaltonfailure(),
1346 test.getErrorProperty(),
1347 test.getFailureProperty());
1348 }
1349
1350 /**
1351 * equality test checks all the member variables
1352 * @param other
1353 * @return true if everything is equal
1354 */
1355 public boolean equals(Object other) {
1356 if (other == null
1357 || other.getClass() != ForkedTestConfiguration.class) {
1358 return false;
1359 }
1360 ForkedTestConfiguration o = (ForkedTestConfiguration) other;
1361 return filterTrace == o.filterTrace
1362 && haltOnError == o.haltOnError
1363 && haltOnFailure == o.haltOnFailure
1364 && ((errorProperty == null && o.errorProperty == null)
1365 ||
1366 (errorProperty != null
1367 && errorProperty.equals(o.errorProperty)))
1368 && ((failureProperty == null && o.failureProperty == null)
1369 ||
1370 (failureProperty != null
1371 && failureProperty.equals(o.failureProperty)));
1372 }
1373
1374 /**
1375 * hashcode is based only on the boolean members, and returns a value
1376 * in the range 0-7.
1377 * @return
1378 */
1379 public int hashCode() {
1380 return (filterTrace ? 1 : 0)
1381 + (haltOnError ? 2 : 0)
1382 + (haltOnFailure ? 4 : 0);
1383 }
1384 }
1385
1386 /**
1387 * These are the different forking options
1388 * @since 1.6.2
1389 */
1390 public static final class ForkMode extends EnumeratedAttribute {
1391
1392 /**
1393 * fork once only
1394 */
1395 public static final String ONCE = "once";
1396 /**
1397 * fork once per test class
1398 */
1399 public static final String PER_TEST = "perTest";
1400 /**
1401 * fork once per batch of tests
1402 */
1403 public static final String PER_BATCH = "perBatch";
1404
1405 public ForkMode() {
1406 super();
1407 }
1408
1409 public ForkMode(String value) {
1410 super();
1411 setValue(value);
1412 }
1413
1414 public String[] getValues() {
1415 return new String[] {ONCE, PER_TEST, PER_BATCH};
1416 }
1417 }
1418
1419 /**
1420 * Executes all tests that don't need to be forked (or all tests
1421 * if the runIndividual argument is true. Returns a collection of
1422 * lists of tests that share the same VM configuration and haven't
1423 * been executed yet.
1424 *
1425 * @since 1.6.2
1426 */
1427 protected Collection executeOrQueue(Enumeration testList,
1428 boolean runIndividual) {
1429 Map testConfigurations = new HashMap();
1430 while (testList.hasMoreElements()) {
1431 JUnitTest test = (JUnitTest) testList.nextElement();
1432 if (test.shouldRun(getProject())) {
1433 if (runIndividual || !test.getFork()) {
1434 execute(test);
1435 } else {
1436 ForkedTestConfiguration c =
1437 new ForkedTestConfiguration(test);
1438 List l = (List) testConfigurations.get(c);
1439 if (l == null) {
1440 l = new ArrayList();
1441 testConfigurations.put(c, l);
1442 }
1443 l.add(test);
1444 }
1445 }
1446 }
1447 return testConfigurations.values();
1448 }
1449
1450 /**
1451 * Logs information about failed tests, potentially stops
1452 * processing (by throwing a BuildException) if a failure/error
1453 * occured or sets a property.
1454 *
1455 * @since Ant 1.6.2
1456 */
1457 protected void actOnTestResult(int exitValue, boolean wasKilled,
1458 JUnitTest test, String name) {
1459 // if there is an error/failure and that it should halt, stop
1460 // everything otherwise just log a statement
1461 boolean errorOccurredHere =
1462 exitValue == JUnitTestRunner.ERRORS || wasKilled;
1463 boolean failureOccurredHere =
1464 exitValue != JUnitTestRunner.SUCCESS || wasKilled;
1465 if (errorOccurredHere || failureOccurredHere) {
1466 if ((errorOccurredHere && test.getHaltonerror())
1467 || (failureOccurredHere && test.getHaltonfailure())) {
1468 throw new BuildException(name + " failed"
1469 + (wasKilled ? " (timeout)" : ""), getLocation());
1470 } else {
1471 log(name + " FAILED"
1472 + (wasKilled ? " (timeout)" : ""), Project.MSG_ERR);
1473 if (errorOccurredHere && test.getErrorProperty() != null) {
1474 getProject().setNewProperty(test.getErrorProperty(), "true");
1475 }
1476 if (failureOccurredHere && test.getFailureProperty() != null) {
1477 getProject().setNewProperty(test.getFailureProperty(), "true");
1478 }
1479 }
1480 }
1481 }
1482
1483}
Note: See TracBrowser for help on using the repository browser.