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.util.ArrayList;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentLinkedQueue;
26  
27  import org.apache.maven.surefire.api.report.ReportEntry;
28  import org.apache.maven.surefire.shared.utils.logging.MessageBuilder;
29  
30  import static org.apache.maven.surefire.api.report.CategorizedReportEntry.GROUP_PREFIX;
31  import static org.apache.maven.surefire.shared.utils.logging.MessageUtils.buffer;
32  
33  /**
34   * Maintains per-thread test result state. Not thread safe.
35   */
36  public class TestSetStats {
37      private static final String TESTS = "Tests ";
38      private static final String RUN = "run: ";
39      private static final String TESTS_RUN = "Tests run: ";
40      private static final String FAILURES = "Failures: ";
41      private static final String ERRORS = "Errors: ";
42      private static final String SKIPPED = "Skipped: ";
43      private static final String FAILURE_MARKER = " <<< FAILURE!";
44      private static final String IN_MARKER = " -- in ";
45      private static final String COMMA = ", ";
46  
47      private final Queue<WrappedReportEntry> reportEntries = new ConcurrentLinkedQueue<>();
48  
49      private final boolean trimStackTrace;
50  
51      private final boolean plainFormat;
52  
53      private long testSetStartAt;
54  
55      private long testStartAt;
56  
57      private int completedCount;
58  
59      private int errors;
60  
61      private int failures;
62  
63      private int skipped;
64  
65      private long lastStartAt;
66  
67      public TestSetStats(boolean trimStackTrace, boolean plainFormat) {
68          this.trimStackTrace = trimStackTrace;
69          this.plainFormat = plainFormat;
70      }
71  
72      public int getElapsedSinceTestSetStart() {
73          return testSetStartAt > 0 ? (int) (System.currentTimeMillis() - testSetStartAt) : 0;
74      }
75  
76      public int getElapsedSinceLastStart() {
77          return lastStartAt > 0 ? (int) (System.currentTimeMillis() - lastStartAt) : 0;
78      }
79  
80      public void testSetStart() {
81          testSetStartAt = System.currentTimeMillis();
82          lastStartAt = testSetStartAt;
83      }
84  
85      long getTestSetStartAt() {
86          return testSetStartAt;
87      }
88  
89      public void testStart() {
90          testStartAt = System.currentTimeMillis();
91          lastStartAt = testStartAt;
92      }
93  
94      long getTestStartAt() {
95          return testStartAt;
96      }
97  
98      private void finishTest(WrappedReportEntry reportEntry) {
99          reportEntries.add(reportEntry);
100         incrementCompletedCount();
101         // SUREFIRE-398 skipped tests call endTest without calling testStarting
102         // if startTime = 0, set it to endTime, so the diff will be 0
103         if (testStartAt == 0) {
104             testStartAt = System.currentTimeMillis();
105         }
106     }
107 
108     public void testSucceeded(WrappedReportEntry reportEntry) {
109         finishTest(reportEntry);
110     }
111 
112     public void testError(WrappedReportEntry reportEntry) {
113         errors += 1;
114         finishTest(reportEntry);
115     }
116 
117     public void testFailure(WrappedReportEntry reportEntry) {
118         failures += 1;
119         finishTest(reportEntry);
120     }
121 
122     public void testSkipped(WrappedReportEntry reportEntry) {
123         skipped += 1;
124         finishTest(reportEntry);
125     }
126 
127     public void reset() {
128         completedCount = 0;
129         errors = 0;
130         failures = 0;
131         skipped = 0;
132 
133         for (WrappedReportEntry entry : reportEntries) {
134             entry.getStdout().free();
135             entry.getStdErr().free();
136         }
137 
138         reportEntries.clear();
139     }
140 
141     public int getCompletedCount() {
142         return completedCount;
143     }
144 
145     public int getErrors() {
146         return errors;
147     }
148 
149     public int getFailures() {
150         return failures;
151     }
152 
153     public int getSkipped() {
154         return skipped;
155     }
156 
157     private void incrementCompletedCount() {
158         completedCount += 1;
159     }
160 
161     public String getTestSetSummary(WrappedReportEntry reportEntry, boolean phrasedClassName) {
162         String summary = TESTS_RUN
163                 + completedCount
164                 + COMMA
165                 + FAILURES
166                 + failures
167                 + COMMA
168                 + ERRORS
169                 + errors
170                 + COMMA
171                 + SKIPPED
172                 + skipped
173                 + COMMA
174                 + reportEntry.getElapsedTimeVerbose();
175 
176         if (failures > 0 || errors > 0) {
177             summary += FAILURE_MARKER;
178         }
179 
180         summary += IN_MARKER;
181         summary += phrasedClassName ? reportEntry.getReportNameWithGroup() : reportEntry.getNameWithGroup();
182 
183         return summary;
184     }
185 
186     public String getColoredTestSetSummary(WrappedReportEntry reportEntry, boolean phrasedClassName) {
187         final boolean isSuccessful = failures == 0 && errors == 0 && skipped == 0;
188         final boolean isFailure = failures > 0;
189         final boolean isError = errors > 0;
190         final boolean isFailureOrError = isFailure | isError;
191         final boolean isSkipped = skipped > 0;
192         final MessageBuilder builder = buffer();
193         if (isSuccessful) {
194             if (completedCount == 0) {
195                 builder.strong(TESTS_RUN).strong(completedCount);
196             } else {
197                 builder.success(TESTS_RUN).success(completedCount);
198             }
199         } else {
200             if (isFailureOrError) {
201                 builder.failure(TESTS).strong(RUN).strong(completedCount);
202             } else {
203                 builder.warning(TESTS).strong(RUN).strong(completedCount);
204             }
205         }
206         builder.a(COMMA);
207         if (isFailure) {
208             builder.failure(FAILURES).failure(failures);
209         } else {
210             builder.a(FAILURES).a(failures);
211         }
212         builder.a(COMMA);
213         if (isError) {
214             builder.failure(ERRORS).failure(errors);
215         } else {
216             builder.a(ERRORS).a(errors);
217         }
218         builder.a(COMMA);
219         if (isSkipped) {
220             builder.warning(SKIPPED).warning(skipped);
221         } else {
222             builder.a(SKIPPED).a(skipped);
223         }
224         builder.a(COMMA).a(reportEntry.getElapsedTimeVerbose());
225         if (isFailureOrError) {
226             builder.failure(FAILURE_MARKER);
227         }
228         builder.a(IN_MARKER);
229         return concatenateWithTestGroup(builder, reportEntry, phrasedClassName);
230     }
231 
232     public List<String> getTestResults() {
233         List<String> result = new ArrayList<>();
234         for (WrappedReportEntry testResult : reportEntries) {
235             if (testResult.isErrorOrFailure()) {
236                 result.add(testResult.getOutput(trimStackTrace));
237             } else if (plainFormat && testResult.isSkipped()) {
238                 result.add(testResult.getFullName() + " skipped");
239             } else if (plainFormat && testResult.isSucceeded()) {
240                 result.add(testResult.getElapsedTimeSummary());
241             }
242         }
243         // This should be Map with an enum and the enums will be displayed with colors on console.
244         return result;
245     }
246 
247     public Collection<WrappedReportEntry> getReportEntries() {
248         return reportEntries;
249     }
250 
251     /**
252      * Append the test set message for a report.
253      * e.g. "org.foo.BarTest ( of group )" or phrased text "test class description ( of group )".
254      *
255      * @param builder    MessageBuilder with preceded text inside
256      * @param report     report whose test set is starting
257      * @return the message
258      */
259     static String concatenateWithTestGroup(MessageBuilder builder, ReportEntry report, boolean phrasedClassName) {
260         if (phrasedClassName && report.getReportNameWithGroup() != null) {
261             return builder.strong(report.getReportNameWithGroup()).toString();
262         } else {
263             String testClass = report.getSourceText() != null ? report.getSourceText() : report.getNameWithGroup();
264             int indexOfGroup = testClass.indexOf(GROUP_PREFIX);
265             int delimiter = testClass.lastIndexOf('.', indexOfGroup == -1 ? testClass.length() : indexOfGroup);
266             String pkg = testClass.substring(0, 1 + delimiter);
267             String cls = testClass.substring(1 + delimiter);
268             return builder.a(pkg).strong(cls).toString();
269         }
270     }
271 }