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