1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
166
167
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
178
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 <= 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 > 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 }