View Javadoc
1   package org.apache.maven.surefire.booter;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.surefire.testset.TestSetFailedException;
23  import org.junit.After;
24  import org.junit.Before;
25  import org.junit.Test;
26  import org.junit.runner.RunWith;
27  
28  import java.io.ByteArrayOutputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.PrintStream;
32  import java.nio.ByteBuffer;
33  import java.util.Iterator;
34  import java.util.NoSuchElementException;
35  import java.util.concurrent.BlockingQueue;
36  import java.util.concurrent.CountDownLatch;
37  import java.util.concurrent.ExecutionException;
38  import java.util.concurrent.FutureTask;
39  import java.util.concurrent.LinkedBlockingQueue;
40  import java.util.concurrent.TimeUnit;
41  
42  import static java.nio.charset.StandardCharsets.ISO_8859_1;
43  
44  import static org.junit.Assert.assertFalse;
45  import static org.junit.Assert.assertTrue;
46  import static org.junit.Assert.fail;
47  import static org.hamcrest.MatcherAssert.assertThat;
48  import static org.hamcrest.Matchers.is;
49  import static org.fest.assertions.Assertions.assertThat;
50  
51  /**
52   * Testing singleton {@code MasterProcessReader} in multiple class loaders.
53   *
54   * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
55   * @since 2.19
56   */
57  @RunWith( NewClassLoaderRunner.class )
58  public class CommandReaderTest
59  {
60      private final BlockingQueue<Byte> blockingStream = new LinkedBlockingQueue<>();
61      private InputStream realInputStream;
62      private CommandReader reader;
63  
64      static class A {
65      }
66  
67      static class B {
68      }
69  
70      static class C {
71      }
72  
73      static class D {
74      }
75  
76      @Before
77      public void init()
78      {
79          Thread.interrupted();
80          realInputStream = System.in;
81          addTestToPipeline( getClass().getName() );
82          System.setIn( new SystemInputStream() );
83          reader = CommandReader.getReader();
84      }
85  
86      @After
87      public void deinit()
88      {
89          reader.stop();
90          System.setIn( realInputStream );
91      }
92  
93      @Test
94      public void readJustOneClass()
95      {
96          Iterator<String> it = reader.getIterableClasses( nul() ).iterator();
97          assertTrue( it.hasNext() );
98          assertThat( it.next(), is( getClass().getName() ) );
99          reader.stop();
100         assertFalse( it.hasNext() );
101         try
102         {
103             it.next();
104             fail();
105         }
106         catch ( NoSuchElementException e )
107         {
108             // expected
109         }
110     }
111 
112     @Test
113     public void manyClasses()
114     {
115         Iterator<String> it1 = reader.getIterableClasses( nul() ).iterator();
116         assertThat( it1.next(), is( getClass().getName() ) );
117         addTestToPipeline( A.class.getName() );
118         assertThat( it1.next(), is( A.class.getName() ) );
119         addTestToPipeline( B.class.getName() );
120         assertThat( it1.next(), is( B.class.getName() ) );
121         addTestToPipeline( C.class.getName() );
122         assertThat( it1.next(), is( C.class.getName() ) );
123         addEndOfPipeline();
124         addTestToPipeline( D.class.getName() );
125         assertFalse( it1.hasNext() );
126     }
127 
128     @Test
129     public void twoIterators() throws Exception
130     {
131         Iterator<String> it1 = reader.getIterableClasses( nul() ).iterator();
132 
133         assertThat( it1.next(), is( getClass().getName() ) );
134         addTestToPipeline( A.class.getName() );
135         assertThat( it1.next(), is( A.class.getName() ) );
136         addTestToPipeline( B.class.getName() );
137 
138         TimeUnit.MILLISECONDS.sleep( 200 ); // give the test chance to fail
139 
140         Iterator<String> it2 = reader.iterated();
141 
142         assertThat( it1.next(), is( B.class.getName() ) );
143         addTestToPipeline( C.class.getName() );
144 
145         assertThat( it2.hasNext(), is( true ) );
146         assertThat( it2.next(), is( getClass().getName() ) );
147         assertThat( it2.hasNext(), is( true ) );
148         assertThat( it2.next(), is( A.class.getName() ) );
149         assertThat( it2 ).isEmpty();
150 
151         assertThat( it1.next(), is( C.class.getName() ) );
152         addEndOfPipeline();
153         assertThat( it1 ).isEmpty();
154     }
155 
156     @Test( expected = NoSuchElementException.class )
157     public void stopBeforeReadInThread()
158         throws Throwable
159     {
160         Runnable runnable = new Runnable()
161         {
162             @Override
163             public void run()
164             {
165                 Iterator<String> it = reader.getIterableClasses( nul() ).iterator();
166                 assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
167             }
168         };
169         FutureTask<Object> futureTask = new FutureTask<>( runnable, null );
170         Thread t = new Thread( futureTask );
171         reader.stop();
172         t.start();
173         try
174         {
175             futureTask.get();
176         }
177         catch ( ExecutionException e )
178         {
179             throw e.getCause();
180         }
181     }
182 
183     @Test
184     public void readTwoClassesInThread()
185         throws Throwable
186     {
187         final CountDownLatch counter = new CountDownLatch( 1 );
188         Runnable runnable = new Runnable()
189         {
190             @Override
191             public void run()
192             {
193                 Iterator<String> it = reader.getIterableClasses( nul() ).iterator();
194                 assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
195                 counter.countDown();
196                 assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
197             }
198         };
199         FutureTask<Object> futureTask = new FutureTask<>( runnable, null );
200         Thread t = new Thread( futureTask );
201         t.start();
202         counter.await();
203         addTestToPipeline( PropertiesWrapperTest.class.getName() );
204         try
205         {
206             futureTask.get();
207         }
208         catch ( ExecutionException e )
209         {
210             throw e.getCause();
211         }
212     }
213 
214     @Test( timeout = 15000 )
215     public void shouldAwaitReaderUp()
216         throws TestSetFailedException
217     {
218         assertTrue( reader.awaitStarted() );
219         reader.stop();
220         assertFalse( reader.awaitStarted() );
221     }
222 
223     private class SystemInputStream
224         extends InputStream
225     {
226         @Override
227         public int read()
228             throws IOException
229         {
230             try
231             {
232                 return CommandReaderTest.this.blockingStream.take();
233             }
234             catch ( InterruptedException e )
235             {
236                 throw new IOException( e );
237             }
238         }
239     }
240 
241     private void addTestToPipeline( String cls )
242     {
243         byte[] clazz = cls.getBytes( ISO_8859_1 );
244         ByteBuffer buffer = ByteBuffer.allocate( 8 + clazz.length )
245             .putInt( MasterProcessCommand.RUN_CLASS.getId() )
246             .putInt( clazz.length )
247             .put( clazz );
248         buffer.rewind();
249         for ( ; buffer.hasRemaining(); )
250         {
251             blockingStream.add( buffer.get() );
252         }
253     }
254 
255     private void addEndOfPipeline()
256     {
257         ByteBuffer buffer = ByteBuffer.allocate( 8 )
258                 .putInt( MasterProcessCommand.TEST_SET_FINISHED.getId() )
259                 .putInt( 0 );
260         buffer.rewind();
261         for ( ; buffer.hasRemaining(); )
262         {
263             blockingStream.add( buffer.get() );
264         }
265     }
266 
267     private static PrintStream nul()
268     {
269         return new PrintStream( new ByteArrayOutputStream() );
270     }
271 }