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