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   * <p/>
40   * The Stream provides only one test at a time, but only after {@link #provideNewTest()} has been invoked.
41   * <p/>
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   * <p/>
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<Command>();
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      public void skipSinceNextTest()
86      {
87          if ( canContinue() )
88          {
89              commands.add( SKIP_SINCE_NEXT_TEST );
90              barrier.release();
91          }
92      }
93  
94      public void shutdown( Shutdown shutdownType )
95      {
96          if ( canContinue() )
97          {
98              commands.add( toShutdown( shutdownType ) );
99              barrier.release();
100         }
101     }
102 
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 ? Command.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     protected boolean isClosed()
146     {
147         return closed.get();
148     }
149 
150     /**
151      * Signal that a new test is to be provided.
152      */
153     public void provideNewTest()
154     {
155         if ( canContinue() )
156         {
157             barrier.release();
158         }
159     }
160 
161     @Override
162     public void close()
163     {
164         if ( closed.compareAndSet( false, true ) )
165         {
166             invalidateInternalBuffer();
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             // help GC to free this object because StreamFeeder Thread cannot read it anyway after IOE
182             invalidateInternalBuffer();
183             throw new IOException( e.getLocalizedMessage() );
184         }
185     }
186 }