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