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