diff --git a/WHATSNEW b/WHATSNEW
index 675778943..951f7c376 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -20,9 +20,9 @@ org.apache.tools.ant to org.apache.tools.ant.types.
Other changes:
--------------
-* New tasks: sql, genkey, cab, ftp.
+* New tasks: sql, genkey, cab, ftp, junit.
-* New tasks junit, mparse, execon. All pending documentation, most of
+* New tasks mparse, execon. All pending documentation, most of
them pending review.
* todo.html
are excluded.
This task runs tests from the JUnit testing framework. The latest +version of the framework can be found at http://www.xprogramming.com/software.htm. +This task requires JUnit 3.0 or above.
+ +Tests are defined by nested test
or
+batchtest
tags, see nested
+elements.
Attribute | +Description | +Required | +
printsummary | +Print one line statistics for each testcase. | +No, default is "off" | +
fork | +Run the tests in a separate VM. | +No, default is "off" | +
haltonerror | +Stop the build process if an error occures during the test + run. | +No, default is "off" | +
haltonfailure | +Stop the build process if a test fails (errors are + considered failures as well). | +No, default is "off" | +
timeout | +Cancel the individual tests if the don't finish + in the given time (measured in milliseconds). Ignored if fork is + disabled. | +No | +
maxmemory | +Max amount of memory to allocate to the forked VM + (ignored if fork is disabled) | +No | +
jvm | +the command used to invoke the Java Virtual Machine, + default is 'java'. The command is resolved by java.lang.Runtime.exec(). + Ignored if fork is disabled. | +No, default "java" | +
junit
supports a nested <classpath>
+element, that represents a PATH like
+structure. The value is ignore if fork
is
+disabled.
If fork is enabled, additional parameters may be passed to the new
+VM via nested <jvmarg>
attributes, for example:
+would run the test in a VM without JIT. + ++<junit fork="yes"> + <jvmarg value="-Djava.compiler=NONE"/> +</junit> +
Attribute | +Description | +Required | +
value | +a single command line argument. | +Exactly one of these. | +
file | +The name of a file as a single command line argument. | +|
path | +A string that shall be treated as a PATH + (i.e. the PATH separator will be set according to the plattform's + convention) as a single command line argument. | +|
line | +a space delimited list of command line arguments. | +
The results of the tests can be printed in different
+formats. Output will always be sent to a file, the name of the file is
+determined by the name of the test and can be set by the
+outfile
attribute of <test>
.
+
+
There are two predefined formatters, one prints the test results in
+XML format, the other emits plain text. Custom formatters that need to
+implement
+org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter
+can be specified.
Attribute | +Description | +Required | +
type | +Use a predefined formatter (either "xml" or "plain"). | +Exactly one of these. | +
classname | +Name of a custo formatter class. | +|
extension | +Extension to append to the output filename. | +Yes, if classname has been used. | +
Defines a single test class.
+ +Attribute | +Description | +Required | +
name | +Name of the test class | +Yes | +
fork | +Run the tests in a separate VM.
+ Overrides value set in <junit> . |
+ No | +
haltonerror | +Stop the build process if an error occures during the test
+ run. Overrides value set in <junit> . |
+ No | +
haltonfailure | +Stop the build process if a test fails (errors are
+ considered failures as well). Overrides value set in
+ <junit> . |
+ No | +
outfile | +Basename of the test result. The full filename is
+ determined by this attribute and the extension of
+ formatter . |
+ No, default is
+ TEST-name using the name attribute. |
+
if | +Only run test if the named property is set. | +No | +
unless | +Only run test if the named property is not set. | +No | +
Tests can define their own formatters via nested
+<formatter>
elements.
Define a number of tests based on pattern matching.
+ +batchtest
collects the included files from any number
+of nested <fileset>
and
+<filesetref>
elements. It then generates a test
+class name for each file that ends in .java
or
+.class
.
Attribute | +Description | +Required | +
fork | +Run the tests in a separate VM.
+ Overrides value set in <junit> . |
+ No | +
haltonerror | +Stop the build process if an error occures during the test
+ run. Overrides value set in <junit> . |
+ No | +
haltonfailure | +Stop the build process if a test fails (errors are
+ considered failures as well). Overrides value set in
+ <junit> . |
+ No | +
if | +Only run tests if the named property is set. | +No | +
unless | +Only run tests if the named property is not set. | +No | +
Batchtests can define their own formatters via nested
+<formatter>
elements.
+ ++<junit> + <test name="my.test.TestCase" /> +</junit> +
Runs the test defined in my.test.TestCase
in the same
+VM. No output will be generated unless the test fails.
+ ++<junit printsummary="yes" fork="yes" haltonfailure="yes"> + <formatter type="plain" /> + <test name="my.test.TestCase" /> +</junit> +
Runs the test defined in my.test.TestCase
in a
+separate VM. At the end of the test a single line summary will be
+printed. A detailed report of the test can be found in
+TEST-my.test.TestCase.txt
. The build process will be
+stopped if the test fails.
+ ++<junit printsummary="yes" haltonfailure="yes"> + <classpath> + <pathelement location="${build.tests}" /> + <pathelement path="${java.class.path}" /> + </classpath> + + <formatter type="plain" /> + + <test name="my.test.TestCase" haltonfailure="no" outfile="result" > + <formatter type="xml" /> + </test> + + <batchtest fork="yes"> + <fileset dir="${src.tests}"> + <include name="**/*Test*.java" /> + <exclude name="**/AllTests.java" /< + </fileset> + </batchtest> +</junit> +
Runs my.test.TestCase
in the same VM (ignoring the
+given CLASSPATH), only a warning is printed if this test fails. In
+addition to the plain text testresults, for this test a XML result
+will be output to result.xml
.
For each matching file in the directory ${src.tests}
a
+test is run in a separate VM. If a test fails, the build process is
+aborted. Results are collected in files named
+TEST-name.txt
.
fork
has been disabled.
*
* @author Thomas Haas
+ * @author Stefan Bodewig
*/
public class JUnitTask extends Task {
- // A new Test is started. */ - public void startTest(Test t) { - failed = false; - } + public void startTest(Test t) {} /** * Interface TestListener. * *
A Test is finished. */ - public void endTest(Test test) { - } + public void endTest(Test test) {} /** * Interface TestListener. @@ -222,9 +225,7 @@ public class JUnitTestRunner implements TestListener { *
A Test failed. */ public void addFailure(Test test, Throwable t) { - failed = true; - - if (junitTest.getHaltonfailure()) { + if (haltOnFailure) { res.stop(); } } @@ -235,9 +236,7 @@ public class JUnitTestRunner implements TestListener { *
An error occured while running the test. */ public void addError(Test test, Throwable t) { - failed = true; - - if (junitTest.getHaltonerror()) { + if (haltOnError) { res.stop(); } } @@ -261,65 +260,74 @@ public class JUnitTestRunner implements TestListener { /** * Entry point for standalone (forked) mode. * - * Parameters: testcaseclassname plus (up to) 6 parameters in the - * format key=value. + * Parameters: testcaseclassname plus parameters in the format + * key=value, none of which is required. * - *
key | description | default value |
---|---|---|
exit | exit with System.exit after testcase is - * complete? | true |
haltOnError | halt test on * errors? | false |
haltOnFailure | halt test on * failures? | false |
printSummary | print summary to System.out? | - *true |
printXML | generate XML report? | - *false |
outfile | where to print the XML report - a - * filename | System.out |
formatter | A JUnitResultFormatter given as + * classname,filename. If filename is ommitted, System.out is + * assumed. | none |
A new Test is started. + */ + public void startTest(Test t) { + lastTestStart = System.currentTimeMillis(); + wri.print("Testcase: " + ((TestCase) t).name()); + failed = false; + } + + /** + * Interface TestListener. + * + *
A Test is finished. + */ + public void endTest(Test test) { + if (failed) return; + wri.println(" took " + + nf.format((System.currentTimeMillis()-lastTestStart) + / 1000.0) + + " sec"); + } + + /** + * Interface TestListener. + * + *
A Test failed. + */ + public void addFailure(Test test, Throwable t) { + formatError("\tFAILED", test, t); + } + + /** + * Interface TestListener. + * + *
An error occured while running the test. + */ + public void addError(Test test, Throwable t) { + formatError("\tCaused an ERROR", test, t); + } + + private void formatError(String type, Test test, Throwable t) { + if (test != null) { + endTest(test); + } + failed = true; + + wri.println(type); + wri.println(t.getMessage()); + t.printStackTrace(wri); + wri.println(""); + } +} // PlainJUnitResultFormatter diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java index 4580b4db0..ea7751d51 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java @@ -55,10 +55,14 @@ package org.apache.tools.ant.taskdefs.optional.junit; import java.text.NumberFormat; +import java.io.IOException; +import java.io.OutputStream; import junit.framework.Test; +import org.apache.tools.ant.BuildException; + /** - * Prints short summary output of the test to System.out + * Prints short summary output of the test to Ant's logging system. * * @author Stefan Bodewig */ @@ -69,60 +73,63 @@ public class SummaryJUnitResultFormatter implements JUnitResultFormatter { * Formatter for timings. */ private NumberFormat nf = NumberFormat.getInstance(); - - public SummaryJUnitResultFormatter() { - } - /** - * The whole testsuite started. + * OutputStream to write to. */ - public void startTestSuite(JUnitTest suite) { - } + private OutputStream out; /** - * Interface TestListener. - * - *
A new Test is started. + * Empty */ - public void startTest(Test t) { - } - + public SummaryJUnitResultFormatter() {} /** - * Interface TestListener. - * - *
A Test is finished. + * Empty */ - public void endTest(Test test) { - } - + public void startTestSuite(JUnitTest suite) {} /** - * Interface TestListener. - * - *
A Test failed. + * Empty */ - public void addFailure(Test test, Throwable t) { - } - + public void startTest(Test t) {} + /** + * Empty + */ + public void endTest(Test test) {} + /** + * Empty + */ + public void addFailure(Test test, Throwable t) {} /** - * Interface TestListener. - * - *
An error occured while running the test.
+ * Empty
*/
- public void addError(Test test, Throwable t) {
+ public void addError(Test test, Throwable t) {}
+
+ public void setOutput(OutputStream out) {
+ this.out = out;
}
/**
* The whole testsuite ended.
*/
- public void endTestSuite(JUnitTest suite) {
- System.out.print("Tests run: ");
- System.out.print(suite.runCount());
- System.out.print(", Failures: ");
- System.out.print(suite.failureCount());
- System.out.print(", Errors: ");
- System.out.print(suite.errorCount());
- System.out.print(", Time ellapsed: ");
- System.out.print(nf.format(suite.getRunTime()/1000.0));
- System.out.println(" sec");
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ StringBuffer sb = new StringBuffer("Tests run: ");
+ sb.append(suite.runCount());
+ sb.append(", Failures: ");
+ sb.append(suite.failureCount());
+ sb.append(", Errors: ");
+ sb.append(suite.errorCount());
+ sb.append(", Time elapsed: ");
+ sb.append(nf.format(suite.getRunTime()/1000.0));
+ sb.append(" sec");
+ sb.append(System.getProperty("line.separator"));
+ try {
+ out.write(sb.toString().getBytes());
+ out.flush();
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to write summary output", ioex);
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {}
+ }
}
}
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
index 8d2f0ce62..f9e66fc73 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
@@ -54,11 +54,15 @@
package org.apache.tools.ant.taskdefs.optional.junit;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.text.CharacterIterator;
+import java.io.*;
import java.text.NumberFormat;
+import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
+import java.util.*;
+import javax.xml.parsers.*;
+import org.w3c.dom.*;
+import org.apache.tools.ant.BuildException;
+
import junit.framework.Test;
import junit.framework.TestCase;
@@ -70,57 +74,81 @@ import junit.framework.TestCase;
public class XMLJUnitResultFormatter implements JUnitResultFormatter {
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ }
+ catch(Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
/**
- * OutputStream for XML output.
+ * Formatter for timings.
*/
- private PrintWriter out;
+ private NumberFormat nf = NumberFormat.getInstance();
/**
- * Collects output during the test run.
+ * The XML document.
*/
- private StringBuffer results = new StringBuffer();
+ private Document doc;
/**
- * platform independent line separator.
+ * The wrapper for the whole testsuite.
*/
- private static String newLine = System.getProperty("line.separator");
+ private Element rootElement;
/**
- * Formatter for timings.
+ * Element for the current test.
*/
- private NumberFormat nf = NumberFormat.getInstance();
+ private Element currentTest;
/**
* Timing helper.
*/
private long lastTestStart = 0;
+ /**
+ * Where to write the log to.
+ */
+ private OutputStream out;
- public XMLJUnitResultFormatter(PrintWriter out) {
+ public XMLJUnitResultFormatter() {}
+
+ public void setOutput(OutputStream out) {
this.out = out;
}
/**
- * The whole testsuite ended.
+ * The whole testsuite started.
*/
- public void endTestSuite(JUnitTest suite) {
- out.println("");
- out.print("
A Test is finished.
*/
public void endTest(Test test) {
- formatTestCaseOpenTag(test);
- results.append(" ");
- results.append(newLine);
+ currentTest.setAttribute("time",
+ nf.format((System.currentTimeMillis()-lastTestStart)
+ / 1000.0));
}
/**
@@ -161,8 +192,33 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter {
formatError("error", test, t);
}
+ private void formatError(String type, Test test, Throwable t) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ Element nested = doc.createElement(type);
+ if (test != null) {
+ currentTest.appendChild(nested);
+ } else {
+ rootElement.appendChild(nested);
+ }
+
+ String message = t.getMessage();
+ if (message != null && message.length() > 0) {
+ nested.setAttribute("message", xmlEscape(t.getMessage()));
+ }
+ nested.setAttribute("type", xmlEscape(t.getClass().getName()));
+
+ StringWriter swr = new StringWriter();
+ t.printStackTrace(new PrintWriter(swr, true));
+ Text trace = doc.createTextNode(swr.toString());
+ nested.appendChild(trace);
+ }
+
+
/**
- * Translates <, & and > to corresponding entities.
+ * Translates <, & , " and > to corresponding entities.
*/
private String xmlEscape(String orig) {
if (orig == null) return "";
@@ -178,6 +234,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter {
case '>':
temp.append(">");
break;
+ case '\"':
+ temp.append(""");
+ break;
case '&':
temp.append("&");
break;
@@ -189,47 +248,65 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter {
return temp.toString();
}
- private void formatTestCaseOpenTag(Test test) {
- results.append("