1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.surefire.report;
20
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Queue;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentLinkedQueue;
27 import java.util.concurrent.ConcurrentMap;
28
29 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
30 import org.apache.maven.surefire.api.report.ReportEntry;
31 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
32 import org.apache.maven.surefire.api.report.TestReportListener;
33 import org.apache.maven.surefire.api.report.TestSetReportEntry;
34 import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
35 import org.apache.maven.surefire.extensions.StatelessReportEventListener;
36 import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
37 import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
38
39 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
40 import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
41 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
42 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
43
44
45
46
47
48
49
50 public class TestSetRunListener implements TestReportListener<TestOutputReportEntry> {
51 private final Queue<TestMethodStats> testMethodStats = new ConcurrentLinkedQueue<>();
52
53
54
55
56
57
58 private final ConcurrentMap<String, TestSetStats> detailsPerSource = new ConcurrentHashMap<>();
59
60 private final TestSetStats currentTestSetStats;
61
62 private final ConsoleOutputReportEventListener testOutputReceiver;
63
64 private final boolean briefOrPlainFormat;
65
66 private final StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter;
67
68 private final StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> consoleReporter;
69
70 private final StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> fileReporter;
71
72 private final StatisticsReporter statisticsReporter;
73
74 private final Object lock;
75
76 private final boolean trimStackTrace;
77
78 private final boolean isPlainFormat;
79
80 private final boolean statPerSourceName;
81
82 private Utf8RecodingDeferredFileOutputStream testStdOut = initDeferred("stdout");
83
84 private Utf8RecodingDeferredFileOutputStream testStdErr = initDeferred("stderr");
85
86 @SuppressWarnings("checkstyle:parameternumber")
87 public TestSetRunListener(
88 StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> consoleReporter,
89 StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> fileReporter,
90 StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter,
91 ConsoleOutputReportEventListener testOutputReceiver,
92 StatisticsReporter statisticsReporter,
93 boolean trimStackTrace,
94 boolean isPlainFormat,
95 boolean briefOrPlainFormat,
96 Object lock,
97 boolean statPerSourceName) {
98 this.consoleReporter = consoleReporter;
99 this.fileReporter = fileReporter;
100 this.statisticsReporter = statisticsReporter;
101 this.simpleXMLReporter = simpleXMLReporter;
102 this.testOutputReceiver = testOutputReceiver;
103 this.briefOrPlainFormat = briefOrPlainFormat;
104 this.trimStackTrace = trimStackTrace;
105 this.isPlainFormat = isPlainFormat;
106 this.currentTestSetStats = new TestSetStats(trimStackTrace, isPlainFormat);
107 this.lock = lock;
108 this.statPerSourceName = statPerSourceName;
109 }
110
111 @Override
112 public boolean isDebugEnabled() {
113 return consoleReporter.getConsoleLogger().isDebugEnabled();
114 }
115
116 @Override
117 public void debug(String message) {
118 synchronized (lock) {
119 consoleReporter.getConsoleLogger().debug(trimTrailingNewLine(message));
120 }
121 }
122
123 @Override
124 public boolean isInfoEnabled() {
125 return consoleReporter.getConsoleLogger().isInfoEnabled();
126 }
127
128 @Override
129 public void info(String message) {
130 synchronized (lock) {
131 consoleReporter.getConsoleLogger().info(trimTrailingNewLine(message));
132 }
133 }
134
135 @Override
136 public boolean isWarnEnabled() {
137 return consoleReporter.getConsoleLogger().isWarnEnabled();
138 }
139
140 @Override
141 public void warning(String message) {
142 synchronized (lock) {
143 consoleReporter.getConsoleLogger().warning(trimTrailingNewLine(message));
144 }
145 }
146
147 @Override
148 public boolean isErrorEnabled() {
149 return consoleReporter.getConsoleLogger().isErrorEnabled();
150 }
151
152 @Override
153 public void error(String message) {
154 synchronized (lock) {
155 consoleReporter.getConsoleLogger().error(trimTrailingNewLine(message));
156 }
157 }
158
159 @Override
160 public void error(String message, Throwable t) {
161 synchronized (lock) {
162 consoleReporter.getConsoleLogger().error(trimTrailingNewLine(message), t);
163 }
164 }
165
166 @Override
167 public void error(Throwable t) {
168 synchronized (lock) {
169 consoleReporter.getConsoleLogger().error(t);
170 }
171 }
172
173 @Override
174 public void writeTestOutput(TestOutputReportEntry reportEntry) {
175 try {
176 synchronized (lock) {
177 Utf8RecodingDeferredFileOutputStream stream = reportEntry.isStdOut() ? testStdOut : testStdErr;
178 stream.write(reportEntry.getLog(), reportEntry.isNewLine());
179 testOutputReceiver.writeTestOutput(reportEntry);
180 }
181 } catch (IOException e) {
182 throw new RuntimeException(e);
183 }
184 }
185
186 private TestSetStats getTestSetStats(ReportEntry report) {
187 if (statPerSourceName) {
188 return detailsPerSource.computeIfAbsent(
189 report.getSourceName(), s -> new TestSetStats(trimStackTrace, isPlainFormat));
190 }
191 return currentTestSetStats;
192 }
193
194 @Override
195 public void testSetStarting(TestSetReportEntry report) {
196 getTestSetStats(report).testSetStart();
197 consoleReporter.testSetStarting(report);
198 testOutputReceiver.testSetStarting(report);
199 }
200
201 private void clearCapture() {
202 if (testStdOut != null) {
203 testStdOut.commit();
204 }
205
206 if (testStdErr != null) {
207 testStdErr.commit();
208 }
209
210 testStdOut = initDeferred("stdout");
211 testStdErr = initDeferred("stderr");
212 }
213
214 @Override
215 public void testSetCompleted(TestSetReportEntry report) {
216 final WrappedReportEntry wrap = wrapTestSet(report);
217 TestSetStats testSetStats = getTestSetStats(report);
218 final List<String> testResults = briefOrPlainFormat ? testSetStats.getTestResults() : Collections.emptyList();
219 fileReporter.testSetCompleted(wrap, testSetStats, testResults);
220 simpleXMLReporter.testSetCompleted(wrap, testSetStats);
221 statisticsReporter.testSetCompleted();
222 consoleReporter.testSetCompleted(wrap, testSetStats, testResults);
223 testOutputReceiver.testSetCompleted(wrap);
224 consoleReporter.reset();
225
226 wrap.getStdout().free();
227 wrap.getStdErr().free();
228
229 addTestMethodStats(report);
230 testSetStats.reset();
231 clearCapture();
232 }
233
234
235
236
237
238 @Override
239 public void testStarting(ReportEntry report) {
240 getTestSetStats(report).testStart();
241 }
242
243 @Override
244 public void testSucceeded(ReportEntry reportEntry) {
245 WrappedReportEntry wrapped = wrap(reportEntry, SUCCESS);
246 getTestSetStats(reportEntry).testSucceeded(wrapped);
247 statisticsReporter.testSucceeded(reportEntry);
248 clearCapture();
249 }
250
251 @Override
252 public void testError(ReportEntry reportEntry) {
253 WrappedReportEntry wrapped = wrap(reportEntry, ERROR);
254 getTestSetStats(reportEntry).testError(wrapped);
255 statisticsReporter.testError(reportEntry);
256 clearCapture();
257 }
258
259 @Override
260 public void testFailed(ReportEntry reportEntry) {
261 WrappedReportEntry wrapped = wrap(reportEntry, FAILURE);
262 getTestSetStats(reportEntry).testFailure(wrapped);
263 statisticsReporter.testFailed(reportEntry);
264 clearCapture();
265 }
266
267
268
269
270
271 @Override
272 public void testSkipped(ReportEntry reportEntry) {
273 WrappedReportEntry wrapped = wrap(reportEntry, SKIPPED);
274 getTestSetStats(reportEntry).testSkipped(wrapped);
275 statisticsReporter.testSkipped(reportEntry);
276 clearCapture();
277 }
278
279 @Override
280 public void testExecutionSkippedByUser() {
281 clearCapture();
282 }
283
284 @Override
285 public void testAssumptionFailure(ReportEntry report) {
286 testSkipped(report);
287 }
288
289 private WrappedReportEntry wrap(ReportEntry other, ReportEntryType reportEntryType) {
290 int estimatedElapsed = 0;
291 if (reportEntryType != SKIPPED) {
292 Integer etime = other.getElapsed();
293 estimatedElapsed = etime == null ? getTestSetStats(other).getElapsedSinceLastStart() : etime;
294 }
295
296 return new WrappedReportEntry(other, reportEntryType, estimatedElapsed, testStdOut, testStdErr);
297 }
298
299 private WrappedReportEntry wrapTestSet(TestSetReportEntry other) {
300 return new WrappedReportEntry(
301 other,
302 null,
303 other.getElapsed() != null
304 ? other.getElapsed()
305 : getTestSetStats(other).getElapsedSinceTestSetStart(),
306 testStdOut,
307 testStdErr,
308 other.getSystemProperties());
309 }
310
311 public void close() {
312 testOutputReceiver.close();
313 }
314
315 private void addTestMethodStats(TestSetReportEntry report) {
316 for (WrappedReportEntry reportEntry : getTestSetStats(report).getReportEntries()) {
317 TestMethodStats methodStats = new TestMethodStats(
318 reportEntry.getClassMethodName(),
319 reportEntry.getReportEntryType(),
320 reportEntry.getStackTraceWriter());
321 testMethodStats.add(methodStats);
322 }
323 }
324
325 public Queue<TestMethodStats> getTestMethodStats() {
326 return testMethodStats;
327 }
328
329 private static String trimTrailingNewLine(final String message) {
330 final int e = message == null ? 0 : lineBoundSymbolWidth(message);
331 return message != null && e != 0 ? message.substring(0, message.length() - e) : message;
332 }
333
334 private static int lineBoundSymbolWidth(String message) {
335 return message.endsWith("\r\n") ? 2 : (message.endsWith("\n") || message.endsWith("\r") ? 1 : 0);
336 }
337
338 private static Utf8RecodingDeferredFileOutputStream initDeferred(String channel) {
339 return new Utf8RecodingDeferredFileOutputStream(channel);
340 }
341 }