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