1 package org.apache.maven.plugin.surefire.report;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
23 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
24 import org.apache.maven.plugin.surefire.log.api.Level;
25 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
26 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
27 import org.apache.maven.shared.utils.logging.MessageBuilder;
28 import org.apache.maven.surefire.report.ReporterFactory;
29 import org.apache.maven.surefire.report.RunListener;
30 import org.apache.maven.surefire.report.RunStatistics;
31 import org.apache.maven.surefire.report.StackTraceWriter;
32 import org.apache.maven.surefire.suite.RunResult;
33
34 import java.io.File;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.TreeMap;
41 import java.util.concurrent.ConcurrentLinkedQueue;
42
43 import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
44 import static org.apache.maven.plugin.surefire.report.ConsoleReporter.PLAIN;
45 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.error;
46 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.failure;
47 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.flake;
48 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.skipped;
49 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.success;
50 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType.unknown;
51 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
52 import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
53 import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
54 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
55 import static org.apache.maven.surefire.util.internal.ObjectUtils.useNonNull;
56
57
58
59
60
61
62
63
64 public class DefaultReporterFactory
65 implements ReporterFactory
66 {
67 private final StartupReportConfiguration reportConfiguration;
68 private final ConsoleLogger consoleLogger;
69 private final Collection<TestSetRunListener> listeners;
70
71 private RunStatistics globalStats = new RunStatistics();
72
73
74 private Map<String, List<TestMethodStats>> flakyTests;
75
76
77 private Map<String, List<TestMethodStats>> failedTests;
78
79
80 private Map<String, List<TestMethodStats>> errorTests;
81
82 public DefaultReporterFactory( StartupReportConfiguration reportConfiguration, ConsoleLogger consoleLogger )
83 {
84 this.reportConfiguration = reportConfiguration;
85 this.consoleLogger = consoleLogger;
86 listeners = new ConcurrentLinkedQueue<TestSetRunListener>();
87 }
88
89 public RunListener createReporter()
90 {
91 TestSetRunListener testSetRunListener =
92 new TestSetRunListener( createConsoleReporter(),
93 createFileReporter(),
94 createSimpleXMLReporter(),
95 createConsoleOutputReceiver(),
96 createStatisticsReporter(),
97 reportConfiguration.isTrimStackTrace(),
98 PLAIN.equals( reportConfiguration.getReportFormat() ),
99 reportConfiguration.isBriefOrPlainFormat() );
100 addListener( testSetRunListener );
101 return testSetRunListener;
102 }
103
104 public File getReportsDirectory()
105 {
106 return reportConfiguration.getReportsDirectory();
107 }
108
109 private ConsoleReporter createConsoleReporter()
110 {
111 return shouldReportToConsole() ? new ConsoleReporter( consoleLogger ) : NullConsoleReporter.INSTANCE;
112 }
113
114 private FileReporter createFileReporter()
115 {
116 final FileReporter fileReporter = reportConfiguration.instantiateFileReporter();
117 return useNonNull( fileReporter, NullFileReporter.INSTANCE );
118 }
119
120 private StatelessXmlReporter createSimpleXMLReporter()
121 {
122 final StatelessXmlReporter xmlReporter = reportConfiguration.instantiateStatelessXmlReporter();
123 return useNonNull( xmlReporter, NullStatelessXmlReporter.INSTANCE );
124 }
125
126 private TestcycleConsoleOutputReceiver createConsoleOutputReceiver()
127 {
128 final TestcycleConsoleOutputReceiver consoleOutputReceiver =
129 reportConfiguration.instantiateConsoleOutputFileReporter();
130 return useNonNull( consoleOutputReceiver, NullConsoleOutputReceiver.INSTANCE );
131 }
132
133 private StatisticsReporter createStatisticsReporter()
134 {
135 final StatisticsReporter statisticsReporter = reportConfiguration.getStatisticsReporter();
136 return useNonNull( statisticsReporter, NullStatisticsReporter.INSTANCE );
137 }
138
139 private boolean shouldReportToConsole()
140 {
141 return reportConfiguration.isUseFile()
142 ? reportConfiguration.isPrintSummary()
143 : reportConfiguration.isRedirectTestOutputToFile() || reportConfiguration.isBriefOrPlainFormat();
144 }
145
146 public void mergeFromOtherFactories( Collection<DefaultReporterFactory> factories )
147 {
148 for ( DefaultReporterFactory factory : factories )
149 {
150 for ( TestSetRunListener listener : factory.listeners )
151 {
152 listeners.add( listener );
153 }
154 }
155 }
156
157 final void addListener( TestSetRunListener listener )
158 {
159 listeners.add( listener );
160 }
161
162 public RunResult close()
163 {
164 mergeTestHistoryResult();
165 runCompleted();
166 for ( TestSetRunListener listener : listeners )
167 {
168 listener.close();
169 }
170 return globalStats.getRunResult();
171 }
172
173 public void runStarting()
174 {
175 log( "" );
176 log( "-------------------------------------------------------" );
177 log( " T E S T S" );
178 log( "-------------------------------------------------------" );
179 }
180
181 private void runCompleted()
182 {
183 if ( reportConfiguration.isPrintSummary() )
184 {
185 log( "" );
186 log( "Results:" );
187 log( "" );
188 }
189 boolean printedFailures = printTestFailures( failure );
190 boolean printedErrors = printTestFailures( error );
191 boolean printedFlakes = printTestFailures( flake );
192 if ( printedFailures | printedErrors | printedFlakes )
193 {
194 log( "" );
195 }
196 boolean hasSuccessful = globalStats.getCompletedCount() > 0;
197 boolean hasSkipped = globalStats.getSkipped() > 0;
198 log( globalStats.getSummary(), hasSuccessful, printedFailures, printedErrors, hasSkipped, printedFlakes );
199 log( "" );
200 }
201
202 public RunStatistics getGlobalRunStatistics()
203 {
204 mergeTestHistoryResult();
205 return globalStats;
206 }
207
208
209
210
211 public static DefaultReporterFactory defaultNoXml()
212 {
213 return new DefaultReporterFactory( StartupReportConfiguration.defaultNoXml(), new NullConsoleLogger() );
214 }
215
216
217
218
219
220
221
222
223
224
225 static TestResultType getTestResultType( List<ReportEntryType> reportEntries, int rerunFailingTestsCount )
226 {
227 if ( reportEntries == null || reportEntries.isEmpty() )
228 {
229 return unknown;
230 }
231
232 boolean seenSuccess = false, seenFailure = false, seenError = false;
233 for ( ReportEntryType resultType : reportEntries )
234 {
235 if ( resultType == SUCCESS )
236 {
237 seenSuccess = true;
238 }
239 else if ( resultType == FAILURE )
240 {
241 seenFailure = true;
242 }
243 else if ( resultType == ERROR )
244 {
245 seenError = true;
246 }
247 }
248
249 if ( seenFailure || seenError )
250 {
251 if ( seenSuccess && rerunFailingTestsCount > 0 )
252 {
253 return flake;
254 }
255 else
256 {
257 if ( seenError )
258 {
259 return error;
260 }
261 else
262 {
263 return failure;
264 }
265 }
266 }
267 else if ( seenSuccess )
268 {
269 return success;
270 }
271 else
272 {
273 return skipped;
274 }
275 }
276
277
278
279
280
281 void mergeTestHistoryResult()
282 {
283 globalStats = new RunStatistics();
284 flakyTests = new TreeMap<String, List<TestMethodStats>>();
285 failedTests = new TreeMap<String, List<TestMethodStats>>();
286 errorTests = new TreeMap<String, List<TestMethodStats>>();
287
288 Map<String, List<TestMethodStats>> mergedTestHistoryResult = new HashMap<String, List<TestMethodStats>>();
289
290 for ( TestSetRunListener listener : listeners )
291 {
292 List<TestMethodStats> testMethodStats = listener.getTestMethodStats();
293 for ( TestMethodStats methodStats : testMethodStats )
294 {
295 List<TestMethodStats> currentMethodStats =
296 mergedTestHistoryResult.get( methodStats.getTestClassMethodName() );
297 if ( currentMethodStats == null )
298 {
299 currentMethodStats = new ArrayList<TestMethodStats>();
300 currentMethodStats.add( methodStats );
301 mergedTestHistoryResult.put( methodStats.getTestClassMethodName(), currentMethodStats );
302 }
303 else
304 {
305 currentMethodStats.add( methodStats );
306 }
307 }
308 }
309
310
311 int completedCount = 0, skipped = 0;
312
313 for ( Map.Entry<String, List<TestMethodStats>> entry : mergedTestHistoryResult.entrySet() )
314 {
315 List<TestMethodStats> testMethodStats = entry.getValue();
316 String testClassMethodName = entry.getKey();
317 completedCount++;
318
319 List<ReportEntryType> resultTypes = new ArrayList<ReportEntryType>();
320 for ( TestMethodStats methodStats : testMethodStats )
321 {
322 resultTypes.add( methodStats.getResultType() );
323 }
324
325 switch ( getTestResultType( resultTypes, reportConfiguration.getRerunFailingTestsCount() ) )
326 {
327 case success:
328
329 int successCount = 0;
330 for ( ReportEntryType type : resultTypes )
331 {
332 if ( type == SUCCESS )
333 {
334 successCount++;
335 }
336 }
337 completedCount += successCount - 1;
338 break;
339 case skipped:
340 skipped++;
341 break;
342 case flake:
343 flakyTests.put( testClassMethodName, testMethodStats );
344 break;
345 case failure:
346 failedTests.put( testClassMethodName, testMethodStats );
347 break;
348 case error:
349 errorTests.put( testClassMethodName, testMethodStats );
350 break;
351 default:
352 throw new IllegalStateException( "Get unknown test result type" );
353 }
354 }
355
356 globalStats.set( completedCount, errorTests.size(), failedTests.size(), skipped, flakyTests.size() );
357 }
358
359
360
361
362
363
364
365
366
367 boolean printTestFailures( TestResultType type )
368 {
369 final Map<String, List<TestMethodStats>> testStats;
370 final Level level;
371 switch ( type )
372 {
373 case failure:
374 testStats = failedTests;
375 level = Level.FAILURE;
376 break;
377 case error:
378 testStats = errorTests;
379 level = Level.FAILURE;
380 break;
381 case flake:
382 testStats = flakyTests;
383 level = Level.UNSTABLE;
384 break;
385 default:
386 return false;
387 }
388
389 boolean printed = false;
390 if ( !testStats.isEmpty() )
391 {
392 log( type.getLogPrefix(), level );
393 printed = true;
394 }
395
396 for ( Map.Entry<String, List<TestMethodStats>> entry : testStats.entrySet() )
397 {
398 printed = true;
399 List<TestMethodStats> testMethodStats = entry.getValue();
400 if ( testMethodStats.size() == 1 )
401 {
402
403 failure( " " + testMethodStats.get( 0 ).getStackTraceWriter().smartTrimmedStackTrace() );
404 }
405 else
406 {
407 log( entry.getKey(), level );
408 for ( int i = 0; i < testMethodStats.size(); i++ )
409 {
410 StackTraceWriter failureStackTrace = testMethodStats.get( i ).getStackTraceWriter();
411 if ( failureStackTrace == null )
412 {
413 success( " Run " + ( i + 1 ) + ": PASS" );
414 }
415 else
416 {
417 failure( " Run " + ( i + 1 ) + ": " + failureStackTrace.smartTrimmedStackTrace() );
418 }
419 }
420 log( "" );
421 }
422 }
423 return printed;
424 }
425
426
427 enum TestResultType
428 {
429
430 error( "Errors: " ),
431 failure( "Failures: " ),
432 flake( "Flakes: " ),
433 success( "Success: " ),
434 skipped( "Skipped: " ),
435 unknown( "Unknown: " );
436
437 private final String logPrefix;
438
439 TestResultType( String logPrefix )
440 {
441 this.logPrefix = logPrefix;
442 }
443
444 public String getLogPrefix()
445 {
446 return logPrefix;
447 }
448 }
449
450 private void log( String s, boolean success, boolean failures, boolean errors, boolean skipped, boolean flakes )
451 {
452 Level level = resolveLevel( success, failures, errors, skipped, flakes );
453 log( s, level );
454 }
455
456 private void log( String s, Level level )
457 {
458 MessageBuilder builder = buffer();
459 switch ( level )
460 {
461 case FAILURE:
462 consoleLogger.error( builder.failure( s ).toString() );
463 break;
464 case UNSTABLE:
465 consoleLogger.warning( builder.warning( s ).toString() );
466 break;
467 case SUCCESS:
468 consoleLogger.info( builder.success( s ).toString() );
469 break;
470 default:
471 consoleLogger.info( builder.a( s ).toString() );
472 }
473 }
474
475 private void log( String s )
476 {
477 consoleLogger.info( s );
478 }
479
480 private void info( String s )
481 {
482 MessageBuilder builder = buffer();
483 consoleLogger.info( builder.info( s ).toString() );
484 }
485
486 private void err( String s )
487 {
488 MessageBuilder builder = buffer();
489 consoleLogger.error( builder.error( s ).toString() );
490 }
491
492 private void success( String s )
493 {
494 MessageBuilder builder = buffer();
495 consoleLogger.info( builder.success( s ).toString() );
496 }
497
498 private void failure( String s )
499 {
500 MessageBuilder builder = buffer();
501 consoleLogger.error( builder.failure( s ).toString() );
502 }
503 }