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   * After providing each test class name, followed by a newline character, a flush is performed on the
44   * {@link FlushReceiver} provided by the {@link FlushReceiverProvider} that can be set using
45   * {@link #setFlushReceiverProvider(FlushReceiverProvider)}.
46   * <br>
47   * The instance is used only in reusable forks in {@link org.apache.maven.plugin.surefire.booterclient.ForkStarter}
48   * by one Thread.
49   *
50   * @author Andreas Gudian
51   * @author Tibor Digana (tibor17)
52   */
53  public final class TestProvidingInputStream
54          extends DefaultCommandReader
55  {
56      private final Semaphore barrier = new Semaphore( 0 );
57  
58      private final Queue<Command> commands = new ConcurrentLinkedQueue<>();
59  
60      private final AtomicBoolean closed = new AtomicBoolean();
61  
62      private final Queue<String> testClassNames;
63  
64      /**
65       * C'tor
66       *
67       * @param testClassNames source of the tests to be read from this stream
68       */
69      public TestProvidingInputStream( Queue<String> testClassNames )
70      {
71          this.testClassNames = testClassNames;
72      }
73  
74      /**
75       * For testing purposes.
76       */
77      void testSetFinished()
78      {
79          if ( canContinue() )
80          {
81              commands.add( TEST_SET_FINISHED );
82              barrier.release();
83          }
84      }
85  
86      @Override
87      public void skipSinceNextTest()
88      {
89          if ( canContinue() )
90          {
91              commands.add( SKIP_SINCE_NEXT_TEST );
92              barrier.release();
93          }
94      }
95  
96      @Override
97      public void shutdown( Shutdown shutdownType )
98      {
99          if ( canContinue() )
100         {
101             commands.add( toShutdown( shutdownType ) );
102             barrier.release();
103         }
104     }
105 
106     @Override
107     public void noop()
108     {
109         if ( canContinue() )
110         {
111             commands.add( NOOP );
112             barrier.release();
113         }
114     }
115 
116     @Override
117     public void acknowledgeByeEventReceived()
118     {
119         if ( canContinue() )
120         {
121             commands.add( BYE_ACK );
122             barrier.release();
123         }
124     }
125 
126     @Override
127     protected Command nextCommand()
128     {
129         Command cmd = commands.poll();
130         if ( cmd == null )
131         {
132             String cmdData = testClassNames.poll();
133             return cmdData == null ? TEST_SET_FINISHED : toRunClass( cmdData );
134         }
135         else
136         {
137             return cmd;
138         }
139     }
140 
141     @Override
142     protected void beforeNextCommand()
143         throws IOException
144     {
145         awaitNextTest();
146     }
147 
148     @Override
149     public boolean isClosed()
150     {
151         return closed.get();
152     }
153 
154     /**
155      * Signal that a new test is to be provided.
156      */
157     @Override
158     public void provideNewTest()
159     {
160         if ( canContinue() )
161         {
162             barrier.release();
163         }
164     }
165 
166     @Override
167     public void close()
168     {
169         if ( closed.compareAndSet( false, true ) )
170         {
171             barrier.drainPermits();
172             barrier.release();
173         }
174     }
175 
176     private void awaitNextTest()
177         throws IOException
178     {
179         try
180         {
181             barrier.acquire();
182         }
183         catch ( InterruptedException e )
184         {
185             throw new IOException( e.getLocalizedMessage(), e );
186         }
187     }
188 }