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