1 package org.apache.maven.plugin.surefire.booterclient;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import junit.framework.TestCase;
23 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.NotifiableTestStream;
24 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
25 import org.apache.maven.plugin.surefire.extensions.EventConsumerThread;
26 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
27 import org.apache.maven.surefire.api.booter.ForkingRunListener;
28 import org.apache.maven.surefire.api.report.TestOutputReportEntry;
29 import org.apache.maven.surefire.booter.spi.EventChannelEncoder;
30 import org.apache.maven.surefire.api.event.Event;
31 import org.apache.maven.surefire.extensions.EventHandler;
32 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
33 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
34 import org.apache.maven.surefire.api.report.CategorizedReportEntry;
35 import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriter;
36 import org.apache.maven.surefire.api.report.ReportEntry;
37 import org.apache.maven.surefire.api.report.ReporterException;
38 import org.apache.maven.surefire.api.report.RunListener;
39 import org.apache.maven.surefire.api.report.SimpleReportEntry;
40 import org.apache.maven.surefire.api.report.StackTraceWriter;
41 import org.apache.maven.surefire.api.report.TestOutputReceiver;
42 import org.apache.maven.surefire.api.report.TestSetReportEntry;
43 import org.apache.maven.surefire.api.report.TestReportListener;
44 import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
45
46 import javax.annotation.Nonnull;
47 import java.io.ByteArrayInputStream;
48 import java.io.ByteArrayOutputStream;
49 import java.io.Closeable;
50 import java.io.File;
51 import java.io.PrintStream;
52 import java.nio.channels.ReadableByteChannel;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.concurrent.BlockingQueue;
58 import java.util.concurrent.ConcurrentLinkedQueue;
59 import java.util.concurrent.LinkedBlockingQueue;
60 import java.util.concurrent.TimeUnit;
61
62 import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
63 import static org.apache.maven.surefire.api.report.TestOutputReportEntry.stdOut;
64 import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
65 import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
66 import static org.assertj.core.api.Assertions.assertThat;
67 import static org.mockito.Mockito.mock;
68 import static org.mockito.Mockito.verifyZeroInteractions;
69
70
71
72
73 @SuppressWarnings( "checkstyle:magicnumber" )
74 public class ForkingRunListenerTest
75 extends TestCase
76 {
77 private final ByteArrayOutputStream content, anotherContent;
78
79 private final PrintStream printStream, anotherPrintStream;
80
81 public ForkingRunListenerTest()
82 {
83 content = new ByteArrayOutputStream();
84 printStream = new PrintStream( content );
85
86 anotherContent = new ByteArrayOutputStream();
87 anotherPrintStream = new PrintStream( anotherContent );
88 }
89
90 private void reset()
91 {
92 printStream.flush();
93 content.reset();
94 }
95
96 public void testSetStarting() throws Exception
97 {
98 final StandardTestRun standardTestRun = new StandardTestRun();
99 TestSetReportEntry expected = createDefaultReportEntry();
100 standardTestRun.run().testSetStarting( expected );
101 standardTestRun.assertExpected( MockReporter.SET_STARTING, expected );
102 }
103
104 public void testSetCompleted() throws Exception
105 {
106 final StandardTestRun standardTestRun = new StandardTestRun();
107 TestSetReportEntry expected = createDefaultReportEntry();
108 standardTestRun.run().testSetCompleted( expected );
109 standardTestRun.assertExpected( MockReporter.SET_COMPLETED, expected );
110 }
111
112 public void testStarting() throws Exception
113 {
114 final StandardTestRun standardTestRun = new StandardTestRun();
115 ReportEntry expected = createDefaultReportEntry();
116 standardTestRun.run().testStarting( expected );
117 standardTestRun.assertExpected( MockReporter.TEST_STARTING, expected );
118 }
119
120 public void testSucceeded() throws Exception
121 {
122 final StandardTestRun standardTestRun = new StandardTestRun();
123 ReportEntry expected = createDefaultReportEntry();
124 standardTestRun.run().testSucceeded( expected );
125 standardTestRun.assertExpected( MockReporter.TEST_SUCCEEDED, expected );
126 }
127
128 public void testFailed() throws Exception
129 {
130 final StandardTestRun standardTestRun = new StandardTestRun();
131 ReportEntry expected = createReportEntryWithStackTrace();
132 standardTestRun.run().testFailed( expected );
133 standardTestRun.assertExpected( MockReporter.TEST_FAILED, expected );
134 }
135
136 public void testFailedWithCommaInMessage() throws Exception
137 {
138 final StandardTestRun standardTestRun = new StandardTestRun();
139 ReportEntry expected = createReportEntryWithSpecialMessage( "We, the people" );
140 standardTestRun.run().testFailed( expected );
141 standardTestRun.assertExpected( MockReporter.TEST_FAILED, expected );
142 }
143
144 public void testFailedWithUnicodeEscapeInMessage() throws Exception
145 {
146 final StandardTestRun standardTestRun = new StandardTestRun();
147 ReportEntry expected = createReportEntryWithSpecialMessage( "We, \\u0177 people" );
148 standardTestRun.run().testFailed( expected );
149 standardTestRun.assertExpected( MockReporter.TEST_FAILED, expected );
150 }
151
152 public void testFailure() throws Exception
153 {
154 final StandardTestRun standardTestRun = new StandardTestRun();
155 ReportEntry expected = createDefaultReportEntry();
156 standardTestRun.run().testError( expected );
157 standardTestRun.assertExpected( MockReporter.TEST_ERROR, expected );
158 }
159
160 public void testSkipped() throws Exception
161 {
162 final StandardTestRun standardTestRun = new StandardTestRun();
163 ReportEntry expected = createDefaultReportEntry();
164 standardTestRun.run().testSkipped( expected );
165 standardTestRun.assertExpected( MockReporter.TEST_SKIPPED, expected );
166 }
167
168 public void testAssumptionFailure() throws Exception
169 {
170 final StandardTestRun standardTestRun = new StandardTestRun();
171 ReportEntry expected = createDefaultReportEntry();
172 standardTestRun.run().testAssumptionFailure( expected );
173 standardTestRun.assertExpected( MockReporter.TEST_ASSUMPTION_FAIL, expected );
174 }
175
176 public void testConsole() throws Exception
177 {
178 final StandardTestRun standardTestRun = new StandardTestRun();
179 ConsoleLogger directConsoleReporter = standardTestRun.run();
180 directConsoleReporter.info( "HeyYou" );
181 standardTestRun.assertExpected( MockReporter.CONSOLE_INFO, "HeyYou" );
182 }
183
184 public void testConsoleOutput() throws Exception
185 {
186 final StandardTestRun standardTestRun = new StandardTestRun();
187 TestOutputReceiver<TestOutputReportEntry> directConsoleReporter = standardTestRun.run();
188 directConsoleReporter.writeTestOutput( new TestOutputReportEntry( stdOut( "HeyYou" ), NORMAL_RUN, 1L ) );
189 standardTestRun.assertExpected( MockReporter.STDOUT, "HeyYou" );
190 }
191
192 public void testSystemProperties() throws Exception
193 {
194 StandardTestRun standardTestRun = new StandardTestRun();
195 standardTestRun.run();
196
197 reset();
198 createForkingRunListener();
199
200 TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
201 ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
202
203 byte[] cmd = ( ":maven-surefire-event:\u0008:sys-prop:" + (char) 10 + ":normal-run:"
204 + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
205 + "\u0005:UTF-8:"
206 + "\u0000\u0000\u0000\u0002:k1:\u0000\u0000\u0000\u0002:v1:" ).getBytes();
207 for ( Event e : streamToEvent( cmd ) )
208 {
209 forkStreamClient.handleEvent( e );
210 }
211 cmd = ( "\n:maven-surefire-event:\u0008:sys-prop:" + (char) 10 + ":normal-run:"
212 + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
213 + "\u0005:UTF-8:"
214 + "\u0000\u0000\u0000\u0002:k2:\u0000\u0000\u0000\u0002:v2:" ).getBytes();
215 for ( Event e : streamToEvent( cmd ) )
216 {
217 forkStreamClient.handleEvent( e );
218 }
219
220 assertThat( forkStreamClient.getTestVmSystemProperties() )
221 .hasSize( 2 );
222
223 assertThat( forkStreamClient.getTestVmSystemProperties() )
224 .containsEntry( "k1", "v1" );
225
226 assertThat( forkStreamClient.getTestVmSystemProperties() )
227 .containsEntry( "k2", "v2" );
228 }
229
230 public void testMultipleEntries() throws Exception
231 {
232 StandardTestRun standardTestRun = new StandardTestRun();
233 standardTestRun.run();
234
235 reset();
236 RunListener forkingReporter = createForkingRunListener();
237
238 TestSetReportEntry reportEntry = createDefaultReportEntry();
239 forkingReporter.testSetStarting( reportEntry );
240 forkingReporter.testStarting( reportEntry );
241 forkingReporter.testSucceeded( reportEntry );
242 forkingReporter.testSetCompleted( reportEntry );
243
244 TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
245 ForkClient forkStreamClient =
246 new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
247
248 for ( Event e : streamToEvent( content.toByteArray() ) )
249 {
250 forkStreamClient.handleEvent( e );
251 }
252
253 final MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
254 final List<String> events = reporter.getEvents();
255 assertEquals( MockReporter.SET_STARTING, events.get( 0 ) );
256 assertEquals( MockReporter.TEST_STARTING, events.get( 1 ) );
257 assertEquals( MockReporter.TEST_SUCCEEDED, events.get( 2 ) );
258 assertEquals( MockReporter.SET_COMPLETED, events.get( 3 ) );
259 }
260
261 public void test2DifferentChannels()
262 throws Exception
263 {
264 reset();
265 ReportEntry expected = createDefaultReportEntry();
266 SimpleReportEntry secondExpected = createAnotherDefaultReportEntry();
267
268 new ForkingRunListener( new EventChannelEncoder( newBufferedChannel( printStream ) ), false )
269 .testStarting( expected );
270
271 new ForkingRunListener(
272 new EventChannelEncoder( newBufferedChannel( anotherPrintStream ) ), false )
273 .testSkipped( secondExpected );
274
275 TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
276 NotifiableTestStream notifiableTestStream = new MockNotifiableTestStream();
277
278 ForkClient forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, 1 );
279 for ( Event e : streamToEvent( content.toByteArray() ) )
280 {
281 forkStreamClient.handleEvent( e );
282 }
283
284 MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
285 assertThat( reporter.getFirstEvent() ).isEqualTo( MockReporter.TEST_STARTING );
286 assertThat( reporter.getFirstData() ).isEqualTo( expected );
287 assertThat( reporter.getEvents() ).hasSize( 1 );
288
289 forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, 2 );
290 for ( Event e : streamToEvent( anotherContent.toByteArray() ) )
291 {
292 forkStreamClient.handleEvent( e );
293 }
294 MockReporter reporter2 = (MockReporter) forkStreamClient.getReporter();
295 assertThat( reporter2.getFirstEvent() ).isEqualTo( MockReporter.TEST_SKIPPED );
296 assertThat( reporter2.getFirstData() ).isEqualTo( secondExpected );
297 assertThat( reporter2.getEvents() ).hasSize( 1 );
298 }
299
300 private static List<Event> streamToEvent( byte[] stream ) throws Exception
301 {
302 List<Event> events = new ArrayList<>();
303 EH handler = new EH();
304 CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 1 );
305 ConsoleLogger logger = mock( ConsoleLogger.class );
306 ForkNodeArgumentsMock arguments = new ForkNodeArgumentsMock( logger, new File( "" ) );
307 ReadableByteChannel channel = newChannel( new ByteArrayInputStream( stream ) );
308 try ( EventConsumerThread t = new EventConsumerThread( "t", channel, handler, countdown, arguments ) )
309 {
310 t.start();
311 countdown.awaitClosed();
312 verifyZeroInteractions( logger );
313 assertThat( arguments.isCalled() )
314 .isFalse();
315 for ( int i = 0, size = handler.countEventsInCache(); i < size; i++ )
316 {
317 events.add( handler.pullEvent() );
318 }
319 assertEquals( 0, handler.countEventsInCache() );
320 return events;
321 }
322 }
323
324
325
326
327 private static class ForkNodeArgumentsMock implements ForkNodeArguments
328 {
329 private final ConcurrentLinkedQueue<String> dumpStreamText = new ConcurrentLinkedQueue<>();
330 private final ConcurrentLinkedQueue<String> logWarningAtEnd = new ConcurrentLinkedQueue<>();
331 private final ConsoleLogger logger;
332 private final File dumpStreamTextFile;
333
334 ForkNodeArgumentsMock( ConsoleLogger logger, File dumpStreamTextFile )
335 {
336 this.logger = logger;
337 this.dumpStreamTextFile = dumpStreamTextFile;
338 }
339
340 @Nonnull
341 @Override
342 public String getSessionId()
343 {
344 throw new UnsupportedOperationException();
345 }
346
347 @Override
348 public int getForkChannelId()
349 {
350 return 0;
351 }
352
353 @Nonnull
354 @Override
355 public File dumpStreamText( @Nonnull String text )
356 {
357 dumpStreamText.add( text );
358 return dumpStreamTextFile;
359 }
360
361 @Nonnull
362 @Override
363 public File dumpStreamException( @Nonnull Throwable t )
364 {
365 throw new UnsupportedOperationException();
366 }
367
368 @Override
369 public void logWarningAtEnd( @Nonnull String text )
370 {
371 logWarningAtEnd.add( text );
372 }
373
374 @Nonnull
375 @Override
376 public ConsoleLogger getConsoleLogger()
377 {
378 return logger;
379 }
380
381 @Nonnull
382 @Override
383 public Object getConsoleLock()
384 {
385 return logger;
386 }
387
388 boolean isCalled()
389 {
390 return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty();
391 }
392
393 @Override
394 public File getEventStreamBinaryFile()
395 {
396 return null;
397 }
398
399 @Override
400 public File getCommandStreamBinaryFile()
401 {
402 return null;
403 }
404 }
405
406 private static class EH implements EventHandler<Event>
407 {
408 private final BlockingQueue<Event> cache = new LinkedBlockingQueue<>();
409
410 Event pullEvent() throws InterruptedException
411 {
412 return cache.poll( 1, TimeUnit.MINUTES );
413 }
414
415 int countEventsInCache()
416 {
417 return cache.size();
418 }
419
420 @Override
421 public void handleEvent( @Nonnull Event event )
422 {
423 cache.add( event );
424 }
425 }
426
427
428
429 private SimpleReportEntry createDefaultReportEntry( Map<String, String> sysProps )
430 {
431 return new SimpleReportEntry( NORMAL_RUN, 1L,
432 "com.abc.TestClass", null, "testMethod", null, null, 22, sysProps );
433 }
434
435 private SimpleReportEntry createDefaultReportEntry()
436 {
437 return createDefaultReportEntry( Collections.<String, String>emptyMap() );
438 }
439
440 private SimpleReportEntry createAnotherDefaultReportEntry()
441 {
442 return new SimpleReportEntry( NORMAL_RUN, 0L,
443 "com.abc.AnotherTestClass", null, "testAnotherMethod", null, 42 );
444 }
445
446 private SimpleReportEntry createReportEntryWithStackTrace()
447 {
448 try
449 {
450 throw new RuntimeException();
451 }
452 catch ( RuntimeException e )
453 {
454 StackTraceWriter stackTraceWriter =
455 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
456 return new CategorizedReportEntry( NORMAL_RUN, 0L,
457 "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
458 }
459 }
460
461 private SimpleReportEntry createReportEntryWithSpecialMessage( String message )
462 {
463 try
464 {
465 throw new RuntimeException( message );
466 }
467 catch ( RuntimeException e )
468 {
469 StackTraceWriter stackTraceWriter =
470 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
471 return new CategorizedReportEntry( NORMAL_RUN, 1L,
472 "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
473 }
474 }
475
476 private TestReportListener<TestOutputReportEntry> createForkingRunListener()
477 {
478 WritableBufferedByteChannel channel = (WritableBufferedByteChannel) newChannel( printStream );
479 return new ForkingRunListener( new EventChannelEncoder( channel ), false );
480 }
481
482 private class StandardTestRun
483 {
484 private MockReporter reporter;
485
486 public TestReportListener<TestOutputReportEntry> run()
487 throws ReporterException
488 {
489 reset();
490 return createForkingRunListener();
491 }
492
493 public void clientReceiveContent() throws Exception
494 {
495 TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
496 ForkClient handler = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
497 for ( Event e : streamToEvent( content.toByteArray() ) )
498 {
499 handler.handleEvent( e );
500 }
501 reporter = (MockReporter) handler.getReporter();
502 }
503
504 public String getFirstEvent()
505 {
506 return reporter.getEvents().get( 0 );
507 }
508
509 public ReportEntry getFirstData()
510 {
511 return (ReportEntry) reporter.getData().get( 0 );
512 }
513
514 private void assertExpected( String actionCode, ReportEntry expected ) throws Exception
515 {
516 clientReceiveContent();
517 assertEquals( actionCode, getFirstEvent() );
518 final ReportEntry firstData = getFirstData();
519 assertEquals( expected.getSourceName(), firstData.getSourceName() );
520 assertEquals( expected.getName(), firstData.getName() );
521
522 assertEquals( expected.getElapsed(), firstData.getElapsed() );
523 assertEquals( expected.getGroup(), firstData.getGroup() );
524 if ( expected.getStackTraceWriter() != null )
525 {
526
527 assertEquals( expected.getStackTraceWriter().getThrowable().getLocalizedMessage(),
528 firstData.getStackTraceWriter().getThrowable().getLocalizedMessage() );
529 assertEquals( expected.getStackTraceWriter().writeTraceToString(),
530 firstData.getStackTraceWriter().writeTraceToString() );
531 }
532 }
533
534 private void assertExpected( String actionCode, String expected ) throws Exception
535 {
536 clientReceiveContent();
537 assertEquals( actionCode, getFirstEvent() );
538 final String firstData = (String) reporter.getData().get( 0 );
539 assertEquals( expected, firstData );
540 }
541
542 }
543 }