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