git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267904 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -20,9 +20,9 @@ org.apache.tools.ant to org.apache.tools.ant.types. | |||||
| Other changes: | 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. | them pending review. | ||||
| * <java> uses ClassLoader of its own in no-fork mode if a classpath is | * <java> uses ClassLoader of its own in no-fork mode if a classpath is | ||||
| @@ -295,7 +295,7 @@ | |||||
| This would make the buildprocess fail if using an Ant version | This would make the buildprocess fail if using an Ant version | ||||
| without the junit task | without the junit task | ||||
| <junit defaultprintxml="false" defaultprintsummary="true" fork="on"> | |||||
| <junit printsummary="yes" fork="yes" haltonfailure="yes"> | |||||
| <classpath> | <classpath> | ||||
| <pathelement location="${lib.dir}/${name}.jar" /> | <pathelement location="${lib.dir}/${name}.jar" /> | ||||
| <pathelement location="${build.tests}" /> | <pathelement location="${build.tests}" /> | ||||
| @@ -303,11 +303,12 @@ | |||||
| <pathelement path="${java.class.path}" /> | <pathelement path="${java.class.path}" /> | ||||
| </classpath> | </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> | </junit> | ||||
| --> | --> | ||||
| @@ -3305,6 +3305,7 @@ and <code>todo.html</code> are excluded.</p> | |||||
| <ul> | <ul> | ||||
| <li><a href="#cab">Cab</a></li> | <li><a href="#cab">Cab</a></li> | ||||
| <li><a href="#ftp">FTP</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="#netrexxc">NetRexxC</a></li> | ||||
| <li><a href="#renameexts">RenameExtensions</a></li> | <li><a href="#renameexts">RenameExtensions</a></li> | ||||
| <li><a href="#script">Script</a></li> | <li><a href="#script">Script</a></li> | ||||
| @@ -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 "java"</td> | |||||
| </tr> | |||||
| </table> | |||||
| <h3><a name="nested">Nested Elements</a></h3> | |||||
| <p><code>junit</code> supports a nested <code><classpath></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><jvmarg></code> attributes, for example:</p> | |||||
| <pre><blockquote> | |||||
| <junit fork="yes"> | |||||
| <jvmarg value="-Djava.compiler=NONE"/> | |||||
| </junit> | |||||
| </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><test></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><junit></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><junit></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><junit></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><formatter></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><fileset></code> and | |||||
| <code><filesetref></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><junit></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><junit></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><junit></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><formatter></code> elements.</p> | |||||
| <h3>Examples</h3> | |||||
| <pre><blockquote> | |||||
| <junit> | |||||
| <test name="my.test.TestCase" /> | |||||
| </junit> | |||||
| </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> | |||||
| <junit printsummary="yes" fork="yes" haltonfailure="yes"> | |||||
| <formatter type="plain" /> | |||||
| <test name="my.test.TestCase" /> | |||||
| </junit> | |||||
| </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> | |||||
| <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> | |||||
| </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> | |||||
| @@ -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, '.'); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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"}; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -54,6 +54,7 @@ | |||||
| package org.apache.tools.ant.taskdefs.optional.junit; | package org.apache.tools.ant.taskdefs.optional.junit; | ||||
| import org.apache.tools.ant.BuildException; | |||||
| import junit.framework.TestListener; | import junit.framework.TestListener; | ||||
| /** | /** | ||||
| @@ -66,10 +67,15 @@ public interface JUnitResultFormatter extends TestListener { | |||||
| /** | /** | ||||
| * The whole testsuite started. | * The whole testsuite started. | ||||
| */ | */ | ||||
| public void startTestSuite(JUnitTest suite); | |||||
| public void startTestSuite(JUnitTest suite) throws BuildException; | |||||
| /** | /** | ||||
| * The whole testsuite ended. | * 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); | |||||
| } | } | ||||
| @@ -82,49 +82,38 @@ import java.util.Vector; | |||||
| * unless <code>fork</code> has been disabled. | * unless <code>fork</code> has been disabled. | ||||
| * | * | ||||
| * @author Thomas Haas | * @author Thomas Haas | ||||
| * @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a> | |||||
| */ | */ | ||||
| public class JUnitTask extends Task { | 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 CommandlineJava commandline = new CommandlineJava(); | ||||
| private Vector tests = new Vector(); | private Vector tests = new Vector(); | ||||
| private Vector batchTests = new Vector(); | |||||
| private Vector formatters = new Vector(); | |||||
| private JUnitTest defaults = new JUnitTest(); | private JUnitTest defaults = new JUnitTest(); | ||||
| private boolean defaultOutfile = true; | |||||
| private Integer timeout = null; | 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); | defaults.setHaltonerror(value); | ||||
| } | } | ||||
| public void setDefaulthaltonfailure(boolean value) { | |||||
| public void setHaltonfailure(boolean value) { | |||||
| defaults.setHaltonfailure(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) { | public void setTimeout(Integer value) { | ||||
| @@ -136,42 +125,36 @@ public class JUnitTask extends Task { | |||||
| } | } | ||||
| public void setJvm(String value) { | public void setJvm(String value) { | ||||
| defaults.setName(value); | |||||
| commandline.setVm(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() { | public Path createClasspath() { | ||||
| return commandline.createClasspath(project); | 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. | * 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 errorOccurred = false; | ||||
| boolean failureOccurred = 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()) { | 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 { | } 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 { | 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() | if (errorOccurredHere && test.getHaltonerror() | ||||
| || failureOccurredHere && test.getHaltonfailure()) { | || 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 { | protected ExecuteWatchdog createWatchdog() throws BuildException { | ||||
| @@ -57,36 +57,37 @@ package org.apache.tools.ant.taskdefs.optional.junit; | |||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.types.Commandline; | import org.apache.tools.ant.types.Commandline; | ||||
| import java.io.File; | |||||
| import java.util.Vector; | |||||
| /** | /** | ||||
| * | * | ||||
| * @author Thomas Haas | * @author Thomas Haas | ||||
| * @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a> | |||||
| */ | */ | ||||
| public class JUnitTest { | public class JUnitTest { | ||||
| private boolean systemExit = false; | |||||
| private boolean haltOnError = false; | private boolean haltOnError = false; | ||||
| private boolean haltOnFail = false; | private boolean haltOnFail = false; | ||||
| private boolean printSummary = true; | |||||
| private boolean printXml = true; | |||||
| private String name = null; | private String name = null; | ||||
| private String outfile = null; | |||||
| private File outfile = null; | |||||
| private boolean fork = false; | private boolean fork = false; | ||||
| private long runs, failures, errors; | private long runs, failures, errors; | ||||
| private long runTime; | private long runTime; | ||||
| private Vector formatters = new Vector(); | |||||
| public JUnitTest() { | 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.haltOnError = haltOnError; | ||||
| this.haltOnFail = haltOnFail; | this.haltOnFail = haltOnFail; | ||||
| this.printSummary = printSummary; | |||||
| this.printXml = printXml; | |||||
| this.name = name; | |||||
| this.outfile = outfile; | |||||
| } | } | ||||
| public void setFork(boolean value) { | public void setFork(boolean value) { | ||||
| @@ -105,23 +106,14 @@ public class JUnitTest { | |||||
| haltOnFail = value; | haltOnFail = value; | ||||
| } | } | ||||
| public void setPrintsummary(boolean value) { | |||||
| printSummary = value; | |||||
| } | |||||
| public void setPrintxml(boolean value) { | |||||
| printXml = value; | |||||
| } | |||||
| public void setName(String value) { | public void setName(String value) { | ||||
| name = value; | name = value; | ||||
| } | } | ||||
| public void setOutfile(String value) { | |||||
| public void setOutfile(File value) { | |||||
| outfile = value; | outfile = value; | ||||
| } | } | ||||
| public boolean getHaltonerror() { | public boolean getHaltonerror() { | ||||
| return haltOnError; | return haltOnError; | ||||
| } | } | ||||
| @@ -130,53 +122,15 @@ public class JUnitTest { | |||||
| return haltOnFail; | return haltOnFail; | ||||
| } | } | ||||
| public boolean getPrintsummary() { | |||||
| return printSummary; | |||||
| } | |||||
| public boolean getPrintxml() { | |||||
| return printXml; | |||||
| } | |||||
| public String getName() { | public String getName() { | ||||
| return name; | return name; | ||||
| } | } | ||||
| public String getOutfile() { | 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) { | public void setCounts(long runs, long failures, long errors) { | ||||
| @@ -194,9 +148,34 @@ public class JUnitTest { | |||||
| public long errorCount() {return errors;} | public long errorCount() {return errors;} | ||||
| public long getRunTime() {return runTime;} | 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; | |||||
| } | |||||
| } | } | ||||
| @@ -54,11 +54,13 @@ | |||||
| package org.apache.tools.ant.taskdefs.optional.junit; | package org.apache.tools.ant.taskdefs.optional.junit; | ||||
| import org.apache.tools.ant.BuildException; | |||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import junit.framework.*; | import junit.framework.*; | ||||
| import java.lang.reflect.*; | import java.lang.reflect.*; | ||||
| import java.io.*; | import java.io.*; | ||||
| import java.util.StringTokenizer; | |||||
| import java.util.Vector; | import java.util.Vector; | ||||
| /** | /** | ||||
| @@ -78,6 +80,21 @@ import java.util.Vector; | |||||
| public class JUnitTestRunner implements TestListener { | 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. | * Holds the registered formatters. | ||||
| */ | */ | ||||
| @@ -89,14 +106,14 @@ public class JUnitTestRunner implements TestListener { | |||||
| private TestResult res; | 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. | * The corresponding testsuite. | ||||
| @@ -104,38 +121,29 @@ public class JUnitTestRunner implements TestListener { | |||||
| private Test suite = null; | 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 { | try { | ||||
| Method suiteMethod= testClass.getMethod("suite", new Class[0]); | Method suiteMethod= testClass.getMethod("suite", new Class[0]); | ||||
| suite = (Test)suiteMethod.invoke(null, new Class[0]); | suite = (Test)suiteMethod.invoke(null, new Class[0]); | ||||
| @@ -143,50 +151,48 @@ public class JUnitTestRunner implements TestListener { | |||||
| } catch(InvocationTargetException e) { | } catch(InvocationTargetException e) { | ||||
| } catch(IllegalAccessException e) { | } catch(IllegalAccessException e) { | ||||
| } | } | ||||
| if (suite == null) { | if (suite == null) { | ||||
| // try to extract a test suite automatically | // try to extract a test suite automatically | ||||
| // this will generate warnings if the class is no suitable Test | // this will generate warnings if the class is no suitable Test | ||||
| suite= new TestSuite(testClass); | 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) { | } 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() { | 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(); | 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(); | fireEndTestSuite(); | ||||
| if (res.errorCount() != 0) { | |||||
| retCode = 2; | |||||
| if (retCode != SUCCESS || res.errorCount() != 0) { | |||||
| retCode = ERRORS; | |||||
| } else if (res.failureCount() != 0) { | } else if (res.failureCount() != 0) { | ||||
| retCode = 1; | |||||
| retCode = FAILURES; | |||||
| } | } | ||||
| } | } | ||||
| @@ -204,17 +210,14 @@ public class JUnitTestRunner implements TestListener { | |||||
| * | * | ||||
| * <p>A new Test is started. | * <p>A new Test is started. | ||||
| */ | */ | ||||
| public void startTest(Test t) { | |||||
| failed = false; | |||||
| } | |||||
| public void startTest(Test t) {} | |||||
| /** | /** | ||||
| * Interface TestListener. | * Interface TestListener. | ||||
| * | * | ||||
| * <p>A Test is finished. | * <p>A Test is finished. | ||||
| */ | */ | ||||
| public void endTest(Test test) { | |||||
| } | |||||
| public void endTest(Test test) {} | |||||
| /** | /** | ||||
| * Interface TestListener. | * Interface TestListener. | ||||
| @@ -222,9 +225,7 @@ public class JUnitTestRunner implements TestListener { | |||||
| * <p>A Test failed. | * <p>A Test failed. | ||||
| */ | */ | ||||
| public void addFailure(Test test, Throwable t) { | public void addFailure(Test test, Throwable t) { | ||||
| failed = true; | |||||
| if (junitTest.getHaltonfailure()) { | |||||
| if (haltOnFailure) { | |||||
| res.stop(); | res.stop(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -235,9 +236,7 @@ public class JUnitTestRunner implements TestListener { | |||||
| * <p>An error occured while running the test. | * <p>An error occured while running the test. | ||||
| */ | */ | ||||
| public void addError(Test test, Throwable t) { | public void addError(Test test, Throwable t) { | ||||
| failed = true; | |||||
| if (junitTest.getHaltonerror()) { | |||||
| if (haltOnError) { | |||||
| res.stop(); | res.stop(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -261,65 +260,74 @@ public class JUnitTestRunner implements TestListener { | |||||
| /** | /** | ||||
| * Entry point for standalone (forked) mode. | * 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><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 | * <tr><td>haltOnError</td><td>halt test on | ||||
| * errors?</td><td>false</td></tr> | * errors?</td><td>false</td></tr> | ||||
| * | * | ||||
| * <tr><td>haltOnFailure</td><td>halt test on | * <tr><td>haltOnFailure</td><td>halt test on | ||||
| * failures?</td><td>false</td></tr> | * 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 { | public static void main(String[] args) throws IOException { | ||||
| boolean exitAtEnd = true; | boolean exitAtEnd = true; | ||||
| boolean haltError = false; | boolean haltError = false; | ||||
| boolean haltFail = false; | boolean haltFail = false; | ||||
| boolean printSummary = true; | |||||
| boolean printXml = false; | |||||
| PrintWriter out = null; | |||||
| if (args.length == 0) { | if (args.length == 0) { | ||||
| System.err.println("required argument TestClassName missing"); | 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 | } // JUnitTestRunner | ||||
| @@ -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 | |||||
| @@ -55,10 +55,14 @@ | |||||
| package org.apache.tools.ant.taskdefs.optional.junit; | package org.apache.tools.ant.taskdefs.optional.junit; | ||||
| import java.text.NumberFormat; | import java.text.NumberFormat; | ||||
| import java.io.IOException; | |||||
| import java.io.OutputStream; | |||||
| import junit.framework.Test; | 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> | * @author <a href="mailto:stefan.bodewig@megabit.net">Stefan Bodewig</a> | ||||
| */ | */ | ||||
| @@ -69,60 +73,63 @@ public class SummaryJUnitResultFormatter implements JUnitResultFormatter { | |||||
| * Formatter for timings. | * Formatter for timings. | ||||
| */ | */ | ||||
| private NumberFormat nf = NumberFormat.getInstance(); | 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. | * 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) {} | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -54,11 +54,15 @@ | |||||
| package org.apache.tools.ant.taskdefs.optional.junit; | 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.NumberFormat; | ||||
| import java.text.CharacterIterator; | |||||
| import java.text.StringCharacterIterator; | 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.Test; | ||||
| import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
| @@ -70,57 +74,81 @@ import junit.framework.TestCase; | |||||
| public class XMLJUnitResultFormatter implements JUnitResultFormatter { | 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. | * Timing helper. | ||||
| */ | */ | ||||
| private long lastTestStart = 0; | 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; | 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) { | public void startTest(Test t) { | ||||
| lastTestStart = System.currentTimeMillis(); | 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. | * <p>A Test is finished. | ||||
| */ | */ | ||||
| public void endTest(Test test) { | 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); | 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) { | private String xmlEscape(String orig) { | ||||
| if (orig == null) return ""; | if (orig == null) return ""; | ||||
| @@ -178,6 +234,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||||
| case '>': | case '>': | ||||
| temp.append(">"); | temp.append(">"); | ||||
| break; | break; | ||||
| case '\"': | |||||
| temp.append("""); | |||||
| break; | |||||
| case '&': | case '&': | ||||
| temp.append("&"); | temp.append("&"); | ||||
| break; | break; | ||||
| @@ -189,47 +248,65 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { | |||||
| return temp.toString(); | 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 | } // XMLJUnitResultFormatter | ||||