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