From 818cb969adef1df0f5918ba62f80f62502d5e53e Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 9 Aug 2000 13:12:13 +0000 Subject: [PATCH] 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 --- WHATSNEW | 4 +- build.xml | 13 +- docs/index.html | 1 + docs/junit.html | 313 ++++++++++++++++++ .../taskdefs/optional/junit/BatchTest.java | 179 ++++++++++ .../optional/junit/FormatterElement.java | 157 +++++++++ .../optional/junit/JUnitResultFormatter.java | 10 +- .../taskdefs/optional/junit/JUnitTask.java | 243 +++++++------- .../taskdefs/optional/junit/JUnitTest.java | 109 +++--- .../optional/junit/JUnitTestRunner.java | 216 ++++++------ .../junit/PlainJUnitResultFormatter.java | 201 +++++++++++ .../junit/SummaryJUnitResultFormatter.java | 87 ++--- .../junit/XMLJUnitResultFormatter.java | 223 +++++++++---- 13 files changed, 1341 insertions(+), 415 deletions(-) create mode 100644 docs/junit.html create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java diff --git a/WHATSNEW b/WHATSNEW index 675778943..951f7c376 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -20,9 +20,9 @@ org.apache.tools.ant to org.apache.tools.ant.types. Other changes: -------------- -* New tasks: sql, genkey, cab, ftp. +* New tasks: sql, genkey, cab, ftp, junit. -* New tasks junit, mparse, execon. All pending documentation, most of +* New tasks mparse, execon. All pending documentation, most of them pending review. * uses ClassLoader of its own in no-fork mode if a classpath is diff --git a/build.xml b/build.xml index 6cc470530..3ea136cc5 100644 --- a/build.xml +++ b/build.xml @@ -295,7 +295,7 @@ This would make the buildprocess fail if using an Ant version without the junit task - + @@ -303,11 +303,12 @@ - - - - - + + + + + + --> diff --git a/docs/index.html b/docs/index.html index dc5fdaedc..e7aae28d5 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3305,6 +3305,7 @@ and todo.html are excluded.

  • Cab
  • FTP
  • +
  • JUnit
  • NetRexxC
  • RenameExtensions
  • Script
  • diff --git a/docs/junit.html b/docs/junit.html new file mode 100644 index 000000000..ccf6f33c2 --- /dev/null +++ b/docs/junit.html @@ -0,0 +1,313 @@ + + + + + +

    JUnit

    +

    Description

    + +

    This task runs tests from the JUnit testing framework. The latest +version of the framework can be found at http://www.xprogramming.com/software.htm. +This task requires JUnit 3.0 or above.

    + +

    Tests are defined by nested test or +batchtest tags, see nested +elements.

    + +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescriptionRequired
    printsummaryPrint one line statistics for each testcase.No, default is "off"
    forkRun the tests in a separate VM.No, default is "off"
    haltonerrorStop the build process if an error occures during the test + run.No, default is "off"
    haltonfailureStop the build process if a test fails (errors are + considered failures as well).No, default is "off"
    timeoutCancel the individual tests if the don't finish + in the given time (measured in milliseconds). Ignored if fork is + disabled.No
    maxmemoryMax amount of memory to allocate to the forked VM + (ignored if fork is disabled)No
    jvmthe command used to invoke the Java Virtual Machine, + default is 'java'. The command is resolved by java.lang.Runtime.exec(). + Ignored if fork is disabled.No, default "java"
    + +

    Nested Elements

    + +

    junit supports a nested <classpath> +element, that represents a PATH like +structure. The value is ignore if fork is +disabled.

    + +

    jvmarg

    + +

    If fork is enabled, additional parameters may be passed to the new +VM via nested <jvmarg> attributes, for example:

    + +
    +<junit fork="yes"> + <jvmarg value="-Djava.compiler=NONE"/> +</junit> +
    +would run the test in a VM without JIT.

    + + + + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescriptionRequired
    valuea single command line argument.Exactly one of these.
    fileThe name of a file as a single command line argument.
    pathA 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.
    linea space delimited list of command line arguments.
    + +

    formatter

    + +

    The results of the tests can be printed in different +formats. Output will always be sent to a file, the name of the file is +determined by the name of the test and can be set by the +outfile attribute of <test>. + +

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

    + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescriptionRequired
    typeUse a predefined formatter (either "xml" or "plain").Exactly one of these.
    classnameName of a custo formatter class.
    extensionExtension to append to the output filename.Yes, if classname has been used.
    + +

    test

    + +

    Defines a single test class.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescriptionRequired
    nameName of the test classYes
    forkRun the tests in a separate VM. + Overrides value set in <junit>.No
    haltonerrorStop the build process if an error occures during the test + run. Overrides value set in <junit>.No
    haltonfailureStop the build process if a test fails (errors are + considered failures as well). Overrides value set in + <junit>.No
    outfileBasename of the test result. The full filename is + determined by this attribute and the extension of + formatter.No, default is + TEST-name using the name attribute.
    ifOnly run test if the named property is set.No
    unlessOnly run test if the named property is not set.No
    + +

    Tests can define their own formatters via nested +<formatter> elements.

    + +

    batchtest

    + +

    Define a number of tests based on pattern matching.

    + +

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

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescriptionRequired
    forkRun the tests in a separate VM. + Overrides value set in <junit>.No
    haltonerrorStop the build process if an error occures during the test + run. Overrides value set in <junit>.No
    haltonfailureStop the build process if a test fails (errors are + considered failures as well). Overrides value set in + <junit>.No
    ifOnly run tests if the named property is set.No
    unlessOnly run tests if the named property is not set.No
    + +

    Batchtests can define their own formatters via nested +<formatter> elements.

    + +

    Examples

    +
    +<junit> + <test name="my.test.TestCase" /> +</junit> +
    + +

    Runs the test defined in my.test.TestCase in the same +VM. No output will be generated unless the test fails.

    + +
    +<junit printsummary="yes" fork="yes" haltonfailure="yes"> + <formatter type="plain" /> + <test name="my.test.TestCase" /> +</junit> +
    + +

    Runs the test defined in my.test.TestCase in a +separate VM. At the end of the test a single line summary will be +printed. A detailed report of the test can be found in +TEST-my.test.TestCase.txt. The build process will be +stopped if the test fails.

    + +
    +<junit printsummary="yes" haltonfailure="yes"> + <classpath> + <pathelement location="${build.tests}" /> + <pathelement path="${java.class.path}" /> + </classpath> + + <formatter type="plain" /> + + <test name="my.test.TestCase" haltonfailure="no" outfile="result" > + <formatter type="xml" /> + </test> + + <batchtest fork="yes"> + <fileset dir="${src.tests}"> + <include name="**/*Test*.java" /> + <exclude name="**/AllTests.java" /< + </fileset> + </batchtest> +</junit> +
    + +

    Runs my.test.TestCase in the same VM (ignoring the +given CLASSPATH), only a warning is printed if this test fails. In +addition to the plain text testresults, for this test a XML result +will be output to result.xml.

    + +

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

    + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java new file mode 100644 index 000000000..3d04aeeac --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java @@ -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 + * . + */ + +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 Jeff Martin + * @author Stefan Bodewig + */ +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. + */ + +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 element in . + * + * @author Stefan Bodewig + */ +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"}; + } + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java index e8bd1692f..82e18ea45 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java @@ -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); } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java index 771e1035b..76de6db82 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java @@ -82,49 +82,38 @@ import java.util.Vector; * unless fork has been disabled. * * @author Thomas Haas + * @author Stefan Bodewig */ public class JUnitTask extends Task { - // private final static int HALT_NOT = 1; - // private final static int HALT_IMMEDIATELY = 2; - // 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; - // private int haltOnError = HALT_AT_END; - // 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 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 ; } - - // later add HALT_AT_END option - // Then test errorOccurred and failureOccurred here. } protected ExecuteWatchdog createWatchdog() throws BuildException { diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java index 6ed484af4..dab32d73c 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java @@ -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 Stefan Bodewig */ 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; iA new Test is started. */ - public void startTest(Test t) { - failed = false; - } + public void startTest(Test t) {} /** * Interface TestListener. * *

    A Test is finished. */ - public void endTest(Test test) { - } + public void endTest(Test test) {} /** * Interface TestListener. @@ -222,9 +225,7 @@ public class JUnitTestRunner implements TestListener { *

    A Test failed. */ public void addFailure(Test test, Throwable t) { - failed = true; - - if (junitTest.getHaltonfailure()) { + if (haltOnFailure) { res.stop(); } } @@ -235,9 +236,7 @@ public class JUnitTestRunner implements TestListener { *

    An error occured while running the test. */ public void addError(Test test, Throwable t) { - failed = true; - - if (junitTest.getHaltonerror()) { + if (haltOnError) { res.stop(); } } @@ -261,65 +260,74 @@ public class JUnitTestRunner implements TestListener { /** * Entry point for standalone (forked) mode. * - * Parameters: testcaseclassname plus (up to) 6 parameters in the - * format key=value. + * Parameters: testcaseclassname plus parameters in the format + * key=value, none of which is required. * - * + *
    * * - * - * * * * * - * - * - * - * - * - * - * + * * - *
    keydescriptiondefault value
    exitexit with System.exit after testcase is - * complete?true
    haltOnErrorhalt test on * errors?false
    haltOnFailurehalt test on * failures?false
    printSummaryprint summary to System.out?true
    printXMLgenerate XML report?false
    outfilewhere to print the XML report - a - * filename System.out
    formatterA JUnitResultFormatter given as + * classname,filename. If filename is ommitted, System.out is + * assumed.none
    + * */ 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. + */ + +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 Stefan Bodewig + */ + +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. + * + *

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

    A Test is finished. + */ + public void endTest(Test test) { + if (failed) return; + wri.println(" took " + + nf.format((System.currentTimeMillis()-lastTestStart) + / 1000.0) + + " sec"); + } + + /** + * Interface TestListener. + * + *

    A Test failed. + */ + public void addFailure(Test test, Throwable t) { + formatError("\tFAILED", test, t); + } + + /** + * Interface TestListener. + * + *

    An error occured while running the test. + */ + public void addError(Test test, Throwable t) { + formatError("\tCaused an ERROR", test, t); + } + + private void formatError(String type, Test test, Throwable t) { + if (test != null) { + endTest(test); + } + failed = true; + + wri.println(type); + wri.println(t.getMessage()); + t.printStackTrace(wri); + wri.println(""); + } +} // PlainJUnitResultFormatter diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java index 4580b4db0..ea7751d51 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java @@ -55,10 +55,14 @@ package org.apache.tools.ant.taskdefs.optional.junit; import java.text.NumberFormat; +import java.io.IOException; +import java.io.OutputStream; import junit.framework.Test; +import org.apache.tools.ant.BuildException; + /** - * Prints short summary output of the test to System.out + * Prints short summary output of the test to Ant's logging system. * * @author Stefan Bodewig */ @@ -69,60 +73,63 @@ public class SummaryJUnitResultFormatter implements JUnitResultFormatter { * Formatter for timings. */ private NumberFormat nf = NumberFormat.getInstance(); - - public SummaryJUnitResultFormatter() { - } - /** - * The whole testsuite started. + * OutputStream to write to. */ - public void startTestSuite(JUnitTest suite) { - } + private OutputStream out; /** - * Interface TestListener. - * - *

    A new Test is started. + * Empty */ - public void startTest(Test t) { - } - + public SummaryJUnitResultFormatter() {} /** - * Interface TestListener. - * - *

    A Test is finished. + * Empty */ - public void endTest(Test test) { - } - + public void startTestSuite(JUnitTest suite) {} /** - * Interface TestListener. - * - *

    A Test failed. + * Empty */ - public void addFailure(Test test, Throwable t) { - } - + public void startTest(Test t) {} + /** + * Empty + */ + public void endTest(Test test) {} + /** + * Empty + */ + public void addFailure(Test test, Throwable t) {} /** - * Interface TestListener. - * - *

    An error occured while running the test. + * Empty */ - public void addError(Test test, Throwable t) { + public void addError(Test test, Throwable t) {} + + public void setOutput(OutputStream out) { + this.out = out; } /** * The whole testsuite ended. */ - public void endTestSuite(JUnitTest suite) { - System.out.print("Tests run: "); - System.out.print(suite.runCount()); - System.out.print(", Failures: "); - System.out.print(suite.failureCount()); - System.out.print(", Errors: "); - System.out.print(suite.errorCount()); - System.out.print(", Time ellapsed: "); - System.out.print(nf.format(suite.getRunTime()/1000.0)); - System.out.println(" sec"); + public void endTestSuite(JUnitTest suite) throws BuildException { + StringBuffer sb = new StringBuffer("Tests run: "); + sb.append(suite.runCount()); + sb.append(", Failures: "); + sb.append(suite.failureCount()); + sb.append(", Errors: "); + sb.append(suite.errorCount()); + sb.append(", Time elapsed: "); + sb.append(nf.format(suite.getRunTime()/1000.0)); + sb.append(" sec"); + sb.append(System.getProperty("line.separator")); + try { + out.write(sb.toString().getBytes()); + out.flush(); + } catch (IOException ioex) { + throw new BuildException("Unable to write summary output", ioex); + } finally { + try { + out.close(); + } catch (IOException e) {} + } } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java index 8d2f0ce62..f9e66fc73 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java @@ -54,11 +54,15 @@ package org.apache.tools.ant.taskdefs.optional.junit; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.CharacterIterator; +import java.io.*; import java.text.NumberFormat; +import java.text.CharacterIterator; import java.text.StringCharacterIterator; +import java.util.*; +import javax.xml.parsers.*; +import org.w3c.dom.*; +import org.apache.tools.ant.BuildException; + import junit.framework.Test; import junit.framework.TestCase; @@ -70,57 +74,81 @@ import junit.framework.TestCase; public class XMLJUnitResultFormatter implements JUnitResultFormatter { + private static DocumentBuilder getDocumentBuilder() { + try { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + catch(Exception exc) { + throw new ExceptionInInitializerError(exc); + } + } + /** - * OutputStream for XML output. + * Formatter for timings. */ - private PrintWriter out; + private NumberFormat nf = NumberFormat.getInstance(); /** - * Collects output during the test run. + * The XML document. */ - private StringBuffer results = new StringBuffer(); + private Document doc; /** - * platform independent line separator. + * The wrapper for the whole testsuite. */ - private static String newLine = System.getProperty("line.separator"); + private Element rootElement; /** - * Formatter for timings. + * Element for the current test. */ - private NumberFormat nf = NumberFormat.getInstance(); + private Element currentTest; /** * Timing helper. */ private long lastTestStart = 0; + /** + * Where to write the log to. + */ + private OutputStream out; - public XMLJUnitResultFormatter(PrintWriter out) { + public XMLJUnitResultFormatter() {} + + public void setOutput(OutputStream out) { this.out = out; } /** - * The whole testsuite ended. + * The whole testsuite started. */ - public void endTestSuite(JUnitTest suite) { - out.println(""); - out.print(""); - out.print(results.toString()); - out.println(""); - 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("\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 { *

    A Test is finished. */ public void endTest(Test test) { - formatTestCaseOpenTag(test); - results.append(" "); - results.append(newLine); + currentTest.setAttribute("time", + nf.format((System.currentTimeMillis()-lastTestStart) + / 1000.0)); } /** @@ -161,8 +192,33 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { formatError("error", test, t); } + private void formatError(String type, Test test, Throwable t) { + if (test != null) { + endTest(test); + } + + Element nested = doc.createElement(type); + if (test != null) { + currentTest.appendChild(nested); + } else { + rootElement.appendChild(nested); + } + + String message = t.getMessage(); + if (message != null && message.length() > 0) { + nested.setAttribute("message", xmlEscape(t.getMessage())); + } + nested.setAttribute("type", xmlEscape(t.getClass().getName())); + + StringWriter swr = new StringWriter(); + t.printStackTrace(new PrintWriter(swr, true)); + Text trace = doc.createTextNode(swr.toString()); + nested.appendChild(trace); + } + + /** - * Translates <, & and > to corresponding entities. + * Translates <, & , " and > to corresponding entities. */ private String xmlEscape(String orig) { if (orig == null) return ""; @@ -178,6 +234,9 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { case '>': temp.append(">"); break; + case '\"': + temp.append("""); + break; case '&': temp.append("&"); break; @@ -189,47 +248,65 @@ public class XMLJUnitResultFormatter implements JUnitResultFormatter { return temp.toString(); } - private void formatTestCaseOpenTag(Test test) { - results.append(" "); - 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(""); - results.append(newLine); + // Write element + out.write("<"); + out.write(element.getTagName()); - 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(" "); - 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(""); + } + } + + // 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("\n"); + } } // XMLJUnitResultFormatter