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.plugin.surefire.report;
20  
21  import java.io.IOException;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.ConcurrentLinkedQueue;
27  import java.util.concurrent.ConcurrentMap;
28  
29  import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
30  import org.apache.maven.surefire.api.report.ReportEntry;
31  import org.apache.maven.surefire.api.report.TestOutputReportEntry;
32  import org.apache.maven.surefire.api.report.TestReportListener;
33  import org.apache.maven.surefire.api.report.TestSetReportEntry;
34  import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
35  import org.apache.maven.surefire.extensions.StatelessReportEventListener;
36  import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
37  import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
38  
39  import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
40  import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
41  import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
42  import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
43  
44  /**
45   * Reports data for a single test set.
46   * <br>
47   *
48   * @author Kristian Rosenvold
49   */
50  public class TestSetRunListener implements TestReportListener<TestOutputReportEntry> {
51      private final Queue<TestMethodStats> testMethodStats = new ConcurrentLinkedQueue<>();
52  
53      /**
54       * will be used only if report entry have a sourceName other than that #currentTestSetStats will be used
55       * it looks some provider doesn't provide enough information so we assume to use previous technique
56       * class field (this is definitely hackish)
57       */
58      private final ConcurrentMap<String, TestSetStats> detailsPerSource = new ConcurrentHashMap<>();
59  
60      private final TestSetStats currentTestSetStats;
61  
62      private final ConsoleOutputReportEventListener testOutputReceiver;
63  
64      private final boolean briefOrPlainFormat;
65  
66      private final StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter;
67  
68      private final StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> consoleReporter;
69  
70      private final StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> fileReporter;
71  
72      private final StatisticsReporter statisticsReporter;
73  
74      private final Object lock;
75  
76      private final boolean trimStackTrace;
77  
78      private final boolean isPlainFormat;
79  
80      private final boolean statPerSourceName;
81  
82      private Utf8RecodingDeferredFileOutputStream testStdOut = initDeferred("stdout");
83  
84      private Utf8RecodingDeferredFileOutputStream testStdErr = initDeferred("stderr");
85  
86      @SuppressWarnings("checkstyle:parameternumber")
87      public TestSetRunListener(
88              StatelessTestsetInfoConsoleReportEventListener<WrappedReportEntry, TestSetStats> consoleReporter,
89              StatelessTestsetInfoFileReportEventListener<WrappedReportEntry, TestSetStats> fileReporter,
90              StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter,
91              ConsoleOutputReportEventListener testOutputReceiver,
92              StatisticsReporter statisticsReporter,
93              boolean trimStackTrace,
94              boolean isPlainFormat,
95              boolean briefOrPlainFormat,
96              Object lock,
97              boolean statPerSourceName) {
98          this.consoleReporter = consoleReporter;
99          this.fileReporter = fileReporter;
100         this.statisticsReporter = statisticsReporter;
101         this.simpleXMLReporter = simpleXMLReporter;
102         this.testOutputReceiver = testOutputReceiver;
103         this.briefOrPlainFormat = briefOrPlainFormat;
104         this.trimStackTrace = trimStackTrace;
105         this.isPlainFormat = isPlainFormat;
106         this.currentTestSetStats = new TestSetStats(trimStackTrace, isPlainFormat);
107         this.lock = lock;
108         this.statPerSourceName = statPerSourceName;
109     }
110 
111     @Override
112     public boolean isDebugEnabled() {
113         return consoleReporter.getConsoleLogger().isDebugEnabled();
114     }
115 
116     @Override
117     public void debug(String message) {
118         synchronized (lock) {
119             consoleReporter.getConsoleLogger().debug(trimTrailingNewLine(message));
120         }
121     }
122 
123     @Override
124     public boolean isInfoEnabled() {
125         return consoleReporter.getConsoleLogger().isInfoEnabled();
126     }
127 
128     @Override
129     public void info(String message) {
130         synchronized (lock) {
131             consoleReporter.getConsoleLogger().info(trimTrailingNewLine(message));
132         }
133     }
134 
135     @Override
136     public boolean isWarnEnabled() {
137         return consoleReporter.getConsoleLogger().isWarnEnabled();
138     }
139 
140     @Override
141     public void warning(String message) {
142         synchronized (lock) {
143             consoleReporter.getConsoleLogger().warning(trimTrailingNewLine(message));
144         }
145     }
146 
147     @Override
148     public boolean isErrorEnabled() {
149         return consoleReporter.getConsoleLogger().isErrorEnabled();
150     }
151 
152     @Override
153     public void error(String message) {
154         synchronized (lock) {
155             consoleReporter.getConsoleLogger().error(trimTrailingNewLine(message));
156         }
157     }
158 
159     @Override
160     public void error(String message, Throwable t) {
161         synchronized (lock) {
162             consoleReporter.getConsoleLogger().error(trimTrailingNewLine(message), t);
163         }
164     }
165 
166     @Override
167     public void error(Throwable t) {
168         synchronized (lock) {
169             consoleReporter.getConsoleLogger().error(t);
170         }
171     }
172 
173     @Override
174     public void writeTestOutput(TestOutputReportEntry reportEntry) {
175         try {
176             synchronized (lock) {
177                 Utf8RecodingDeferredFileOutputStream stream = reportEntry.isStdOut() ? testStdOut : testStdErr;
178                 stream.write(reportEntry.getLog(), reportEntry.isNewLine());
179                 testOutputReceiver.writeTestOutput(reportEntry);
180             }
181         } catch (IOException e) {
182             throw new RuntimeException(e);
183         }
184     }
185 
186     private TestSetStats getTestSetStats(ReportEntry report) {
187         if (statPerSourceName) {
188             return detailsPerSource.computeIfAbsent(
189                     report.getSourceName(), s -> new TestSetStats(trimStackTrace, isPlainFormat));
190         }
191         return currentTestSetStats;
192     }
193 
194     @Override
195     public void testSetStarting(TestSetReportEntry report) {
196         getTestSetStats(report).testSetStart();
197         consoleReporter.testSetStarting(report);
198         testOutputReceiver.testSetStarting(report);
199     }
200 
201     private void clearCapture() {
202         if (testStdOut != null) {
203             testStdOut.commit();
204         }
205 
206         if (testStdErr != null) {
207             testStdErr.commit();
208         }
209 
210         testStdOut = initDeferred("stdout");
211         testStdErr = initDeferred("stderr");
212     }
213 
214     @Override
215     public void testSetCompleted(TestSetReportEntry report) {
216         final WrappedReportEntry wrap = wrapTestSet(report);
217         TestSetStats testSetStats = getTestSetStats(report);
218         final List<String> testResults = briefOrPlainFormat ? testSetStats.getTestResults() : Collections.emptyList();
219         fileReporter.testSetCompleted(wrap, testSetStats, testResults);
220         simpleXMLReporter.testSetCompleted(wrap, testSetStats);
221         statisticsReporter.testSetCompleted();
222         consoleReporter.testSetCompleted(wrap, testSetStats, testResults);
223         testOutputReceiver.testSetCompleted(wrap);
224         consoleReporter.reset();
225 
226         wrap.getStdout().free();
227         wrap.getStdErr().free();
228 
229         addTestMethodStats(report);
230         testSetStats.reset();
231         clearCapture();
232     }
233 
234     // ----------------------------------------------------------------------
235     // Test callback methods:
236     // ----------------------------------------------------------------------
237 
238     @Override
239     public void testStarting(ReportEntry report) {
240         getTestSetStats(report).testStart();
241     }
242 
243     @Override
244     public void testSucceeded(ReportEntry reportEntry) {
245         WrappedReportEntry wrapped = wrap(reportEntry, SUCCESS);
246         getTestSetStats(reportEntry).testSucceeded(wrapped);
247         statisticsReporter.testSucceeded(reportEntry);
248         clearCapture();
249     }
250 
251     @Override
252     public void testError(ReportEntry reportEntry) {
253         WrappedReportEntry wrapped = wrap(reportEntry, ERROR);
254         getTestSetStats(reportEntry).testError(wrapped);
255         statisticsReporter.testError(reportEntry);
256         clearCapture();
257     }
258 
259     @Override
260     public void testFailed(ReportEntry reportEntry) {
261         WrappedReportEntry wrapped = wrap(reportEntry, FAILURE);
262         getTestSetStats(reportEntry).testFailure(wrapped);
263         statisticsReporter.testFailed(reportEntry);
264         clearCapture();
265     }
266 
267     // ----------------------------------------------------------------------
268     // Counters
269     // ----------------------------------------------------------------------
270 
271     @Override
272     public void testSkipped(ReportEntry reportEntry) {
273         WrappedReportEntry wrapped = wrap(reportEntry, SKIPPED);
274         getTestSetStats(reportEntry).testSkipped(wrapped);
275         statisticsReporter.testSkipped(reportEntry);
276         clearCapture();
277     }
278 
279     @Override
280     public void testExecutionSkippedByUser() {
281         clearCapture();
282     }
283 
284     @Override
285     public void testAssumptionFailure(ReportEntry report) {
286         testSkipped(report);
287     }
288 
289     private WrappedReportEntry wrap(ReportEntry other, ReportEntryType reportEntryType) {
290         int estimatedElapsed = 0;
291         if (reportEntryType != SKIPPED) {
292             Integer etime = other.getElapsed();
293             estimatedElapsed = etime == null ? getTestSetStats(other).getElapsedSinceLastStart() : etime;
294         }
295 
296         return new WrappedReportEntry(other, reportEntryType, estimatedElapsed, testStdOut, testStdErr);
297     }
298 
299     private WrappedReportEntry wrapTestSet(TestSetReportEntry other) {
300         return new WrappedReportEntry(
301                 other,
302                 null,
303                 other.getElapsed() != null
304                         ? other.getElapsed()
305                         : getTestSetStats(other).getElapsedSinceTestSetStart(),
306                 testStdOut,
307                 testStdErr,
308                 other.getSystemProperties());
309     }
310 
311     public void close() {
312         testOutputReceiver.close();
313     }
314 
315     private void addTestMethodStats(TestSetReportEntry report) {
316         for (WrappedReportEntry reportEntry : getTestSetStats(report).getReportEntries()) {
317             TestMethodStats methodStats = new TestMethodStats(
318                     reportEntry.getClassMethodName(),
319                     reportEntry.getReportEntryType(),
320                     reportEntry.getStackTraceWriter());
321             testMethodStats.add(methodStats);
322         }
323     }
324 
325     public Queue<TestMethodStats> getTestMethodStats() {
326         return testMethodStats;
327     }
328 
329     private static String trimTrailingNewLine(final String message) {
330         final int e = message == null ? 0 : lineBoundSymbolWidth(message);
331         return message != null && e != 0 ? message.substring(0, message.length() - e) : message;
332     }
333 
334     private static int lineBoundSymbolWidth(String message) {
335         return message.endsWith("\r\n") ? 2 : (message.endsWith("\n") || message.endsWith("\r") ? 1 : 0);
336     }
337 
338     private static Utf8RecodingDeferredFileOutputStream initDeferred(String channel) {
339         return new Utf8RecodingDeferredFileOutputStream(channel);
340     }
341 }