KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > junit > XMLResultAggregator


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs.optional.junit;
19
20 import java.io.BufferedOutputStream JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.io.OutputStreamWriter JavaDoc;
26 import java.io.PrintWriter JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Vector JavaDoc;
29 import javax.xml.parsers.DocumentBuilder JavaDoc;
30 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.DirectoryScanner;
33 import org.apache.tools.ant.Project;
34 import org.apache.tools.ant.Task;
35 import org.apache.tools.ant.types.FileSet;
36 import org.apache.tools.ant.util.DOMElementWriter;
37 import org.apache.tools.ant.util.FileUtils;
38 import org.apache.tools.ant.util.StringUtils;
39 import org.w3c.dom.Document JavaDoc;
40 import org.w3c.dom.Element JavaDoc;
41 import org.xml.sax.SAXException JavaDoc;
42
43
44 /**
45  * Aggregates all <junit> XML formatter testsuite data under
46  * a specific directory and transforms the results via XSLT.
47  * It is not particulary clean but
48  * should be helpful while I am thinking about another technique.
49  *
50  * <p> The main problem is due to the fact that a JVM can be forked for a testcase
51  * thus making it impossible to aggregate all testcases since the listener is
52  * (obviously) in the forked JVM. A solution could be to write a
53  * TestListener that will receive events from the TestRunner via sockets. This
54  * is IMHO the simplest way to do it to avoid this file hacking thing.
55  *
56  * @ant.task name="junitreport" category="testing"
57  */

58 public class XMLResultAggregator extends Task implements XMLConstants {
59
60     // CheckStyle:VisibilityModifier OFF - bc
61
/** the list of all filesets, that should contains the xml to aggregate */
62     protected Vector JavaDoc filesets = new Vector JavaDoc();
63
64     /** the name of the result file */
65     protected String JavaDoc toFile;
66
67     /** the directory to write the file to */
68     protected File JavaDoc toDir;
69
70     protected Vector JavaDoc transformers = new Vector JavaDoc();
71
72     /** The default directory: <tt>&#046;</tt>. It is resolved from the project directory */
73     public static final String JavaDoc DEFAULT_DIR = ".";
74
75     /** the default file name: <tt>TESTS-TestSuites.xml</tt> */
76     public static final String JavaDoc DEFAULT_FILENAME = "TESTS-TestSuites.xml";
77
78     /** the current generated id */
79     protected int generatedId = 0;
80
81     /**
82      * text checked for in tests, {@value}
83      */

84     static final String JavaDoc WARNING_IS_POSSIBLY_CORRUPTED
85         = " is not a valid XML document. It is possibly corrupted.";
86     /**
87      * text checked for in tests, {@value}
88      */

89     static final String JavaDoc WARNING_INVALID_ROOT_ELEMENT
90         = " is not a valid testsuite XML document";
91     /**
92      * text checked for in tests, {@value}
93      */

94     static final String JavaDoc WARNING_EMPTY_FILE
95         = " is empty.\nThis can be caused by the test JVM exiting unexpectedly";
96     // CheckStyle:VisibilityModifier ON
97

98     /**
99      * Generate a report based on the document created by the merge.
100      * @return the report
101      */

102     public AggregateTransformer createReport() {
103         AggregateTransformer transformer = new AggregateTransformer(this);
104         transformers.addElement(transformer);
105         return transformer;
106     }
107
108     /**
109      * Set the name of the aggregegated results file. It must be relative
110      * from the <tt>todir</tt> attribute. If not set it will use {@link #DEFAULT_FILENAME}
111      * @param value the name of the file.
112      * @see #setTodir(File)
113      */

114     public void setTofile(String JavaDoc value) {
115         toFile = value;
116     }
117
118     /**
119      * Set the destination directory where the results should be written. If not
120      * set if will use {@link #DEFAULT_DIR}. When given a relative directory
121      * it will resolve it from the project directory.
122      * @param value the directory where to write the results, absolute or
123      * relative.
124      */

125     public void setTodir(File JavaDoc value) {
126         toDir = value;
127     }
128
129     /**
130      * Add a new fileset containing the XML results to aggregate
131      * @param fs the new fileset of xml results.
132      */

133     public void addFileSet(FileSet fs) {
134         filesets.addElement(fs);
135     }
136
137     /**
138      * Aggregate all testsuites into a single document and write it to the
139      * specified directory and file.
140      * @throws BuildException thrown if there is a serious error while writing
141      * the document.
142      */

143     public void execute() throws BuildException {
144         Element JavaDoc rootElement = createDocument();
145         File JavaDoc destFile = getDestinationFile();
146         // write the document
147
try {
148             writeDOMTree(rootElement.getOwnerDocument(), destFile);
149         } catch (IOException JavaDoc e) {
150             throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e);
151         }
152         // apply transformation
153
Enumeration JavaDoc e = transformers.elements();
154         while (e.hasMoreElements()) {
155             AggregateTransformer transformer =
156                 (AggregateTransformer) e.nextElement();
157             transformer.setXmlDocument(rootElement.getOwnerDocument());
158             transformer.transform();
159         }
160     }
161
162     /**
163      * Get the full destination file where to write the result. It is made of
164      * the <tt>todir</tt> and <tt>tofile</tt> attributes.
165      * @return the destination file where should be written the result file.
166      */

