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.filters;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23 import static com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck.MSG_INVALID_PATTERN;
24 import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
25 import static org.junit.jupiter.api.Assumptions.assumeTrue;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.net.HttpURLConnection;
31 import java.net.URI;
32 import java.net.URL;
33 import java.util.UUID;
34
35 import org.junit.jupiter.api.Test;
36 import org.junit.jupiter.api.io.TempDir;
37
38 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
39 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
40 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
41 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
42 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
43 import com.puppycrawl.tools.checkstyle.api.Violation;
44 import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
45 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
46
47 public class SuppressionFilterTest extends AbstractModuleTestSupport {
48
49 @TempDir
50 public File temporaryFolder;
51
52 @Override
53 public String getPackageLocation() {
54 return "com/puppycrawl/tools/checkstyle/filters/suppressionfilter";
55 }
56
57 @Test
58 public void testAccept() throws Exception {
59 final String fileName = getPath("InputSuppressionFilterNone.xml");
60 final boolean optional = false;
61 final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
62
63 final AuditEvent ev = new AuditEvent(this, "ATest.java", null);
64
65 assertWithMessage("Audit event should be excepted when there are no suppressions")
66 .that(filter.accept(ev))
67 .isTrue();
68 }
69
70 @Test
71 public void testAcceptFalse() throws Exception {
72 final String fileName = getPath("InputSuppressionFilterSuppress.xml");
73 final boolean optional = false;
74 final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
75
76 final Violation message = new Violation(1, 1, null, "msg", null,
77 SeverityLevel.ERROR, null, getClass(), null);
78 final AuditEvent ev = new AuditEvent(this, "ATest.java", message);
79
80 assertWithMessage("Audit event should be rejected when there is a matching suppression")
81 .that(filter.accept(ev))
82 .isFalse();
83 }
84
85 @Test
86 public void testAcceptOnNullFile() throws CheckstyleException {
87 final String fileName = null;
88 final boolean optional = false;
89 final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
90
91 final AuditEvent ev = new AuditEvent(this, "AnyJava.java", null);
92 assertWithMessage("Audit event on null file should be excepted, but was not")
93 .that(filter.accept(ev))
94 .isTrue();
95 }
96
97 @Test
98 public void testNonExistentSuppressionFileWithFalseOptional() {
99 final String fileName = "non_existent_suppression_file.xml";
100 final boolean optional = false;
101 final CheckstyleException exc = getExpectedThrowable(
102 CheckstyleException.class,
103 () -> {
104 createSuppressionFilter(fileName, optional);
105 });
106 assertWithMessage("Invalid error message")
107 .that(exc.getMessage())
108 .isEqualTo("Unable to find: " + fileName);
109 }
110
111 @Test
112 public void testExistingInvalidSuppressionFileWithTrueOptional() throws Exception {
113 final String fileName = getPath("InputSuppressionFilterInvalidFile.xml");
114 final boolean optional = true;
115 final CheckstyleException exc = getExpectedThrowable(
116 CheckstyleException.class,
117 () -> {
118 createSuppressionFilter(fileName, optional);
119 });
120 assertWithMessage("Invalid error message")
121 .that(exc.getMessage())
122 .isEqualTo("Unable to parse " + fileName
123 + " - invalid files or checks or message format");
124 }
125
126 @Test
127 public void testExistingSuppressionFileWithTrueOptional() throws Exception {
128 final String fileName = getPath("InputSuppressionFilterNone.xml");
129 final boolean optional = true;
130 final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
131
132 final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
133
134 assertWithMessage("Suppression file with true optional was not accepted")
135 .that(filter.accept(ev))
136 .isTrue();
137 }
138
139 @Test
140 public void testNonExistentSuppressionFileWithTrueOptional() throws Exception {
141 final String fileName = "non_existent_suppression_file.xml";
142 final boolean optional = true;
143 final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
144
145 final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
146
147 assertWithMessage("Should except event when suppression file does not exist")
148 .that(filter.accept(ev))
149 .isTrue();
150 }
151
152 @Test
153 public void testNonExistentSuppressionUrlWithTrueOptional() throws Exception {
154 final String fileName =
155 "https://checkstyle.org/non_existent_suppression.xml";
156 final boolean optional = true;
157 final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
158
159 final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
160
161 assertWithMessage("Should except event when suppression file url does not exist")
162 .that(filter.accept(ev))
163 .isTrue();
164 }
165
166 @Test
167 public void testUseCacheLocalFileExternalResourceContentDoesNotChange() throws Exception {
168 final DefaultConfiguration filterConfig = createModuleConfig(SuppressionFilter.class);
169 filterConfig.addProperty("file", getPath("InputSuppressionFilterNone.xml"));
170
171 final DefaultConfiguration checkerConfig = createRootConfig(filterConfig);
172 final String uniqueFileName = "junit_" + UUID.randomUUID() + ".java";
173 final File cacheFile = new File(temporaryFolder, uniqueFileName);
174 checkerConfig.addProperty("cacheFile", cacheFile.getPath());
175
176 final File filePath = new File(temporaryFolder, uniqueFileName);
177
178 execute(checkerConfig, filePath.toString());
179
180 execute(checkerConfig, filePath.toString());
181 }
182
183 @Test
184 public void testUseCacheRemoteFileExternalResourceContentDoesNotChange() throws Exception {
185 final String[] urlCandidates = {
186 "https://checkstyle.org/files/suppressions_none.xml",
187 "https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/site/resources/"
188 + "files/suppressions_none.xml",
189 };
190
191 String urlForTest = null;
192 for (String url : urlCandidates) {
193 if (isConnectionAvailableAndStable(url)) {
194 urlForTest = url;
195 break;
196 }
197 }
198
199 assumeTrue(urlForTest != null, "No Internet connection.");
200 final DefaultConfiguration firstFilterConfig = createModuleConfig(SuppressionFilter.class);
201
202 firstFilterConfig.addProperty("file", urlForTest);
203
204 final DefaultConfiguration firstCheckerConfig = createRootConfig(firstFilterConfig);
205 final String uniqueFileName1 = "junit_" + UUID.randomUUID() + ".java";
206 final File cacheFile = new File(temporaryFolder, uniqueFileName1);
207 firstCheckerConfig.addProperty("cacheFile", cacheFile.getPath());
208
209 final String uniqueFileName2 = "file_" + UUID.randomUUID() + ".java";
210 final File pathToEmptyFile = new File(temporaryFolder, uniqueFileName2);
211
212 execute(firstCheckerConfig, pathToEmptyFile.toString());
213
214
215 final DefaultConfiguration secondFilterConfig =
216 createModuleConfig(SuppressionFilter.class);
217
218 secondFilterConfig.addProperty("file", urlForTest);
219
220 final DefaultConfiguration secondCheckerConfig = createRootConfig(secondFilterConfig);
221 secondCheckerConfig.addProperty("cacheFile", cacheFile.getPath());
222
223 execute(secondCheckerConfig, pathToEmptyFile.toString());
224 }
225
226 private static boolean isConnectionAvailableAndStable(String url) throws Exception {
227 boolean available = false;
228
229 if (isUrlReachable(url)) {
230 final int attemptLimit = 5;
231 int attemptCount = 0;
232
233 while (attemptCount <= attemptLimit) {
234 try (InputStream stream = URI.create(url).toURL().openStream()) {
235
236 available = stream.read() != -1;
237 break;
238 }
239 catch (IOException exc) {
240 if (attemptCount < attemptLimit && exc.getMessage()
241 .contains("Unable to read")) {
242 attemptCount++;
243 available = false;
244
245 Thread.sleep(1000);
246 }
247 else {
248 throw exc;
249 }
250 }
251 }
252 }
253 return available;
254 }
255
256 private static boolean isUrlReachable(String url) {
257 boolean result = true;
258 try {
259 final URL verifiableUrl = URI.create(url).toURL();
260 final HttpURLConnection urlConnect = (HttpURLConnection) verifiableUrl.openConnection();
261 urlConnect.getContent();
262 }
263 catch (IOException ignored) {
264 result = false;
265 }
266 return result;
267 }
268
269 private static SuppressionFilter createSuppressionFilter(String fileName, boolean optional)
270 throws CheckstyleException {
271 final SuppressionFilter suppressionFilter = new SuppressionFilter();
272 suppressionFilter.setFile(fileName);
273 suppressionFilter.setOptional(optional);
274 suppressionFilter.finishLocalSetup();
275 return suppressionFilter;
276 }
277
278 @Test
279 public void testXpathSuppression() throws Exception {
280 for (int test = 1; test <= 6; test++) {
281 final String pattern = "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$";
282 final String[] expected = {
283 "19:29: " + getCheckMessage(ConstantNameCheck.class, MSG_INVALID_PATTERN,
284 "different_name_than_suppression", pattern),
285 };
286 final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
287 final String path = "InputSuppressionFilter" + test + ".java";
288 verifyFilterWithInlineConfigParser(getPath(path),
289 expected, suppressed);
290 }
291 }
292
293 @Test
294 public void testSuppression2() throws Exception {
295 final String pattern = "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$";
296 final String[] expected = {
297 "19:29: " + getCheckMessage(ConstantNameCheck.class,
298 MSG_INVALID_PATTERN, "bad_name", pattern),
299 };
300 final String[] suppressed = {
301 "19:29: " + getCheckMessage(ConstantNameCheck.class,
302 MSG_INVALID_PATTERN, "bad_name", pattern),
303
304 };
305 verifyFilterWithInlineConfigParser(getPath("InputSuppressionFilter7.java"),
306 expected, removeSuppressed(expected, suppressed));
307 }
308
309 @Test
310 public void testSuppression3() throws Exception {
311 final String pattern = "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$";
312 final String[] expected = {
313 "19:29: " + getCheckMessage(ConstantNameCheck.class,
314 MSG_INVALID_PATTERN, "bad_name", pattern),
315 };
316 final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
317 verifyFilterWithInlineConfigParser(getPath("InputSuppressionFilter8.java"),
318 expected, removeSuppressed(expected, suppressed));
319 }
320 }