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.io.UnsupportedEncodingException;
33  import java.nio.ByteBuffer;
34  import java.util.Iterator;
35  import java.util.NoSuchElementException;
36  import java.util.concurrent.BlockingQueue;
37  import java.util.concurrent.CountDownLatch;
38  import java.util.concurrent.ExecutionException;
39  import java.util.concurrent.FutureTask;
40  import java.util.concurrent.LinkedBlockingQueue;
41  import java.util.concurrent.TimeUnit;
42  
43  import static org.apache.maven.surefire.util.internal.StringUtils.ISO_8859_1;
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<Byte>();
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          throws UnsupportedEncodingException
79      {
80          Thread.interrupted();
81          realInputStream = System.in;
82          addTestToPipeline( getClass().getName() );
83          System.setIn( new SystemInputStream() );
84          reader = CommandReader.getReader();
85      }
86  
87      @After
88      public void deinit()
89      {
90          reader.stop();
91          System.setIn( realInputStream );
92      }
93  
94      @Test
95      public void readJustOneClass() throws Exception
96      {
97          Iterator<String> it = reader.getIterableClasses( nul() ).iterator();
98          assertTrue( it.hasNext() );
99          assertThat( it.next(), is( getClass().getName() ) );
100         reader.stop();
101         assertFalse( it.hasNext() );
102         try
103         {
104             it.next();
105             fail();
106         }
107         catch ( NoSuchElementException e )
108         {
109             // expected
110         }
111     }
112 
113     @Test
114     public void manyClasses() throws Exception
115     {
116         Iterator<String> it1 = reader.getIterableClasses( nul() ).iterator();
117         assertThat( it1.next(), is( getClass().getName() ) );
118         addTestToPipeline( A.class.getName() );
119         assertThat( it1.next(), is( A.class.getName() ) );
120         addTestToPipeline( B.class.getName() );
121         assertThat( it1.next(), is( B.class.getName() ) );
122         addTestToPipeline( C.class.getName() );
123         assertThat( it1.next(), is( C.class.getName() ) );
124         addEndOfPipeline();
125         addTestToPipeline( D.class.getName() );
126         assertFalse( it1.hasNext() );
127     }
128 
129     @Test
130     public void twoIterators() throws Exception
131     {
132         Iterator<String> it1 = reader.getIterableClasses( nul() ).iterator();
133 
134         assertThat( it1.next(), is( getClass().getName() ) );
135         addTestToPipeline( A.class.getName() );
136         assertThat( it1.next(), is( A.class.getName() ) );
137         addTestToPipeline( B.class.getName() );
138 
139         TimeUnit.MILLISECONDS.sleep( 200 ); // give the test chance to fail
140 
141         Iterator<String> it2 = reader.iterated();
142 
143         assertThat( it1.next(), is( B.class.getName() ) );
144         addTestToPipeline( C.class.getName() );
145 
146         assertThat( it2.hasNext(), is( true ) );
147         assertThat( it2.next(), is( getClass().getName() ) );
148         assertThat( it2.hasNext(), is( true ) );
149         assertThat( it2.next(), is( A.class.getName() ) );
150         assertThat( it2 ).isEmpty();
151 
152         assertThat( it1.next(), is( C.class.getName() ) );
153         addEndOfPipeline();
154         assertThat( it1 ).isEmpty();
155     }
156 
157     @Test( expected = NoSuchElementException.class )
158     public void stopBeforeReadInThread()
159         throws Throwable
160     {
161         Runnable runnable = new Runnable()
162         {
163             @Override
164             public void run()
165             {
166                 Iterator<String> it = reader.getIterableClasses( nul() ).iterator();
167                 assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
168             }
169         };
170         FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
171         Thread t = new Thread( futureTask );
172         reader.stop();
173         t.start();
174         try
175         {
176             futureTask.get();
177         }
178         catch ( ExecutionException e )
179         {
180             throw e.getCause();
181         }
182     }
183 
184     @Test
185     public void readTwoClassesInThread()
186         throws Throwable
187     {
188         final CountDownLatch counter = new CountDownLatch( 1 );
189         Runnable runnable = new Runnable()
190         {
191             @Override
192             public void run()
193             {
194                 Iterator<String> it = reader.getIterableClasses( nul() ).iterator();
195                 assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
196                 counter.countDown();
197                 assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
198             }
199         };
200         FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
201         Thread t = new Thread( futureTask );
202         t.start();
203         counter.await();
204         addTestToPipeline( PropertiesWrapperTest.class.getName() );
205         try
206         {
207             futureTask.get();
208         }
209         catch ( ExecutionException e )
210         {
211             throw e.getCause();
212         }
213     }
214 
215     @Test( timeout = 15000 )
216     public void shouldAwaitReaderUp()
217         throws TestSetFailedException
218     {
219         assertTrue( reader.awaitStarted() );
220         reader.stop();
221         assertFalse( reader.awaitStarted() );
222     }
223 
224     private class SystemInputStream
225         extends InputStream
226     {
227         @Override
228         public int read()
229             throws IOException
230         {
231             try
232             {
233                 return CommandReaderTest.this.blockingStream.take();
234             }
235             catch ( InterruptedException e )
236             {
237                 throw new IOException( e );
238             }
239         }
240     }
241 
242     private void addTestToPipeline( String cls )
243         throws UnsupportedEncodingException
244     {
245         byte[] clazz = cls.getBytes( ISO_8859_1 );
246         ByteBuffer buffer = ByteBuffer.allocate( 8 + clazz.length )
247             .putInt( MasterProcessCommand.RUN_CLASS.getId() )
248             .putInt( clazz.length )
249             .put( clazz );
250         buffer.rewind();
251         for ( ; buffer.hasRemaining(); )
252         {
253             blockingStream.add( buffer.get() );
254         }
255     }
256 
257     private void addEndOfPipeline()
258             throws UnsupportedEncodingException
259     {
260         ByteBuffer buffer = ByteBuffer.allocate( 8 )
261                 .putInt( MasterProcessCommand.TEST_SET_FINISHED.getId() )
262                 .putInt( 0 );
263         buffer.rewind();
264         for ( ; buffer.hasRemaining(); )
265         {
266             blockingStream.add( buffer.get() );
267         }
268     }
269 
270     private static PrintStream nul()
271     {
272         return new PrintStream( new ByteArrayOutputStream() );
273     }
274 }