View Javadoc
1   package org.apache.maven.surefire.junitcore;
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 java.util.Map;
23  import org.apache.maven.surefire.report.ConsoleOutputReceiver;
24  import org.apache.maven.surefire.report.ConsoleStream;
25  import org.apache.maven.surefire.report.ReportEntry;
26  import org.apache.maven.surefire.report.ReporterFactory;
27  import org.apache.maven.surefire.report.RunListener;
28  import org.apache.maven.surefire.report.StackTraceWriter;
29  import org.apache.maven.surefire.report.TestSetReportEntry;
30  import org.apache.maven.surefire.testset.TestSetFailedException;
31  
32  import static org.apache.maven.surefire.junitcore.TestMethod.getThreadTestMethod;
33  
34  /**
35   * Handles responses from concurrent junit
36   * <br>
37   * Stuff to remember about JUnit threading:
38   * parallel=classes; beforeClass/afterClass, constructor and all tests method run on same thread
39   * parallel=methods; beforeClass/afterClass run on main thread, constructor + each test method run on same thread
40   * parallel=both; same as parallel=methods
41   *
42   * @see org.apache.maven.surefire.junitcore.JUnitCoreRunListener for details about regular junit run listening
43   * @author Kristian Rosenvold
44   */
45  public abstract class ConcurrentRunListener
46      implements RunListener, ConsoleOutputReceiver
47  {
48      private final Map<String, TestSet> classMethodCounts;
49  
50      private final ThreadLocal<RunListener> reporterManagerThreadLocal;
51  
52      private final boolean reportImmediately;
53  
54      private final ConsoleStream consoleStream;
55  
56      ConcurrentRunListener( final ReporterFactory reporterFactory, ConsoleStream consoleStream,
57                             boolean reportImmediately, Map<String, TestSet> classMethodCounts )
58          throws TestSetFailedException
59      {
60          this.reportImmediately = reportImmediately;
61          this.classMethodCounts = classMethodCounts;
62          this.consoleStream = consoleStream;
63          reporterManagerThreadLocal = new ThreadLocal<RunListener>()
64          {
65              @Override
66              protected RunListener initialValue()
67              {
68                  return reporterFactory.createReporter();
69              }
70          };
71      }
72  
73      @Override
74      public void testSetStarting( TestSetReportEntry description )
75      {
76      }
77  
78      @Override
79      public void testSetCompleted( TestSetReportEntry result )
80      {
81          final RunListener reporterManager = getRunListener();
82          for ( TestSet testSet : classMethodCounts.values() )
83          {
84              testSet.replay( reporterManager );
85          }
86          reporterManagerThreadLocal.remove();
87      }
88  
89      @Override
90      public void testFailed( ReportEntry failure )
91      {
92          final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
93          if ( testMethod != null )
94          {
95              testMethod.testFailure( failure );
96              testMethod.detachFromCurrentThread();
97          }
98      }
99  
100     @Override
101     public void testError( ReportEntry failure )
102     {
103         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
104         if ( testMethod != null )
105         {
106             testMethod.testError( failure );
107             testMethod.detachFromCurrentThread();
108         }
109     }
110 
111     @Override
112     public void testSkipped( ReportEntry description )
113     {
114         TestSet testSet = getTestSet( description );
115         TestMethod testMethod = testSet.createThreadAttachedTestMethod( description );
116         testMethod.testIgnored( description );
117         testSet.incrementFinishedTests( getRunListener(), reportImmediately );
118         testMethod.detachFromCurrentThread();
119     }
120 
121     @Override
122     public void testExecutionSkippedByUser()
123     {
124         // cannot guarantee proper call to all listeners
125         reporterManagerThreadLocal.get().testExecutionSkippedByUser();
126     }
127 
128     @Override
129     public void testAssumptionFailure( ReportEntry failure )
130     {
131         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
132         if ( testMethod != null )
133         {
134             testMethod.testAssumption( failure );
135             testMethod.detachFromCurrentThread();
136         }
137     }
138 
139     @Override
140     public void testStarting( ReportEntry description )
141     {
142         TestSet testSet = getTestSet( description );
143         testSet.createThreadAttachedTestMethod( description );
144 
145         checkIfTestSetCanBeReported( testSet );
146         testSet.attachToThread();
147     }
148 
149     @Override
150     public void testSucceeded( ReportEntry report )
151     {
152         TestMethod testMethod = getThreadTestMethod();
153         if ( testMethod != null )
154         {
155             testMethod.testFinished();
156             testMethod.getTestSet().incrementFinishedTests( getRunListener(), reportImmediately );
157             testMethod.detachFromCurrentThread();
158         }
159     }
160 
161     private TestMethod getOrCreateThreadAttachedTestMethod( ReportEntry description )
162     {
163         TestMethod threadTestMethod = getThreadTestMethod();
164         if ( threadTestMethod != null )
165         {
166             return threadTestMethod;
167         }
168         TestSet testSet = getTestSet( description );
169         if ( testSet == null )
170         {
171             consoleStream.println( description.getName() );
172             StackTraceWriter writer = description.getStackTraceWriter();
173             if ( writer != null )
174             {
175                 consoleStream.println( writer.writeTraceToString() );
176             }
177             return null;
178         }
179         else
180         {
181             return testSet.createThreadAttachedTestMethod( description );
182         }
183     }
184 
185     protected abstract void checkIfTestSetCanBeReported( TestSet testSetForTest );
186 
187     private TestSet getTestSet( ReportEntry description )
188     {
189         return classMethodCounts.get( description.getSourceName() );
190     }
191 
192     RunListener getRunListener()
193     {
194         return reporterManagerThreadLocal.get();
195     }
196 
197     public static ConcurrentRunListener createInstance( Map<String, TestSet> classMethodCounts,
198                                                             ReporterFactory reporterFactory,
199                                                             boolean parallelClasses, boolean parallelBoth,
200                                                             ConsoleStream consoleStream )
201         throws TestSetFailedException
202     {
203         return parallelClasses
204             ? new ClassesParallelRunListener( classMethodCounts, reporterFactory, consoleStream )
205             : new MethodsParallelRunListener( classMethodCounts, reporterFactory, !parallelBoth, consoleStream );
206     }
207 
208 
209     @Override
210     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
211     {
212         TestMethod threadTestMethod = getThreadTestMethod();
213         if ( threadTestMethod != null )
214         {
215             LogicalStream logicalStream = threadTestMethod.getLogicalStream();
216             logicalStream.write( stdout, buf, off, len );
217         }
218         else
219         {
220             // Not able to associate output with any thread. Just dump to console
221             consoleStream.println( buf, off, len );
222         }
223     }
224 }