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