View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.junitcore;
20  
21  import java.util.Map;
22  
23  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
24  import org.apache.maven.surefire.api.report.ReportEntry;
25  import org.apache.maven.surefire.api.report.ReporterFactory;
26  import org.apache.maven.surefire.api.report.StackTraceWriter;
27  import org.apache.maven.surefire.api.report.TestOutputReportEntry;
28  import org.apache.maven.surefire.api.report.TestReportListener;
29  import org.apache.maven.surefire.api.report.TestSetReportEntry;
30  
31  import static java.lang.ThreadLocal.withInitial;
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  @Deprecated // remove this class after StatelessXmlReporter is capable of parallel test sets processing
46  abstract class ConcurrentRunListener implements TestReportListener<TestOutputReportEntry> {
47      private final Map<String, TestSet> classMethodCounts;
48  
49      private final ThreadLocal<TestReportListener<TestOutputReportEntry>> reporterManagerThreadLocal;
50  
51      private final boolean reportImmediately;
52  
53      private final ConsoleLogger logger;
54  
55      ConcurrentRunListener(
56              final ReporterFactory reporterFactory, boolean reportImmediately, Map<String, TestSet> classMethodCounts) {
57          this.reportImmediately = reportImmediately;
58          this.classMethodCounts = classMethodCounts;
59          logger = reporterFactory.createTestReportListener();
60          reporterManagerThreadLocal = withInitial(reporterFactory::createTestReportListener);
61      }
62  
63      @Override
64      public void testSetStarting(TestSetReportEntry description) {}
65  
66      @Override
67      public void testSetCompleted(TestSetReportEntry result) {
68          try {
69              TestReportListener<TestOutputReportEntry> reporterManager = getRunListener();
70              for (TestSet testSet : classMethodCounts.values()) {
71                  testSet.replay(reporterManager);
72              }
73          } finally {
74              reporterManagerThreadLocal.remove();
75          }
76      }
77  
78      @Override
79      public void testFailed(ReportEntry failure) {
80          final TestMethod testMethod = getOrCreateThreadAttachedTestMethod(failure);
81          if (testMethod != null) {
82              testMethod.testFailure(failure);
83              testMethod.detachFromCurrentThread();
84          }
85      }
86  
87      @Override
88      public void testError(ReportEntry failure) {
89          final TestMethod testMethod = getOrCreateThreadAttachedTestMethod(failure);
90          if (testMethod != null) {
91              testMethod.testError(failure);
92              testMethod.detachFromCurrentThread();
93          }
94      }
95  
96      @Override
97      public void testSkipped(ReportEntry description) {
98          TestSet testSet = getTestSet(description);
99          TestMethod testMethod = testSet.createThreadAttachedTestMethod(description);
100         testMethod.testIgnored(description);
101         testSet.incrementFinishedTests(getRunListener(), reportImmediately);
102         testMethod.detachFromCurrentThread();
103     }
104 
105     @Override
106     public void testExecutionSkippedByUser() {
107         // cannot guarantee proper call to all listeners
108         getRunListener().testExecutionSkippedByUser();
109     }
110 
111     @Override
112     public void testAssumptionFailure(ReportEntry failure) {
113         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod(failure);
114         if (testMethod != null) {
115             testMethod.testAssumption(failure);
116             testMethod.detachFromCurrentThread();
117         }
118     }
119 
120     @Override
121     public void testStarting(ReportEntry description) {
122         TestSet testSet = getTestSet(description);
123         testSet.createThreadAttachedTestMethod(description);
124 
125         checkIfTestSetCanBeReported(testSet);
126         testSet.attachToThread();
127     }
128 
129     @Override
130     public void testSucceeded(ReportEntry report) {
131         TestMethod testMethod = getThreadTestMethod();
132         if (testMethod != null) {
133             testMethod.testFinished();
134             testMethod.getTestSet().incrementFinishedTests(getRunListener(), reportImmediately);
135             testMethod.detachFromCurrentThread();
136         }
137     }
138 
139     private TestMethod getOrCreateThreadAttachedTestMethod(ReportEntry description) {
140         TestMethod threadTestMethod = getThreadTestMethod();
141         if (threadTestMethod != null) {
142             return threadTestMethod;
143         }
144         TestSet testSet = getTestSet(description);
145         if (testSet == null) {
146             logger.warning(description.getName());
147             StackTraceWriter writer = description.getStackTraceWriter();
148             if (writer != null) {
149                 logger.error(writer.writeTraceToString());
150             }
151             return null;
152         } else {
153             return testSet.createThreadAttachedTestMethod(description);
154         }
155     }
156 
157     protected abstract void checkIfTestSetCanBeReported(TestSet testSetForTest);
158 
159     private TestSet getTestSet(ReportEntry description) {
160         return classMethodCounts.get(description.getSourceName());
161     }
162 
163     final TestReportListener<TestOutputReportEntry> getRunListener() {
164         return reporterManagerThreadLocal.get();
165     }
166 
167     public static ConcurrentRunListener createInstance(
168             Map<String, TestSet> classMethodCounts,
169             ReporterFactory reporterFactory,
170             boolean parallelClasses,
171             boolean parallelBoth) {
172         return parallelClasses
173                 ? new ClassesParallelRunListener(classMethodCounts, reporterFactory)
174                 : new MethodsParallelRunListener(classMethodCounts, reporterFactory, !parallelBoth);
175     }
176 
177     @Override
178     public void writeTestOutput(TestOutputReportEntry reportEntry) {
179         TestMethod threadTestMethod = getThreadTestMethod();
180         if (threadTestMethod != null) {
181             LogicalStream logicalStream = threadTestMethod.getLogicalStream();
182             logicalStream.write(reportEntry);
183         } else {
184             // Not able to associate output with any thread. Just dump to console
185             logger.info(reportEntry.getLog());
186         }
187     }
188 
189     @Override
190     public boolean isDebugEnabled() {
191         return logger.isDebugEnabled();
192     }
193 
194     @Override
195     public void debug(String message) {
196         logger.debug(message);
197     }
198 
199     @Override
200     public boolean isInfoEnabled() {
201         return logger.isInfoEnabled();
202     }
203 
204     @Override
205     public void info(String message) {
206         logger.info(message);
207     }
208 
209     @Override
210     public boolean isWarnEnabled() {
211         return logger.isWarnEnabled();
212     }
213 
214     @Override
215     public void warning(String message) {
216         logger.warning(message);
217     }
218 
219     @Override
220     public boolean isErrorEnabled() {
221         return logger.isErrorEnabled();
222     }
223 
224     @Override
225     public void error(String message) {
226         logger.error(message);
227     }
228 
229     @Override
230     public void error(String message, Throwable t) {
231         logger.error(message, t);
232     }
233 
234     @Override
235     public void error(Throwable t) {
236         logger.error(t);
237     }
238 }