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