001 /*
002 * Created on Jun 6, 2007
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005 * the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011 * specific language governing permissions and limitations under the License.
012 *
013 * Copyright @2007-2009 the original author or authors.
014 */
015 package org.fest.swing.junit.ant;
016
017 import static org.apache.tools.ant.taskdefs.optional.junit.XMLConstants.*;
018
019 import java.io.OutputStream;
020
021 import junit.framework.AssertionFailedError;
022 import junit.framework.Test;
023
024 import org.apache.tools.ant.BuildException;
025 import org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter;
026 import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
027 import org.fest.swing.junit.xml.XmlDocument;
028 import org.fest.swing.junit.xml.XmlNode;
029
030 /**
031 * Understands a copy of the original <code>XMLJUnitResultFormatter</code>, with flexibility for extension.
032 *
033 * @author Alex Ruiz
034 */
035 public class XmlJUnitResultFormatter implements JUnitResultFormatter {
036
037 private XmlNode xmlRoot;
038
039 private OutputStream out; // where to write the log to
040
041 private final TestCollection tests;
042
043 private final SuiteXmlNodeWriter suiteXmlNodeWriter;
044 private final EnvironmentXmlNodeWriter environmentXmlNodeWriter;
045 private final TestXmlNodeWriter testXmlNodeWriter;
046
047 private final XmlOutputWriter xmlOutputWriter;
048
049 /**
050 * Creates a new </code>{@link XmlJUnitResultFormatter}</code>.
051 */
052 public XmlJUnitResultFormatter() {
053 tests = new TestCollection();
054 suiteXmlNodeWriter = new SuiteXmlNodeWriter();
055 environmentXmlNodeWriter = new EnvironmentXmlNodeWriter();
056 testXmlNodeWriter = new TestXmlNodeWriter();
057 xmlOutputWriter = new XmlOutputWriter();
058 }
059
060 // for testing only
061 final TestCollection tests() { return tests; }
062
063 /**
064 * Sets the stream the formatter is supposed to write its results to.
065 * @param out the output stream to use.
066 */
067 public final void setOutput(OutputStream out) {
068 this.out = out;
069 }
070
071 /**
072 * This is what the test has written to <code>System.out</code>,
073 * @param out the <code>String</code> to write.
074 */
075 public final void setSystemOutput(String out) {
076 formatOutput(SYSTEM_OUT, out);
077 }
078
079 /**
080 * This is what the test has written to <code>System.err</code>.
081 * @param out the <code>String</code> to write.
082 */
083 public final void setSystemError(String out) {
084 formatOutput(SYSTEM_ERR, out);
085 }
086
087 private void formatOutput(String type, String output) {
088 xmlRoot.addNewNode(type).addCdata(output);
089 }
090
091 protected final XmlNode xmlRootNode() { return xmlRoot; }
092
093 /**
094 * The whole test suite started. This method starts creation of the XML report.
095 * @param suite the test suite.
096 * @throws ExceptionInInitializerError if the underlying XML document could not be created.
097 */
098 public final void startTestSuite(JUnitTest suite) {
099 XmlDocument document = new XmlDocument();
100 xmlRoot = document.newRoot(TESTSUITE);
101 suiteXmlNodeWriter.writeSuiteName(xmlRoot, suite)
102 .writeSuiteProperties(xmlRoot, suite);
103 environmentXmlNodeWriter.writeHostName(xmlRoot)
104 .writeTimestamp(xmlRoot);
105 onStartTestSuite(suite);
106 }
107
108 /**
109 * Hook for subclasses to add extra functionality after the whole test suite started.
110 * @param suite the test suite.
111 */
112 protected void onStartTestSuite(JUnitTest suite) {}
113
114 /**
115 * The whole test suite ended. This method finishes writing the XML report and writes its contents to this
116 * formatter's <code>{@link OutputStream}</code>.
117 * @param suite the test suite.
118 * @throws BuildException on error.
119 */
120 public final void endTestSuite(JUnitTest suite) {
121 suiteXmlNodeWriter.writeSuiteStatistics(xmlRoot, suite);
122 if (out == null) return;
123 xmlOutputWriter.write(xmlRoot, out);
124 }
125
126 /**
127 * A new test is started.
128 * @param test the test.
129 */
130 public final void startTest(Test test) {
131 tests.started(test);
132 }
133
134 /**
135 * A test is finished.
136 * @param test the test.
137 */
138 public final void endTest(Test test) {
139 if (!tests.wasStarted(test)) startTest(test);
140 XmlNode testNode = xmlNodeForFinished(test);
141 testXmlNodeWriter.writeTestExecutionTime(testNode, tests.startTimeOf(test));
142 }
143
144 private XmlNode xmlNodeForFinished(Test test) {
145 if (tests.wasFailed(test)) return tests.xmlNodeFor(test);
146 XmlNode newTestXmlNode = testXmlNodeWriter.addNewTestXmlNode(xmlRoot, test);
147 tests.addXmlNode(test, newTestXmlNode);
148 return newTestXmlNode;
149 }
150
151 /**
152 * A test failed.
153 * @param test the test.
154 * @param failedAssertion the failed assertion.
155 */
156 public final void addFailure(Test test, AssertionFailedError failedAssertion) {
157 addFailure(test, (Throwable)failedAssertion);
158 }
159
160 /**
161 * A test failed.
162 * @param test the test.
163 * @param error the exception.
164 */
165 public final void addFailure(Test test, Throwable error) {
166 XmlNode errorXmlNode = formatError(FAILURE, test, error);
167 onFailureOrError(test, error, errorXmlNode);
168 }
169
170 /**
171 * An error occurred while running the test.
172 * @param test the test.
173 * @param error the error.
174 */
175 public final void addError(Test test, Throwable error) {
176 XmlNode errorXmlNode = formatError(ERROR, test, error);
177 onFailureOrError(test, error, errorXmlNode);
178 }
179
180 private XmlNode formatError(String type, Test test, Throwable error) {
181 if (test != null) {
182 endTest(test);
183 tests.failed(test);
184 }
185 XmlNode errorXmlNode = xmlForFailed(test).addNewNode(type);
186 writeErrorAndStackTrace(error, errorXmlNode);
187 return errorXmlNode;
188 }
189
190 private XmlNode xmlForFailed(Test test) {
191 if (test != null) return tests.xmlNodeFor(test);
192 return xmlRoot;
193 }
194
195 /**
196 * Writes the stack trace and message of the given error to the given XML node.
197 * @param error the given error.
198 * @param errorXmlNode the XML node to write to.
199 */
200 protected final void writeErrorAndStackTrace(Throwable error, XmlNode errorXmlNode) {
201 testXmlNodeWriter.writeErrorAndStackTrace(errorXmlNode, error);
202 }
203
204 /**
205 * Hook for subclasses to add extra functionality after a test failure or a test execution error.
206 * @param test the executing test.
207 * @param error the reason of the failure or error.
208 * @param errorXmlNode the XML element containing information about the test failure or error.
209 */
210 protected void onFailureOrError(Test test, Throwable error, XmlNode errorXmlNode) {}
211 }