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