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.File;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.net.HttpURLConnection;
30  import java.net.URL;
31  
32  import org.junit.Rule;
33  import org.junit.Test;
34  import org.junit.rules.TemporaryFolder;
35  
36  import com.google.common.io.Closeables;
37  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
38  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
39  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
42  import nl.jqno.equalsverifier.EqualsVerifier;
43  import nl.jqno.equalsverifier.Warning;
44  
45  public class SuppressionFilterTest extends AbstractModuleTestSupport {
46  
47      @Rule
48      public final TemporaryFolder temporaryFolder = new TemporaryFolder();
49  
50      @Override
51      protected String getPackageLocation() {
52          return "com/puppycrawl/tools/checkstyle/filters/suppressionfilter";
53      }
54  
55      @Test
56      public void testEqualsAndHashCode() {
57          EqualsVerifier
58                  .forClass(SuppressionFilter.class)
59                  .usingGetClass()
60                  .withIgnoredFields("file", "optional", "configuration")
61                  .suppress(Warning.NONFINAL_FIELDS)
62                  .verify();
63      }
64  
65      @Test
66      public void testAccept() throws Exception {
67          final String fileName = getPath("InputSuppressionFilterNone.xml");
68          final boolean optional = false;
69          final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
70  
71          final AuditEvent ev = new AuditEvent(this, "ATest.java", null);
72  
73          assertTrue("Audit event should be excepted when there are no suppressions",
74              filter.accept(ev));
75      }
76  
77      @Test
78      public void testAcceptOnNullFile() throws CheckstyleException {
79          final String fileName = null;
80          final boolean optional = false;
81          final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
82  
83          final AuditEvent ev = new AuditEvent(this, "AnyJava.java", null);
84          assertTrue("Audit event on null file should be excepted, but was not", filter.accept(ev));
85      }
86  
87      @Test
88      public void testNonExistentSuppressionFileWithFalseOptional() {
89          final String fileName = "non_existent_suppression_file.xml";
90          try {
91              final boolean optional = false;
92              createSuppressionFilter(fileName, optional);
93              fail("Exception is expected");
94          }
95          catch (CheckstyleException ex) {
96              assertEquals("Invalid error message",
97                  "Unable to find: " + fileName, ex.getMessage());
98          }
99      }
100 
101     @Test
102     public void testExistingInvalidSuppressionFileWithTrueOptional() throws IOException {
103         final String fileName = getPath("InputSuppressionFilterInvalidFile.xml");
104         try {
105             final boolean optional = true;
106             createSuppressionFilter(fileName, optional);
107             fail("Exception is expected");
108         }
109         catch (CheckstyleException ex) {
110             assertEquals("Invalid error message",
111                 "Unable to parse " + fileName + " - invalid files or checks or message format",
112                 ex.getMessage());
113         }
114     }
115 
116     @Test
117     public void testExistingSuppressionFileWithTrueOptional() throws Exception {
118         final String fileName = getPath("InputSuppressionFilterNone.xml");
119         final boolean optional = true;
120         final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
121 
122         final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
123 
124         assertTrue("Suppression file with true optional was not accepted",
125             filter.accept(ev));
126     }
127 
128     @Test
129     public void testNonExistentSuppressionFileWithTrueOptional() throws Exception {
130         final String fileName = "non_existent_suppression_file.xml";
131         final boolean optional = true;
132         final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
133 
134         final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
135 
136         assertTrue("Should except event when suppression file does not exist",
137             filter.accept(ev));
138     }
139 
140     @Test
141     public void testNonExistentSuppressionUrlWithTrueOptional() throws Exception {
142         final String fileName =
143                 "http://checkstyle.sourceforge.net/non_existent_suppression.xml";
144         final boolean optional = true;
145         final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
146 
147         final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
148 
149         assertTrue("Should except event when suppression file url does not exist",
150             filter.accept(ev));
151     }
152 
153     @Test
154     public void testLocalFileExternalResourceContentDoesNotChange() throws Exception {
155         final DefaultConfiguration filterConfig = createModuleConfig(SuppressionFilter.class);
156         filterConfig.addAttribute("file", getPath("InputSuppressionFilterNone.xml"));
157 
158         final DefaultConfiguration checkerConfig = createRootConfig(filterConfig);
159         final File cacheFile = temporaryFolder.newFile();
160         checkerConfig.addAttribute("cacheFile", cacheFile.getPath());
161 
162         final String filePath = temporaryFolder.newFile("file.java").getPath();
163         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
164 
165         verify(checkerConfig, filePath, expected);
166         // One more time to use cache.
167         verify(checkerConfig, filePath, expected);
168     }
169 
170     @Test
171     public void testRemoteFileExternalResourceContentDoesNotChange() throws Exception {
172         final String[] urlCandidates = {
173             "http://checkstyle.sourceforge.net/files/suppressions_none.xml",
174             "https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/site/resources/"
175                 + "files/suppressions_none.xml",
176         };
177 
178         String urlForTest = null;
179         for (String url : urlCandidates) {
180             if (isConnectionAvailableAndStable(url)) {
181                 urlForTest = url;
182                 break;
183             }
184         }
185 
186         // Run the test only if connection is available and url is reachable.
187         // We must use an if statement over junit's rule or assume because
188         // powermock disrupts the assume exception and turns into a failure
189         // instead of a skip when it doesn't pass
190         if (urlForTest != null) {
191             final DefaultConfiguration firstFilterConfig =
192                 createModuleConfig(SuppressionFilter.class);
193             // -@cs[CheckstyleTestMakeup] need to test dynamic property
194             firstFilterConfig.addAttribute("file", urlForTest);
195 
196             final DefaultConfiguration firstCheckerConfig = createRootConfig(firstFilterConfig);
197             final File cacheFile = temporaryFolder.newFile();
198             firstCheckerConfig.addAttribute("cacheFile", cacheFile.getPath());
199 
200             final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath();
201             final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
202 
203             verify(firstCheckerConfig, pathToEmptyFile, expected);
204 
205             // One more time to use cache.
206             final DefaultConfiguration secondFilterConfig =
207                 createModuleConfig(SuppressionFilter.class);
208             // -@cs[CheckstyleTestMakeup] need to test dynamic property
209             secondFilterConfig.addAttribute("file", urlForTest);
210 
211             final DefaultConfiguration secondCheckerConfig = createRootConfig(secondFilterConfig);
212             secondCheckerConfig.addAttribute("cacheFile", cacheFile.getPath());
213 
214             verify(secondCheckerConfig, pathToEmptyFile, expected);
215         }
216     }
217 
218     private static boolean isConnectionAvailableAndStable(String url) throws Exception {
219         boolean available = false;
220 
221         if (isUrlReachable(url)) {
222             final int attemptLimit = 5;
223             int attemptCount = 0;
224 
225             while (attemptCount <= attemptLimit) {
226                 InputStream stream = null;
227                 try {
228                     final URL address = new URL(url);
229                     stream = address.openStream();
230                     // Attempt to read a byte in order to check whether file content is available
231                     available = stream.read() != -1;
232                     break;
233                 }
234                 catch (IOException ex) {
235                     // for some reason Travis CI failed some times (unstable) on reading the file
236                     if (attemptCount < attemptLimit && ex.getMessage().contains("Unable to read")) {
237                         attemptCount++;
238                         available = false;
239                         // wait for bad / disconnection time to pass
240                         Thread.sleep(1000);
241                     }
242                     else {
243                         Closeables.closeQuietly(stream);
244                         throw ex;
245                     }
246                 }
247                 finally {
248                     Closeables.closeQuietly(stream);
249                 }
250             }
251         }
252         return available;
253     }
254 
255     private static boolean isUrlReachable(String url) {
256         boolean result = true;
257         try {
258             final URL verifiableUrl = new URL(url);
259             final HttpURLConnection urlConnect = (HttpURLConnection) verifiableUrl.openConnection();
260             urlConnect.getContent();
261         }
262         catch (IOException ignored) {
263             result = false;
264         }
265         return result;
266     }
267 
268     private static SuppressionFilter createSuppressionFilter(String fileName, boolean optional)
269             throws CheckstyleException {
270         final SuppressionFilter suppressionFilter = new SuppressionFilter();
271         suppressionFilter.setFile(fileName);
272         suppressionFilter.setOptional(optional);
273         suppressionFilter.finishLocalSetup();
274         return suppressionFilter;
275     }
276 }