1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.checkstyle.suppressionxpathfilter;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23
24 import java.io.File;
25 import java.io.Writer;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.Files;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.UUID;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import org.checkstyle.base.AbstractCheckstyleModuleTestSupport;
35 import org.junit.jupiter.api.io.TempDir;
36
37 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
38 import com.puppycrawl.tools.checkstyle.JavaParser;
39 import com.puppycrawl.tools.checkstyle.TreeWalker;
40 import com.puppycrawl.tools.checkstyle.api.DetailAST;
41 import com.puppycrawl.tools.checkstyle.api.FileText;
42 import com.puppycrawl.tools.checkstyle.filters.SuppressionXpathFilter;
43 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
44 import com.puppycrawl.tools.checkstyle.xpath.XpathQueryGenerator;
45
46 public abstract class AbstractXpathTestSupport extends AbstractCheckstyleModuleTestSupport {
47
48 private static final int DEFAULT_TAB_WIDTH = 4;
49
50 private static final String DELIMITER = " | \n";
51
52 private static final Pattern LINE_COLUMN_NUMBER_REGEX =
53 Pattern.compile("(\\d+):(\\d+):");
54
55
56
57
58 @TempDir
59 public File temporaryFolder;
60
61
62
63
64
65
66 protected abstract String getCheckName();
67
68 @Override
69 protected String getPackageLocation() {
70 final String subpackage = getCheckName().toLowerCase(Locale.ENGLISH)
71 .replace("check", "");
72 return "org/checkstyle/suppressionxpathfilter/" + subpackage;
73 }
74
75
76
77
78
79
80
81
82
83 private static List<String> generateXpathQueries(File fileToProcess,
84 ViolationPosition position)
85 throws Exception {
86 final FileText fileText = new FileText(fileToProcess,
87 StandardCharsets.UTF_8.name());
88 final DetailAST rootAst = JavaParser.parseFile(fileToProcess,
89 JavaParser.Options.WITH_COMMENTS);
90 final XpathQueryGenerator queryGenerator = new XpathQueryGenerator(rootAst,
91 position.violationLineNumber, position.violationColumnNumber,
92 fileText, DEFAULT_TAB_WIDTH);
93
94 return queryGenerator.generate();
95 }
96
97
98
99
100
101
102
103 private static void verifyXpathQueries(List<String> generatedXpathQueries,
104 List<String> expectedXpathQueries) {
105 assertWithMessage("Generated queries do not match expected ones")
106 .that(generatedXpathQueries)
107 .isEqualTo(expectedXpathQueries);
108 }
109
110
111
112
113
114
115
116
117
118
119
120 private String createSuppressionsXpathConfigFile(String checkName,
121 List<String> xpathQueries)
122 throws Exception {
123 final String uniqueFileName =
124 "suppressions_xpath_config_" + UUID.randomUUID() + ".xml";
125 final File suppressionsXpathConfigPath = new File(temporaryFolder, uniqueFileName);
126 try (Writer bw = Files.newBufferedWriter(suppressionsXpathConfigPath.toPath(),
127 StandardCharsets.UTF_8)) {
128 bw.write("<?xml version=\"1.0\"?>\n");
129 bw.write("<!DOCTYPE suppressions PUBLIC\n");
130 bw.write(" \"-//Checkstyle//DTD SuppressionXpathFilter ");
131 bw.write("Experimental Configuration 1.2//EN\"\n");
132 bw.write(" \"https://checkstyle.org/dtds/");
133 bw.write("suppressions_1_2_xpath_experimental.dtd\">\n");
134 bw.write("<suppressions>\n");
135 bw.write(" <suppress-xpath\n");
136 bw.write(" checks=\"");
137 bw.write(checkName);
138 bw.write("\"\n");
139 bw.write(" query=\"");
140 bw.write(String.join(DELIMITER, xpathQueries));
141 bw.write("\"/>\n");
142 bw.write("</suppressions>");
143 }
144
145 return suppressionsXpathConfigPath.toString();
146 }
147
148
149
150
151
152
153
154
155
156 private DefaultConfiguration createSuppressionXpathFilter(String checkName,
157 List<String> xpathQueries) throws Exception {
158 final DefaultConfiguration suppressionXpathFilterConfig =
159 createModuleConfig(SuppressionXpathFilter.class);
160 suppressionXpathFilterConfig.addProperty("file",
161 createSuppressionsXpathConfigFile(checkName, xpathQueries));
162
163 return suppressionXpathFilterConfig;
164 }
165
166
167
168
169
170
171
172 private static ViolationPosition extractLineAndColumnNumber(String... expectedViolations) {
173 ViolationPosition violation = null;
174 final Matcher matcher =
175 LINE_COLUMN_NUMBER_REGEX.matcher(expectedViolations[0]);
176 if (matcher.find()) {
177 final int violationLineNumber = Integer.parseInt(matcher.group(1));
178 final int violationColumnNumber = Integer.parseInt(matcher.group(2));
179 violation = new ViolationPosition(violationLineNumber, violationColumnNumber);
180 }
181 return violation;
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 protected void runVerifications(DefaultConfiguration moduleConfig,
201 File fileToProcess,
202 String[] expectedViolation,
203 List<String> expectedXpathQueries) throws Exception {
204 if (expectedViolation.length != 1) {
205 throw new IllegalArgumentException(
206 "Expected violations should contain exactly one element."
207 + " Multiple violations are not supported."
208 );
209 }
210
211 final ViolationPosition position =
212 extractLineAndColumnNumber(expectedViolation);
213 final List<String> generatedXpathQueries =
214 generateXpathQueries(fileToProcess, position);
215
216 final DefaultConfiguration treeWalkerConfigWithXpath =
217 createModuleConfig(TreeWalker.class);
218 treeWalkerConfigWithXpath.addChild(moduleConfig);
219 treeWalkerConfigWithXpath.addChild(createSuppressionXpathFilter(moduleConfig.getName(),
220 generatedXpathQueries));
221
222 final Integer[] warnList = getLinesWithWarn(fileToProcess.getPath());
223 verify(moduleConfig, fileToProcess.getPath(), expectedViolation, warnList);
224 verifyXpathQueries(generatedXpathQueries, expectedXpathQueries);
225 verify(treeWalkerConfigWithXpath, fileToProcess.getPath(), CommonUtil.EMPTY_STRING_ARRAY);
226 }
227
228 private static final class ViolationPosition {
229 private final int violationLineNumber;
230 private final int violationColumnNumber;
231
232
233
234
235
236
237
238 private ViolationPosition(int violationLineNumber,
239 int violationColumnNumber) {
240 this.violationLineNumber = violationLineNumber;
241 this.violationColumnNumber = violationColumnNumber;
242 }
243 }
244 }