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.MasterProcessCommand.TEST_SET_FINISHED;
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     protected Command nextCommand()
114     {
115         Command cmd = commands.poll();
116         if ( cmd == null )
117         {
118             String cmdData = testClassNames.poll();
119             return cmdData == null ? Command.TEST_SET_FINISHED : toRunClass( cmdData );
120         }
121         else
122         {
123             return cmd;
124         }
125     }
126 
127     @Override
128     protected void beforeNextCommand()
129         throws IOException
130     {
131         awaitNextTest();
132     }
133 
134     @Override
135     protected boolean isClosed()
136     {
137         return closed.get();
138     }
139 
140     @Override
141     protected boolean canContinue()
142     {
143         return getLastCommand() != TEST_SET_FINISHED && !isClosed();
144     }
145 
146     /**
147      * Signal that a new test is to be provided.
148      */
149     public void provideNewTest()
150     {
151         if ( canContinue() )
152         {
153             barrier.release();
154         }
155     }
156 
157     @Override
158     public void close()
159     {
160         if ( closed.compareAndSet( false, true ) )
161         {
162             invalidateInternalBuffer();
163             barrier.drainPermits();
164             barrier.release();
165         }
166     }
167 
168     private void awaitNextTest()
169         throws IOException
170     {
171         try
172         {
173             barrier.acquire();
174         }
175         catch ( InterruptedException e )
176         {
177             // help GC to free this object because StreamFeeder Thread cannot read it anyway after IOE
178             invalidateInternalBuffer();
179             throw new IOException( e.getLocalizedMessage() );
180         }
181     }
182 }