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