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