Browse Source

<junit> more or less completely rewritten and documented.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267904 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 25 years ago
parent
commit
818cb969ad
13 changed files with 1341 additions and 415 deletions
  1. +2
    -2
      WHATSNEW
  2. +7
    -6
      build.xml
  3. +1
    -0
      docs/index.html
  4. +313
    -0
      docs/junit.html
  5. +179
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java
  6. +157
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java
  7. +8
    -2
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java
  8. +120
    -123
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
  9. +44
    -65
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
  10. +112
    -104
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
  11. +201
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java
  12. +47
    -40
      src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java
  13. +150
    -73
      src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java

+ 2
- 2
WHATSNEW View File

@@ -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.

* <java> uses ClassLoader of its own in no-fork mode if a classpath is


+ 7
- 6
build.xml View File

@@ -295,7 +295,7 @@
This would make the buildprocess fail if using an Ant version
without the junit task

<junit defaultprintxml="false" defaultprintsummary="true" fork="on">
<junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath>
<pathelement location="${lib.dir}/${name}.jar" />
<pathelement location="${build.tests}" />
@@ -303,11 +303,12 @@
<pathelement path="${java.class.path}" />
</classpath>

<test name="org.apache.tools.ant.IntrospectionHelperTest" />
<test name="org.apache.tools.ant.types.CommandlineTest" />
<test name="org.apache.tools.ant.types.CommandlineJavaTest" />
<test name="org.apache.tools.ant.types.EnumeratedAttributeTest" />
<test name="org.apache.tools.ant.types.PathTest" />
<batchtest>
<fileset dir="${src.tests.dir}">
<include name="**/*Test*" />
<exclude name="**/All*" />
</fileset>
</batchtest>
</junit>
-->



+ 1
- 0
docs/index.html View File

@@ -3305,6 +3305,7 @@ and <code>todo.html</code> are excluded.</p>
<ul>
<li><a href="#cab">Cab</a></li>
<li><a href="#ftp">FTP</a></li>
<li><a href="junit.html">JUnit</a></li>
<li><a href="#netrexxc">NetRexxC</a></li>
<li><a href="#renameexts">RenameExtensions</a></li>
<li><a href="#script">Script</a></li>


+ 313
- 0
docs/junit.html View File

@@ -0,0 +1,313 @@
<html>
<head>
</head>
<body>

<h2><a name="junit">JUnit</a></h2>
<h3>Description</h3>

<p>This task runs tests from the JUnit testing framework. The latest
version of the framework can be found at <a
href="http://www.xprogramming.com/software.htm">http://www.xprogramming.com/software.htm</a>.
This task requires JUnit 3.0 or above.</p>

<p>Tests are defined by nested <code>test</code> or
<code>batchtest</code> tags, see <a href="#nested">nested
elements</a>.</p>

<h3>Parameters</h3>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td width="12%" valign="top"><b>Attribute</b></td>
<td width="78%" valign="top"><b>Description</b></td>
<td width="10%" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">printsummary</td>
<td valign="top">Print one line statistics for each testcase.</td>
<td align="center" valign="top">No, default is "off"</td>
</tr>
<tr>
<td valign="top">fork</td>
<td valign="top">Run the tests in a separate VM.</td>
<td align="center" valign="top">No, default is "off"</td>
</tr>
<tr>
<td valign="top">haltonerror</td>
<td valign="top">Stop the build process if an error occures during the test
run.</td>
<td align="center" valign="top">No, default is "off"</td>
</tr>
<tr>
<td valign="top">haltonfailure</td>
<td valign="top">Stop the build process if a test fails (errors are
considered failures as well).</td>
<td align="center" valign="top">No, default is "off"</td>
</tr>
<tr>
<td valign="top">timeout</td>
<td valign="top">Cancel the individual tests if the don't finish
in the given time (measured in milliseconds). Ignored if fork is
disabled.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">maxmemory</td>
<td valign="top">Max amount of memory to allocate to the forked VM
(ignored if fork is disabled)</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">jvm</td>
<td valign="top">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.</td>
<td align="center" valign="top">No, default &quot;java&quot;</td>
</tr>
</table>

<h3><a name="nested">Nested Elements</a></h3>

<p><code>junit</code> supports a nested <code>&lt;classpath&gt;</code>
element, that represents a <a href="index.html#path">PATH like
structure</a>. The value is ignore if <code>fork</code> is
disabled.</p>

<h4>jvmarg</h4>

<p>If fork is enabled, additional parameters may be passed to the new
VM via nested <code>&lt;jvmarg&gt;</code> attributes, for example:</p>

<pre><blockquote>
&lt;junit fork=&quot;yes&quot;&gt;
&lt;jvmarg value=&quot;-Djava.compiler=NONE&quot;/&gt;
&lt;/junit&gt;
</blockquote></pre>
would run the test in a VM without JIT.</p>

<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td width="12%" valign="top"><b>Attribute</b></td>
<td width="78%" valign="top"><b>Description</b></td>
<td width="10%" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">value</td>
<td valign="top">a single command line argument.</td>
<td align="center" rowspan="4">Exactly one of these.</td>
</tr>
<tr>
<td valign="top">file</td>
<td valign="top">The name of a file as a single command line argument.</td>
</tr>
<tr>
<td valign="top">path</td>
<td valign="top">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.</td>
</tr>
<tr>
<td valign="top">line</td>
<td valign="top">a space delimited list of command line arguments.</td>
</tr>
</table>

<h4>formatter</h4>

<p>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
<code>outfile</code> attribute of <code>&lt;test&gt;</code>.

<p>There are two predefined formatters, one prints the test results in
XML format, the other emits plain text. Custom formatters that need to
implement
<code>org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter</code>
can be specified.</p>

