View Javadoc
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