0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.tools.ant.taskdefs.optional.junit;
0020:
0021: import java.io.BufferedReader;
0022: import java.io.ByteArrayOutputStream;
0023: import java.io.File;
0024: import java.io.FileInputStream;
0025: import java.io.FileWriter;
0026: import java.io.IOException;
0027: import java.io.OutputStream;
0028: import java.io.PrintStream;
0029: import java.io.PrintWriter;
0030: import java.io.StringReader;
0031: import java.io.StringWriter;
0032: import java.lang.reflect.Method;
0033: import java.util.Enumeration;
0034: import java.util.Hashtable;
0035: import java.util.Properties;
0036: import java.util.StringTokenizer;
0037: import java.util.Vector;
0038: import junit.framework.AssertionFailedError;
0039: import junit.framework.Test;
0040: import junit.framework.TestFailure;
0041: import junit.framework.TestListener;
0042: import junit.framework.TestResult;
0043: import junit.framework.TestSuite;
0044: import org.apache.tools.ant.BuildException;
0045: import org.apache.tools.ant.Project;
0046: import org.apache.tools.ant.types.Permissions;
0047: import org.apache.tools.ant.util.StringUtils;
0048: import org.apache.tools.ant.util.TeeOutputStream;
0049:
0050: /**
0051: * Simple Testrunner for JUnit that runs all tests of a testsuite.
0052: *
0053: * <p>This TestRunner expects a name of a TestCase class as its
0054: * argument. If this class provides a static suite() method it will be
0055: * called and the resulting Test will be run. So, the signature should be
0056: * <pre><code>
0057: * public static junit.framework.Test suite()
0058: * </code></pre>
0059: *
0060: * <p> If no such method exists, all public methods starting with
0061: * "test" and taking no argument will be run.
0062: *
0063: * <p> Summary output is generated at the end.
0064: *
0065: * @since Ant 1.2
0066: */
0067:
0068: public class JUnitTestRunner implements TestListener,
0069: JUnitTaskMirror.JUnitTestRunnerMirror {
0070:
0071: /**
0072: * Holds the registered formatters.
0073: */
0074: private Vector formatters = new Vector();
0075:
0076: /**
0077: * Collects TestResults.
0078: */
0079: private TestResult res;
0080:
0081: /**
0082: * Do we filter junit.*.* stack frames out of failure and error exceptions.
0083: */
0084: private static boolean filtertrace = true;
0085:
0086: /**
0087: * Do we send output to System.out/.err in addition to the formatters?
0088: */
0089: private boolean showOutput = false;
0090:
0091: private boolean outputToFormatters = true;
0092:
0093: /**
0094: * The permissions set for the test to run.
0095: */
0096: private Permissions perm = null;
0097:
0098: private static final String[] DEFAULT_TRACE_FILTERS = new String[] {
0099: "junit.framework.TestCase",
0100: "junit.framework.TestResult",
0101: "junit.framework.TestSuite",
0102: "junit.framework.Assert.", // don't filter AssertionFailure
0103: "junit.swingui.TestRunner", "junit.awtui.TestRunner",
0104: "junit.textui.TestRunner",
0105: "java.lang.reflect.Method.invoke(", "sun.reflect.",
0106: "org.apache.tools.ant.",
0107: // JUnit 4 support:
0108: "org.junit.", "junit.framework.JUnit4TestAdapter",
0109: // See wrapListener for reason:
0110: "Caused by: java.lang.AssertionError", " more", };
0111:
0112: /**
0113: * Do we stop on errors.
0114: */
0115: private boolean haltOnError = false;
0116:
0117: /**
0118: * Do we stop on test failures.
0119: */
0120: private boolean haltOnFailure = false;
0121:
0122: /**
0123: * Returncode
0124: */
0125: private int retCode = SUCCESS;
0126:
0127: /**
0128: * The TestSuite we are currently running.
0129: */
0130: private JUnitTest junitTest;
0131:
0132: /** output written during the test */
0133: private PrintStream systemError;
0134:
0135: /** Error output during the test */
0136: private PrintStream systemOut;
0137:
0138: /** is this runner running in forked mode? */
0139: private boolean forked = false;
0140:
0141: /** Running more than one test suite? */
0142: private static boolean multipleTests = false;
0143:
0144: /** ClassLoader passed in in non-forked mode. */
0145: private ClassLoader loader;
0146:
0147: /** Do we print TestListener events? */
0148: private boolean logTestListenerEvents = false;
0149:
0150: /** Turned on if we are using JUnit 4 for this test suite. see #38811 */
0151: private boolean junit4;
0152:
0153: /**
0154: * The file used to indicate that the build crashed.
0155: * File will be empty in case the build did not crash.
0156: */
0157: private static String crashFile = null;
0158:
0159: /**
0160: * Constructor for fork=true or when the user hasn't specified a
0161: * classpath.
0162: * @param test the test to run.
0163: * @param haltOnError whether to stop the run if an error is found.
0164: * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
0165: * @param haltOnFailure whether to stop the run if failure is found.
0166: */
0167: public JUnitTestRunner(JUnitTest test, boolean haltOnError,
0168: boolean filtertrace, boolean haltOnFailure) {
0169: this (test, haltOnError, filtertrace, haltOnFailure, false);
0170: }
0171:
0172: /**
0173: * Constructor for fork=true or when the user hasn't specified a
0174: * classpath.
0175: * @param test the test to run.
0176: * @param haltOnError whether to stop the run if an error is found.
0177: * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
0178: * @param haltOnFailure whether to stop the run if failure is found.
0179: * @param showOutput whether to send output to System.out/.err as well as formatters.
0180: */
0181: public JUnitTestRunner(JUnitTest test, boolean haltOnError,
0182: boolean filtertrace, boolean haltOnFailure,
0183: boolean showOutput) {
0184: this (test, haltOnError, filtertrace, haltOnFailure, showOutput,
0185: false);
0186: }
0187:
0188: /**
0189: * Constructor for fork=true or when the user hasn't specified a
0190: * classpath.
0191: * @param test the test to run.
0192: * @param haltOnError whether to stop the run if an error is found.
0193: * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
0194: * @param haltOnFailure whether to stop the run if failure is found.
0195: * @param showOutput whether to send output to System.out/.err as well as formatters.
0196: * @param logTestListenerEvents whether to print TestListener events.
0197: * @since Ant 1.7
0198: */
0199: public JUnitTestRunner(JUnitTest test, boolean haltOnError,
0200: boolean filtertrace, boolean haltOnFailure,
0201: boolean showOutput, boolean logTestListenerEvents) {
0202: this (test, haltOnError, filtertrace, haltOnFailure, showOutput,
0203: logTestListenerEvents, null);
0204: }
0205:
0206: /**
0207: * Constructor to use when the user has specified a classpath.
0208: * @param test the test to run.
0209: * @param haltOnError whether to stop the run if an error is found.
0210: * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
0211: * @param haltOnFailure whether to stop the run if failure is found.
0212: * @param loader the classloader to use running the test.
0213: */
0214: public JUnitTestRunner(JUnitTest test, boolean haltOnError,
0215: boolean filtertrace, boolean haltOnFailure,
0216: ClassLoader loader) {
0217: this (test, haltOnError, filtertrace, haltOnFailure, false,
0218: loader);
0219: }
0220:
0221: /**
0222: * Constructor to use when the user has specified a classpath.
0223: * @param test the test to run.
0224: * @param haltOnError whether to stop the run if an error is found.
0225: * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
0226: * @param haltOnFailure whether to stop the run if failure is found.
0227: * @param showOutput whether to send output to System.out/.err as well as formatters.
0228: * @param loader the classloader to use running the test.
0229: */
0230: public JUnitTestRunner(JUnitTest test, boolean haltOnError,
0231: boolean filtertrace, boolean haltOnFailure,
0232: boolean showOutput, ClassLoader loader) {
0233: this (test, haltOnError, filtertrace, haltOnFailure, showOutput,
0234: false, loader);
0235: }
0236:
0237: /**
0238: * Constructor to use when the user has specified a classpath.
0239: * @param test the test to run.
0240: * @param haltOnError whether to stop the run if an error is found.
0241: * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
0242: * @param haltOnFailure whether to stop the run if failure is found.
0243: * @param showOutput whether to send output to System.out/.err as well as formatters.
0244: * @param logTestListenerEvents whether to print TestListener events.
0245: * @param loader the classloader to use running the test.
0246: * @since Ant 1.7
0247: */
0248: public JUnitTestRunner(JUnitTest test, boolean haltOnError,
0249: boolean filtertrace, boolean haltOnFailure,
0250: boolean showOutput, boolean logTestListenerEvents,
0251: ClassLoader loader) {
0252: JUnitTestRunner.filtertrace = filtertrace;
0253: this .junitTest = test;
0254: this .haltOnError = haltOnError;
0255: this .haltOnFailure = haltOnFailure;
0256: this .showOutput = showOutput;
0257: this .logTestListenerEvents = logTestListenerEvents;
0258: this .loader = loader;
0259: }
0260:
0261: private PrintStream savedOut = null;
0262:
0263: /**
0264: * Run the test.
0265: */
0266: public void run() {
0267: res = new TestResult();
0268: res.addListener(wrapListener(this ));
0269: for (int i = 0; i < formatters.size(); i++) {
0270: res.addListener(wrapListener((TestListener) formatters
0271: .elementAt(i)));
0272: }
0273:
0274: ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
0275: systemError = new PrintStream(errStrm);
0276:
0277: ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
0278: systemOut = new PrintStream(outStrm);
0279:
0280: PrintStream savedErr = null;
0281:
0282: if (forked) {
0283: if (!outputToFormatters) {
0284: if (!showOutput) {
0285: savedOut = System.out;
0286: savedErr = System.err;
0287: System.setOut(new PrintStream(new OutputStream() {
0288: public void write(int b) {
0289: }
0290: }));
0291: System.setErr(new PrintStream(new OutputStream() {
0292: public void write(int b) {
0293: }
0294: }));
0295: }
0296: } else {
0297: savedOut = System.out;
0298: savedErr = System.err;
0299: if (!showOutput) {
0300: System.setOut(systemOut);
0301: System.setErr(systemError);
0302: } else {
0303: System.setOut(new PrintStream(new TeeOutputStream(
0304: savedOut, systemOut)));
0305: System.setErr(new PrintStream(new TeeOutputStream(
0306: savedErr, systemError)));
0307: }
0308: perm = null;
0309: }
0310: } else {
0311: if (perm != null) {
0312: perm.setSecurityManager();
0313: }
0314: }
0315:
0316: Test suite = null;
0317: Throwable exception = null;
0318: boolean startTestSuiteSuccess = false;
0319:
0320: try {
0321:
0322: try {
0323: Class testClass = null;
0324: if (loader == null) {
0325: testClass = Class.forName(junitTest.getName());
0326: } else {
0327: testClass = Class.forName(junitTest.getName(),
0328: true, loader);
0329: }
0330:
0331: // check for a static suite method first, even when using
0332: // JUnit 4
0333: Method suiteMethod = null;
0334: try {
0335: // check if there is a suite method
0336: suiteMethod = testClass.getMethod("suite",
0337: new Class[0]);
0338: } catch (NoSuchMethodException e) {
0339: // no appropriate suite method found. We don't report any
0340: // error here since it might be perfectly normal.
0341: }
0342:
0343: if (suiteMethod != null) {
0344: // if there is a suite method available, then try
0345: // to extract the suite from it. If there is an error
0346: // here it will be caught below and reported.
0347: suite = (Test) suiteMethod.invoke(null,
0348: new Class[0]);
0349:
0350: } else {
0351: Class junit4TestAdapterClass = null;
0352:
0353: // Check for JDK 5 first. Will *not* help on JDK 1.4
0354: // if only junit-4.0.jar in CP because in that case
0355: // linkage of whole task will already have failed! But
0356: // will help if CP has junit-3.8.2.jar:junit-4.0.jar.
0357:
0358: // In that case first C.fN will fail with CNFE and we
0359: // will avoid UnsupportedClassVersionError.
0360:
0361: try {
0362: Class
0363: .forName("java.lang.annotation.Annotation");
0364: if (loader == null) {
0365: junit4TestAdapterClass = Class
0366: .forName("junit.framework.JUnit4TestAdapter");
0367: } else {
0368: junit4TestAdapterClass = Class
0369: .forName(
0370: "junit.framework.JUnit4TestAdapter",
0371: true, loader);
0372: }
0373: } catch (ClassNotFoundException e) {
0374: // OK, fall back to JUnit 3.
0375: }
0376: junit4 = junit4TestAdapterClass != null;
0377:
0378: if (junit4) {
0379: // Let's use it!
0380: suite = (Test) junit4TestAdapterClass
0381: .getConstructor(
0382: new Class[] { Class.class })
0383: .newInstance(new Object[] { testClass });
0384: } else {
0385: // Use JUnit 3.
0386:
0387: // try to extract a test suite automatically this
0388: // will generate warnings if the class is no
0389: // suitable Test
0390: suite = new TestSuite(testClass);
0391: }
0392:
0393: }
0394:
0395: } catch (Throwable e) {
0396: retCode = ERRORS;
0397: exception = e;
0398: }
0399:
0400: long start = System.currentTimeMillis();
0401:
0402: fireStartTestSuite();
0403: startTestSuiteSuccess = true;
0404: if (exception != null) { // had an exception constructing suite
0405: for (int i = 0; i < formatters.size(); i++) {
0406: ((TestListener) formatters.elementAt(i)).addError(
0407: null, exception);
0408: }
0409: junitTest.setCounts(1, 0, 1);
0410: junitTest.setRunTime(0);
0411: } else {
0412: try {
0413: logTestListenerEvent("tests to run: "
0414: + suite.countTestCases());
0415: suite.run(res);
0416: } finally {
0417: if (junit4) {
0418: int[] cnts = findJUnit4FailureErrorCount(res);
0419: junitTest.setCounts(res.runCount(), cnts[0],
0420: cnts[1]);
0421: } else {
0422: junitTest.setCounts(res.runCount(), res
0423: .failureCount(), res.errorCount());
0424: }
0425: junitTest.setRunTime(System.currentTimeMillis()
0426: - start);
0427: }
0428: }
0429: } finally {
0430: if (perm != null) {
0431: perm.restoreSecurityManager();
0432: }
0433: if (savedOut != null) {
0434: System.setOut(savedOut);
0435: }
0436: if (savedErr != null) {
0437: System.setErr(savedErr);
0438: }
0439:
0440: systemError.close();
0441: systemError = null;
0442: systemOut.close();
0443: systemOut = null;
0444: if (startTestSuiteSuccess) {
0445: sendOutAndErr(new String(outStrm.toByteArray()),
0446: new String(errStrm.toByteArray()));
0447: }
0448: }
0449: fireEndTestSuite();
0450:
0451: if (retCode != SUCCESS || res.errorCount() != 0) {
0452: retCode = ERRORS;
0453: } else if (res.failureCount() != 0) {
0454: retCode = FAILURES;
0455: }
0456: }
0457:
0458: /**
0459: * Returns what System.exit() would return in the standalone version.
0460: *
0461: * @return 2 if errors occurred, 1 if tests failed else 0.
0462: */
0463: public int getRetCode() {
0464: return retCode;
0465: }
0466:
0467: /**
0468: * Interface TestListener.
0469: *
0470: * <p>A new Test is started.
0471: * @param t the test.
0472: */
0473: public void startTest(Test t) {
0474: String testName = JUnitVersionHelper.getTestCaseName(t);
0475: logTestListenerEvent("startTest(" + testName + ")");
0476: }
0477:
0478: /**
0479: * Interface TestListener.
0480: *
0481: * <p>A Test is finished.
0482: * @param test the test.
0483: */
0484: public void endTest(Test test) {
0485: String testName = JUnitVersionHelper.getTestCaseName(test);
0486: logTestListenerEvent("endTest(" + testName + ")");
0487: }
0488:
0489: private void logTestListenerEvent(String msg) {
0490: PrintStream out = savedOut != null ? savedOut : System.out;
0491: if (logTestListenerEvents) {
0492: out.flush();
0493: out.println(JUnitTask.TESTLISTENER_PREFIX + msg);
0494: out.flush();
0495: }
0496: }
0497:
0498: /**
0499: * Interface TestListener for JUnit <= 3.4.
0500: *
0501: * <p>A Test failed.
0502: * @param test the test.
0503: * @param t the exception thrown by the test.
0504: */
0505: public void addFailure(Test test, Throwable t) {
0506: String testName = JUnitVersionHelper.getTestCaseName(test);
0507: logTestListenerEvent("addFailure(" + testName + ", "
0508: + t.getMessage() + ")");
0509: if (haltOnFailure) {
0510: res.stop();
0511: }
0512: }
0513:
0514: /**
0515: * Interface TestListener for JUnit > 3.4.
0516: *
0517: * <p>A Test failed.
0518: * @param test the test.
0519: * @param t the assertion thrown by the test.
0520: */
0521: public void addFailure(Test test, AssertionFailedError t) {
0522: addFailure(test, (Throwable) t);
0523: }
0524:
0525: /**
0526: * Interface TestListener.
0527: *
0528: * <p>An error occurred while running the test.
0529: * @param test the test.
0530: * @param t the error thrown by the test.
0531: */
0532: public void addError(Test test, Throwable t) {
0533: String testName = JUnitVersionHelper.getTestCaseName(test);
0534: logTestListenerEvent("addError(" + testName + ", "
0535: + t.getMessage() + ")");
0536: if (haltOnError) {
0537: res.stop();
0538: }
0539: }
0540:
0541: /**
0542: * Permissions for the test run.
0543: * @since Ant 1.6
0544: * @param permissions the permissions to use.
0545: */
0546: public void setPermissions(Permissions permissions) {
0547: perm = permissions;
0548: }
0549:
0550: /**
0551: * Handle a string destined for standard output.
0552: * @param output the string to output
0553: */
0554: public void handleOutput(String output) {
0555: if (!logTestListenerEvents
0556: && output.startsWith(JUnitTask.TESTLISTENER_PREFIX)) {
0557: // ignore
0558: } else if (systemOut != null) {
0559: systemOut.print(output);
0560: }
0561: }
0562:
0563: /**
0564: * Handle input.
0565: * @param buffer not used.
0566: * @param offset not used.
0567: * @param length not used.
0568: * @return -1 always.
0569: * @throws IOException never.
0570: * @see org.apache.tools.ant.Task#handleInput(byte[], int, int)
0571: *
0572: * @since Ant 1.6
0573: */
0574: public int handleInput(byte[] buffer, int offset, int length)
0575: throws IOException {
0576: return -1;
0577: }
0578:
0579: /** {@inheritDoc}. */
0580: public void handleErrorOutput(String output) {
0581: if (systemError != null) {
0582: systemError.print(output);
0583: }
0584: }
0585:
0586: /** {@inheritDoc}. */
0587: public void handleFlush(String output) {
0588: if (systemOut != null) {
0589: systemOut.print(output);
0590: }
0591: }
0592:
0593: /** {@inheritDoc}. */
0594: public void handleErrorFlush(String output) {
0595: if (systemError != null) {
0596: systemError.print(output);
0597: }
0598: }
0599:
0600: private void sendOutAndErr(String out, String err) {
0601: for (int i = 0; i < formatters.size(); i++) {
0602: JUnitResultFormatter formatter = ((JUnitResultFormatter) formatters
0603: .elementAt(i));
0604:
0605: formatter.setSystemOutput(out);
0606: formatter.setSystemError(err);
0607: }
0608: }
0609:
0610: private void fireStartTestSuite() {
0611: for (int i = 0; i < formatters.size(); i++) {
0612: ((JUnitResultFormatter) formatters.elementAt(i))
0613: .startTestSuite(junitTest);
0614: }
0615: }
0616:
0617: private void fireEndTestSuite() {
0618: for (int i = 0; i < formatters.size(); i++) {
0619: ((JUnitResultFormatter) formatters.elementAt(i))
0620: .endTestSuite(junitTest);
0621: }
0622: }
0623:
0624: /**
0625: * Add a formatter.
0626: * @param f the formatter to add.
0627: */
0628: public void addFormatter(JUnitResultFormatter f) {
0629: formatters.addElement(f);
0630: }
0631:
0632: /** {@inheritDoc}. */
0633: public void addFormatter(
0634: JUnitTaskMirror.JUnitResultFormatterMirror f) {
0635: formatters.addElement((JUnitResultFormatter) f);
0636: }
0637:
0638: /**
0639: * Entry point for standalone (forked) mode.
0640: *
0641: * Parameters: testcaseclassname plus parameters in the format
0642: * key=value, none of which is required.
0643: *
0644: * <table cols="4" border="1">
0645: * <tr><th>key</th><th>description</th><th>default value</th></tr>
0646: *
0647: * <tr><td>haltOnError</td><td>halt test on
0648: * errors?</td><td>false</td></tr>
0649: *
0650: * <tr><td>haltOnFailure</td><td>halt test on
0651: * failures?</td><td>false</td></tr>
0652: *
0653: * <tr><td>formatter</td><td>A JUnitResultFormatter given as
0654: * classname,filename. If filename is ommitted, System.out is
0655: * assumed.</td><td>none</td></tr>
0656: *
0657: * <tr><td>showoutput</td><td>send output to System.err/.out as
0658: * well as to the formatters?</td><td>false</td></tr>
0659: *
0660: * <tr><td>logtestlistenerevents</td><td>log TestListener events to
0661: * System.out.</td><td>false</td></tr>
0662: *
0663: * </table>
0664: * @param args the command line arguments.
0665: * @throws IOException on error.
0666: */
0667: public static void main(String[] args) throws IOException {
0668: boolean haltError = false;
0669: boolean haltFail = false;
0670: boolean stackfilter = true;
0671: Properties props = new Properties();
0672: boolean showOut = false;
0673: boolean outputToFormat = true;
0674: boolean logTestListenerEvents = false;
0675:
0676: if (args.length == 0) {
0677: System.err
0678: .println("required argument TestClassName missing");
0679: System.exit(ERRORS);
0680: }
0681:
0682: if (args[0].startsWith(Constants.TESTSFILE)) {
0683: multipleTests = true;
0684: args[0] = args[0].substring(Constants.TESTSFILE.length());
0685: }
0686:
0687: for (int i = 1; i < args.length; i++) {
0688: if (args[i].startsWith(Constants.HALT_ON_ERROR)) {
0689: haltError = Project.toBoolean(args[i]
0690: .substring(Constants.HALT_ON_ERROR.length()));
0691: } else if (args[i].startsWith(Constants.HALT_ON_FAILURE)) {
0692: haltFail = Project.toBoolean(args[i]
0693: .substring(Constants.HALT_ON_FAILURE.length()));
0694: } else if (args[i].startsWith(Constants.FILTERTRACE)) {
0695: stackfilter = Project.toBoolean(args[i]
0696: .substring(Constants.FILTERTRACE.length()));
0697: } else if (args[i].startsWith(Constants.CRASHFILE)) {
0698: crashFile = args[i].substring(Constants.CRASHFILE
0699: .length());
0700: registerTestCase(Constants.BEFORE_FIRST_TEST);
0701: } else if (args[i].startsWith(Constants.FORMATTER)) {
0702: try {
0703: createAndStoreFormatter(args[i]
0704: .substring(Constants.FORMATTER.length()));
0705: } catch (BuildException be) {
0706: System.err.println(be.getMessage());
0707: System.exit(ERRORS);
0708: }
0709: } else if (args[i].startsWith(Constants.PROPSFILE)) {
0710: FileInputStream in = new FileInputStream(args[i]
0711: .substring(Constants.PROPSFILE.length()));
0712: props.load(in);
0713: in.close();
0714: } else if (args[i].startsWith(Constants.SHOWOUTPUT)) {
0715: showOut = Project.toBoolean(args[i]
0716: .substring(Constants.SHOWOUTPUT.length()));
0717: } else if (args[i]
0718: .startsWith(Constants.LOGTESTLISTENEREVENTS)) {
0719: logTestListenerEvents = Project.toBoolean(args[i]
0720: .substring(Constants.LOGTESTLISTENEREVENTS
0721: .length()));
0722: } else if (args[i]
0723: .startsWith(Constants.OUTPUT_TO_FORMATTERS)) {
0724: outputToFormat = Project.toBoolean(args[i]
0725: .substring(Constants.OUTPUT_TO_FORMATTERS
0726: .length()));
0727: }
0728: }
0729:
0730: // Add/overlay system properties on the properties from the Ant project
0731: Hashtable p = System.getProperties();
0732: for (Enumeration e = p.keys(); e.hasMoreElements();) {
0733: Object key = e.nextElement();
0734: props.put(key, p.get(key));
0735: }
0736:
0737: int returnCode = SUCCESS;
0738: if (multipleTests) {
0739: try {
0740: java.io.BufferedReader reader = new java.io.BufferedReader(
0741: new java.io.FileReader(args[0]));
0742: String testCaseName;
0743: int code = 0;
0744: boolean errorOccurred = false;
0745: boolean failureOccurred = false;
0746: String line = null;
0747: while ((line = reader.readLine()) != null) {
0748: StringTokenizer st = new StringTokenizer(line, ",");
0749: testCaseName = st.nextToken();
0750: JUnitTest t = new JUnitTest(testCaseName);
0751: t.setTodir(new File(st.nextToken()));
0752: t.setOutfile(st.nextToken());
0753: code = launch(t, haltError, stackfilter, haltFail,
0754: showOut, outputToFormat,
0755: logTestListenerEvents, props);
0756: errorOccurred = (code == ERRORS);
0757: failureOccurred = (code != SUCCESS);
0758: if (errorOccurred || failureOccurred) {
0759: if ((errorOccurred && haltError)
0760: || (failureOccurred && haltFail)) {
0761: registerNonCrash();
0762: System.exit(code);
0763: } else {
0764: if (code > returnCode) {
0765: returnCode = code;
0766: }
0767: System.out.println("TEST " + t.getName()
0768: + " FAILED");
0769: }
0770: }
0771: }
0772: } catch (IOException e) {
0773: e.printStackTrace();
0774: }
0775: } else {
0776: returnCode = launch(new JUnitTest(args[0]), haltError,
0777: stackfilter, haltFail, showOut, outputToFormat,
0778: logTestListenerEvents, props);
0779: }
0780:
0781: registerNonCrash();
0782: System.exit(returnCode);
0783: }
0784:
0785: private static Vector fromCmdLine = new Vector();
0786:
0787: private static void transferFormatters(JUnitTestRunner runner,
0788: JUnitTest test) {
0789: runner.addFormatter(new JUnitResultFormatter() {
0790:
0791: public void startTestSuite(JUnitTest suite)
0792: throws BuildException {
0793: }
0794:
0795: public void endTestSuite(JUnitTest suite)
0796: throws BuildException {
0797: }
0798:
0799: public void setOutput(OutputStream out) {
0800: }
0801:
0802: public void setSystemOutput(String out) {
0803: }
0804:
0805: public void setSystemError(String err) {
0806: }
0807:
0808: public void addError(Test arg0, Throwable arg1) {
0809: }
0810:
0811: public void addFailure(Test arg0, AssertionFailedError arg1) {
0812: }
0813:
0814: public void endTest(Test arg0) {
0815: }
0816:
0817: public void startTest(Test arg0) {
0818: registerTestCase(JUnitVersionHelper
0819: .getTestCaseName(arg0));
0820: }
0821: });
0822: for (int i = 0; i < fromCmdLine.size(); i++) {
0823: FormatterElement fe = (FormatterElement) fromCmdLine
0824: .elementAt(i);
0825: if (multipleTests && fe.getUseFile()) {
0826: File destFile = new File(test.getTodir(), test
0827: .getOutfile()
0828: + fe.getExtension());
0829: fe.setOutfile(destFile);
0830: }
0831: runner.addFormatter((JUnitResultFormatter) fe
0832: .createFormatter());
0833: }
0834: }
0835:
0836: /**
0837: * Line format is: formatter=<classname>(,<pathname>)?
0838: */
0839: private static void createAndStoreFormatter(String line)
0840: throws BuildException {
0841: FormatterElement fe = new FormatterElement();
0842: int pos = line.indexOf(',');
0843: if (pos == -1) {
0844: fe.setClassname(line);
0845: fe.setUseFile(false);
0846: } else {
0847: fe.setClassname(line.substring(0, pos));
0848: fe.setUseFile(true);
0849: if (!multipleTests) {
0850: fe.setOutfile(new File(line.substring(pos + 1)));
0851: } else {
0852: int fName = line.indexOf(IGNORED_FILE_NAME);
0853: if (fName > -1) {
0854: fe.setExtension(line.substring(fName
0855: + IGNORED_FILE_NAME.length()));
0856: }
0857: }
0858: }
0859: fromCmdLine.addElement(fe);
0860: }
0861:
0862: /**
0863: * Returns a filtered stack trace.
0864: * This is ripped out of junit.runner.BaseTestRunner.
0865: * @param t the exception to filter.
0866: * @return the filtered stack trace.
0867: */
0868: public static String getFilteredTrace(Throwable t) {
0869: String trace = StringUtils.getStackTrace(t);
0870: return JUnitTestRunner.filterStack(trace);
0871: }
0872:
0873: /**
0874: * Filters stack frames from internal JUnit and Ant classes
0875: * @param stack the stack trace to filter.
0876: * @return the filtered stack.
0877: */
0878: public static String filterStack(String stack) {
0879: if (!filtertrace) {
0880: return stack;
0881: }
0882: StringWriter sw = new StringWriter();
0883: PrintWriter pw = new PrintWriter(sw);
0884: StringReader sr = new StringReader(stack);
0885: BufferedReader br = new BufferedReader(sr);
0886:
0887: String line;
0888: try {
0889: while ((line = br.readLine()) != null) {
0890: if (!filterLine(line)) {
0891: pw.println(line);
0892: }
0893: }
0894: } catch (Exception e) {
0895: return stack; // return the stack unfiltered
0896: }
0897: return sw.toString();
0898: }
0899:
0900: private static boolean filterLine(String line) {
0901: for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) {
0902: if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) != -1) {
0903: return true;
0904: }
0905: }
0906: return false;
0907: }
0908:
0909: /**
0910: * @since Ant 1.6.2
0911: */
0912: private static int launch(JUnitTest t, boolean haltError,
0913: boolean stackfilter, boolean haltFail, boolean showOut,
0914: boolean outputToFormat, boolean logTestListenerEvents,
0915: Properties props) {
0916: t.setProperties(props);
0917: JUnitTestRunner runner = new JUnitTestRunner(t, haltError,
0918: stackfilter, haltFail, showOut, logTestListenerEvents,
0919: null);
0920: runner.forked = true;
0921: runner.outputToFormatters = outputToFormat;
0922: transferFormatters(runner, t);
0923:
0924: runner.run();
0925: return runner.getRetCode();
0926: }
0927:
0928: /**
0929: * @since Ant 1.7
0930: */
0931: private static void registerNonCrash() throws IOException {
0932: if (crashFile != null) {
0933: FileWriter out = null;
0934: try {
0935: out = new FileWriter(crashFile);
0936: out.write(Constants.TERMINATED_SUCCESSFULLY + "\n");
0937: out.flush();
0938: } finally {
0939: if (out != null) {
0940: out.close();
0941: }
0942: }
0943: }
0944: }
0945:
0946: private static void registerTestCase(String testCase) {
0947: if (crashFile != null) {
0948: try {
0949: FileWriter out = null;
0950: try {
0951: out = new FileWriter(crashFile);
0952: out.write(testCase + "\n");
0953: out.flush();
0954: } finally {
0955: if (out != null) {
0956: out.close();
0957: }
0958: }
0959: } catch (IOException e) {
0960: // ignored.
0961: }
0962: }
0963: }
0964:
0965: /**
0966: * Modifies a TestListener when running JUnit 4: treats AssertionFailedError
0967: * as a failure not an error.
0968: *
0969: * @since Ant 1.7
0970: */
0971: private TestListener wrapListener(final TestListener testListener) {
0972: return new TestListener() {
0973: public void addError(Test test, Throwable t) {
0974: if (junit4 && t instanceof AssertionFailedError) {
0975: // JUnit 4 does not distinguish between errors and failures
0976: // even in the JUnit 3 adapter.
0977: // So we need to help it a bit to retain compatibility for JUnit 3 tests.
0978: testListener.addFailure(test,
0979: (AssertionFailedError) t);
0980: } else if (junit4
0981: && t.getClass().getName().equals(
0982: "java.lang.AssertionError")) {
0983: // Not strictly necessary but probably desirable.
0984: // JUnit 4-specific test GUIs will show just "failures".
0985: // But Ant's output shows "failures" vs. "errors".
0986: // We would prefer to show "failure" for things that logically are.
0987: try {
0988: String msg = t.getMessage();
0989: AssertionFailedError failure = msg != null ? new AssertionFailedError(
0990: msg)
0991: : new AssertionFailedError();
0992: // To compile on pre-JDK 4 (even though this should always succeed):
0993: Method initCause = Throwable.class.getMethod(
0994: "initCause",
0995: new Class[] { Throwable.class });
0996: initCause.invoke(failure, new Object[] { t });
0997: testListener.addFailure(test, failure);
0998: } catch (Exception e) {
0999: // Rats.
1000: e.printStackTrace(); // should not happen
1001: testListener.addError(test, t);
1002: }
1003: } else {
1004: testListener.addError(test, t);
1005: }
1006: }
1007:
1008: public void addFailure(Test test, AssertionFailedError t) {
1009: testListener.addFailure(test, t);
1010: }
1011:
1012: public void addFailure(Test test, Throwable t) { // pre-3.4
1013: if (t instanceof AssertionFailedError) {
1014: testListener.addFailure(test,
1015: (AssertionFailedError) t);
1016: } else {
1017: testListener.addError(test, t);
1018: }
1019: }
1020:
1021: public void endTest(Test test) {
1022: testListener.endTest(test);
1023: }
1024:
1025: public void startTest(Test test) {
1026: testListener.startTest(test);
1027: }
1028: };
1029: }
1030:
1031: /**
1032: * Use instead of TestResult.get{Failure,Error}Count on JUnit 4,
1033: * since the adapter claims that all failures are errors.
1034: * @since Ant 1.7
1035: */
1036: private int[] findJUnit4FailureErrorCount(TestResult res) {
1037: int failures = 0;
1038: int errors = 0;
1039: Enumeration e = res.failures();
1040: while (e.hasMoreElements()) {
1041: e.nextElement();
1042: failures++;
1043: }
1044: e = res.errors();
1045: while (e.hasMoreElements()) {
1046: Throwable t = ((TestFailure) e.nextElement())
1047: .thrownException();
1048: if (t instanceof AssertionFailedError
1049: || t.getClass().getName().equals(
1050: "java.lang.AssertionError")) {
1051: failures++;
1052: } else {
1053: errors++;
1054: }
1055: }
1056: return new int[] { failures, errors };
1057: }
1058:
1059: } // JUnitTestRunner
|