View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.filters;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static org.junit.jupiter.api.Assumptions.assumeTrue;
24  
25  import java.io.IOException;
26  import java.net.HttpURLConnection;
27  import java.net.URL;
28  import java.util.HashSet;
29  import java.util.Set;
30  
31  import org.junit.jupiter.api.Test;
32  import org.xml.sax.InputSource;
33  
34  import com.puppycrawl.tools.checkstyle.AbstractPathTestSupport;
35  import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
36  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
37  import com.puppycrawl.tools.checkstyle.api.FilterSet;
38  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
39  
40  /**
41   * Tests SuppressionsLoader.
42   */
43  public class SuppressionsLoaderTest extends AbstractPathTestSupport {
44  
45      @Override
46      protected String getPackageLocation() {
47          return "com/puppycrawl/tools/checkstyle/filters/suppressionsloader";
48      }
49  
50      @Test
51      public void testNoSuppressions() throws Exception {
52          final FilterSet fc =
53              SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderNone.xml"));
54          final FilterSet fc2 = new FilterSet();
55          assertWithMessage("No suppressions should be loaded, but found: " + fc.getFilters().size())
56              .that(fc.getFilters())
57              .isEqualTo(fc2.getFilters());
58      }
59  
60      @Test
61      public void testLoadFromUrl() throws Exception {
62          final String[] urlCandidates = {
63              "https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/site/resources/"
64                  + "files/suppressions_none.xml",
65              "https://checkstyle.org/files/suppressions_none.xml",
66          };
67          FilterSet actualFilterSet = null;
68  
69          for (String url : urlCandidates) {
70              actualFilterSet = loadFilterSet(url);
71  
72              if (actualFilterSet != null) {
73                  break;
74              }
75          }
76  
77          assumeTrue(actualFilterSet != null, "No Internet connection.");
78          final FilterSet expectedFilterSet = new FilterSet();
79          assertWithMessage("Failed to load from url")
80              .that(actualFilterSet.getFilters())
81              .isEqualTo(expectedFilterSet.getFilters());
82      }
83  
84      @Test
85      public void testLoadFromMalformedUrl() {
86          try {
87              SuppressionsLoader.loadSuppressions("http");
88              assertWithMessage("exception expected").fail();
89          }
90          catch (CheckstyleException ex) {
91              assertWithMessage("Invalid error message")
92                  .that(ex.getMessage())
93                  .isEqualTo("Unable to find: http");
94          }
95      }
96  
97      @Test
98      public void testLoadFromNonExistentUrl() {
99          try {
100             SuppressionsLoader.loadSuppressions("http://^%$^* %&% %^&");
101             assertWithMessage("exception expected").fail();
102         }
103         catch (CheckstyleException ex) {
104             assertWithMessage("Invalid error message")
105                 .that(ex.getMessage())
106                 .isEqualTo("Unable to find: http://^%$^* %&% %^&");
107         }
108     }
109 
110     @Test
111     public void testMultipleSuppression() throws Exception {
112         final FilterSet fc =
113             SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderMultiple.xml"));
114         final FilterSet fc2 = new FilterSet();
115 
116         final SuppressFilterElement se0 =
117                 new SuppressFilterElement("file0", "check0", null, null, null, null);
118         fc2.addFilter(se0);
119         final SuppressFilterElement se1 =
120                 new SuppressFilterElement("file1", "check1", null, null, "1,2-3", null);
121         fc2.addFilter(se1);
122         final SuppressFilterElement se2 =
123                 new SuppressFilterElement("file2", "check2", null, null, null, "1,2-3");
124         fc2.addFilter(se2);
125         final SuppressFilterElement se3 =
126                 new SuppressFilterElement("file3", "check3", null, null, "1,2-3", "1,2-3");
127         fc2.addFilter(se3);
128         final SuppressFilterElement se4 =
129                 new SuppressFilterElement(null, null, "message0", null, null, null);
130         fc2.addFilter(se4);
131         assertWithMessage("Multiple suppressions were loaded incorrectly")
132             .that(fc.getFilters())
133             .isEqualTo(fc2.getFilters());
134     }
135 
136     @Test
137     public void testNoFile() throws IOException {
138         final String fn = getPath("InputSuppressionsLoaderNoFile.xml");
139         try {
140             SuppressionsLoader.loadSuppressions(fn);
141             assertWithMessage("Exception is expected").fail();
142         }
143         catch (CheckstyleException ex) {
144             final String messageStart = "Unable to parse " + fn;
145             assertWithMessage("Exception message should start with: " + messageStart)
146                     .that(ex.getMessage())
147                     .startsWith("Unable to parse " + fn);
148             assertWithMessage("Exception message should contain \"files\"")
149                     .that(ex.getMessage())
150                     .contains("\"files\"");
151             assertWithMessage("Exception message should contain \"suppress\"")
152                     .that(ex.getMessage())
153                     .contains("\"suppress\"");
154         }
155     }
156 
157     @Test
158     public void testNoCheck() throws IOException {
159         final String fn = getPath("InputSuppressionsLoaderNoCheck.xml");
160         try {
161             SuppressionsLoader.loadSuppressions(fn);
162             assertWithMessage("Exception is expected").fail();
163         }
164         catch (CheckstyleException ex) {
165             final String messageStart = "Unable to parse " + fn;
166             assertWithMessage("Exception message should start with: " + messageStart)
167                     .that(ex.getMessage())
168                     .startsWith(messageStart);
169             assertWithMessage("Exception message should contain \"checks\"")
170                     .that(ex.getMessage())
171                     .contains("\"checks\"");
172             assertWithMessage("Exception message should contain \"suppress\"")
173                     .that(ex.getMessage())
174                     .contains("\"suppress\"");
175         }
176     }
177 
178     @Test
179     public void testBadInt() throws IOException {
180         final String fn = getPath("InputSuppressionsLoaderBadInt.xml");
181         try {
182             SuppressionsLoader.loadSuppressions(fn);
183             assertWithMessage("Exception is expected").fail();
184         }
185         catch (CheckstyleException ex) {
186             assertWithMessage(ex.getMessage())
187                 .that(ex.getMessage())
188                 .startsWith("Number format exception " + fn + " - For input string: \"a\"");
189         }
190     }
191 
192     private static FilterSet loadFilterSet(String url) throws Exception {
193         FilterSet filterSet = null;
194 
195         if (isUrlReachable(url)) {
196             int attemptCount = 0;
197             final int attemptLimit = 5;
198 
199             while (attemptCount <= attemptLimit) {
200                 try {
201                     filterSet = SuppressionsLoader.loadSuppressions(url);
202                     break;
203                 }
204                 catch (CheckstyleException ex) {
205                     // for some reason Travis CI failed sometimes (unstable) on reading this file
206                     if (attemptCount < attemptLimit && ex.getMessage().contains("Unable to read")) {
207                         attemptCount++;
208                         // wait for bad/disconnection time to pass
209                         Thread.sleep(1000);
210                     }
211                     else {
212                         throw ex;
213                     }
214                 }
215             }
216         }
217         return filterSet;
218     }
219 
220     private static boolean isUrlReachable(String url) {
221         boolean result = true;
222         try {
223             final URL verifiableUrl = new URL(url);
224             final HttpURLConnection urlConnect = (HttpURLConnection) verifiableUrl.openConnection();
225             urlConnect.getContent();
226         }
227         catch (IOException ignored) {
228             result = false;
229         }
230         return result;
231     }
232 
233     @Test
234     public void testUnableToFindSuppressions() {
235         final String sourceName = "InputSuppressionsLoaderNone.xml";
236 
237         try {
238             TestUtil.invokeStaticMethod(SuppressionsLoader.class, "loadSuppressions",
239                     new InputSource(sourceName), sourceName);
240             assertWithMessage("InvocationTargetException is expected").fail();
241         }
242         catch (ReflectiveOperationException ex) {
243             assertWithMessage("Invalid exception cause message")
244                 .that(ex)
245                     .hasCauseThat()
246                         .hasMessageThat()
247                         .isEqualTo("Unable to find: " + sourceName);
248         }
249     }
250 
251     @Test
252     public void testUnableToReadSuppressions() {
253         final String sourceName = "InputSuppressionsLoaderNone.xml";
254 
255         try {
256             TestUtil.invokeStaticMethod(SuppressionsLoader.class, "loadSuppressions",
257                     new InputSource(), sourceName);
258             assertWithMessage("InvocationTargetException is expected").fail();
259         }
260         catch (ReflectiveOperationException ex) {
261             assertWithMessage("Invalid exception cause message")
262                 .that(ex)
263                     .hasCauseThat()
264                         .hasMessageThat()
265                         .isEqualTo("Unable to read " + sourceName);
266         }
267     }
268 
269     @Test
270     public void testNoCheckNoId() throws IOException {
271         final String fn = getPath("InputSuppressionsLoaderNoCheckAndId.xml");
272         try {
273             SuppressionsLoader.loadSuppressions(fn);
274             assertWithMessage("Exception is expected").fail();
275         }
276         catch (CheckstyleException ex) {
277             assertWithMessage("Invalid error message")
278                 .that(ex.getMessage())
279                 .isEqualTo("Unable to parse " + fn
280                         + " - missing checks or id or message attribute");
281         }
282     }
283 
284     @Test
285     public void testNoCheckYesId() throws Exception {
286         final String fn = getPath("InputSuppressionsLoaderId.xml");
287         final FilterSet set = SuppressionsLoader.loadSuppressions(fn);
288 
289         assertWithMessage("Invalid number of filters")
290             .that(set.getFilters())
291             .hasSize(1);
292     }
293 
294     @Test
295     public void testInvalidFileFormat() throws IOException {
296         final String fn = getPath("InputSuppressionsLoaderInvalidFile.xml");
297         try {
298             SuppressionsLoader.loadSuppressions(fn);
299             assertWithMessage("Exception is expected").fail();
300         }
301         catch (CheckstyleException ex) {
302             assertWithMessage("Invalid error message")
303                 .that(ex.getMessage())
304                 .isEqualTo("Unable to parse " + fn
305                         + " - invalid files or checks or message format");
306         }
307     }
308 
309     @Test
310     public void testLoadFromClasspath() throws Exception {
311         final FilterSet fc =
312             SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderNone.xml"));
313         final FilterSet fc2 = new FilterSet();
314         assertWithMessage("Suppressions were not loaded")
315             .that(fc.getFilters())
316             .isEqualTo(fc2.getFilters());
317     }
318 
319     @Test
320     public void testSettingModuleId() throws Exception {
321         final FilterSet fc =
322                 SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderWithId.xml"));
323         final SuppressFilterElement suppressElement = (SuppressFilterElement) fc.getFilters()
324                 .toArray()[0];
325 
326         final String id = TestUtil.getInternalState(suppressElement, "moduleId");
327         assertWithMessage("Id has to be defined")
328             .that(id)
329             .isEqualTo("someId");
330     }
331 
332     @Test
333     public void testXpathSuppressions() throws Exception {
334         final String fn = getPath("InputSuppressionsLoaderXpathCorrect.xml");
335         final Set<TreeWalkerFilter> filterSet = SuppressionsLoader.loadXpathSuppressions(fn);
336 
337         final Set<TreeWalkerFilter> expectedFilterSet = new HashSet<>();
338         final XpathFilterElement xf0 =
339                 new XpathFilterElement("file1", "test", null, "id1", "//CLASS_DEF");
340         expectedFilterSet.add(xf0);
341         final XpathFilterElement xf1 =
342                 new XpathFilterElement(null, null, "message1", null, "//CLASS_DEF");
343         expectedFilterSet.add(xf1);
344         assertWithMessage("Multiple xpath suppressions were loaded incorrectly")
345             .that(filterSet)
346             .isEqualTo(expectedFilterSet);
347     }
348 
349     @Test
350     public void testXpathInvalidFileFormat() throws IOException {
351         final String fn = getPath("InputSuppressionsLoaderXpathInvalidFile.xml");
352         try {
353             SuppressionsLoader.loadXpathSuppressions(fn);
354             assertWithMessage("Exception should be thrown").fail();
355         }
356         catch (CheckstyleException ex) {
357             assertWithMessage("Invalid error message")
358                 .that(ex.getMessage())
359                 .isEqualTo("Unable to parse " + fn
360                         + " - invalid files or checks or message format for suppress-xpath");
361         }
362     }
363 
364     @Test
365     public void testXpathNoCheckNoId() throws IOException {
366         final String fn =
367                 getPath("InputSuppressionsLoaderXpathNoCheckAndId.xml");
368         try {
369             SuppressionsLoader.loadXpathSuppressions(fn);
370             assertWithMessage("Exception should be thrown").fail();
371         }
372         catch (CheckstyleException ex) {
373             assertWithMessage("Invalid error message")
374                 .that(ex.getMessage())
375                 .isEqualTo("Unable to parse " + fn
376                         + " - missing checks or id or message attribute for suppress-xpath");
377         }
378     }
379 
380     @Test
381     public void testXpathNoCheckYesId() throws Exception {
382         final String fn = getPath("InputSuppressionsLoaderXpathId.xml");
383         final Set<TreeWalkerFilter> filterSet = SuppressionsLoader.loadXpathSuppressions(fn);
384 
385         assertWithMessage("Invalid number of filters")
386             .that(filterSet)
387             .hasSize(1);
388     }
389 
390 }