View Javadoc
1   package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
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.api.booter.Command;
23  import org.apache.maven.surefire.api.booter.Shutdown;
24  
25  import java.io.IOException;
26  import java.util.Queue;
27  import java.util.concurrent.ConcurrentLinkedQueue;
28  import java.util.concurrent.Semaphore;
29  import java.util.concurrent.atomic.AtomicBoolean;
30  
31  import static org.apache.maven.surefire.api.booter.Command.BYE_ACK;
32  import static org.apache.maven.surefire.api.booter.Command.NOOP;
33  import static org.apache.maven.surefire.api.booter.Command.SKIP_SINCE_NEXT_TEST;
34  import static org.apache.maven.surefire.api.booter.Command.TEST_SET_FINISHED;
35  import static org.apache.maven.surefire.api.booter.Command.toRunClass;
36  import static org.apache.maven.surefire.api.booter.Command.toShutdown;
37  
38  /**
39   * An {@link java.io.InputStream} that, when read, provides test class names out of a queue.
40   * <br>
41   * The Stream provides only one test at a time, but only after {@link #provideNewTest()} has been invoked.
42   * <br>
43   * The instance is used only in reusable forks in {@link org.apache.maven.plugin.surefire.booterclient.ForkStarter}
44   * by one Thread.
45   *
46   * @author Andreas Gudian
47   * @author Tibor Digana (tibor17)
48   */
49  public final class TestProvidingInputStream
50          extends DefaultCommandReader
51  {
52      private final Semaphore barrier = new Semaphore( 0 );
53  
54      private final Queue<Command> commands = new ConcurrentLinkedQueue<>();
55  
56      private final AtomicBoolean closed = new AtomicBoolean();
57  
58      private final Queue<String> testClassNames;
59  
60      /**
61       * C'tor
62       *
63       * @param testClassNames source of the tests to be read from this stream
64       */
65      public TestProvidingInputStream( Queue<String> testClassNames )
66      {
67          this.testClassNames = testClassNames;
68      }
69  
70      /**
71       * For testing purposes.
72       */
73      void testSetFinished()
74      {
75          if ( canContinue() )
76          {
77              commands.add( TEST_SET_FINISHED );
78              barrier.release();
79          }
80      }
81  
82      @Override
83      public void skipSinceNextTest()
84      {
85          if ( canContinue() )
86          {
87              commands.add( SKIP_SINCE_NEXT_TEST );
88              barrier.release();
89          }
90      }
91  
92      @Override
93      public void shutdown( Shutdown shutdownType )
94      {
95          if ( canContinue() )
96          {
97              commands.add( toShutdown( shutdownType ) );
98              barrier.release();
99          }
100     }
101 
102     @Override
103     public void noop()
104     {
105         if ( canContinue() )
106         {
107             commands.add( NOOP );
108             barrier.release();
109         }
110     }
111 
112     @Override
113     public void acknowledgeByeEventReceived()
114     {
115         if ( canContinue() )
116         {
117             commands.add( BYE_ACK );
118             barrier.release();
119         }
120     }
121 
122     @Override
123     protected Command nextCommand()
124     {
125         Command cmd = commands.poll();
126         if ( cmd == null )
127         {
128             String cmdData = testClassNames.poll();
129             return cmdData == null ? TEST_SET_FINISHED : toRunClass( cmdData );
130         }
131         else
132         {
133             return cmd;
134         }
135     }
136 
137     @Override
138     protected void beforeNextCommand()
139         throws IOException
140     {
141         awaitNextTest();
142     }
143 
144     @Override
145     public boolean isClosed()
146     {
147         return closed.get();
148     }
149 
150     /**
151      * Signal that a new test is to be provided.
152      */
153     @Override
154     public void provideNewTest()
155     {
156         if ( canContinue() )
157         {
158             barrier.release();
159         }
160     }
161 
162     @Override
163     public void close()
164     {
165         if ( closed.compareAndSet( false, true ) )
166         {
167             barrier.drainPermits();
168             barrier.release();
169         }
170     }
171 
172     private void awaitNextTest()
173         throws IOException
174     {
175         try
176         {
177             barrier.acquire();
178         }
179         catch ( InterruptedException e )
180         {
181             throw new IOException( e.getLocalizedMessage(), e );
182         }
183     }
184 }