aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java
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/XMLResultAggregator.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java329
1 files changed, 329 insertions, 0 deletions
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 <junit> 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);
+ }
+ }
+
+}