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