-
Notifications
You must be signed in to change notification settings - Fork 727
/
JUnit3TestNotRun.java
139 lines (127 loc) · 6.04 KB
/
JUnit3TestNotRun.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* Copyright 2013 The Error Prone Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone.bugpatterns;
import static com.google.errorprone.BugPattern.Category.JUNIT;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.fixes.SuggestedFixes.addModifiers;
import static com.google.errorprone.fixes.SuggestedFixes.removeModifiers;
import static com.google.errorprone.fixes.SuggestedFixes.renameMethod;
import static com.google.errorprone.matchers.Description.NO_MATCH;
import static com.google.errorprone.matchers.JUnitMatchers.isJUnit3TestClass;
import static com.google.errorprone.matchers.JUnitMatchers.isJunit3TestCase;
import static com.google.errorprone.matchers.JUnitMatchers.wouldRunInJUnit4;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.enclosingClass;
import static com.google.errorprone.matchers.Matchers.methodHasParameters;
import static com.google.errorprone.matchers.Matchers.methodNameStartsWith;
import static com.google.errorprone.matchers.Matchers.methodReturns;
import static com.google.errorprone.matchers.Matchers.not;
import static com.google.errorprone.suppliers.Suppliers.VOID_TYPE;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.ProvidesFix;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
import com.google.errorprone.fixes.Fix;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.MethodTree;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.lang.model.element.Modifier;
/** @author rburny@google.com (Radoslaw Burny) */
@BugPattern(
name = "JUnit3TestNotRun",
summary =
"Test method will not be run; please correct method signature "
+ "(Should be public, non-static, and method name should begin with \"test\").",
category = JUNIT,
severity = ERROR,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION)
public class JUnit3TestNotRun extends BugChecker implements MethodTreeMatcher {
/*
* Regular expression for test method name that is misspelled and should be replaced with "test".
* ".est" and "est" are omitted, because they catch real words like "restore", "destroy", "best",
* "establish". ".test" is omitted, because people use it on purpose, to disable the test.
* Otherwise, I haven't found any false positives; "tes" was most common typo.
* There are some ambiguities in this regex that lead to bad corrections
* (i.e. tets -> tests, tesst -> testst), but the error is still found
* (those could be improved with regex lookahead, but I prefer simpler regex).
* TODO(rburny): see if we can cleanup intentional ".test" misspellings
*/
private static final Pattern MISSPELLED_NAME =
Pattern.compile(
"t.est|te.st|"
+ // letter inserted
"tst|tet|tes|"
+ // letter removed
"etst|tset|tets|"
+ // letters swapped
"t.st|te.t|"
+ // letter changed
"[tT][eE][sS][tT]" // miscapitalized
);
private static final Matcher<MethodTree> LOOKS_LIKE_TEST_CASE =
allOf(
enclosingClass(isJUnit3TestClass),
not(isJunit3TestCase),
methodReturns(VOID_TYPE),
methodHasParameters());
/**
* Matches if: 1) Method's name begins with misspelled variation of "test". 2) Method is public,
* returns void, and has no parameters. 3) Enclosing class is JUnit3 test (extends TestCase, has
* no {@code @RunWith} annotation, no {@code @Test}-annotated methods, and is not abstract).
*/
@Override
public Description matchMethod(MethodTree methodTree, VisitorState state) {
if (!LOOKS_LIKE_TEST_CASE.matches(methodTree, state)) {
return NO_MATCH;
}
List<SuggestedFix> fixes = new ArrayList<>(0);
if (not(methodNameStartsWith("test")).matches(methodTree, state)) {
String fixedName = methodTree.getName().toString();
// N.B. regex.Matcher class name collides with errorprone.Matcher
java.util.regex.Matcher matcher = MISSPELLED_NAME.matcher(fixedName);
if (matcher.lookingAt()) {
fixedName = matcher.replaceFirst("test");
} else if (wouldRunInJUnit4.matches(methodTree, state)) {
fixedName = "test" + fixedName.substring(0, 1).toUpperCase() + fixedName.substring(1);
} else {
return NO_MATCH;
}
// Rename test method appropriately.
fixes.add(renameMethod(methodTree, fixedName, state));
}
// Make method public (if not already public).
addModifiers(methodTree, state, Modifier.PUBLIC).ifPresent(fixes::add);
// Remove any other visibility modifiers (if present).
removeModifiers(methodTree, state, Modifier.PRIVATE, Modifier.PROTECTED).ifPresent(fixes::add);
// Remove static modifier (if present).
// N.B. must occur in separate step because removeModifiers only removes one modifier at a time.
removeModifiers(methodTree, state, Modifier.STATIC).ifPresent(fixes::add);
return describeMatch(methodTree, mergeFixes(fixes));
}
private static Fix mergeFixes(List<SuggestedFix> fixesToMerge) {
SuggestedFix.Builder builderForResult = SuggestedFix.builder();
for (SuggestedFix fix : fixesToMerge) {
if (fix != null) {
builderForResult.merge(fix);
}
}
return builderForResult.build();
}
}