1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.woopi.ant.taskdefs.junit;
19 import java.io.BufferedReader;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.PrintStream;
25 import java.io.PrintWriter;
26 import java.io.StringReader;
27 import java.io.StringWriter;
28 import java.lang.reflect.Method;
29 import java.util.Enumeration;
30 import java.util.Hashtable;
31 import java.util.Properties;
32 import java.util.StringTokenizer;
33 import java.util.Vector;
34 import junit.framework.AssertionFailedError;
35 import junit.framework.Test;
36 import junit.framework.TestListener;
37 import junit.framework.TestResult;
38 import junit.framework.TestSuite;
39 import org.apache.tools.ant.BuildException;
40 import org.apache.tools.ant.Project;
41 import org.apache.tools.ant.types.Permissions;
42 import org.apache.tools.ant.util.StringUtils;
43 import org.apache.tools.ant.util.TeeOutputStream;
44
45 /***
46 * Simple Testrunner for JUnit that runs all tests of a testsuite.
47 *
48 * <p>This TestRunner expects a name of a TestCase class as its
49 * argument. If this class provides a static suite() method it will be
50 * called and the resulting Test will be run. So, the signature should be
51 * <pre><code>
52 * public static junit.framework.Test suite()
53 * </code></pre>
54 *
55 * <p> If no such method exists, all public methods starting with
56 * "test" and taking no argument will be run.
57 *
58 * <p> Summary output is generated at the end.
59 *
60 * @since Ant 1.2
61 */
62
63 public class JUnitTestRunner implements TestListener {
64
65 /***
66 * No problems with this test.
67 */
68 public static final int SUCCESS = 0;
69
70 /***
71 * Some tests failed.
72 */
73 public static final int FAILURES = 1;
74
75 /***
76 * An error occurred.
77 */
78 public static final int ERRORS = 2;
79
80 /***
81 * Holds the registered formatters.
82 */
83 private Vector formatters = new Vector();
84
85 /***
86 * Collects TestResults.
87 */
88 private TestResult res;
89
90 /***
91 * Do we filter junit.*.* stack frames out of failure and error exceptions.
92 */
93 private static boolean filtertrace = true;
94
95 /***
96 * Do we send output to System.out/.err in addition to the formatters?
97 */
98 private boolean showOutput = false;
99
100 /***
101 * The permissions set for the test to run.
102 */
103 private Permissions perm = null;
104
105 private static final String[] DEFAULT_TRACE_FILTERS = new String[] {
106 "junit.framework.TestCase",
107 "junit.framework.TestResult",
108 "junit.framework.TestSuite",
109 "junit.framework.Assert.",
110 "junit.swingui.TestRunner",
111 "junit.awtui.TestRunner",
112 "junit.textui.TestRunner",
113 "java.lang.reflect.Method.invoke(",
114 "org.apache.tools.ant."
115 };
116
117
118 /***
119 * Do we stop on errors.
120 */
121 private boolean haltOnError = false;
122
123 /***
124 * Do we stop on test failures.
125 */
126 private boolean haltOnFailure = false;
127
128 /***
129 * The corresponding testsuite.
130 */
131 private Test suite = null;
132
133 /***
134 * Exception caught in constructor.
135 */
136 private Exception exception;
137
138 /***
139 * Returncode
140 */
141 private int retCode = SUCCESS;
142
143 /***
144 * The TestSuite we are currently running.
145 */
146 private JUnitTest junitTest;
147
148 /*** output written during the test */
149 private PrintStream systemError;
150
151 /*** Error output during the test */
152 private PrintStream systemOut;
153
154 /*** is this runner running in forked mode? */
155 private boolean forked = false;
156
157 /*** Running more than one test suite? */
158 private static boolean multipleTests = false;
159
160 /***
161 * Constructor for fork=true or when the user hasn't specified a
162 * classpath.
163 */
164 public JUnitTestRunner(JUnitTest test, boolean haltOnError,
165 boolean filtertrace, boolean haltOnFailure) {
166 this(test, haltOnError, filtertrace, haltOnFailure, false);
167 }
168
169 /***
170 * Constructor for fork=true or when the user hasn't specified a
171 * classpath.
172 */
173 public JUnitTestRunner(JUnitTest test, boolean haltOnError,
174 boolean filtertrace, boolean haltOnFailure,
175 boolean showOutput) {
176 this(test, haltOnError, filtertrace, haltOnFailure, showOutput, null);
177 }
178
179 /***
180 * Constructor to use when the user has specified a classpath.
181 */
182 public JUnitTestRunner(JUnitTest test, boolean haltOnError,
183 boolean filtertrace, boolean haltOnFailure,
184 ClassLoader loader) {
185 this(test, haltOnError, filtertrace, haltOnFailure, false, loader);
186 }
187
188 /***
189 * Constructor to use when the user has specified a classpath.
190 */
191 public JUnitTestRunner(JUnitTest test, boolean haltOnError,
192 boolean filtertrace, boolean haltOnFailure,
193 boolean showOutput, ClassLoader loader) {
194 this.filtertrace = filtertrace;
195 this.junitTest = test;
196 this.haltOnError = haltOnError;
197 this.haltOnFailure = haltOnFailure;
198 this.showOutput = showOutput;
199
200 try {
201 Class testClass = null;
202 if (loader == null) {
203 testClass = Class.forName(test.getName());
204 } else {
205 testClass = Class.forName(test.getName(), true, loader);
206 }
207
208 Method suiteMethod = null;
209 try {
210
211 suiteMethod = testClass.getMethod("suite", new Class[0]);
212 } catch (NoSuchMethodException e) {
213
214
215 }
216 if (suiteMethod != null) {
217
218
219
220 suite = (Test) suiteMethod.invoke(null, new Class[0]);
221 } else {
222
223
224 suite = new TestSuite(testClass);
225 }
226
227 } catch (Exception e) {
228 retCode = ERRORS;
229 exception = e;
230 }
231 }
232
233 public void run() {
234 res = new TestResult();
235 res.addListener(this);
236 for (int i = 0; i < formatters.size(); i++) {
237 res.addListener((TestListener) formatters.elementAt(i));
238 }
239
240 long start = System.currentTimeMillis();
241
242 fireStartTestSuite();
243 if (exception != null) {
244 for (int i = 0; i < formatters.size(); i++) {
245 ((TestListener) formatters.elementAt(i)).addError(null,
246 exception);
247 }
248 junitTest.setCounts(1, 0, 1);
249 junitTest.setRunTime(0);
250 } else {
251
252
253 ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
254 systemError = new PrintStream(errStrm);
255
256 ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
257 systemOut = new PrintStream(outStrm);
258
259 PrintStream savedOut = null;
260 PrintStream savedErr = null;
261
262 if (forked) {
263 savedOut = System.out;
264 savedErr = System.err;
265 if (!showOutput) {
266 System.setOut(systemOut);
267 System.setErr(systemError);
268 } else {
269 System.setOut(new PrintStream(
270 new TeeOutputStream(savedOut, systemOut)
271 )
272 );
273 System.setErr(new PrintStream(
274 new TeeOutputStream(savedErr,
275 systemError)
276 )
277 );
278 }
279 perm = null;
280 } else {
281 if (perm != null) {
282 perm.setSecurityManager();
283 }
284 }
285
286
287 try {
288 suite.run(res);
289 } finally {
290 if (perm != null) {
291 perm.restoreSecurityManager();
292 }
293 if (savedOut != null) {
294 System.setOut(savedOut);
295 }
296 if (savedErr != null) {
297 System.setErr(savedErr);
298 }
299
300 systemError.close();
301 systemError = null;
302 systemOut.close();
303 systemOut = null;
304 sendOutAndErr(new String(outStrm.toByteArray()),
305 new String(errStrm.toByteArray()));
306
307 junitTest.setCounts(res.runCount(), res.failureCount(),
308 res.errorCount());
309 junitTest.setRunTime(System.currentTimeMillis() - start);
310 }
311 }
312 fireEndTestSuite();
313
314 if (retCode != SUCCESS || res.errorCount() != 0) {
315 retCode = ERRORS;
316 } else if (res.failureCount() != 0) {
317 retCode = FAILURES;
318 }
319 }
320
321 /***
322 * Returns what System.exit() would return in the standalone version.
323 *
324 * @return 2 if errors occurred, 1 if tests failed else 0.
325 */
326 public int getRetCode() {
327 return retCode;
328 }
329
330 /***
331 * Interface TestListener.
332 *
333 * <p>A new Test is started.
334 */
335 public void startTest(Test t) {
336 }
337
338 /***
339 * Interface TestListener.
340 *
341 * <p>A Test is finished.
342 */
343 public void endTest(Test test) {
344 }
345
346 /***
347 * Interface TestListener for JUnit <= 3.4.
348 *
349 * <p>A Test failed.
350 */
351 public void addFailure(Test test, Throwable t) {
352 if (haltOnFailure) {
353 res.stop();
354 }
355 }
356
357 /***
358 * Interface TestListener for JUnit > 3.4.
359 *
360 * <p>A Test failed.
361 */
362 public void addFailure(Test test, AssertionFailedError t) {
363 addFailure(test, (Throwable) t);
364 }
365
366 /***
367 * Interface TestListener.
368 *
369 * <p>An error occurred while running the test.
370 */
371 public void addError(Test test, Throwable t) {
372 if (haltOnError) {
373 res.stop();
374 }
375 }
376
377 /***
378 * Permissions for the test run.
379 * @since Ant 1.6
380 * @param permissions
381 */
382 public void setPermissions(Permissions permissions) {
383 perm = permissions;
384 }
385
386 protected void handleOutput(String output) {
387 if (systemOut != null) {
388 systemOut.print(output);
389 }
390 }
391
392 /***
393 * @see Task#handleInput(byte[], int, int)
394 *
395 * @since Ant 1.6
396 */
397 protected int handleInput(byte[] buffer, int offset, int length)
398 throws IOException {
399 return -1;
400 }
401
402 protected void handleErrorOutput(String output) {
403 if (systemError != null) {
404 systemError.print(output);
405 }
406 }
407
408 protected void handleFlush(String output) {
409 if (systemOut != null) {
410 systemOut.print(output);
411 }
412 }
413
414 protected void handleErrorFlush(String output) {
415 if (systemError != null) {
416 systemError.print(output);
417 }
418 }
419
420 private void sendOutAndErr(String out, String err) {
421 for (int i = 0; i < formatters.size(); i++) {
422 JUnitResultFormatter formatter =
423 ((JUnitResultFormatter) formatters.elementAt(i));
424
425 formatter.setSystemOutput(out);
426 formatter.setSystemError(err);
427 }
428 }
429
430 private void fireStartTestSuite() {
431 for (int i = 0; i < formatters.size(); i++) {
432 ((JUnitResultFormatter) formatters.elementAt(i))
433 .startTestSuite(junitTest);
434 }
435 }
436
437 private void fireEndTestSuite() {
438 for (int i = 0; i < formatters.size(); i++) {
439 ((JUnitResultFormatter) formatters.elementAt(i))
440 .endTestSuite(junitTest);
441 }
442 }
443
444 public void addFormatter(JUnitResultFormatter f) {
445 formatters.addElement(f);
446 }
447
448 /***
449 * Entry point for standalone (forked) mode.
450 *
451 * Parameters: testcaseclassname plus parameters in the format
452 * key=value, none of which is required.
453 *
454 * <table cols="4" border="1">
455 * <tr><th>key</th><th>description</th><th>default value</th></tr>
456 *
457 * <tr><td>haltOnError</td><td>halt test on
458 * errors?</td><td>false</td></tr>
459 *
460 * <tr><td>haltOnFailure</td><td>halt test on
461 * failures?</td><td>false</td></tr>
462 *
463 * <tr><td>formatter</td><td>A JUnitResultFormatter given as
464 * classname,filename. If filename is ommitted, System.out is
465 * assumed.</td><td>none</td></tr>
466 *
467 * <tr><td>showoutput</td><td>send output to System.err/.out as
468 * well as to the formatters?</td><td>false</td></tr>
469 *
470 * </table>
471 */
472 public static void main(String[] args) throws IOException {
473 boolean haltError = false;
474 boolean haltFail = false;
475 boolean stackfilter = true;
476 Properties props = new Properties();
477 boolean showOut = false;
478
479 if (args.length == 0) {
480 System.err.println("required argument TestClassName missing");
481 System.exit(ERRORS);
482 }
483
484 if (args[0].startsWith("testsfile=")) {
485 multipleTests = true;
486 args[0] = args[0].substring(10
487 }
488
489 for (int i = 1; i < args.length; i++) {
490 if (args[i].startsWith("haltOnError=")) {
491 haltError = Project.toBoolean(args[i].substring(12));
492 } else if (args[i].startsWith("haltOnFailure=")) {
493 haltFail = Project.toBoolean(args[i].substring(14));
494 } else if (args[i].startsWith("filtertrace=")) {
495 stackfilter = Project.toBoolean(args[i].substring(12));
496 } else if (args[i].startsWith("formatter=")) {
497 try {
498 createAndStoreFormatter(args[i].substring(10));
499 } catch (BuildException be) {
500 System.err.println(be.getMessage());
501 System.exit(ERRORS);
502 }
503 } else if (args[i].startsWith("propsfile=")) {
504 FileInputStream in = new FileInputStream(args[i]
505 .substring(10));
506 props.load(in);
507 in.close();
508 } else if (args[i].startsWith("showoutput=")) {
509 showOut = Project.toBoolean(args[i].substring(11));
510 }
511 }
512
513
514 Hashtable p = System.getProperties();
515 for (Enumeration e = p.keys(); e.hasMoreElements();) {
516 Object key = e.nextElement();
517 props.put(key, p.get(key));
518 }
519
520 int returnCode = SUCCESS;
521 if (multipleTests) {
522 try {
523 java.io.BufferedReader reader =
524 new java.io.BufferedReader(new java.io.FileReader(args[0]));
525 String testCaseName;
526 int code = 0;
527 boolean errorOccured = false;
528 boolean failureOccured = false;
529 String line = null;
530 while ((line = reader.readLine()) != null) {
531 StringTokenizer st = new StringTokenizer(line, ",");
532 testCaseName = st.nextToken();
533 JUnitTest t = new JUnitTest(testCaseName);
534 t.setTodir(new File(st.nextToken()));
535 t.setOutfile(st.nextToken());
536 code = launch(t, haltError, stackfilter, haltFail,
537 showOut, props);
538 errorOccured = (code == ERRORS);
539 failureOccured = (code != SUCCESS);
540 if (errorOccured || failureOccured ) {
541 if ((errorOccured && haltError)
542 || (failureOccured && haltFail)) {
543 System.exit(code);
544 } else {
545 if (code > returnCode) {
546 returnCode = code;
547 }
548 System.out.println("TEST " + t.getName()
549 + " FAILED");
550 }
551 }
552 }
553 } catch(IOException e) {
554 e.printStackTrace();
555 }
556 } else {
557 returnCode = launch(new JUnitTest(args[0]), haltError,
558 stackfilter, haltFail, showOut, props);
559 }
560
561 System.exit(returnCode);
562 }
563
564 private static Vector fromCmdLine = new Vector();
565
566 private static void transferFormatters(JUnitTestRunner runner,
567 JUnitTest test) {
568 for (int i = 0; i < fromCmdLine.size(); i++) {
569 FormatterElement fe = (FormatterElement) fromCmdLine.elementAt(i);
570 if (multipleTests && fe.getUseFile()) {
571 File destFile =
572 new File(test.getTodir(),
573 test.getOutfile() + fe.getExtension());
574 fe.setOutfile(destFile);
575 }
576 runner.addFormatter(fe.createFormatter());
577 }
578 }
579
580 /***
581 * Line format is: formatter=<classname>(,<pathname>)?
582 */
583 private static void createAndStoreFormatter(String line)
584 throws BuildException {
585 FormatterElement fe = new FormatterElement();
586 int pos = line.indexOf(',');
587 if (pos == -1) {
588 fe.setClassname(line);
589 fe.setUseFile(false);
590 } else {
591 fe.setClassname(line.substring(0, pos));
592 fe.setUseFile(true);
593 if (!multipleTests) {
594 fe.setOutfile(new File(line.substring(pos + 1)));
595 }
596 }
597 fromCmdLine.addElement(fe);
598 }
599
600 /***
601 * Returns a filtered stack trace.
602 * This is ripped out of junit.runner.BaseTestRunner.
603 */
604 public static String getFilteredTrace(Throwable t) {
605 String trace = StringUtils.getStackTrace(t);
606 return JUnitTestRunner.filterStack(trace);
607 }
608
609 /***
610 * Filters stack frames from internal JUnit and Ant classes
611 */
612 public static String filterStack(String stack) {
613 if (!filtertrace) {
614 return stack;
615 }
616 StringWriter sw = new StringWriter();
617 PrintWriter pw = new PrintWriter(sw);
618 StringReader sr = new StringReader(stack);
619 BufferedReader br = new BufferedReader(sr);
620
621 String line;
622 try {
623 while ((line = br.readLine()) != null) {
624 if (!filterLine(line)) {
625 pw.println(line);
626 }
627 }
628 } catch (Exception IOException) {
629 return stack;
630 }
631 return sw.toString();
632 }
633
634 private static boolean filterLine(String line) {
635 for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) {
636 if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) > 0) {
637 return true;
638 }
639 }
640 return false;
641 }
642
643 /***
644 * @since Ant 1.6.2
645 */
646 private static int launch(JUnitTest t, boolean haltError,
647 boolean stackfilter, boolean haltFail,
648 boolean showOut, Properties props) {
649 t.setProperties(props);
650 JUnitTestRunner runner =
651 new JUnitTestRunner(t, haltError, stackfilter, haltFail, showOut);
652 runner.forked = true;
653 transferFormatters(runner, t);
654
655 runner.run();
656 return runner.getRetCode();
657 }
658 }