View Javadoc

1   /*
2    * Copyright  2000-2004 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  
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.", // don't filter AssertionFailure
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                 // check if there is a suite method
211                 suiteMethod = testClass.getMethod("suite", new Class[0]);
212             } catch (NoSuchMethodException e) {
213                 // no appropriate suite method found. We don't report any
214                 // error here since it might be perfectly normal.
215             }
216             if (suiteMethod != null) {
217                 // if there is a suite method available, then try
218                 // to extract the suite from it. If there is an error
219                 // here it will be caught below and reported.
220                 suite = (Test) suiteMethod.invoke(null, new Class[0]);
221             } else {
222                 // try to extract a test suite automatically
223                 // this will generate warnings if the class is no suitable Test
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) { // had an exception in the constructor
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 &lt;= 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 &gt; 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 /* "testsfile=".length() */);
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         // Add/overlay system properties on the properties from the Ant project
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; // return the stack unfiltered
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 } // JUnitTestRunner