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