aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java346
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java241
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java202
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java300
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java46
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java90
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java228
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java177
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java448
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java401
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java52
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java99
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java193
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java65
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java2283
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java190
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java109
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java542
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java1297
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java179
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java36
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java317
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java213
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java144
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java35
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java63
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java141
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java366
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java329
29 files changed, 9132 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java
new file mode 100644
index 00000000..ec3506d4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Delete;
+import org.apache.tools.ant.taskdefs.TempFile;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.URLResource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.w3c.dom.Document;
+
+/**
+ * Transform a JUnit xml report.
+ * The default transformation generates an html report in either framed or non-framed
+ * style. The non-framed style is convenient to have a concise report via mail, the
+ * framed report is much more convenient if you want to browse into different
+ * packages or testcases since it is a Javadoc like report.
+ *
+ */
+public class AggregateTransformer {
+ /**
+ * name of the frames format.
+ */
+ public static final String FRAMES = "frames";
+
+ /**
+ * name of the no frames format.
+ */
+ public static final String NOFRAMES = "noframes";
+
+ /**
+ * defines acceptable formats.
+ */
+ public static class Format extends EnumeratedAttribute {
+ /**
+ * list authorized values.
+ * @return authorized values.
+ */
+ public String[] getValues() {
+ return new String[]{FRAMES, NOFRAMES};
+ }
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** Task */
+ protected Task task;
+
+ /** the xml document to process */
+ protected Document document;
+
+ /** the style directory. XSLs should be read from here if necessary */
+ protected File styleDir;
+
+ /** the destination directory, this is the root from where html should be generated */
+ protected File toDir;
+
+ /**
+ * The internal XSLT task used to perform the transformation.
+ *
+ * @since Ant 1.9.5
+ */
+ private XSLTProcess xsltTask;
+
+ /**
+ * Instance of a utility class to use for file operations.
+ *
+ * @since Ant 1.7
+ */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Used to ensure the uniqueness of a property
+ */
+ private static int counter = 0;
+
+ /** the format to use for the report. Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> */
+ protected String format = FRAMES;
+
+ /** XML Parser factory */
+ private static DocumentBuilderFactory privateDBFactory;
+
+ /** XML Parser factory accessible to subclasses */
+ protected static DocumentBuilderFactory dbfactory;
+
+ static {
+ privateDBFactory = DocumentBuilderFactory.newInstance();
+ dbfactory = privateDBFactory;
+ }
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * constructor creating the transformer from the junitreport task.
+ * @param task task delegating to this class
+ */
+ public AggregateTransformer(Task task) {
+ this.task = task;
+ xsltTask = new XSLTProcess();
+ xsltTask.bindToOwner(task);
+ }
+
+ /**
+ * Get the Document Builder Factory
+ *
+ * @return the DocumentBuilderFactory instance in use
+ */
+ protected static DocumentBuilderFactory getDocumentBuilderFactory() {
+ return privateDBFactory;
+ }
+
+ /**
+ * sets the format.
+ * @param format Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt>
+ */
+ public void setFormat(Format format) {
+ this.format = format.getValue();
+ }
+
+ /**
+ * sets the input document.
+ * @param doc input dom tree
+ */
+ public void setXmlDocument(Document doc) {
+ this.document = doc;
+ }
+
+ /**
+ * Set the xml file to be processed. This is a helper if you want
+ * to set the file directly. Much more for testing purposes.
+ * @param xmlfile xml file to be processed
+ * @throws BuildException if the document cannot be parsed.
+ */
+ protected void setXmlfile(File xmlfile) throws BuildException {
+ try {
+ DocumentBuilder builder = privateDBFactory.newDocumentBuilder();
+ InputStream in = new FileInputStream(xmlfile);
+ try {
+ Document doc = builder.parse(in);
+ setXmlDocument(doc);
+ } finally {
+ in.close();
+ }
+ } catch (Exception e) {
+ throw new BuildException("Error while parsing document: " + xmlfile, e);
+ }
+ }
+
+ /**
+ * set the style directory. It is optional and will override the
+ * default xsl used.
+ * @param styledir the directory containing the xsl files if the user
+ * would like to override with its own style.
+ */
+ public void setStyledir(File styledir) {
+ this.styleDir = styledir;
+ }
+
+ /** set the destination directory.
+ * @param todir the destination directory
+ */
+ public void setTodir(File todir) {
+ this.toDir = todir;
+ }
+
+ /** set the extension of the output files
+ * @param ext extension.
+ */
+ public void setExtension(String ext) {
+ task.log("extension is not used anymore", Project.MSG_WARN);
+ }
+
+ /**
+ * Create an instance of an XSL parameter for configuration by Ant.
+ *
+ * @return an instance of the Param class to be configured.
+ * @since Ant 1.7
+ */
+ public XSLTProcess.Param createParam() {
+ return xsltTask.createParam();
+ }
+
+ /**
+ * Creates a classpath to be used for the internal XSLT task.
+ *
+ * @return the classpath to be configured
+ * @since Ant 1.9.5
+ */
+ public Path createClasspath() {
+ return xsltTask.createClasspath();
+ }
+
+ /**
+ * Creates a factory configuration to be used for the internal XSLT task.
+ *
+ * @return the factory description to be configured
+ * @since Ant 1.9.5
+ */
+ public XSLTProcess.Factory createFactory() {
+ return xsltTask.createFactory();
+ }
+
+ /**
+ * transformation
+ * @throws BuildException exception if something goes wrong with the transformation.
+ */
+ public void transform() throws BuildException {
+ checkOptions();
+ Project project = task.getProject();
+
+ TempFile tempFileTask = new TempFile();
+ tempFileTask.bindToOwner(task);
+
+ xsltTask.setXslResource(getStylesheet());
+
+ // acrobatic cast.
+ xsltTask.setIn(((XMLResultAggregator) task).getDestinationFile());
+ File outputFile = null;
+ if (format.equals(FRAMES)) {
+ String tempFileProperty = getClass().getName() + String.valueOf(counter++);
+ File tmp = FILE_UTILS.resolveFile(project.getBaseDir(), project
+ .getProperty("java.io.tmpdir"));
+ tempFileTask.setDestDir(tmp);
+ tempFileTask.setProperty(tempFileProperty);
+ tempFileTask.execute();
+ outputFile = new File(project.getProperty(tempFileProperty));
+ } else {
+ outputFile = new File(toDir, "junit-noframes.html");
+ }
+ xsltTask.setOut(outputFile);
+ XSLTProcess.Param paramx = xsltTask.createParam();
+ paramx.setProject(task.getProject());
+ paramx.setName("output.dir");
+ paramx.setExpression(toDir.getAbsolutePath());
+ final long t0 = System.currentTimeMillis();
+ try {
+ xsltTask.execute();
+ } catch (Exception e) {
+ throw new BuildException("Errors while applying transformations: " + e.getMessage(), e);
+ }
+ final long dt = System.currentTimeMillis() - t0;
+ task.log("Transform time: " + dt + "ms");
+ if (format.equals(FRAMES)) {
+ Delete delete = new Delete();
+ delete.bindToOwner(task);
+ delete.setFile(outputFile);
+ delete.execute();
+ }
+ }
+
+ /**
+ * access the stylesheet to be used as a resource.
+ * @return stylesheet as a resource
+ */
+ protected Resource getStylesheet() {
+ String xslname = "junit-frames.xsl";
+ if (NOFRAMES.equals(format)) {
+ xslname = "junit-noframes.xsl";
+ }
+ if (styleDir == null) {
+ // If style dir is not specified we have to retrieve
+ // the stylesheet from the classloader
+ URL stylesheetURL = getClass().getClassLoader().getResource(
+ "org/apache/tools/ant/taskdefs/optional/junit/xsl/" + xslname);
+ return new URLResource(stylesheetURL);
+ }
+ // If we are here, then the style dir is here and we
+ // should read the stylesheet from the filesystem
+ return new FileResource(new File(styleDir, xslname));
+ }
+
+ /** check for invalid options
+ * @throws BuildException if something goes wrong.
+ */
+ protected void checkOptions() throws BuildException {
+ // set the destination directory relative from the project if needed.
+ if (toDir == null) {
+ toDir = task.getProject().resolveFile(".");
+ } else if (!toDir.isAbsolute()) {
+ toDir = task.getProject().resolveFile(toDir.getPath());
+ }
+ }
+
+ /**
+ * Get the systemid of the appropriate stylesheet based on its
+ * name and styledir. If no styledir is defined it will load
+ * it as a java resource in the xsl child package, otherwise it
+ * will get it from the given directory.
+ * @return system ID of the stylesheet.
+ * @throws IOException thrown if the requested stylesheet does
+ * not exist.
+ */
+ protected String getStylesheetSystemId() throws IOException {
+ String xslname = "junit-frames.xsl";
+ if (NOFRAMES.equals(format)) {
+ xslname = "junit-noframes.xsl";
+ }
+ if (styleDir == null) {
+ URL url = getClass().getResource("xsl/" + xslname);
+ if (url == null) {
+ throw new FileNotFoundException("Could not find jar resource " + xslname);
+ }
+ return url.toExternalForm();
+ }
+ File file = new File(styleDir, xslname);
+ if (!file.exists()) {
+ throw new FileNotFoundException("Could not find file '" + file + "'");
+ }
+ return JAXPUtils.getSystemId(file);
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java
new file mode 100644
index 00000000..55e7a5d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.util.Vector;
+
+/**
+ * Baseclass for BatchTest and JUnitTest.
+ *
+ */
+public abstract class BaseTest {
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected boolean haltOnError = false;
+ protected boolean haltOnFail = false;
+ protected boolean filtertrace = true;
+ protected boolean fork = false;
+ protected String ifProperty = null;
+ protected String unlessProperty = null;
+ protected Vector formatters = new Vector();
+ /** destination directory */
+ protected File destDir = null;
+
+ protected String failureProperty;
+ protected String errorProperty;
+ // CheckStyle:VisibilityModifier ON
+
+ private Object ifCond, unlessCond;
+ private boolean skipNonTests;
+
+ /**
+ * Set the filtertrace attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setFiltertrace(boolean value) {
+ filtertrace = value;
+ }
+
+ /**
+ * Get the filtertrace attribute.
+ * @return the attribute.
+ */
+ public boolean getFiltertrace() {
+ return filtertrace;
+ }
+
+ /**
+ * Set the fork attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setFork(boolean value) {
+ fork = value;
+ }
+
+ /**
+ * Get the fork attribute.
+ * @return the attribute.
+ */
+ public boolean getFork() {
+ return fork;
+ }
+
+ /**
+ * Set the haltonerror attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setHaltonerror(boolean value) {
+ haltOnError = value;
+ }
+
+ /**
+ * Set the haltonfailure attribute.
+ * @param value a <code>boolean</code> value.
+ */
+ public void setHaltonfailure(boolean value) {
+ haltOnFail = value;
+ }
+
+ /**
+ * Get the haltonerror attribute.
+ * @return the attribute.
+ */
+ public boolean getHaltonerror() {
+ return haltOnError;
+ }
+
+ /**
+ * Get the haltonfailure attribute.
+ * @return the attribute.
+ */
+ public boolean getHaltonfailure() {
+ return haltOnFail;
+ }
+
+ /**
+ * Set the if attribute.
+ * If this expression evaluates to true or the name of a property
+ * which is present in project, the test will be run.
+ * @param ifCondition the expression to evaluate
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object ifCondition) {
+ ifCond = ifCondition;
+ ifProperty = ifCondition != null ? String.valueOf(ifCondition) : null;
+ }
+
+ /**
+ * Set the if attribute.
+ * If this expression evaluates to true or the name of a property
+ * which is present in project, the test will be run.
+ * @param propertyName the expression to evaluate
+ */
+ public void setIf(String propertyName) {
+ setIf((Object) propertyName);
+ }
+
+ /**
+ * The if expression
+ * @since Ant 1.8.0
+ */
+ public Object getIfCondition() {
+ return ifCond;
+ }
+
+ /**
+ * Set the unless attribute. If this expression evaluates to
+ * false or the name of a property which is not present in
+ * project, the test will be run.
+ * @param unlessCondition the expression to evaluate
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object unlessCondition) {
+ unlessCond = unlessCondition;
+ unlessProperty = unlessCondition != null
+ ? String.valueOf(unlessCondition) : null;
+ }
+
+ /**
+ * Set the unless attribute. If this expression evaluates to
+ * false or the name of a property which is not present in
+ * project, the test will be run.
+ * @param propertyName the expression to evaluate
+ */
+ public void setUnless(String propertyName) {
+ setUnless((Object) propertyName);
+ }
+
+ /**
+ * The unless expression
+ * @since Ant 1.8.0
+ */
+ public Object getUnlessCondition() {
+ return unlessCond;
+ }
+
+ /**
+ * Allow a formatter nested element.
+ * @param elem a formatter nested element.
+ */
+ public void addFormatter(FormatterElement elem) {
+ formatters.addElement(elem);
+ }
+
+ /**
+ * Sets the destination directory.
+ * @param destDir the destination directory.
+ */
+ public void setTodir(File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Get the destination directory.
+ * @return the destination directory as an absolute path if it exists
+ * otherwise return <tt>null</tt>
+ */
+ public String getTodir() {
+ if (destDir != null) {
+ return destDir.getAbsolutePath();
+ }
+ return null;
+ }
+
+ /**
+ * Get the failure property name.
+ * @return the name of the property to set on failure.
+ */
+ public String getFailureProperty() {
+ return failureProperty;
+ }
+
+ /**
+ * Set the name of the failure property.
+ * @param failureProperty the name of the property to set if
+ * the test fails.
+ */
+ public void setFailureProperty(String failureProperty) {
+ this.failureProperty = failureProperty;
+ }
+
+ /**
+ * Get the failure property name.
+ * @return the name of the property to set on failure.
+ */
+ public String getErrorProperty() {
+ return errorProperty;
+ }
+
+ /**
+ * Set the name of the error property.
+ * @param errorProperty the name of the property to set if
+ * the test has an error.
+ */
+ public void setErrorProperty(String errorProperty) {
+ this.errorProperty = errorProperty;
+ }
+
+ public void setSkipNonTests(boolean skipNonTests) {
+ this.skipNonTests = skipNonTests;
+ }
+
+ public boolean isSkipNonTests() {
+ return skipNonTests;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java
new file mode 100644
index 00000000..f41b96f1
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Resources;
+
+/**
+ * <p> Create then run <code>JUnitTest</code>'s based on the list of files
+ * given by the fileset attribute.
+ *
+ * <p> Every <code>.java</code> or <code>.class</code> file in the fileset is
+ * assumed to be a testcase.
+ * A <code>JUnitTest</code> is created for each of these named classes with
+ * basic setup inherited from the parent <code>BatchTest</code>.
+ *
+ * @see JUnitTest
+ */
+public final class BatchTest extends BaseTest {
+
+ /** the reference to the project */
+ private Project project;
+
+ /** the list of filesets containing the testcase filename rules */
+ private Resources resources = new Resources();
+
+ /**
+ * create a new batchtest instance
+ * @param project the project it depends on.
+ */
+ public BatchTest(Project project) {
+ this.project = project;
+ resources.setCache(true);
+ }
+
+ /**
+ * Add a new fileset instance to this batchtest. Whatever the fileset is,
+ * only filename that are <tt>.java</tt> or <tt>.class</tt> will be
+ * considered as 'candidates'.
+ * @param fs the new fileset containing the rules to get the testcases.
+ */
+ public void addFileSet(FileSet fs) {
+ add(fs);
+
+ // this one is here because the changes to support ResourceCollections
+ // have broken Magic's JUnitTestTask.
+ //
+ // The task adds a FileSet to a BatchTest instance using the
+ // Java API and without telling the FileSet about its project
+ // instance. The original code would pass in project on the
+ // call to getDirectoryScanner - which is now hidden deep into
+ // Resources.iterator() and not reachable.
+ if (fs.getProject() == null) {
+ fs.setProject(project);
+ }
+ }
+
+
+ /**
+ * Add a new ResourceCollection instance to this
+ * batchtest. Whatever the collection is, only names that are
+ * <tt>.java</tt> or <tt>.class</tt> will be considered as
+ * 'candidates'.
+ * @param rc the new ResourceCollection containing the rules to
+ * get the testcases.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ resources.add(rc);
+ }
+
+ /**
+ * Return all <tt>JUnitTest</tt> instances obtain by applying the fileset rules.
+ * @return an enumeration of all elements of this batchtest that are
+ * a <tt>JUnitTest</tt> instance.
+ */
+ public Enumeration elements() {
+ JUnitTest[] tests = createAllJUnitTest();
+ return Enumerations.fromArray(tests);
+ }
+
+ /**
+ * Convenient method to merge the <tt>JUnitTest</tt>s of this batchtest
+ * to a <tt>Vector</tt>.
+ * @param v the vector to which should be added all individual tests of this
+ * batch test.
+ */
+ void addTestsTo(Vector v) {
+ JUnitTest[] tests = createAllJUnitTest();
+ v.ensureCapacity(v.size() + tests.length);
+ for (int i = 0; i < tests.length; i++) {
+ v.addElement(tests[i]);
+ }
+ }
+
+ /**
+ * Create all <tt>JUnitTest</tt>s based on the filesets. Each instance
+ * is configured to match this instance properties.
+ * @return the array of all <tt>JUnitTest</tt>s that belongs to this batch.
+ */
+ private JUnitTest[] createAllJUnitTest() {
+ String[] filenames = getFilenames();
+ JUnitTest[] tests = new JUnitTest[filenames.length];
+ for (int i = 0; i < tests.length; i++) {
+ String classname = javaToClass(filenames[i]);
+ tests[i] = createJUnitTest(classname);
+ }
+ return tests;
+ }
+
+ /**
+ * Iterate over all filesets and return the filename of all files
+ * that end with <tt>.java</tt> or <tt>.class</tt>. This is to avoid
+ * wrapping a <tt>JUnitTest</tt> over an xml file for example. A Testcase
+ * is obviously a java file (compiled or not).
+ * @return an array of filenames without their extension. As they should
+ * normally be taken from their root, filenames should match their fully
+ * qualified class name (If it is not the case it will fail when running the test).
+ * For the class <tt>org/apache/Whatever.class</tt> it will return <tt>org/apache/Whatever</tt>.
+ */
+ private String[] getFilenames() {
+ Vector v = new Vector();
+ for (Resource r : resources) {
+ if (r.isExists()) {
+ String pathname = r.getName();
+ if (pathname.endsWith(".java")) {
+ v.addElement(pathname.substring(0, pathname.length() - ".java".length()));
+ } else if (pathname.endsWith(".class")) {
+ v.addElement(pathname.substring(0, pathname.length() - ".class".length()));
+ }
+ }
+ }
+
+ String[] files = new String[v.size()];
+ v.copyInto(files);
+ return files;
+ }
+
+ /**
+ * Convenient method to convert a pathname without extension to a
+ * fully qualified classname. For example <tt>org/apache/Whatever</tt> will
+ * be converted to <tt>org.apache.Whatever</tt>
+ * @param filename the filename to "convert" to a classname.
+ * @return the classname matching the filename.
+ */
+ public static String javaToClass(String filename) {
+ return filename.replace(File.separatorChar, '.').replace('/', '.')
+ .replace('\\', '.');
+ }
+
+ /**
+ * Create a <tt>JUnitTest</tt> that has the same property as this
+ * <tt>BatchTest</tt> instance.
+ * @param classname the name of the class that should be run as a
+ * <tt>JUnitTest</tt>. It must be a fully qualified name.
+ * @return the <tt>JUnitTest</tt> over the given classname.
+ */
+ private JUnitTest createJUnitTest(String classname) {
+ JUnitTest test = new JUnitTest();
+ test.setName(classname);
+ test.setHaltonerror(this.haltOnError);
+ test.setHaltonfailure(this.haltOnFail);
+ test.setFiltertrace(this.filtertrace);
+ test.setFork(this.fork);
+ test.setIf(getIfCondition());
+ test.setUnless(getUnlessCondition());
+ test.setTodir(this.destDir);
+ test.setFailureProperty(failureProperty);
+ test.setErrorProperty(errorProperty);
+ test.setSkipNonTests(isSkipNonTests());
+ Enumeration list = this.formatters.elements();
+ while (list.hasMoreElements()) {
+ test.addFormatter((FormatterElement) list.nextElement());
+ }
+ return test;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java
new file mode 100644
index 00000000..46d6c616
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Prints plain text output of the test to a specified Writer.
+ * Inspired by the PlainJUnitResultFormatter.
+ *
+ * @see FormatterElement
+ * @see PlainJUnitResultFormatter
+ */
+public class BriefJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /**
+ * Where to write the log to.
+ */
+ private OutputStream out;
+
+ /**
+ * Used for writing the results.
+ */
+ private BufferedWriter output;
+
+ /**
+ * Used as part of formatting the results.
+ */
+ private StringWriter results;
+
+ /**
+ * Used for writing formatted results to.
+ */
+ private BufferedWriter resultWriter;
+
+ /**
+ * Formatter for timings.
+ */
+ private NumberFormat numberFormat = NumberFormat.getInstance();
+
+ /**
+ * Output suite has written to System.out
+ */
+ private String systemOutput = null;
+
+ /**
+ * Output suite has written to System.err
+ */
+ private String systemError = null;
+
+ /**
+ * Constructor for BriefJUnitResultFormatter.
+ */
+ public BriefJUnitResultFormatter() {
+ results = new StringWriter();
+ resultWriter = new BufferedWriter(results);
+ }
+
+ /**
+ * Sets the stream the formatter is supposed to write its results to.
+ * @param out the output stream to write to
+ */
+ public void setOutput(OutputStream out) {
+ this.out = out;
+ output = new BufferedWriter(new java.io.OutputStreamWriter(out));
+ }
+
+ /**
+ * @see JUnitResultFormatter#setSystemOutput(String)
+ */
+ /** {@inheritDoc}. */
+ public void setSystemOutput(String out) {
+ systemOutput = out;
+ }
+
+ /**
+ * @see JUnitResultFormatter#setSystemError(String)
+ */
+ /** {@inheritDoc}. */
+ public void setSystemError(String err) {
+ systemError = err;
+ }
+
+
+ /**
+ * The whole testsuite started.
+ * @param suite the test suite
+ */
+ public void startTestSuite(JUnitTest suite) {
+ if (output == null) {
+ return; // Quick return - no output do nothing.
+ }
+ StringBuffer sb = new StringBuffer("Testsuite: ");
+ sb.append(suite.getName());
+ sb.append(StringUtils.LINE_SEP);
+ try {
+ output.write(sb.toString());
+ output.flush();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the test suite
+ */
+ public void endTestSuite(JUnitTest suite) {
+ 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(", Skipped: ");
+ sb.append(suite.skipCount());
+ sb.append(", Time elapsed: ");
+ sb.append(numberFormat.format(suite.getRunTime() / ONE_SECOND));
+ sb.append(" sec");
+ sb.append(StringUtils.LINE_SEP);
+ sb.append(StringUtils.LINE_SEP);
+
+ // append the err and output streams to the log
+ if (systemOutput != null && systemOutput.length() > 0) {
+ sb.append("------------- Standard Output ---------------")
+ .append(StringUtils.LINE_SEP)
+ .append(systemOutput)
+ .append("------------- ---------------- ---------------")
+ .append(StringUtils.LINE_SEP);
+ }
+
+ if (systemError != null && systemError.length() > 0) {
+ sb.append("------------- Standard Error -----------------")
+ .append(StringUtils.LINE_SEP)
+ .append(systemError)
+ .append("------------- ---------------- ---------------")
+ .append(StringUtils.LINE_SEP);
+ }
+
+ if (output != null) {
+ try {
+ output.write(sb.toString());
+ resultWriter.close();
+ output.write(results.toString());
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ try {
+ output.flush();
+ } catch (IOException ex) {
+ // swallow, there has likely been an exception before this
+ }
+ if (out != System.out && out != System.err) {
+ FileUtils.close(out);
+ }
+ }
+ }
+ }
+
+ /**
+ * A test started.
+ * @param test a test
+ */
+ public void startTest(Test test) {
+ }
+
+ /**
+ * A test ended.
+ * @param test a test
+ */
+ public void endTest(Test test) {
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test a test
+ * @param t the exception thrown by the test
+ */
+ public void addFailure(Test test, Throwable t) {
+ formatError("\tFAILED", test, t);
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test a test
+ * @param t the assertion failed by the test
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * A test caused an error.
+ * @param test a test
+ * @param error the error thrown by the test
+ */
+ public void addError(Test test, Throwable error) {
+ formatError("\tCaused an ERROR", test, error);
+ }
+
+ /**
+ * Format the test for printing..
+ * @param test a test
+ * @return the formatted testname
+ */
+ protected String formatTest(Test test) {
+ if (test == null) {
+ return "Null Test: ";
+ } else {
+ return "Testcase: " + test.toString() + ":";
+ }
+ }
+
+ /**
+ * Format an error and print it.
+ * @param type the type of error
+ * @param test the test that failed
+ * @param error the exception that the test threw
+ */
+ protected synchronized void formatError(String type, Test test,
+ Throwable error) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ try {
+ resultWriter.write(formatTest(test) + type);
+ resultWriter.newLine();
+ resultWriter.write(String.valueOf(error.getMessage()));
+ resultWriter.newLine();
+ String strace = JUnitTestRunner.getFilteredTrace(error);
+ resultWriter.write(strace);
+ resultWriter.newLine();
+ resultWriter.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+
+ public void testIgnored(Test test) {
+ formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
+ }
+
+
+ public void formatSkip(Test test, String message) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ try {
+ resultWriter.write(formatTest(test) + "SKIPPED");
+ if (message != null) {
+ resultWriter.write(": ");
+ resultWriter.write(message);
+ }
+ resultWriter.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+
+ }
+
+ public void testAssumptionFailure(Test test, Throwable cause) {
+ formatSkip(test, cause.getMessage());
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java
new file mode 100644
index 00000000..368c72e2
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Constants.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+/**
+ * Constants, like filenames shared between various classes in this package.
+ */
+public class Constants {
+
+ private Constants() {
+ }
+
+ static final String METHOD_NAMES = "methods=";
+ static final String HALT_ON_ERROR = "haltOnError=";
+ static final String HALT_ON_FAILURE = "haltOnFailure=";
+ static final String FILTERTRACE = "filtertrace=";
+ static final String CRASHFILE = "crashfile=";
+ static final String BEFORE_FIRST_TEST = "BeforeFirstTest";
+ static final String PROPSFILE = "propsfile=";
+ static final String SHOWOUTPUT = "showoutput=";
+ static final String OUTPUT_TO_FORMATTERS = "outputtoformatters=";
+ static final String FORMATTER = "formatter=";
+ static final String LOGTESTLISTENEREVENTS = "logtestlistenerevents=";
+ static final String TESTSFILE = "testsfile=";
+ static final String TERMINATED_SUCCESSFULLY = "terminated successfully";
+ static final String LOG_FAILED_TESTS="logfailedtests=";
+ static final String SKIP_NON_TESTS = "skipNonTests=";
+ /** @since Ant 1.9.4 */
+ static final String THREADID="threadid=";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java
new file mode 100644
index 00000000..8ad40dd6
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/CustomJUnit4TestAdapterCache.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.JUnit4TestAdapterCache;
+import junit.framework.TestResult;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * Provides a custom implementation of the notifier for a JUnit4TestAdapter
+ * so that skipped and ignored tests can be reported to the existing
+ * <tt>TestListener</tt>s.
+ *
+ */
+public class CustomJUnit4TestAdapterCache extends JUnit4TestAdapterCache {
+
+ private static final CustomJUnit4TestAdapterCache INSTANCE = new CustomJUnit4TestAdapterCache();
+
+ public static CustomJUnit4TestAdapterCache getInstance() {
+ return INSTANCE;
+ }
+
+ private CustomJUnit4TestAdapterCache() {
+ super();
+ }
+
+ public RunNotifier getNotifier(final TestResult result, final JUnit4TestAdapter adapter) {
+ return getNotifier(result);
+ }
+
+ public RunNotifier getNotifier(final TestResult result) {
+
+ final IgnoredTestResult resultWrapper = (IgnoredTestResult) result;
+
+ RunNotifier notifier = new RunNotifier();
+ notifier.addListener(new RunListener() {
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ result.addError(asTest(failure.getDescription()), failure.getException());
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ result.endTest(asTest(description));
+ }
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ result.startTest(asTest(description));
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ if (resultWrapper != null) {
+ resultWrapper.testIgnored(asTest(description));
+ }
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ if (resultWrapper != null) {
+ resultWrapper.testAssumptionFailure(asTest(failure.getDescription()), failure.getException());
+ }
+ }
+ });
+
+ return notifier;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java
new file mode 100644
index 00000000..325f44cf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Vector;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+/**
+ * Some utilities that might be useful when manipulating DOM trees.
+ *
+ */
+public final class DOMUtil {
+
+ /** unused constructor */
+ private DOMUtil() {
+ }
+
+ /**
+ * Filter interface to be applied when iterating over a DOM tree.
+ * Just think of it like a <tt>FileFilter</tt> clone.
+ */
+ public interface NodeFilter {
+ /**
+ * @param node the node to check for acceptance.
+ * @return <tt>true</tt> if the node is accepted by this filter,
+ * otherwise <tt>false</tt>
+ */
+ boolean accept(Node node);
+ }
+
+ /**
+ * list a set of node that match a specific filter. The list can be made
+ * recursively or not.
+ * @param parent the parent node to search from
+ * @param filter the filter that children should match.
+ * @param recurse <tt>true</tt> if you want the list to be made recursively
+ * otherwise <tt>false</tt>.
+ * @return the node list that matches the filter.
+ */
+ public static NodeList listChildNodes(Node parent, NodeFilter filter, boolean recurse) {
+ NodeListImpl matches = new NodeListImpl();
+ NodeList children = parent.getChildNodes();
+ if (children != null) {
+ final int len = children.getLength();
+ for (int i = 0; i < len; i++) {
+ Node child = children.item(i);
+ if (filter.accept(child)) {
+ matches.addElement(child);
+ }
+ if (recurse) {
+ NodeList recmatches = listChildNodes(child, filter, recurse);
+ final int reclength = recmatches.getLength();
+ for (int j = 0; j < reclength; j++) {
+ matches.addElement(recmatches.item(i));
+ }
+ }
+ }
+ }
+ return matches;
+ }
+
+ /** custom implementation of a nodelist */
+ public static class NodeListImpl extends Vector implements NodeList {
+ private static final long serialVersionUID = 3175749150080946423L;
+
+ /**
+ * Get the number of nodes in the list.
+ * @return the length of the list.
+ */
+ public int getLength() {
+ return size();
+ }
+ /**
+ * Get a particular node.
+ * @param i the index of the node to get.
+ * @return the node if the index is in bounds, null otherwise.
+ */
+ public Node item(int i) {
+ try {
+ return (Node) elementAt(i);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null; // conforming to NodeList interface
+ }
+ }
+ }
+
+ /**
+ * return the attribute value of an element.
+ * @param node the node to get the attribute from.
+ * @param name the name of the attribute we are looking for the value.
+ * @return the value of the requested attribute or <tt>null</tt> if the
+ * attribute was not found or if <tt>node</tt> is not an <tt>Element</tt>.
+ */
+ public static String getNodeAttribute(Node node, String name) {
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ return element.getAttribute(name);
+ }
+ return null;
+ }
+
+
+ /**
+ * Iterate over the children of a given node and return the first node
+ * that has a specific name.
+ * @param parent the node to search child from. Can be <tt>null</tt>.
+ * @param tagname the child name we are looking for. Cannot be <tt>null</tt>.
+ * @return the first child that matches the given name or <tt>null</tt> if
+ * the parent is <tt>null</tt> or if a child does not match the
+ * given name.
+ */
+ public static Element getChildByTagName (Node parent, String tagname) {
+ if (parent == null) {
+ return null;
+ }
+ NodeList childList = parent.getChildNodes();
+ final int len = childList.getLength();
+ for (int i = 0; i < len; i++) {
+ Node child = childList.item(i);
+ if (child != null && child.getNodeType() == Node.ELEMENT_NODE
+ && child.getNodeName().equals(tagname)) {
+ return (Element) child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Simple tree walker that will clone recursively a node. This is to
+ * avoid using parser-specific API such as Sun's <tt>changeNodeOwner</tt>
+ * when we are dealing with DOM L1 implementations since <tt>cloneNode(boolean)</tt>
+ * will not change the owner document.
+ * <tt>changeNodeOwner</tt> is much faster and avoid the costly cloning process.
+ * <tt>importNode</tt> is in the DOM L2 interface.
+ * @param parent the node parent to which we should do the import to.
+ * @param child the node to clone recursively. Its clone will be
+ * appended to <tt>parent</tt>.
+ * @return the cloned node that is appended to <tt>parent</tt>
+ */
+ public static Node importNode(Node parent, Node child) {
+ Node copy = null;
+ final Document doc = parent.getOwnerDocument();
+
+ switch (child.getNodeType()) {
+ case Node.CDATA_SECTION_NODE:
+ copy = doc.createCDATASection(((CDATASection) child).getData());
+ break;
+ case Node.COMMENT_NODE:
+ copy = doc.createComment(((Comment) child).getData());
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ copy = doc.createDocumentFragment();
+ break;
+ case Node.ELEMENT_NODE:
+ final Element elem = doc.createElement(((Element) child).getTagName());
+ copy = elem;
+ final NamedNodeMap attributes = child.getAttributes();
+ if (attributes != null) {
+ final int size = attributes.getLength();
+ for (int i = 0; i < size; i++) {
+ final Attr attr = (Attr) attributes.item(i);
+ elem.setAttribute(attr.getName(), attr.getValue());
+ }
+ }
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ copy = doc.createEntityReference(child.getNodeName());
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ final ProcessingInstruction pi = (ProcessingInstruction) child;
+ copy = doc.createProcessingInstruction(pi.getTarget(), pi.getData());
+ break;
+ case Node.TEXT_NODE:
+ copy = doc.createTextNode(((Text) child).getData());
+ break;
+ default:
+ // this should never happen
+ throw new IllegalStateException("Invalid node type: " + child.getNodeType());
+ }
+
+ // okay we have a copy of the child, now the child becomes the parent
+ // and we are iterating recursively over its children.
+ try {
+ final NodeList children = child.getChildNodes();
+ if (children != null) {
+ final int size = children.getLength();
+ for (int i = 0; i < size; i++) {
+ final Node newChild = children.item(i);
+ if (newChild != null) {
+ importNode(copy, newChild);
+ }
+ }
+ }
+ } catch (DOMException ignored) {
+ // Ignore
+ }
+
+ // bingo append it. (this should normally not be done here)
+ parent.appendChild(copy);
+ return copy;
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java
new file mode 100644
index 00000000..327547ef
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * A couple of methods related to enumerations that might be useful.
+ * This class should probably disappear once the required JDK is set to 1.2
+ * instead of 1.1.
+ *
+ */
+public final class Enumerations {
+
+ private Enumerations() {
+ }
+
+ /**
+ * creates an enumeration from an array of objects.
+ * @param array the array of object to enumerate.
+ * @return the enumeration over the array of objects.
+ */
+ public static Enumeration fromArray(Object[] array) {
+ return new ArrayEnumeration(array);
+ }
+
+ /**
+ * creates an enumeration from an array of enumeration. The created enumeration
+ * will sequentially enumerate over all elements of each enumeration and skip
+ * <tt>null</tt> enumeration elements in the array.
+ * @param enums the array of enumerations.
+ * @return the enumeration over the array of enumerations.
+ */
+ public static Enumeration fromCompound(Enumeration[] enums) {
+ return new CompoundEnumeration(enums);
+ }
+
+}
+
+
+/**
+ * Convenient enumeration over an array of objects.
+ */
+class ArrayEnumeration implements Enumeration {
+
+ /** object array */
+ private Object[] array;
+
+ /** current index */
+ private int pos;
+
+ /**
+ * Initialize a new enumeration that wraps an array.
+ * @param array the array of object to enumerate.
+ */
+ public ArrayEnumeration(Object[] array) {
+ this.array = array;
+ this.pos = 0;
+ }
+ /**
+ * Tests if this enumeration contains more elements.
+ *
+ * @return <code>true</code> if and only if this enumeration object
+ * contains at least one more element to provide;
+ * <code>false</code> otherwise.
+ */
+ public boolean hasMoreElements() {
+ return (pos < array.length);
+ }
+
+ /**
+ * Returns the next element of this enumeration if this enumeration
+ * object has at least one more element to provide.
+ *
+ * @return the next element of this enumeration.
+ * @throws NoSuchElementException if no more elements exist.
+ */
+ public Object nextElement() throws NoSuchElementException {
+ if (hasMoreElements()) {
+ Object o = array[pos];
+ pos++;
+ return o;
+ }
+ throw new NoSuchElementException();
+ }
+}
+/**
+ * Convenient enumeration over an array of enumeration. For example:
+ * <pre>
+ * Enumeration e1 = v1.elements();
+ * while (e1.hasMoreElements()) {
+ * // do something
+ * }
+ * Enumeration e2 = v2.elements();
+ * while (e2.hasMoreElements()) {
+ * // do the same thing
+ * }
+ * </pre>
+ * can be written as:
+ * <pre>
+ * Enumeration[] enums = { v1.elements(), v2.elements() };
+ * Enumeration e = Enumerations.fromCompound(enums);
+ * while (e.hasMoreElements()) {
+ * // do something
+ * }
+ * </pre>
+ * Note that the enumeration will skip null elements in the array. The following is
+ * thus possible:
+ * <pre>
+ * Enumeration[] enums = { v1.elements(), null, v2.elements() }; // a null enumeration in the array
+ * Enumeration e = Enumerations.fromCompound(enums);
+ * while (e.hasMoreElements()) {
+ * // do something
+ * }
+ * </pre>
+ */
+ class CompoundEnumeration implements Enumeration {
+
+ /** enumeration array */
+ private Enumeration[] enumArray;
+
+ /** index in the enums array */
+ private int index = 0;
+
+ public CompoundEnumeration(Enumeration[] enumarray) {
+ this.enumArray = enumarray;
+ }
+
+ /**
+ * Tests if this enumeration contains more elements.
+ *
+ * @return <code>true</code> if and only if this enumeration object
+ * contains at least one more element to provide;
+ * <code>false</code> otherwise.
+ */
+ public boolean hasMoreElements() {
+ while (index < enumArray.length) {
+ if (enumArray[index] != null && enumArray[index].hasMoreElements()) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the next element of this enumeration if this enumeration
+ * object has at least one more element to provide.
+ *
+ * @return the next element of this enumeration.
+ * @throws NoSuchElementException if no more elements exist.
+ */
+ public Object nextElement() throws NoSuchElementException {
+ if (hasMoreElements()) {
+ return enumArray[index].nextElement();
+ }
+ throw new NoSuchElementException();
+ }
+}
+
+
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java
new file mode 100644
index 00000000..3046b75a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FailureRecorder.java
@@ -0,0 +1,448 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * <p>Collects all failing test <i>cases</i> and creates a new JUnit test class containing
+ * a suite() method which calls these failed tests.</p>
+ * <p>Having classes <i>A</i> ... <i>D</i> with each several testcases you could earn a new
+ * test class like
+ * <pre>
+ * // generated on: 2007.08.06 09:42:34,555
+ * import junit.framework.*;
+ * public class FailedTests extends TestCase {
+ * public FailedTests(String testname) {
+ * super(testname);
+ * }
+ * public static Test suite() {
+ * TestSuite suite = new TestSuite();
+ * suite.addTest( new B("test04") );
+ * suite.addTest( new org.D("test10") );
+ * return suite;
+ * }
+ * }
+ * </pre>
+ *
+ * Because each running test case gets its own formatter, we collect
+ * the failing test cases in a static list. Because we dont have a finalizer
+ * method in the formatters "lifecycle", we register this formatter as
+ * BuildListener and generate the new java source on taskFinished event.
+ *
+ * @since Ant 1.8.0
+ */
+public class FailureRecorder extends ProjectComponent implements JUnitResultFormatter, BuildListener {
+
+ /**
+ * This is the name of a magic System property ({@value}). The value of this
+ * <b>System</b> property should point to the location where to store the
+ * generated class (without suffix).
+ * Default location and name is defined in DEFAULT_CLASS_LOCATION.
+ * @see #DEFAULT_CLASS_LOCATION
+ */
+ public static final String MAGIC_PROPERTY_CLASS_LOCATION
+ = "ant.junit.failureCollector";
+
+ /** Default location and name for the generated JUnit class file,
+ * in the temp directory + FailedTests */
+ public static final String DEFAULT_CLASS_LOCATION
+ = System.getProperty("java.io.tmpdir") + "FailedTests";
+
+ /** Prefix for logging. {@value} */
+ private static final String LOG_PREFIX = " [junit]";
+
+ /** Class names of failed tests without duplicates. */
+ private static SortedSet/*<TestInfos>*/ failedTests = new TreeSet();
+
+ /** A writer for writing the generated source to. */
+ private BufferedWriter writer;
+
+ /**
+ * Location and name of the generated JUnit class.
+ * Lazy instantiated via getLocationName().
+ */
+ private static String locationName;
+
+ /**
+ * Returns the (lazy evaluated) location for the collector class.
+ * Order for evaluation: System property > Ant property > default value
+ * @return location for the collector class
+ * @see #MAGIC_PROPERTY_CLASS_LOCATION
+ * @see #DEFAULT_CLASS_LOCATION
+ */
+ private String getLocationName() {
+ if (locationName == null) {
+ String syspropValue = System.getProperty(MAGIC_PROPERTY_CLASS_LOCATION);
+ String antpropValue = getProject().getProperty(MAGIC_PROPERTY_CLASS_LOCATION);
+
+ if (syspropValue != null) {
+ locationName = syspropValue;
+ verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use "
+ + "its value '" + syspropValue + "' as location for collector class.");
+ } else if (antpropValue != null) {
+ locationName = antpropValue;
+ verbose("Ant property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' set, so use "
+ + "its value '" + antpropValue + "' as location for collector class.");
+ } else {
+ locationName = DEFAULT_CLASS_LOCATION;
+ verbose("System property '" + MAGIC_PROPERTY_CLASS_LOCATION + "' not set, so use "
+ + "value as location for collector class: '"
+ + DEFAULT_CLASS_LOCATION + "'");
+ }
+
+ File locationFile = new File(locationName);
+ if (!locationFile.isAbsolute()) {
+ File f = new File(getProject().getBaseDir(), locationName);
+ locationName = f.getAbsolutePath();
+ verbose("Location file is relative (" + locationFile + ")"
+ + " use absolute path instead (" + locationName + ")");
+ }
+ }
+
+ return locationName;
+ }
+
+ /**
+ * This method is called by the Ant runtime by reflection. We use the project reference for
+ * registration of this class as BuildListener.
+ *
+ * @param project
+ * project reference
+ */
+ public void setProject(Project project) {
+ // store project reference for logging
+ super.setProject(project);
+ // check if already registered
+ boolean alreadyRegistered = false;
+ Vector allListeners = project.getBuildListeners();
+ final int size = allListeners.size();
+ for (int i = 0; i < size; i++) {
+ Object listener = allListeners.get(i);
+ if (listener instanceof FailureRecorder) {
+ alreadyRegistered = true;
+ break;
+ }
+ }
+ // register if needed
+ if (!alreadyRegistered) {
+ verbose("Register FailureRecorder (@" + this.hashCode() + ") as BuildListener");
+ project.addBuildListener(this);
+ }
+ }
+
+ // ===== JUnitResultFormatter =====
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ }
+
+ /**
+ * Add the failed test to the list.
+ * @param test the test that errored.
+ * @param throwable the reason it errored.
+ * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable)
+ */
+ public void addError(Test test, Throwable throwable) {
+ failedTests.add(new TestInfos(test));
+ }
+
+ // CheckStyle:LineLengthCheck OFF - @see is long
+ /**
+ * Add the failed test to the list.
+ * @param test the test that failed.
+ * @param error the assertion that failed.
+ * @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError)
+ */
+ // CheckStyle:LineLengthCheck ON
+ public void addFailure(Test test, AssertionFailedError error) {
+ failedTests.add(new TestInfos(test));
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void setOutput(OutputStream out) {
+ // unused, close output file so it can be deleted before the VM exits
+ if (out != System.out) {
+ FileUtils.close(out);
+ }
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void setSystemError(String err) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void setSystemOutput(String out) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void startTestSuite(JUnitTest suite) throws BuildException {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void endTest(Test test) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void startTest(Test test) {
+ }
+
+ // ===== "Templates" for generating the JUnit class =====
+
+ private void writeJavaClass() {
+ try {
+ File sourceFile = new File((getLocationName() + ".java"));
+ verbose("Write collector class to '" + sourceFile.getAbsolutePath() + "'");
+
+ if (sourceFile.exists() && !sourceFile.delete()) {
+ throw new IOException("could not delete " + sourceFile);
+ }
+ writer = new BufferedWriter(new FileWriter(sourceFile));
+
+ createClassHeader();
+ createSuiteMethod();
+ createClassFooter();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ FileUtils.close(writer);
+ }
+ }
+
+ private void createClassHeader() throws IOException {
+ String className = getLocationName().replace('\\', '/');
+ if (className.indexOf('/') > -1) {
+ className = className.substring(className.lastIndexOf('/') + 1);
+ }
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss,SSS");
+ writer.write("// generated on: ");
+ writer.write(sdf.format(new Date()));
+ writer.newLine();
+ writer.write("import junit.framework.*;");
+ writer.newLine();
+ writer.write("public class ");
+ writer.write(className);
+ // If this class does not extend TC, Ant doesn't run these
+ writer.write(" extends TestCase {");
+ writer.newLine();
+ // standard String-constructor
+ writer.write(" public ");
+ writer.write(className);
+ writer.write("(String testname) {");
+ writer.newLine();
+ writer.write(" super(testname);");
+ writer.newLine();
+ writer.write(" }");
+ writer.newLine();
+ }
+
+ private void createSuiteMethod() throws IOException {
+ writer.write(" public static Test suite() {");
+ writer.newLine();
+ writer.write(" TestSuite suite = new TestSuite();");
+ writer.newLine();
+ for (Iterator iter = failedTests.iterator(); iter.hasNext();) {
+ TestInfos testInfos = (TestInfos) iter.next();
+ writer.write(" suite.addTest(");
+ writer.write(String.valueOf(testInfos));
+ writer.write(");");
+ writer.newLine();
+ }
+ writer.write(" return suite;");
+ writer.newLine();
+ writer.write(" }");
+ writer.newLine();
+ }
+
+ private void createClassFooter() throws IOException {
+ writer.write("}");
+ writer.newLine();
+ }
+
+ // ===== Helper classes and methods =====
+
+ /**
+ * Logging facade in INFO-mode.
+ * @param message Log-message
+ */
+ public void log(String message) {
+ getProject().log(LOG_PREFIX + " " + message, Project.MSG_INFO);
+ }
+
+ /**
+ * Logging facade in VERBOSE-mode.
+ * @param message Log-message
+ */
+ public void verbose(String message) {
+ getProject().log(LOG_PREFIX + " " + message, Project.MSG_VERBOSE);
+ }
+
+ /**
+ * TestInfos holds information about a given test for later use.
+ */
+ public static class TestInfos implements Comparable {
+
+ /** The class name of the test. */
+ private final String className;
+
+ /** The method name of the testcase. */
+ private final String methodName;
+
+ /**
+ * This constructor extracts the needed information from the given test.
+ * @param test Test to analyze
+ */
+ public TestInfos(Test test) {
+ className = test.getClass().getName();
+ String _methodName = test.toString();
+ methodName = _methodName.substring(0, _methodName.indexOf('('));
+ }
+
+ /**
+ * This String-Representation can directly be used for instantiation of
+ * the JUnit testcase.
+ * @return the string representation.
+ * @see java.lang.Object#toString()
+ * @see FailureRecorder#createSuiteMethod()
+ */
+ public String toString() {
+ return "new " + className + "(\"" + methodName + "\")";
+ }
+
+ /**
+ * The SortedMap needs comparable elements.
+ * @param other the object to compare to.
+ * @return the result of the comparison.
+ * @see java.lang.Comparable#compareTo
+ * @see SortedSet#comparator()
+ */
+ public int compareTo(Object other) {
+ if (other instanceof TestInfos) {
+ TestInfos otherInfos = (TestInfos) other;
+ return toString().compareTo(otherInfos.toString());
+ } else {
+ return -1;
+ }
+ }
+ public boolean equals(Object obj) {
+ return obj instanceof TestInfos && toString().equals(obj.toString());
+ }
+ public int hashCode() {
+ return toString().hashCode();
+ }
+ }
+
+ // ===== BuildListener =====
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void buildFinished(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void buildStarted(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void messageLogged(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void targetFinished(BuildEvent event) {
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void targetStarted(BuildEvent event) {
+ }
+
+ /**
+ * The task outside of this JUnitResultFormatter is the <junit> task. So all tests passed
+ * and we could create the new java class.
+ * @param event not used
+ * @see org.apache.tools.ant.BuildListener#taskFinished(org.apache.tools.ant.BuildEvent)
+ */
+ public void taskFinished(BuildEvent event) {
+ if (!failedTests.isEmpty()) {
+ writeJavaClass();
+ }
+ }
+
+ /**
+ * Not used
+ * {@inheritDoc}
+ */
+ public void taskStarted(BuildEvent event) {
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java
new file mode 100644
index 00000000..f9fbcb0a
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java
@@ -0,0 +1,401 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+
+/**
+ * <p> A wrapper for the implementations of <code>JUnitResultFormatter</code>.
+ * In particular, used as a nested <code>&lt;formatter&gt;</code> element in
+ * a <code>&lt;junit&gt;</code> task.
+ * <p> For example,
+ * <code><pre>
+ * &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
+ * &lt;formatter type="plain" usefile="false" /&gt;
+ * &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
+ * &lt;/junit&gt;</pre></code>
+ * adds a <code>plain</code> type implementation
+ * (<code>PlainJUnitResultFormatter</code>) to display the results of the test.
+ *
+ * <p> Either the <code>type</code> or the <code>classname</code> attribute
+ * must be set.
+ *
+ * @see JUnitTask
+ * @see XMLJUnitResultFormatter
+ * @see BriefJUnitResultFormatter
+ * @see PlainJUnitResultFormatter
+ * @see FailureRecorder
+ * @see JUnitResultFormatter
+ */
+public class FormatterElement {
+
+ private String classname;
+ private String extension;
+ private OutputStream out = new KeepAliveOutputStream(System.out);
+ private File outFile;
+ private boolean useFile = true;
+ private Object ifCond;
+ private Object unlessCond;
+
+ /**
+ * Store the project reference for passing it to nested components.
+ * @since Ant 1.8
+ */
+ private Project project;
+
+ /** xml formatter class */
+ public static final String XML_FORMATTER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter";
+ /** brief formatter class */
+ public static final String BRIEF_FORMATTER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter";
+ /** plain formatter class */
+ public static final String PLAIN_FORMATTER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter";
+ /** failure recorder class */
+ public static final String FAILURE_RECORDER_CLASS_NAME =
+ "org.apache.tools.ant.taskdefs.optional.junit.FailureRecorder";
+
+ /**
+ * <p> Quick way to use a standard formatter.
+ *
+ * <p> At the moment, there are three supported standard formatters.
+ * <ul>
+ * <li> The <code>xml</code> type uses a <code>XMLJUnitResultFormatter</code>.
+ * <li> The <code>brief</code> type uses a <code>BriefJUnitResultFormatter</code>.
+ * <li> The <code>plain</code> type (the default) uses a <code>PlainJUnitResultFormatter</code>.
+ * <li> The <code>failure</code> type uses a <code>FailureRecorder</code>.
+ * </ul>
+ *
+ * <p> Sets <code>classname</code> attribute - so you can't use that
+ * attribute if you use this one.
+ * @param type the enumerated value to use.
+ */
+ public void setType(TypeAttribute type) {
+ if ("xml".equals(type.getValue())) {
+ setClassname(XML_FORMATTER_CLASS_NAME);
+ } else {
+ if ("brief".equals(type.getValue())) {
+ setClassname(BRIEF_FORMATTER_CLASS_NAME);
+ } else {
+ if ("failure".equals(type.getValue())) {
+ setClassname(FAILURE_RECORDER_CLASS_NAME);
+ } else { // must be plain, ensured by TypeAttribute
+ setClassname(PLAIN_FORMATTER_CLASS_NAME);
+ }
+ }
+ }
+ }
+
+ /**
+ * <p> Set name of class to be used as the formatter.
+ *
+ * <p> This class must implement <code>JUnitResultFormatter</code>
+ * @param classname the name of the formatter class.
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ if (XML_FORMATTER_CLASS_NAME.equals(classname)) {
+ setExtension(".xml");
+ } else if (PLAIN_FORMATTER_CLASS_NAME.equals(classname)) {
+ setExtension(".txt");
+ } else if (BRIEF_FORMATTER_CLASS_NAME.equals(classname)) {
+ setExtension(".txt");
+ }
+ }
+
+ /**
+ * Get name of class to be used as the formatter.
+ * @return the name of the class.
+ */
+ public String getClassname() {
+ return classname;
+ }
+
+ /**
+ * Set the extension to use for the report file.
+ * @param ext the extension to use.
+ */
+ public void setExtension(String ext) {
+ this.extension = ext;
+ }
+
+ /**
+ * Get the extension used for the report file.
+ * @return the extension.
+ */
+ public String getExtension() {
+ return extension;
+ }
+
+ /**
+ * <p> Set the file which the formatte should log to.
+ *
+ * <p> Note that logging to file must be enabled .
+ */
+ void setOutfile(File out) {
+ this.outFile = out;
+ }
+
+ /**
+ * <p> Set output stream for formatter to use.
+ *
+ * <p> Defaults to standard out.
+ * @param out the output stream to use.
+ */
+ public void setOutput(OutputStream out) {
+ if (out == System.out || out == System.err) {
+ out = new KeepAliveOutputStream(out);
+ }
+ this.out = out;
+ }
+
+ /**
+ * Set whether the formatter should log to file.
+ * @param useFile if true use a file, if false send
+ * to standard out.
+ */
+ public void setUseFile(boolean useFile) {
+ this.useFile = useFile;
+ }
+
+ /**
+ * Get whether the formatter should log to file.
+ */
+ boolean getUseFile() {
+ return useFile;
+ }
+
+ /**
+ * Set whether this formatter should be used. It will be used if
+ * the expression evaluates to true or the name of a property
+ * which has been set, otherwise it won't.
+ * @param ifCond name of property
+ * @since Ant 1.8.0
+ */
+ public void setIf(Object ifCond) {
+ this.ifCond = ifCond;
+ }
+
+ /**
+ * Set whether this formatter should be used. It will be used if
+ * the expression evaluates to true or the name of a property
+ * which has been set, otherwise it won't.
+ * @param ifCond name of property
+ */
+ public void setIf(String ifCond) {
+ setIf((Object) ifCond);
+ }
+
+ /**
+ * Set whether this formatter should NOT be used. It will be used
+ * if the expression evaluates to false or the name of a property
+ * which has not been set, orthwise it will not be used.
+ * @param unlessCond name of property
+ * @since Ant 1.8.0
+ */
+ public void setUnless(Object unlessCond) {
+ this.unlessCond = unlessCond;
+ }
+
+ /**
+ * Set whether this formatter should NOT be used. It will be used
+ * if the expression evaluates to false or the name of a property
+ * which has not been set, orthwise it will not be used.
+ * @param unlessCond name of property
+ */
+ public void setUnless(String unlessCond) {
+ setUnless((Object) unlessCond);
+ }
+
+ /**
+ * Ensures that the selector passes the conditions placed
+ * on it with <code>if</code> and <code>unless</code> properties.
+ * @param t the task the this formatter is used in.
+ * @return true if the formatter should be used.
+ */
+ public boolean shouldUse(Task t) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(t.getProject());
+ return ph.testIfCondition(ifCond)
+ && ph.testUnlessCondition(unlessCond);
+ }
+
+ /**
+ * @since Ant 1.2
+ */
+ JUnitTaskMirror.JUnitResultFormatterMirror createFormatter() throws BuildException {
+ return createFormatter(null);
+ }
+
+ /**
+ * Store the project reference for passing it to nested components.
+ * @param project the reference
+ * @since Ant 1.8
+ */
+ public void setProject(Project project) {
+ this.project = project;
+ }
+
+
+ /**
+ * @since Ant 1.6
+ */
+ JUnitTaskMirror.JUnitResultFormatterMirror createFormatter(ClassLoader loader)
+ throws BuildException {
+
+ if (classname == null) {
+ throw new BuildException("you must specify type or classname");
+ }
+ //although this code appears to duplicate that of ClasspathUtils.newInstance,
+ //we cannot use that because this formatter may run in a forked process,
+ //without that class.
+ Class f = null;
+ try {
+ if (loader == null) {
+ f = Class.forName(classname);
+ } else {
+ f = Class.forName(classname, true, loader);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(
+ "Using loader " + loader + " on class " + classname
+ + ": " + e, e);
+ } catch (NoClassDefFoundError e) {
+ throw new BuildException(
+ "Using loader " + loader + " on class " + classname
+ + ": " + e, 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 JUnitTaskMirror.JUnitResultFormatterMirror)) {
+ throw new BuildException(classname + " is not a JUnitResultFormatter");
+ }
+ JUnitTaskMirror.JUnitResultFormatterMirror r =
+ (JUnitTaskMirror.JUnitResultFormatterMirror) o;
+ if (useFile && outFile != null) {
+ out = new DelayedFileOutputStream(outFile);
+ }
+ r.setOutput(out);
+
+
+ boolean needToSetProjectReference = true;
+ try {
+ Field field = r.getClass().getField("project");
+ Object value = field.get(r);
+ if (value instanceof Project) {
+ // there is already a project reference so dont overwrite this
+ needToSetProjectReference = false;
+ }
+ } catch (NoSuchFieldException e) {
+ // no field present, so no previous reference exists
+ } catch (IllegalAccessException e) {
+ throw new BuildException(e);
+ }
+
+ if (needToSetProjectReference) {
+ Method setter;
+ try {
+ setter = r.getClass().getMethod("setProject", new Class[] {Project.class});
+ setter.invoke(r, new Object[] {project});
+ } catch (NoSuchMethodException e) {
+ // no setProject to invoke; just ignore
+ } catch (IllegalAccessException e) {
+ throw new BuildException(e);
+ } catch (InvocationTargetException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ return r;
+ }
+
+ /**
+ * <p> Enumerated attribute with the values "plain", "xml", "brief" and "failure".
+ *
+ * <p> Use to enumerate options for <code>type</code> attribute.
+ */
+ public static class TypeAttribute extends EnumeratedAttribute {
+ /** {@inheritDoc}. */
+ public String[] getValues() {
+ return new String[] {"plain", "xml", "brief", "failure"};
+ }
+ }
+
+ /**
+ * A standard FileOutputStream creates a file as soon as it's opened. This
+ * class delays the creation of the file until the first time a caller attempts
+ * to write to it so we don't end up with empty files if the listeners don't use
+ * them.
+ */
+ private static class DelayedFileOutputStream extends OutputStream {
+
+ private BufferedOutputStream outputStream;
+ private final File file;
+
+ public DelayedFileOutputStream(File file) {
+ this.file = file;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ synchronized (this) {
+ if (outputStream == null) {
+ outputStream = new BufferedOutputStream(new FileOutputStream(file));
+ }
+ }
+ outputStream.write(b);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (outputStream != null) {
+ outputStream.flush();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java
new file mode 100644
index 00000000..6741912e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestListener.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+/**
+ * Provides the functionality for TestListeners to be able to be notified of
+ * the necessary JUnit4 events for test being ignored (@Ignore annotation)
+ * or skipped (Assume failures). Tests written in JUnit4 will report against
+ * the methods in this interface alongside the methods in the existing TestListener
+ */
+public interface IgnoredTestListener extends TestListener {
+
+ /**
+ * Reports when a test has been marked with the @Ignore annotation. The parameter
+ * should normally be typed to JUnit's {@link junit.framework.JUnit4TestCaseFacade}
+ * so implementing classes should be able to get the details of the ignore by casting
+ * the argument and retrieving the descriptor from the test.
+ * @param test the details of the test and failure that have triggered this report.
+ */
+ void testIgnored(Test test);
+
+ /**
+ * Receive a report that a test has failed an assumption. Within JUnit4
+ * this is normally treated as a test being skipped, although how any
+ * listener handles this is up to that specific listener.<br />
+ * <b>Note:</b> Tests that throw assumption failures will still report
+ * the endTest method, which may differ from how the addError and addFailure
+ * methods work, it's up for any implementing classes to handle this.
+ * @param test the details of the test and failure that have triggered this report.
+ * @param exception the AssumptionViolatedException thrown from the current assumption failure.
+ */
+ void testAssumptionFailure(Test test, Throwable exception);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java
new file mode 100644
index 00000000..c3bb18da
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/IgnoredTestResult.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+
+/**
+ * Records ignored and skipped tests reported as part of the execution of
+ * JUnit 4 tests.
+ *
+ */
+public class IgnoredTestResult extends TestResult {
+
+
+ private List<IgnoredTestListener> listeners = new ArrayList<IgnoredTestListener>();
+ private List<TestIgnored> ignored = new ArrayList<TestIgnored>();
+ private List<TestIgnored> skipped = new ArrayList<TestIgnored>();
+
+ public IgnoredTestResult() {
+ super();
+ }
+
+
+ public synchronized void addListener(TestListener listener) {
+ if (listener instanceof IgnoredTestListener) {
+ listeners.add((IgnoredTestListener)listener);
+ }
+ super.addListener(listener);
+ }
+
+ public synchronized void removeListener(TestListener listener) {
+ if (listener instanceof IgnoredTestListener) {
+ listeners.remove(listener);
+ }
+ super.removeListener(listener);
+ }
+
+ /**
+ * Record a test as having been ignored, normally by the @Ignore annotation.
+ * @param test the test that was ignored.
+ * @throws Exception is the listener thrown an exception on handling the notification.
+ */
+ public synchronized void testIgnored(Test test) throws Exception {
+ ignored.add(new TestIgnored(test));
+ for (IgnoredTestListener listener : listeners) {
+ listener.testIgnored(test);
+ }
+ }
+
+ /**
+ * Report how many tests were ignored.
+ * @return the number of tests reported as ignored during the current execution.
+ */
+ public long ignoredCount() {
+ return ignored.size();
+ }
+
+ /**
+ * Records a test as having an assumption failure so JUnit will no longer be executing it.
+ * Under normal circumstances this would be counted as a skipped test.
+ * @param test the test to record
+ * @param cause the details of the test and assumption failure.
+ */
+ public void testAssumptionFailure(Test test, Throwable cause) {
+ skipped.add(new TestIgnored(test));
+ for (IgnoredTestListener listener : listeners) {
+ listener.testAssumptionFailure(test, cause);
+ }
+ }
+
+ /**
+ * Report how many tests has assumption failures.
+ * @return the number of tests that reported assumption failures during the current execution.
+ */
+ public long skippedCount() {
+ return skipped.size();
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java
new file mode 100644
index 00000000..f03a409b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.junit.runner.Description;
+import org.junit.runner.Request;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * Adapter between JUnit 3.8.x API and JUnit 4.x API for execution of tests
+ * and listening of events (test start, test finish, test failure, test skipped).
+ * The constructor is passed a JUnit 4 test class and a list of name of methods
+ * in it that should be executed. Method {@link #run run(TestResult)} executes
+ * the given JUnit-4-style test methods and notifies the given {@code TestResult}
+ * object using its old (JUnit 3.8.x style) API.
+ *
+ * @author Marian Petras
+ */
+public class JUnit4TestMethodAdapter implements Test {
+
+ private final Class testClass;
+ private final String[] methodNames;
+ private final Runner runner;
+ private final CustomJUnit4TestAdapterCache cache;
+
+ /**
+ * Creates a new adapter for the given class and a method within the class.
+ *
+ * @param testClass test class containing the method to be executed
+ * @param methodNames names of the test methods that are to be executed
+ * @exception java.lang.IllegalArgumentException
+ * if any of the arguments is {@code null}
+ * or if any of the given method names is {@code null} or empty
+ */
+ public JUnit4TestMethodAdapter(final Class testClass,
+ final String[] methodNames) {
+ if (testClass == null) {
+ throw new IllegalArgumentException("testClass is <null>");
+ }
+ if (methodNames == null) {
+ throw new IllegalArgumentException("methodNames is <null>");
+ }
+ for (int i = 0; i < methodNames.length; i++) {
+ if (methodNames[i] == null) {
+ throw new IllegalArgumentException("method name #" + i + " is <null>");
+ }
+ if (methodNames[i].length() == 0) {
+ throw new IllegalArgumentException("method name #" + i + " is empty");
+ }
+ }
+ this.testClass = testClass;
+ this.methodNames = methodNames.clone();
+ this.cache = CustomJUnit4TestAdapterCache.getInstance();
+
+ // Warning: If 'testClass' is an old-style (pre-JUnit-4) class,
+ // then all its test methods will be executed by the returned runner!
+ Request request;
+ if (methodNames.length == 1) {
+ request = Request.method(testClass, methodNames[0]);
+ } else {
+ request = Request.aClass(testClass).filterWith(
+ new MultipleMethodsFilter(testClass, methodNames));
+ }
+ runner = request.getRunner();
+ }
+
+ public int countTestCases() {
+ return runner.testCount();
+ }
+
+ public Description getDescription() {
+ return runner.getDescription();
+ }
+
+ public List/*<Test>*/ getTests() {
+ return cache.asTestList(getDescription());
+ }
+
+ public Class getTestClass() {
+ return testClass;
+ }
+
+ public void run(final TestResult result) {
+ runner.run(cache.getNotifier(result));
+ }
+
+ @Override
+ public String toString() {
+ String testClassName = testClass.getName();
+ StringBuilder buf = new StringBuilder(testClassName.length()
+ + 12 * methodNames.length)
+ .append(':');
+ if (methodNames.length != 0) {
+ buf.append(methodNames[0]);
+ for (int i = 1; i < methodNames.length; i++) {
+ buf.append(',')
+ .append(methodNames[i]);
+ }
+ }
+ return buf.toString();
+ }
+
+ private static final class MultipleMethodsFilter extends Filter {
+
+ private final Description methodsListDescription;
+ private final Class testClass;
+ private final String[] methodNames;
+
+ private MultipleMethodsFilter(Class testClass, String[] methodNames) {
+ if (testClass == null) {
+ throw new IllegalArgumentException("testClass is <null>");
+ }
+ if (methodNames == null) {
+ throw new IllegalArgumentException("methodNames is <null>");
+ }
+ methodsListDescription = Description.createSuiteDescription(testClass);
+ for (int i = 0; i < methodNames.length; i++) {
+ methodsListDescription.addChild(
+ Description.createTestDescription(testClass, methodNames[i]));
+ }
+ this.testClass = testClass;
+ this.methodNames = methodNames;
+ }
+
+ @Override
+ public boolean shouldRun(Description description) {
+ if (methodNames.length == 0) {
+ return false;
+ }
+ if (description.isTest()) {
+ Iterator/*<Description>*/ it = methodsListDescription.getChildren().iterator();
+ while (it.hasNext()) {
+ Description methodDescription = (Description) it.next();
+ if (methodDescription.equals(description)) {
+ return true;
+ }
+ }
+ } else {
+ Iterator/*<Description>*/ it = description.getChildren().iterator();
+ while (it.hasNext()) {
+ Description each = (Description) it.next();
+ if (shouldRun(each)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String describe() {
+ StringBuilder buf = new StringBuilder(40);
+ if (methodNames.length == 0) {
+ buf.append("No methods");
+ } else {
+ buf.append(methodNames.length == 1 ? "Method" : "Methods");
+ buf.append(' ');
+ buf.append(methodNames[0]);
+ for (int i = 1; i < methodNames.length; i++) {
+ buf.append(',').append(methodNames[i]);
+ }
+ }
+ buf.append('(').append(testClass.getName()).append(')');
+ return buf.toString();
+ }
+
+ }
+
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java
new file mode 100644
index 00000000..2119fc94
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+
+import junit.framework.TestListener;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This Interface describes classes that format the results of a JUnit
+ * testrun.
+ *
+ */
+public interface JUnitResultFormatter
+ extends TestListener, JUnitTaskMirror.JUnitResultFormatterMirror {
+ /**
+ * The whole testsuite started.
+ * @param suite the suite.
+ * @throws BuildException on error.
+ */
+ void startTestSuite(JUnitTest suite) throws BuildException;
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the suite.
+ * @throws BuildException on error.
+ */
+ void endTestSuite(JUnitTest suite) throws BuildException;
+
+ /**
+ * Sets the stream the formatter is supposed to write its results to.
+ * @param out the output stream to use.
+ */
+ void setOutput(OutputStream out);
+
+ /**
+ * This is what the test has written to System.out
+ * @param out the string to write.
+ */
+ void setSystemOutput(String out);
+
+ /**
+ * This is what the test has written to System.err
+ * @param err the string to write.
+ */
+ void setSystemError(String err);
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
new file mode 100644
index 00000000..459bd3d5
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
@@ -0,0 +1,2283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.types.Assertions;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.SplitClassLoader;
+
+/**
+ * Runs JUnit tests.
+ *
+ * <p> JUnit is a framework to create unit tests. It has been initially
+ * created by Erich Gamma and Kent Beck. JUnit can be found at <a
+ * href="http://www.junit.org">http://www.junit.org</a>.
+ *
+ * <p> <code>JUnitTask</code> can run a single specific
+ * <code>JUnitTest</code> using the <code>test</code> element.</p>
+ * For example, the following target <code><pre>
+ * &lt;target name="test-int-chars" depends="jar-test"&gt;
+ * &lt;echo message="testing international characters"/&gt;
+ * &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
+ * &lt;classpath refid="classpath"/&gt;
+ * &lt;formatter type="plain" usefile="false" /&gt;
+ * &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
+ * &lt;/junit&gt;
+ * &lt;/target&gt;
+ * </pre></code>
+ * <p>runs a single junit test
+ * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
+ * VM using the path with id <code>classpath</code> as classpath and
+ * presents the results formatted using the standard
+ * <code>plain</code> formatter on the command line.</p>
+ *
+ * <p> This task can also run batches of tests. The
+ * <code>batchtest</code> element creates a <code>BatchTest</code>
+ * based on a fileset. This allows, for example, all classes found in
+ * directory to be run as testcases.</p>
+ *
+ * <p>For example,</p><code><pre>
+ * &lt;target name="run-tests" depends="dump-info,compile-tests" if="junit.present"&gt;
+ * &lt;junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"&gt;
+ * &lt;jvmarg value="-classic"/&gt;
+ * &lt;classpath refid="tests-classpath"/&gt;
+ * &lt;sysproperty key="build.tests" value="${build.tests}"/&gt;
+ * &lt;formatter type="brief" usefile="false" /&gt;
+ * &lt;batchtest&gt;
+ * &lt;fileset dir="${tests.dir}"&gt;
+ * &lt;include name="**&#047;*Test*" /&gt;
+ * &lt;/fileset&gt;
+ * &lt;/batchtest&gt;
+ * &lt;/junit&gt;
+ * &lt;/target&gt;
+ * </pre></code>
+ * <p>this target finds any classes with a <code>test</code> directory
+ * anywhere in their path (under the top <code>${tests.dir}</code>, of
+ * course) and creates <code>JUnitTest</code>'s for each one.</p>
+ *
+ * <p> Of course, <code>&lt;junit&gt;</code> and
+ * <code>&lt;batch&gt;</code> elements can be combined for more
+ * complex tests. For an example, see the ant <code>build.xml</code>
+ * target <code>run-tests</code> (the second example is an edited
+ * version).</p>
+ *
+ * <p> To spawn a new Java VM to prevent interferences between
+ * different testcases, you need to enable <code>fork</code>. A
+ * number of attributes and elements allow you to set up how this JVM
+ * runs.
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @see JUnitTest
+ * @see BatchTest
+ */
+public class JUnitTask extends Task {
+
+ private static final String LINE_SEP
+ = System.getProperty("line.separator");
+ private static final String CLASSPATH = "CLASSPATH";
+ private CommandlineJava commandline;
+ private final Vector<JUnitTest> tests = new Vector<JUnitTest>();
+ private final Vector<BatchTest> batchTests = new Vector<BatchTest>();
+ private final Vector<FormatterElement> formatters = new Vector<FormatterElement>();
+ private File dir = null;
+
+ private Integer timeout = null;
+ private boolean summary = false;
+ private boolean reloading = true;
+ private String summaryValue = "";
+ private JUnitTaskMirror.JUnitTestRunnerMirror runner = null;
+
+ private boolean newEnvironment = false;
+ private final Environment env = new Environment();
+
+ private boolean includeAntRuntime = true;
+ private Path antRuntimeClasses = null;
+
+ // Do we send output to System.out/.err in addition to the formatters?
+ private boolean showOutput = false;
+
+ // Do we send output to the formatters ?
+ private boolean outputToFormatters = true;
+
+ private boolean logFailedTests = true;
+
+ private File tmpDir;
+ private AntClassLoader classLoader = null;
+ private Permissions perm = null;
+ private ForkMode forkMode = new ForkMode("perTest");
+
+ private boolean splitJUnit = false;
+ private boolean enableTestListenerEvents = false;
+ private JUnitTaskMirror delegate;
+ private ClassLoader mirrorLoader;
+
+ /** A boolean on whether to get the forked path for ant classes */
+ private boolean forkedPathChecked = false;
+
+ /* set when a test fails/errs with haltonfailure/haltonerror and >1 thread to stop other threads */
+ private volatile BuildException caughtBuildException = null;
+
+ // Attributes for basetest
+ private boolean haltOnError = false;
+ private boolean haltOnFail = false;
+ private boolean filterTrace = true;
+ private boolean fork = false;
+ private int threads = 1;
+ private String failureProperty;
+ private String errorProperty;
+
+ private static final int STRING_BUFFER_SIZE = 128;
+ /**
+ * @since Ant 1.7
+ */
+ public static final String TESTLISTENER_PREFIX =
+ "junit.framework.TestListener: ";
+
+ /**
+ * Name of magic property that enables test listener events.
+ */
+ public static final String ENABLE_TESTLISTENER_EVENTS =
+ "ant.junit.enabletestlistenerevents";
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * If true, force ant to re-classload all classes for each JUnit TestCase
+ *
+ * @param value force class reloading for each test case
+ */
+ public void setReloading(final boolean value) {
+ reloading = value;
+ }
+
+ /**
+ * If true, smartly filter the stack frames of
+ * JUnit errors and failures before reporting them.
+ *
+ * <p>This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test) however it can possibly be overridden by their
+ * own properties.</p>
+ * @param value <tt>false</tt> if it should not filter, otherwise
+ * <tt>true<tt>
+ *
+ * @since Ant 1.5
+ */
+ public void setFiltertrace(final boolean value) {
+ this.filterTrace = value;
+ }
+
+ /**
+ * If true, stop the build process when there is an error in a test.
+ * This property is applied on all BatchTest (batchtest) and JUnitTest
+ * (test) however it can possibly be overridden by their own
+ * properties.
+ * @param value <tt>true</tt> if it should halt, otherwise
+ * <tt>false</tt>
+ *
+ * @since Ant 1.2
+ */
+ public void setHaltonerror(final boolean value) {
+ this.haltOnError = value;
+ }
+
+ /**
+ * Property to set to "true" if there is a error in a test.
+ *
+ * <p>This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test), however, it can possibly be overridden by
+ * their own properties.</p>
+ * @param propertyName the name of the property to set in the
+ * event of an error.
+ *
+ * @since Ant 1.4
+ */
+ public void setErrorProperty(final String propertyName) {
+ this.errorProperty = propertyName;
+ }
+
+ /**
+ * If true, stop the build process if a test fails
+ * (errors are considered failures as well).
+ * This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test) however it can possibly be overridden by their
+ * own properties.
+ * @param value <tt>true</tt> if it should halt, otherwise
+ * <tt>false</tt>
+ *
+ * @since Ant 1.2
+ */
+ public void setHaltonfailure(final boolean value) {
+ this.haltOnFail = value;
+ }
+
+ /**
+ * Property to set to "true" if there is a failure in a test.
+ *
+ * <p>This property is applied on all BatchTest (batchtest) and
+ * JUnitTest (test), however, it can possibly be overridden by
+ * their own properties.</p>
+ * @param propertyName the name of the property to set in the
+ * event of an failure.
+ *
+ * @since Ant 1.4
+ */
+ public void setFailureProperty(final String propertyName) {
+ this.failureProperty = propertyName;
+ }
+
+ /**
+ * If true, JVM should be forked for each test.
+ *
+ * <p>It avoids interference between testcases and possibly avoids
+ * hanging the build. this property is applied on all BatchTest
+ * (batchtest) and JUnitTest (test) however it can possibly be
+ * overridden by their own properties.</p>
+ * @param value <tt>true</tt> if a JVM should be forked, otherwise
+ * <tt>false</tt>
+ * @see #setTimeout
+ *
+ * @since Ant 1.2
+ */
+ public void setFork(final boolean value) {
+ this.fork = value;
+ }
+
+ /**
+ * Set the behavior when {@link #setFork fork} fork has been enabled.
+ *
+ * <p>Possible values are "once", "perTest" and "perBatch". If
+ * set to "once", only a single Java VM will be forked for all
+ * tests, with "perTest" (the default) each test will run in a
+ * fresh Java VM and "perBatch" will run all tests from the same
+ * &lt;batchtest&gt; in the same Java VM.</p>
+ *
+ * <p>This attribute will be ignored if tests run in the same VM
+ * as Ant.</p>
+ *
+ * <p>Only tests with the same configuration of haltonerror,
+ * haltonfailure, errorproperty, failureproperty and filtertrace
+ * can share a forked Java VM, so even if you set the value to
+ * "once", Ant may need to fork multiple VMs.</p>
+ * @param mode the mode to use.
+ * @since Ant 1.6.2
+ */
+ public void setForkMode(final ForkMode mode) {
+ this.forkMode = mode;
+ }
+
+ /**
+ * Set the number of test threads to be used for parallel test
+ * execution. The default is 1, which is the same behavior as
+ * before parallel test execution was possible.
+ *
+ * <p>This attribute will be ignored if tests run in the same VM
+ * as Ant.</p>
+ *
+ * @since Ant 1.9.4
+ */
+ public void setThreads(final int threads) {
+ if (threads >= 0) {
+ this.threads = threads;
+ }
+ }
+
+ /**
+ * If true, print one-line statistics for each test, or "withOutAndErr"
+ * to also show standard output and error.
+ *
+ * Can take the values on, off, and withOutAndErr.
+ * @param value <tt>true</tt> to print a summary,
+ * <tt>withOutAndErr</tt> to include the test&apos;s output as
+ * well, <tt>false</tt> otherwise.
+ * @see SummaryJUnitResultFormatter
+ *
+ * @since Ant 1.2
+ */
+ public void setPrintsummary(final SummaryAttribute value) {
+ summaryValue = value.getValue();
+ summary = value.asBoolean();
+ }
+
+ /**
+ * Print summary enumeration values.
+ */
+ public static class SummaryAttribute extends EnumeratedAttribute {
+ /**
+ * list the possible values
+ * @return array of allowed values
+ */
+ @Override
+ public String[] getValues() {
+ return new String[] {"true", "yes", "false", "no",
+ "on", "off", "withOutAndErr"};
+ }
+
+ /**
+ * gives the boolean equivalent of the authorized values
+ * @return boolean equivalent of the value
+ */
+ public boolean asBoolean() {
+ final String v = getValue();
+ return "true".equals(v)
+ || "on".equals(v)
+ || "yes".equals(v)
+ || "withOutAndErr".equals(v);
+ }
+ }
+
+ /**
+ * Set the timeout value (in milliseconds).
+ *
+ * <p>If the test is running for more than this value, the test
+ * will be canceled. (works only when in 'fork' mode).</p>
+ * @param value the maximum time (in milliseconds) allowed before
+ * declaring the test as 'timed-out'
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public void setTimeout(final Integer value) {
+ timeout = value;
+ }
+
+ /**
+ * Set the maximum memory to be used by all forked JVMs.
+ * @param max the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
+ * in the java command line options.
+ *
+ * @since Ant 1.2
+ */
+ public void setMaxmemory(final String max) {
+ getCommandline().setMaxmemory(max);
+ }
+
+ /**
+ * 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.
+ *
+ * @param value the new VM to use instead of <tt>java</tt>
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public void setJvm(final String value) {
+ getCommandline().setVm(value);
+ }
+
+ /**
+ * Adds a JVM argument; ignored if not forking.
+ *
+ * @return create a new JVM argument so that any argument can be
+ * passed to the JVM.
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public Commandline.Argument createJvmarg() {
+ return getCommandline().createVmArgument();
+ }
+
+ /**
+ * The directory to invoke the VM in. Ignored if no JVM is forked.
+ * @param dir the directory to invoke the JVM from.
+ * @see #setFork(boolean)
+ *
+ * @since Ant 1.2
+ */
+ public void setDir(final File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * Adds a system property that tests can access.
+ * This might be useful to transfer Ant properties to the
+ * testcases when JVM forking is not enabled.
+ *
+ * @since Ant 1.3
+ * @deprecated since ant 1.6
+ * @param sysp environment variable to add
+ */
+ @Deprecated
+ public void addSysproperty(final Environment.Variable sysp) {
+
+ getCommandline().addSysproperty(sysp);
+ }
+
+ /**
+ * Adds a system property that tests can access.
+ * This might be useful to transfer Ant properties to the
+ * testcases when JVM forking is not enabled.
+ * @param sysp new environment variable to add
+ * @since Ant 1.6
+ */
+ public void addConfiguredSysproperty(final Environment.Variable sysp) {
+ // get a build exception if there is a missing key or value
+ // see bugzilla report 21684
+ final String testString = sysp.getContent();
+ getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG);
+ getCommandline().addSysproperty(sysp);
+ }
+
+ /**
+ * Adds a set of properties that will be used as system properties
+ * that tests can access.
+ *
+ * This might be useful to transfer Ant properties to the
+ * testcases when JVM forking is not enabled.
+ *
+ * @param sysp set of properties to be added
+ * @since Ant 1.6
+ */
+ public void addSyspropertyset(final PropertySet sysp) {
+ getCommandline().addSyspropertyset(sysp);
+ }
+
+ /**
+ * Adds path to classpath used for tests.
+ *
+ * @return reference to the classpath in the embedded java command line
+ * @since Ant 1.2
+ */
+ public Path createClasspath() {
+ return getCommandline().createClasspath(getProject()).createPath();
+ }
+
+ /**
+ * Adds a path to the bootclasspath.
+ * @return reference to the bootclasspath in the embedded java command line
+ * @since Ant 1.6
+ */
+ public Path createBootclasspath() {
+ return getCommandline().createBootclasspath(getProject()).createPath();
+ }
+
+ /**
+ * Adds an environment variable; used when forking.
+ *
+ * <p>Will be ignored if we are not forking a new VM.</p>
+ * @param var environment variable to be added
+ * @since Ant 1.5
+ */
+ public void addEnv(final Environment.Variable var) {
+ env.addVariable(var);
+ }
+
+ /**
+ * If true, use a new environment when forked.
+ *
+ * <p>Will be ignored if we are not forking a new VM.</p>
+ *
+ * @param newenv boolean indicating if setting a new environment is wished
+ * @since Ant 1.5
+ */
+ public void setNewenvironment(final boolean newenv) {
+ newEnvironment = newenv;
+ }
+
+ /**
+ * Preset the attributes of the test
+ * before configuration in the build
+ * script.
+ * This allows attributes in the <junit> task
+ * be be defaults for the tests, but allows
+ * individual tests to override the defaults.
+ */
+ private void preConfigure(final BaseTest test) {
+ test.setFiltertrace(filterTrace);
+ test.setHaltonerror(haltOnError);
+ if (errorProperty != null) {
+ test.setErrorProperty(errorProperty);
+ }
+ test.setHaltonfailure(haltOnFail);
+ if (failureProperty != null) {
+ test.setFailureProperty(failureProperty);
+ }
+ test.setFork(fork);
+ }
+
+ /**
+ * Add a new single testcase.
+ * @param test a new single testcase
+ * @see JUnitTest
+ *
+ * @since Ant 1.2
+ */
+ public void addTest(final JUnitTest test) {
+ tests.addElement(test);
+ preConfigure(test);
+ }
+
+ /**
+ * Adds a set of tests based on pattern matching.
+ *
+ * @return a new instance of a batch test.
+ * @see BatchTest
+ *
+ * @since Ant 1.2
+ */
+ public BatchTest createBatchTest() {
+ final BatchTest test = new BatchTest(getProject());
+ batchTests.addElement(test);
+ preConfigure(test);
+ return test;
+ }
+
+ /**
+ * Add a new formatter to all tests of this task.
+ *
+ * @param fe formatter element
+ * @since Ant 1.2
+ */
+ public void addFormatter(final FormatterElement fe) {
+ formatters.addElement(fe);
+ }
+
+ /**
+ * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
+ *
+ * @param b include ant run time yes or no
+ * @since Ant 1.5
+ */
+ public void setIncludeantruntime(final boolean b) {
+ includeAntRuntime = b;
+ }
+
+ /**
+ * If true, send any output generated by tests to Ant's logging system
+ * as well as to the formatters.
+ * By default only the formatters receive the output.
+ *
+ * <p>Output will always be passed to the formatters and not by
+ * shown by default. This option should for example be set for
+ * tests that are interactive and prompt the user to do
+ * something.</p>
+ *
+ * @param showOutput if true, send output to Ant's logging system too
+ * @since Ant 1.5
+ */
+ public void setShowOutput(final boolean showOutput) {
+ this.showOutput = showOutput;
+ }
+
+ /**
+ * If true, send any output generated by tests to the formatters.
+ *
+ * @param outputToFormatters if true, send output to formatters (Default
+ * is true).
+ * @since Ant 1.7.0
+ */
+ public void setOutputToFormatters(final boolean outputToFormatters) {
+ this.outputToFormatters = outputToFormatters;
+ }
+
+ /**
+ * If true, write a single "FAILED" line for failed tests to Ant's
+ * log system.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setLogFailedTests(final boolean logFailedTests) {
+ this.logFailedTests = logFailedTests;
+ }
+
+ /**
+ * Assertions to enable in this program (if fork=true)
+ * @since Ant 1.6
+ * @param asserts assertion set
+ */
+ public void addAssertions(final Assertions asserts) {
+ if (getCommandline().getAssertions() != null) {
+ throw new BuildException("Only one assertion declaration is allowed");
+ }
+ getCommandline().setAssertions(asserts);
+ }
+
+ /**
+ * Sets the permissions for the application run inside the same JVM.
+ * @since Ant 1.6
+ * @return .
+ */
+ public Permissions createPermissions() {
+ if (perm == null) {
+ perm = new Permissions();
+ }
+ return perm;
+ }
+
+ /**
+ * If set, system properties will be copied to the cloned VM - as
+ * well as the bootclasspath unless you have explicitly specified
+ * a bootclasspath.
+ *
+ * <p>Doesn't have any effect unless fork is true.</p>
+ * @param cloneVm a <code>boolean</code> value.
+ * @since Ant 1.7
+ */
+ public void setCloneVm(final boolean cloneVm) {
+ getCommandline().setCloneVm(cloneVm);
+ }
+
+ /**
+ * Creates a new JUnitRunner and enables fork of a new Java VM.
+ *
+ * @throws Exception under ??? circumstances
+ * @since Ant 1.2
+ */
+ public JUnitTask() throws Exception {
+ }
+
+ /**
+ * Where Ant should place temporary files.
+ *
+ * @param tmpDir location where temporary files should go to
+ * @since Ant 1.6
+ */
+ public void setTempdir(final File tmpDir) {
+ if (tmpDir != null) {
+ if (!tmpDir.exists() || !tmpDir.isDirectory()) {
+ throw new BuildException(tmpDir.toString()
+ + " is not a valid temp directory");
+ }
+ }
+ this.tmpDir = tmpDir;
+ }
+
+ /**
+ * Whether test listener events shall be generated.
+ *
+ * <p>Defaults to false.</p>
+ *
+ * <p>This value will be overridden by the magic property
+ * ant.junit.enabletestlistenerevents if it has been set.</p>
+ *
+ * @since Ant 1.8.2
+ */
+ public void setEnableTestListenerEvents(final boolean b) {
+ enableTestListenerEvents = b;
+ }
+
+ /**
+ * Whether test listener events shall be generated.
+ * @since Ant 1.8.2
+ */
+ public boolean getEnableTestListenerEvents() {
+ final String e = getProject().getProperty(ENABLE_TESTLISTENER_EVENTS);
+ if (e != null) {
+ return Project.toBoolean(e);
+ }
+ return enableTestListenerEvents;
+ }
+
+ /**
+ * Adds the jars or directories containing Ant, this task and
+ * JUnit to the classpath - this should make the forked JVM work
+ * without having to specify them directly.
+ *
+ * @since Ant 1.4
+ */
+ @Override
+ public void init() {
+ antRuntimeClasses = new Path(getProject());
+ splitJUnit = !addClasspathResource("/junit/framework/TestCase.class");
+ addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class");
+ addClasspathEntry("/org/apache/tools/ant/Task.class");
+ addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
+ addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnit4TestMethodAdapter.class");
+ }
+
+ private static JUnitTaskMirror createMirror(final JUnitTask task, final ClassLoader loader) {
+ try {
+ loader.loadClass("junit.framework.Test"); // sanity check
+ } catch (final ClassNotFoundException e) {
+ throw new BuildException(
+ "The <classpath> for <junit> must include junit.jar "
+ + "if not in Ant's own classpath",
+ e, task.getLocation());
+ }
+ try {
+ final Class c = loader.loadClass(JUnitTaskMirror.class.getName() + "Impl");
+ if (c.getClassLoader() != loader) {
+ throw new BuildException("Overdelegating loader", task.getLocation());
+ }
+ final Constructor cons = c.getConstructor(new Class[] {JUnitTask.class});
+ return (JUnitTaskMirror) cons.newInstance(new Object[] {task});
+ } catch (final Exception e) {
+ throw new BuildException(e, task.getLocation());
+ }
+ }
+
+ /**
+ * Sets up the delegate that will actually run the tests.
+ *
+ * <p>Will be invoked implicitly once the delegate is needed.</p>
+ *
+ * @since Ant 1.7.1
+ */
+ protected void setupJUnitDelegate() {
+ final ClassLoader myLoader = JUnitTask.class.getClassLoader();
+ if (splitJUnit) {
+ final Path path = new Path(getProject());
+ path.add(antRuntimeClasses);
+ final Path extra = getCommandline().getClasspath();
+ if (extra != null) {
+ path.add(extra);
+ }
+ mirrorLoader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return new SplitClassLoader(myLoader, path, getProject(),
+ new String[] {
+ "BriefJUnitResultFormatter",
+ "JUnit4TestMethodAdapter",
+ "JUnitResultFormatter",
+ "JUnitTaskMirrorImpl",
+ "JUnitTestRunner",
+ "JUnitVersionHelper",
+ "OutErrSummaryJUnitResultFormatter",
+ "PlainJUnitResultFormatter",
+ "SummaryJUnitResultFormatter",
+ "TearDownOnVmCrash",
+ "XMLJUnitResultFormatter",
+ "IgnoredTestListener",
+ "IgnoredTestResult",
+ "CustomJUnit4TestAdapterCache",
+ "TestListenerWrapper"
+ });
+ }
+ });
+ } else {
+ mirrorLoader = myLoader;
+ }
+ delegate = createMirror(this, mirrorLoader);
+ }
+
+ /**
+ * Runs the testcase.
+ *
+ * @throws BuildException in case of test failures or errors
+ * @since Ant 1.2
+ */
+ @Override
+ public void execute() throws BuildException {
+ checkMethodLists();
+
+ setupJUnitDelegate();
+
+ final List<List> testLists = new ArrayList<List>();
+ /* parallel test execution is only supported for multi-process execution */
+ final int threads = ((!fork) || (forkMode.getValue().equals(ForkMode.ONCE))
+ ? 1
+ : this.threads);
+
+ final boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST);
+ if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) {
+ testLists.addAll(executeOrQueue(getIndividualTests(),
+ forkPerTest));
+ } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */
+ final int count = batchTests.size();
+ for (int i = 0; i < count; i++) {
+ final BatchTest batchtest = batchTests.elementAt(i);
+ testLists.addAll(executeOrQueue(batchtest.elements(), false));
+ }
+ testLists.addAll(executeOrQueue(tests.elements(), forkPerTest));
+ }
+
+ try {
+ /* prior to parallel the code in 'oneJunitThread' used to be here. */
+ runTestsInThreads(testLists, threads);
+ } finally {
+ cleanup();
+ }
+ }
+
+ /*
+ * When the list of tests is established, an array of threads is created to pick the
+ * tests off the list one at a time and execute them until the list is empty. Tests are
+ * not assigned to threads until the thread is available.
+ *
+ * This class is the runnable thread subroutine that takes care of passing the shared
+ * list iterator and the handle back to the main class to the test execution subroutine
+ * code 'runTestsInThreads'. One object is created for each thread and each one gets
+ * a unique thread id that can be useful for tracing test starts and stops.
+ *
+ * Because the threads are picking tests off the same list, it is the list *iterator*
+ * that must be shared, not the list itself - and the iterator must have a thread-safe
+ * ability to pop the list - hence the synchronized 'getNextTest'.
+ */
+ private class JunitTestThread implements Runnable {
+
+ JunitTestThread(final JUnitTask master, final Iterator<List> iterator, final int id) {
+ this.masterTask = master;
+ this.iterator = iterator;
+ this.id = id;
+ }
+
+ public void run() {
+ try {
+ masterTask.oneJunitThread(iterator, id);
+ } catch (final BuildException b) {
+ /* saved to rethrow in main thread to be like single-threaded case */
+ caughtBuildException = b;
+ }
+ }
+
+ private final JUnitTask masterTask;
+ private final Iterator<List> iterator;
+ private final int id;
+ }
+
+ /*
+ * Because the threads are picking tests off the same list, it is the list *iterator*
+ * that must be shared, not the list itself - and the iterator must have a thread-safe
+ * ability to pop the list - hence the synchronized 'getNextTest'. We can't have two
+ * threads get the same test, or two threads simultaneously pop the list so that a test
+ * gets skipped!
+ */
+ private List getNextTest(final Iterator<List> iter) {
+ synchronized(iter) {
+ if (iter.hasNext()) {
+ return iter.next();
+ }
+ return null;
+ }
+ }
+
+ /*
+ * This code loops keeps executing the next test or test bunch (depending on fork mode)
+ * on the list of test cases until none are left. Basically this body of code used to
+ * be in the execute routine above; now, several copies (one for each test thread) execute
+ * simultaneously. The while loop was modified to call the new thread-safe atomic list
+ * popping subroutine and the logging messages were added.
+ *
+ * If one thread aborts due to a BuildException (haltOnError, haltOnFailure, or any other
+ * fatal reason, no new tests/batches will be started but the running threads will be
+ * permitted to complete. Additional tests may start in already-running batch-test threads.
+ */
+ private void oneJunitThread(final Iterator<List> iter, final int threadId) {
+
+ List l;
+ log("Starting test thread " + threadId, Project.MSG_VERBOSE);
+ while ((caughtBuildException == null) && ((l = getNextTest(iter)) != null)) {
+ log("Running test " + l.get(0).toString() + "(" + l.size() + ") in thread " + threadId, Project.MSG_VERBOSE);
+ if (l.size() == 1) {
+ execute((JUnitTest) l.get(0), threadId);
+ } else {
+ execute(l, threadId);
+ }
+ }
+ log("Ending test thread " + threadId, Project.MSG_VERBOSE);
+ }
+
+
+ private void runTestsInThreads(final List<List> testList, final int numThreads) {
+
+ Iterator<List> iter = testList.iterator();
+
+ if (numThreads == 1) {
+ /* with just one thread just run the test - don't create any threads */
+ oneJunitThread(iter, 0);
+ } else {
+ final Thread[] threads = new Thread[numThreads];
+ int i;
+ boolean exceptionOccurred;
+
+ /* Need to split apart tests, which are still grouped in batches */
+ /* is there a simpler Java mechanism to do this? */
+ /* I assume we don't want to do this with "per batch" forking. */
+ List<List> newlist = new ArrayList<List>();
+ if (forkMode.getValue().equals(ForkMode.PER_TEST)) {
+ final Iterator<List> i1 = testList.iterator();
+ while (i1.hasNext()) {
+ final List l = i1.next();
+ if (l.size() == 1) {
+ newlist.add(l);
+ } else {
+ final Iterator i2 = l.iterator();
+ while (i2.hasNext()) {
+ final List tmpSingleton = new ArrayList();
+ tmpSingleton.add(i2.next());
+ newlist.add(tmpSingleton);
+ }
+ }
+ }
+ } else {
+ newlist = testList;
+ }
+ iter = newlist.iterator();
+
+ /* create 1 thread using the passthrough class, and let each thread start */
+ for (i = 0; i < numThreads; i++) {
+ threads[i] = new Thread(new JunitTestThread(this, iter, i+1));
+ threads[i].start();
+ }
+
+ /* wait for all of the threads to complete. Not sure if the exception can actually occur in this use case. */
+ do {
+ exceptionOccurred = false;
+
+ try {
+ for (i = 0; i < numThreads; i++) {
+ threads[i].join();
+ }
+ } catch (final InterruptedException e) {
+ exceptionOccurred = true;
+ }
+ } while (exceptionOccurred);
+
+ /* an exception occurred in one of the threads - usually a haltOnError/Failure.
+ throw the exception again so it behaves like the single-thread case */
+ if (caughtBuildException != null) {
+ throw new BuildException(caughtBuildException);
+ }
+
+ /* all threads are completed - that's all there is to do. */
+ /* control will flow back to the test cleanup call and then execute is done. */
+ }
+ }
+
+ /**
+ * Run the tests.
+ * @param arg one JUnitTest
+ * @param thread Identifies which thread is test running in (0 for single-threaded runs)
+ * @throws BuildException in case of test failures or errors
+ */
+ protected void execute(final JUnitTest arg, final int thread) throws BuildException {
+ validateTestName(arg.getName());
+
+ final JUnitTest test = (JUnitTest) arg.clone();
+ test.setThread(thread);
+
+ // set the default values if not specified
+ //@todo should be moved to the test class instead.
+ if (test.getTodir() == null) {
+ test.setTodir(getProject().resolveFile("."));
+ }
+
+ if (test.getOutfile() == null) {
+ test.setOutfile("TEST-" + test.getName());
+ }
+
+ // execute the test and get the return code
+ TestResultHolder result = null;
+ if (!test.getFork()) {
+ result = executeInVM(test);
+ } else {
+ final ExecuteWatchdog watchdog = createWatchdog();
+ result = executeAsForked(test, watchdog, null);
+ // null watchdog means no timeout, you'd better not check with null
+ }
+ actOnTestResult(result, test, "Test " + test.getName());
+ }
+
+ /**
+ * Run the tests.
+ * @param arg one JUnitTest
+ * @throws BuildException in case of test failures or errors
+ */
+ protected void execute(final JUnitTest arg) throws BuildException {
+ execute(arg, 0);
+ }
+
+ /**
+ * Throws a <code>BuildException</code> if the given test name is invalid.
+ * Validity is defined as not <code>null</code>, not empty, and not the
+ * string &quot;null&quot;.
+ * @param testName the test name to be validated
+ * @throws BuildException if <code>testName</code> is not a valid test name
+ */
+ private void validateTestName(final String testName) throws BuildException {
+ if (testName == null || testName.length() == 0
+ || testName.equals("null")) {
+ throw new BuildException("test name must be specified");
+ }
+ }
+
+ /**
+ * Execute a list of tests in a single forked Java VM.
+ * @param testList the list of tests to execute.
+ * @param thread Identifies which thread is test running in (0 for single-threaded runs)
+ * @throws BuildException on error.
+ */
+ protected void execute(final List testList, final int thread) throws BuildException {
+ JUnitTest test = null;
+ // Create a temporary file to pass the test cases to run to
+ // the runner (one test case per line)
+ final File casesFile = createTempPropertiesFile("junittestcases");
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(casesFile));
+
+ log("Creating casesfile '" + casesFile.getAbsolutePath()
+ + "' with content: ", Project.MSG_VERBOSE);
+ final PrintStream logWriter =
+ new PrintStream(new LogOutputStream(this, Project.MSG_VERBOSE));
+
+ final Iterator iter = testList.iterator();
+ while (iter.hasNext()) {
+ test = (JUnitTest) iter.next();
+ test.setThread(thread);
+ printDual(writer, logWriter, test.getName());
+ if (test.getMethods() != null) {
+ printDual(writer, logWriter, ":" + test.getMethodsString().replace(',', '+'));
+ }
+ if (test.getTodir() == null) {
+ printDual(writer, logWriter,
+ "," + getProject().resolveFile("."));
+ } else {
+ printDual(writer, logWriter, "," + test.getTodir());
+ }
+
+ if (test.getOutfile() == null) {
+ printlnDual(writer, logWriter,
+ "," + "TEST-" + test.getName());
+ } else {
+ printlnDual(writer, logWriter, "," + test.getOutfile());
+ }
+ }
+ writer.flush();
+ writer.close();
+ writer = null;
+
+ // execute the test and get the return code
+ final ExecuteWatchdog watchdog = createWatchdog();
+ final TestResultHolder result =
+ executeAsForked(test, watchdog, casesFile);
+ actOnTestResult(result, test, "Tests");
+ } catch (final IOException e) {
+ log(e.toString(), Project.MSG_ERR);
+ throw new BuildException(e);
+ } finally {
+ FileUtils.close(writer);
+
+ try {
+ FILE_UTILS.tryHardToDelete(casesFile);
+ } catch (final Exception e) {
+ log(e.toString(), Project.MSG_ERR);
+ }
+ }
+ }
+
+ /**
+ * Execute a list of tests in a single forked Java VM.
+ * @param testList the list of tests to execute.
+ * @throws BuildException on error.
+ */
+ protected void execute(final List testList) throws BuildException {
+ execute(testList, 0);
+ }
+
+ /**
+ * Execute a testcase by forking a new JVM. The command will block
+ * until it finishes. To know if the process was destroyed or not
+ * or whether the forked Java VM exited abnormally, use the
+ * attributes of the returned holder object.
+ * @param test the testcase to execute.
+ * @param watchdog the watchdog in charge of cancelling the test if it
+ * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
+ * the test could probably hang forever.
+ * @param casesFile list of test cases to execute. Can be <tt>null</tt>,
+ * in this case only one test is executed.
+ * @return the test results from the JVM itself.
+ * @throws BuildException in case of error creating a temporary property file,
+ * or if the junit process can not be forked
+ */
+ private TestResultHolder executeAsForked(JUnitTest test,
+ final ExecuteWatchdog watchdog,
+ final File casesFile)
+ throws BuildException {
+
+ if (perm != null) {
+ log("Permissions ignored when running in forked mode!",
+ Project.MSG_WARN);
+ }
+
+ CommandlineJava cmd;
+ try {
+ cmd = (CommandlineJava) (getCommandline().clone());
+ } catch (final CloneNotSupportedException e) {
+ throw new BuildException("This shouldn't happen", e, getLocation());
+ }
+ if (casesFile == null) {
+ cmd.createArgument().setValue(test.getName());
+ if (test.getMethods() != null) {
+ cmd.createArgument().setValue(Constants.METHOD_NAMES + test.getMethodsString());
+ }
+ } else {
+ log("Running multiple tests in the same VM", Project.MSG_VERBOSE);
+ cmd.createArgument().setValue(Constants.TESTSFILE + casesFile);
+ }
+
+ cmd.createArgument().setValue(Constants.SKIP_NON_TESTS + String.valueOf(test.isSkipNonTests()));
+ cmd.createArgument().setValue(Constants.FILTERTRACE + test.getFiltertrace());
+ cmd.createArgument().setValue(Constants.HALT_ON_ERROR + test.getHaltonerror());
+ cmd.createArgument().setValue(Constants.HALT_ON_FAILURE
+ + test.getHaltonfailure());
+ checkIncludeAntRuntime(cmd);
+
+ checkIncludeSummary(cmd);
+
+ cmd.createArgument().setValue(Constants.SHOWOUTPUT
+ + String.valueOf(showOutput));
+ cmd.createArgument().setValue(Constants.OUTPUT_TO_FORMATTERS
+ + String.valueOf(outputToFormatters));
+ cmd.createArgument().setValue(Constants.LOG_FAILED_TESTS
+ + String.valueOf(logFailedTests));
+ cmd.createArgument().setValue(Constants.THREADID
+ + String.valueOf(test.getThread()));
+
+ // #31885
+ cmd.createArgument().setValue(Constants.LOGTESTLISTENEREVENTS
+ + String.valueOf(getEnableTestListenerEvents()));
+
+ StringBuffer formatterArg = new StringBuffer(STRING_BUFFER_SIZE);
+ final FormatterElement[] feArray = mergeFormatters(test);
+ for (int i = 0; i < feArray.length; i++) {
+ final FormatterElement fe = feArray[i];
+ if (fe.shouldUse(this)) {
+ formatterArg.append(Constants.FORMATTER);
+ formatterArg.append(fe.getClassname());
+ final File outFile = getOutput(fe, test);
+ if (outFile != null) {
+ formatterArg.append(",");
+ formatterArg.append(outFile);
+ }
+ cmd.createArgument().setValue(formatterArg.toString());
+ formatterArg = new StringBuffer();
+ }
+ }
+
+ final File vmWatcher = createTempPropertiesFile("junitvmwatcher");
+ cmd.createArgument().setValue(Constants.CRASHFILE
+ + vmWatcher.getAbsolutePath());
+ final File propsFile = createTempPropertiesFile("junit");
+ cmd.createArgument().setValue(Constants.PROPSFILE
+ + propsFile.getAbsolutePath());
+ final Hashtable p = getProject().getProperties();
+ final Properties props = new Properties();
+ for (final Enumeration e = p.keys(); e.hasMoreElements();) {
+ final Object key = e.nextElement();
+ props.put(key, p.get(key));
+ }
+ try {
+ final FileOutputStream outstream = new FileOutputStream(propsFile);
+ props.store(outstream, "Ant JUnitTask generated properties file");
+ outstream.close();
+ } catch (final java.io.IOException e) {
+ FILE_UTILS.tryHardToDelete(propsFile);
+ throw new BuildException("Error creating temporary properties "
+ + "file.", e, getLocation());
+ }
+
+ final Execute execute = new Execute(
+ new JUnitLogStreamHandler(
+ this,
+ Project.MSG_INFO,
+ Project.MSG_WARN),
+ watchdog);
+ execute.setCommandline(cmd.getCommandline());
+ execute.setAntRun(getProject());
+ if (dir != null) {
+ execute.setWorkingDirectory(dir);
+ }
+
+ final String[] environment = env.getVariables();
+ if (environment != null) {
+ for (int i = 0; i < environment.length; i++) {
+ log("Setting environment variable: " + environment[i],
+ Project.MSG_VERBOSE);
+ }
+ }
+ execute.setNewenvironment(newEnvironment);
+ execute.setEnvironment(environment);
+
+ log(cmd.describeCommand(), Project.MSG_VERBOSE);
+
+ checkForkedPath(cmd);
+
+ final TestResultHolder result = new TestResultHolder();
+ try {
+ result.exitCode = execute.execute();
+ } catch (final IOException e) {
+ throw new BuildException("Process fork failed.", e, getLocation());
+ } finally {
+ String vmCrashString = "unknown";
+ BufferedReader br = null;
+ try {
+ if (vmWatcher.exists()) {
+ br = new BufferedReader(new FileReader(vmWatcher));
+ vmCrashString = br.readLine();
+ } else {
+ vmCrashString = "Monitor file ("
+ + vmWatcher.getAbsolutePath()
+ + ") missing, location not writable,"
+ + " testcase not started or mixing ant versions?";
+ }
+ } catch (final Exception e) {
+ e.printStackTrace();
+ // ignored.
+ } finally {
+ FileUtils.close(br);
+ if (vmWatcher.exists()) {
+ FILE_UTILS.tryHardToDelete(vmWatcher);
+ }
+ }
+
+ final boolean crash = (watchdog != null && watchdog.killedProcess())
+ || !Constants.TERMINATED_SUCCESSFULLY.equals(vmCrashString);
+
+ if (casesFile != null && crash) {
+ test = createDummyTestForBatchTest(test);
+ }
+
+ if (watchdog != null && watchdog.killedProcess()) {
+ result.timedOut = true;
+ logTimeout(feArray, test, vmCrashString);
+ } else if (crash) {
+ result.crashed = true;
+ logVmCrash(feArray, test, vmCrashString);
+ }
+
+ if (!FILE_UTILS.tryHardToDelete(propsFile)) {
+ throw new BuildException("Could not delete temporary "
+ + "properties file '"
+ + propsFile.getAbsolutePath() + "'.");
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Adding ant runtime.
+ * @param cmd command to run
+ */
+ private void checkIncludeAntRuntime(final CommandlineJava cmd) {
+ if (includeAntRuntime) {
+ final Map/*<String, String>*/ env = Execute.getEnvironmentVariables();
+ final String cp = (String) env.get(CLASSPATH);
+ if (cp != null) {
+ cmd.createClasspath(getProject()).createPath()
+ .append(new Path(getProject(), cp));
+ }
+ log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH",
+ Project.MSG_VERBOSE);
+ cmd.createClasspath(getProject()).createPath()
+ .append(antRuntimeClasses);
+ }
+ }
+
+
+ /**
+ * check for the parameter being "withoutanderr" in a locale-independent way.
+ * @param summaryOption the summary option -can be null
+ * @return true if the run should be withoutput and error
+ */
+ private boolean equalsWithOutAndErr(final String summaryOption) {
+ return "withoutanderr".equalsIgnoreCase(summaryOption);
+ }
+
+ private void checkIncludeSummary(final CommandlineJava cmd) {
+ if (summary) {
+ String prefix = "";
+ if (equalsWithOutAndErr(summaryValue)) {
+ prefix = "OutErr";
+ }
+ cmd.createArgument()
+ .setValue(Constants.FORMATTER
+ + "org.apache.tools.ant.taskdefs.optional.junit."
+ + prefix + "SummaryJUnitResultFormatter");
+ }
+ }
+
+ /**
+ * Check the path for multiple different versions of
+ * ant.
+ * @param cmd command to execute
+ */
+ private void checkForkedPath(final CommandlineJava cmd) {
+ if (forkedPathChecked) {
+ return;
+ }
+ forkedPathChecked = true;
+ if (!cmd.haveClasspath()) {
+ return;
+ }
+ AntClassLoader loader = null;
+ try {
+ loader =
+ AntClassLoader.newAntClassLoader(null, getProject(),
+ cmd.createClasspath(getProject()),
+ true);
+ final String projectResourceName =
+ LoaderUtils.classNameToResource(Project.class.getName());
+ URL previous = null;
+ try {
+ for (final Enumeration e = loader.getResources(projectResourceName);
+ e.hasMoreElements();) {
+ final URL current = (URL) e.nextElement();
+ if (previous != null && !urlEquals(current, previous)) {
+ log("WARNING: multiple versions of ant detected "
+ + "in path for junit "
+ + LINE_SEP + " " + previous
+ + LINE_SEP + " and " + current,
+ Project.MSG_WARN);
+ return;
+ }
+ previous = current;
+ }
+ } catch (final Exception ex) {
+ // Ignore exception
+ }
+ } finally {
+ if (loader != null) {
+ loader.cleanup();
+ }
+ }
+ }
+
+ /**
+ * Compares URLs for equality but takes case-sensitivity into
+ * account when comparing file URLs and ignores the jar specific
+ * part of the URL if present.
+ */
+ private static boolean urlEquals(final URL u1, final URL u2) {
+ final String url1 = maybeStripJarAndClass(u1);
+ final String url2 = maybeStripJarAndClass(u2);
+ if (url1.startsWith("file:") && url2.startsWith("file:")) {
+ return new File(FILE_UTILS.fromURI(url1))
+ .equals(new File(FILE_UTILS.fromURI(url2)));
+ }
+ return url1.equals(url2);
+ }
+
+ private static String maybeStripJarAndClass(final URL u) {
+ String s = u.toString();
+ if (s.startsWith("jar:")) {
+ final int pling = s.indexOf('!');
+ s = s.substring(4, pling == -1 ? s.length() : pling);
+ }
+ return s;
+ }
+
+ /**
+ * Create a temporary file to pass the properties to a new process.
+ * Will auto-delete on (graceful) exit.
+ * The file will be in the project basedir unless tmpDir declares
+ * something else.
+ * @param prefix
+ * @return created file
+ */
+ private File createTempPropertiesFile(final String prefix) {
+ final File propsFile =
+ FILE_UTILS.createTempFile(prefix, ".properties",
+ tmpDir != null ? tmpDir : getProject().getBaseDir(), true, true);
+ return propsFile;
+ }
+
+
+ /**
+ * Pass output sent to System.out to the TestRunner so it can
+ * collect it for the formatters.
+ *
+ * @param output output coming from System.out
+ * @since Ant 1.5
+ */
+ @Override
+ protected void handleOutput(final String output) {
+ if (output.startsWith(TESTLISTENER_PREFIX)) {
+ log(output, Project.MSG_VERBOSE);
+ } else if (runner != null) {
+ if (outputToFormatters) {
+ runner.handleOutput(output);
+ }
+ if (showOutput) {
+ super.handleOutput(output);
+ }
+ } else {
+ super.handleOutput(output);
+ }
+ }
+
+ /**
+ * Handle an input request by this task.
+ * @see Task#handleInput(byte[], int, int)
+ * This implementation delegates to a runner if it
+ * present.
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ * @exception IOException if the data cannot be read.
+ *
+ * @since Ant 1.6
+ */
+ @Override
+ protected int handleInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ if (runner != null) {
+ return runner.handleInput(buffer, offset, length);
+ } else {
+ return super.handleInput(buffer, offset, length);
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.out to the TestRunner so it can
+ * collect ot for the formatters.
+ *
+ * @param output output coming from System.out
+ * @since Ant 1.5.2
+ */
+ @Override
+ protected void handleFlush(final String output) {
+ if (runner != null) {
+ runner.handleFlush(output);
+ if (showOutput) {
+ super.handleFlush(output);
+ }
+ } else {
+ super.handleFlush(output);
+ }
+ }
+
+ /**
+ * Pass output sent to System.err to the TestRunner so it can
+ * collect it for the formatters.
+ *
+ * @param output output coming from System.err
+ * @since Ant 1.5
+ */
+ @Override
+ public void handleErrorOutput(final String output) {
+ if (runner != null) {
+ runner.handleErrorOutput(output);
+ if (showOutput) {
+ super.handleErrorOutput(output);
+ }
+ } else {
+ super.handleErrorOutput(output);
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.err to the TestRunner so it can
+ * collect it for the formatters.
+ *
+ * @param output coming from System.err
+ * @since Ant 1.5.2
+ */
+ @Override
+ public void handleErrorFlush(final String output) {
+ if (runner != null) {
+ runner.handleErrorFlush(output);
+ if (showOutput) {
+ super.handleErrorFlush(output);
+ }
+ } else {
+ super.handleErrorFlush(output);
+ }
+ }
+
+ // in VM is not very nice since it could probably hang the
+ // whole build. IMHO this method should be avoided and it would be best
+ // to remove it in future versions. TBD. (SBa)
+
+ /**
+ * Execute inside VM.
+ * @param arg one JUnitTest
+ * @throws BuildException under unspecified circumstances
+ * @return the results
+ */
+ private TestResultHolder executeInVM(final JUnitTest arg) throws BuildException {
+ if (delegate == null) {
+ setupJUnitDelegate();
+ }
+
+ final JUnitTest test = (JUnitTest) arg.clone();
+ test.setProperties(getProject().getProperties());
+ if (dir != null) {
+ log("dir attribute ignored if running in the same VM",
+ Project.MSG_WARN);
+ }
+
+ if (newEnvironment || null != env.getVariables()) {
+ log("Changes to environment variables are ignored if running in "
+ + "the same VM.", Project.MSG_WARN);
+ }
+
+ if (getCommandline().getBootclasspath() != null) {
+ log("bootclasspath is ignored if running in the same VM.",
+ Project.MSG_WARN);
+ }
+
+ final CommandlineJava.SysProperties sysProperties =
+ getCommandline().getSystemProperties();
+ if (sysProperties != null) {
+ sysProperties.setSystem();
+ }
+
+ try {
+ log("Using System properties " + System.getProperties(),
+ Project.MSG_VERBOSE);
+ if (splitJUnit) {
+ classLoader = (AntClassLoader) delegate.getClass().getClassLoader();
+ } else {
+ createClassLoader();
+ }
+ if (classLoader != null) {
+ classLoader.setThreadContextLoader();
+ }
+ runner = delegate.newJUnitTestRunner(test, test.getMethods(), test.getHaltonerror(),
+ test.getFiltertrace(),
+ test.getHaltonfailure(), false,
+ getEnableTestListenerEvents(),
+ classLoader);
+ if (summary) {
+
+ final JUnitTaskMirror.SummaryJUnitResultFormatterMirror f =
+ delegate.newSummaryJUnitResultFormatter();
+ f.setWithOutAndErr(equalsWithOutAndErr(summaryValue));
+ f.setOutput(getDefaultOutput());
+ runner.addFormatter(f);
+ }
+
+ runner.setPermissions(perm);
+
+ final FormatterElement[] feArray = mergeFormatters(test);
+ for (int i = 0; i < feArray.length; i++) {
+ final FormatterElement fe = feArray[i];
+ if (fe.shouldUse(this)) {
+ final File outFile = getOutput(fe, test);
+ if (outFile != null) {
+ fe.setOutfile(outFile);
+ } else {
+ fe.setOutput(getDefaultOutput());
+ }
+ runner.addFormatter(fe.createFormatter(classLoader));
+ }
+ }
+
+ runner.run();
+ final TestResultHolder result = new TestResultHolder();
+ result.exitCode = runner.getRetCode();
+ return result;
+ } finally {
+ if (sysProperties != null) {
+ sysProperties.restoreSystem();
+ }
+ if (classLoader != null) {
+ classLoader.resetThreadContextLoader();
+ }
+ }
+ }
+
+ /**
+ * @return <tt>null</tt> if there is a timeout value, otherwise the
+ * watchdog instance.
+ *
+ * @throws BuildException under unspecified circumstances
+ * @since Ant 1.2
+ */
+ protected ExecuteWatchdog createWatchdog() throws BuildException {
+ if (timeout == null) {
+ return null;
+ }
+ return new ExecuteWatchdog((long) timeout.intValue());
+ }
+
+ /**
+ * Get the default output for a formatter.
+ *
+ * @return default output stream for a formatter
+ * @since Ant 1.3
+ */
+ protected OutputStream getDefaultOutput() {
+ return new LogOutputStream(this, Project.MSG_INFO);
+ }
+
+ /**
+ * Merge all individual tests from the batchtest with all individual tests
+ * and return an enumeration over all <tt>JUnitTest</tt>.
+ *
+ * @return enumeration over individual tests
+ * @since Ant 1.3
+ */
+ protected Enumeration<JUnitTest> getIndividualTests() {
+ final int count = batchTests.size();
+ final Enumeration[] enums = new Enumeration[ count + 1];
+ for (int i = 0; i < count; i++) {
+ final BatchTest batchtest = batchTests.elementAt(i);
+ enums[i] = batchtest.elements();
+ }
+ enums[enums.length - 1] = tests.elements();
+ return Enumerations.fromCompound(enums);
+ }
+
+ /**
+ * Verifies all <code>test</code> elements having the <code>methods</code>
+ * attribute specified and having the <code>if</code>-condition resolved
+ * to true, that the value of the <code>methods</code> attribute is valid.
+ * @exception BuildException if some of the tests matching the described
+ * conditions has invalid value of the
+ * <code>methods</code> attribute
+ * @since 1.8.2
+ */
+ private void checkMethodLists() throws BuildException {
+ if (tests.isEmpty()) {
+ return;
+ }
+
+ final Enumeration<JUnitTest> testsEnum = tests.elements();
+ while (testsEnum.hasMoreElements()) {
+ final JUnitTest test = testsEnum.nextElement();
+ if (test.hasMethodsSpecified() && test.shouldRun(getProject())) {
+ test.resolveMethods();
+ }
+ }
+ }
+
+ /**
+ * return an enumeration listing each test, then each batchtest
+ * @return enumeration
+ * @since Ant 1.3
+ */
+ protected Enumeration<JUnitTest> allTests() {
+ final Enumeration[] enums = {tests.elements(), batchTests.elements()};
+ return Enumerations.fromCompound(enums);
+ }
+
+ /**
+ * @param test junit test
+ * @return array of FormatterElement
+ * @since Ant 1.3
+ */
+ private FormatterElement[] mergeFormatters(final JUnitTest test) {
+ final Vector<FormatterElement> feVector = (Vector<FormatterElement>) formatters.clone();
+ test.addFormattersTo(feVector);
+ final FormatterElement[] feArray = new FormatterElement[feVector.size()];
+ feVector.copyInto(feArray);
+ return feArray;
+ }
+
+ /**
+ * If the formatter sends output to a file, return that file.
+ * null otherwise.
+ * @param fe formatter element
+ * @param test one JUnit test
+ * @return file reference
+ * @since Ant 1.3
+ */
+ protected File getOutput(final FormatterElement fe, final JUnitTest test) {
+ if (fe.getUseFile()) {
+ String base = test.getOutfile();
+ if (base == null) {
+ base = JUnitTaskMirror.JUnitTestRunnerMirror.IGNORED_FILE_NAME;
+ }
+ final String filename = base + fe.getExtension();
+ final File destFile = new File(test.getTodir(), filename);
+ final String absFilename = destFile.getAbsolutePath();
+ return getProject().resolveFile(absFilename);
+ }
+ return null;
+ }
+
+ /**
+ * Search for the given resource and add the directory or archive
+ * that contains it to the classpath.
+ *
+ * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+ * getResource doesn't contain the name of the archive.</p>
+ *
+ * @param resource resource that one wants to lookup
+ * @since Ant 1.4
+ */
+ protected void addClasspathEntry(final String resource) {
+ addClasspathResource(resource);
+ }
+
+ /**
+ * Implementation of addClasspathEntry.
+ *
+ * @param resource resource that one wants to lookup
+ * @return true if something was in fact added
+ * @since Ant 1.7.1
+ */
+ private boolean addClasspathResource(String resource) {
+ /*
+ * pre Ant 1.6 this method used to call getClass().getResource
+ * while Ant 1.6 will call ClassLoader.getResource().
+ *
+ * The difference is that Class.getResource expects a leading
+ * slash for "absolute" resources and will strip it before
+ * delegating to ClassLoader.getResource - so we now have to
+ * emulate Class's behavior.
+ */
+ if (resource.startsWith("/")) {
+ resource = resource.substring(1);
+ } else {
+ resource = "org/apache/tools/ant/taskdefs/optional/junit/"
+ + resource;
+ }
+
+ final File f = LoaderUtils.getResourceSource(JUnitTask.class.getClassLoader(),
+ resource);
+ if (f != null) {
+ log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+ antRuntimeClasses.createPath().setLocation(f);
+ return true;
+ } else {
+ log("Couldn\'t find " + resource, Project.MSG_DEBUG);
+ return false;
+ }
+ }
+
+ static final String TIMEOUT_MESSAGE =
+ "Timeout occurred. Please note the time in the report does"
+ + " not reflect the time until the timeout.";
+
+ /**
+ * Take care that some output is produced in report files if the
+ * watchdog kills the test.
+ *
+ * @since Ant 1.5.2
+ */
+ private void logTimeout(final FormatterElement[] feArray, final JUnitTest test,
+ final String testCase) {
+ logVmExit(feArray, test, TIMEOUT_MESSAGE, testCase);
+ }
+
+ /**
+ * Take care that some output is produced in report files if the
+ * forked machine exited before the test suite finished but the
+ * reason is not a timeout.
+ *
+ * @since Ant 1.7
+ */
+ private void logVmCrash(final FormatterElement[] feArray, final JUnitTest test, final String testCase) {
+ logVmExit(
+ feArray, test,
+ "Forked Java VM exited abnormally. Please note the time in the report"
+ + " does not reflect the time until the VM exit.",
+ testCase);
+ }
+
+ /**
+ * Take care that some output is produced in report files if the
+ * forked machine terminated before the test suite finished
+ *
+ * @since Ant 1.7
+ */
+ private void logVmExit(final FormatterElement[] feArray, final JUnitTest test,
+ final String message, final String testCase) {
+ if (delegate == null) {
+ setupJUnitDelegate();
+ }
+
+ try {
+ log("Using System properties " + System.getProperties(),
+ Project.MSG_VERBOSE);
+ if (splitJUnit) {
+ classLoader = (AntClassLoader) delegate.getClass().getClassLoader();
+ } else {
+ createClassLoader();
+ }
+ if (classLoader != null) {
+ classLoader.setThreadContextLoader();
+ }
+
+ test.setCounts(1, 0, 1, 0);
+ test.setProperties(getProject().getProperties());
+ for (int i = 0; i < feArray.length; i++) {
+ final FormatterElement fe = feArray[i];
+ if (fe.shouldUse(this)) {
+ final JUnitTaskMirror.JUnitResultFormatterMirror formatter =
+ fe.createFormatter(classLoader);
+ if (formatter != null) {
+ OutputStream out = null;
+ final File outFile = getOutput(fe, test);
+ if (outFile != null) {
+ try {
+ out = new FileOutputStream(outFile);
+ } catch (final IOException e) {
+ // ignore
+ }
+ }
+ if (out == null) {
+ out = getDefaultOutput();
+ }
+ delegate.addVmExit(test, formatter, out, message,
+ testCase);
+ }
+ }
+ }
+ if (summary) {
+ final JUnitTaskMirror.SummaryJUnitResultFormatterMirror f =
+ delegate.newSummaryJUnitResultFormatter();
+ f.setWithOutAndErr(equalsWithOutAndErr(summaryValue));
+ delegate.addVmExit(test, f, getDefaultOutput(), message, testCase);
+ }
+ } finally {
+ if (classLoader != null) {
+ classLoader.resetThreadContextLoader();
+ }
+ }
+ }
+
+ /**
+ * Creates and configures an AntClassLoader instance from the
+ * nested classpath element.
+ *
+ * @since Ant 1.6
+ */
+ private void createClassLoader() {
+ final Path userClasspath = getCommandline().getClasspath();
+ if (userClasspath != null) {
+ if (reloading || classLoader == null) {
+ deleteClassLoader();
+ final Path classpath = (Path) userClasspath.clone();
+ if (includeAntRuntime) {
+ log("Implicitly adding " + antRuntimeClasses
+ + " to CLASSPATH", Project.MSG_VERBOSE);
+ classpath.append(antRuntimeClasses);
+ }
+ classLoader = getProject().createClassLoader(classpath);
+ if (getClass().getClassLoader() != null
+ && getClass().getClassLoader() != Project.class.getClassLoader()) {
+ classLoader.setParent(getClass().getClassLoader());
+ }
+ classLoader.setParentFirst(false);
+ classLoader.addJavaLibraries();
+ log("Using CLASSPATH " + classLoader.getClasspath(),
+ Project.MSG_VERBOSE);
+ // make sure the test will be accepted as a TestCase
+ classLoader.addSystemPackageRoot("junit");
+ // make sure the test annotation are accepted
+ classLoader.addSystemPackageRoot("org.junit");
+ // will cause trouble in JDK 1.1 if omitted
+ classLoader.addSystemPackageRoot("org.apache.tools.ant");
+ }
+ }
+ }
+
+ /**
+ * Removes resources.
+ *
+ * <p>Is invoked in {@link #execute execute}. Subclasses that
+ * don't invoke execute should invoke this method in a finally
+ * block.</p>
+ *
+ * @since Ant 1.7.1
+ */
+ protected void cleanup() {
+ deleteClassLoader();
+ delegate = null;
+ }
+
+ /**
+ * Removes a classloader if needed.
+ * @since Ant 1.7
+ */
+ private void deleteClassLoader() {
+ if (classLoader != null) {
+ classLoader.cleanup();
+ classLoader = null;
+ }
+ if (mirrorLoader instanceof SplitClassLoader) {
+ ((SplitClassLoader) mirrorLoader).cleanup();
+ }
+ mirrorLoader = null;
+ }
+
+ /**
+ * Get the command line used to run the tests.
+ * @return the command line.
+ * @since Ant 1.6.2
+ */
+ protected CommandlineJava getCommandline() {
+ if (commandline == null) {
+ commandline = new CommandlineJava();
+ commandline.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
+ }
+ return commandline;
+ }
+
+ /**
+ * Forked test support
+ * @since Ant 1.6.2
+ */
+ private static final class ForkedTestConfiguration {
+ private final boolean filterTrace;
+ private final boolean haltOnError;
+ private final boolean haltOnFailure;
+ private final String errorProperty;
+ private final String failureProperty;
+
+ /**
+ * constructor for forked test configuration
+ * @param filterTrace
+ * @param haltOnError
+ * @param haltOnFailure
+ * @param errorProperty
+ * @param failureProperty
+ */
+ ForkedTestConfiguration(final boolean filterTrace, final boolean haltOnError,
+ final boolean haltOnFailure, final String errorProperty,
+ final String failureProperty) {
+ this.filterTrace = filterTrace;
+ this.haltOnError = haltOnError;
+ this.haltOnFailure = haltOnFailure;
+ this.errorProperty = errorProperty;
+ this.failureProperty = failureProperty;
+ }
+
+ /**
+ * configure from a test; sets member variables to attributes of the test
+ * @param test
+ */
+ ForkedTestConfiguration(final JUnitTest test) {
+ this(test.getFiltertrace(),
+ test.getHaltonerror(),
+ test.getHaltonfailure(),
+ test.getErrorProperty(),
+ test.getFailureProperty());
+ }
+
+ /**
+ * equality test checks all the member variables
+ * @param other
+ * @return true if everything is equal
+ */
+ @Override
+ public boolean equals(final Object other) {
+ if (other == null
+ || other.getClass() != ForkedTestConfiguration.class) {
+ return false;
+ }
+ final ForkedTestConfiguration o = (ForkedTestConfiguration) other;
+ return filterTrace == o.filterTrace
+ && haltOnError == o.haltOnError
+ && haltOnFailure == o.haltOnFailure
+ && ((errorProperty == null && o.errorProperty == null)
+ ||
+ (errorProperty != null
+ && errorProperty.equals(o.errorProperty)))
+ && ((failureProperty == null && o.failureProperty == null)
+ ||
+ (failureProperty != null
+ && failureProperty.equals(o.failureProperty)));
+ }
+
+ /**
+ * hashcode is based only on the boolean members, and returns a value
+ * in the range 0-7.
+ * @return hash code value
+ */
+ @Override
+ public int hashCode() {
+ // CheckStyle:MagicNumber OFF
+ return (filterTrace ? 1 : 0)
+ + (haltOnError ? 2 : 0)
+ + (haltOnFailure ? 4 : 0);
+ // CheckStyle:MagicNumber ON
+ }
+ }
+
+ /**
+ * These are the different forking options
+ * @since 1.6.2
+ */
+ public static final class ForkMode extends EnumeratedAttribute {
+
+ /**
+ * fork once only
+ */
+ public static final String ONCE = "once";
+ /**
+ * fork once per test class
+ */
+ public static final String PER_TEST = "perTest";
+ /**
+ * fork once per batch of tests
+ */
+ public static final String PER_BATCH = "perBatch";
+
+ /** No arg constructor. */
+ public ForkMode() {
+ super();
+ }
+
+ /**
+ * Constructor using a value.
+ * @param value the value to use - once, perTest or perBatch.
+ */
+ public ForkMode(final String value) {
+ super();
+ setValue(value);
+ }
+
+ /** {@inheritDoc}. */
+ @Override
+ public String[] getValues() {
+ return new String[] {ONCE, PER_TEST, PER_BATCH};
+ }
+ }
+
+ /**
+ * Executes all tests that don't need to be forked (or all tests
+ * if the runIndividual argument is true. Returns a collection of
+ * lists of tests that share the same VM configuration and haven't
+ * been executed yet.
+ * @param testList the list of tests to be executed or queued.
+ * @param runIndividual if true execute each test individually.
+ * @return a list of tasks to be executed.
+ * @since 1.6.2
+ */
+ protected Collection<List> executeOrQueue(final Enumeration<JUnitTest> testList,
+ final boolean runIndividual) {
+ final Map<ForkedTestConfiguration, List> testConfigurations = new HashMap<ForkedTestConfiguration, List>();
+ while (testList.hasMoreElements()) {
+ final JUnitTest test = testList.nextElement();
+ if (test.shouldRun(getProject())) {
+ /* with multi-threaded runs need to defer execution of even */
+ /* individual tests so the threads can pick tests off the queue. */
+ if ((runIndividual || !test.getFork()) && (threads == 1)) {
+ execute(test, 0);
+ } else {
+ final ForkedTestConfiguration c =
+ new ForkedTestConfiguration(test);
+ List<JUnitTest> l = testConfigurations.get(c);
+ if (l == null) {
+ l = new ArrayList<JUnitTest>();
+ testConfigurations.put(c, l);
+ }
+ l.add(test);
+ }
+ }
+ }
+ return testConfigurations.values();
+ }
+
+ /**
+ * Logs information about failed tests, potentially stops
+ * processing (by throwing a BuildException) if a failure/error
+ * occurred or sets a property.
+ * @param exitValue the exitValue of the test.
+ * @param wasKilled if true, the test had been killed.
+ * @param test the test in question.
+ * @param name the name of the test.
+ * @since Ant 1.6.2
+ */
+ protected void actOnTestResult(final int exitValue, final boolean wasKilled,
+ final JUnitTest test, final String name) {
+ final TestResultHolder t = new TestResultHolder();
+ t.exitCode = exitValue;
+ t.timedOut = wasKilled;
+ actOnTestResult(t, test, name);
+ }
+
+ /**
+ * Logs information about failed tests, potentially stops
+ * processing (by throwing a BuildException) if a failure/error
+ * occurred or sets a property.
+ * @param result the result of the test.
+ * @param test the test in question.
+ * @param name the name of the test.
+ * @since Ant 1.7
+ */
+ protected void actOnTestResult(final TestResultHolder result, final JUnitTest test,
+ final String name) {
+ // if there is an error/failure and that it should halt, stop
+ // everything otherwise just log a statement
+ final boolean fatal = result.timedOut || result.crashed;
+ final boolean errorOccurredHere =
+ result.exitCode == JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS || fatal;
+ final boolean failureOccurredHere =
+ result.exitCode != JUnitTaskMirror.JUnitTestRunnerMirror.SUCCESS || fatal;
+ if (errorOccurredHere || failureOccurredHere) {
+ if ((errorOccurredHere && test.getHaltonerror())
+ || (failureOccurredHere && test.getHaltonfailure())) {
+ throw new BuildException(name + " failed"
+ + (result.timedOut ? " (timeout)" : "")
+ + (result.crashed ? " (crashed)" : ""), getLocation());
+ } else {
+ if (logFailedTests) {
+ log(name + " FAILED"
+ + (result.timedOut ? " (timeout)" : "")
+ + (result.crashed ? " (crashed)" : ""),
+ Project.MSG_ERR);
+ }
+ if (errorOccurredHere && test.getErrorProperty() != null) {
+ getProject().setNewProperty(test.getErrorProperty(), "true");
+ }
+ if (failureOccurredHere && test.getFailureProperty() != null) {
+ getProject().setNewProperty(test.getFailureProperty(), "true");
+ }
+ }
+ }
+ }
+
+ /**
+ * A value class that contains the result of a test.
+ */
+ protected static class TestResultHolder {
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** the exit code of the test. */
+ public int exitCode = JUnitTaskMirror.JUnitTestRunnerMirror.ERRORS;
+ /** true if the test timed out */
+ public boolean timedOut = false;
+ /** true if the test crashed */
+ public boolean crashed = false;
+ // CheckStyle:VisibilityModifier ON
+ }
+
+ /**
+ * A stream handler for handling the junit task.
+ * @since Ant 1.7
+ */
+ protected static class JUnitLogOutputStream extends LogOutputStream {
+ private final Task task; // local copy since LogOutputStream.task is private
+
+ /**
+ * Constructor.
+ * @param task the task being logged.
+ * @param level the log level used to log data written to this stream.
+ */
+ public JUnitLogOutputStream(final Task task, final int level) {
+ super(task, level);
+ this.task = task;
+ }
+
+ /**
+ * Logs a line.
+ * If the line starts with junit.framework.TestListener: set the level
+ * to MSG_VERBOSE.
+ * @param line the line to log.
+ * @param level the logging level to use.
+ */
+ @Override
+ protected void processLine(final String line, final int level) {
+ if (line.startsWith(TESTLISTENER_PREFIX)) {
+ task.log(line, Project.MSG_VERBOSE);
+ } else {
+ super.processLine(line, level);
+ }
+ }
+ }
+
+ /**
+ * A log stream handler for junit.
+ * @since Ant 1.7
+ */
+ protected static class JUnitLogStreamHandler extends PumpStreamHandler {
+ /**
+ * Constructor.
+ * @param task the task to log.
+ * @param outlevel the level to use for standard output.
+ * @param errlevel the level to use for error output.
+ */
+ public JUnitLogStreamHandler(final Task task, final int outlevel, final int errlevel) {
+ super(new JUnitLogOutputStream(task, outlevel),
+ new LogOutputStream(task, errlevel));
+ }
+ }
+
+ static final String NAME_OF_DUMMY_TEST = "Batch-With-Multiple-Tests";
+
+ /**
+ * Creates a JUnitTest instance that shares all flags with the
+ * passed in instance but has a more meaningful name.
+ *
+ * <p>If a VM running multiple tests crashes, we don't know which
+ * test failed. Prior to Ant 1.8.0 Ant would log the error with
+ * the last test of the batch test, which caused some confusion
+ * since the log might look as if a test had been executed last
+ * that was never started. With Ant 1.8.0 the test's name will
+ * indicate that something went wrong with a test inside the batch
+ * without giving it a real name.</p>
+ *
+ * @see "https://issues.apache.org/bugzilla/show_bug.cgi?id=45227"
+ */
+ private static JUnitTest createDummyTestForBatchTest(final JUnitTest test) {
+ final JUnitTest t = (JUnitTest) test.clone();
+ final int index = test.getName().lastIndexOf('.');
+ // make sure test looks as if it was in the same "package" as
+ // the last test of the batch
+ final String pack = index > 0 ? test.getName().substring(0, index + 1) : "";
+ t.setName(pack + NAME_OF_DUMMY_TEST);
+ return t;
+ }
+
+ private static void printDual(final BufferedWriter w, final PrintStream s, final String text)
+ throws IOException {
+ w.write(String.valueOf(text));
+ s.print(text);
+ }
+
+ private static void printlnDual(final BufferedWriter w, final PrintStream s, final String text)
+ throws IOException {
+ w.write(String.valueOf(text));
+ w.newLine();
+ s.println(text);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java
new file mode 100644
index 00000000..694e1d8c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirror.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.types.Permissions;
+
+/**
+ * Handles the portions of {@link JUnitTask} which need to directly access
+ * actual JUnit classes, so that junit.jar need not be on Ant's startup classpath.
+ * Neither JUnitTask.java nor JUnitTaskMirror.java nor their transitive static
+ * deps may import any junit.** classes!
+ * Specifically, need to not refer to
+ * - JUnitResultFormatter or its subclasses
+ * - JUnitVersionHelper
+ * - JUnitTestRunner
+ * Cf. JUnitTask.SplitLoader#isSplit(String)
+ * Public only to permit access from classes in this package; do not use directly.
+ *
+ * @since 1.7
+ * @see "bug #38799"
+ */
+public interface JUnitTaskMirror {
+
+ /**
+ * Add the formatter to be called when the jvm exits before
+ * the test suite finishes.
+ * @param test the test.
+ * @param formatter the formatter to use.
+ * @param out the output stream to use.
+ * @param message the message to write out.
+ * @param testCase the name of the test.
+ */
+ void addVmExit(JUnitTest test, JUnitResultFormatterMirror formatter,
+ OutputStream out, String message, String testCase);
+
+ /**
+ * Create a new test runner for a test.
+ * @param test the test to run.
+ * @param methods names of the test methods to be run.
+ * @param haltOnError if true halt the tests if an error occurs.
+ * @param filterTrace if true filter the stack traces.
+ * @param haltOnFailure if true halt the test if a failure occurs.
+ * @param showOutput if true show output.
+ * @param logTestListenerEvents if true log test listener events.
+ * @param classLoader the classloader to use to create the runner.
+ * @return the test runner.
+ */
+ JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test, String[] methods, boolean haltOnError,
+ boolean filterTrace, boolean haltOnFailure, boolean showOutput,
+ boolean logTestListenerEvents, AntClassLoader classLoader);
+
+ /**
+ * Create a summary result formatter.
+ * @return the created formatter.
+ */
+ SummaryJUnitResultFormatterMirror newSummaryJUnitResultFormatter();
+
+
+ /** The interface that JUnitResultFormatter extends. */
+ public interface JUnitResultFormatterMirror {
+ /**
+ * Set the output stream.
+ * @param outputStream the stream to use.
+ */
+ void setOutput(OutputStream outputStream);
+ }
+
+ /** The interface that SummaryJUnitResultFormatter extends. */
+ public interface SummaryJUnitResultFormatterMirror
+ extends JUnitResultFormatterMirror {
+
+ /**
+ * Set where standard out and standard error should be included.
+ * @param value if true include the outputs in the summary.
+ */
+ void setWithOutAndErr(boolean value);
+ }
+
+ /** Interface that test runners implement. */
+ public interface JUnitTestRunnerMirror {
+
+ /**
+ * Used in formatter arguments as a placeholder for the basename
+ * of the output file (which gets replaced by a test specific
+ * output file name later).
+ *
+ * @since Ant 1.6.3
+ */
+ String IGNORED_FILE_NAME = "IGNORETHIS";
+
+ /**
+ * No problems with this test.
+ */
+ int SUCCESS = 0;
+
+ /**
+ * Some tests failed.
+ */
+ int FAILURES = 1;
+
+ /**
+ * An error occurred.
+ */
+ int ERRORS = 2;
+
+ /**
+ * Permissions for the test run.
+ * @param perm the permissions to use.
+ */
+ void setPermissions(Permissions perm);
+
+ /** Run the test. */
+ void run();
+
+ /**
+ * Add a formatter to the test.
+ * @param formatter the formatter to use.
+ */
+ void addFormatter(JUnitResultFormatterMirror formatter);
+
+ /**
+ * Returns what System.exit() would return in the standalone version.
+ *
+ * @return 2 if errors occurred, 1 if tests failed else 0.
+ */
+ int getRetCode();
+
+ /**
+ * Handle output sent to System.err.
+ *
+ * @param output coming from System.err
+ */
+ void handleErrorFlush(String output);
+
+ /**
+ * Handle output sent to System.err.
+ *
+ * @param output output for System.err
+ */
+ void handleErrorOutput(String output);
+
+ /**
+ * Handle output sent to System.out.
+ *
+ * @param output output for System.out.
+ */
+ void handleOutput(String output);
+
+ /**
+ * Handle an input request.
+ *
+ * @param buffer the buffer into which data is to be read.
+ * @param offset the offset into the buffer at which data is stored.
+ * @param length the amount of data to read.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception IOException if the data cannot be read.
+ */
+ int handleInput(byte[] buffer, int offset, int length) throws IOException;
+
+ /**
+ * Handle output sent to System.out.
+ *
+ * @param output output for System.out.
+ */
+ void handleFlush(String output);
+
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java
new file mode 100644
index 00000000..c7dae258
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTaskMirrorImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+import org.apache.tools.ant.AntClassLoader;
+
+/**
+ * Implementation of the part of the junit task which can directly refer to junit.* classes.
+ * Public only to permit use of reflection; do not use directly.
+ * @see JUnitTaskMirror
+ * @see "bug #38799"
+ * @since 1.7
+ */
+public final class JUnitTaskMirrorImpl implements JUnitTaskMirror {
+
+ private final JUnitTask task;
+
+ /**
+ * Constructor.
+ * @param task the junittask that uses this mirror.
+ */
+ public JUnitTaskMirrorImpl(JUnitTask task) {
+ this.task = task;
+ }
+
+ /** {@inheritDoc}. */
+ public void addVmExit(JUnitTest test, JUnitTaskMirror.JUnitResultFormatterMirror aFormatter,
+ OutputStream out, String message, String testCase) {
+ JUnitResultFormatter formatter = (JUnitResultFormatter) aFormatter;
+ formatter.setOutput(out);
+ formatter.startTestSuite(test);
+ //the trick to integrating test output to the formatter, is to
+ //create a special test class that asserts an error
+ //and tell the formatter that it raised.
+ TestCase t = new VmExitErrorTest(message, test, testCase);
+ formatter.startTest(t);
+ formatter.addError(t, new AssertionFailedError(message));
+ formatter.endTestSuite(test);
+ }
+
+ /** {@inheritDoc}. */
+ public JUnitTaskMirror.JUnitTestRunnerMirror newJUnitTestRunner(JUnitTest test,
+ String[] methods,
+ boolean haltOnError, boolean filterTrace, boolean haltOnFailure,
+ boolean showOutput, boolean logTestListenerEvents, AntClassLoader classLoader) {
+ return new JUnitTestRunner(test, methods, haltOnError, filterTrace, haltOnFailure,
+ showOutput, logTestListenerEvents, classLoader);
+ }
+
+ /** {@inheritDoc}. */
+ public JUnitTaskMirror.SummaryJUnitResultFormatterMirror newSummaryJUnitResultFormatter() {
+ return new SummaryJUnitResultFormatter();
+ }
+
+ static class VmExitErrorTest extends TestCase {
+
+ private String message;
+ private JUnitTest test;
+ private String testCase;
+
+ VmExitErrorTest(String aMessage, JUnitTest anOriginalTest, String aTestCase) {
+ message = aMessage;
+ test = anOriginalTest;
+ testCase = aTestCase;
+ }
+
+ public int countTestCases() {
+ return 1;
+ }
+
+ public void run(TestResult r) {
+ throw new AssertionFailedError(message);
+ }
+
+ public String getName() {
+ return testCase;
+ }
+
+ String getClassName() {
+ return test.getName();
+ }
+
+ public String toString() {
+ return test.getName() + ":" + testCase;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
new file mode 100644
index 00000000..835c013b
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
@@ -0,0 +1,542 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+
+/**
+ * <p> Run a single JUnit test.
+ *
+ * <p> The JUnit test is actually run by {@link JUnitTestRunner}.
+ * So read the doc comments for that class :)
+ *
+ * @since Ant 1.2
+ *
+ * @see JUnitTask
+ * @see JUnitTestRunner
+ */
+public class JUnitTest extends BaseTest implements Cloneable {
+
+ /** the name of the test case */
+ private String name = null;
+
+ /**
+ * whether the list of test methods has been specified
+ * @see #setMethods(java.lang.String)
+ * @see #setMethods(java.lang.String[])
+ */
+ private boolean methodsSpecified = false;
+
+ /** comma-separated list of names of test methods to execute */
+ private String methodsList = null;
+
+ /** the names of test methods to execute */
+ private String[] methods = null;
+
+ /** the name of the result file */
+ private String outfile = null;
+
+ // @todo this is duplicating TestResult information. Only the time is not
+ // part of the result. So we'd better derive a new class from TestResult
+ // and deal with it. (SB)
+ private long runs, failures, errors;
+ /**
+ @since Ant 1.9.0
+ */
+ private long skips;
+
+ private long runTime;
+
+ private int antThreadID;
+
+ // Snapshot of the system properties
+ private Properties props = null;
+
+ /** No arg constructor. */
+ public JUnitTest() {
+ }
+
+ /**
+ * Constructor with name.
+ * @param name the name of the test.
+ */
+ public JUnitTest(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Constructor with options.
+ * @param name the name of the test.
+ * @param haltOnError if true halt the tests if there is an error.
+ * @param haltOnFailure if true halt the tests if there is a failure.
+ * @param filtertrace if true filter stack traces.
+ */
+ public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+ boolean filtertrace) {
+ this(name, haltOnError, haltOnFailure, filtertrace, null, 0);
+ }
+
+ /**
+ * Constructor with options.
+ * @param name the name of the test.
+ * @param haltOnError if true halt the tests if there is an error.
+ * @param haltOnFailure if true halt the tests if there is a failure.
+ * @param filtertrace if true filter stack traces.
+ * @param methods if non-null run only these test methods
+ * @since 1.8.2
+ */
+ public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+ boolean filtertrace, String[] methods) {
+ this(name, haltOnError, haltOnFailure, filtertrace, methods, 0);
+ }
+
+ /**
+ * Constructor with options.
+ * @param name the name of the test.
+ * @param haltOnError if true halt the tests if there is an error.
+ * @param haltOnFailure if true halt the tests if there is a failure.
+ * @param filtertrace if true filter stack traces.
+ * @param methods if non-null run only these test methods
+ * @param thread Ant thread ID in which test is currently running
+ * @since 1.9.4
+ */
+ public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+ boolean filtertrace, String[] methods, int thread) {
+ this.name = name;
+ this.haltOnError = haltOnError;
+ this.haltOnFail = haltOnFailure;
+ this.filtertrace = filtertrace;
+ this.methodsSpecified = methods != null;
+ this.methods = methodsSpecified ? (String[]) methods.clone() : null;
+ this.antThreadID = thread;
+ }
+
+ /**
+ * Sets names of individual test methods to be executed.
+ * @param value comma-separated list of names of individual test methods
+ * to be executed,
+ * or <code>null</code> if all test methods should be executed
+ * @since 1.8.2
+ */
+ public void setMethods(String value) {
+ methodsList = value;
+ methodsSpecified = (value != null);
+ methods = null;
+ }
+
+ /**
+ * Sets names of individual test methods to be executed.
+ * @param value non-empty array of names of test methods to be executed
+ * @see #setMethods(String)
+ * @since 1.8.2
+ */
+ void setMethods(String[] value) {
+ methods = value;
+ methodsSpecified = (value != null);
+ methodsList = null;
+ }
+
+ /**
+ * Set the name of the test class.
+ * @param value the name to use.
+ */
+ public void setName(String value) {
+ name = value;
+ }
+
+ /**
+ * Set the thread id
+ * @param thread the Ant id of the thread running this test
+ * (this is not the system process or thread id)
+ * (this will be 0 in single-threaded mode).
+ * @since Ant 1.9.4
+ */
+ public void setThread(int thread) {
+ this.antThreadID = thread;
+ }
+
+ /**
+ * Set the name of the output file.
+ * @param value the name of the output file to use.
+ */
+ public void setOutfile(String value) {
+ outfile = value;
+ }
+
+ /**
+ * Informs whether a list of test methods has been specified in this test.
+ * @return <code>true</code> if test methods to be executed have been
+ * specified, <code>false</code> otherwise
+ * @see #setMethods(java.lang.String)
+ * @see #setMethods(java.lang.String[])
+ * @since 1.8.2
+ */
+ boolean hasMethodsSpecified() {
+ return methodsSpecified;
+ }
+
+ /**
+ * Get names of individual test methods to be executed.
+ *
+ * @return array of names of the individual test methods to be executed,
+ * or <code>null</code> if all test methods in the suite
+ * defined by the test class will be executed
+ * @since 1.8.2
+ */
+ String[] getMethods() {
+ if (methodsSpecified && (methods == null)) {
+ resolveMethods();
+ }
+ return methods;
+ }
+
+ /**
+ * Gets a comma-separated list of names of methods that are to be executed
+ * by this test.
+ * @return the comma-separated list of test method names, or an empty
+ * string of no method is to be executed, or <code>null</code>
+ * if no method is specified
+ * @since 1.8.2
+ */
+ String getMethodsString() {
+ if ((methodsList == null) && methodsSpecified) {
+ if (methods.length == 0) {
+ methodsList = "";
+ } else if (methods.length == 1) {
+ methodsList = methods[0];
+ } else {
+ StringBuffer buf = new StringBuffer(methods.length * 16);
+ buf.append(methods[0]);
+ for (int i = 1; i < methods.length; i++) {
+ buf.append(',').append(methods[i]);
+ }
+ methodsList = buf.toString();
+ }
+ }
+ return methodsList;
+ }
+
+ /**
+ * Computes the value of the {@link #methods} field from the value
+ * of the {@link #methodsList} field, if it has not been computed yet.
+ * @exception BuildException if the value of the {@link #methodsList} field
+ * was invalid
+ * @since 1.8.2
+ */
+ void resolveMethods() {
+ if ((methods == null) && methodsSpecified) {
+ try {
+ methods = parseTestMethodNamesList(methodsList);
+ } catch (IllegalArgumentException ex) {
+ throw new BuildException(
+ "Invalid specification of test methods: \""
+ + methodsList
+ + "\"; expected: comma-separated list of valid Java identifiers",
+ ex);
+ }
+ }
+ }
+
+ /**
+ * Parses a comma-separated list of method names and check their validity.
+ * @param methodNames comma-separated list of method names to be parsed
+ * @return array of individual test method names
+ * @exception java.lang.IllegalArgumentException
+ * if the given string is <code>null</code> or if it is not
+ * a comma-separated list of valid Java identifiers;
+ * an empty string is acceptable and is handled as an empty
+ * list
+ * @since 1.8.2
+ */
+ public static String[] parseTestMethodNamesList(String methodNames)
+ throws IllegalArgumentException {
+ if (methodNames == null) {
+ throw new IllegalArgumentException("methodNames is <null>");
+ }
+
+ methodNames = methodNames.trim();
+
+ int length = methodNames.length();
+ if (length == 0) {
+ return new String[0];
+ }
+
+ /* strip the trailing comma, if any */
+ if (methodNames.charAt(length - 1) == ',') {
+ methodNames = methodNames.substring(0, length - 1).trim();
+ length = methodNames.length();
+ if (length == 0) {
+ throw new IllegalArgumentException("Empty method name");
+ }
+ }
+
+ final char[] chars = methodNames.toCharArray();
+ /* easy detection of one particular case of illegal string: */
+ if (chars[0] == ',') {
+ throw new IllegalArgumentException("Empty method name");
+ }
+ /* count number of method names: */
+ int wordCount = 1;
+ for (int i = 1; i < chars.length; i++) {
+ if (chars[i] == ',') {
+ wordCount++;
+ }
+ }
+ /* prepare the resulting array: */
+ String[] result = new String[wordCount];
+ /* parse the string: */
+ final int stateBeforeWord = 1;
+ final int stateInsideWord = 2;
+ final int stateAfterWord = 3;
+ //
+ int state = stateBeforeWord;
+ int wordStartIndex = -1;
+ int wordIndex = 0;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ switch (state) {
+ case stateBeforeWord:
+ if (c == ',') {
+ throw new IllegalArgumentException("Empty method name");
+ } else if (c == ' ') {
+ // remain in the same state
+ } else if (Character.isJavaIdentifierStart(c)) {
+ wordStartIndex = i;
+ state = stateInsideWord;
+ } else {
+ throw new IllegalArgumentException("Illegal start of method name: " + c);
+ }
+ break;
+ case stateInsideWord:
+ if (c == ',') {
+ result[wordIndex++] = methodNames.substring(wordStartIndex, i);
+ state = stateBeforeWord;
+ } else if (c == ' ') {
+ result[wordIndex++] = methodNames.substring(wordStartIndex, i);
+ state = stateAfterWord;
+ } else if (Character.isJavaIdentifierPart(c)) {
+ // remain in the same state
+ } else {
+ throw new IllegalArgumentException("Illegal character in method name: " + c);
+ }
+ break;
+ case stateAfterWord:
+ if (c == ',') {
+ state = stateBeforeWord;
+ } else if (c == ' ') {
+ // remain in the same state
+ } else {
+ throw new IllegalArgumentException("Space in method name");
+ }
+ break;
+ default:
+ // this should never happen
+ }
+ }
+ switch (state) {
+ case stateBeforeWord:
+ case stateAfterWord:
+ break;
+ case stateInsideWord:
+ result[wordIndex++] = methodNames.substring(wordStartIndex, chars.length);
+ break;
+ default:
+ // this should never happen
+ }
+ return result;
+ }
+
+ /**
+ * Get the name of the test class.
+ * @return the name of the test.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the Ant id of the thread running the test.
+ * @return the thread id
+ */
+ public int getThread() {
+ return antThreadID;
+ }
+
+ /**
+ * Get the name of the output file
+ *
+ * @return the name of the output file.
+ */
+ public String getOutfile() {
+ return outfile;
+ }
+
+ /**
+ * Set the number of runs, failures, errors, and skipped tests.
+ * @param runs the number of runs.
+ * @param failures the number of failures.
+ * @param errors the number of errors.
+ * Kept for backward compatibility with Ant 1.8.4
+ */
+ public void setCounts(long runs, long failures, long errors) {
+ this.runs = runs;
+ this.failures = failures;
+ this.errors = errors;
+ }
+ /**
+ * Set the number of runs, failures, errors, and skipped tests.
+ * @param runs the number of runs.
+ * @param failures the number of failures.
+ * @param errors the number of errors.
+ * @param skips the number of skipped tests.
+ * @since Ant 1.9.0
+ */
+ public void setCounts(long runs, long failures, long errors, long skips) {
+ this.runs = runs;
+ this.failures = failures;
+ this.errors = errors;
+ this.skips = skips;
+ }
+
+ /**
+ * Set the runtime.
+ * @param runTime the time in milliseconds.
+ */
+ public void setRunTime(long runTime) {
+ this.runTime = runTime;
+ }
+
+ /**
+ * Get the number of runs.
+ * @return the number of runs.
+ */
+ public long runCount() {
+ return runs;
+ }
+
+ /**
+ * Get the number of failures.
+ * @return the number of failures.
+ */
+ public long failureCount() {
+ return failures;
+ }
+
+ /**
+ * Get the number of errors.
+ * @return the number of errors.
+ */
+ public long errorCount() {
+ return errors;
+ }
+
+ /**
+ * Get the number of skipped tests.
+ * @return the number of skipped tests.
+ */
+ public long skipCount() {
+ return skips;
+ }
+
+ /**
+ * Get the run time.
+ * @return the run time in milliseconds.
+ */
+ public long getRunTime() {
+ return runTime;
+ }
+
+ /**
+ * Get the properties used in the test.
+ * @return the properties.
+ */
+ public Properties getProperties() {
+ return props;
+ }
+
+ /**
+ * Set the properties to be used in the test.
+ * @param p the properties.
+ * This is a copy of the projects ant properties.
+ */
+ public void setProperties(Hashtable p) {
+ props = new Properties();
+ for (Enumeration e = p.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ props.put(key, p.get(key));
+ }
+ }
+
+ /**
+ * Check if this test should run based on the if and unless
+ * attributes.
+ * @param p the project to use to check if the if and unless
+ * properties exist in.
+ * @return true if this test or testsuite should be run.
+ */
+ public boolean shouldRun(Project p) {
+ PropertyHelper ph = PropertyHelper.getPropertyHelper(p);
+ return ph.testIfCondition(getIfCondition())
+ && ph.testUnlessCondition(getUnlessCondition());
+ }
+
+ /**
+ * Get the formatters set for this test.
+ * @return the formatters as an array.
+ */
+ public FormatterElement[] getFormatters() {
+ FormatterElement[] fes = new FormatterElement[formatters.size()];
+ formatters.copyInto(fes);
+ return fes;
+ }
+
+ /**
+ * Convenient method to add formatters to a vector
+ */
+ void addFormattersTo(Vector v) {
+ final int count = formatters.size();
+ for (int i = 0; i < count; i++) {
+ v.addElement(formatters.elementAt(i));
+ }
+ }
+
+ /**
+ * @since Ant 1.5
+ * @return a clone of this test.
+ */
+ @Override
+ public Object clone() {
+ try {
+ JUnitTest t = (JUnitTest) super.clone();
+ t.props = props == null ? null : (Properties) props.clone();
+ t.formatters = (Vector) formatters.clone();
+ return t;
+ } catch (CloneNotSupportedException e) {
+ // plain impossible
+ return this;
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
new file mode 100644
index 00000000..a457375e
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
@@ -0,0 +1,1297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * Simple Testrunner for JUnit that runs all tests of a testsuite.
+ *
+ * <p>This TestRunner expects a name of a TestCase class as its
+ * argument. If this class provides a static suite() method it will be
+ * called and the resulting Test will be run. So, the signature should be
+ * <pre><code>
+ * public static junit.framework.Test suite()
+ * </code></pre>
+ *
+ * <p> If no such method exists, all public methods starting with
+ * "test" and taking no argument will be run.
+ *
+ * <p> Summary output is generated at the end.
+ *
+ * @since Ant 1.2
+ */
+
+public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestRunnerMirror {
+
+ /**
+ * Holds the registered formatters.
+ */
+ private final Vector<JUnitTaskMirror.JUnitResultFormatterMirror> formatters = new Vector();
+
+ /**
+ * Collects TestResults.
+ */
+ private IgnoredTestResult res;
+
+ /**
+ * Do we filter junit.*.* stack frames out of failure and error exceptions.
+ */
+ private static boolean filtertrace = true;
+
+ /**
+ * Do we send output to System.out/.err in addition to the formatters?
+ */
+ private boolean showOutput = false;
+
+ private boolean outputToFormatters = true;
+
+ /**
+ * The permissions set for the test to run.
+ */
+ private Permissions perm = null;
+
+ private static final String JUNIT_4_TEST_ADAPTER
+ = "junit.framework.JUnit4TestAdapter";
+
+ private static final String[] DEFAULT_TRACE_FILTERS = new String[] {
+ "junit.framework.TestCase",
+ "junit.framework.TestResult",
+ "junit.framework.TestSuite",
+ "junit.framework.Assert.", // don't filter AssertionFailure
+ "junit.swingui.TestRunner",
+ "junit.awtui.TestRunner",
+ "junit.textui.TestRunner",
+ "java.lang.reflect.Method.invoke(",
+ "sun.reflect.",
+ "org.apache.tools.ant.",
+ // JUnit 4 support:
+ "org.junit.",
+ "junit.framework.JUnit4TestAdapter",
+ " more",
+ };
+
+
+ /**
+ * Do we stop on errors.
+ */
+ private boolean haltOnError = false;
+
+ /**
+ * Do we stop on test failures.
+ */
+ private boolean haltOnFailure = false;
+
+ /**
+ * Returncode
+ */
+ private int retCode = SUCCESS;
+
+ /**
+ * The TestSuite we are currently running.
+ */
+ private final JUnitTest junitTest;
+
+ /** output written during the test */
+ private PrintStream systemError;
+
+ /** Error output during the test */
+ private PrintStream systemOut;
+
+ /** is this runner running in forked mode? */
+ private boolean forked = false;
+
+ /** Running more than one test suite? */
+ private static boolean multipleTests = false;
+
+ /** ClassLoader passed in in non-forked mode. */
+ private final ClassLoader loader;
+
+ /** Do we print TestListener events? */
+ private boolean logTestListenerEvents = false;
+
+ /** Turned on if we are using JUnit 4 for this test suite. see #38811 */
+ private boolean junit4;
+
+ /**
+ * The file used to indicate that the build crashed.
+ * File will be empty in case the build did not crash.
+ */
+ private static String crashFile = null;
+
+ /** Names of test methods to execute */
+ private String[] methods = null;
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure) {
+ this(test, haltOnError, filtertrace, haltOnFailure, false);
+ }
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput) {
+ this(test, haltOnError, filtertrace, haltOnFailure, showOutput, false);
+ }
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param logTestListenerEvents whether to print TestListener events.
+ * @since Ant 1.7
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents) {
+ this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput,
+ logTestListenerEvents, null);
+ }
+
+ /**
+ * Constructor for fork=true or when the user hasn't specified a
+ * classpath.
+ * @param test the test to run.
+ * @param methods names of methods of the test to be executed.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param logTestListenerEvents whether to print TestListener events.
+ * @since 1.8.2
+ */
+ public JUnitTestRunner(final JUnitTest test, final String[] methods, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents) {
+ this(test, methods, haltOnError, filtertrace, haltOnFailure, showOutput,
+ logTestListenerEvents, null);
+ }
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param loader the classloader to use running the test.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final ClassLoader loader) {
+ this(test, haltOnError, filtertrace, haltOnFailure, false, loader);
+ }
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param loader the classloader to use running the test.
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final ClassLoader loader) {
+ this(test, haltOnError, filtertrace, haltOnFailure, showOutput,
+ false, loader);
+ }
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @param test the test to run.
+ * @param haltOnError whether to stop the run if an error is found.
+ * @param filtertrace whether to filter junit.*.* stack frames out of exceptions
+ * @param haltOnFailure whether to stop the run if failure is found.
+ * @param showOutput whether to send output to System.out/.err as well as formatters.
+ * @param logTestListenerEvents whether to print TestListener events.
+ * @param loader the classloader to use running the test.
+ * @since Ant 1.7
+ */
+ public JUnitTestRunner(final JUnitTest test, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents,
+ final ClassLoader loader) {
+ this(test, null, haltOnError, filtertrace, haltOnFailure, showOutput,
+ logTestListenerEvents, loader);
+ }
+
+
+ /**
+ * Constructor to use when the user has specified a classpath.
+ * @since 1.8.2
+ */
+ public JUnitTestRunner(final JUnitTest test, final String[] methods, final boolean haltOnError,
+ final boolean filtertrace, final boolean haltOnFailure,
+ final boolean showOutput, final boolean logTestListenerEvents,
+ final ClassLoader loader) {
+ super();
+ JUnitTestRunner.filtertrace = filtertrace; // TODO clumsy, should use instance field somehow
+ this.junitTest = test;
+ this.haltOnError = haltOnError;
+ this.haltOnFailure = haltOnFailure;
+ this.showOutput = showOutput;
+ this.logTestListenerEvents = logTestListenerEvents;
+ this.methods = methods != null ? (String[]) methods.clone() : null;
+ this.loader = loader;
+ }
+
+ private PrintStream savedOut = null;
+ private PrintStream savedErr = null;
+
+ private PrintStream createEmptyStream() {
+ return new PrintStream(
+ new OutputStream() {
+ @Override
+ public void write(final int b) {
+ }
+ });
+ }
+
+ private PrintStream createTeePrint(final PrintStream ps1, final PrintStream ps2) {
+ return new PrintStream(new TeeOutputStream(ps1, ps2));
+ }
+
+ private void setupIOStreams(final ByteArrayOutputStream o,
+ final ByteArrayOutputStream e) {
+ systemOut = new PrintStream(o);
+ systemError = new PrintStream(e);
+
+ if (forked) {
+ if (!outputToFormatters) {
+ if (!showOutput) {
+ savedOut = System.out;
+ savedErr = System.err;
+ System.setOut(createEmptyStream());
+ System.setErr(createEmptyStream());
+ }
+ } else {
+ savedOut = System.out;
+ savedErr = System.err;
+ if (!showOutput) {
+ System.setOut(systemOut);
+ System.setErr(systemError);
+ } else {
+ System.setOut(createTeePrint(savedOut, systemOut));
+ System.setErr(createTeePrint(savedErr, systemError));
+ }
+ perm = null;
+ }
+ } else {
+ if (perm != null) {
+ perm.setSecurityManager();
+ }
+ }
+ }
+
+ /**
+ * Run the test.
+ */
+ public void run() {
+ res = new IgnoredTestResult();
+ res.addListener(wrapListener(this));
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ res.addListener(wrapListener((TestListener) formatters.elementAt(i)));
+ }
+
+ final ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
+ final ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
+
+ setupIOStreams(outStrm, errStrm);
+
+ Test suite = null;
+ Throwable exception = null;
+ boolean startTestSuiteSuccess = false;
+
+ try {
+
+ try {
+ Class testClass = null;
+ if (loader == null) {
+ testClass = Class.forName(junitTest.getName());
+ } else {
+ testClass = Class.forName(junitTest.getName(), true,
+ loader);
+ }
+
+ final boolean testMethodsSpecified = (methods != null);
+
+ // check for a static suite method first, even when using
+ // JUnit 4
+ Method suiteMethod = null;
+ if (!testMethodsSpecified) {
+ try {
+ // check if there is a suite method
+ suiteMethod = testClass.getMethod("suite", new Class[0]);
+ } catch (final NoSuchMethodException e) {
+ // no appropriate suite method found. We don't report any
+ // error here since it might be perfectly normal.
+ }
+ }
+
+ if (suiteMethod != null) {
+ // if there is a suite method available, then try
+ // to extract the suite from it. If there is an error
+ // here it will be caught below and reported.
+ suite = (Test) suiteMethod.invoke(null, new Object[0]);
+
+ } else {
+ Class junit4TestAdapterClass = null;
+ Class junit4TestAdapterCacheClass = null;
+ boolean useSingleMethodAdapter = false;
+
+ if (junit.framework.TestCase.class.isAssignableFrom(testClass)) {
+ // Do not use JUnit 4 API for running JUnit 3.x
+ // tests - it is not able to run individual test
+ // methods.
+ //
+ // Technical details:
+ // org.junit.runner.Request.method(Class, String).getRunner()
+ // would return a runner which always executes all
+ // test methods. The reason is that the Runner would be
+ // an instance of class
+ // org.junit.internal.runners.OldTestClassRunner
+ // that does not implement interface Filterable - so it
+ // is unable to filter out test methods not matching
+ // the requested name.
+ } else {
+ // Check for JDK 5 first. Will *not* help on JDK 1.4
+ // if only junit-4.0.jar in CP because in that case
+ // linkage of whole task will already have failed! But
+ // will help if CP has junit-3.8.2.jar:junit-4.0.jar.
+
+ // In that case first C.fN will fail with CNFE and we
+ // will avoid UnsupportedClassVersionError.
+
+ try {
+ Class.forName("java.lang.annotation.Annotation");
+ junit4TestAdapterCacheClass = Class.forName("org.apache.tools.ant.taskdefs.optional.junit.CustomJUnit4TestAdapterCache");
+ if (loader == null) {
+ junit4TestAdapterClass =
+ Class.forName(JUNIT_4_TEST_ADAPTER);
+ if (testMethodsSpecified) {
+ /*
+ * We cannot try to load the JUnit4TestAdapter
+ * before trying to load JUnit4TestMethodAdapter
+ * because it might fail with
+ * NoClassDefFoundException, instead of plain
+ * ClassNotFoundException.
+ */
+ junit4TestAdapterClass = Class.forName(
+ "org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter");
+ useSingleMethodAdapter = true;
+ }
+ } else {
+ junit4TestAdapterClass =
+ Class.forName(JUNIT_4_TEST_ADAPTER,
+ true, loader);
+ if (testMethodsSpecified) {
+ junit4TestAdapterClass =
+ Class.forName(
+ "org.apache.tools.ant.taskdefs.optional.junit.JUnit4TestMethodAdapter",
+ true, loader);
+ useSingleMethodAdapter = true;
+ }
+ }
+ } catch (final ClassNotFoundException e) {
+ // OK, fall back to JUnit 3.
+ }
+ }
+ junit4 = junit4TestAdapterClass != null;
+
+ if (junitTest.isSkipNonTests()) {
+ if (!containsTests(testClass, junit4)) {
+ return;
+ }
+ }
+
+
+ if (junit4) {
+ // Let's use it!
+ Class[] formalParams;
+ Object[] actualParams;
+ if (useSingleMethodAdapter) {
+ formalParams = new Class[] {Class.class, String[].class};
+ actualParams = new Object[] {testClass, methods};
+ } else {
+ formalParams = new Class[] {Class.class, Class.forName("junit.framework.JUnit4TestAdapterCache")};
+ actualParams = new Object[] {testClass, junit4TestAdapterCacheClass.getMethod("getInstance").invoke(null)};
+ }
+ suite =
+ (Test) junit4TestAdapterClass
+ .getConstructor(formalParams).
+ newInstance(actualParams);
+ } else {
+ // Use JUnit 3.
+
+ // try to extract a test suite automatically this
+ // will generate warnings if the class is no
+ // suitable Test
+ if (!testMethodsSpecified) {
+ suite = new TestSuite(testClass);
+ } else if (methods.length == 1) {
+ suite = TestSuite.createTest(testClass, methods[0]);
+ } else {
+ final TestSuite testSuite = new TestSuite(testClass.getName());
+ for (int i = 0; i < methods.length; i++) {
+ testSuite.addTest(
+ TestSuite.createTest(testClass, methods[i]));
+ }
+ suite = testSuite;
+ }
+ }
+
+ }
+
+ } catch (final Throwable e) {
+ retCode = ERRORS;
+ exception = e;
+ }
+
+ final long start = System.currentTimeMillis();
+
+ fireStartTestSuite();
+ startTestSuiteSuccess = true;
+ if (exception != null) { // had an exception constructing suite
+ final int formatterSize = formatters.size();
+ for (int i = 0; i < formatterSize; i++) {
+ ((TestListener) formatters.elementAt(i))
+ .addError(null, exception);
+ }
+ junitTest.setCounts(1, 0, 1, 0);
+ junitTest.setRunTime(0);
+ } else {
+ try {
+ logTestListenerEvent("tests to run: " + suite.countTestCases());
+ suite.run(res);
+ } finally {
+ if (junit4 ||
+ suite.getClass().getName().equals(JUNIT_4_TEST_ADAPTER)) {
+ final int[] cnts = findJUnit4FailureErrorCount(res);
+ junitTest.setCounts(res.runCount() + res.ignoredCount(), cnts[0], cnts[1], res.ignoredCount() + res.skippedCount());
+ } else {
+ junitTest.setCounts(res.runCount() + res.ignoredCount(), res.failureCount(),
+ res.errorCount(), res.ignoredCount() + res.skippedCount());
+ }
+ junitTest.setRunTime(System.currentTimeMillis() - start);
+ }
+ }
+ } finally {
+ if (perm != null) {
+ perm.restoreSecurityManager();
+ }
+ if (savedOut != null) {
+ System.setOut(savedOut);
+ }
+ if (savedErr != null) {
+ System.setErr(savedErr);
+ }
+
+ systemError.close();
+ systemError = null;
+ systemOut.close();
+ systemOut = null;
+ if (startTestSuiteSuccess) {
+ String out, err;
+ try {
+ out = new String(outStrm.toByteArray());
+ } catch (final OutOfMemoryError ex) {
+ out = "out of memory on output stream";
+ }
+ try {
+ err = new String(errStrm.toByteArray());
+ } catch (final OutOfMemoryError ex) {
+ err = "out of memory on error stream";
+ }
+ sendOutAndErr(out, err);
+ }
+ }
+ fireEndTestSuite();
+
+ // junitTest has the correct counts for JUnit4, while res doesn't
+ if (retCode != SUCCESS || junitTest.errorCount() != 0) {
+ retCode = ERRORS;
+ } else if (junitTest.failureCount() != 0) {
+ retCode = FAILURES;
+ }
+ }
+
+ private static boolean containsTests(final Class<?> testClass, final boolean isJUnit4) {
+ Class testAnnotation = null;
+ Class suiteAnnotation = null;
+ Class runWithAnnotation = null;
+
+ try {
+ testAnnotation = Class.forName("org.junit.Test");
+ } catch (final ClassNotFoundException e) {
+ if (isJUnit4) {
+ // odd - we think we're JUnit4 but don't support the test annotation. We therefore can't have any tests!
+ return false;
+ }
+ // else... we're a JUnit3 test and don't need the annotation
+ }
+
+ try {
+ suiteAnnotation = Class.forName("org.junit.Suite.SuiteClasses");
+ } catch(final ClassNotFoundException ex) {
+ // ignore - we don't have this annotation so make sure we don't check for it
+ }
+ try {
+ runWithAnnotation = Class.forName("org.junit.runner.RunWith");
+ } catch(final ClassNotFoundException ex) {
+ // also ignore as this annotation doesn't exist so tests can't use it
+ }
+
+
+ if (!isJUnit4 && !TestCase.class.isAssignableFrom(testClass)) {
+ //a test we think is JUnit3 but does not extend TestCase. Can't really be a test.
+ return false;
+ }
+
+ // check if we have any inner classes that contain suitable test methods
+ for (final Class<?> innerClass : testClass.getDeclaredClasses()) {
+ if (containsTests(innerClass, isJUnit4) || containsTests(innerClass, !isJUnit4)) {
+ return true;
+ }
+ }
+
+ if (Modifier.isAbstract(testClass.getModifiers()) || Modifier.isInterface(testClass.getModifiers())) {
+ // can't instantiate class and no inner classes are tests either
+ return false;
+ }
+
+ if (isJUnit4) {
+ if (suiteAnnotation != null && testClass.getAnnotation(suiteAnnotation) != null) {
+ // class is marked as a suite. Let JUnit try and work its magic on it.
+ return true;
+ }
+ if (runWithAnnotation != null && testClass.getAnnotation(runWithAnnotation) != null) {
+ /* Class is marked with @RunWith. If this class is badly written (no test methods, multiple
+ * constructors, private constructor etc) then the class is automatically run and fails in the
+ * IDEs I've tried... so I'm happy handing the class to JUnit to try and run, and let JUnit
+ * report a failure if a bad test case is provided. Trying to do anything else is likely to
+ * result in us filtering out cases that could be valid for future versions of JUnit so would
+ * just increase future maintenance work.
+ */
+ return true;
+ }
+ }
+
+ for (final Method m : testClass.getMethods()) {
+ if (isJUnit4) {
+ // check if suspected JUnit4 classes have methods with @Test annotation
+ if (m.getAnnotation(testAnnotation) != null) {
+ return true;
+ }
+ } else {
+ // check if JUnit3 class have public or protected no-args methods starting with names starting with test
+ if (m.getName().startsWith("test") && m.getParameterTypes().length == 0
+ && (Modifier.isProtected(m.getModifiers()) || Modifier.isPublic(m.getModifiers()))) {
+ return true;
+ }
+ }
+ // check if JUnit3 or JUnit4 test have a public or protected, static,
+ // no-args 'suite' method
+ if (m.getName().equals("suite") && m.getParameterTypes().length == 0
+ && (Modifier.isProtected(m.getModifiers()) || Modifier.isPublic(m.getModifiers()))
+ && Modifier.isStatic(m.getModifiers())) {
+ return true;
+ }
+ }
+
+ // no test methods found
+ return false;
+ }
+
+ /**
+ * Returns what System.exit() would return in the standalone version.
+ *
+ * @return 2 if errors occurred, 1 if tests failed else 0.
+ */
+ public int getRetCode() {
+ return retCode;
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A new Test is started.
+ * @param t the test.
+ */
+ public void startTest(final Test t) {
+ final String testName = JUnitVersionHelper.getTestCaseName(t);
+ logTestListenerEvent("startTest(" + testName + ")");
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A Test is finished.
+ * @param test the test.
+ */
+ public void endTest(final Test test) {
+ final String testName = JUnitVersionHelper.getTestCaseName(test);
+ logTestListenerEvent("endTest(" + testName + ")");
+ }
+
+ private void logTestListenerEvent(String msg) {
+ if (logTestListenerEvents) {
+ final PrintStream out = savedOut != null ? savedOut : System.out;
+ out.flush();
+ if (msg == null) {
+ msg = "null";
+ }
+ final StringTokenizer msgLines = new StringTokenizer(msg, "\r\n", false);
+ while (msgLines.hasMoreTokens()) {
+ out.println(JUnitTask.TESTLISTENER_PREFIX
+ + msgLines.nextToken());
+ }
+ out.flush();
+ }
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the exception thrown by the test.
+ */
+ public void addFailure(final Test test, final Throwable t) {
+ final String testName = JUnitVersionHelper.getTestCaseName(test);
+ logTestListenerEvent("addFailure(" + testName + ", " + t.getMessage() + ")");
+ if (haltOnFailure) {
+ res.stop();
+ }
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the assertion thrown by the test.
+ */
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>An error occurred while running the test.
+ * @param test the test.
+ * @param t the error thrown by the test.
+ */
+ public void addError(final Test test, final Throwable t) {
+ final String testName = JUnitVersionHelper.getTestCaseName(test);
+ logTestListenerEvent("addError(" + testName + ", " + t.getMessage() + ")");
+ if (haltOnError) {
+ res.stop();
+ }
+ }
+
+ /**
+ * Permissions for the test run.
+ * @since Ant 1.6
+ * @param permissions the permissions to use.
+ */
+ public void setPermissions(final Permissions permissions) {
+ perm = permissions;
+ }
+
+ /**
+ * Handle a string destined for standard output.
+ * @param output the string to output
+ */
+ public void handleOutput(final String output) {
+ if (!logTestListenerEvents && output.startsWith(JUnitTask.TESTLISTENER_PREFIX)) {
+ // ignore
+ } else if (systemOut != null) {
+ systemOut.print(output);
+ }
+ }
+
+ /**
+ * Handle input.
+ * @param buffer not used.
+ * @param offset not used.
+ * @param length not used.
+ * @return -1 always.
+ * @throws IOException never.
+ * @see org.apache.tools.ant.Task#handleInput(byte[], int, int)
+ *
+ * @since Ant 1.6
+ */
+ public int handleInput(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ return -1;
+ }
+
+ /** {@inheritDoc}. */
+ public void handleErrorOutput(final String output) {
+ if (systemError != null) {
+ systemError.print(output);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public void handleFlush(final String output) {
+ if (systemOut != null) {
+ systemOut.print(output);
+ }
+ }
+
+ /** {@inheritDoc}. */
+ public void handleErrorFlush(final String output) {
+ if (systemError != null) {
+ systemError.print(output);
+ }
+ }
+
+ private void sendOutAndErr(final String out, final String err) {
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ final JUnitResultFormatter formatter =
+ ((JUnitResultFormatter) formatters.elementAt(i));
+
+ formatter.setSystemOutput(out);
+ formatter.setSystemError(err);
+ }
+ }
+
+ private void fireStartTestSuite() {
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ ((JUnitResultFormatter) formatters.elementAt(i))
+ .startTestSuite(junitTest);
+ }
+ }
+
+ private void fireEndTestSuite() {
+ final int size = formatters.size();
+ for (int i = 0; i < size; i++) {
+ ((JUnitResultFormatter) formatters.elementAt(i))
+ .endTestSuite(junitTest);
+ }
+ }
+
+ /**
+ * Add a formatter.
+ * @param f the formatter to add.
+ */
+ public void addFormatter(final JUnitResultFormatter f) {
+ formatters.addElement(f);
+ }
+
+ /** {@inheritDoc}. */
+ public void addFormatter(final JUnitTaskMirror.JUnitResultFormatterMirror f) {
+ formatters.addElement(f);
+ }
+
+ /**
+ * Entry point for standalone (forked) mode.
+ *
+ * Parameters: testcaseclassname plus parameters in the format
+ * key=value, none of which is required.
+ *
+ * <table cols="4" border="1">
+ * <tr><th>key</th><th>description</th><th>default value</th></tr>
+ *
+ * <tr><td>haltOnError</td><td>halt test on
+ * errors?</td><td>false</td></tr>
+ *
+ * <tr><td>haltOnFailure</td><td>halt test on
+ * failures?</td><td>false</td></tr>
+ *
+ * <tr><td>formatter</td><td>A JUnitResultFormatter given as
+ * classname,filename. If filename is omitted, System.out is
+ * assumed.</td><td>none</td></tr>
+ *
+ * <tr><td>showoutput</td><td>send output to System.err/.out as
+ * well as to the formatters?</td><td>false</td></tr>
+ *
+ * <tr><td>logtestlistenerevents</td><td>log TestListener events to
+ * System.out.</td><td>false</td></tr>
+ *
+ * <tr><td>methods</td><td>Comma-separated list of names of individual
+ * test methods to execute.
+ * </td><td>null</td></tr>
+ *
+ * </table>
+ * @param args the command line arguments.
+ * @throws IOException on error.
+ */
+ public static void main(final String[] args) throws IOException {
+ String[] methods = null;
+ boolean haltError = false;
+ boolean haltFail = false;
+ boolean stackfilter = true;
+ final Properties props = new Properties();
+ boolean showOut = false;
+ boolean outputToFormat = true;
+ boolean logFailedTests = true;
+ boolean logTestListenerEvents = false;
+ boolean skipNonTests = false;
+ int antThreadID = 0; /* Ant id of thread running this unit test, 0 in single-threaded mode */
+
+ if (args.length == 0) {
+ System.err.println("required argument TestClassName missing");
+ System.exit(ERRORS);
+ }
+
+ if (args[0].startsWith(Constants.TESTSFILE)) {
+ multipleTests = true;
+ args[0] = args[0].substring(Constants.TESTSFILE.length());
+ }
+
+ for (int i = 1; i < args.length; i++) {
+ if (args[i].startsWith(Constants.METHOD_NAMES)) {
+ try {
+ final String methodsList = args[i].substring(Constants.METHOD_NAMES.length());
+ methods = JUnitTest.parseTestMethodNamesList(methodsList);
+ } catch (final IllegalArgumentException ex) {
+ System.err.println("Invalid specification of test method names: " + args[i]);
+ System.exit(ERRORS);
+ }
+ } else if (args[i].startsWith(Constants.HALT_ON_ERROR)) {
+ haltError = Project.toBoolean(args[i].substring(Constants.HALT_ON_ERROR.length()));
+ } else if (args[i].startsWith(Constants.HALT_ON_FAILURE)) {
+ haltFail = Project.toBoolean(args[i].substring(Constants.HALT_ON_FAILURE.length()));
+ } else if (args[i].startsWith(Constants.FILTERTRACE)) {
+ stackfilter = Project.toBoolean(args[i].substring(Constants.FILTERTRACE.length()));
+ } else if (args[i].startsWith(Constants.CRASHFILE)) {
+ crashFile = args[i].substring(Constants.CRASHFILE.length());
+ registerTestCase(Constants.BEFORE_FIRST_TEST);
+ } else if (args[i].startsWith(Constants.FORMATTER)) {
+ try {
+ createAndStoreFormatter(args[i].substring(Constants.FORMATTER.length()));
+ } catch (final BuildException be) {
+ System.err.println(be.getMessage());
+ System.exit(ERRORS);
+ }
+ } else if (args[i].startsWith(Constants.PROPSFILE)) {
+ final FileInputStream in = new FileInputStream(args[i]
+ .substring(Constants.PROPSFILE.length()));
+ props.load(in);
+ in.close();
+ } else if (args[i].startsWith(Constants.SHOWOUTPUT)) {
+ showOut = Project.toBoolean(args[i].substring(Constants.SHOWOUTPUT.length()));
+ } else if (args[i].startsWith(Constants.LOGTESTLISTENEREVENTS)) {
+ logTestListenerEvents = Project.toBoolean(
+ args[i].substring(Constants.LOGTESTLISTENEREVENTS.length()));
+ } else if (args[i].startsWith(Constants.OUTPUT_TO_FORMATTERS)) {
+ outputToFormat = Project.toBoolean(
+ args[i].substring(Constants.OUTPUT_TO_FORMATTERS.length()));
+ } else if (args[i].startsWith(Constants.LOG_FAILED_TESTS)) {
+ logFailedTests = Project.toBoolean(
+ args[i].substring(Constants.LOG_FAILED_TESTS.length()));
+ } else if (args[i].startsWith(Constants.SKIP_NON_TESTS)) {
+ skipNonTests = Project.toBoolean(
+ args[i].substring(Constants.SKIP_NON_TESTS.length()));
+ } else if (args[i].startsWith(Constants.THREADID)) {
+ antThreadID = Integer.parseInt(args[i].substring(Constants.THREADID.length()));
+ }
+ }
+
+ // Add/overlay system properties on the properties from the Ant project
+ final Hashtable p = System.getProperties();
+ for (final Enumeration e = p.keys(); e.hasMoreElements();) {
+ final Object key = e.nextElement();
+ props.put(key, p.get(key));
+ }
+
+ int returnCode = SUCCESS;
+ if (multipleTests) {
+ try {
+ final java.io.BufferedReader reader =
+ new java.io.BufferedReader(new java.io.FileReader(args[0]));
+ String testCaseName;
+ String[] testMethodNames;
+ int code = 0;
+ boolean errorOccurred = false;
+ boolean failureOccurred = false;
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ final StringTokenizer st = new StringTokenizer(line, ",");
+ final String testListSpec = st.nextToken();
+ final int colonIndex = testListSpec.indexOf(':');
+ if (colonIndex == -1) {
+ testCaseName = testListSpec;
+ testMethodNames = null;
+ } else {
+ testCaseName = testListSpec.substring(0, colonIndex);
+ testMethodNames = JUnitTest.parseTestMethodNamesList(
+ testListSpec
+ .substring(colonIndex + 1)
+ .replace('+', ','));
+ }
+ final JUnitTest t = new JUnitTest(testCaseName);
+ t.setTodir(new File(st.nextToken()));
+ t.setOutfile(st.nextToken());
+ t.setProperties(props);
+ t.setSkipNonTests(skipNonTests);
+ t.setThread(antThreadID);
+ code = launch(t, testMethodNames, haltError, stackfilter, haltFail,
+ showOut, outputToFormat,
+ logTestListenerEvents);
+ errorOccurred = (code == ERRORS);
+ failureOccurred = (code != SUCCESS);
+ if (errorOccurred || failureOccurred) {
+ if ((errorOccurred && haltError)
+ || (failureOccurred && haltFail)) {
+ registerNonCrash();
+ System.exit(code);
+ } else {
+ if (code > returnCode) {
+ returnCode = code;
+ }
+ if (logFailedTests) {
+ System.out.println("TEST " + t.getName()
+ + " FAILED");
+ }
+ }
+ }
+ }
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ final JUnitTest t = new JUnitTest(args[0]);
+ t.setThread(antThreadID);
+ t.setProperties(props);
+ t.setSkipNonTests(skipNonTests);
+ returnCode = launch(
+ t, methods, haltError, stackfilter, haltFail,
+ showOut, outputToFormat, logTestListenerEvents);
+ }
+
+ registerNonCrash();
+ System.exit(returnCode);
+ }
+
+ private static Vector fromCmdLine = new Vector();
+
+ private static void transferFormatters(final JUnitTestRunner runner,
+ final JUnitTest test) {
+ runner.addFormatter(new JUnitResultFormatter() {
+
+ public void startTestSuite(final JUnitTest suite) throws BuildException {
+ }
+
+ public void endTestSuite(final JUnitTest suite) throws BuildException {
+ }
+
+ public void setOutput(final OutputStream out) {
+ }
+
+ public void setSystemOutput(final String out) {
+ }
+
+ public void setSystemError(final String err) {
+ }
+
+ public void addError(final Test arg0, final Throwable arg1) {
+ }
+
+ public void addFailure(final Test arg0, final AssertionFailedError arg1) {
+ }
+
+ public void endTest(final Test arg0) {
+ }
+
+ public void startTest(final Test arg0) {
+ registerTestCase(JUnitVersionHelper.getTestCaseName(arg0));
+ }
+ });
+ final int size = fromCmdLine.size();
+ for (int i = 0; i < size; i++) {
+ final FormatterElement fe = (FormatterElement) fromCmdLine.elementAt(i);
+ if (multipleTests && fe.getUseFile()) {
+ final File destFile =
+ new File(test.getTodir(),
+ test.getOutfile() + fe.getExtension());
+ fe.setOutfile(destFile);
+ }
+ runner.addFormatter((JUnitResultFormatter) fe.createFormatter());
+ }
+ }
+
+ /**
+ * Line format is: formatter=<classname>(,<pathname>)?
+ */
+ private static void createAndStoreFormatter(final String line)
+ throws BuildException {
+ final FormatterElement fe = new FormatterElement();
+ final int pos = line.indexOf(',');
+ if (pos == -1) {
+ fe.setClassname(line);
+ fe.setUseFile(false);
+ } else {
+ fe.setClassname(line.substring(0, pos));
+ fe.setUseFile(true);
+ if (!multipleTests) {
+ fe.setOutfile(new File(line.substring(pos + 1)));
+ } else {
+ final int fName = line.indexOf(IGNORED_FILE_NAME);
+ if (fName > -1) {
+ fe.setExtension(line
+ .substring(fName
+ + IGNORED_FILE_NAME.length()));
+ }
+ }
+ }
+ fromCmdLine.addElement(fe);
+ }
+
+ /**
+ * Returns a filtered stack trace.
+ * This is ripped out of junit.runner.BaseTestRunner.
+ * @param t the exception to filter.
+ * @return the filtered stack trace.
+ */
+ public static String getFilteredTrace(final Throwable t) {
+ final String trace = StringUtils.getStackTrace(t);
+ return JUnitTestRunner.filterStack(trace);
+ }
+
+ /**
+ * Filters stack frames from internal JUnit and Ant classes
+ * @param stack the stack trace to filter.
+ * @return the filtered stack.
+ */
+ public static String filterStack(final String stack) {
+ if (!filtertrace) {
+ return stack;
+ }
+ final StringWriter sw = new StringWriter();
+ final BufferedWriter pw = new BufferedWriter(sw);
+ final StringReader sr = new StringReader(stack);
+ final BufferedReader br = new BufferedReader(sr);
+
+ String line;
+ try {
+ boolean firstLine = true;
+ while ((line = br.readLine()) != null) {
+ if (firstLine || !filterLine(line)) {
+ pw.write(line);
+ pw.newLine();
+ }
+ firstLine = false;
+ }
+ } catch (final Exception e) {
+ return stack; // return the stack unfiltered
+ } finally {
+ FileUtils.close(pw);
+ }
+ return sw.toString();
+ }
+
+ private static boolean filterLine(final String line) {
+ for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) {
+ if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @since Ant 1.6.2
+ */
+ private static int launch(final JUnitTest t, final String[] methods, final boolean haltError,
+ final boolean stackfilter, final boolean haltFail,
+ final boolean showOut, final boolean outputToFormat,
+ final boolean logTestListenerEvents) {
+ final JUnitTestRunner runner =
+ new JUnitTestRunner(t, methods, haltError, stackfilter, haltFail, showOut,
+ logTestListenerEvents, null);
+ runner.forked = true;
+ runner.outputToFormatters = outputToFormat;
+ transferFormatters(runner, t);
+
+ runner.run();
+ return runner.getRetCode();
+ }
+
+ /**
+ * @since Ant 1.7
+ */
+ private static void registerNonCrash()
+ throws IOException {
+ if (crashFile != null) {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(crashFile);
+ out.write(Constants.TERMINATED_SUCCESSFULLY + "\n");
+ out.flush();
+ } finally {
+ FileUtils.close(out);
+ }
+ }
+ }
+
+ private static void registerTestCase(final String testCase) {
+ if (crashFile != null) {
+ try {
+ FileWriter out = null;
+ try {
+ out = new FileWriter(crashFile);
+ out.write(testCase + "\n");
+ out.flush();
+ } finally {
+ FileUtils.close(out);
+ }
+ } catch (final IOException e) {
+ // ignored.
+ }
+ }
+ }
+
+ /**
+ * Modifies a TestListener when running JUnit 4: treats AssertionFailedError
+ * as a failure not an error.
+ *
+ * @since Ant 1.7
+ */
+ private TestListenerWrapper wrapListener(final TestListener testListener) {
+ return new TestListenerWrapper(testListener) {
+ @Override
+ public void addError(final Test test, final Throwable t) {
+ if (junit4 && t instanceof AssertionFailedError) {
+ // JUnit 4 does not distinguish between errors and failures
+ // even in the JUnit 3 adapter.
+ // So we need to help it a bit to retain compatibility for JUnit 3 tests.
+ testListener.addFailure(test, (AssertionFailedError) t);
+ } else if (junit4 && t instanceof AssertionError) {
+ // Not strictly necessary but probably desirable.
+ // JUnit 4-specific test GUIs will show just "failures".
+ // But Ant's output shows "failures" vs. "errors".
+ // We would prefer to show "failure" for things that logically are.
+ final String msg = t.getMessage();
+ final AssertionFailedError failure = msg != null
+ ? new AssertionFailedError(msg) : new AssertionFailedError();
+ failure.setStackTrace(t.getStackTrace());
+ testListener.addFailure(test, failure);
+ } else {
+ testListener.addError(test, t);
+ }
+ }
+ @Override
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ testListener.addFailure(test, t);
+ }
+ public void addFailure(final Test test, final Throwable t) { // pre-3.4
+ if (t instanceof AssertionFailedError) {
+ testListener.addFailure(test, (AssertionFailedError) t);
+ } else {
+ testListener.addError(test, t);
+ }
+ }
+ @Override
+ public void endTest(final Test test) {
+ testListener.endTest(test);
+ }
+ @Override
+ public void startTest(final Test test) {
+ testListener.startTest(test);
+ }
+ };
+ }
+
+ /**
+ * Use instead of TestResult.get{Failure,Error}Count on JUnit 4,
+ * since the adapter claims that all failures are errors.
+ * @since Ant 1.7
+ */
+ private int[] findJUnit4FailureErrorCount(final TestResult result) {
+ int failures = 0;
+ int errors = 0;
+ Enumeration e = result.failures();
+ while (e.hasMoreElements()) {
+ e.nextElement();
+ failures++;
+ }
+ e = result.errors();
+ while (e.hasMoreElements()) {
+ final Throwable t = ((TestFailure) e.nextElement()).thrownException();
+ if (t instanceof AssertionFailedError
+ || t instanceof AssertionError) {
+ failures++;
+ } else {
+ errors++;
+ }
+ }
+ return new int[] {failures, errors};
+ }
+
+} // JUnitTestRunner
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java
new file mode 100644
index 00000000..9a21caee
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Work around for some changes to the public JUnit API between
+ * different JUnit releases.
+ * @since Ant 1.7
+ */
+// CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
+public class JUnitVersionHelper {
+
+ private static Method testCaseName = null;
+
+ /**
+ * Name of the JUnit4 class we look for.
+ * {@value}
+ * @since Ant 1.7.1
+ */
+ public static final String JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE
+ = "junit.framework.JUnit4TestCaseFacade";
+ private static final String UNKNOWN_TEST_CASE_NAME = "unknown";
+
+ static {
+ try {
+ testCaseName = TestCase.class.getMethod("getName", new Class[0]);
+ } catch (NoSuchMethodException e) {
+ // pre JUnit 3.7
+ try {
+ testCaseName = TestCase.class.getMethod("name", new Class[0]);
+ } catch (NoSuchMethodException ignored) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * JUnit 3.7 introduces TestCase.getName() and subsequent versions
+ * of JUnit remove the old name() method. This method provides
+ * access to the name of a TestCase via reflection that is
+ * supposed to work with version before and after JUnit 3.7.
+ *
+ * <p>since Ant 1.5.1 this method will invoke &quot;<code>public
+ * String getName()</code>&quot; on any implementation of Test if
+ * it exists.</p>
+ *
+ * <p>Since Ant 1.7 also checks for JUnit4TestCaseFacade explicitly.
+ * This is used by junit.framework.JUnit4TestAdapter.</p>
+ * @param t the test.
+ * @return the name of the test.
+ */
+ public static String getTestCaseName(Test t) {
+ if (t == null) {
+ return UNKNOWN_TEST_CASE_NAME;
+ }
+ if (t.getClass().getName().equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) {
+ // Self-describing as of JUnit 4 (#38811). But trim "(ClassName)".
+ String name = t.toString();
+ if (name.endsWith(")")) {
+ int paren = name.lastIndexOf('(');
+ return name.substring(0, paren);
+ } else {
+ return name;
+ }
+ }
+ if (t instanceof TestCase && testCaseName != null) {
+ try {
+ return (String) testCaseName.invoke(t, new Object[0]);
+ } catch (Throwable ignored) {
+ // ignore
+ }
+ } else {
+ try {
+ Method getNameMethod = null;
+ try {
+ getNameMethod =
+ t.getClass().getMethod("getName", new Class [0]);
+ } catch (NoSuchMethodException e) {
+ getNameMethod = t.getClass().getMethod("name",
+ new Class [0]);
+ }
+ if (getNameMethod != null
+ && getNameMethod.getReturnType() == String.class) {
+ return (String) getNameMethod.invoke(t, new Object[0]);
+ }
+ } catch (Throwable ignored) {
+ // ignore
+ }
+ }
+ return UNKNOWN_TEST_CASE_NAME;
+ }
+
+ /**
+ * Tries to find the name of the class which a test represents
+ * across JUnit 3 and 4. For JUnit4 it parses the toString() value of the
+ * test, and extracts it from there.
+ * @since Ant 1.7.1 (it was private until then)
+ * @param test test case to look at
+ * @return the extracted class name.
+ */
+ public static String getTestCaseClassName(Test test) {
+ String className = test.getClass().getName();
+ if (test instanceof JUnitTaskMirrorImpl.VmExitErrorTest) {
+ className = ((JUnitTaskMirrorImpl.VmExitErrorTest) test).getClassName();
+ } else
+ if (className.equals(JUNIT_FRAMEWORK_JUNIT4_TEST_CASE_FACADE)) {
+ // JUnit 4 wraps solo tests this way. We can extract
+ // the original test name with a little hack.
+ String name = test.toString();
+ int paren = name.lastIndexOf('(');
+ if (paren != -1 && name.endsWith(")")) {
+ className = name.substring(paren + 1, name.length() - 1);
+ }
+ }
+ return className;
+ }
+
+ public static String getIgnoreMessage(Test test) {
+ String message = null;
+
+ try {
+ Class<?> junit4FacadeClass = Class.forName("junit.framework.JUnit4TestCaseFacade");
+ if (test != null && test.getClass().isAssignableFrom(junit4FacadeClass)) {
+ //try and get the message coded as part of the ignore
+ /*
+ * org.junit.runner.Description contains a getAnnotation(Class) method... but this
+ * wasn't in older versions of JUnit4 so we have to try and do this by reflection
+ */
+ Class<?> testClass = Class.forName(JUnitVersionHelper.getTestCaseClassName(test));
+
+ Method testMethod = testClass.getMethod(JUnitVersionHelper.getTestCaseName(test));
+ Class ignoreAnnotation = Class.forName("org.junit.Ignore");
+ Annotation annotation = testMethod.getAnnotation(ignoreAnnotation);
+ if (annotation != null) {
+ Method valueMethod = annotation.getClass().getMethod("value");
+ String value = (String) valueMethod.invoke(annotation);
+ if (value != null && value.length() > 0) {
+ message = value;
+ }
+ }
+
+ }
+ } catch (NoSuchMethodException e) {
+ // silently ignore - we'll report a skip with no message
+ } catch (ClassNotFoundException e) {
+ // silently ignore - we'll report a skip with no message
+ } catch (InvocationTargetException e) {
+ // silently ignore - we'll report a skip with no message
+ } catch (IllegalAccessException e) {
+ // silently ignore - we'll report a skip with no message
+ }
+ return message;
+
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java
new file mode 100644
index 00000000..e0dc16c3
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/OutErrSummaryJUnitResultFormatter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+/**
+ * Used instead of SummaryJUnitResultFormatter in forked tests if
+ * withOutAndErr is requested.
+ */
+
+public class OutErrSummaryJUnitResultFormatter
+ extends SummaryJUnitResultFormatter {
+
+ /**
+ * Empty
+ */
+ public OutErrSummaryJUnitResultFormatter() {
+ super();
+ setWithOutAndErr(true);
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java
new file mode 100644
index 00000000..3386ee50
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+import java.util.Hashtable;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+
+/**
+ * Prints plain text output of the test to a specified Writer.
+ *
+ */
+
+public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /**
+ * Formatter for timings.
+ */
+ private NumberFormat nf = NumberFormat.getInstance();
+ /**
+ * Timing helper.
+ */
+ private Hashtable testStarts = new Hashtable();
+ /**
+ * 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 BufferedWriter wri;
+ /**
+ * Suppress endTest if testcase failed.
+ */
+ private Hashtable failed = new Hashtable();
+
+ private String systemOutput = null;
+ private String systemError = null;
+
+ /** No arg constructor */
+ public PlainJUnitResultFormatter() {
+ inner = new StringWriter();
+ wri = new BufferedWriter(inner);
+ }
+
+ /** {@inheritDoc}. */
+ public void setOutput(OutputStream out) {
+ this.out = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemOutput(String out) {
+ systemOutput = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemError(String err) {
+ systemError = err;
+ }
+
+ /**
+ * The whole testsuite started.
+ * @param suite the test suite
+ * @throws BuildException if unable to write the output
+ */
+ public void startTestSuite(JUnitTest suite) throws BuildException {
+ if (out == null) {
+ return; // Quick return - no output do nothing.
+ }
+ StringBuffer sb = new StringBuffer("Testsuite: ");
+ sb.append(suite.getName());
+ sb.append(StringUtils.LINE_SEP);
+ try {
+ out.write(sb.toString().getBytes());
+ out.flush();
+ } catch (IOException ex) {
+ throw new BuildException("Unable to write output", ex);
+ }
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the test suite
+ * @throws BuildException if unable to write the output
+ */
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ try {
+ 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(", Skipped: ");
+ sb.append(suite.skipCount());
+ sb.append(", Time elapsed: ");
+ sb.append(nf.format(suite.getRunTime() / ONE_SECOND));
+ sb.append(" sec");
+ sb.append(StringUtils.LINE_SEP);
+ write(sb.toString());
+
+ // write the err and output streams to the log
+ if (systemOutput != null && systemOutput.length() > 0) {
+ write("------------- Standard Output ---------------");
+ write(StringUtils.LINE_SEP);
+ write(systemOutput);
+ write("------------- ---------------- ---------------");
+ write(StringUtils.LINE_SEP);
+ }
+
+ if (systemError != null && systemError.length() > 0) {
+ write("------------- Standard Error -----------------");
+ write(StringUtils.LINE_SEP);
+ write(systemError);
+ write("------------- ---------------- ---------------");
+ write(StringUtils.LINE_SEP);
+ }
+
+ write(StringUtils.LINE_SEP);
+ if (out != null) {
+ try {
+ wri.flush();
+ write(inner.toString());
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to write output", ioex);
+ }
+ }
+ } finally {
+ if (out != null) {
+ try {
+ wri.close();
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to flush output", ioex);
+ } finally {
+ if (out != System.out && out != System.err) {
+ FileUtils.close(out);
+ }
+ wri = null;
+ out = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A new Test is started.
+ * @param t the test.
+ */
+ public void startTest(Test t) {
+ testStarts.put(t, new Long(System.currentTimeMillis()));
+ failed.put(t, Boolean.FALSE);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A Test is finished.
+ * @param test the test.
+ */
+ public void endTest(Test test) {
+ if (Boolean.TRUE.equals(failed.get(test))) {
+ return;
+ }
+ synchronized (wri) {
+ try {
+ wri.write("Testcase: "
+ + JUnitVersionHelper.getTestCaseName(test));
+ Long l = (Long) testStarts.get(test);
+ double seconds = 0;
+ // can be null if an error occurred in setUp
+ if (l != null) {
+ seconds =
+ (System.currentTimeMillis() - l.longValue()) / ONE_SECOND;
+ }
+
+ wri.write(" took " + nf.format(seconds) + " sec");
+ wri.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the exception.
+ */
+ public void addFailure(Test test, Throwable t) {
+ formatError("\tFAILED", test, t);
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the assertion that failed.
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>An error occurred while running the test.
+ * @param test the test.
+ * @param t the exception.
+ */
+ public void addError(Test test, Throwable t) {
+ formatError("\tCaused an ERROR", test, t);
+ }
+
+ private void formatError(String type, Test test, Throwable t) {
+ synchronized (wri) {
+ if (test != null) {
+ endTest(test);
+ failed.put(test, Boolean.TRUE);
+ }
+
+ try {
+ wri.write(type);
+ wri.newLine();
+ wri.write(String.valueOf(t.getMessage()));
+ wri.newLine();
+ String strace = JUnitTestRunner.getFilteredTrace(t);
+ wri.write(strace);
+ wri.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ }
+
+ public void testIgnored(Test test) {
+ formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
+ }
+
+
+ public void formatSkip(Test test, String message) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ try {
+ wri.write("\tSKIPPED");
+ if (message != null) {
+ wri.write(": ");
+ wri.write(message);
+ }
+ wri.newLine();
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+
+ }
+
+ public void testAssumptionFailure(Test test, Throwable throwable) {
+ formatSkip(test, throwable.getMessage());
+ }
+
+ /**
+ * Print out some text, and flush the output stream; encoding in the platform
+ * local default encoding.
+ * @param text text to write.
+ * @throws BuildException on IO Problems.
+ */
+ private void write(String text) {
+ if (out == null) {
+ return;
+ }
+ try {
+ out.write(text.getBytes());
+ out.flush();
+ } catch (IOException ex) {
+ throw new BuildException("Unable to write output " + ex, ex);
+ }
+ }
+} // PlainJUnitResultFormatter
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java
new file mode 100644
index 00000000..0b09fa20
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Prints short summary output of the test to Ant's logging system.
+ *
+ */
+
+public class SummaryJUnitResultFormatter
+ implements JUnitResultFormatter, JUnitTaskMirror.SummaryJUnitResultFormatterMirror {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /**
+ * Formatter for timings.
+ */
+ private NumberFormat nf = NumberFormat.getInstance();
+ /**
+ * OutputStream to write to.
+ */
+ private OutputStream out;
+
+ private boolean withOutAndErr = false;
+ private String systemOutput = null;
+ private String systemError = null;
+
+ /**
+ * Empty
+ */
+ public SummaryJUnitResultFormatter() {
+ }
+
+ /**
+ * Insures that a line of log output is written and flushed as a single
+ * operation, to prevent lines from being spliced into other lines.
+ * (Hopefully this solves the issue of run on lines -
+ * [junit] Tests Run: 2 Failures: 2 [junit] Tests run: 5...
+ * synchronized doesn't seem to be to harsh a penalty since it only
+ * occurs twice per test - at the beginning and end. Note that message
+ * construction occurs outside the locked block.
+ *
+ * @param b data to be written as an unbroken block
+ */
+ private synchronized void writeOutputLine(byte[] b) {
+ try {
+ out.write(b);
+ out.flush();
+ } catch (IOException ioex) {
+ throw new BuildException("Unable to write summary output", ioex);
+ }
+ }
+
+ /**
+ * The testsuite started.
+ * @param suite the testsuite.
+ */
+ public void startTestSuite(JUnitTest suite) {
+ String newLine = System.getProperty("line.separator");
+ StringBuffer sb = new StringBuffer("Running ");
+ int antThreadID = suite.getThread();
+
+ sb.append(suite.getName());
+ /* only write thread id in multi-thread mode so default old way doesn't change output */
+ if (antThreadID > 0) {
+ sb.append(" in thread ");
+ sb.append(antThreadID);
+ }
+ sb.append(newLine);
+ writeOutputLine(sb.toString().getBytes());
+ }
+ /**
+ * Empty
+ * @param t not used.
+ */
+ public void startTest(Test t) {
+ }
+ /**
+ * Empty
+ * @param test not used.
+ */
+ public void endTest(Test test) {
+ }
+ /**
+ * Empty
+ * @param test not used.
+ * @param t not used.
+ */
+ public void addFailure(Test test, Throwable t) {
+ }
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test not used.
+ * @param t not used.
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+ /**
+ * Empty
+ * @param test not used.
+ * @param t not used.
+ */
+ public void addError(Test test, Throwable t) {
+ }
+
+ /** {@inheritDoc}. */
+ public void setOutput(OutputStream out) {
+ this.out = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemOutput(String out) {
+ systemOutput = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemError(String err) {
+ systemError = err;
+ }
+
+ /**
+ * Should the output to System.out and System.err be written to
+ * the summary.
+ * @param value if true write System.out and System.err to the summary.
+ */
+ public void setWithOutAndErr(boolean value) {
+ withOutAndErr = value;
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the testsuite.
+ * @throws BuildException if there is an error.
+ */
+ public void endTestSuite(JUnitTest suite) throws BuildException {
+ String newLine = System.getProperty("line.separator");
+ 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(", Skipped: ");
+ sb.append(suite.skipCount());
+ sb.append(", Time elapsed: ");
+ sb.append(nf.format(suite.getRunTime() / ONE_SECOND));
+ sb.append(" sec");
+
+ /* class name needed with multi-threaded execution because
+ results line may not appear immediately below start line.
+ only write thread id, class name in multi-thread mode so
+ the line still looks as much like the old line as possible. */
+ if (suite.getThread() > 0) {
+ sb.append(", Thread: ");
+ sb.append(suite.getThread());
+ sb.append(", Class: ");
+ sb.append(suite.getName());
+ }
+ sb.append(newLine);
+
+ if (withOutAndErr) {
+ if (systemOutput != null && systemOutput.length() > 0) {
+ sb.append("Output:").append(newLine).append(systemOutput)
+ .append(newLine);
+ }
+
+ if (systemError != null && systemError.length() > 0) {
+ sb.append("Error: ").append(newLine).append(systemError)
+ .append(newLine);
+ }
+ }
+
+ try {
+ writeOutputLine(sb.toString().getBytes());
+ } finally {
+ if (out != System.out && out != System.err) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java
new file mode 100644
index 00000000..e381a70c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TearDownOnVmCrash.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+/**
+ * Formatter that doesn't create any output but tries to invoke the
+ * tearDown method on a testcase if that test was forked and caused a
+ * timeout or VM crash.
+ *
+ * <p>This formatter has some limitations, for details see the
+ * &lt;junit&gt; task's manual.</p>
+ *
+ * @since Ant 1.8.0
+ */
+public class TearDownOnVmCrash implements JUnitResultFormatter {
+
+ private String suiteName;
+
+ /**
+ * Records the suite's name to later determine the class to invoke
+ * tearDown on.
+ */
+ public void startTestSuite(final JUnitTest suite) {
+ suiteName = suite.getName();
+ if (suiteName != null &&
+ suiteName.endsWith(JUnitTask.NAME_OF_DUMMY_TEST)) {
+ // no way to know which class caused the timeout
+ suiteName = null;
+ }
+ }
+
+ /**
+ * Only invoke tearDown if the suite is known and not the dummy
+ * test we get when a Batch fails and the error is an actual
+ * error generated by Ant.
+ */
+ public void addError(final Test fakeTest, final Throwable t) {
+ if (suiteName != null
+ && fakeTest instanceof JUnitTaskMirrorImpl.VmExitErrorTest) {
+ tearDown();
+ }
+ }
+
+ // no need to implement the rest
+ public void addFailure(Test test, Throwable t) {}
+
+ public void addFailure(Test test, AssertionFailedError t) {}
+
+ public void startTest(Test test) {}
+
+ public void endTest(Test test) {}
+
+ public void endTestSuite(JUnitTest suite) {}
+
+ public void setOutput(OutputStream out) {}
+
+ public void setSystemOutput(String out) {}
+
+ public void setSystemError(String err) {}
+
+ private void tearDown() {
+ try {
+ // first try to load the class and let's hope it is on our
+ // classpath
+ Class testClass = null;
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ try {
+ testClass = Thread.currentThread().getContextClassLoader()
+ .loadClass(suiteName);
+ } catch (ClassNotFoundException cnfe) {
+ // ignore
+ }
+ }
+ if (testClass == null && getClass().getClassLoader() != null) {
+ try {
+ testClass =
+ getClass().getClassLoader().loadClass(suiteName);
+ } catch (ClassNotFoundException cnfe) {
+ // ignore
+ }
+ }
+ if (testClass == null) {
+ // fall back to system classloader
+ testClass = Class.forName(suiteName);
+ }
+
+ // if the test has a suite method, then we can't know
+ // which test of the executed suite timed out, ignore it
+ try {
+ // check if there is a suite method
+ testClass.getMethod("suite", new Class[0]);
+ return;
+ } catch (NoSuchMethodException e) {
+ // no suite method
+ }
+
+ // a loadable class and no suite method
+ // no reason to check for JUnit 4 since JUnit4TestAdapter
+ // doesn't have any tearDown method.
+
+ try {
+ Method td = testClass.getMethod("tearDown", new Class[0]);
+ if (td.getReturnType() == Void.TYPE) {
+ td.invoke(testClass.newInstance(), new Object[0]);
+ }
+ } catch (NoSuchMethodException nsme) {
+ // no tearDown, fine
+ }
+
+ } catch (ClassNotFoundException cnfe) {
+ // class probably is not in our classpath, there is
+ // nothing we can do
+ } catch (InvocationTargetException ite) {
+ System.err.println("Caught an exception while trying to invoke"
+ + " tearDown: " + ite.getMessage());
+ } catch (Throwable t) {
+ System.err.println("Caught an exception while trying to invoke"
+ + " tearDown: " + t.getMessage());
+ }
+ }
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java
new file mode 100644
index 00000000..79a88785
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestIgnored.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.Test;
+
+public class TestIgnored {
+
+ private Test test;
+
+ public TestIgnored(Test test) {
+ this.test = test;
+ }
+
+ public Test getTest() {
+ return test;
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java
new file mode 100644
index 00000000..692e4fc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/TestListenerWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+
+public class TestListenerWrapper implements TestListener, IgnoredTestListener {
+
+ private TestListener wrapped;
+
+ public TestListenerWrapper(TestListener listener) {
+ super();
+ wrapped = listener;
+ }
+
+ public void addError(Test test, Throwable throwable) {
+ wrapped.addError(test, throwable);
+ }
+
+ public void addFailure(Test test, AssertionFailedError assertionFailedError) {
+ wrapped.addFailure(test, assertionFailedError);
+ }
+
+ public void endTest(Test test) {
+ wrapped.endTest(test);
+ }
+
+ public void startTest(Test test) {
+ wrapped.startTest(test);
+ }
+
+ public void testIgnored(Test test) {
+ if (wrapped instanceof IgnoredTestListener) {
+ ((IgnoredTestListener)wrapped).testIgnored(test);
+ }
+ }
+
+ public void testAssumptionFailure(Test test, Throwable throwable) {
+ if (wrapped instanceof IgnoredTestListener) {
+ ((IgnoredTestListener)wrapped).testAssumptionFailure(test, throwable);
+ }
+ }
+
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java
new file mode 100644
index 00000000..03760cc9
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+/**
+ * <p> Interface groups XML constants.
+ * Interface that groups all constants used throughout the <tt>XML</tt>
+ * documents that are generated by the <tt>XMLJUnitResultFormatter</tt>.
+ * <p>
+ * As of now the DTD is:
+ * <code><pre>
+ * &lt;!ELEMENT testsuites (testsuite*)&gt;
+ *
+ * &lt;!ELEMENT testsuite (properties, testcase*,
+ * failure?, error?,
+ * system-out?, system-err?)&gt;
+ * &lt;!ATTLIST testsuite name CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite tests CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite failures CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite errors CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite time CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testsuite package CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST testsuite id CDATA #IMPLIED&gt;
+ *
+ *
+ * &lt;!ELEMENT properties (property*)&gt;
+ *
+ * &lt;!ELEMENT property EMPTY&gt;
+ * &lt;!ATTLIST property name CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST property value CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT testcase (failure?, error?)&gt;
+ * &lt;!ATTLIST testcase name CDATA #REQUIRED&gt;
+ * &lt;!ATTLIST testcase classname CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST testcase time CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT failure (#PCDATA)&gt;
+ * &lt;!ATTLIST failure message CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST failure type CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT error (#PCDATA)&gt;
+ * &lt;!ATTLIST error message CDATA #IMPLIED&gt;
+ * &lt;!ATTLIST error type CDATA #REQUIRED&gt;
+ *
+ * &lt;!ELEMENT system-err (#PCDATA)&gt;
+ *
+ * &lt;!ELEMENT system-out (#PCDATA)&gt;
+ *
+ * </pre></code>
+ * @see XMLJUnitResultFormatter
+ * @see XMLResultAggregator
+ */
+// CheckStyle:InterfaceIsTypeCheck OFF (bc)
+public interface XMLConstants {
+ /** the testsuites element for the aggregate document */
+ String TESTSUITES = "testsuites";
+
+ /** the testsuite element */
+ String TESTSUITE = "testsuite";
+
+ /** the testcase element */
+ String TESTCASE = "testcase";
+
+ /** the error element */
+ String ERROR = "error";
+
+ /** the failure element */
+ String FAILURE = "failure";
+
+ /** the system-err element */
+ String SYSTEM_ERR = "system-err";
+
+ /** the system-out element */
+ String SYSTEM_OUT = "system-out";
+
+ /** package attribute for the aggregate document */
+ String ATTR_PACKAGE = "package";
+
+ /** name attribute for property, testcase and testsuite elements */
+ String ATTR_NAME = "name";
+
+ /** time attribute for testcase and testsuite elements */
+ String ATTR_TIME = "time";
+
+ /** errors attribute for testsuite elements */
+ String ATTR_ERRORS = "errors";
+
+ /** failures attribute for testsuite elements */
+ String ATTR_FAILURES = "failures";
+
+ /** tests attribute for testsuite elements */
+ String ATTR_TESTS = "tests";
+
+ String ATTR_SKIPPED = "skipped";
+
+ /** type attribute for failure and error elements */
+ String ATTR_TYPE = "type";
+
+ /** message attribute for failure elements */
+ String ATTR_MESSAGE = "message";
+
+ /** the properties element */
+ String PROPERTIES = "properties";
+
+ /** the property element */
+ String PROPERTY = "property";
+
+ /** value attribute for property elements */
+ String ATTR_VALUE = "value";
+
+ /** classname attribute for testcase elements */
+ String ATTR_CLASSNAME = "classname";
+
+ /** id attribute */
+ String ATTR_ID = "id";
+
+ /**
+ * timestamp of test cases
+ */
+ String TIMESTAMP = "timestamp";
+
+ /**
+ * name of host running the tests
+ */
+ String HOSTNAME = "hostname";
+}
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
new file mode 100644
index 00000000..416c10d4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+
+/**
+ * Prints XML output of the test to a specified Writer.
+ *
+ * @see FormatterElement
+ */
+
+public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants, IgnoredTestListener {
+
+ private static final double ONE_SECOND = 1000.0;
+
+ /** constant for unnnamed testsuites/cases */
+ private static final String UNKNOWN = "unknown";
+
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (final Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
+ /**
+ * The XML document.
+ */
+ private Document doc;
+
+ /**
+ * The wrapper for the whole testsuite.
+ */
+ private Element rootElement;
+
+ /**
+ * Element for the current test.
+ *
+ * The keying of this map is a bit of a hack: tests are keyed by caseName(className) since
+ * the Test we get for Test-start isn't the same as the Test we get during test-assumption-fail,
+ * so we can't easily match Test objects without manually iterating over all keys and checking
+ * individual fields.
+ */
+ private final Hashtable<String, Element> testElements = new Hashtable<String, Element>();
+
+ /**
+ * tests that failed.
+ */
+ private final Hashtable failedTests = new Hashtable();
+
+ /**
+ * Tests that were skipped.
+ */
+ private final Hashtable<String, Test> skippedTests = new Hashtable<String, Test>();
+ /**
+ * Tests that were ignored. See the note above about the key being a bit of a hack.
+ */
+ private final Hashtable<String, Test> ignoredTests = new Hashtable<String, Test>();
+ /**
+ * Timing helper.
+ */
+ private final Hashtable<String, Long> testStarts = new Hashtable<String, Long>();
+ /**
+ * Where to write the log to.
+ */
+ private OutputStream out;
+
+ /** No arg constructor. */
+ public XMLJUnitResultFormatter() {
+ }
+
+ /** {@inheritDoc}. */
+ public void setOutput(final OutputStream out) {
+ this.out = out;
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemOutput(final String out) {
+ formatOutput(SYSTEM_OUT, out);
+ }
+
+ /** {@inheritDoc}. */
+ public void setSystemError(final String out) {
+ formatOutput(SYSTEM_ERR, out);
+ }
+
+ /**
+ * The whole testsuite started.
+ * @param suite the testsuite.
+ */
+ public void startTestSuite(final JUnitTest suite) {
+ doc = getDocumentBuilder().newDocument();
+ rootElement = doc.createElement(TESTSUITE);
+ final String n = suite.getName();
+ rootElement.setAttribute(ATTR_NAME, n == null ? UNKNOWN : n);
+
+ //add the timestamp
+ final String timestamp = DateUtils.format(new Date(),
+ DateUtils.ISO8601_DATETIME_PATTERN);
+ rootElement.setAttribute(TIMESTAMP, timestamp);
+ //and the hostname.
+ rootElement.setAttribute(HOSTNAME, getHostname());
+
+ // Output properties
+ final Element propsElement = doc.createElement(PROPERTIES);
+ rootElement.appendChild(propsElement);
+ final Properties props = suite.getProperties();
+ if (props != null) {
+ final Enumeration e = props.propertyNames();
+ while (e.hasMoreElements()) {
+ final String name = (String) e.nextElement();
+ final Element propElement = doc.createElement(PROPERTY);
+ propElement.setAttribute(ATTR_NAME, name);
+ propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
+ propsElement.appendChild(propElement);
+ }
+ }
+ }
+
+ /**
+ * get the local hostname
+ * @return the name of the local host, or "localhost" if we cannot work it out
+ */
+ private String getHostname() {
+ String hostname = "localhost";
+ try {
+ final InetAddress localHost = InetAddress.getLocalHost();
+ if (localHost != null) {
+ hostname = localHost.getHostName();
+ }
+ } catch (final UnknownHostException e) {
+ // fall back to default 'localhost'
+ }
+ return hostname;
+ }
+
+ /**
+ * The whole testsuite ended.
+ * @param suite the testsuite.
+ * @throws BuildException on error.
+ */
+ public void endTestSuite(final JUnitTest suite) throws BuildException {
+ rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount());
+ rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount());
+ rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount());
+ rootElement.setAttribute(ATTR_SKIPPED, "" + suite.skipCount());
+ rootElement.setAttribute(
+ ATTR_TIME, "" + (suite.getRunTime() / ONE_SECOND));
+ if (out != null) {
+ Writer wri = null;
+ try {
+ wri = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
+ wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ (new DOMElementWriter()).write(rootElement, wri, 0, " ");
+ } catch (final IOException exc) {
+ throw new BuildException("Unable to write log file", exc);
+ } finally {
+ if (wri != null) {
+ try {
+ wri.flush();
+ } catch (final IOException ex) {
+ // ignore
+ }
+ }
+ if (out != System.out && out != System.err) {
+ FileUtils.close(wri);
+ }
+ }
+ }
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A new Test is started.
+ * @param t the test.
+ */
+ public void startTest(final Test t) {
+ testStarts.put(createDescription(t), System.currentTimeMillis());
+ }
+
+ private static String createDescription(final Test test) throws BuildException {
+ return JUnitVersionHelper.getTestCaseName(test) + "(" + JUnitVersionHelper.getTestCaseClassName(test) + ")";
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>A Test is finished.
+ * @param test the test.
+ */
+ public void endTest(final Test test) {
+ final String testDescription = createDescription(test);
+
+ // Fix for bug #5637 - if a junit.extensions.TestSetup is
+ // used and throws an exception during setUp then startTest
+ // would never have been called
+ if (!testStarts.containsKey(testDescription)) {
+ startTest(test);
+ }
+ Element currentTest;
+ if (!failedTests.containsKey(test) && !skippedTests.containsKey(testDescription) && !ignoredTests.containsKey(testDescription)) {
+ currentTest = doc.createElement(TESTCASE);
+ final String n = JUnitVersionHelper.getTestCaseName(test);
+ currentTest.setAttribute(ATTR_NAME,
+ n == null ? UNKNOWN : n);
+ // a TestSuite can contain Tests from multiple classes,
+ // even tests with the same name - disambiguate them.
+ currentTest.setAttribute(ATTR_CLASSNAME,
+ JUnitVersionHelper.getTestCaseClassName(test));
+ rootElement.appendChild(currentTest);
+ testElements.put(createDescription(test), currentTest);
+ } else {
+ currentTest = testElements.get(testDescription);
+ }
+
+ final Long l = testStarts.get(createDescription(test));
+ currentTest.setAttribute(ATTR_TIME,
+ "" + ((System.currentTimeMillis() - l) / ONE_SECOND));
+ }
+
+ /**
+ * Interface TestListener for JUnit &lt;= 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the exception.
+ */
+ public void addFailure(final Test test, final Throwable t) {
+ formatError(FAILURE, test, t);
+ }
+
+ /**
+ * Interface TestListener for JUnit &gt; 3.4.
+ *
+ * <p>A Test failed.
+ * @param test the test.
+ * @param t the assertion.
+ */
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ addFailure(test, (Throwable) t);
+ }
+
+ /**
+ * Interface TestListener.
+ *
+ * <p>An error occurred while running the test.
+ * @param test the test.
+ * @param t the error.
+ */
+ public void addError(final Test test, final Throwable t) {
+ formatError(ERROR, test, t);
+ }
+
+ private void formatError(final String type, final Test test, final Throwable t) {
+ if (test != null) {
+ endTest(test);
+ failedTests.put(test, test);
+ }
+
+ final Element nested = doc.createElement(type);
+ Element currentTest;
+ if (test != null) {
+ currentTest = testElements.get(createDescription(test));
+ } else {
+ currentTest = rootElement;
+ }
+
+ currentTest.appendChild(nested);
+
+ final String message = t.getMessage();
+ if (message != null && message.length() > 0) {
+ nested.setAttribute(ATTR_MESSAGE, t.getMessage());
+ }
+ nested.setAttribute(ATTR_TYPE, t.getClass().getName());
+
+ final String strace = JUnitTestRunner.getFilteredTrace(t);
+ final Text trace = doc.createTextNode(strace);
+ nested.appendChild(trace);
+ }
+
+ private void formatOutput(final String type, final String output) {
+ final Element nested = doc.createElement(type);
+ rootElement.appendChild(nested);
+ nested.appendChild(doc.createCDATASection(output));
+ }
+
+ public void testIgnored(final Test test) {
+ formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
+ if (test != null) {
+ ignoredTests.put(createDescription(test), test);
+ }
+ }
+
+
+ public void formatSkip(final Test test, final String message) {
+ if (test != null) {
+ endTest(test);
+ }
+
+ final Element nested = doc.createElement("skipped");
+
+ if (message != null) {
+ nested.setAttribute("message", message);
+ }
+
+ Element currentTest;
+ if (test != null) {
+ currentTest = testElements.get(createDescription(test));
+ } else {
+ currentTest = rootElement;
+ }
+
+ currentTest.appendChild(nested);
+
+ }
+
+ public void testAssumptionFailure(final Test test, final Throwable failure) {
+ formatSkip(test, failure.getMessage());
+ skippedTests.put(createDescription(test), test);
+
+ }
+} // XMLJUnitResultFormatter
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java
new file mode 100644
index 00000000..4f76c968
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Aggregates all &lt;junit&gt; XML formatter testsuite data under
+ * a specific directory and transforms the results via XSLT.
+ * It is not particularly clean but
+ * should be helpful while I am thinking about another technique.
+ *
+ * <p> The main problem is due to the fact that a JVM can be forked for a testcase
+ * thus making it impossible to aggregate all testcases since the listener is
+ * (obviously) in the forked JVM. A solution could be to write a
+ * TestListener that will receive events from the TestRunner via sockets. This
+ * is IMHO the simplest way to do it to avoid this file hacking thing.
+ *
+ * @ant.task name="junitreport" category="testing"
+ */
+public class XMLResultAggregator extends Task implements XMLConstants {
+
+ // CheckStyle:VisibilityModifier OFF - bc
+ /** the list of all filesets, that should contains the xml to aggregate */
+ protected Vector filesets = new Vector();
+
+ /** the name of the result file */
+ protected String toFile;
+
+ /** the directory to write the file to */
+ protected File toDir;
+
+ protected Vector transformers = new Vector();
+
+ /** The default directory: <tt>&#046;</tt>. It is resolved from the project directory */
+ public static final String DEFAULT_DIR = ".";
+
+ /** the default file name: <tt>TESTS-TestSuites.xml</tt> */
+ public static final String DEFAULT_FILENAME = "TESTS-TestSuites.xml";
+
+ /** the current generated id */
+ protected int generatedId = 0;
+
+ /**
+ * text checked for in tests, {@value}
+ */
+ static final String WARNING_IS_POSSIBLY_CORRUPTED
+ = " is not a valid XML document. It is possibly corrupted.";
+ /**
+ * text checked for in tests, {@value}
+ */
+ static final String WARNING_INVALID_ROOT_ELEMENT
+ = " is not a valid testsuite XML document";
+ /**
+ * text checked for in tests, {@value}
+ */
+ static final String WARNING_EMPTY_FILE
+ = " is empty.\nThis can be caused by the test JVM exiting unexpectedly";
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * Generate a report based on the document created by the merge.
+ * @return the report
+ */
+ public AggregateTransformer createReport() {
+ AggregateTransformer transformer = new AggregateTransformer(this);
+ transformers.addElement(transformer);
+ return transformer;
+ }
+
+ /**
+ * Set the name of the aggregegated results file. It must be relative
+ * from the <tt>todir</tt> attribute. If not set it will use {@link #DEFAULT_FILENAME}
+ * @param value the name of the file.
+ * @see #setTodir(File)
+ */
+ public void setTofile(String value) {
+ toFile = value;
+ }
+
+ /**
+ * Set the destination directory where the results should be written. If not
+ * set if will use {@link #DEFAULT_DIR}. When given a relative directory
+ * it will resolve it from the project directory.
+ * @param value the directory where to write the results, absolute or
+ * relative.
+ */
+ public void setTodir(File value) {
+ toDir = value;
+ }
+
+ /**
+ * Add a new fileset containing the XML results to aggregate
+ * @param fs the new fileset of xml results.
+ */
+ public void addFileSet(FileSet fs) {
+ filesets.addElement(fs);
+ }
+
+ /**
+ * Aggregate all testsuites into a single document and write it to the
+ * specified directory and file.
+ * @throws BuildException thrown if there is a serious error while writing
+ * the document.
+ */
+ public void execute() throws BuildException {
+ Element rootElement = createDocument();
+ File destFile = getDestinationFile();
+ // write the document
+ try {
+ writeDOMTree(rootElement.getOwnerDocument(), destFile);
+ } catch (IOException e) {
+ throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e);
+ }
+ // apply transformation
+ Enumeration e = transformers.elements();
+ while (e.hasMoreElements()) {
+ AggregateTransformer transformer =
+ (AggregateTransformer) e.nextElement();
+ transformer.setXmlDocument(rootElement.getOwnerDocument());
+ transformer.transform();
+ }
+ }
+
+ /**
+ * Get the full destination file where to write the result. It is made of
+ * the <tt>todir</tt> and <tt>tofile</tt> attributes.
+ * @return the destination file where should be written the result file.
+ */
+ public File getDestinationFile() {
+ if (toFile == null) {
+ toFile = DEFAULT_FILENAME;
+ }
+ if (toDir == null) {
+ toDir = getProject().resolveFile(DEFAULT_DIR);
+ }
+ return new File(toDir, toFile);
+ }
+
+ /**
+ * Get all <code>.xml</code> files in the fileset.
+ *
+ * @return all files in the fileset that end with a '.xml'.
+ */
+ protected File[] getFiles() {
+ Vector v = new Vector();
+ final int size = filesets.size();
+ for (int i = 0; i < size; i++) {
+ FileSet fs = (FileSet) filesets.elementAt(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ ds.scan();
+ String[] f = ds.getIncludedFiles();
+ for (int j = 0; j < f.length; j++) {
+ String pathname = f[j];
+ if (pathname.endsWith(".xml")) {
+ File file = new File(ds.getBasedir(), pathname);
+ file = getProject().resolveFile(file.getPath());
+ v.addElement(file);
+ }
+ }
+ }
+
+ File[] files = new File[v.size()];
+ v.copyInto(files);
+ return files;
+ }
+
+ //----- from now, the methods are all related to DOM tree manipulation
+
+ /**
+ * Write the DOM tree to a file.
+ * @param doc the XML document to dump to disk.
+ * @param file the filename to write the document to. Should obviously be a .xml file.
+ * @throws IOException thrown if there is an error while writing the content.
+ */
+ protected void writeDOMTree(Document doc, File file) throws IOException {
+ OutputStream os = new FileOutputStream(file);
+ try {
+ PrintWriter wri = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(os), "UTF8"));
+ wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, " ");
+ wri.flush();
+ // writers do not throw exceptions, so check for them.
+ if (wri.checkError()) {
+ throw new IOException("Error while writing DOM content");
+ }
+ } finally {
+ os.close();
+ }
+ }
+
+ /**
+ * <p> Create a DOM tree.
+ * Has 'testsuites' as firstchild and aggregates all
+ * testsuite results that exists in the base directory.
+ * @return the root element of DOM tree that aggregates all testsuites.
+ */
+ protected Element createDocument() {
+ // create the dom tree
+ DocumentBuilder builder = getDocumentBuilder();
+ Document doc = builder.newDocument();
+ Element rootElement = doc.createElement(TESTSUITES);
+ doc.appendChild(rootElement);
+
+ generatedId = 0;
+
+ // get all files and add them to the document
+ File[] files = getFiles();
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ try {
+ log("Parsing file: '" + file + "'", Project.MSG_VERBOSE);
+ if (file.length() > 0) {
+ Document testsuiteDoc
+ = builder.parse(
+ FileUtils.getFileUtils().toURI(files[i].getAbsolutePath()));
+ Element elem = testsuiteDoc.getDocumentElement();
+ // make sure that this is REALLY a testsuite.
+ if (TESTSUITE.equals(elem.getNodeName())) {
+ addTestSuite(rootElement, elem);
+ generatedId++;
+ } else {
+ //wrong root element name
+ // issue a warning.
+ log("the file " + file
+ + WARNING_INVALID_ROOT_ELEMENT,
+ Project.MSG_WARN);
+ }
+ } else {
+ log("the file " + file
+ + WARNING_EMPTY_FILE,
+ Project.MSG_WARN);
+ }
+ } catch (SAXException e) {
+ // a testcase might have failed and write a zero-length document,
+ // It has already failed, but hey.... mm. just put a warning
+ log("The file " + file + WARNING_IS_POSSIBLY_CORRUPTED, Project.MSG_WARN);
+ log(StringUtils.getStackTrace(e), Project.MSG_DEBUG);
+ } catch (IOException e) {
+ log("Error while accessing file " + file + ": "
+ + e.getMessage(), Project.MSG_ERR);
+ log("Error while accessing file " + file + ": "
+ + e.getMessage(), e, Project.MSG_VERBOSE);
+ }
+ }
+ return rootElement;
+ }
+
+ /**
+ * <p> Add a new testsuite node to the document.
+ * The main difference is that it
+ * split the previous fully qualified name into a package and a name.
+ * <p> For example: <tt>org.apache.Whatever</tt> will be split into
+ * <tt>org.apache</tt> and <tt>Whatever</tt>.
+ * @param root the root element to which the <tt>testsuite</tt> node should
+ * be appended.
+ * @param testsuite the element to append to the given root. It will slightly
+ * modify the original node to change the name attribute and add
+ * a package one.
+ */
+ protected void addTestSuite(Element root, Element testsuite) {
+ String fullclassname = testsuite.getAttribute(ATTR_NAME);
+ int pos = fullclassname.lastIndexOf('.');
+
+ // a missing . might imply no package at all. Don't get fooled.
+ String pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos);
+ String classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
+ Element copy = (Element) DOMUtil.importNode(root, testsuite);
+
+ // modify the name attribute and set the package
+ copy.setAttribute(ATTR_NAME, classname);
+ copy.setAttribute(ATTR_PACKAGE, pkgName);
+ copy.setAttribute(ATTR_ID, Integer.toString(generatedId));
+ }
+
+ /**
+ * Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt>
+ * if something is going wrong. It is fatal anyway.
+ * @todo factorize this somewhere else. It is duplicated code.
+ * @return a new document builder to create a DOM
+ */
+ private static DocumentBuilder getDocumentBuilder() {
+ try {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (Exception exc) {
+ throw new ExceptionInInitializerError(exc);
+ }
+ }
+
+}