1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.checks.imports;
21  
22  import static com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck.MSG_DISALLOWED;
23  import static com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck.MSG_MISSING_FILE;
24  import static com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck.MSG_UNKNOWN_PKG;
25  import static org.junit.Assert.assertArrayEquals;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.io.File;
30  import java.nio.charset.StandardCharsets;
31  import java.nio.file.Files;
32  
33  import org.junit.Rule;
34  import org.junit.Test;
35  import org.junit.rules.TemporaryFolder;
36  
37  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
38  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
39  import com.puppycrawl.tools.checkstyle.TreeWalker;
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
42  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
43  
44  public class ImportControlCheckTest extends AbstractModuleTestSupport {
45  
46      @Rule
47      public final TemporaryFolder temporaryFolder = new TemporaryFolder();
48  
49      @Override
50      protected String getPackageLocation() {
51          return "com/puppycrawl/tools/checkstyle/checks/imports/importcontrol";
52      }
53  
54      @Test
55      public void testGetRequiredTokens() {
56          final ImportControlCheck checkObj = new ImportControlCheck();
57          final int[] expected = {
58              TokenTypes.PACKAGE_DEF,
59              TokenTypes.IMPORT,
60              TokenTypes.STATIC_IMPORT,
61          };
62          assertArrayEquals("Default required tokens are invalid",
63              expected, checkObj.getRequiredTokens());
64      }
65  
66      @Test
67      public void testOne() throws Exception {
68          final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
69          checkConfig.addAttribute("file", getPath("InputImportControlOne.xml"));
70          final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
71  
72          verify(checkConfig, getPath("InputImportControl.java"), expected);
73      }
74  
75      @Test
76      public void testTwo() throws Exception {
77          final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
78          checkConfig.addAttribute("file", getPath("InputImportControlTwo.xml"));
79          final String[] expected = {
80              "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
81              "4:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
82              "6:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
83          };
84  
85          verify(checkConfig, getPath("InputImportControl.java"), expected);
86      }
87  
88      @Test
89      public void testWrong() throws Exception {
90          final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
91          checkConfig.addAttribute("file", getPath("InputImportControlWrong.xml"));
92          final String[] expected = {"1:1: " + getCheckMessage(MSG_UNKNOWN_PKG)};
93          verify(checkConfig, getPath("InputImportControl.java"), expected);
94      }
95  
96      @Test
97      public void testMissing() throws Exception {
98          final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
99          final String[] expected = {"1:1: " + getCheckMessage(MSG_MISSING_FILE)};
100         verify(checkConfig, getPath("InputImportControl.java"), expected);
101     }
102 
103     @Test
104     public void testEmpty() throws Exception {
105         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
106         checkConfig.addAttribute("file", "   ");
107         final String[] expected = {"1:1: " + getCheckMessage(MSG_MISSING_FILE)};
108         verify(checkConfig, getPath("InputImportControl.java"), expected);
109     }
110 
111     @Test
112     public void testNull() throws Exception {
113         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
114         checkConfig.addAttribute("file", null);
115         final String[] expected = {"1:1: " + getCheckMessage(MSG_MISSING_FILE)};
116         verify(checkConfig, getPath("InputImportControl.java"), expected);
117     }
118 
119     @Test
120     public void testUnknown() throws Exception {
121         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
122         checkConfig.addAttribute("file", "unknown-file");
123         try {
124             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
125             verify(checkConfig, getPath("InputImportControl.java"), expected);
126             fail("Test should fail if exception was not thrown");
127         }
128         catch (CheckstyleException ex) {
129             final String message = getCheckstyleExceptionMessage(ex);
130             final String messageStart = "Unable to find: ";
131 
132             assertTrue("Invalid message, should start with: " + messageStart,
133                 message.startsWith(message));
134         }
135     }
136 
137     @Test
138     public void testBroken() throws Exception {
139         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
140         checkConfig.addAttribute("file", getPath("InputImportControlBroken.xml"));
141         try {
142             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
143             verify(checkConfig, getPath("InputImportControl.java"), expected);
144             fail("Test should fail if exception was not thrown");
145         }
146         catch (CheckstyleException ex) {
147             final String message = getCheckstyleExceptionMessage(ex);
148             final String messageStart = "Unable to load ";
149 
150             assertTrue("Invalid message, should start with: " + messageStart,
151                 message.startsWith(message));
152         }
153     }
154 
155     @Test
156     public void testOneRegExp() throws Exception {
157         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
158         checkConfig.addAttribute("file", getPath("InputImportControlOneRegExp.xml"));
159         final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
160 
161         verify(checkConfig, getPath("InputImportControl.java"), expected);
162     }
163 
164     @Test
165     public void testTwoRegExp() throws Exception {
166         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
167         checkConfig.addAttribute("file", getPath("InputImportControlTwoRegExp.xml"));
168         final String[] expected = {
169             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
170             "4:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
171             "6:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
172         };
173 
174         verify(checkConfig, getPath("InputImportControl.java"), expected);
175     }
176 
177     @Test
178     public void testNotRegExpNoMatch() throws Exception {
179         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
180         checkConfig.addAttribute("file", getPath("InputImportControlNotRegExpNoMatch.xml"));
181 
182         verify(checkConfig, getPath("InputImportControl.java"), CommonUtil.EMPTY_STRING_ARRAY);
183     }
184 
185     @Test
186     public void testBlacklist() throws Exception {
187         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
188         checkConfig.addAttribute("file", getPath("InputImportControlBlacklist.xml"));
189         final String[] expected = {
190             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.Stream"),
191             "4:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.Date"),
192             "6:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.Collectors"),
193             "7:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.IntStream"),
194         };
195 
196         verify(checkConfig, getPath("InputImportControl_Blacklist.java"), expected);
197     }
198 
199     @Test
200     public void testStrategyOnMismatchOne() throws Exception {
201         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
202         checkConfig.addAttribute("file", getPath("InputImportControlStrategyOnMismatchOne.xml"));
203         final String[] expected = {
204             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
205             "4:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
206             "6:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
207         };
208 
209         verify(checkConfig, getPath("InputImportControl.java"), expected);
210     }
211 
212     @Test
213     public void testStrategyOnMismatchTwo() throws Exception {
214         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
215         checkConfig.addAttribute("file", getPath("InputImportControlStrategyOnMismatchTwo.xml"));
216         final String[] expected = {
217             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
218             "6:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
219         };
220 
221         verify(checkConfig, getPath("InputImportControl.java"), expected);
222     }
223 
224     @Test
225     public void testStrategyOnMismatchThree() throws Exception {
226         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
227         checkConfig.addAttribute("file", getPath("InputImportControlStrategyOnMismatchThree.xml"));
228         final String[] expected = {
229             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
230         };
231 
232         verify(checkConfig, getPath("InputImportControl.java"), expected);
233     }
234 
235     @Test
236     public void testStrategyOnMismatchFour() throws Exception {
237         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
238         checkConfig.addAttribute("file", getPath("InputImportControlStrategyOnMismatchFour.xml"));
239         final String[] expected = {
240             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
241             "4:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
242         };
243 
244         verify(checkConfig, getPath("InputImportControl.java"), expected);
245     }
246 
247     @Test
248     public void testPkgRegExpInParent() throws Exception {
249         testRegExpInPackage("InputImportControlPkgRegExpInParent.xml");
250     }
251 
252     @Test
253     public void testPkgRegExpInChild() throws Exception {
254         testRegExpInPackage("InputImportControlPkgRegExpInChild.xml");
255     }
256 
257     @Test
258     public void testPkgRegExpInBoth() throws Exception {
259         testRegExpInPackage("InputImportControlPkgRegExpInBoth.xml");
260     }
261 
262     // all import-control_pkg-re* files should be equivalent so use one test for all
263     private void testRegExpInPackage(String file) throws Exception {
264         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
265         checkConfig.addAttribute("file", getPath(file));
266         final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
267 
268         verify(checkConfig, getPath("InputImportControl.java"), expected);
269     }
270 
271     @Test
272     public void testGetAcceptableTokens() {
273         final ImportControlCheck testCheckObject =
274                 new ImportControlCheck();
275         final int[] actual = testCheckObject.getAcceptableTokens();
276         final int[] expected = {
277             TokenTypes.PACKAGE_DEF,
278             TokenTypes.IMPORT,
279             TokenTypes.STATIC_IMPORT,
280         };
281 
282         assertArrayEquals("Default acceptable tokens are invalid", expected, actual);
283     }
284 
285     @Test
286     public void testResource() throws Exception {
287         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
288         checkConfig.addAttribute("file", getResourcePath("InputImportControlOne.xml"));
289         final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
290 
291         verify(checkConfig, getPath("InputImportControl.java"), expected);
292     }
293 
294     @Test
295     public void testResourceUnableToLoad() throws Exception {
296         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
297         checkConfig.addAttribute("file", getResourcePath("import-control_unknown.xml"));
298 
299         try {
300             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
301             verify(checkConfig, getPath("InputImportControl.java"), expected);
302             fail("Test should fail if exception was not thrown");
303         }
304         catch (CheckstyleException ex) {
305             final String message = getCheckstyleExceptionMessage(ex);
306             final String messageStart = "Unable to find: ";
307 
308             assertTrue("Invalid message, should start with: " + messageStart,
309                 message.startsWith(message));
310         }
311     }
312 
313     @Test
314     public void testUrlInFileProperty() throws Exception {
315         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
316         checkConfig.addAttribute("file", getUriString("InputImportControlOne.xml"));
317         final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
318 
319         verify(checkConfig, getPath("InputImportControl.java"), expected);
320     }
321 
322     @Test
323     public void testUrlInFilePropertyUnableToLoad() throws Exception {
324         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
325         checkConfig.addAttribute("file", "https://UnableToLoadThisURL");
326 
327         try {
328             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
329             verify(checkConfig, getPath("InputImportControl.java"), expected);
330             fail("Test should fail if exception was not thrown");
331         }
332         catch (CheckstyleException ex) {
333             final String message = getCheckstyleExceptionMessage(ex);
334             final String messageStart = "Unable to load ";
335 
336             assertTrue("Invalid message, should start with: " + messageStart,
337                 message.startsWith(message));
338         }
339     }
340 
341     @Test
342     public void testCacheWhenFileExternalResourceContentDoesNotChange() throws Exception {
343         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
344         checkConfig.addAttribute("file", getPath("InputImportControlOneRegExp.xml"));
345 
346         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
347         treeWalkerConfig.addChild(checkConfig);
348 
349         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
350         final File cacheFile = temporaryFolder.newFile();
351         checkerConfig.addAttribute("cacheFile", cacheFile.getPath());
352 
353         final String filePath = temporaryFolder.newFile("EmptyFile.java").getPath();
354         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
355 
356         verify(checkerConfig, filePath, expected);
357         // One more time to use cache.
358         verify(checkerConfig, filePath, expected);
359 
360         assertTrue("External resource is not present in cache",
361                 new String(Files.readAllBytes(cacheFile.toPath()),
362                         StandardCharsets.UTF_8).contains("InputImportControlOneRegExp.xml"));
363     }
364 
365     @Test
366     public void testPathRegexMatches() throws Exception {
367         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
368         checkConfig.addAttribute("file", getResourcePath("InputImportControlOne.xml"));
369         checkConfig.addAttribute("path", "^.*[\\\\/]src[\\\\/]test[\\\\/].*$");
370         final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
371 
372         verify(checkConfig, getPath("InputImportControl.java"), expected);
373     }
374 
375     @Test
376     public void testPathRegexMatchesPartially() throws Exception {
377         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
378         checkConfig.addAttribute("file", getResourcePath("InputImportControlOne.xml"));
379         checkConfig.addAttribute("path", "[\\\\/]InputImportControl\\.java");
380         final String[] expected = {"5:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
381 
382         verify(checkConfig, getPath("InputImportControl.java"), expected);
383     }
384 
385     @Test
386     public void testPathRegexDoesntMatch() throws Exception {
387         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
388         checkConfig.addAttribute("file", getResourcePath("InputImportControlOne.xml"));
389         checkConfig.addAttribute("path", "^.*[\\\\/]src[\\\\/]main[\\\\/].*$");
390         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
391 
392         verify(checkConfig, getPath("InputImportControl.java"), expected);
393     }
394 
395     @Test
396     public void testPathRegexDoesntMatchPartially() throws Exception {
397         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
398         checkConfig.addAttribute("file", getResourcePath("InputImportControlOne.xml"));
399         checkConfig.addAttribute("path", "[\\\\/]NoMatch\\.java");
400         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
401 
402         verify(checkConfig, getPath("InputImportControl.java"), expected);
403     }
404 
405     @Test
406     public void testDisallowClassOfAllowPackage() throws Exception {
407         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
408         checkConfig.addAttribute("file",
409                 getPath("InputImportControlDisallowClassOfAllowPackage.xml"));
410         final String[] expected = {
411             "4:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.Date"),
412         };
413 
414         verify(checkConfig, getPath("InputImportControlDisallowClassOfAllowPackage.java"),
415                 expected);
416     }
417 
418     @Test
419     public void testFileName() throws Exception {
420         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
421         checkConfig.addAttribute("file", getResourcePath("InputImportControlFileName.xml"));
422         final String[] expected = {
423             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
424         };
425 
426         verify(checkConfig, getPath("InputImportControlFileName.java"), expected);
427     }
428 
429     @Test
430     public void testFileNameNoExtension() throws Exception {
431         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
432         checkConfig.addAttribute("file",
433                 getResourcePath("InputImportControlFileNameNoExtension.xml"));
434         final DefaultConfiguration treewalkerConfig = createModuleConfig(TreeWalker.class);
435         treewalkerConfig.addAttribute("fileExtensions", "");
436         treewalkerConfig.addChild(checkConfig);
437         final String[] expected = {
438             "3:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
439         };
440 
441         verify(treewalkerConfig, getPath("InputImportControlFileNameNoExtension"), expected);
442     }
443 
444     /**
445      * Returns String message of original exception that was thrown in
446      * ImportControlCheck.setUrl or ImportControlCheck.setFile
447      * and caught in test (it was caught and re-thrown twice after that)
448      * Note: this is helper method with hard-coded structure of exception causes. It works
449      * fine for methods mentioned, you may need to adjust it if you try to use it for other needs
450      * @param exception Exception
451      * @return String message of original exception
452      */
453     private static String getCheckstyleExceptionMessage(CheckstyleException exception) {
454         return exception.getCause().getCause().getCause().getCause().getMessage();
455     }
456 
457 }