<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td width="12%" valign="top"><b>Attribute</b></td>
<td width="78%" valign="top"><b>Description</b></td>
<td width="10%" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">type</td>
<td valign="top">Use a predefined formatter (either "xml" or "plain").</td>
<td align="center" rowspan="2">Exactly one of these.</td>
</tr>
<tr>
<td valign="top">classname</td>
<td valign="top">Name of a custo formatter class.</td>
</tr>
<tr>
<td valign="top">extension</td>
<td valign="top">Extension to append to the output filename.</td>
<td align="center">Yes, if classname has been used.</td>
</tr>
</table>

<h4>test</h4>

<p>Defines a single test class.</p>

<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td width="12%" valign="top"><b>Attribute</b></td>
<td width="78%" valign="top"><b>Description</b></td>
<td width="10%" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">name</td>
<td valign="top">Name of the test class</td>
<td align="center">Yes</td>
</tr>
<tr>
<td valign="top">fork</td>
<td valign="top">Run the tests in a separate VM.
Overrides value set in <code>&lt;junit&gt;</code>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">haltonerror</td>
<td valign="top">Stop the build process if an error occures during the test
run. Overrides value set in <code>&lt;junit&gt;</code>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">haltonfailure</td>
<td valign="top">Stop the build process if a test fails (errors are
considered failures as well). Overrides value set in
<code>&lt;junit&gt;</code>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">outfile</td>
<td valign="top">Basename of the test result. The full filename is
determined by this attribute and the extension of
<code>formatter</code>.</td>
<td align="center" valign="top">No, default is
<code>TEST-name</code> using the <code>name</code> attribute.</td>
</tr>
<tr>
<td valign="top">if</td>
<td valign="top">Only run test if the named property is set.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">unless</td>
<td valign="top">Only run test if the named property is <b>not</b> set.</td>
<td align="center" valign="top">No</td>
</tr>
</table>

<p>Tests can define their own formatters via nested
<code>&lt;formatter&gt;</code> elements.</p>

<h4>batchtest</h4>

<p>Define a number of tests based on pattern matching.</p>

<p><code>batchtest</code> collects the included files from any number
of nested <code>&lt;fileset&gt;</code> and
<code>&lt;filesetref&gt;</code> elements. It then generates a test
class name for each file that ends in <code>.java</code> or
<code>.class</code>.</p>

<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td width="12%" valign="top"><b>Attribute</b></td>
<td width="78%" valign="top"><b>Description</b></td>
<td width="10%" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">fork</td>
<td valign="top">Run the tests in a separate VM.
Overrides value set in <code>&lt;junit&gt;</code>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">haltonerror</td>
<td valign="top">Stop the build process if an error occures during the test
run. Overrides value set in <code>&lt;junit&gt;</code>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">haltonfailure</td>
<td valign="top">Stop the build process if a test fails (errors are
considered failures as well). Overrides value set in
<code>&lt;junit&gt;</code>.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">if</td>
<td valign="top">Only run tests if the named property is set.</td>
<td align="center" valign="top">No</td>
</tr>
<tr>
<td valign="top">unless</td>
<td valign="top">Only run tests if the named property is <b>not</b> set.</td>
<td align="center" valign="top">No</td>
</tr>
</table>

<p>Batchtests can define their own formatters via nested
<code>&lt;formatter&gt;</code> elements.</p>

<h3>Examples</h3>
<pre><blockquote>
&lt;junit&gt;
&lt;test name="my.test.TestCase" /&gt;
&lt;/junit&gt;
</pre></blockquote>

<p>Runs the test defined in <code>my.test.TestCase</code> in the same
VM. No output will be generated unless the test fails.</p>

<pre><blockquote>
&lt;junit printsummary="yes" fork="yes" haltonfailure="yes"&gt;
&lt;formatter type="plain" /&gt;
&lt;test name="my.test.TestCase" /&gt;
&lt;/junit&gt;
</pre></blockquote>

<p>Runs the test defined in <code>my.test.TestCase</code> 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
<code>TEST-my.test.TestCase.txt</code>. The build process will be
stopped if the test fails.</p>

<pre><blockquote>
&lt;junit printsummary="yes" haltonfailure="yes"&gt;
&lt;classpath&gt;
&lt;pathelement location="${build.tests}" /&gt;
&lt;pathelement path="${java.class.path}" /&gt;
&lt;/classpath&gt;

&lt;formatter type="plain" /&gt;

&lt;test name="my.test.TestCase" haltonfailure="no" outfile="result" &gt;
&lt;formatter type="xml" /&gt;
&lt;/test&gt;

&lt;batchtest fork="yes"&gt;
&lt;fileset dir="${src.tests}"&gt;
&lt;include name="**/*Test*.java" /&gt;
&lt;exclude name="**/AllTests.java" /&lt;
&lt;/fileset&gt;
&lt;/batchtest&gt;
&lt;/junit&gt;
</pre></blockquote>

<p>Runs <code>my.test.TestCase</code> 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 <code>result.xml</code>.</p>

<p>For each matching file in the directory <code>${src.tests}</code> a
test is run in a separate VM. If a test fails, the build process is
aborted. Results are collected in files named
<code>TEST-<em>name</em>.txt</code>.</p>

</body>
</html>

+ 179
- 0
src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java View File

@@ -0,0 +1,179 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant.taskdefs.optional.junit;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Reference;

import java.util.*;