167     public File JavaDoc getDestinationFile() {
168         if (toFile == null) {
169             toFile = DEFAULT_FILENAME;
170         }
171         if (toDir == null) {
172             toDir = getProject().resolveFile(DEFAULT_DIR);
173         }
174         return new File JavaDoc(toDir, toFile);
175     }
176
177     /**
178      * Get all <code>.xml</code> files in the fileset.
179      *
180      * @return all files in the fileset that end with a '.xml'.
181      */

182     protected File JavaDoc[] getFiles() {
183         Vector JavaDoc v = new Vector JavaDoc();
184         final int size = filesets.size();
185         for (int i = 0; i < size; i++) {
186             FileSet fs = (FileSet) filesets.elementAt(i);
187             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
188             ds.scan();
189             String JavaDoc[] f = ds.getIncludedFiles();
190             for (int j = 0; j < f.length; j++) {
191                 String JavaDoc pathname = f[j];
192                 if (pathname.endsWith(".xml")) {
193                     File JavaDoc file = new File JavaDoc(ds.getBasedir(), pathname);
194                     file = getProject().resolveFile(file.getPath());
195                     v.addElement(file);
196                 }
197             }
198         }
199
200         File JavaDoc[] files = new File JavaDoc[v.size()];
201         v.copyInto(files);
202         return files;
203     }
204
205     //----- from now, the methods are all related to DOM tree manipulation
206

207     /**
208      * Write the DOM tree to a file.
209      * @param doc the XML document to dump to disk.
210      * @param file the filename to write the document to. Should obviouslly be a .xml file.
211      * @throws IOException thrown if there is an error while writing the content.
212      */

213     protected void writeDOMTree(Document JavaDoc doc, File JavaDoc file) throws IOException JavaDoc {
214         OutputStream JavaDoc out = null;
215         PrintWriter JavaDoc wri = null;
216         try {
217             out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(file));
218             wri = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(out, "UTF8"));
219             wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
220             (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, " ");
221             wri.flush();
222             // writers do not throw exceptions, so check for them.
223
if (wri.checkError()) {
224                 throw new IOException JavaDoc("Error while writing DOM content");
225             }
226         } finally {
227             FileUtils.close(wri);
228             FileUtils.close(out);
229         }
230     }
231
232     /**
233      * <p> Create a DOM tree.
234      * Has 'testsuites' as firstchild and aggregates all
235      * testsuite results that exists in the base directory.
236      * @return the root element of DOM tree that aggregates all testsuites.
237      */

