1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.internal;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23
24 import java.io.IOException;
25 import java.nio.file.DirectoryStream;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.util.HashSet;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.function.Function;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36 import java.util.stream.Collectors;
37
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40
41 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
42 import com.puppycrawl.tools.checkstyle.Definitions;
43 import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
44 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck;
45 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocStyleCheck;
46 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck;
47 import com.puppycrawl.tools.checkstyle.checks.javadoc.WriteTagCheck;
48 import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
49
50 public class XpathRegressionTest extends AbstractModuleTestSupport {
51
52
53 public static final Set<String> INCOMPATIBLE_CHECK_NAMES = Set.of(
54 "NoCodeInFile (reason is that AST is not generated for a file not containing code)",
55 "Regexp (reason is at #7759)",
56 "RegexpSinglelineJava (reason is at #7759)"
57 );
58
59
60
61
62
63 public static final Set<String> INCOMPATIBLE_JAVADOC_CHECK_NAMES = Set.of(
64 "AtclauseOrder",
65 "JavadocBlockTagLocation",
66 "JavadocMethod",
67 "JavadocMissingLeadingAsterisk",
68 "JavadocLeadingAsteriskAlign",
69 "JavadocMissingWhitespaceAfterAsterisk",
70 "JavadocParagraph",
71 "JavadocStyle",
72 "JavadocTagContinuationIndentation",
73 "JavadocType",
74 "MissingDeprecated",
75 "NonEmptyAtclauseDescription",
76 "RequireEmptyLineBeforeBlockTagGroup",
77 "SingleLineJavadoc",
78 "SummaryJavadoc",
79 "WriteTag"
80 );
81
82
83
84 private static final Set<Class<?>> REGEXP_JAVADOC_CHECKS = Set.of(
85 JavadocStyleCheck.class,
86 JavadocMethodCheck.class,
87 JavadocTypeCheck.class,
88 WriteTagCheck.class
89 );
90
91
92
93 private static final Set<String> MISSING_CHECK_NAMES = Set.of(
94 "ClassFanOutComplexity",
95 "ClassTypeParameterName",
96 "DescendantToken",
97 "DesignForExtension",
98 "HideUtilityClassConstructor",
99 "InterfaceTypeParameterName",
100 "LocalVariableName",
101 "MutableException",
102 "RedundantModifier",
103 "SeparatorWrap",
104 "SuperFinalize",
105 "SuppressWarnings");
106
107
108 private static final Set<String> NO_VIOLATION_MODULES = Set.of(
109 "SuppressWarningsHolder"
110 );
111
112 private static final Set<String> SIMPLE_CHECK_NAMES = getSimpleCheckNames();
113 private static final Map<String, String> ALLOWED_DIRECTORY_AND_CHECKS =
114 getAllowedDirectoryAndChecks();
115
116 private static final Set<String> INTERNAL_MODULES = getInternalModules();
117
118 private Path javaDir;
119 private Path inputDir;
120
121 private static Set<String> getSimpleCheckNames() {
122 try {
123 return CheckUtil.getSimpleNames(CheckUtil.getCheckstyleChecks());
124 }
125 catch (IOException ex) {
126 throw new ExceptionInInitializerError(ex);
127 }
128 }
129
130 private static Map<String, String> getAllowedDirectoryAndChecks() {
131 return SIMPLE_CHECK_NAMES
132 .stream()
133 .collect(Collectors.toUnmodifiableMap(
134 id -> id.toLowerCase(Locale.ENGLISH), Function.identity()));
135 }
136
137 private static Set<String> getInternalModules() {
138 return Definitions.INTERNAL_MODULES.stream()
139 .map(moduleName -> {
140 final String[] packageTokens = moduleName.split("\\.");
141 return packageTokens[packageTokens.length - 1];
142 })
143 .collect(Collectors.toUnmodifiableSet());
144 }
145
146 @BeforeEach
147 public void setUp() throws Exception {
148 javaDir = Paths.get("src/it/java/" + getPackageLocation());
149 inputDir = Paths.get(getPath(""));
150 }
151
152 @Override
153 protected String getPackageLocation() {
154 return "org/checkstyle/suppressionxpathfilter";
155 }
156
157 @Override
158 protected String getResourceLocation() {
159 return "it";
160 }
161
162 @Test
163 public void validateIncompatibleJavadocCheckNames() throws IOException {
164
165 final Set<Class<?>> abstractJavadocCheckNames = CheckUtil.getCheckstyleChecks()
166 .stream()
167 .filter(AbstractJavadocCheck.class::isAssignableFrom)
168 .collect(Collectors.toCollection(HashSet::new));
169
170 abstractJavadocCheckNames.addAll(REGEXP_JAVADOC_CHECKS);
171 final Set<String> abstractJavadocCheckSimpleNames =
172 CheckUtil.getSimpleNames(abstractJavadocCheckNames);
173 abstractJavadocCheckSimpleNames.removeAll(INTERNAL_MODULES);
174 assertWithMessage("INCOMPATIBLE_JAVADOC_CHECK_NAMES should contains all descendants "
175 + "of AbstractJavadocCheck")
176 .that(abstractJavadocCheckSimpleNames)
177 .isEqualTo(INCOMPATIBLE_JAVADOC_CHECK_NAMES);
178 }
179
180 @Test
181 public void validateIntegrationTestClassNames() throws Exception {
182 final Set<String> compatibleChecks = new HashSet<>();
183 final Pattern pattern = Pattern.compile("^XpathRegression(.+)Test\\.java$");
184 try (DirectoryStream<Path> javaPaths = Files.newDirectoryStream(javaDir)) {
185 for (Path path : javaPaths) {
186 assertWithMessage(path + " is not a regular file")
187 .that(Files.isRegularFile(path))
188 .isTrue();
189 final String filename = path.toFile().getName();
190 if (filename.startsWith("Abstract")) {
191 continue;
192 }
193
194 final Matcher matcher = pattern.matcher(filename);
195 assertWithMessage(
196 "Invalid test file: " + filename + ", expected pattern: " + pattern)
197 .that(matcher.matches())
198 .isTrue();
199
200 final String check = matcher.group(1);
201 assertWithMessage("Unknown check '" + check + "' in test file: " + filename)
202 .that(SIMPLE_CHECK_NAMES)
203 .contains(check);
204
205 assertWithMessage(
206 "Check '" + check + "' is now tested. Please update the todo list in"
207 + " XpathRegressionTest.MISSING_CHECK_NAMES")
208 .that(MISSING_CHECK_NAMES.contains(check))
209 .isFalse();
210 assertWithMessage(
211 "Check '" + check + "' is now compatible with SuppressionXpathFilter."
212 + " Please update the todo list in"
213 + " XpathRegressionTest.INCOMPATIBLE_CHECK_NAMES")
214 .that(INCOMPATIBLE_CHECK_NAMES.contains(check))
215 .isFalse();
216 compatibleChecks.add(check);
217 }
218 }
219
220
221 final Set<String> allChecks = new HashSet<>(SIMPLE_CHECK_NAMES);
222 allChecks.removeAll(INCOMPATIBLE_JAVADOC_CHECK_NAMES);
223 allChecks.removeAll(INCOMPATIBLE_CHECK_NAMES);
224 allChecks.removeAll(Set.of("Regexp", "RegexpSinglelineJava", "NoCodeInFile"));
225 allChecks.removeAll(MISSING_CHECK_NAMES);
226 allChecks.removeAll(NO_VIOLATION_MODULES);
227 allChecks.removeAll(compatibleChecks);
228 allChecks.removeAll(INTERNAL_MODULES);
229
230 assertWithMessage("XpathRegressionTest is missing for [" + String.join(", ", allChecks)
231 + "]. Please add them to src/it/java/org/checkstyle/suppressionxpathfilter")
232 .that(allChecks)
233 .isEmpty();
234 }
235
236 @Test
237 public void validateInputFiles() throws Exception {
238 try (DirectoryStream<Path> dirs = Files.newDirectoryStream(inputDir)) {
239 for (Path dir : dirs) {
240
241 assertWithMessage(dir + " is not a directory")
242 .that(Files.isDirectory(dir))
243 .isTrue();
244 final String dirName = dir.toFile().getName();
245 assertWithMessage("Invalid directory name: " + dirName)
246 .that(ALLOWED_DIRECTORY_AND_CHECKS)
247 .containsKey(dirName);
248
249
250 final String check = ALLOWED_DIRECTORY_AND_CHECKS.get(dirName);
251 final Path javaPath = javaDir.resolve("XpathRegression" + check + "Test.java");
252 assertWithMessage("Input directory '" + dir
253 + "' is not connected to Java test case: " + javaPath)
254 .that(Files.exists(javaPath))
255 .isTrue();
256
257
258 validateInputDirectory(dir);
259 }
260 }
261 }
262
263 private static void validateInputDirectory(Path checkDir) throws IOException {
264 final Pattern pattern = Pattern.compile("^InputXpath(.+)\\.java$");
265 final String check = ALLOWED_DIRECTORY_AND_CHECKS.get(checkDir.toFile().getName());
266
267 try (DirectoryStream<Path> inputPaths = Files.newDirectoryStream(checkDir)) {
268 for (Path inputPath : inputPaths) {
269 final String filename = inputPath.toFile().getName();
270 if (filename.endsWith("java")) {
271 final Matcher matcher = pattern.matcher(filename);
272 assertWithMessage(
273 "Invalid input file '" + inputPath
274 + "', expected pattern:" + pattern)
275 .that(matcher.matches())
276 .isTrue();
277
278 final String remaining = matcher.group(1);
279 assertWithMessage("Check name '" + check
280 + "' should be included in input file: " + inputPath)
281 .that(remaining)
282 .startsWith(check);
283 }
284 }
285 }
286 }
287 }