/**
* Create JUnitTests from a list of files.
*
* @author <a href="mailto:jeff.martin@synamic.co.uk">Jeff Martin</a>
* @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a>
*/
public final class BatchTest {
private boolean fork=false;
private boolean haltOnError=false;
private boolean haltOnFailure=false;
private Project project;
private String ifCond = null;
private String unlessCond = null;

private Vector filesets = new Vector();
private Vector formatters = new Vector();

public BatchTest(Project project){
this.project = project;
}

public void addFileSet(FileSet fs) {
filesets.addElement(fs);
}

public void addFileSetRef(Reference r) {
filesets.addElement(r);
}

public void addFormatter(FormatterElement elem) {
formatters.addElement(elem);
}

public void setIf(String propertyName) {
ifCond = propertyName;
}

public void setUnless(String propertyName) {
unlessCond = propertyName;
}

public final void setFork(boolean value) {
this.fork = value;
}
public final void setHaltonerror(boolean value) {
this.haltOnError = value;
}
public final void setHaltonfailure(boolean value) {
this.haltOnFailure = value;
}
public final Enumeration elements(){
return new FileList();
}
public class FileList implements Enumeration{
private String files[]=null;
private int i=0;
private FileList(){
Vector v = new Vector();
for (int j=0; j<filesets.size(); j++) {
Object o = filesets.elementAt(j);
FileSet fs = null;
if (o instanceof FileSet) {
fs = (FileSet) o;
} else {
Reference r = (Reference) o;
o = r.getReferencedObject(project);
if (o instanceof FileSet) {
fs = (FileSet) o;
} else {
String msg = r.getRefId()+" doesn\'t denote a fileset";
throw new BuildException(msg);
}
}
DirectoryScanner ds = fs.getDirectoryScanner(project);
ds.scan();
String[] f = ds.getIncludedFiles();
for (int k=0; k<f.length; k++) {
if (f[k].endsWith(".java")) {
v.addElement(f[k].substring(0, f[k].length()-5));
} else if (f[k].endsWith(".class")) {
v.addElement(f[k].substring(0, f[k].length()-6));
}
}
}

files = new String[v.size()];
v.copyInto(files);
}
public final boolean hasMoreElements(){
if(i<files.length)return true;
return false;
}
public final Object nextElement() throws NoSuchElementException{
if(hasMoreElements()){
JUnitTest test = new JUnitTest(javaToClass(files[i]));
test.setHaltonerror(haltOnError);
test.setHaltonfailure(haltOnFailure);
test.setFork(fork);
test.setIf(ifCond);
test.setUnless(unlessCond);
Enumeration list = formatters.elements();
while (list.hasMoreElements()) {
test.addFormatter((FormatterElement)list.nextElement());
}
i++;
return test;
}
throw new NoSuchElementException();
}
public final String javaToClass(String fileName){
return fileName.replace(java.io.File.separatorChar, '.');
}
}
}

+ 157
- 0
src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java View File

@@ -0,0 +1,157 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant.taskdefs.optional.junit;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.EnumeratedAttribute;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
* Serves as a wrapper the implementations of JUnitResultFormatter,
* for example as a nested <formatter> element in <junit>.
*
* @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a>
*/
public class FormatterElement {

private String classname;
private String extension;
private OutputStream out = System.out;
private File outFile;

public void setType(TypeAttribute type) {
if ("xml".equals(type.getValue())) {
setClassname("org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter");
setExtension(".xml");
} else { // must be plain, ensured by TypeAttribute
setClassname("org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter");
setExtension(".txt");
}
}

public void setClassname(String classname) {
this.classname = classname;
}

public String getClassname() {
return classname;
}

public void setExtension(String ext) {
this.extension = ext;
}

public String getExtension() {
return extension;
}

void setOutfile(File out) {
this.outFile = out;
}

public void setOutput(OutputStream out) {
this.out = out;
}

JUnitResultFormatter createFormatter() throws BuildException {
if (classname == null) {
throw new BuildException("you must specify type or classname");
}
Class f = null;
try {
f = Class.forName(classname);
} catch (ClassNotFoundException e) {
throw new BuildException(e);
}

Object o = null;
try {
o = f.newInstance();
} catch (InstantiationException e) {
throw new BuildException(e);
} catch (IllegalAccessException e) {
throw new BuildException(e);
}

if (!(o instanceof JUnitResultFormatter)) {
throw new BuildException(classname+" is not a JUnitResultFormatter");
}

JUnitResultFormatter r = (JUnitResultFormatter) o;

if (outFile != null) {
try {
out = new FileOutputStream(outFile);
} catch (java.io.IOException e) {
throw new BuildException(e);
}
}
r.setOutput(out);
return r;
}

/**
* Enumerated attribute with the values "plain" and "xml".
*/
public static class TypeAttribute extends EnumeratedAttribute {
public String[] getValues() {
return new String[] {"plain", "xml"};
}
}
}

+ 8
- 2
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java View File

@@ -54,6 +54,7 @@

package org.apache.tools.ant.taskdefs.optional.junit;

import org.apache.tools.ant.BuildException;
import junit.framework.TestListener;

/**
@@ -66,10 +67,15 @@ public interface JUnitResultFormatter extends TestListener {
/**
* The whole testsuite started.
*/
public void startTestSuite(JUnitTest suite);
public void startTestSuite(JUnitTest suite) throws BuildException;

/**
* The whole testsuite ended.
*/
public void endTestSuite(JUnitTest suite);
public void endTestSuite(JUnitTest suite) throws BuildException;

/**
* Sets the stream the formatter is supposed to write its results to.
*/
public void setOutput(java.io.OutputStream out);
}

+ 120
- 123
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java View File