238     protected Element JavaDoc createDocument() {
239         // create the dom tree
240
DocumentBuilder JavaDoc builder = getDocumentBuilder();
241         Document JavaDoc doc = builder.newDocument();
242         Element JavaDoc rootElement = doc.createElement(TESTSUITES);
243         doc.appendChild(rootElement);
244
245         generatedId = 0;
246
247         // get all files and add them to the document
248
File JavaDoc[] files = getFiles();
249         for (int i = 0; i < files.length; i++) {
250             File JavaDoc file = files[i];
251             try {
252                 log("Parsing file: '" + file + "'", Project.MSG_VERBOSE);
253                 if (file.length() > 0) {
254                     Document JavaDoc testsuiteDoc
255                             = builder.parse(
256                                 FileUtils.getFileUtils().toURI(files[i].getAbsolutePath()));
257                     Element JavaDoc elem = testsuiteDoc.getDocumentElement();
258                     // make sure that this is REALLY a testsuite.
259
if (TESTSUITE.equals(elem.getNodeName())) {
260                         addTestSuite(rootElement, elem);
261                         generatedId++;
262                     } else {
263                         //wrong root element name
264
// issue a warning.
265
log("the file " + file
266                                 + WARNING_INVALID_ROOT_ELEMENT,
267                                 Project.MSG_WARN);
268                     }
269                 } else {
270                     log("the file " + file
271                             + WARNING_EMPTY_FILE,
272                             Project.MSG_WARN);
273                 }
274             } catch (SAXException JavaDoc e) {
275                 // a testcase might have failed and write a zero-length document,
276
// It has already failed, but hey.... mm. just put a warning
277
log("The file " + file + WARNING_IS_POSSIBLY_CORRUPTED, Project.MSG_WARN);
278                 log(StringUtils.getStackTrace(e), Project.MSG_DEBUG);
279             } catch (IOException JavaDoc e) {
280                 log("Error while accessing file " + file + ": "
281                     + e.getMessage(), Project.MSG_ERR);
282             }
283         }
284         return rootElement;
285     }
286
287     /**
288      * <p> Add a new testsuite node to the document.
289      * The main difference is that it
290      * split the previous fully qualified name into a package and a name.
291      * <p> For example: <tt>org.apache.Whatever</tt> will be split into
292      * <tt>org.apache</tt> and <tt>Whatever</tt>.
293      * @param root the root element to which the <tt>testsuite</tt> node should
294      * be appended.
295      * @param testsuite the element to append to the given root. It will slightly
296      * modify the original node to change the name attribute and add
297      * a package one.
298      */

299     protected void addTestSuite(Element JavaDoc root, Element JavaDoc testsuite) {
300         String JavaDoc fullclassname = testsuite.getAttribute(ATTR_NAME);
301         int pos = fullclassname.lastIndexOf('.');
302
303         // a missing . might imply no package at all. Don't get fooled.
304
String JavaDoc pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos);
305         String JavaDoc classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
306         Element JavaDoc copy = (Element JavaDoc) DOMUtil.importNode(root, testsuite);
307
308         // modify the name attribute and set the package
309
copy.setAttribute(ATTR_NAME, classname);
310         copy.setAttribute(ATTR_PACKAGE, pkgName);
311         copy.setAttribute(ATTR_ID, Integer.toString(generatedId));
312     }
313
314     /**
315      * Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt>
316      * if something is going wrong. It is fatal anyway.
317      * @todo factorize this somewhere else. It is duplicated code.
318      * @return a new document builder to create a DOM
319      */

320     private static DocumentBuilder JavaDoc getDocumentBuilder() {
321         try {
322             return DocumentBuilderFactory.newInstance().newDocumentBuilder();
323         } catch (Exception JavaDoc exc) {
324             throw new ExceptionInInitializerError JavaDoc(exc);
325         }
326     }
327
328 }
329
Popular Tags