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.checks.imports;
21  
22  import static com.google.common.truth.Truth.assertThat;
23  import static com.google.common.truth.Truth.assertWithMessage;
24  import static com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck.MSG_DISALLOWED;
25  import static com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck.MSG_MISSING_FILE;
26  import static com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck.MSG_UNKNOWN_PKG;
27  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
28  
29  import java.io.File;
30  import java.nio.file.Files;
31  import java.util.Arrays;
32  import java.util.List;
33  import java.util.UUID;
34  
35  import org.junit.jupiter.api.Test;
36  import org.junit.jupiter.api.io.TempDir;
37  
38  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
39  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
40  import com.puppycrawl.tools.checkstyle.TreeWalker;
41  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
42  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
43  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
44  
45  public class ImportControlCheckTest extends AbstractModuleTestSupport {
46  
47      @TempDir
48      public File temporaryFolder;
49  
50      @Override
51      protected String getPackageLocation() {
52          return "com/puppycrawl/tools/checkstyle/checks/imports/importcontrol";
53      }
54  
55      @Test
56      public void testGetRequiredTokens() {
57          final ImportControlCheck checkObj = new ImportControlCheck();
58          final int[] expected = {
59              TokenTypes.PACKAGE_DEF,
60              TokenTypes.IMPORT,
61              TokenTypes.STATIC_IMPORT,
62          };
63          assertWithMessage("Default required tokens are invalid")
64              .that(checkObj.getRequiredTokens())
65              .isEqualTo(expected);
66      }
67  
68      @Test
69      public void testOne() throws Exception {
70          final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
71  
72          verifyWithInlineConfigParser(
73                  getPath("InputImportControl.java"), expected);
74      }
75  
76      @Test
77      public void testTwo() throws Exception {
78          final String[] expected = {
79              "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
80              "12:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
81              "14:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
82          };
83  
84          verifyWithInlineConfigParser(
85                  getPath("InputImportControl2.java"), expected);
86      }
87  
88      @Test
89      public void testWrong() throws Exception {
90          final String[] expected = {"9:1: " + getCheckMessage(MSG_UNKNOWN_PKG)};
91          verifyWithInlineConfigParser(
92                  getPath("InputImportControl3.java"), expected);
93      }
94  
95      @Test
96      public void testMissing() throws Exception {
97          final String[] expected = {"9:1: " + getCheckMessage(MSG_MISSING_FILE)};
98          verifyWithInlineConfigParser(
99                  getPath("InputImportControl4.java"), expected);
100     }
101 
102     @Test
103     public void testEmpty() throws Exception {
104         final String[] expected = {"9:1: " + getCheckMessage(MSG_MISSING_FILE)};
105         verifyWithInlineConfigParser(
106                 getPath("InputImportControl5.java"), expected);
107     }
108 
109     @Test
110     public void testNull() throws Exception {
111         final String[] expected = {"9:1: " + getCheckMessage(MSG_MISSING_FILE)};
112         verifyWithInlineConfigParser(
113                 getPath("InputImportControl6.java"), expected);
114     }
115 
116     @Test
117     public void testUnknown() throws Exception {
118         try {
119             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
120             verifyWithInlineConfigParser(
121                     getPath("InputImportControl7.java"), expected);
122             assertWithMessage("Test should fail if exception was not thrown").fail();
123         }
124         catch (CheckstyleException ex) {
125             final String message = getCheckstyleExceptionMessage(ex);
126             final String messageStart = "Unable to find: ";
127 
128             assertWithMessage("Invalid message, should start with: %s", messageStart)
129                     .that(message)
130                     .startsWith(messageStart);
131         }
132     }
133 
134     @Test
135     public void testBroken() throws Exception {
136         try {
137             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
138             verifyWithInlineConfigParser(getPath("InputImportControl8.java"), expected);
139             assertWithMessage("Test should fail if exception was not thrown").fail();
140         }
141         catch (CheckstyleException ex) {
142             final String message = getCheckstyleExceptionMessage(ex);
143             final String messageStart = "Unable to load ";
144 
145             assertWithMessage("Invalid message, should start with: %s", messageStart)
146                     .that(message)
147                     .startsWith(messageStart);
148         }
149     }
150 
151     @Test
152     public void testOneRegExp() throws Exception {
153         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
154 
155         verifyWithInlineConfigParser(
156                 getPath("InputImportControl9.java"), expected);
157     }
158 
159     @Test
160     public void testTwoRegExp() throws Exception {
161         final String[] expected = {
162             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
163             "12:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
164             "14:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
165         };
166 
167         verifyWithInlineConfigParser(
168                 getPath("InputImportControl10.java"), expected);
169     }
170 
171     @Test
172     public void testNotRegExpNoMatch() throws Exception {
173 
174         verifyWithInlineConfigParser(
175                 getPath("InputImportControl11.java"), CommonUtil.EMPTY_STRING_ARRAY);
176     }
177 
178     @Test
179     public void testBlacklist() throws Exception {
180         final String[] expected = {
181             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.Stream"),
182             "12:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.Date"),
183             "14:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.Collectors"),
184             "15:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.IntStream"),
185         };
186 
187         verifyWithInlineConfigParser(
188                 getPath("InputImportControl_Blacklist.java"), expected);
189     }
190 
191     @Test
192     public void testStrategyOnMismatchOne() throws Exception {
193         final String[] expected = {
194             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
195             "12:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
196             "14:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
197         };
198 
199         verifyWithInlineConfigParser(
200                 getPath("InputImportControl12.java"), expected);
201     }
202 
203     @Test
204     public void testStrategyOnMismatchTwo() throws Exception {
205         final String[] expected = {
206             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
207             "14:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Button.ABORT"),
208         };
209 
210         verifyWithInlineConfigParser(
211                 getPath("InputImportControl13.java"), expected);
212     }
213 
214     @Test
215     public void testStrategyOnMismatchThree() throws Exception {
216         final String[] expected = {
217             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
218         };
219 
220         verifyWithInlineConfigParser(
221                 getPath("InputImportControl14.java"), expected);
222     }
223 
224     @Test
225     public void testStrategyOnMismatchFour() throws Exception {
226         final String[] expected = {
227             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
228             "12:1: " + getCheckMessage(MSG_DISALLOWED, "javax.swing.border.*"),
229         };
230 
231         verifyWithInlineConfigParser(
232                 getPath("InputImportControl15.java"), expected);
233     }
234 
235     @Test
236     public void testWithoutRegexAndWithStrategyOnMismatch() throws Exception {
237         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
238 
239         verifyWithInlineConfigParser(
240             getPath("InputImportControlWithoutRegexAndWithStrategyOnMismatch.java"),
241             expected);
242     }
243 
244     @Test
245     public void testPkgRegExpInParent() throws Exception {
246         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
247 
248         verifyWithInlineConfigParser(
249                 getPath("InputImportControl16.java"), expected);
250     }
251 
252     @Test
253     public void testPkgRegExpInChild() throws Exception {
254         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
255 
256         verifyWithInlineConfigParser(
257                 getPath("InputImportControl162.java"), expected);
258     }
259 
260     @Test
261     public void testPkgRegExpInBoth() throws Exception {
262         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
263 
264         verifyWithInlineConfigParser(
265                 getPath("InputImportControl163.java"), expected);
266     }
267 
268     @Test
269     public void testGetAcceptableTokens() {
270         final ImportControlCheck testCheckObject =
271                 new ImportControlCheck();
272         final int[] actual = testCheckObject.getAcceptableTokens();
273         final int[] expected = {
274             TokenTypes.PACKAGE_DEF,
275             TokenTypes.IMPORT,
276             TokenTypes.STATIC_IMPORT,
277         };
278 
279         assertWithMessage("Default acceptable tokens are invalid")
280             .that(actual)
281             .isEqualTo(expected);
282     }
283 
284     @Test
285     public void testResource() throws Exception {
286         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
287 
288         verifyWithInlineConfigParser(
289                 getPath("InputImportControl17.java"), expected);
290     }
291 
292     @Test
293     public void testResourceUnableToLoad() throws Exception {
294         try {
295             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
296             verifyWithInlineConfigParser(getPath("InputImportControl18.java"), expected);
297             assertWithMessage("Test should fail if exception was not thrown").fail();
298         }
299         catch (CheckstyleException ex) {
300             final String message = getCheckstyleExceptionMessage(ex);
301             final String messageStart = "Unable to find: ";
302 
303             assertWithMessage("Invalid message, should start with: %s", messageStart)
304                     .that(message)
305                     .startsWith(messageStart);
306         }
307     }
308 
309     @Test
310     public void testUrlInFileProperty() throws Exception {
311         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
312 
313         verifyWithInlineConfigParser(
314                 getPath("InputImportControl19.java"), expected);
315     }
316 
317     @Test
318     public void testUrlInFilePropertyUnableToLoad() throws Exception {
319 
320         try {
321             final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
322             verifyWithInlineConfigParser(
323                     getPath("InputImportControl20.java"), expected);
324             assertWithMessage("Test should fail if exception was not thrown").fail();
325         }
326         catch (CheckstyleException ex) {
327             final String message = getCheckstyleExceptionMessage(ex);
328             final String messageStart = "Unable to load ";
329 
330             assertWithMessage("Invalid message, should start with: %s", messageStart)
331                     .that(message)
332                     .startsWith(messageStart);
333         }
334     }
335 
336     @Test
337     public void testCacheWhenFileExternalResourceContentDoesNotChange() throws Exception {
338         final DefaultConfiguration checkConfig = createModuleConfig(ImportControlCheck.class);
339         checkConfig.addProperty("file", getPath("InputImportControlOneRegExp.xml"));
340 
341         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
342         treeWalkerConfig.addChild(checkConfig);
343 
344         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
345         final String uniqueFileName1 = "junit_" + UUID.randomUUID() + ".java";
346         final File cacheFile = new File(temporaryFolder, uniqueFileName1);
347         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
348 
349         final String uniqueFileName2 = "empty_" + UUID.randomUUID() + ".java";
350         final File filePath = new File(temporaryFolder, uniqueFileName2);
351 
352         execute(checkerConfig, filePath.toString());
353         // One more time to use cache.
354         execute(checkerConfig, filePath.toString());
355 
356         final String contents = Files.readString(cacheFile.toPath());
357         assertWithMessage("External resource is not present in cache")
358                 .that(contents)
359                 .contains("InputImportControlOneRegExp.xml");
360     }
361 
362     @Test
363     public void testPathRegexMatches() throws Exception {
364         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
365 
366         verifyWithInlineConfigParser(
367                 getPath("InputImportControl21.java"), expected);
368     }
369 
370     @Test
371     public void testPathRegexMatchesPartially() throws Exception {
372         final String[] expected = {"13:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File")};
373 
374         verifyWithInlineConfigParser(
375                 getPath("InputImportControl22.java"), expected);
376     }
377 
378     @Test
379     public void testPathRegexDoesntMatch() throws Exception {
380         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
381 
382         verifyWithInlineConfigParser(
383                 getPath("InputImportControl23.java"), expected);
384     }
385 
386     @Test
387     public void testPathRegexDoesntMatchPartially() throws Exception {
388         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
389 
390         verifyWithInlineConfigParser(
391                 getPath("InputImportControl24.java"), expected);
392     }
393 
394     @Test
395     public void testDisallowClassOfAllowPackage() throws Exception {
396         final String[] expected = {
397             "12:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.Date"),
398         };
399 
400         verifyWithInlineConfigParser(
401                 getPath("InputImportControlDisallowClassOfAllowPackage.java"),
402                 expected);
403     }
404 
405     @Test
406     public void testFileName() throws Exception {
407         final String[] expected = {
408             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
409         };
410 
411         verifyWithInlineConfigParser(
412                 getPath("InputImportControlFileName.java"), expected);
413     }
414 
415     @Test
416     public void testWithRegex() throws Exception {
417         final String[] expected = {
418             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.io.File"),
419         };
420 
421         verifyWithInlineConfigParser(
422             getPath("InputImportControlWithRegex.java"), expected);
423     }
424 
425     @Test
426     public void testFileNameNoExtension() throws Exception {
427         final String[] expected = {
428             "13:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
429         };
430 
431         verifyWithInlineConfigParser(
432                 getPath("InputImportControlFileNameNoExtension"), expected);
433     }
434 
435     @Test
436     public void testBeginTreeCurrentImportControl() throws Exception {
437         final String file1 = getPath("InputImportControlBeginTree1.java");
438         final String file2 = getPath("InputImportControlBeginTree2.java");
439         final List<String> expectedFirstInput = Arrays.asList(
440             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.Stream"),
441             "12:1: " + getCheckMessage(MSG_DISALLOWED, "java.util.stream.Collectors")
442         );
443         final List<String> expectedSecondInput = Arrays.asList(CommonUtil.EMPTY_STRING_ARRAY);
444         verifyWithInlineConfigParser(file1, file2, expectedFirstInput, expectedSecondInput);
445     }
446 
447     @Test
448     public void testImportControlFileName() throws Exception {
449         final String[] expected = {
450             "11:1: " + getCheckMessage(MSG_DISALLOWED, "java.awt.Image"),
451         };
452 
453         verifyWithInlineConfigParser(
454                 getPath("InputImportControlTestRegexpInFile.java"), expected);
455     }
456 
457     @Test
458     public void testImportControlFileName2() throws Exception {
459         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
460 
461         verifyWithInlineConfigParser(
462                 getPath("InputImportControlTestRegexpInFile2.java"), expected);
463     }
464 
465     @Test
466     public void testImportControlTestException() {
467         final CheckstyleException ex = getExpectedThrowable(CheckstyleException.class,
468                 () -> {
469                     verifyWithInlineConfigParser(getPath("InputImportControlTestException.java"));
470                 });
471 
472         assertThat(ex.getCause().getCause().getCause().getCause().getCause().getMessage())
473                 .startsWith("unable to parse file:");
474         assertThat(ex.getCause().getCause().getCause().getCause().getCause().getMessage())
475                 .endsWith("- Document root element \"import-control\", must match DOCTYPE"
476                  + " root \"null\".");
477     }
478 
479     /**
480      * Returns String message of original exception that was thrown in
481      * ImportControlCheck.setUrl or ImportControlCheck.setFile
482      * and caught in test (it was caught and re-thrown twice after that)
483      * Note: this is helper method with hard-coded structure of exception causes. It works
484      * fine for methods mentioned, you may need to adjust it if you try to use it for other needs
485      *
486      * @param exception Exception
487      * @return String message of original exception
488      */
489     private static String getCheckstyleExceptionMessage(CheckstyleException exception) {
490         return exception.getCause().getCause().getCause().getCause().getMessage();
491     }
492 
493 }