1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.geronimo.mavenplugins.geronimo.reporting;
21
22 import java.io.BufferedInputStream;
23 import java.io.BufferedWriter;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileNotFoundException;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStreamWriter;
31 import java.io.PrintWriter;
32 import java.io.UnsupportedEncodingException;
33 import java.io.FileWriter;
34 import java.text.NumberFormat;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Enumeration;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Properties;
42
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45
46 import org.apache.maven.surefire.report.ReporterException;
47 import org.apache.maven.surefire.util.PrettyPrintXMLWriter;
48
49 import org.codehaus.plexus.util.FileUtils;
50 import org.codehaus.plexus.util.IOUtil;
51 import org.codehaus.plexus.util.StringUtils;
52 import org.codehaus.plexus.util.xml.Xpp3Dom;
53 import org.codehaus.plexus.util.xml.Xpp3DomWriter;
54
55 /**
56 * A reporter that generates Surefire result data, so the Surefire report can be used.
57 *
58 * @version $Rev: 450613 $ $Date: 2006-09-27 15:45:46 -0700 (Wed, 27 Sep 2006) $
59 */
60 public class SurefireReporter
61 implements Reporter
62 {
63 private static final Log log = LogFactory.getLog(SurefireReporter.class);
64
65 private static final String LS = System.getProperty("line.separator");
66
67 /**
68 * The name of the test (goal name).
69 */
70 private String testName;
71
72 /**
73 * The input log file, may or may not exist.
74 */
75 private File logFile;
76
77 /**
78 * The failure cause.
79 */
80 private Throwable failureCause;
81
82 /**
83 * @parameter expression="${project.build.directory}/surefire-reports"
84 */
85 private File reportsDirectory = null;
86
87 /**
88 * The file where the test output text will be written.
89 */
90 private File outputFile;
91
92 /**
93 * The file where the text result xml will be written.
94 */
95 private File reportFile;
96
97 /**
98 * The time when the test started.
99 */
100 private long startTime;
101
102 /**
103 * The number of errors.
104 */
105 private int numErrors = 0;
106
107 private static final int MS_PER_SEC = 1000;
108
109 private NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
110
111 private List results = Collections.synchronizedList(new ArrayList());
112
113
114
115
116
117 public void reportBegin(final Reportable source) {
118 assert source != null;
119
120 logFile = source.getLogFile();
121 testName = source.getName();
122 startTime = source.getStartTime().getTime();
123
124 try {
125 FileUtils.forceMkdir(reportsDirectory);
126 }
127 catch (IOException e) {
128
129
130
131
132 log.error("Failed to make reports directory: " + reportsDirectory, e);
133 }
134
135 outputFile = new File(reportsDirectory, testName + ".txt");
136 reportFile = new File(reportsDirectory, "TEST-" + testName + ".xml");
137 if (reportFile.exists()) {
138 reportFile.delete();
139 }
140 }
141
142 public void reportError(final Throwable cause) {
143 assert cause != null;
144
145 log.debug("Capturing failed report from cause", cause);
146
147 this.failureCause = cause;
148 }
149
150 public void reportEnd() {
151 try {
152 if (logFile.exists()) {
153 FileUtils.copyFile(logFile, outputFile);
154 }
155 }
156 catch (Exception e) {
157 log.warn("Failed to update outputFile", e);
158 }
159
160 if (failureCause != null) {
161 try {
162 boolean append = outputFile.exists();
163 PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(outputFile, append)));
164 try {
165 writer.println("Exception Detail");
166 writer.println("================");
167 failureCause.printStackTrace(writer);
168 writer.flush();
169 }
170 finally {
171 writer.close();
172 }
173 }
174 catch (Exception e) {
175 log.warn("Failed to append error detail to outputFile", e);
176 }
177
178 testFailed();
179 }
180 else {
181 testSucceeded();
182 }
183
184 try {
185 testSetCompleted();
186 }
187 catch (ReporterException e) {
188 log.warn("Failed to set test completed", e);
189 }
190 }
191
192
193
194
195
196 private void testSetCompleted() throws ReporterException {
197 long runTime = System.currentTimeMillis() - this.startTime;
198
199 Xpp3Dom testSuite = createTestElement("testsuite", testName, runTime);
200
201 showProperties(testSuite);
202
203 testSuite.setAttribute("tests", "1");
204 testSuite.setAttribute("errors", String.valueOf(numErrors));
205 testSuite.setAttribute("skipped", "0");
206 testSuite.setAttribute("failures", "0");
207
208 for (Iterator i = results.iterator(); i.hasNext();) {
209 Xpp3Dom testcase = (Xpp3Dom) i.next();
210 testSuite.addChild(testcase);
211 }
212
213 PrintWriter writer = null;
214
215 try {
216 writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(reportFile), "UTF-8")));
217 writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + LS);
218 Xpp3DomWriter.write(new PrettyPrintXMLWriter(writer), testSuite);
219 }
220 catch (UnsupportedEncodingException e) {
221 throw new ReporterException("Unable to use UTF-8 encoding", e);
222 }
223 catch (FileNotFoundException e) {
224 throw new ReporterException("Unable to create file: " + e.getMessage(), e);
225 }
226 finally {
227 IOUtil.close(writer);
228 }
229 }
230
231 private Xpp3Dom createTestElement(String element, String testName, long runTime) {
232 Xpp3Dom testCase = new Xpp3Dom(element);
233 testCase.setAttribute("name", testName);
234 testCase.setAttribute("time", elapsedTimeAsString(runTime));
235
236 return testCase;
237 }
238
239 private Xpp3Dom createElement(Xpp3Dom element, String testName) {
240 Xpp3Dom component = new Xpp3Dom(testName);
241 element.addChild(component);
242
243 return component;
244 }
245
246 private void testSucceeded() {
247 long runTime = System.currentTimeMillis() - this.startTime;
248 Xpp3Dom testCase = createTestElement("testcase", testName, runTime);
249 results.add(testCase);
250 }
251
252 private void testFailed() {
253 ++numErrors;
254
255 try {
256 InputStream input = new BufferedInputStream(new FileInputStream(outputFile));
257 int length = input.available();
258 byte[] b = new byte[length];
259 input.read(b, 0, length);
260 writeTestProblems(testName, new String(b));
261 }
262 catch (IOException e) {
263 log.error("Failed to write test problems", e);
264 }
265 }
266
267 private void writeTestProblems(String testName, String stdErr) {
268 long runTime = System.currentTimeMillis() - this.startTime;
269
270 Xpp3Dom testCase = createTestElement("testcase", testName, runTime);
271 Xpp3Dom element = createElement(testCase, "failure");
272
273 element.setAttribute("message", escapeAttribute(getMessage(stdErr)));
274 element.setAttribute("type", getType(stdErr));
275 element.setValue(stdErr);
276
277 results.add(testCase);
278 }
279
280 /**
281 * Adds system properties to the XML report.
282 */
283 private void showProperties(Xpp3Dom testSuite) {
284 Xpp3Dom properties = createElement(testSuite, "properties");
285
286 Properties systemProperties = System.getProperties();
287
288 if (systemProperties != null) {
289 Enumeration propertyKeys = systemProperties.propertyNames();
290
291 while (propertyKeys.hasMoreElements()) {
292 String key = (String) propertyKeys.nextElement();
293 String value = systemProperties.getProperty(key);
294
295 if (value == null) {
296 value = "null";
297 }
298
299 Xpp3Dom property = createElement(properties, "property");
300
301 property.setAttribute("name", key);
302 property.setAttribute("value", escapeAttribute(value));
303 }
304 }
305 }
306
307 private static String escapeAttribute(String attribute) {
308
309 String s = StringUtils.replace(attribute, "<", "<");
310 return StringUtils.replace(s, ">", ">");
311 }
312
313 private Iterator getResults() {
314 return results.iterator();
315 }
316
317 private String elapsedTimeAsString(long runTime) {
318 return numberFormat.format((double) runTime / MS_PER_SEC);
319 }
320
321 /**
322 * Gets the messages following the exception type.
323 */
324 private String getMessage(String stdErr) {
325 int beginMarker = stdErr.indexOf("Exception:") + 10;
326 int endMarker = stdErr.indexOf("\n", beginMarker);
327 return stdErr.substring(beginMarker, endMarker);
328 }
329
330 /**
331 * Gets the type of exception from the stacktrace.
332 */
333 private String getType(String stdErr) {
334 int endMarker = stdErr.indexOf("Exception:") + 9;
335 int beginMarker = stdErr.lastIndexOf("\n", endMarker) + 1;
336 return stdErr.substring(beginMarker, endMarker);
337 }
338
339 /**
340 * The generated reports xml file for surefire..
341 *
342 * @return generated reports xml file
343 */
344 public File getReportsFile() {
345 return this.reportFile;
346 }
347
348 /**
349 * The text file which holds the stdout or stderr.
350 *
351 * @return File
352 */
353 public File getOutputFile() {
354 return this.outputFile;
355 }
356 }