1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.surefire;
20
21 import javax.annotation.Nonnull;
22
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Deque;
27 import java.util.LinkedList;
28 import java.util.List;
29
30 import org.apache.maven.execution.MavenExecutionRequest;
31 import org.apache.maven.execution.MavenSession;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
35 import org.apache.maven.surefire.api.cli.CommandLineOption;
36 import org.apache.maven.surefire.api.suite.RunResult;
37 import org.apache.maven.surefire.api.testset.TestSetFailedException;
38 import org.apache.maven.surefire.booter.SurefireBooterForkException;
39
40 import static java.util.Collections.unmodifiableList;
41 import static org.apache.maven.surefire.api.booter.DumpErrorSingleton.DUMPSTREAM_FILE_EXT;
42 import static org.apache.maven.surefire.api.booter.DumpErrorSingleton.DUMP_FILE_EXT;
43 import static org.apache.maven.surefire.api.cli.CommandLineOption.LOGGING_LEVEL_DEBUG;
44 import static org.apache.maven.surefire.api.cli.CommandLineOption.LOGGING_LEVEL_ERROR;
45 import static org.apache.maven.surefire.api.cli.CommandLineOption.LOGGING_LEVEL_INFO;
46 import static org.apache.maven.surefire.api.cli.CommandLineOption.LOGGING_LEVEL_WARN;
47 import static org.apache.maven.surefire.api.cli.CommandLineOption.SHOW_ERRORS;
48 import static org.apache.maven.surefire.api.util.internal.DumpFileUtils.newFormattedDateFileName;
49 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
50
51
52
53
54 public final class SurefireHelper {
55 private static final String DUMP_FILE_DATE = newFormattedDateFileName();
56
57 public static final String DUMP_FILE_PREFIX = DUMP_FILE_DATE + "-jvmRun";
58
59 public static final String DUMP_FILENAME_FORMATTER = DUMP_FILE_PREFIX + "%d" + DUMP_FILE_EXT;
60
61 public static final String DUMPSTREAM_FILENAME_FORMATTER = DUMP_FILE_PREFIX + "%d" + DUMPSTREAM_FILE_EXT;
62
63 public static final String DUMPSTREAM_FILENAME = DUMP_FILE_DATE + DUMPSTREAM_FILE_EXT;
64
65 public static final String DUMP_FILENAME = DUMP_FILE_DATE + DUMP_FILE_EXT;
66
67 public static final String EVENTS_BINARY_DUMP_FILENAME_FORMATTER = DUMP_FILE_DATE + "-jvmRun%d-events.bin";
68
69
70
71
72
73
74
75
76
77
78
79
80 private static final int MAX_PATH_LENGTH_WINDOWS = 247;
81
82 private static final String[] DUMP_FILES_PRINT = {
83 "[date]" + DUMP_FILE_EXT,
84 "[date]-jvmRun[N]" + DUMP_FILE_EXT,
85 "[date]" + DUMPSTREAM_FILE_EXT,
86 "[date]-jvmRun[N]" + DUMPSTREAM_FILE_EXT
87 };
88
89
90
91
92
93
94 private static final String THREAD_NUMBER_PLACEHOLDER = "${surefire.threadNumber}";
95
96
97
98
99
100 private static final String FORK_NUMBER_PLACEHOLDER = "${surefire.forkNumber}";
101
102
103
104
105 private SurefireHelper() {
106 throw new IllegalAccessError("Utility class");
107 }
108
109 @Nonnull
110 public static String replaceThreadNumberPlaceholders(@Nonnull String argLine, int threadNumber) {
111 String threadNumberAsString = String.valueOf(threadNumber);
112 return argLine.replace(THREAD_NUMBER_PLACEHOLDER, threadNumberAsString)
113 .replace(FORK_NUMBER_PLACEHOLDER, threadNumberAsString);
114 }
115
116 public static File replaceForkThreadsInPath(File path, int replacement) {
117 Deque<String> dirs = new LinkedList<>();
118 File root = path;
119 while (!root.exists()) {
120 dirs.addFirst(replaceThreadNumberPlaceholders(root.getName(), replacement));
121 root = root.getParentFile();
122 }
123 File replacedPath = root;
124 for (String dir : dirs) {
125 replacedPath = new File(replacedPath, dir);
126 }
127 return replacedPath;
128 }
129
130 public static String[] getDumpFilesToPrint() {
131 return DUMP_FILES_PRINT.clone();
132 }
133
134 public static void reportExecution(
135 SurefireReportParameters reportParameters,
136 RunResult result,
137 PluginConsoleLogger log,
138 Exception firstForkException)
139 throws MojoFailureException, MojoExecutionException {
140 boolean isError = firstForkException != null || result.isTimeout() || !result.isErrorFree();
141 boolean isTooFlaky = isTooFlaky(result, reportParameters);
142 if (!isError && !isTooFlaky) {
143 if (result.getCompletedCount() == 0 && failIfNoTests(reportParameters)) {
144 throw new MojoFailureException(
145 "No tests were executed! " + "(Set -DfailIfNoTests=false to ignore this error.)");
146 }
147 return;
148 }
149
150 if (reportParameters.isTestFailureIgnore()) {
151 String errorMessage = createErrorMessage(reportParameters, result, firstForkException);
152
153 if (firstForkException instanceof SurefireBooterForkException) {
154 throw new MojoExecutionException(errorMessage, firstForkException);
155 }
156
157 log.error(errorMessage);
158 } else {
159 throwException(reportParameters, result, firstForkException);
160 }
161 }
162
163 public static List<CommandLineOption> commandLineOptions(MavenSession session, PluginConsoleLogger log) {
164 List<CommandLineOption> cli = new ArrayList<>();
165 if (log.isErrorEnabled()) {
166 cli.add(LOGGING_LEVEL_ERROR);
167 }
168
169 if (log.isWarnEnabled()) {
170 cli.add(LOGGING_LEVEL_WARN);
171 }
172
173 if (log.isInfoEnabled()) {
174 cli.add(LOGGING_LEVEL_INFO);
175 }
176
177 if (log.isDebugEnabled()) {
178 cli.add(LOGGING_LEVEL_DEBUG);
179 }
180
181 MavenExecutionRequest request = session.getRequest();
182
183 if (request.isShowErrors()) {
184 cli.add(SHOW_ERRORS);
185 }
186
187 String failureBehavior = request.getReactorFailureBehavior();
188 if (failureBehavior != null) {
189 try {
190 cli.add(CommandLineOption.valueOf(failureBehavior));
191 } catch (IllegalArgumentException e) {
192
193 }
194 }
195
196 return unmodifiableList(cli);
197 }
198
199 public static void logDebugOrCliShowErrors(String s, PluginConsoleLogger log, Collection<CommandLineOption> cli) {
200 if (cli.contains(LOGGING_LEVEL_DEBUG)) {
201 log.debug(s);
202 } else if (cli.contains(SHOW_ERRORS)) {
203 if (log.isDebugEnabled()) {
204 log.debug(s);
205 } else {
206 log.info(s);
207 }
208 }
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 public static String escapeToPlatformPath(String path) {
225 if (IS_OS_WINDOWS && path.length() > MAX_PATH_LENGTH_WINDOWS) {
226 path = path.startsWith("\\\\") ? "\\\\?\\UNC\\" + path.substring(2) : "\\\\?\\" + path;
227 }
228 return path;
229 }
230
231 private static boolean failIfNoTests(SurefireReportParameters reportParameters) {
232 return reportParameters.getFailIfNoTests();
233 }
234
235 private static boolean isFatal(Exception firstForkException) {
236 return firstForkException != null && !(firstForkException instanceof TestSetFailedException);
237 }
238
239 private static void throwException(
240 SurefireReportParameters reportParameters, RunResult result, Exception firstForkException)
241 throws MojoFailureException, MojoExecutionException {
242 if (isFatal(firstForkException) || result.isInternalError()) {
243 throw new MojoExecutionException(
244 createErrorMessage(reportParameters, result, firstForkException), firstForkException);
245 } else {
246 throw new MojoFailureException(
247 createErrorMessage(reportParameters, result, firstForkException), firstForkException);
248 }
249 }
250
251 private static String createErrorMessage(
252 SurefireReportParameters reportParameters, RunResult result, Exception firstForkException) {
253 StringBuilder msg = new StringBuilder(512);
254
255 if (result.isTimeout()) {
256 msg.append("There was a timeout in the fork");
257 } else {
258 if (result.getFailures() > 0) {
259 msg.append("There are test failures.");
260 }
261 if (isTooFlaky(result, reportParameters)) {
262 if (result.getFailures() > 0) {
263 msg.append("\n");
264 }
265 msg.append("There")
266 .append(result.getFlakes() == 1 ? " is " : " are ")
267 .append(result.getFlakes())
268 .append(result.getFlakes() == 1 ? " flake " : " flakes ")
269 .append("and failOnFlakeCount is set to ")
270 .append(reportParameters.getFailOnFlakeCount())
271 .append(".");
272 }
273 msg.append("\n\nPlease refer to ")
274 .append(reportParameters.getReportsDirectory())
275 .append(" for the individual test results.")
276 .append('\n')
277 .append("Please refer to dump files (if any exist) ")
278 .append(DUMP_FILES_PRINT[0])
279 .append(", ")
280 .append(DUMP_FILES_PRINT[1])
281 .append(" and ")
282 .append(DUMP_FILES_PRINT[2])
283 .append(".");
284 }
285
286 if (firstForkException != null && firstForkException.getLocalizedMessage() != null) {
287 msg.append('\n').append(firstForkException.getLocalizedMessage());
288 }
289
290 if (result.isFailure()) {
291 msg.append('\n').append(result.getFailure());
292 }
293
294 return msg.toString();
295 }
296
297 private static boolean isTooFlaky(RunResult result, SurefireReportParameters reportParameters) {
298 int failOnFlakeCount = reportParameters.getFailOnFlakeCount();
299 return failOnFlakeCount > 0 && result.getFlakes() >= failOnFlakeCount;
300 }
301 }