View Javadoc
1   package org.apache.maven.plugin.surefire.booterclient;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import junit.framework.TestCase;
23  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.NotifiableTestStream;
24  import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
25  import org.apache.maven.plugin.surefire.extensions.EventConsumerThread;
26  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
27  import org.apache.maven.surefire.api.booter.ForkingRunListener;
28  import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
29  import org.apache.maven.surefire.api.event.Event;
30  import org.apache.maven.surefire.extensions.EventHandler;
31  import org.apache.maven.surefire.extensions.ForkNodeArguments;
32  import org.apache.maven.surefire.extensions.util.CountdownCloseable;
33  import org.apache.maven.surefire.api.report.CategorizedReportEntry;
34  import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
35  import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriter;
36  import org.apache.maven.surefire.api.report.ReportEntry;
37  import org.apache.maven.surefire.api.report.ReporterException;
38  import org.apache.maven.surefire.api.report.RunListener;
39  import org.apache.maven.surefire.api.report.SimpleReportEntry;
40  import org.apache.maven.surefire.api.report.StackTraceWriter;
41  import org.apache.maven.surefire.api.report.TestSetReportEntry;
42  import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
43  
44  import javax.annotation.Nonnull;
45  import java.io.ByteArrayInputStream;
46  import java.io.ByteArrayOutputStream;
47  import java.io.Closeable;
48  import java.io.PrintStream;
49  import java.nio.channels.ReadableByteChannel;
50  import java.util.ArrayList;
51  import java.util.Collections;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.concurrent.BlockingQueue;
55  import java.util.concurrent.LinkedBlockingQueue;
56  import java.util.concurrent.TimeUnit;
57  
58  import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
59  import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
60  import static org.mockito.Mockito.mock;
61  import static org.mockito.Mockito.when;
62  
63  /**
64   * @author Kristian Rosenvold
65   */
66  @SuppressWarnings( "checkstyle:magicnumber" )
67  public class ForkingRunListenerTest
68      extends TestCase
69  {
70      private final ByteArrayOutputStream content, anotherContent;
71  
72      private final PrintStream printStream, anotherPrintStream;
73  
74      public ForkingRunListenerTest()
75      {
76          content = new ByteArrayOutputStream();
77          printStream = new PrintStream( content );
78  
79          anotherContent = new ByteArrayOutputStream();
80          anotherPrintStream = new PrintStream( anotherContent );
81      }
82  
83      private void reset()
84      {
85          printStream.flush();
86          content.reset();
87      }
88  
89      public void testSetStarting() throws Exception
90      {
91          final StandardTestRun standardTestRun = new StandardTestRun();
92          TestSetReportEntry expected = createDefaultReportEntry();
93          standardTestRun.run().testSetStarting( expected );
94          standardTestRun.assertExpected( MockReporter.SET_STARTING, expected );
95      }
96  
97      public void testSetCompleted() throws Exception
98      {
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     {
107         final StandardTestRun standardTestRun = new StandardTestRun();
108         ReportEntry expected = createDefaultReportEntry();
109         standardTestRun.run().testStarting( expected );
110         standardTestRun.assertExpected( MockReporter.TEST_STARTING, expected );
111     }
112 
113     public void testSucceeded() throws Exception
114     {
115         final StandardTestRun standardTestRun = new StandardTestRun();
116         ReportEntry expected = createDefaultReportEntry();
117         standardTestRun.run().testSucceeded( expected );
118         standardTestRun.assertExpected( MockReporter.TEST_SUCCEEDED, expected );
119     }
120 
121     public void testFailed() throws Exception
122     {
123         final StandardTestRun standardTestRun = new StandardTestRun();
124         ReportEntry expected = createReportEntryWithStackTrace();
125         standardTestRun.run().testFailed( expected );
126         standardTestRun.assertExpected( MockReporter.TEST_FAILED, expected );
127     }
128 
129     public void testFailedWithCommaInMessage() throws Exception
130     {
131         final StandardTestRun standardTestRun = new StandardTestRun();
132         ReportEntry expected = createReportEntryWithSpecialMessage( "We, the people" );
133         standardTestRun.run().testFailed( expected );
134         standardTestRun.assertExpected( MockReporter.TEST_FAILED, expected );
135     }
136 
137     public void testFailedWithUnicodeEscapeInMessage() throws Exception
138     {
139         final StandardTestRun standardTestRun = new StandardTestRun();
140         ReportEntry expected = createReportEntryWithSpecialMessage( "We, \\u0177 people" );
141         standardTestRun.run().testFailed( expected );
142         standardTestRun.assertExpected( MockReporter.TEST_FAILED, expected );
143     }
144 
145     public void testFailure() throws Exception
146     {
147         final StandardTestRun standardTestRun = new StandardTestRun();
148         ReportEntry expected = createDefaultReportEntry();
149         standardTestRun.run().testError( expected );
150         standardTestRun.assertExpected( MockReporter.TEST_ERROR, expected );
151     }
152 
153     public void testSkipped() throws Exception
154     {
155         final StandardTestRun standardTestRun = new StandardTestRun();
156         ReportEntry expected = createDefaultReportEntry();
157         standardTestRun.run().testSkipped( expected );
158         standardTestRun.assertExpected( MockReporter.TEST_SKIPPED, expected );
159     }
160 
161     public void testAssumptionFailure() throws Exception
162     {
163         final StandardTestRun standardTestRun = new StandardTestRun();
164         ReportEntry expected = createDefaultReportEntry();
165         standardTestRun.run().testAssumptionFailure( expected );
166         standardTestRun.assertExpected( MockReporter.TEST_ASSUMPTION_FAIL, expected );
167     }
168 
169     public void testConsole() throws Exception
170     {
171         final StandardTestRun standardTestRun = new StandardTestRun();
172         ConsoleLogger directConsoleReporter = (ConsoleLogger) standardTestRun.run();
173         directConsoleReporter.info( "HeyYou" );
174         standardTestRun.assertExpected( MockReporter.CONSOLE_INFO, "HeyYou" );
175     }
176 
177     public void testConsoleOutput() throws Exception
178     {
179         final StandardTestRun standardTestRun = new StandardTestRun();
180         ConsoleOutputReceiver directConsoleReporter = (ConsoleOutputReceiver) standardTestRun.run();
181         directConsoleReporter.writeTestOutput( "HeyYou", false, true );
182         standardTestRun.assertExpected( MockReporter.STDOUT, "HeyYou" );
183     }
184 
185     public void testSystemProperties() throws Exception
186     {
187         StandardTestRun standardTestRun = new StandardTestRun();
188         standardTestRun.run();
189 
190         reset();
191         createForkingRunListener();
192 
193         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
194         ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
195 
196         byte[] cmd = ":maven-surefire-event:sys-prop:normal-run:UTF-8:azE=:djE=:\n".getBytes();
197         for ( Event e : streamToEvent( cmd ) )
198         {
199             forkStreamClient.handleEvent( e );
200         }
201         cmd = "\n:maven-surefire-event:sys-prop:normal-run:UTF-8:azI=:djI=:\n".getBytes();
202         for ( Event e : streamToEvent( cmd ) )
203         {
204             forkStreamClient.handleEvent( e );
205         }
206 
207         assertTrue( forkStreamClient.getTestVmSystemProperties().size() == 2 );
208         assertTrue( forkStreamClient.getTestVmSystemProperties().containsKey( "k1" ) );
209         assertTrue( forkStreamClient.getTestVmSystemProperties().containsKey( "k2" ) );
210     }
211 
212     public void testMultipleEntries() throws Exception
213     {
214         StandardTestRun standardTestRun = new StandardTestRun();
215         standardTestRun.run();
216 
217         reset();
218         RunListener forkingReporter = createForkingRunListener();
219 
220         TestSetReportEntry reportEntry = createDefaultReportEntry();
221         forkingReporter.testSetStarting( reportEntry );
222         forkingReporter.testStarting( reportEntry );
223         forkingReporter.testSucceeded( reportEntry );
224         forkingReporter.testSetCompleted( reportEntry );
225 
226         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
227         ForkClient forkStreamClient =
228                 new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
229 
230         for ( Event e : streamToEvent( content.toByteArray() ) )
231         {
232             forkStreamClient.handleEvent( e );
233         }
234 
235         final MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
236         final List<String> events = reporter.getEvents();
237         assertEquals( MockReporter.SET_STARTING, events.get( 0 ) );
238         assertEquals( MockReporter.TEST_STARTING, events.get( 1 ) );
239         assertEquals( MockReporter.TEST_SUCCEEDED, events.get( 2 ) );
240         assertEquals( MockReporter.SET_COMPLETED, events.get( 3 ) );
241     }
242 
243     public void test2DifferentChannels()
244         throws Exception
245     {
246         reset();
247         ReportEntry expected = createDefaultReportEntry();
248         SimpleReportEntry secondExpected = createAnotherDefaultReportEntry();
249 
250         new ForkingRunListener( new LegacyMasterProcessChannelEncoder( newBufferedChannel( printStream ) ), false )
251                 .testStarting( expected );
252 
253         new ForkingRunListener(
254             new LegacyMasterProcessChannelEncoder( newBufferedChannel( anotherPrintStream ) ), false )
255                 .testSkipped( secondExpected );
256 
257         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
258         NotifiableTestStream notifiableTestStream = new MockNotifiableTestStream();
259 
260         ForkClient forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, 1 );
261         for ( Event e : streamToEvent( content.toByteArray() ) )
262         {
263             forkStreamClient.handleEvent( e );
264         }
265 
266         MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
267         assertEquals( MockReporter.TEST_STARTING, reporter.getFirstEvent() );
268         assertEquals( expected, reporter.getFirstData() );
269         assertEquals( 1, reporter.getEvents().size() );
270 
271         forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, 2 );
272         for ( Event e : streamToEvent( anotherContent.toByteArray() ) )
273         {
274             forkStreamClient.handleEvent( e );
275         }
276         MockReporter reporter2 = (MockReporter) forkStreamClient.getReporter();
277         assertEquals( MockReporter.TEST_SKIPPED, reporter2.getFirstEvent() );
278         assertEquals( secondExpected, reporter2.getFirstData() );
279         assertEquals( 1, reporter2.getEvents().size() );
280     }
281 
282     private static List<Event> streamToEvent( byte[] stream ) throws Exception
283     {
284         List<Event> events = new ArrayList<>();
285         EH handler = new EH();
286         CountdownCloseable countdown = new CountdownCloseable( mock( Closeable.class ), 1 );
287         ConsoleLogger logger = mock( ConsoleLogger.class );
288         ForkNodeArguments arguments = mock( ForkNodeArguments.class );
289         when( arguments.getConsoleLogger() ).thenReturn( logger );
290         ReadableByteChannel channel = newChannel( new ByteArrayInputStream( stream ) );
291         try ( EventConsumerThread t = new EventConsumerThread( "t", channel, handler, countdown, arguments ) )
292         {
293             t.start();
294             countdown.awaitClosed();
295             for ( int i = 0, size = handler.countEventsInCache(); i < size; i++ )
296             {
297                 events.add( handler.pullEvent() );
298             }
299             assertEquals( 0, handler.countEventsInCache() );
300             return events;
301         }
302     }
303 
304     private static class EH implements EventHandler<Event>
305     {
306         private final BlockingQueue<Event> cache = new LinkedBlockingQueue<>();
307 
308         Event pullEvent() throws InterruptedException
309         {
310             return cache.poll( 1, TimeUnit.MINUTES );
311         }
312 
313         int countEventsInCache()
314         {
315             return cache.size();
316         }
317 
318         @Override
319         public void handleEvent( @Nonnull Event event )
320         {
321             cache.add( event );
322         }
323     }
324 
325     // Todo: Test weird characters
326 
327     private SimpleReportEntry createDefaultReportEntry( Map<String, String> sysProps )
328     {
329         return new SimpleReportEntry( "com.abc.TestClass", null, "testMethod", null, null, 22, sysProps );
330     }
331 
332     private SimpleReportEntry createDefaultReportEntry()
333     {
334         return createDefaultReportEntry( Collections.<String, String>emptyMap() );
335     }
336 
337     private SimpleReportEntry createAnotherDefaultReportEntry()
338     {
339         return new SimpleReportEntry( "com.abc.AnotherTestClass", null, "testAnotherMethod", null, 42 );
340     }
341 
342     private SimpleReportEntry createReportEntryWithStackTrace()
343     {
344         try
345         {
346             throw new RuntimeException();
347         }
348         catch ( RuntimeException e )
349         {
350             StackTraceWriter stackTraceWriter =
351                 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
352             return new CategorizedReportEntry( "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
353         }
354     }
355 
356     private SimpleReportEntry createReportEntryWithSpecialMessage( String message )
357     {
358         try
359         {
360             throw new RuntimeException( message );
361         }
362         catch ( RuntimeException e )
363         {
364             StackTraceWriter stackTraceWriter =
365                 new LegacyPojoStackTraceWriter( "org.apache.tests.TestClass", "testMethod11", e );
366             return new CategorizedReportEntry( "com.abc.TestClass", "testMethod", "aGroup", stackTraceWriter, 77 );
367         }
368     }
369 
370     private RunListener createForkingRunListener()
371     {
372         WritableBufferedByteChannel channel = (WritableBufferedByteChannel) newChannel( printStream );
373         return new ForkingRunListener( new LegacyMasterProcessChannelEncoder( channel ), false );
374     }
375 
376     private class StandardTestRun
377     {
378         private MockReporter reporter;
379 
380         public RunListener run()
381             throws ReporterException
382         {
383             reset();
384             return createForkingRunListener();
385         }
386 
387         public void clientReceiveContent() throws Exception
388         {
389             TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
390             ForkClient handler = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), 1 );
391             for ( Event e : streamToEvent( content.toByteArray() ) )
392             {
393                 handler.handleEvent( e );
394             }
395             reporter = (MockReporter) handler.getReporter();
396         }
397 
398         public String getFirstEvent()
399         {
400             return reporter.getEvents().get( 0 );
401         }
402 
403         public ReportEntry getFirstData()
404         {
405             return (ReportEntry) reporter.getData().get( 0 );
406         }
407 
408         private void assertExpected( String actionCode, ReportEntry expected ) throws Exception
409         {
410             clientReceiveContent();
411             assertEquals( actionCode, getFirstEvent() );
412             final ReportEntry firstData = getFirstData();
413             assertEquals( expected.getSourceName(), firstData.getSourceName() );
414             assertEquals( expected.getName(), firstData.getName() );
415             //noinspection deprecation
416             assertEquals( expected.getElapsed(), firstData.getElapsed() );
417             assertEquals( expected.getGroup(), firstData.getGroup() );
418             if ( expected.getStackTraceWriter() != null )
419             {
420                 //noinspection ThrowableResultOfMethodCallIgnored
421                 assertEquals( expected.getStackTraceWriter().getThrowable().getLocalizedMessage(),
422                               firstData.getStackTraceWriter().getThrowable().getLocalizedMessage() );
423                 assertEquals( expected.getStackTraceWriter().writeTraceToString(),
424                               firstData.getStackTraceWriter().writeTraceToString() );
425             }
426         }
427 
428         private void assertExpected( String actionCode, String expected ) throws Exception
429         {
430             clientReceiveContent();
431             assertEquals( actionCode, getFirstEvent() );
432             final String firstData = (String) reporter.getData().get( 0 );
433             assertEquals( expected, firstData );
434         }
435 
436     }
437 }