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