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