1 package org.apache.maven.surefire.testset;
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.shared.utils.StringUtils;
23 import org.apache.maven.shared.utils.io.MatchPatterns;
24
25 import java.util.regex.Pattern;
26
27 import static java.io.File.separatorChar;
28 import static java.util.regex.Pattern.compile;
29 import static org.apache.maven.shared.utils.StringUtils.isBlank;
30 import static org.apache.maven.shared.utils.io.MatchPatterns.from;
31 import static org.apache.maven.shared.utils.io.SelectorUtils.PATTERN_HANDLER_SUFFIX;
32 import static org.apache.maven.shared.utils.io.SelectorUtils.REGEX_HANDLER_PREFIX;
33 import static org.apache.maven.shared.utils.io.SelectorUtils.matchPath;
34
35
36
37
38
39
40 @Deprecated
41 public final class ResolvedTest
42 {
43
44
45
46 public enum Type
47 {
48 CLASS, METHOD
49 }
50
51 private static final String CLASS_FILE_EXTENSION = ".class";
52
53 private static final String JAVA_FILE_EXTENSION = ".java";
54
55 private static final String WILDCARD_PATH_PREFIX = "**/";
56
57 private static final String WILDCARD_FILENAME_POSTFIX = ".*";
58
59 private final String classPattern;
60
61 private final String methodPattern;
62
63 private final boolean isRegexTestClassPattern;
64
65 private final boolean isRegexTestMethodPattern;
66
67 private final String description;
68
69 private final ClassMatcher classMatcher = new ClassMatcher();
70
71 private final MethodMatcher methodMatcher = new MethodMatcher();
72
73
74
75
76
77
78
79
80
81
82
83 public ResolvedTest( String classPattern, String methodPattern, boolean isRegex )
84 {
85 classPattern = tryBlank( classPattern );
86 methodPattern = tryBlank( methodPattern );
87 description = description( classPattern, methodPattern, isRegex );
88
89 if ( isRegex && classPattern != null )
90 {
91 classPattern = wrapRegex( classPattern );
92 }
93
94 if ( isRegex && methodPattern != null )
95 {
96 methodPattern = wrapRegex( methodPattern );
97 }
98
99 this.classPattern = reformatClassPattern( classPattern, isRegex );
100 this.methodPattern = methodPattern;
101 isRegexTestClassPattern = isRegex;
102 isRegexTestMethodPattern = isRegex;
103 methodMatcher.sanityCheck();
104 }
105
106
107
108
109 public ResolvedTest( Type type, String pattern, boolean isRegex )
110 {
111 pattern = tryBlank( pattern );
112 final boolean isClass = type == Type.CLASS;
113 description = description( isClass ? pattern : null, !isClass ? pattern : null, isRegex );
114 if ( isRegex && pattern != null )
115 {
116 pattern = wrapRegex( pattern );
117 }
118 classPattern = isClass ? reformatClassPattern( pattern, isRegex ) : null;
119 methodPattern = !isClass ? pattern : null;
120 isRegexTestClassPattern = isRegex && isClass;
121 isRegexTestMethodPattern = isRegex && !isClass;
122 methodMatcher.sanityCheck();
123 }
124
125
126
127
128
129
130
131
132
133 public String getTestClassPattern()
134 {
135 return classPattern;
136 }
137
138 public boolean hasTestClassPattern()
139 {
140 return classPattern != null;
141 }
142
143
144
145
146
147
148
149
150 public String getTestMethodPattern()
151 {
152 return methodPattern;
153 }
154
155 public boolean hasTestMethodPattern()
156 {
157 return methodPattern != null;
158 }
159
160 public boolean isRegexTestClassPattern()
161 {
162 return isRegexTestClassPattern;
163 }
164
165 public boolean isRegexTestMethodPattern()
166 {
167 return isRegexTestMethodPattern;
168 }
169
170 public boolean isEmpty()
171 {
172 return classPattern == null && methodPattern == null;
173 }
174
175 public boolean matchAsInclusive( String testClassFile, String methodName )
176 {
177 testClassFile = tryBlank( testClassFile );
178 methodName = tryBlank( methodName );
179
180 return isEmpty() || alwaysInclusiveQuietly( testClassFile ) || match( testClassFile, methodName );
181 }
182
183 public boolean matchAsExclusive( String testClassFile, String methodName )
184 {
185 testClassFile = tryBlank( testClassFile );
186 methodName = tryBlank( methodName );
187
188 return !isEmpty() && canMatchExclusive( testClassFile, methodName ) && match( testClassFile, methodName );
189 }
190
191 @Override
192 public boolean equals( Object o )
193 {
194 if ( this == o )
195 {
196 return true;
197 }
198 if ( o == null || getClass() != o.getClass() )
199 {
200 return false;
201 }
202
203 ResolvedTest that = (ResolvedTest) o;
204
205 return ( classPattern == null ? that.classPattern == null : classPattern.equals( that.classPattern ) )
206 && ( methodPattern == null ? that.methodPattern == null : methodPattern.equals( that.methodPattern ) );
207 }
208
209 @Override
210 public int hashCode()
211 {
212 int result = classPattern != null ? classPattern.hashCode() : 0;
213 result = 31 * result + ( methodPattern != null ? methodPattern.hashCode() : 0 );
214 return result;
215 }
216
217 @Override
218 public String toString()
219 {
220 return isEmpty() ? "" : description;
221 }
222
223 private static String description( String clazz, String method, boolean isRegex )
224 {
225 String description;
226 if ( clazz == null && method == null )
227 {
228 description = null;
229 }
230 else if ( clazz == null )
231 {
232 description = "#" + method;
233 }
234 else if ( method == null )
235 {
236 description = clazz;
237 }
238 else
239 {
240 description = clazz + "#" + method;
241 }
242 return isRegex && description != null ? wrapRegex( description ) : description;
243 }
244
245 private boolean canMatchExclusive( String testClassFile, String methodName )
246 {
247 return canMatchExclusiveMethods( testClassFile, methodName )
248 || canMatchExclusiveClasses( testClassFile, methodName )
249 || canMatchExclusiveAll( testClassFile, methodName );
250 }
251
252 private boolean canMatchExclusiveMethods( String testClassFile, String methodName )
253 {
254 return testClassFile == null && methodName != null && classPattern == null && methodPattern != null;
255 }
256
257 private boolean canMatchExclusiveClasses( String testClassFile, String methodName )
258 {
259 return testClassFile != null && methodName == null && classPattern != null && methodPattern == null;
260 }
261
262 private boolean canMatchExclusiveAll( String testClassFile, String methodName )
263 {
264 return testClassFile != null && methodName != null && ( classPattern != null || methodPattern != null );
265 }
266
267
268
269
270 private boolean alwaysInclusiveQuietly( String testClassFile )
271 {
272 return testClassFile == null && classPattern != null;
273 }
274
275 private boolean match( String testClassFile, String methodName )
276 {
277 return matchClass( testClassFile ) && matchMethod( methodName );
278 }
279
280 private boolean matchClass( String testClassFile )
281 {
282 return classPattern == null || classMatcher.matchTestClassFile( testClassFile );
283 }
284
285 private boolean matchMethod( String methodName )
286 {
287 return methodPattern == null || methodName == null || methodMatcher.matchMethodName( methodName );
288 }
289
290 private static String tryBlank( String s )
291 {
292 if ( s == null )
293 {
294 return null;
295 }
296 else
297 {
298 String trimmed = s.trim();
299 return StringUtils.isEmpty( trimmed ) ? null : trimmed;
300 }
301 }
302
303 private static String reformatClassPattern( String s, boolean isRegex )
304 {
305 if ( s != null && !isRegex )
306 {
307 String path = convertToPath( s );
308 path = fromFullyQualifiedClass( path );
309 if ( path != null && !path.startsWith( WILDCARD_PATH_PREFIX ) )
310 {
311 path = WILDCARD_PATH_PREFIX + path;
312 }
313 return path;
314 }
315 else
316 {
317 return s;
318 }
319 }
320
321 private static String convertToPath( String className )
322 {
323 if ( isBlank( className ) )
324 {
325 return null;
326 }
327 else
328 {
329 if ( className.endsWith( JAVA_FILE_EXTENSION ) )
330 {
331 className = className.substring( 0, className.length() - JAVA_FILE_EXTENSION.length() )
332 + CLASS_FILE_EXTENSION;
333 }
334 return className;
335 }
336 }
337
338 static String wrapRegex( String unwrapped )
339 {
340 return REGEX_HANDLER_PREFIX + unwrapped + PATTERN_HANDLER_SUFFIX;
341 }
342
343 static String fromFullyQualifiedClass( String cls )
344 {
345 if ( cls.endsWith( CLASS_FILE_EXTENSION ) )
346 {
347 String className = cls.substring( 0, cls.length() - CLASS_FILE_EXTENSION.length() );
348 return className.replace( '.', '/' ) + CLASS_FILE_EXTENSION;
349 }
350 else if ( !cls.contains( "/" ) )
351 {
352 if ( cls.endsWith( WILDCARD_FILENAME_POSTFIX ) )
353 {
354 String clsName = cls.substring( 0, cls.length() - WILDCARD_FILENAME_POSTFIX.length() );
355 return clsName.contains( "." ) ? clsName.replace( '.', '/' ) + WILDCARD_FILENAME_POSTFIX : cls;
356 }
357 else
358 {
359 return cls.replace( '.', '/' );
360 }
361 }
362 else
363 {
364 return cls;
365 }
366 }
367
368 private final class ClassMatcher
369 {
370 private volatile MatchPatterns cache;
371
372 boolean matchTestClassFile( String testClassFile )
373 {
374 return ResolvedTest.this.isRegexTestClassPattern()
375 ? matchClassRegexPatter( testClassFile )
376 : matchClassPatter( testClassFile );
377 }
378
379 private MatchPatterns of( String... sources )
380 {
381 if ( cache == null )
382 {
383 try
384 {
385 checkIllegalCharacters( sources );
386 cache = from( sources );
387 }
388 catch ( IllegalArgumentException e )
389 {
390 throwSanityError( e );
391 }
392 }
393 return cache;
394 }
395
396 private boolean matchClassPatter( String testClassFile )
397 {
398
399 String classPattern = ResolvedTest.this.classPattern;
400 if ( separatorChar != '/' )
401 {
402 testClassFile = testClassFile.replace( '/', separatorChar );
403 classPattern = classPattern.replace( '/', separatorChar );
404 }
405
406 if ( classPattern.endsWith( WILDCARD_FILENAME_POSTFIX ) || classPattern.endsWith( CLASS_FILE_EXTENSION ) )
407 {
408 return of( classPattern ).matches( testClassFile, true );
409 }
410 else
411 {
412 String[] classPatterns = { classPattern + CLASS_FILE_EXTENSION, classPattern };
413 return of( classPatterns ).matches( testClassFile, true );
414 }
415 }
416
417 private boolean matchClassRegexPatter( String testClassFile )
418 {
419 String realFile = separatorChar == '/' ? testClassFile : testClassFile.replace( '/', separatorChar );
420 return of( classPattern ).matches( realFile, true );
421 }
422 }
423
424 private final class MethodMatcher
425 {
426 private volatile Pattern cache;
427
428 boolean matchMethodName( String methodName )
429 {
430 if ( ResolvedTest.this.isRegexTestMethodPattern() )
431 {
432 fetchCache();
433 return cache.matcher( methodName )
434 .matches();
435 }
436 else
437 {
438 return matchPath( ResolvedTest.this.methodPattern, methodName );
439 }
440 }
441
442 void sanityCheck()
443 {
444 if ( ResolvedTest.this.isRegexTestMethodPattern() && ResolvedTest.this.hasTestMethodPattern() )
445 {
446 try
447 {
448 checkIllegalCharacters( ResolvedTest.this.methodPattern );
449 fetchCache();
450 }
451 catch ( IllegalArgumentException e )
452 {
453 throwSanityError( e );
454 }
455 }
456 }
457
458 private void fetchCache()
459 {
460 if ( cache == null )
461 {
462 int from = REGEX_HANDLER_PREFIX.length();
463 int to = ResolvedTest.this.methodPattern.length() - PATTERN_HANDLER_SUFFIX.length();
464 String pattern = ResolvedTest.this.methodPattern.substring( from, to );
465 cache = compile( pattern );
466 }
467 }
468 }
469
470 private static void checkIllegalCharacters( String... expressions )
471 {
472 for ( String expression : expressions )
473 {
474 if ( expression.contains( "#" ) )
475 {
476 throw new IllegalArgumentException( "Extra '#' in regex: " + expression );
477 }
478 }
479 }
480
481 private static void throwSanityError( IllegalArgumentException e )
482 {
483 throw new IllegalArgumentException( "%regex[] usage rule violation, valid regex rules:\n"
484 + " * <classNameRegex>#<methodNameRegex> - "
485 + "where both regex can be individually evaluated as a regex\n"
486 + " * you may use at most 1 '#' to in one regex filter. "
487 + e.getLocalizedMessage(), e );
488 }
489 }