View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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 org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.io.IOException;
27  import java.lang.reflect.Method;
28  import java.net.HttpURLConnection;
29  import java.net.URL;
30  import java.util.HashSet;
31  import java.util.Set;
32  
33  import org.junit.Rule;
34  import org.junit.Test;
35  import org.junit.rules.ExpectedException;
36  import org.junit.runner.RunWith;
37  import org.powermock.core.classloader.annotations.PrepareForTest;
38  import org.powermock.modules.junit4.PowerMockRunner;
39  import org.powermock.reflect.Whitebox;
40  import org.xml.sax.InputSource;
41  
42  import com.puppycrawl.tools.checkstyle.AbstractPathTestSupport;
43  import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
44  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
45  import com.puppycrawl.tools.checkstyle.api.FilterSet;
46  
47  /**
48   * Tests SuppressionsLoader.
49   * @author Rick Giles
50   * @author <a href="mailto:andreyselkin@gmail.com">Andrei Selkin</a>
51   */
52  @RunWith(PowerMockRunner.class)
53  @PrepareForTest({ SuppressionsLoader.class, SuppressionsLoaderTest.class })
54  public class SuppressionsLoaderTest extends AbstractPathTestSupport {
55      @Rule
56      public final ExpectedException thrown = ExpectedException.none();
57  
58      @Override
59      protected String getPackageLocation() {
60          return "com/puppycrawl/tools/checkstyle/filters/suppressionsloader";
61      }
62  
63      @Test
64      public void testNoSuppressions() throws Exception {
65          final FilterSet fc =
66              SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderNone.xml"));
67          final FilterSet fc2 = new FilterSet();
68          assertEquals("No suppressions should be loaded, but found: " + fc.getFilters().size(),
69              fc2, fc);
70      }
71  
72      @Test
73      public void testLoadFromUrl() throws Exception {
74          final String[] urlCandidates = {
75              "http://checkstyle.sourceforge.net/files/suppressions_none.xml",
76              "https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/site/resources/"
77                  + "files/suppressions_none.xml",
78          };
79          FilterSet actualFilterSet = null;
80  
81          for (String url : urlCandidates) {
82              actualFilterSet = loadFilterSet(url);
83  
84              if (actualFilterSet != null) {
85                  break;
86              }
87          }
88          // Use Assume.assumeNotNull(actualFilterSet) instead of the if-condition
89          // when https://github.com/jayway/powermock/issues/428 will be fixed
90          if (actualFilterSet != null) {
91              final FilterSet expectedFilterSet = new FilterSet();
92              assertEquals("Failed to load from url", expectedFilterSet, actualFilterSet);
93          }
94      }
95  
96      @Test
97      public void testLoadFromMalformedUrl() {
98          try {
99              SuppressionsLoader.loadSuppressions("http");
100             fail("exception expected");
101         }
102         catch (CheckstyleException ex) {
103             assertEquals("Invalid error message", "Unable to find: http", ex.getMessage());
104         }
105     }
106 
107     @Test
108     public void testLoadFromNonExistentUrl() {
109         try {
110             SuppressionsLoader.loadSuppressions("http://^%$^* %&% %^&");
111             fail("exception expected");
112         }
113         catch (CheckstyleException ex) {
114             assertEquals("Invalid error message",
115                 "Unable to find: http://^%$^* %&% %^&", ex.getMessage());
116         }
117     }
118 
119     @Test
120     public void testMultipleSuppression() throws Exception {
121         final FilterSet fc =
122             SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderMultiple.xml"));
123         final FilterSet fc2 = new FilterSet();
124 
125         final SuppressElement se0 =
126                 new SuppressElement("file0", "check0", null, null, null, null);
127         fc2.addFilter(se0);
128         final SuppressElement se1 =
129                 new SuppressElement("file1", "check1", null, null, "1,2-3", null);
130         fc2.addFilter(se1);
131         final SuppressElement se2 =
132                 new SuppressElement("file2", "check2", null, null, null, "1,2-3");
133         fc2.addFilter(se2);
134         final SuppressElement se3 =
135                 new SuppressElement("file3", "check3", null, null, "1,2-3", "1,2-3");
136         fc2.addFilter(se3);
137         final SuppressElement se4 =
138                 new SuppressElement(null, null, "message0", null, null, null);
139         fc2.addFilter(se4);
140         assertEquals("Multiple suppressions were loaded incorrectly", fc2, fc);
141     }
142 
143     @Test
144     public void testNoFile() throws IOException {
145         final String fn = getPath("InputSuppressionsLoaderNoFile.xml");
146         try {
147             SuppressionsLoader.loadSuppressions(fn);
148             fail("Exception is expected");
149         }
150         catch (CheckstyleException ex) {
151             final String messageStart = "Unable to parse " + fn;
152             assertTrue("Exception message should start with: " + messageStart,
153                 ex.getMessage().startsWith("Unable to parse " + fn));
154             assertTrue("Exception message should contain \"files\"",
155                 ex.getMessage().contains("\"files\""));
156             assertTrue("Exception message should contain \"suppress\"",
157                 ex.getMessage().contains("\"suppress\""));
158         }
159     }
160 
161     @Test
162     public void testNoCheck() throws IOException {
163         final String fn = getPath("InputSuppressionsLoaderNoCheck.xml");
164         try {
165             SuppressionsLoader.loadSuppressions(fn);
166             fail("Exception is expected");
167         }
168         catch (CheckstyleException ex) {
169             final String messageStart = "Unable to parse " + fn;
170             assertTrue("Exception message should start with: " + messageStart,
171                 ex.getMessage().startsWith(messageStart));
172             assertTrue("Exception message should contain \"checks\"",
173                 ex.getMessage().contains("\"checks\""));
174             assertTrue("Exception message should contain \"suppress\"",
175                 ex.getMessage().contains("\"suppress\""));
176         }
177     }
178 
179     @Test
180     public void testBadInt() throws IOException {
181         final String fn = getPath("InputSuppressionsLoaderBadInt.xml");
182         try {
183             SuppressionsLoader.loadSuppressions(fn);
184             fail("Exception is expected");
185         }
186         catch (CheckstyleException ex) {
187             assertTrue(
188                 ex.getMessage(),
189                 ex.getMessage().startsWith("Number format exception " + fn + " - "));
190         }
191     }
192 
193     private static FilterSet loadFilterSet(String url) throws Exception {
194         FilterSet filterSet = null;
195 
196         if (isUrlReachable(url)) {
197             int attemptCount = 0;
198             final int attemptLimit = 5;
199 
200             while (attemptCount <= attemptLimit) {
201                 try {
202                     filterSet = SuppressionsLoader.loadSuppressions(url);
203                     break;
204                 }
205                 catch (CheckstyleException ex) {
206                     // for some reason Travis CI failed some times(unstable) on reading this file
207                     if (attemptCount < attemptLimit && ex.getMessage().contains("Unable to read")) {
208                         attemptCount++;
209                         // wait for bad/disconnection time to pass
210                         Thread.sleep(1000);
211                     }
212                     else {
213                         throw ex;
214                     }
215                 }
216             }
217         }
218         return filterSet;
219     }
220 
221     private static boolean isUrlReachable(String url) {
222         boolean result = true;
223         try {
224             final URL verifiableUrl = new URL(url);
225             final HttpURLConnection urlConnect = (HttpURLConnection) verifiableUrl.openConnection();
226             urlConnect.getContent();
227         }
228         catch (IOException ignored) {
229             result = false;
230         }
231         return result;
232     }
233 
234     @Test
235     public void testUnableToFindSuppressions() throws Exception {
236         final Class<SuppressionsLoader> loaderClass = SuppressionsLoader.class;
237         final Method loadSuppressions =
238             loaderClass.getDeclaredMethod("loadSuppressions", InputSource.class, String.class);
239         loadSuppressions.setAccessible(true);
240 
241         final String sourceName = "InputSuppressionsLoaderNone.xml";
242         final InputSource inputSource = new InputSource(sourceName);
243 
244         thrown.expect(CheckstyleException.class);
245         thrown.expectMessage("Unable to find: " + sourceName);
246 
247         loadSuppressions.invoke(loaderClass, inputSource, sourceName);
248     }
249 
250     @Test
251     public void testUnableToReadSuppressions() throws Exception {
252         final Class<SuppressionsLoader> loaderClass = SuppressionsLoader.class;
253         final Method loadSuppressions =
254             loaderClass.getDeclaredMethod("loadSuppressions", InputSource.class, String.class);
255         loadSuppressions.setAccessible(true);
256 
257         final InputSource inputSource = new InputSource();
258 
259         thrown.expect(CheckstyleException.class);
260         final String sourceName = "InputSuppressionsLoaderNone.xml";
261         thrown.expectMessage("Unable to read " + sourceName);
262 
263         loadSuppressions.invoke(loaderClass, inputSource, sourceName);
264     }
265 
266     @Test
267     public void testNoCheckNoId() throws IOException {
268         final String fn = getPath("InputSuppressionsLoaderNoCheckAndId.xml");
269         try {
270             SuppressionsLoader.loadSuppressions(fn);
271             fail("Exception is expected");
272         }
273         catch (CheckstyleException ex) {
274             assertEquals("Invalid error message",
275                 "Unable to parse " + fn + " - missing checks or id or message attribute",
276                 ex.getMessage());
277         }
278     }
279 
280     @Test
281     public void testNoCheckYesId() throws Exception {
282         final String fn = getPath("InputSuppressionsLoaderId.xml");
283         final FilterSet set = SuppressionsLoader.loadSuppressions(fn);
284 
285         assertEquals("Invalid number of filters", 1, set.getFilters().size());
286     }
287 
288     @Test
289     public void testInvalidFileFormat() throws IOException {
290         final String fn = getPath("InputSuppressionsLoaderInvalidFile.xml");
291         try {
292             SuppressionsLoader.loadSuppressions(fn);
293             fail("Exception is expected");
294         }
295         catch (CheckstyleException ex) {
296             assertEquals("Invalid error message",
297                 "Unable to parse " + fn + " - invalid files or checks or message format",
298                 ex.getMessage());
299         }
300     }
301 
302     @Test
303     public void testLoadFromClasspath() throws Exception {
304         final FilterSet fc =
305             SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderNone.xml"));
306         final FilterSet fc2 = new FilterSet();
307         assertEquals("Suppressions were not loaded", fc2, fc);
308     }
309 
310     @Test
311     public void testSettingModuleId() throws Exception {
312         final FilterSet fc =
313                 SuppressionsLoader.loadSuppressions(getPath("InputSuppressionsLoaderWithId.xml"));
314         final SuppressElement suppressElement = (SuppressElement) fc.getFilters().toArray()[0];
315 
316         final String id = Whitebox.getInternalState(suppressElement, "moduleId");
317         assertEquals("Id has to be defined", "someId", id);
318     }
319 
320     @Test
321     public void testXpathSuppressions() throws Exception {
322         final String fn = getPath("InputSuppressionsLoaderXpathCorrect.xml");
323         final Set<TreeWalkerFilter> filterSet = SuppressionsLoader.loadXpathSuppressions(fn);
324 
325         final Set<TreeWalkerFilter> expectedFilterSet = new HashSet<>();
326         final XpathFilter xf0 =
327                 new XpathFilter("file1", "test", null, "id1", "/CLASS_DEF");
328         expectedFilterSet.add(xf0);
329         final XpathFilter xf1 =
330                 new XpathFilter(null, null, "message1", null, "/CLASS_DEF");
331         expectedFilterSet.add(xf1);
332         assertEquals("Multiple xpath suppressions were loaded incorrectly", expectedFilterSet,
333                 filterSet);
334     }
335 
336     @Test
337     public void testXpathInvalidFileFormat() throws IOException {
338         final String fn = getPath("InputSuppressionsLoaderXpathInvalidFile.xml");
339         try {
340             SuppressionsLoader.loadXpathSuppressions(fn);
341             fail("Exception should be thrown");
342         }
343         catch (CheckstyleException ex) {
344             assertEquals("Invalid error message",
345                     "Unable to parse " + fn + " - invalid files or checks or message format for "
346                             + "suppress-xpath",
347                     ex.getMessage());
348         }
349     }
350 
351     @Test
352     public void testXpathNoCheckNoId() throws IOException {
353         final String fn =
354                 getPath("InputSuppressionsLoaderXpathNoCheckAndId.xml");
355         try {
356             SuppressionsLoader.loadXpathSuppressions(fn);
357             fail("Exception should be thrown");
358         }
359         catch (CheckstyleException ex) {
360             assertEquals("Invalid error message",
361                     "Unable to parse " + fn + " - missing checks or id or message attribute for "
362                             + "suppress-xpath",
363                     ex.getMessage());
364         }
365     }
366 
367     @Test
368     public void testXpathNoCheckYesId() throws Exception {
369         final String fn = getPath("InputSuppressionsLoaderXpathId.xml");
370         final Set<TreeWalkerFilter> filterSet = SuppressionsLoader.loadXpathSuppressions(fn);
371 
372         assertEquals("Invalid number of filters", 1, filterSet.size());
373     }
374 }