1 /* XMLFormatter.java */
2
3 package org.quilt.reports;
4
5 import java.io.OutputStream;
6 import java.io.Writer;
7 import java.io.OutputStreamWriter;
8 import java.io.IOException;
9 import java.util.Properties;
10 import java.util.Enumeration;
11 import java.util.Hashtable;
12 import javax.xml.parsers.DocumentBuilder;
13 import javax.xml.parsers.DocumentBuilderFactory;
14
15 import org.w3c.dom.Document;
16 import org.w3c.dom.Element;
17 import org.w3c.dom.Text;
18
19 import org.apache.tools.ant.BuildException;
20 import org.apache.tools.ant.util.DOMElementWriter;
21
22 import junit.framework.*;
23
24 import org.quilt.framework.*;
25 import org.quilt.runner.Runner;
26
27 /***
28 * Produce an XML document containing the test data for the run.
29 * This will in general contain the results of many tests, but
30 * only those resulting from one QuiltTest.
31 *
32 * @todo Restructure to produce one XML document for the entire
33 * Ant/Quilt run. This needs to be held by the class
34 * managing the whole run, QuiltTask if it is an Ant run.
35 * Simplest solution: when the test is not forked, pass back
36 * the document as a tree instead of serializing it in text
37 * form to output. The manager can then merge the trees and
38 * output a single document for the run.
39 *
40 * @todo Add flag to suppress generation of (quite verbose) properties
41 * element.
42 */
43 public class XMLFormatter implements Formatter {
44
45 /*** Whether to filter Ant/Quilt/JUnit stack traces. */
46 private boolean filtertrace = false;
47
48 // MODIFY TO EXTEND BaseFormatter, then drop this ///////////////
49 /***
50 * Root around in a junit Test and find a name, should there be one.
51 * @return Test/suite name.
52 */
53 protected static String getTestName (Test test) {
54 if ( test instanceof TestSuite ) {
55 return ( (TestSuite) test ) . getName();
56 } else if ( test instanceof TestCase ) {
57 return ( (TestCase) test ) . getName();
58 } else {
59 return "unknown";
60 }
61 }
62 // END BaseFormatter CODE ///////////////////////////////////////
63
64 private static DocumentBuilderFactory dbf = null;
65
66 // none of this is thread safe; JavaDocs recommend ensuring that
67 // there is only one DocumentBuilder per thread
68 private static DocumentBuilder getDocumentBuilder() {
69 try {
70 if (dbf == null) {
71 dbf = DocumentBuilderFactory.newInstance();
72 }
73 return dbf.newDocumentBuilder();
74 } catch (Exception exc) {
75 throw new ExceptionInInitializerError(exc);
76 }
77 }
78
79 /***
80 * The XML document. Unfortunately one XML Document is produced
81 * per QuiltTest. Generally there will be many QuiltTests per run.
82 */
83 private Document doc;
84 /*** Where the output goes. */
85 private OutputStream out;
86 /*** Root node, the Ant/Quilt test run as a whole. */
87 private Element rootNode;
88 /*** The runner, usually an instance of runner.BaseTestRunner. */
89 private Runner runner = null;
90 /*** Nodes in the document that tests hang off of. */
91 private Hashtable testNodes = new Hashtable(); // key = Test test
92 /*** Hash holding information about individual tests. */
93 private Hashtable testStarts = new Hashtable();
94
95 public XMLFormatter() {}
96
97 // FORMATTER INTERFACE //////////////////////////////////////////
98
99 /*** Method called at end of test run. */
100
101 public void endTestSuite(QuiltTest qt) throws BuildException {
102 rootNode.setAttribute("tests", "" + qt.runCount());
103 rootNode.setAttribute("errors", "" + qt.errorCount());
104 rootNode.setAttribute("failures", "" + qt.failureCount());
105 rootNode.setAttribute("time", "" + (qt.getRunTime() / 1000.0));
106 if (out != null) {
107 Writer wri = null;
108 try {
109 wri = new OutputStreamWriter(out, "UTF8");
110 wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
111 (new DOMElementWriter())
112 .write(rootNode, wri, 0, " ");
113 wri.flush();
114 } catch (IOException e) {
115 throw new BuildException("Unable to write log file", e);
116 } finally {
117 if (out != System.out && out != System.err) {
118 if (wri != null) {
119 try {
120 wri.close();
121 } catch (IOException e) {}
122 }
123 }
124 }
125 }
126 }
127 /*** Enable filtering of Ant/Quilt/JUnit lines from stack traces. */
128 public void setFiltertrace (boolean b) {
129 filtertrace = b;
130 }
131 /*** Set the output file. */
132 public void setOutput(OutputStream out) {
133 this.out = out;
134 }
135 /*** Set the test runner to be used. */
136 public void setRunner(Runner testrunner) {
137 runner = testrunner;
138 }
139 /*** Direct the error output. */
140 public void setSystemError(String out) {
141 formatOutput("system-err", out);
142 }
143 /*** Direct standard output. */
144 public void setSystemOutput(String out) {
145 formatOutput("system-out", out);
146 }
147 /*** Method called at the beginning of the test run. */
148 public void startTestSuite(QuiltTest qt) {
149 doc = getDocumentBuilder().newDocument();
150 rootNode = doc.createElement("testsuite");
151 rootNode.setAttribute("name", qt.getName());
152
153 // Output properties - this creates a lot of meaningless
154 // data, far exceeding the JUnit data of real interest.
155 Element propsElement = doc.createElement("properties");
156 rootNode.appendChild(propsElement);
157 Properties props = qt.getProperties();
158 if (props != null) {
159 Enumeration e = props.propertyNames();
160 while (e.hasMoreElements()) {
161 String name = (String) e.nextElement();
162 Element propElement = doc.createElement("property");
163 propElement.setAttribute("name", name);
164 propElement.setAttribute("value", props.getProperty(name));
165 propsElement.appendChild(propElement);
166 }
167 }
168 }
169
170 // TESTLISTENER INTERFACE ///////////////////////////////////////
171 /*** Method called when an unexpected error occurs. */
172 public void addError(Test test, Throwable t) {
173 formatError("error", test, t);
174 }
175 /*** Method called when a failure (or unexpected error) occurs. */
176 public void addFailure(Test test, Throwable t) {
177 formatError("failure", test, t);
178 }
179 /*** Method called when a failure (or unexpected error) occurs. */
180 public void addFailure(Test test, AssertionFailedError t) {
181 addFailure(test, (Throwable) t);
182 }
183 /*** Method called when a JUnit test ends. */
184 public void endTest(Test test) {
185 Element testNode = (Element) testNodes.get(test);
186 if (testNode == null) {
187 startTest(test);
188 testNode = (Element) testNodes.get(test);
189 }
190 Long l = (Long) testStarts.get(test);
191 testNode.setAttribute("time",
192 "" + ((System.currentTimeMillis() - l.longValue()) / 1000.0));
193 }
194 /*** Called at the beginning of a JUnit test. */
195 public void startTest(Test test) {
196 testStarts.put(test, new Long(System.currentTimeMillis()));
197 Element testNode = doc.createElement("testcase");
198 testNode.setAttribute("name", getTestName(test));
199 rootNode.appendChild(testNode);
200 testNodes.put(test, testNode);
201 }
202
203 // OTHER METHODS ////////////////////////////////////////////////
204 /*** Hang an error message off the document tree. */
205 private void formatError(String type, Test test, Throwable t) {
206 if (test != null) {
207 endTest(test);
208 }
209 Element msgNode = doc.createElement(type);
210 Element curNode = null;
211 if (test != null) {
212 curNode = (Element) testNodes.get(test);
213 } else {
214 curNode = rootNode;
215 }
216
217 curNode.appendChild(msgNode);
218
219 String message = t.getMessage();
220 if (message != null && message.length() > 0) {
221 msgNode.setAttribute("message", t.getMessage());
222 }
223 msgNode.setAttribute("type", t.getClass().getName());
224
225 String strace = runner.getFilteredTrace(t, filtertrace);
226 Text trace = doc.createTextNode(strace);
227 msgNode.appendChild(trace);
228 }
229 /***
230 * Hang a text message off the document tree. The message
231 * might be quite long, for example all System.err output
232 * for the run.
233 */
234 private void formatOutput(String type, String output) {
235 Element txtNode = doc.createElement(type);
236 rootNode.appendChild(txtNode);
237 txtNode.appendChild(doc.createCDATASection(output));
238 }
239 }
This page was automatically generated by Maven