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          try
82          {
83              final RunListener reporterManager = getRunListener();
84              for ( TestSet testSet : classMethodCounts.values() )
85              {
86                  testSet.replay( reporterManager );
87              }
88          }
89          finally
90          {
91              reporterManagerThreadLocal.remove();
92          }
93      }
94  
95      @Override
96      public void testFailed( ReportEntry failure )
97      {
98          final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
99          if ( testMethod != null )
100         {
101             testMethod.testFailure( failure );
102             testMethod.detachFromCurrentThread();
103         }
104     }
105 
106     @Override
107     public void testError( ReportEntry failure )
108     {
109         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
110         if ( testMethod != null )
111         {
112             testMethod.testError( failure );
113             testMethod.detachFromCurrentThread();
114         }
115     }
116 
117     @Override
118     public void testSkipped( ReportEntry description )
119     {
120         TestSet testSet = getTestSet( description );
121         TestMethod testMethod = testSet.createThreadAttachedTestMethod( description );
122         testMethod.testIgnored( description );
123         testSet.incrementFinishedTests( getRunListener(), reportImmediately );
124         testMethod.detachFromCurrentThread();
125     }
126 
127     @Override
128     public void testExecutionSkippedByUser()
129     {
130         // cannot guarantee proper call to all listeners
131         getRunListener().testExecutionSkippedByUser();
132     }
133 
134     @Override
135     public void testAssumptionFailure( ReportEntry failure )
136     {
137         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
138         if ( testMethod != null )
139         {
140             testMethod.testAssumption( failure );
141             testMethod.detachFromCurrentThread();
142         }
143     }
144 
145     @Override
146     public void testStarting( ReportEntry description )
147     {
148         TestSet testSet = getTestSet( description );
149         testSet.createThreadAttachedTestMethod( description );
150 
151         checkIfTestSetCanBeReported( testSet );
152         testSet.attachToThread();
153     }
154 
155     @Override
156     public void testSucceeded( ReportEntry report )
157     {
158         TestMethod testMethod = getThreadTestMethod();
159         if ( testMethod != null )
160         {
161             testMethod.testFinished();
162             testMethod.getTestSet().incrementFinishedTests( getRunListener(), reportImmediately );
163             testMethod.detachFromCurrentThread();
164         }
165     }
166 
167     private TestMethod getOrCreateThreadAttachedTestMethod( ReportEntry description )
168     {
169         TestMethod threadTestMethod = getThreadTestMethod();
170         if ( threadTestMethod != null )
171         {
172             return threadTestMethod;
173         }
174         TestSet testSet = getTestSet( description );
175         if ( testSet == null )
176         {
177             consoleStream.println( description.getName() );
178             StackTraceWriter writer = description.getStackTraceWriter();
179             if ( writer != null )
180             {
181                 consoleStream.println( writer.writeTraceToString() );
182             }
183             return null;
184         }
185         else
186         {
187             return testSet.createThreadAttachedTestMethod( description );
188         }
189     }
190 
191     protected abstract void checkIfTestSetCanBeReported( TestSet testSetForTest );
192 
193     private TestSet getTestSet( ReportEntry description )
194     {
195         return classMethodCounts.get( description.getSourceName() );
196     }
197 
198     RunListener getRunListener()
199     {
200         return reporterManagerThreadLocal.get();
201     }
202 
203     public static ConcurrentRunListener createInstance( Map<String, TestSet> classMethodCounts,
204                                                             ReporterFactory reporterFactory,
205                                                             boolean parallelClasses, boolean parallelBoth,
206                                                             ConsoleStream consoleStream )
207         throws TestSetFailedException
208     {
209         return parallelClasses
210             ? new ClassesParallelRunListener( classMethodCounts, reporterFactory, consoleStream )
211             : new MethodsParallelRunListener( classMethodCounts, reporterFactory, !parallelBoth, consoleStream );
212     }
213 
214 
215     @Override
216     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
217     {
218         TestMethod threadTestMethod = getThreadTestMethod();
219         if ( threadTestMethod != null )
220         {
221             LogicalStream logicalStream = threadTestMethod.getLogicalStream();
222             logicalStream.write( stdout, buf, off, len );
223         }
224         else
225         {
226             // Not able to associate output with any thread. Just dump to console
227             consoleStream.println( buf, off, len );
228         }
229     }
230 }