View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  
18  package org.woopi.ant.taskdefs.junit;
19  import java.io.BufferedWriter;
20  import java.io.IOException;
21  import java.io.OutputStream;
22  import java.io.OutputStreamWriter;
23  import java.io.Writer;
24  import java.util.Enumeration;
25  import java.util.Hashtable;
26  import java.util.Properties;
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import junit.framework.AssertionFailedError;
30  import junit.framework.Test;
31  import org.apache.tools.ant.BuildException;
32  import org.apache.tools.ant.util.DOMElementWriter;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  import org.w3c.dom.Text;
36  
37  
38  /***
39   * Prints XML output of the test to a specified Writer.
40   *
41   *
42   * @see FormatterElement
43   */
44  
45  public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants {
46  
47      private static DocumentBuilder getDocumentBuilder() {
48          try {
49              return DocumentBuilderFactory.newInstance().newDocumentBuilder();
50          } catch (Exception exc) {
51              throw new ExceptionInInitializerError(exc);
52          }
53      }
54  
55      /***
56       * The XML document.
57       */
58      private Document doc;
59      /***
60       * The wrapper for the whole testsuite.
61       */
62      private Element rootElement;
63      /***
64       * Element for the current test.
65       */
66      private Hashtable testElements = new Hashtable();
67      /***
68       * tests that failed.
69       */
70      private Hashtable failedTests = new Hashtable();
71      /***
72       * Timing helper.
73       */
74      private Hashtable testStarts = new Hashtable();
75      /***
76       * Where to write the log to.
77       */
78      private OutputStream out;
79  
80      public XMLJUnitResultFormatter() {
81      }
82  
83      public void setOutput(OutputStream out) {
84          this.out = out;
85      }
86  
87      public void setSystemOutput(String out) {
88          formatOutput(SYSTEM_OUT, out);
89      }
90  
91      public void setSystemError(String out) {
92          formatOutput(SYSTEM_ERR, out);
93      }
94  
95      /***
96       * The whole testsuite started.
97       */
98      public void startTestSuite(JUnitTest suite) {
99          doc = getDocumentBuilder().newDocument();
100         rootElement = doc.createElement(TESTSUITE);
101         rootElement.setAttribute(ATTR_NAME, suite.getName());
102 
103         // Output properties
104         Element propsElement = doc.createElement(PROPERTIES);
105         rootElement.appendChild(propsElement);
106         Properties props = suite.getProperties();
107         if (props != null) {
108             Enumeration e = props.propertyNames();
109             while (e.hasMoreElements()) {
110                 String name = (String) e.nextElement();
111                 Element propElement = doc.createElement(PROPERTY);
112                 propElement.setAttribute(ATTR_NAME, name);
113                 propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
114                 propsElement.appendChild(propElement);
115             }
116         }
117     }
118 
119     /***
120      * The whole testsuite ended.
121      */
122     public void endTestSuite(JUnitTest suite) throws BuildException {
123         rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount());
124         rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount());
125         rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount());
126         rootElement.setAttribute(ATTR_TIME, "" + (suite.getRunTime() / 1000.0));
127         if (out != null) {
128             Writer wri = null;
129             try {
130                 wri = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
131                 wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
132                 (new DOMElementWriter()).write(rootElement, wri, 0, "  ");
133                 wri.flush();
134             } catch (IOException exc) {
135                 throw new BuildException("Unable to write log file", exc);
136             } finally {
137                 if (out != System.out && out != System.err) {
138                     if (wri != null) {
139                         try {
140                             wri.close();
141                         } catch (IOException e) {
142                             // ignore
143                         }
144                     }
145                 }
146             }
147         }
148     }
149 
150     /***
151      * Interface TestListener.
152      *
153      * <p>A new Test is started.
154      */
155     public void startTest(Test t) {
156         testStarts.put(t, new Long(System.currentTimeMillis()));
157     }
158 
159     /***
160      * Interface TestListener.
161      *
162      * <p>A Test is finished.
163      */
164     public void endTest(Test test) {
165         // Fix for bug #5637 - if a junit.extensions.TestSetup is
166         // used and throws an exception during setUp then startTest
167         // would never have been called
168         if (!testStarts.containsKey(test)) {
169             startTest(test);
170         }
171 
172         Element currentTest = null;
173         if (!failedTests.containsKey(test)) {
174             currentTest = doc.createElement(TESTCASE);
175             currentTest.setAttribute(ATTR_NAME,
176                                      JUnitVersionHelper.getTestCaseName(test));
177             // a TestSuite can contain Tests from multiple classes,
178             // even tests with the same name - disambiguate them.
179             currentTest.setAttribute(ATTR_CLASSNAME,
180                                      test.getClass().getName());
181             rootElement.appendChild(currentTest);
182             testElements.put(test, currentTest);
183         } else {
184             currentTest = (Element) testElements.get(test);
185         }
186 
187         Long l = (Long) testStarts.get(test);
188         currentTest.setAttribute(ATTR_TIME,
189             "" + ((System.currentTimeMillis() - l.longValue()) / 1000.0));
190     }
191 
192     /***
193      * Interface TestListener for JUnit &lt;= 3.4.
194      *
195      * <p>A Test failed.
196      */
197     public void addFailure(Test test, Throwable t) {
198         formatError(FAILURE, test, t);
199     }
200 
201     /***
202      * Interface TestListener for JUnit &gt; 3.4.
203      *
204      * <p>A Test failed.
205      */
206     public void addFailure(Test test, AssertionFailedError t) {
207         addFailure(test, (Throwable) t);
208     }
209 
210     /***
211      * Interface TestListener.
212      *
213      * <p>An error occurred while running the test.
214      */
215     public void addError(Test test, Throwable t) {
216         formatError(ERROR, test, t);
217     }
218 
219     private void formatError(String type, Test test, Throwable t) {
220         if (test != null) {
221             endTest(test);
222             failedTests.put(test, test);
223         }
224 
225         Element nested = doc.createElement(type);
226         Element currentTest = null;
227         if (test != null) {
228             currentTest = (Element) testElements.get(test);
229         } else {
230             currentTest = rootElement;
231         }
232 
233         currentTest.appendChild(nested);
234 
235         String message = t.getMessage();
236         if (message != null && message.length() > 0) {
237             nested.setAttribute(ATTR_MESSAGE, t.getMessage());
238         }
239         nested.setAttribute(ATTR_TYPE, t.getClass().getName());
240 
241         String strace = JUnitTestRunner.getFilteredTrace(t);
242         Text trace = doc.createTextNode(strace);
243         nested.appendChild(trace);
244     }
245 
246     private void formatOutput(String type, String output) {
247         Element nested = doc.createElement(type);
248         rootElement.appendChild(nested);
249         nested.appendChild(doc.createCDATASection(output));
250     }
251 
252 } // XMLJUnitResultFormatter