View Javadoc
1   package org.apache.maven.plugin.surefire.extensions;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.plugin.surefire.booterclient.output.DeserializedStacktraceWriter;
23  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessEventListener;
24  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessEventNotifier;
25  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessExitErrorListener;
26  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessPropertyEventListener;
27  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessReportEventListener;
28  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessStackTraceEventListener;
29  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessStandardOutErrEventListener;
30  import org.apache.maven.plugin.surefire.booterclient.output.ForkedProcessStringEventListener;
31  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
32  import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerUtils;
33  import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
34  import org.apache.maven.surefire.api.event.Event;
35  import org.apache.maven.surefire.extensions.EventHandler;
36  import org.apache.maven.surefire.extensions.ForkNodeArguments;
37  import org.apache.maven.surefire.extensions.util.CountdownCloseable;
38  import org.apache.maven.surefire.api.report.ReportEntry;
39  import org.apache.maven.surefire.api.report.RunMode;
40  import org.apache.maven.surefire.api.report.SafeThrowable;
41  import org.apache.maven.surefire.api.report.StackTraceWriter;
42  import org.apache.maven.surefire.api.util.internal.ObjectUtils;
43  import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
44  import org.junit.Rule;
45  import org.junit.Test;
46  import org.junit.experimental.runners.Enclosed;
47  import org.junit.experimental.theories.DataPoints;
48  import org.junit.experimental.theories.FromDataPoints;
49  import org.junit.experimental.theories.Theories;
50  import org.junit.experimental.theories.Theory;
51  import org.junit.rules.ExpectedException;
52  import org.junit.runner.RunWith;
53  import org.mockito.ArgumentCaptor;
54  import org.powermock.core.classloader.annotations.PowerMockIgnore;
55  import org.powermock.modules.junit4.PowerMockRunner;
56  
57  import javax.annotation.Nonnull;
58  import java.io.ByteArrayInputStream;
59  import java.io.ByteArrayOutputStream;
60  import java.io.Closeable;
61  import java.io.File;
62  import java.io.LineNumberReader;
63  import java.io.PrintStream;
64  import java.io.StringReader;
65  import java.nio.ByteBuffer;
66  import java.nio.channels.ReadableByteChannel;
67  import java.nio.charset.Charset;
68  import java.util.Map;
69  import java.util.concurrent.BlockingQueue;
70  import java.util.concurrent.LinkedTransferQueue;
71  import java.util.concurrent.TimeUnit;
72  import java.util.concurrent.atomic.AtomicBoolean;
73  import java.util.concurrent.atomic.AtomicInteger;
74  
75  import static java.lang.String.format;
76  import static java.nio.charset.StandardCharsets.UTF_8;
77  import static java.util.Arrays.copyOfRange;
78  import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
79  import static org.apache.maven.surefire.shared.codec.binary.Base64.encodeBase64String;
80  import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
81  import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
82  import static org.fest.assertions.Assertions.assertThat;
83  import static org.fest.assertions.Index.atIndex;
84  import static org.junit.Assert.assertTrue;
85  import static org.junit.rules.ExpectedException.none;
86  import static org.mockito.ArgumentMatchers.anyString;
87  import static org.mockito.Mockito.mock;
88  import static org.mockito.Mockito.times;
89  import static org.mockito.Mockito.verify;
90  import static org.mockito.Mockito.when;
91  
92  /**
93   * Test for {@link ForkedProcessEventNotifier}.
94   *
95   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
96   * @since 3.0.0-M4
97   */
98  @RunWith( Enclosed.class )
99  public class ForkedProcessEventNotifierTest
100 {
101     /**
102      *
103      */
104     @RunWith( PowerMockRunner.class )
105     @PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
106     public static class DecoderOperationsTest
107     {
108         @Rule
109         public final ExpectedException rule = none();
110 
111         @Test
112         public void shouldBeFailSafe()
113         {
114             assertThat( EventConsumerThread.decode( null, UTF_8 ) ).isNull();
115             assertThat( EventConsumerThread.decode( "-", UTF_8 ) ).isNull();
116             assertThat( EventConsumerThread.decodeToInteger( null ) ).isNull();
117             assertThat( EventConsumerThread.decodeToInteger( "-" ) ).isNull();
118         }
119 
120         @Test
121         public void shouldHaveSystemProperty() throws Exception
122         {
123             final Stream out = Stream.newStream();
124             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
125             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
126             Map<String, String> props = ObjectUtils.systemProps();
127             encoder.sendSystemProperties( props );
128             wChannel.close();
129 
130             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
131             PropertyEventAssertionListener listener = new PropertyEventAssertionListener();
132             notifier.setSystemPropertiesListener( listener );
133 
134             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
135 
136             EH eventHandler = new EH();
137             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
138             ConsoleLogger logger = mock( ConsoleLogger.class );
139             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
140             when( arguments.getConsoleLogger() ).thenReturn( logger );
141             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
142             {
143                 t.start();
144                 for ( int i = 0; i < props.size(); i++ )
145                 {
146                     notifier.notifyEvent( eventHandler.pullEvent() );
147                 }
148             }
149 
150             assertThat( listener.counter.get() )
151                 .isEqualTo( props.size() );
152         }
153 
154         @Test
155         public void shouldRecognizeEmptyStream4ReportEntry()
156         {
157             ReportEntry reportEntry = EventConsumerThread.decodeReportEntry( null, null, null, "", "", null, null, "",
158                     "", "", null );
159             assertThat( reportEntry ).isNull();
160 
161             reportEntry = EventConsumerThread.decodeReportEntry( UTF_8, "", "", "", "", "", "", "-", "", "", "" );
162             assertThat( reportEntry ).isNotNull();
163             assertThat( reportEntry.getStackTraceWriter() ).isNotNull();
164             assertThat( reportEntry.getStackTraceWriter().smartTrimmedStackTrace() ).isEmpty();
165             assertThat( reportEntry.getStackTraceWriter().writeTraceToString() ).isEmpty();
166             assertThat( reportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEmpty();
167             assertThat( reportEntry.getSourceName() ).isEmpty();
168             assertThat( reportEntry.getSourceText() ).isEmpty();
169             assertThat( reportEntry.getName() ).isEmpty();
170             assertThat( reportEntry.getNameText() ).isEmpty();
171             assertThat( reportEntry.getGroup() ).isEmpty();
172             assertThat( reportEntry.getNameWithGroup() ).isEmpty();
173             assertThat( reportEntry.getMessage() ).isEmpty();
174             assertThat( reportEntry.getElapsed() ).isNull();
175 
176             rule.expect( NumberFormatException.class );
177             EventConsumerThread.decodeReportEntry( UTF_8, "", "", "", "", "", "", "", "", "", "" );
178         }
179 
180         @Test
181         @SuppressWarnings( "checkstyle:magicnumber" )
182         public void testCreatingReportEntry()
183         {
184             final String exceptionMessage = "msg";
185             final String encodedExceptionMsg = encodeBase64String( toArray( UTF_8.encode( exceptionMessage ) ) );
186 
187             final String smartStackTrace = "MyTest:86 >> Error";
188             final String encodedSmartStackTrace = encodeBase64String( toArray( UTF_8.encode( smartStackTrace ) ) );
189 
190             final String stackTrace = "Exception: msg\ntrace line 1\ntrace line 2";
191             final String encodedStackTrace = encodeBase64String( toArray( UTF_8.encode( stackTrace ) ) );
192 
193             final String trimmedStackTrace = "trace line 1\ntrace line 2";
194             final String encodedTrimmedStackTrace = encodeBase64String( toArray( UTF_8.encode( trimmedStackTrace ) ) );
195 
196             SafeThrowable safeThrowable = new SafeThrowable( exceptionMessage );
197             StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
198             when( stackTraceWriter.getThrowable() ).thenReturn( safeThrowable );
199             when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( smartStackTrace );
200             when( stackTraceWriter.writeTrimmedTraceToString() ).thenReturn( trimmedStackTrace );
201             when( stackTraceWriter.writeTraceToString() ).thenReturn( stackTrace );
202 
203             ReportEntry reportEntry = mock( ReportEntry.class );
204             when( reportEntry.getElapsed() ).thenReturn( 102 );
205             when( reportEntry.getGroup() ).thenReturn( "this group" );
206             when( reportEntry.getMessage() ).thenReturn( "skipped test" );
207             when( reportEntry.getName() ).thenReturn( "my test" );
208             when( reportEntry.getNameText() ).thenReturn( "my display name" );
209             when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
210             when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
211             when( reportEntry.getSourceText() ).thenReturn( "test class display name" );
212             when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
213 
214             String encodedSourceName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceName() ) ) );
215             String encodedSourceText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getSourceText() ) ) );
216             String encodedName = encodeBase64String( toArray( UTF_8.encode( reportEntry.getName() ) ) );
217             String encodedText = encodeBase64String( toArray( UTF_8.encode( reportEntry.getNameText() ) ) );
218             String encodedGroup = encodeBase64String( toArray( UTF_8.encode( reportEntry.getGroup() ) ) );
219             String encodedMessage = encodeBase64String( toArray( UTF_8.encode( reportEntry.getMessage() ) ) );
220 
221             ReportEntry decodedReportEntry = EventConsumerThread.decodeReportEntry( UTF_8, encodedSourceName,
222                 encodedSourceText, encodedName, encodedText, encodedGroup, encodedMessage, "-", null, null, null );
223 
224             assertThat( decodedReportEntry ).isNotNull();
225             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
226             assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
227             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
228             assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
229             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
230             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
231             assertThat( decodedReportEntry.getStackTraceWriter() ).isNull();
232 
233             decodedReportEntry = EventConsumerThread.decodeReportEntry( UTF_8, encodedSourceName, encodedSourceText,
234                 encodedName, encodedText, encodedGroup, encodedMessage, "-", encodedExceptionMsg,
235                 encodedSmartStackTrace, null );
236 
237             assertThat( decodedReportEntry ).isNotNull();
238             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
239             assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
240             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
241             assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
242             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
243             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
244             assertThat( decodedReportEntry.getElapsed() ).isNull();
245             assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
246             assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
247                 .isEqualTo( exceptionMessage );
248             assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
249                 .isEqualTo( smartStackTrace );
250             assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
251                 .isNull();
252 
253             decodedReportEntry = EventConsumerThread.decodeReportEntry( UTF_8, encodedSourceName, encodedSourceText,
254                 encodedName, encodedText, encodedGroup, encodedMessage, "1003", encodedExceptionMsg,
255                 encodedSmartStackTrace, null );
256 
257             assertThat( decodedReportEntry ).isNotNull();
258             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
259             assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
260             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
261             assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
262             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
263             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
264             assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
265             assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
266             assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
267                 .isEqualTo( exceptionMessage );
268             assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
269                 .isEqualTo( smartStackTrace );
270             assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() )
271                 .isNull();
272 
273             decodedReportEntry = EventConsumerThread.decodeReportEntry( UTF_8, encodedSourceName, encodedSourceText,
274                 encodedName, encodedText, encodedGroup, encodedMessage, "1003", encodedExceptionMsg,
275                 encodedSmartStackTrace, encodedStackTrace );
276 
277             assertThat( decodedReportEntry ).isNotNull();
278             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
279             assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
280             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
281             assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
282             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
283             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
284             assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
285             assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
286             assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() ).isNotNull();
287             assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
288                     .isEqualTo( exceptionMessage );
289             assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
290                     .isEqualTo( smartStackTrace );
291             assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( stackTrace );
292             assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() ).isEqualTo( stackTrace );
293 
294             decodedReportEntry = EventConsumerThread.decodeReportEntry( UTF_8, encodedSourceName, encodedSourceText,
295                 encodedName, encodedText, encodedGroup, encodedMessage, "1003", encodedExceptionMsg,
296                 encodedSmartStackTrace, encodedTrimmedStackTrace );
297 
298             assertThat( decodedReportEntry ).isNotNull();
299             assertThat( decodedReportEntry.getSourceName() ).isEqualTo( reportEntry.getSourceName() );
300             assertThat( decodedReportEntry.getSourceText() ).isEqualTo( reportEntry.getSourceText() );
301             assertThat( decodedReportEntry.getName() ).isEqualTo( reportEntry.getName() );
302             assertThat( decodedReportEntry.getNameText() ).isEqualTo( reportEntry.getNameText() );
303             assertThat( decodedReportEntry.getGroup() ).isEqualTo( reportEntry.getGroup() );
304             assertThat( decodedReportEntry.getMessage() ).isEqualTo( reportEntry.getMessage() );
305             assertThat( decodedReportEntry.getElapsed() ).isEqualTo( 1003 );
306             assertThat( decodedReportEntry.getStackTraceWriter() ).isNotNull();
307             assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() ).isNotNull();
308             assertThat( decodedReportEntry.getStackTraceWriter().getThrowable().getMessage() )
309                     .isEqualTo( exceptionMessage );
310             assertThat( decodedReportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
311                     .isEqualTo( smartStackTrace );
312             assertThat( decodedReportEntry.getStackTraceWriter().writeTraceToString() ).isEqualTo( trimmedStackTrace );
313             assertThat( decodedReportEntry.getStackTraceWriter().writeTrimmedTraceToString() )
314                     .isEqualTo( trimmedStackTrace );
315         }
316 
317         @Test
318         public void shouldSendByeEvent() throws Exception
319         {
320             Stream out = Stream.newStream();
321             LegacyMasterProcessChannelEncoder encoder =
322                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
323             encoder.bye();
324             String read = new String( out.toByteArray(), UTF_8 );
325 
326             assertThat( read )
327                     .isEqualTo( ":maven-surefire-event:bye:\n" );
328 
329             LineNumberReader lines = out.newReader( UTF_8 );
330 
331             final String cmd = lines.readLine();
332             assertThat( cmd )
333                     .isNotNull();
334 
335             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
336 
337             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
338             EventAssertionListener listener = new EventAssertionListener();
339             notifier.setByeListener( listener );
340 
341             EH eventHandler = new EH();
342             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
343             ConsoleLogger logger = mock( ConsoleLogger.class );
344             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
345             when( arguments.getConsoleLogger() ).thenReturn( logger );
346             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
347             {
348                 t.start();
349                 notifier.notifyEvent( eventHandler.pullEvent() );
350             }
351 
352             assertThat( listener.called.get() )
353                 .isTrue();
354         }
355 
356         @Test
357         public void shouldSendStopOnNextTestEvent() throws Exception
358         {
359             Stream out = Stream.newStream();
360             LegacyMasterProcessChannelEncoder encoder =
361                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
362             encoder.stopOnNextTest();
363             String read = new String( out.toByteArray(), UTF_8 );
364 
365             assertThat( read )
366                     .isEqualTo( ":maven-surefire-event:stop-on-next-test:\n" );
367 
368             LineNumberReader lines = out.newReader( UTF_8 );
369 
370             final String cmd = lines.readLine();
371             assertThat( cmd )
372                 .isNotNull();
373 
374             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
375 
376             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
377             EventAssertionListener listener = new EventAssertionListener();
378             notifier.setStopOnNextTestListener( listener );
379 
380             EH eventHandler = new EH();
381             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
382             ConsoleLogger logger = mock( ConsoleLogger.class );
383             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
384             when( arguments.getConsoleLogger() ).thenReturn( logger );
385             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
386             {
387                 t.start();
388                 notifier.notifyEvent( eventHandler.pullEvent() );
389             }
390 
391             assertThat( listener.called.get() )
392                 .isTrue();
393         }
394 
395         @Test
396         public void shouldCorrectlyDecodeStackTracesWithEmptyStringTraceMessages() throws Exception
397         {
398             String exceptionMessage = "";
399             String smartStackTrace = "JUnit5Test.failWithEmptyString:16";
400             String exceptionStackTrace = "org.opentest4j.AssertionFailedError: \n"
401                     + "\tat JUnit5Test.failWithEmptyString(JUnit5Test.java:16)\n";
402 
403             StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
404             SafeThrowable safeThrowable = new SafeThrowable( exceptionMessage );
405             when( stackTraceWriter.getThrowable() ).thenReturn( safeThrowable );
406             when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( smartStackTrace );
407             when( stackTraceWriter.writeTrimmedTraceToString() ).thenReturn( exceptionStackTrace );
408             when( stackTraceWriter.writeTraceToString() ).thenReturn( exceptionStackTrace );
409 
410             ReportEntry reportEntry = mock( ReportEntry.class );
411             when( reportEntry.getElapsed() ).thenReturn( 7 );
412             when( reportEntry.getGroup() ).thenReturn( null );
413             when( reportEntry.getMessage() ).thenReturn( null );
414             when( reportEntry.getName() ).thenReturn( "failWithEmptyString" );
415             when( reportEntry.getNameWithGroup() ).thenReturn( "JUnit5Test" );
416             when( reportEntry.getSourceName() ).thenReturn( "JUnit5Test" );
417             when( reportEntry.getSourceText() ).thenReturn( null );
418             when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
419 
420             final Stream out = Stream.newStream();
421             LegacyMasterProcessChannelEncoder encoder =
422                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
423             encoder.testFailed( reportEntry, true );
424 
425             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
426 
427             final ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
428             ReportEventAssertionListener listener = new ReportEventAssertionListener( reportEntry, true );
429             notifier.setTestFailedListener( listener );
430 
431             EH eventHandler = new EH();
432             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
433             ConsoleLogger logger = mock( ConsoleLogger.class );
434             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
435             when( arguments.getConsoleLogger() ).thenReturn( logger );
436             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
437             {
438                 t.start();
439                 notifier.notifyEvent( eventHandler.pullEvent() );
440             }
441 
442             assertThat( listener.called.get() )
443                 .isTrue();
444         }
445 
446         @Test
447         public void shouldSendNextTestEvent() throws Exception
448         {
449             final Stream out = Stream.newStream();
450             LegacyMasterProcessChannelEncoder encoder =
451                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
452             encoder.acquireNextTest();
453             String read = new String( out.toByteArray(), UTF_8 );
454 
455             assertThat( read )
456                     .isEqualTo( ":maven-surefire-event:next-test:\n" );
457 
458             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
459 
460             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
461             EventAssertionListener listener = new EventAssertionListener();
462             notifier.setAcquireNextTestListener( listener );
463 
464             EH eventHandler = new EH();
465             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
466             ConsoleLogger logger = mock( ConsoleLogger.class );
467             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
468             when( arguments.getConsoleLogger() ).thenReturn( logger );
469             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
470             {
471                 t.start();
472                 notifier.notifyEvent( eventHandler.pullEvent() );
473             }
474 
475             assertThat( listener.called.get() )
476                 .isTrue();
477         }
478 
479         @Test
480         public void testConsole() throws Exception
481         {
482             final Stream out = Stream.newStream();
483             LegacyMasterProcessChannelEncoder encoder =
484                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
485             encoder.consoleInfoLog( "msg" );
486 
487             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
488 
489             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
490             StringEventAssertionListener listener = new StringEventAssertionListener( "msg" );
491             notifier.setConsoleInfoListener( listener );
492 
493             EH eventHandler = new EH();
494             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
495             ConsoleLogger logger = mock( ConsoleLogger.class );
496             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
497             when( arguments.getConsoleLogger() ).thenReturn( logger );
498             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
499             {
500                 t.start();
501                 notifier.notifyEvent( eventHandler.pullEvent() );
502             }
503 
504             assertThat( listener.called.get() )
505                 .isTrue();
506         }
507 
508         @Test
509         public void testError() throws Exception
510         {
511             final Stream out = Stream.newStream();
512             LegacyMasterProcessChannelEncoder encoder =
513                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
514             encoder.consoleErrorLog( "msg" );
515 
516             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
517 
518             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
519             StackTraceEventListener listener = new StackTraceEventListener( "msg", null, null );
520             notifier.setConsoleErrorListener( listener );
521 
522             EH eventHandler = new EH();
523             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
524             ConsoleLogger logger = mock( ConsoleLogger.class );
525             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
526             when( arguments.getConsoleLogger() ).thenReturn( logger );
527             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
528             {
529                 t.start();
530                 notifier.notifyEvent( eventHandler.pullEvent() );
531             }
532 
533             assertThat( listener.called.get() )
534                 .isTrue();
535         }
536 
537         @Test
538         public void testErrorWithException() throws Exception
539         {
540             final Stream out = Stream.newStream();
541             LegacyMasterProcessChannelEncoder encoder =
542                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
543             Throwable throwable = new Throwable( "msg" );
544             encoder.consoleErrorLog( throwable );
545 
546             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
547 
548             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
549             String stackTrace = ConsoleLoggerUtils.toString( throwable );
550             StackTraceEventListener listener = new StackTraceEventListener( "msg", null, stackTrace );
551             notifier.setConsoleErrorListener( listener );
552 
553             EH eventHandler = new EH();
554             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
555             ConsoleLogger logger = mock( ConsoleLogger.class );
556             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
557             when( arguments.getConsoleLogger() ).thenReturn( logger );
558             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
559             {
560                 t.start();
561                 notifier.notifyEvent( eventHandler.pullEvent() );
562             }
563 
564             assertThat( listener.called.get() )
565                 .isTrue();
566         }
567 
568         @Test
569         public void testErrorWithStackTraceWriter() throws Exception
570         {
571             final Stream out = Stream.newStream();
572 
573             LegacyMasterProcessChannelEncoder encoder =
574                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
575             StackTraceWriter stackTraceWriter = new DeserializedStacktraceWriter( "1", "2", "3" );
576             encoder.consoleErrorLog( stackTraceWriter, false );
577 
578             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
579 
580             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
581             StackTraceEventListener listener = new StackTraceEventListener( "1", "2", "3" );
582             notifier.setConsoleErrorListener( listener );
583 
584             EH eventHandler = new EH();
585             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
586             ConsoleLogger logger = mock( ConsoleLogger.class );
587             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
588             when( arguments.getConsoleLogger() ).thenReturn( logger );
589             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
590             {
591                 t.start();
592                 notifier.notifyEvent( eventHandler.pullEvent() );
593             }
594 
595             assertThat( listener.called.get() )
596                 .isTrue();
597         }
598 
599         @Test
600         public void testDebug() throws Exception
601         {
602             final Stream out = Stream.newStream();
603 
604             LegacyMasterProcessChannelEncoder encoder =
605                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
606             encoder.consoleDebugLog( "msg" );
607 
608             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
609 
610             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
611             StringEventAssertionListener listener = new StringEventAssertionListener( "msg" );
612             notifier.setConsoleDebugListener( listener );
613 
614             EH eventHandler = new EH();
615             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
616             ConsoleLogger logger = mock( ConsoleLogger.class );
617             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
618             when( arguments.getConsoleLogger() ).thenReturn( logger );
619             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
620             {
621                 t.start();
622                 notifier.notifyEvent( eventHandler.pullEvent() );
623             }
624 
625             assertThat( listener.called.get() )
626                 .isTrue();
627 
628             assertThat( listener.msg )
629                 .isEqualTo( "msg" );
630         }
631 
632         @Test
633         public void testWarning() throws Exception
634         {
635             final Stream out = Stream.newStream();
636 
637             LegacyMasterProcessChannelEncoder encoder =
638                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
639             encoder.consoleWarningLog( "msg" );
640 
641             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
642 
643             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
644             StringEventAssertionListener listener = new StringEventAssertionListener( "msg" );
645             notifier.setConsoleWarningListener( listener );
646 
647             EH eventHandler = new EH();
648             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
649             ConsoleLogger logger = mock( ConsoleLogger.class );
650             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
651             when( arguments.getConsoleLogger() ).thenReturn( logger );
652             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
653             {
654                 t.start();
655                 notifier.notifyEvent( eventHandler.pullEvent() );
656             }
657 
658             assertThat( listener.called.get() )
659                 .isTrue();
660         }
661 
662         @Test
663         public void testStdOutStream() throws Exception
664         {
665             final Stream out = Stream.newStream();
666             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
667             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
668             encoder.stdOut( "msg", false );
669             wChannel.close();
670 
671             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
672 
673             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
674             StandardOutErrEventAssertionListener listener =
675                 new StandardOutErrEventAssertionListener( NORMAL_RUN, "msg", false );
676             notifier.setStdOutListener( listener );
677 
678             EH eventHandler = new EH();
679             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
680             ConsoleLogger logger = mock( ConsoleLogger.class );
681             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
682             when( arguments.getConsoleLogger() ).thenReturn( logger );
683             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
684             {
685                 t.start();
686                 notifier.notifyEvent( eventHandler.pullEvent() );
687             }
688 
689             assertThat( listener.called.get() )
690                 .isTrue();
691         }
692 
693         @Test
694         public void testStdOutStreamPrint() throws Exception
695         {
696             final Stream out = Stream.newStream();
697             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
698             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
699             encoder.stdOut( "", false );
700             wChannel.close();
701 
702             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
703 
704             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
705             StandardOutErrEventAssertionListener listener =
706                 new StandardOutErrEventAssertionListener( NORMAL_RUN, "", false );
707             notifier.setStdOutListener( listener );
708 
709             EH eventHandler = new EH();
710             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
711             ConsoleLogger logger = mock( ConsoleLogger.class );
712             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
713             when( arguments.getConsoleLogger() ).thenReturn( logger );
714             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
715             {
716                 t.start();
717                 notifier.notifyEvent( eventHandler.pullEvent() );
718             }
719 
720             assertThat( listener.called.get() )
721                 .isTrue();
722         }
723 
724         @Test
725         public void testStdOutStreamPrintWithNull() throws Exception
726         {
727             final Stream out = Stream.newStream();
728             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
729             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
730             encoder.stdOut( null, false );
731             wChannel.close();
732 
733             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
734 
735             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
736             StandardOutErrEventAssertionListener listener =
737                 new StandardOutErrEventAssertionListener( NORMAL_RUN, null, false );
738             notifier.setStdOutListener( listener );
739 
740             EH eventHandler = new EH();
741             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
742             ConsoleLogger logger = mock( ConsoleLogger.class );
743             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
744             when( arguments.getConsoleLogger() ).thenReturn( logger );
745             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
746             {
747                 t.start();
748                 notifier.notifyEvent( eventHandler.pullEvent() );
749             }
750 
751             assertThat( listener.called.get() )
752                 .isTrue();
753         }
754 
755         @Test
756         public void testStdOutStreamPrintln() throws Exception
757         {
758             final Stream out = Stream.newStream();
759             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
760             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
761             encoder.stdOut( "", true );
762             wChannel.close();
763 
764             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
765 
766             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
767             StandardOutErrEventAssertionListener listener =
768                 new StandardOutErrEventAssertionListener( NORMAL_RUN, "", true );
769             notifier.setStdOutListener( listener );
770 
771             EH eventHandler = new EH();
772             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
773             ConsoleLogger logger = mock( ConsoleLogger.class );
774             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
775             when( arguments.getConsoleLogger() ).thenReturn( logger );
776             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
777             {
778                 t.start();
779                 notifier.notifyEvent( eventHandler.pullEvent() );
780             }
781 
782             assertThat( listener.called.get() )
783                 .isTrue();
784         }
785 
786         @Test
787         public void testStdOutStreamPrintlnWithNull() throws Exception
788         {
789             final Stream out = Stream.newStream();
790             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
791             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
792             encoder.stdOut( null, true );
793             wChannel.close();
794 
795             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
796 
797             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
798             StandardOutErrEventAssertionListener listener =
799                 new StandardOutErrEventAssertionListener( NORMAL_RUN, null, true );
800             notifier.setStdOutListener( listener );
801 
802             EH eventHandler = new EH();
803             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
804             ConsoleLogger logger = mock( ConsoleLogger.class );
805             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
806             when( arguments.getConsoleLogger() ).thenReturn( logger );
807             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
808             {
809                 t.start();
810                 notifier.notifyEvent( eventHandler.pullEvent() );
811             }
812 
813             assertThat( listener.called.get() )
814                 .isTrue();
815         }
816 
817         @Test
818         public void testStdErrStream() throws Exception
819         {
820             final Stream out = Stream.newStream();
821             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
822             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
823             encoder.stdErr( "msg", false );
824             wChannel.close();
825 
826             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
827 
828             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
829             StandardOutErrEventAssertionListener listener =
830                 new StandardOutErrEventAssertionListener( NORMAL_RUN, "msg", false );
831             notifier.setStdErrListener( listener );
832 
833             EH eventHandler = new EH();
834             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
835             ConsoleLogger logger = mock( ConsoleLogger.class );
836             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
837             when( arguments.getConsoleLogger() ).thenReturn( logger );
838             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
839             {
840                 t.start();
841                 notifier.notifyEvent( eventHandler.pullEvent() );
842             }
843 
844             assertThat( listener.called.get() )
845                 .isTrue();
846         }
847 
848         @Test
849         public void shouldCountSameNumberOfSystemProperties() throws Exception
850         {
851             final Stream out = Stream.newStream();
852             WritableBufferedByteChannel wChannel = newBufferedChannel( out );
853             LegacyMasterProcessChannelEncoder encoder = new LegacyMasterProcessChannelEncoder( wChannel );
854             encoder.sendSystemProperties( ObjectUtils.systemProps() );
855             wChannel.close();
856 
857             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
858 
859             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
860             PropertyEventAssertionListener listener = new PropertyEventAssertionListener();
861             notifier.setSystemPropertiesListener( listener );
862 
863             EH eventHandler = new EH();
864             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
865             ConsoleLogger logger = mock( ConsoleLogger.class );
866             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
867             when( arguments.getConsoleLogger() ).thenReturn( logger );
868             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
869             {
870                 t.start();
871                 notifier.notifyEvent( eventHandler.pullEvent() );
872             }
873 
874             assertThat( listener.called.get() )
875                 .isTrue();
876         }
877 
878         @Test
879         public void shouldHandleErrorAfterNullLine()
880         {
881             ForkedProcessEventNotifier decoder = new ForkedProcessEventNotifier();
882             decoder.setSystemPropertiesListener( new PropertyEventAssertionListener() );
883             rule.expect( NullPointerException.class );
884             decoder.notifyEvent( null );
885         }
886 
887         @Test
888         public void shouldHandleErrorAfterUnknownOperation() throws Exception
889         {
890             String cmd = ":maven-surefire-event:abnormal-run:-:\n";
891 
892             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( cmd.getBytes() ) );
893 
894             EH eventHandler = new EH();
895             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 1 );
896             ConsoleLogger logger = mock( ConsoleLogger.class );
897             when( logger.isDebugEnabled() ).thenReturn( true );
898             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
899             when( arguments.dumpStreamText( anyString() ) ).thenReturn( new File( "" ) );
900             when( arguments.getConsoleLogger() ).thenReturn( logger );
901             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
902             {
903                 t.start();
904                 countdown.awaitClosed();
905             }
906 
907             ArgumentCaptor<String> dumpLine = ArgumentCaptor.forClass( String.class );
908             verify( logger, times( 2 ) ).debug( dumpLine.capture() );
909             assertThat( dumpLine.getAllValues() )
910                 .hasSize( 2 )
911                 .contains( ":maven-surefire-event:abnormal-run:", atIndex( 0 ) )
912                 .contains( "-:", atIndex( 1 ) );
913 
914             ArgumentCaptor<String> dumpText = ArgumentCaptor.forClass( String.class );
915             verify( arguments, times( 2 ) ).dumpStreamText( dumpText.capture() );
916             String dump = "Corrupted STDOUT by directly writing to native stream in forked JVM 0.";
917             assertThat( dumpText.getAllValues() )
918                 .hasSize( 2 )
919                 .contains( format( dump + " Stream '%s'.", ":maven-surefire-event:abnormal-run:" ), atIndex( 0 ) )
920                 .contains( format( dump + " Stream '%s'.", "-:" ), atIndex( 1 ) );
921 
922             ArgumentCaptor<String> warning = ArgumentCaptor.forClass( String.class );
923             verify( arguments, times( 2 ) ).logWarningAtEnd( warning.capture() );
924             dump += " See FAQ web page and the dump file ";
925             assertThat( warning.getAllValues() )
926                 .hasSize( 2 );
927             assertThat( warning.getAllValues().get( 0 ) )
928                 .startsWith( dump );
929             assertThat( warning.getAllValues().get( 1 ) )
930                 .startsWith( dump );
931         }
932 
933         @Test
934         public void shouldHandleExit() throws Exception
935         {
936             final Stream out = Stream.newStream();
937             LegacyMasterProcessChannelEncoder encoder =
938                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
939 
940             StackTraceWriter stackTraceWriter = mock( StackTraceWriter.class );
941             when( stackTraceWriter.getThrowable() ).thenReturn( new SafeThrowable( "1" ) );
942             when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( "2" );
943             when( stackTraceWriter.writeTraceToString() ).thenReturn( "3" );
944             when( stackTraceWriter.writeTrimmedTraceToString() ).thenReturn( "4" );
945             encoder.sendExitError( stackTraceWriter, false );
946 
947             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
948 
949             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
950             ProcessExitErrorListener listener = new ProcessExitErrorListener();
951             notifier.setExitErrorEventListener( listener );
952 
953             EH eventHandler = new EH();
954             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
955             ConsoleLogger logger = mock( ConsoleLogger.class );
956             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
957             when( arguments.getConsoleLogger() ).thenReturn( logger );
958             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
959             {
960                 t.start();
961                 notifier.notifyEvent( eventHandler.pullEvent() );
962             }
963 
964             assertThat( listener.called.get() )
965                 .isTrue();
966         }
967     }
968 
969     /**
970      *
971      */
972     @RunWith( Theories.class )
973     public static class ReportEntryTest
974     {
975         @DataPoints( value = "operation" )
976         @SuppressWarnings( "checkstyle:visibilitymodifier" )
977         public static String[][] operations = { { "testSetStarting", "setTestSetStartingListener" },
978                                                 { "testSetCompleted", "setTestSetCompletedListener" },
979                                                 { "testStarting", "setTestStartingListener" },
980                                                 { "testSucceeded", "setTestSucceededListener" },
981                                                 { "testFailed", "setTestFailedListener" },
982                                                 { "testSkipped", "setTestSkippedListener" },
983                                                 { "testError", "setTestErrorListener" },
984                                                 { "testAssumptionFailure", "setTestAssumptionFailureListener" }
985         };
986 
987         @DataPoints( value = "reportedMessage" )
988         @SuppressWarnings( "checkstyle:visibilitymodifier" )
989         public static String[] reportedMessage = { null, "skipped test" };
990 
991         @DataPoints( value = "elapsed" )
992         @SuppressWarnings( { "checkstyle:visibilitymodifier", "checkstyle:magicnumber" } )
993         public static Integer[] elapsed = { null, 102 };
994 
995         @DataPoints( value = "trim" )
996         @SuppressWarnings( "checkstyle:visibilitymodifier" )
997         public static boolean[] trim = { false, true };
998 
999         @DataPoints( value = "msg" )
1000         @SuppressWarnings( "checkstyle:visibilitymodifier" )
1001         public static boolean[] msg = { false, true };
1002 
1003         @DataPoints( value = "smart" )
1004         @SuppressWarnings( "checkstyle:visibilitymodifier" )
1005         public static boolean[] smart = { false, true };
1006 
1007         @DataPoints( value = "trace" )
1008         @SuppressWarnings( "checkstyle:visibilitymodifier" )
1009         public static boolean[] trace = { false, true };
1010 
1011         @Theory
1012         public void testReportEntryOperations( @FromDataPoints( "operation" ) String[] operation,
1013                                                @FromDataPoints( "reportedMessage" ) String reportedMessage,
1014                                                @FromDataPoints( "elapsed" ) Integer elapsed,
1015                                                @FromDataPoints( "trim" ) boolean trim,
1016                                                @FromDataPoints( "msg" ) boolean msg,
1017                                                @FromDataPoints( "smart" ) boolean smart,
1018                                                @FromDataPoints( "trace" ) boolean trace )
1019                 throws Exception
1020         {
1021             String exceptionMessage = msg ? "msg" : null;
1022             String smartStackTrace = smart ? "MyTest:86 >> Error" : null;
1023             String exceptionStackTrace =
1024                     trace ? ( trim ? "trace line 1\ntrace line 2" : "Exception: msg\ntrace line 1\ntrace line 2" )
1025                             : null;
1026 
1027             StackTraceWriter stackTraceWriter = null;
1028             if ( exceptionStackTrace != null )
1029             {
1030                 SafeThrowable safeThrowable = new SafeThrowable( exceptionMessage );
1031                 stackTraceWriter = mock( StackTraceWriter.class );
1032                 when( stackTraceWriter.getThrowable() ).thenReturn( safeThrowable );
1033                 when( stackTraceWriter.smartTrimmedStackTrace() ).thenReturn( smartStackTrace );
1034                 when( stackTraceWriter.writeTrimmedTraceToString() ).thenReturn( exceptionStackTrace );
1035                 when( stackTraceWriter.writeTraceToString() ).thenReturn( exceptionStackTrace );
1036             }
1037 
1038             ReportEntry reportEntry = mock( ReportEntry.class );
1039             when( reportEntry.getElapsed() ).thenReturn( elapsed );
1040             when( reportEntry.getGroup() ).thenReturn( "this group" );
1041             when( reportEntry.getMessage() ).thenReturn( reportedMessage );
1042             when( reportEntry.getName() ).thenReturn( "my test" );
1043             when( reportEntry.getName() ).thenReturn( "display name of test" );
1044             when( reportEntry.getNameWithGroup() ).thenReturn( "name with group" );
1045             when( reportEntry.getSourceName() ).thenReturn( "pkg.MyTest" );
1046             when( reportEntry.getSourceText() ).thenReturn( "test class display name" );
1047             when( reportEntry.getStackTraceWriter() ).thenReturn( stackTraceWriter );
1048 
1049             final Stream out = Stream.newStream();
1050 
1051             LegacyMasterProcessChannelEncoder encoder =
1052                 new LegacyMasterProcessChannelEncoder( newBufferedChannel( out ) );
1053 
1054             LegacyMasterProcessChannelEncoder.class.getMethod( operation[0], ReportEntry.class, boolean.class )
1055                     .invoke( encoder, reportEntry, trim );
1056 
1057             ForkedProcessEventNotifier notifier = new ForkedProcessEventNotifier();
1058 
1059             ForkedProcessEventNotifier.class.getMethod( operation[1], ForkedProcessReportEventListener.class )
1060                     .invoke( notifier, new ReportEventAssertionListener( reportEntry, stackTraceWriter != null ) );
1061 
1062             ReadableByteChannel channel = newChannel( new ByteArrayInputStream( out.toByteArray() ) );
1063 
1064             EH eventHandler = new EH();
1065             CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 0 );
1066             ConsoleLogger logger = mock( ConsoleLogger.class );
1067             ForkNodeArguments arguments = mock( ForkNodeArguments.class );
1068             when( arguments.getConsoleLogger() ).thenReturn( logger );
1069             try ( EventConsumerThread t = new EventConsumerThread( "t", channel, eventHandler, countdown, arguments ) )
1070             {
1071                 t.start();
1072                 notifier.notifyEvent( eventHandler.pullEvent() );
1073             }
1074         }
1075     }
1076 
1077     private static class ProcessExitErrorListener implements ForkedProcessExitErrorListener
1078     {
1079         final AtomicBoolean called = new AtomicBoolean();
1080 
1081         @Override
1082         public void handle( StackTraceWriter stackTrace )
1083         {
1084             called.set( true );
1085             assertThat( stackTrace.getThrowable().getMessage() ).isEqualTo( "1" );
1086             assertThat( stackTrace.smartTrimmedStackTrace() ).isEqualTo( "2" );
1087             assertThat( stackTrace.writeTraceToString() ).isEqualTo( "3" );
1088         }
1089     }
1090 
1091     private static class PropertyEventAssertionListener implements ForkedProcessPropertyEventListener
1092     {
1093         final AtomicBoolean called = new AtomicBoolean();
1094         private final Map<?, ?> sysProps = System.getProperties();
1095         private final AtomicInteger counter = new AtomicInteger();
1096 
1097         public void handle( RunMode runMode, String key, String value )
1098         {
1099             called.set( true );
1100             counter.incrementAndGet();
1101             assertThat( runMode ).isEqualTo( NORMAL_RUN );
1102             assertTrue( sysProps.containsKey( key ) );
1103             assertThat( sysProps.get( key ) ).isEqualTo( value );
1104         }
1105     }
1106 
1107     private static class EventAssertionListener implements ForkedProcessEventListener
1108     {
1109         final AtomicBoolean called = new AtomicBoolean();
1110 
1111         public void handle()
1112         {
1113             called.set( true );
1114         }
1115     }
1116 
1117     private static class StringEventAssertionListener implements ForkedProcessStringEventListener
1118     {
1119         final AtomicBoolean called = new AtomicBoolean();
1120         private final String msg;
1121 
1122         StringEventAssertionListener( String msg )
1123         {
1124             this.msg = msg;
1125         }
1126 
1127         public void handle( String msg )
1128         {
1129             called.set( true );
1130             assertThat( msg )
1131                     .isEqualTo( this.msg );
1132         }
1133     }
1134 
1135     private static class StackTraceEventListener implements ForkedProcessStackTraceEventListener
1136     {
1137         final AtomicBoolean called = new AtomicBoolean();
1138         private final String msg;
1139         private final String smartStackTrace;
1140         private final String stackTrace;
1141 
1142         StackTraceEventListener( String msg, String smartStackTrace, String stackTrace )
1143         {
1144             this.msg = msg;
1145             this.smartStackTrace = smartStackTrace;
1146             this.stackTrace = stackTrace;
1147         }
1148 
1149         @Override
1150         public void handle( @Nonnull StackTraceWriter stackTrace )
1151         {
1152             called.set( true );
1153 
1154             assertThat( stackTrace.getThrowable().getMessage() )
1155                     .isEqualTo( msg );
1156 
1157             assertThat( stackTrace.smartTrimmedStackTrace() )
1158                     .isEqualTo( smartStackTrace );
1159 
1160             assertThat( stackTrace.writeTraceToString() )
1161                     .isEqualTo( this.stackTrace );
1162         }
1163     }
1164 
1165     private static class StandardOutErrEventAssertionListener implements ForkedProcessStandardOutErrEventListener
1166     {
1167         final AtomicBoolean called = new AtomicBoolean();
1168         private final RunMode runMode;
1169         private final String output;
1170         private final boolean newLine;
1171 
1172         StandardOutErrEventAssertionListener( RunMode runMode, String output, boolean newLine )
1173         {
1174             this.runMode = runMode;
1175             this.output = output;
1176             this.newLine = newLine;
1177         }
1178 
1179         public void handle( RunMode runMode, String output, boolean newLine )
1180         {
1181             called.set( true );
1182 
1183             assertThat( runMode )
1184                     .isEqualTo( this.runMode );
1185 
1186             assertThat( output )
1187                     .isEqualTo( this.output );
1188 
1189             assertThat( newLine )
1190                     .isEqualTo( this.newLine );
1191         }
1192     }
1193 
1194     private static class ReportEventAssertionListener implements ForkedProcessReportEventListener<ReportEntry>
1195     {
1196         final AtomicBoolean called = new AtomicBoolean();
1197         private final ReportEntry reportEntry;
1198         private final boolean hasStackTrace;
1199 
1200         ReportEventAssertionListener( ReportEntry reportEntry, boolean hasStackTrace )
1201         {
1202             this.reportEntry = reportEntry;
1203             this.hasStackTrace = hasStackTrace;
1204         }
1205 
1206         public void handle( RunMode runMode, ReportEntry reportEntry )
1207         {
1208             called.set( true );
1209             assertThat( reportEntry.getSourceName() ).isEqualTo( this.reportEntry.getSourceName() );
1210             assertThat( reportEntry.getSourceText() ).isEqualTo( this.reportEntry.getSourceText() );
1211             assertThat( reportEntry.getName() ).isEqualTo( this.reportEntry.getName() );
1212             assertThat( reportEntry.getNameText() ).isEqualTo( this.reportEntry.getNameText() );
1213             assertThat( reportEntry.getGroup() ).isEqualTo( this.reportEntry.getGroup() );
1214             assertThat( reportEntry.getMessage() ).isEqualTo( this.reportEntry.getMessage() );
1215             assertThat( reportEntry.getElapsed() ).isEqualTo( this.reportEntry.getElapsed() );
1216             if ( reportEntry.getStackTraceWriter() == null )
1217             {
1218                 assertThat( hasStackTrace ).isFalse();
1219                 assertThat( this.reportEntry.getStackTraceWriter() ).isNull();
1220             }
1221             else
1222             {
1223                 assertThat( hasStackTrace ).isTrue();
1224                 assertThat( this.reportEntry.getStackTraceWriter() ).isNotNull();
1225 
1226                 assertThat( reportEntry.getStackTraceWriter().getThrowable().getMessage() )
1227                         .isEqualTo( this.reportEntry.getStackTraceWriter().getThrowable().getMessage() );
1228 
1229                 assertThat( reportEntry.getStackTraceWriter().getThrowable().getLocalizedMessage() )
1230                         .isEqualTo( this.reportEntry.getStackTraceWriter().getThrowable().getLocalizedMessage() );
1231 
1232                 assertThat( reportEntry.getStackTraceWriter().smartTrimmedStackTrace() )
1233                         .isEqualTo( this.reportEntry.getStackTraceWriter().smartTrimmedStackTrace() );
1234             }
1235         }
1236     }
1237 
1238     private static class Stream extends PrintStream
1239     {
1240         private final ByteArrayOutputStream out;
1241 
1242         Stream( ByteArrayOutputStream out )
1243         {
1244             super( out, true );
1245             this.out = out;
1246         }
1247 
1248         byte[] toByteArray()
1249         {
1250             return out.toByteArray();
1251         }
1252 
1253         LineNumberReader newReader( Charset streamCharset )
1254         {
1255             return new LineNumberReader( new StringReader( new String( toByteArray(), streamCharset ) ) );
1256         }
1257 
1258         static Stream newStream()
1259         {
1260             return new Stream( new ByteArrayOutputStream() );
1261         }
1262     }
1263 
1264     private static byte[] toArray( ByteBuffer buffer )
1265     {
1266         return copyOfRange( buffer.array(), buffer.arrayOffset(), buffer.arrayOffset() + buffer.remaining() );
1267     }
1268 
1269     private static class EH implements EventHandler<Event>
1270     {
1271         private final BlockingQueue<Event> cache = new LinkedTransferQueue<>();
1272 
1273         Event pullEvent() throws InterruptedException
1274         {
1275             return cache.poll( 1, TimeUnit.MINUTES );
1276         }
1277 
1278         @Override
1279         public void handleEvent( @Nonnull Event event )
1280         {
1281             cache.add( event );
1282         }
1283     }
1284 }