@@ -82,49 +82,38 @@ import java.util.Vector;
* unless <code>fork</code> has been disabled.
*
* @author Thomas Haas
* @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a>
*/
public class JUnitTask extends Task {

// <XXX> private final static int HALT_NOT = 1;
// <XXX> private final static int HALT_IMMEDIATELY = 2;
// <XXX> private final static int HALT_AT_END = 3;

private CommandlineJava commandline = new CommandlineJava();
private Vector tests = new Vector();
private Vector batchTests = new Vector();
private Vector formatters = new Vector();

private JUnitTest defaults = new JUnitTest();
private boolean defaultOutfile = true;
private Integer timeout = null;
private boolean summary = false;

// <XXX> private int haltOnError = HALT_AT_END;
// <XXX> private int haltOnFailure = HALT_AT_END;

/**
* Set the path to junit classes.
* @param junit path to junit classes.
*/
public void setJunit(File junit) {
commandline.createClasspath(project).createPathElement().setLocation(junit);
}

public void setDefaulthaltonerror(boolean value) {
public void setHaltonerror(boolean value) {
defaults.setHaltonerror(value);
}

public void setDefaulthaltonfailure(boolean value) {
public void setHaltonfailure(boolean value) {
defaults.setHaltonfailure(value);
}

public void setDefaultprintsummary(boolean value) {
defaults.setPrintsummary(value);
public void setPrintsummary(boolean value) {
summary = value;
}

public void setDefaultprintxml(boolean value) {
defaults.setPrintxml(value);
}

public void setDefaultOutFile(boolean value) {
defaultOutfile = value;
public void setMaxmemory(String max) {
if (Project.getJavaVersion().startsWith("1.1")) {
createJvmarg().setValue("-mx"+max);
} else {
createJvmarg().setValue("-Xmx"+max);
}
}

public void setTimeout(Integer value) {
@@ -136,42 +125,36 @@ public class JUnitTask extends Task {
}

public void setJvm(String value) {
defaults.setName(value);
commandline.setVm(value);
}

public void setJvmargs(String value) {
commandline.createVmArgument().setLine(value);
public Commandline.Argument createJvmarg() {
return commandline.createVmArgument();
}


/**
* Set the classpath to be used by the testcase.
* @param classpath the classpath used to run the testcase.
*/
/*public void setClasspath(String classpath) {
this.classpath = classpath;
}*/


public Path createClasspath() {
return commandline.createClasspath(project);
}

public JUnitTest createTest() {
final JUnitTest result;
result = new JUnitTest(
defaults.getFork(),
defaults.getHaltonerror(),
defaults.getHaltonfailure(),
defaults.getPrintsummary(),
defaults.getPrintxml(),
null, null);

tests.addElement(result);
return result;
public void addTest(JUnitTest test) {
test.setHaltonerror(defaults.getHaltonerror());
test.setHaltonfailure(defaults.getHaltonfailure());
test.setFork(defaults.getFork());
tests.addElement(test);
}

public BatchTest createBatchTest() {
BatchTest test = new BatchTest(project);
test.setHaltonerror(defaults.getHaltonerror());
test.setHaltonfailure(defaults.getHaltonfailure());
test.setFork(defaults.getFork());
batchTests.addElement(test);
return test;
}

public void addFormatter(FormatterElement fe) {
formatters.addElement(fe);
}

/**
* Creates a new JUnitRunner and enables fork of a new Java VM.
@@ -187,94 +170,108 @@ public class JUnitTask extends Task {
boolean errorOccurred = false;
boolean failureOccurred = false;

final String oldclasspath = System.getProperty("java.class.path");

commandline.createClasspath(project).createPathElement().setPath(oldclasspath);
/*
* This doesn't work on JDK 1.1, should use a Classloader of our own
* anyway --SB
*
* System.setProperty("java.class.path", commandline.createClasspath().toString());
*/
Enumeration list = batchTests.elements();
while (list.hasMoreElements()) {
BatchTest test = (BatchTest)list.nextElement();
Enumeration list2 = test.elements();
while (list2.hasMoreElements()) {
tests.addElement(list2.nextElement());
}
}

Enumeration list = tests.elements();
list = tests.elements();
while (list.hasMoreElements()) {
final JUnitTest test = (JUnitTest)list.nextElement();

final String filename = "TEST-" + test.getName() + ".xml";
// removed --SB
// if (new File(filename).exists()) {
// project.log("Skipping " + test.getName());
// continue;
// }
project.log("Running " + test.getName());

if (defaultOutfile && (test.getOutfile() == null ||
test.getOutfile().length() == 0)) {

// removed --SB
// test.setOutfile("RUNNING-" + filename);
test.setOutfile(filename);
JUnitTest test = (JUnitTest)list.nextElement();

if (!test.shouldRun(project)) {
continue;
}

int exitValue = 2;
if (test.getOutfile() == null) {
test.setOutfile(project.resolveFile("TEST-" + test.getName()));
}

if (test.getFork()) {
try {
// Create a watchdog based on the timeout attribute
final Execute execute = new Execute(new PumpStreamHandler(), createWatchdog());
final Commandline cmdl = new Commandline();
cmdl.addArguments(commandline.getCommandline());
cmdl.addArguments(test.getCommandline());
execute.setCommandline(cmdl.getCommandline());
log("Execute JUnit: " + cmdl, Project.MSG_VERBOSE);
exitValue = execute.execute();
int exitValue = JUnitTestRunner.ERRORS;
if (!test.getFork()) {
JUnitTestRunner runner =
new JUnitTestRunner(test, test.getHaltonerror(),
test.getHaltonfailure());
if (summary) {
log("Running " + test.getName(), Project.MSG_INFO);
SummaryJUnitResultFormatter f =
new SummaryJUnitResultFormatter();
f.setOutput(new LogOutputStream(this, Project.MSG_INFO));
runner.addFormatter(f);
}
catch (IOException e) {
throw new BuildException("Process fork failed.", e,
location);

for (int i=0; i<formatters.size(); i++) {
FormatterElement fe = (FormatterElement) formatters.elementAt(i);
fe.setOutfile(project.resolveFile(test.getOutfile()
+fe.getExtension()));
runner.addFormatter(fe.createFormatter());
}
FormatterElement[] add = test.getFormatters();
for (int i=0; i<add.length; i++) {
add[i].setOutfile(project.resolveFile(test.getOutfile()
+add[i].getExtension()));
runner.addFormatter(add[i].createFormatter());
}

runner.run();
exitValue = runner.getRetCode();

} else {
final Object[] arg = { test };
final Class[] argType = { arg[0].getClass() };
CommandlineJava cmd = (CommandlineJava) commandline.clone();
cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
cmd.createArgument().setValue(test.getName());
cmd.createArgument().setValue("haltOnError="
+ test.getHaltonerror());
cmd.createArgument().setValue("haltOnFailure="
+ test.getHaltonfailure());
if (summary) {
log("Running " + test.getName(), Project.MSG_INFO);
cmd.createArgument().setValue("formatter=org.apache.tools.ant.taskdefs.optional.junit.SummaryJUnitResultFormatter");
}

for (int i=0; i<formatters.size(); i++) {
FormatterElement fe = (FormatterElement) formatters.elementAt(i);
cmd.createArgument().setValue("formatter=" +
fe.getClassname() + ","
+ project.resolveFile(test.getOutfile()
+fe.getExtension()).getAbsolutePath());
}
FormatterElement[] add = test.getFormatters();
for (int i=0; i<add.length; i++) {
cmd.createArgument().setValue("formatter=" +
add[i].getClassname() + ","
+ project.resolveFile(test.getOutfile()
+add[i].getExtension()).getAbsolutePath());
}

Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN), createWatchdog());
execute.setCommandline(cmd.getCommandline());
try {
final Class target = Class.forName("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
final Method main = target.getMethod("runTest", argType);
project.log("Load JUnit: " + test, Project.MSG_VERBOSE);
exitValue = ((Integer)main.invoke(null, arg)).intValue();
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
String msg = "Running test failed: " + t.getMessage();
throw new BuildException(msg, t, location);
} catch (Exception e) {
String msg = "Running test failed: " + e.getMessage();
throw new BuildException(msg, e, location);
exitValue = execute.execute();
} catch (IOException e) {
throw new BuildException("Process fork failed.", e,
location);
}
}

boolean errorOccurredHere = exitValue == 2;
boolean failureOccurredHere = exitValue == 1;
// removed --SB
// if (exitValue != 0) {
// rename("RUNNING-" + filename, "ERROR-" + filename);
// } else {
// rename("RUNNING-" + filename, filename);
// }
// <XXX> later distinguish HALT_AT_END case
boolean errorOccurredHere = exitValue == JUnitTestRunner.ERRORS;
boolean failureOccurredHere = exitValue != JUnitTestRunner.SUCCESS;
if (errorOccurredHere && test.getHaltonerror()
|| failureOccurredHere && test.getHaltonfailure()) {
throw new BuildException("JUNIT FAILED", location);
} else if (errorOccurredHere || failureOccurredHere) {
log("JUNIT FAILED", Project.MSG_ERR);
throw new BuildException("Test "+test.getName()+" failed",
location);
} else if (errorOccurredHere || failureOccurredHere) {
log("TEST "+test.getName()+" FAILED", Project.MSG_ERR);
}

// Update overall test status
errorOccurred = errorOccurred || errorOccurredHere ;
failureOccurred = failureOccurred || failureOccurredHere ;
}

// <XXX> later add HALT_AT_END option
// Then test errorOccurred and failureOccurred here.
}

protected ExecuteWatchdog createWatchdog() throws BuildException {


+ 44
- 65
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java View File

@@ -57,36 +57,37 @@ package org.apache.tools.ant.taskdefs.optional.junit;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Commandline;

import java.io.File;
import java.util.Vector;

/**
*
* @author Thomas Haas
* @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a>
*/
public class JUnitTest {
private boolean systemExit = false;
private boolean haltOnError = false;
private boolean haltOnFail = false;
private boolean printSummary = true;
private boolean printXml = true;
private String name = null;
private String outfile = null;
private File outfile = null;
private boolean fork = false;

private long runs, failures, errors;
private long runTime;

private Vector formatters = new Vector();

public JUnitTest() {
}

public JUnitTest(boolean fork, boolean haltOnError, boolean haltOnFail,
boolean printSummary, boolean printXml, String name,
String outfile) {
this.fork = fork;
public JUnitTest(String name) {
this.name = name;
}

public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure) {
this.name = name;
this.haltOnError = haltOnError;
this.haltOnFail = haltOnFail;
this.printSummary = printSummary;
this.printXml = printXml;
this.name = name;
this.outfile = outfile;
}

public void setFork(boolean value) {
@@ -105,23 +106,14 @@ public class JUnitTest {
haltOnFail = value;
}

public void setPrintsummary(boolean value) {
printSummary = value;
}

public void setPrintxml(boolean value) {
printXml = value;
}

public void setName(String value) {
name = value;
}

public void setOutfile(String value) {
public void setOutfile(File value) {
outfile = value;
}


public boolean getHaltonerror() {
return haltOnError;
}
@@ -130,53 +122,15 @@ public class JUnitTest {
return haltOnFail;
}

public boolean getPrintsummary() {
return printSummary;
}

public boolean getPrintxml() {
return printXml;
}

public String getName() {
return name;
}

public String getOutfile() {
return outfile;
}

public void setCommandline(String [] args) {
for (int i=0; i<args.length; i++) {
if (args[i] == null) continue;
if (args[i].startsWith("haltOnError=")) {
haltOnError = Project.toBoolean(args[i].substring(12));
} else if (args[i].startsWith("haltOnFailure=")) {
haltOnFail = Project.toBoolean(args[i].substring(14));
} else if (args[i].startsWith("printSummary=")) {
printSummary = Project.toBoolean(args[i].substring(13));
} else if (args[i].startsWith("printXML=")) {
printXml = Project.toBoolean(args[i].substring(9));
} else if (args[i].startsWith("outfile=")) {
outfile = args[i].substring(8);
}
if (outfile != null) {
return outfile.getAbsolutePath();
}
}

public String[] getCommandline() {
final Commandline result = new Commandline();
if (name != null && name.length() > 0) {
result.setExecutable(name);
}
result.createArgument().setValue("exit=" + systemExit);
result.createArgument().setValue("haltOnError=" + haltOnError);
result.createArgument().setValue("haltOnFailure=" + haltOnFail);
result.createArgument().setValue("printSummary=" + printSummary);
result.createArgument().setValue("printXML=" + printXml);
if (outfile != null && outfile.length() > 0) {
result.createArgument().setValue("outfile=" + outfile);
}
return result.getCommandline();
return null;
}

public void setCounts(long runs, long failures, long errors) {
@@ -194,9 +148,34 @@ public class JUnitTest {
public long errorCount() {return errors;}
public long getRunTime() {return runTime;}

private String ifProperty = null;
private String unlessProperty = null;

public void setIf(String propertyName) {
ifProperty = propertyName;
}

public void setUnless(String propertyName) {
unlessProperty = propertyName;
}

public String toString() {
return Commandline.toString(getCommandline());
public boolean shouldRun(Project p) {
if (ifProperty != null && p.getProperty(ifProperty) == null) {
return false;
} else if (unlessProperty != null &&
p.getProperty(unlessProperty) != null) {
return false;
}
return true;
}

public void addFormatter(FormatterElement elem) {
formatters.addElement(elem);
}

public FormatterElement[] getFormatters() {
FormatterElement[] fes = new FormatterElement[formatters.size()];
formatters.copyInto(fes);
return fes;
}
}

+ 112
- 104
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java View File

@@ -54,11 +54,13 @@

package org.apache.tools.ant.taskdefs.optional.junit;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;

import junit.framework.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.StringTokenizer;
import java.util.Vector;

/**
@@ -78,6 +80,21 @@ import java.util.Vector;

public class JUnitTestRunner implements TestListener {

/**
* No problems with this test.
*/
public static final int SUCCESS = 0;

/**
* Some tests failed.
*/
public static final int FAILURES = 1;

/**
* An error occured.
*/
public static final int ERRORS = 2;

/**
* Holds the registered formatters.
*/
@@ -89,14 +106,14 @@ public class JUnitTestRunner implements TestListener {
private TestResult res;

/**
* Flag for endTest.
* Do we stop on errors.
*/
private boolean failed = true;
private boolean haltOnError = false;

/**
* The test I'm going to run.
* Do we stop on test failures.
*/
private JUnitTest junitTest;
private boolean haltOnFailure = false;

/**
* The corresponding testsuite.
@@ -104,38 +121,29 @@ public class JUnitTestRunner implements TestListener {
private Test suite = null;

/**
* Returncode
* Exception caught in constructor.
*/
private int retCode = 0;
private Exception exception;

public JUnitTestRunner(JUnitTest test) {
junitTest = test;
try {
if (junitTest.getPrintxml()) {
if (test.getOutfile() != null
&& test.getOutfile().length() > 0) {

addFormatter(new XMLJUnitResultFormatter(
new PrintWriter(
new FileWriter(test.getOutfile(), false)
)
)
);
} else {
addFormatter(new XMLJUnitResultFormatter(
new PrintWriter(
new OutputStreamWriter(System.out), true)
)
);
}
}
/**
* Returncode
*/
private int retCode = SUCCESS;

if (junitTest.getPrintsummary()) {
addFormatter(new SummaryJUnitResultFormatter());
}
/**
* The TestSuite we are currently running.
*/
private JUnitTest junitTest;

Class testClass = Class.forName(junitTest.getName());
public JUnitTestRunner(JUnitTest test, boolean haltOnError,
boolean haltOnFailure) {
this.junitTest = test;
this.haltOnError = haltOnError;
this.haltOnFailure = haltOnFailure;

try {
Class testClass = Class.forName(test.getName());
try {
Method suiteMethod= testClass.getMethod("suite", new Class[0]);
suite = (Test)suiteMethod.invoke(null, new Class[0]);
@@ -143,50 +151,48 @@ public class JUnitTestRunner implements TestListener {
} catch(InvocationTargetException e) {
} catch(IllegalAccessException e) {
}
if (suite == null) {
// try to extract a test suite automatically
// this will generate warnings if the class is no suitable Test
suite= new TestSuite(testClass);
}

res = new TestResult();
res.addListener(this);
for (int i=0; i < formatters.size(); i++) {
res.addListener((TestListener)formatters.elementAt(i));
}

} catch(Exception e) {
retCode = 2;

fireStartTestSuite();
for (int i=0; i < formatters.size(); i++) {
((TestListener)formatters.elementAt(i)).addError(null, e);
}
junitTest.setCounts(1, 0, 1);
junitTest.setRunTime(0);
fireEndTestSuite();
retCode = ERRORS;
exception = e;
}
}

public void run() {
long start = System.currentTimeMillis();
if (retCode != 0) { // had an exception in the constructor
return;
res = new TestResult();
res.addListener(this);
for (int i=0; i < formatters.size(); i++) {
res.addListener((TestListener)formatters.elementAt(i));
}

long start = System.currentTimeMillis();

fireStartTestSuite();
suite.run(res);
junitTest.setRunTime(System.currentTimeMillis()-start);
junitTest.setCounts(res.runCount(), res.failureCount(),
res.errorCount());
if (exception != null) { // had an exception in the constructor
for (int i=0; i < formatters.size(); i++) {
((TestListener)formatters.elementAt(i)).addError(null,
exception);
}
junitTest.setCounts(1, 0, 1);
junitTest.setRunTime(0);
} else {
suite.run(res);
junitTest.setCounts(res.runCount(), res.failureCount(),
res.errorCount());
junitTest.setRunTime(System.currentTimeMillis() - start);
}
fireEndTestSuite();

if (res.errorCount() != 0) {
retCode = 2;
if (retCode != SUCCESS || res.errorCount() != 0) {
retCode = ERRORS;
} else if (res.failureCount() != 0) {
retCode = 1;
retCode = FAILURES;
}
}

@@ -204,17 +210,14 @@ public class JUnitTestRunner implements TestListener {
*
* <p>A new Test is started.
*/
public void startTest(Test t) {
failed = false;
}
public void startTest(Test t) {}

/**
* Interface TestListener.
*
* <p>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 {
* <p>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 {
* <p>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.
*
* <table cols="3" border="1">
* <table cols="4" border="1">
* <tr><th>key</th><th>description</th><th>default value</th></tr>
*
* <tr><td>exit</td><td>exit with System.exit after testcase is
* complete?</td><td>true</td></tr>
*
* <tr><td>haltOnError</td><td>halt test on
* errors?</td><td>false</td></tr>
*
* <tr><td>haltOnFailure</td><td>halt test on
* failures?</td><td>false</td></tr>
*
* <tr><td>printSummary</td><td>print summary to System.out?</td>
* <td>true</td></tr>
*
* <tr><td>printXML</td><td>generate XML report?</td>
* <td>false</td></tr>
*
* <tr><td>outfile</td><td>where to print the XML report - a
* filename</td> <td>System.out</td></tr>
* <tr><td>formatter</td><td>A JUnitResultFormatter given as
* classname,filename. If filename is ommitted, System.out is
* assumed.</td><td>none</td></tr>
*
* </table>
* </table>
*/
public static void main(String[] args) throws IOException {
boolean exitAtEnd = true;
boolean haltError = false;
boolean haltFail = false;
boolean printSummary = true;
boolean printXml = false;
PrintWriter out = null;

if (args.length == 0) {
System.err.println("required argument TestClassName missing");
if (exitAtEnd) {
System.exit(2);
System.exit(ERRORS);
}

for (int i=1; i<args.length; i++) {
if (args[i].startsWith("haltOnError=")) {
haltError = Project.toBoolean(args[i].substring(12));
} else if (args[i].startsWith("haltOnFailure=")) {
haltFail = Project.toBoolean(args[i].substring(14));
} else if (args[i].startsWith("formatter=")) {
try {
createAndStoreFormatter(args[i].substring(10));
} catch (BuildException be) {
System.err.println(be.getMessage());
System.exit(ERRORS);
}
}
} else {
}
JUnitTest t = new JUnitTest(args[0]);
JUnitTestRunner runner = new JUnitTestRunner(t, haltError, haltFail);
transferFormatters(runner);
runner.run();
System.exit(runner.getRetCode());
}

JUnitTest test = new JUnitTest();
test.setName(args[0]);
args[0] = null;
test.setCommandline(args);
JUnitTestRunner runner = new JUnitTestRunner(test);
runner.run();
private static Vector fromCmdLine = new Vector();

if (exitAtEnd) {
System.exit(runner.getRetCode());
}
private static void transferFormatters(JUnitTestRunner runner) {
for (int i=0; i<fromCmdLine.size(); i++) {
runner.addFormatter((JUnitResultFormatter) fromCmdLine.elementAt(i));
}
}

private static void createAndStoreFormatter(String line)
throws BuildException {

public static int runTest(JUnitTest test) {
final JUnitTestRunner runner = new JUnitTestRunner(test);
runner.run();
return runner.getRetCode();
FormatterElement fe = new FormatterElement();
StringTokenizer tok = new StringTokenizer(line, ",");
fe.setClassname(tok.nextToken());
if (tok.hasMoreTokens()) {
fe.setOutfile(new java.io.File(tok.nextToken()));
}
fromCmdLine.addElement(fe.createFormatter());
}

} // JUnitTestRunner

+ 201
- 0
src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java View File

@@ -0,0 +1,201 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant.taskdefs.optional.junit;

import org.apache.tools.ant.BuildException;

import java.io.*;
import java.text.NumberFormat;

import junit.framework.Test;
import junit.framework.TestCase;

/**
* Prints plain text output of the test to a specified Writer.
*
* @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a>
*/

public class PlainJUnitResultFormatter implements JUnitResultFormatter {

/**
* Formatter for timings.
*/
private NumberFormat nf = NumberFormat.getInstance();
/**
* Timing helper.
*/
private long lastTestStart = 0;
/**
* Where to write the log to.
*/
private OutputStream out;
/**
* Helper to store intermediate output.
*/
private StringWriter inner;
/**
* Convenience layer on top of {@link #inner inner}.
*/
private PrintWriter wri;
/**
* Suppress endTest if testcase failed.
*/
private boolean failed = true;

public PlainJUnitResultFormatter() {
inner = new StringWriter();
wri = new PrintWriter(inner);
}

public void setOutput(OutputStream out) {
this.out = out;
}

/**
* Empty.
*/
public void startTestSuite(JUnitTest suite) {
}

/**
* The whole testsuite ended.
*/
public void endTestSuite(JUnitTest suite) throws BuildException {
StringBuffer sb = new StringBuffer("Testsuite: ");
sb.append(suite.getName());
sb.append(System.getProperty("line.separator"));
sb.append("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"));
sb.append(System.getProperty("line.separator"));

if (out != null) {
try {
out.write(sb.toString().getBytes());
wri.close();
out.write(inner.toString().getBytes());
out.flush();
} catch (IOException ioex) {
throw new BuildException("Unable to write output", ioex);
} finally {
try {
out.close();
} catch (IOException e) {}
}
}
}

/**
* Interface TestListener.
*
* <p>A new Test is started.
*/
public void startTest(Test t) {
lastTestStart = System.currentTimeMillis();
wri.print("Testcase: " + ((TestCase) t).name());
failed = false;
}

/**
* Interface TestListener.
*
* <p>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.
*
* <p>A Test failed.
*/
public void addFailure(Test test, Throwable t) {
formatError("\tFAILED", test, t);
}

/**
* Interface TestListener.
*
* <p>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

+ 47
- 40
src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java View File

@@ -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 <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a>
*/
@@ -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.
*
* <p>A new Test is started.
* Empty
*/
public void startTest(Test t) {
}

public SummaryJUnitResultFormatter() {}
/**
* Interface TestListener.
*
* <p>A Test is finished.
* Empty
*/
public void endTest(Test test) {
}

public void startTestSuite(JUnitTest suite) {}
/**
* Interface TestListener.
*
* <p>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.
*
* <p>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) {}
}
}
}

+ 150
- 73
src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java View File

@@ -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("<?xml version=\"1.0\"?>");
out.print("<testsuite name=\"");
out.print(suite.getName());
out.print("\" tests=\"");
out.print(suite.runCount());
out.print("\" failures=\"");
out.print(suite.failureCount());
out.print("\" errors=\"");
out.print(suite.errorCount());
out.print("\" time=\"");
out.print(nf.format(suite.getRunTime()/1000.0));
out.println(" sec\">");
out.print(results.toString());
out.println("</testsuite>");
out.flush();
out.close();
public void startTestSuite(JUnitTest suite) {
doc = getDocumentBuilder().newDocument();
rootElement = doc.createElement("testsuite");
rootElement.setAttribute("name", xmlEscape(suite.getName()));
}

/**
* The whole testsuite started.
* The whole testsuite ended.
*/
public void startTestSuite(JUnitTest suite) {
public void endTestSuite(JUnitTest suite) throws BuildException {
rootElement.setAttribute("tests", ""+suite.runCount());
rootElement.setAttribute("failures", ""+suite.failureCount());
rootElement.setAttribute("errors", ""+suite.errorCount());
rootElement.setAttribute("time",
nf.format(suite.getRunTime()/1000.0)+" sec");
if (out != null) {
Writer wri = null;
try {
wri = new OutputStreamWriter(out);
wri.write("<?xml version=\"1.0\"?>\n");
write(rootElement, wri, 0);
wri.flush();
} catch(IOException exc) {
throw new BuildException("Unable to write log file", exc);
} finally {
if (wri != null) {
try {
wri.close();
} catch (IOException e) {}
}
}
}
}

/**
@@ -130,6 +158,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter {
*/
public void startTest(Test t) {
lastTestStart = System.currentTimeMillis();
currentTest = doc.createElement("testcase");
currentTest.setAttribute("name", xmlEscape(((TestCase) t).name()));
rootElement.appendChild(currentTest);
}

/**
@@ -138,9 +169,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter {
* <p>A Test is finished.
*/
public void endTest(Test test) {
formatTestCaseOpenTag(test);
results.append(" </testcase>");
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("&gt;");
break;
case '\"':
temp.append("&quot;");
break;
case '&':
temp.append("&amp;");
break;
@@ -189,47 +248,65 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter {
return temp.toString();
}

private void formatTestCaseOpenTag(Test test) {
results.append(" <testcase");
if (test != null && test instanceof TestCase) {
results.append(" name=\"");
results.append(((TestCase) test).name());
results.append("\"");
/**
* Writes a DOM element to a stream.
*/
private static void write(Element element, Writer out, int indent) throws IOException {
// Write indent characters
for (int i = 0; i < indent; i++) {
out.write("\t");
}
results.append(" time=\"");
results.append(nf.format((System.currentTimeMillis()-lastTestStart)
/ 1000.0));
results.append("\">");
results.append(newLine);
}

private void formatError(String type, Test test, Throwable t) {
formatTestCaseOpenTag(test);
results.append(" <");
results.append(type);
results.append(" message=\"");
results.append(xmlEscape(t.getMessage()));
results.append("\" type=\"");
results.append(t.getClass().getName());
results.append("\">");
results.append(newLine);

results.append("<![CDATA[");
results.append(newLine);
StringWriter swr = new StringWriter();
t.printStackTrace(new PrintWriter(swr, true));
results.append(swr.toString());
results.append("]]>");
results.append(newLine);
// Write element
out.write("<");
out.write(element.getTagName());

results.append(" </");
results.append(type);
results.append(">");
results.append(newLine);
// Write attributes
NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Attr attr = (Attr) attrs.item(i);
out.write(" ");
out.write(attr.getName());
out.write("=\"");
out.write(attr.getValue());
out.write("\"");
}
out.write(">");

results.append(" </testcase>");
results.append(newLine);
}
// Write child attributes and text
boolean hasChildren = false;
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);

if (child.getNodeType() == Node.ELEMENT_NODE) {
if (!hasChildren) {
out.write("\n");
hasChildren = true;
}
write((Element)child, out, indent + 1);
}

if (child.getNodeType() == Node.TEXT_NODE) {
out.write("<![CDATA[");
out.write(((Text)child).getData());
out.write("]]>");
}
}

// If we had child elements, we need to indent before we close
// the element, otherwise we're on the same line and don't need
// to indent
if (hasChildren) {
for (int i = 0; i < indent; i++) {
out.write("\t");
}
}

// Write element close
out.write("</");
out.write(element.getTagName());
out.write(">\n");
}

} // XMLJUnitResultFormatter

Loading…
Cancel
Save