View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 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.api;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  
25  import java.io.File;
26  import java.nio.charset.StandardCharsets;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.List;
30  import java.util.SortedSet;
31  import java.util.TreeSet;
32  
33  import org.junit.jupiter.api.Test;
34  
35  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
36  import com.puppycrawl.tools.checkstyle.Checker;
37  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
38  
39  public class AbstractFileSetCheckTest extends AbstractModuleTestSupport {
40  
41      @Override
42      public String getPackageLocation() {
43          return "com/puppycrawl/tools/checkstyle/api/abstractfileset";
44      }
45  
46      @Test
47      public void testTabWidth() {
48          final DummyFileSetCheck check = new DummyFileSetCheck();
49          check.setTabWidth(12345);
50          assertWithMessage("expected tab width")
51                  .that(check.getTabWidth())
52                  .isEqualTo(12345);
53      }
54  
55      @Test
56      public void testFileContents() {
57          final FileContents contents = new FileContents(
58                  new FileText(new File("inputAbstractFileSetCheck.tmp"), Collections.emptyList()));
59          final DummyFileSetCheck check = new DummyFileSetCheck();
60          check.setFileContents(contents);
61          assertWithMessage("expected file contents")
62                  .that(check.getFileContents())
63                  .isSameInstanceAs(contents);
64      }
65  
66      @Test
67      public void testProcessSequential() throws Exception {
68          final DummyFileSetCheck check = new DummyFileSetCheck();
69          check.configure(new DefaultConfiguration("filesetcheck"));
70          check.setFileExtensions("tmp");
71          final File firstFile = new File("inputAbstractFileSetCheck.tmp");
72          final SortedSet<Violation> firstFileMessages =
73              check.process(firstFile, new FileText(firstFile, Collections.emptyList()));
74  
75          assertWithMessage("Invalid message")
76                  .that(firstFileMessages.getFirst().getViolation())
77                  .isEqualTo("File should not be empty.");
78  
79          final SortedSet<Violation> internalMessages =
80                  check.getViolations();
81          assertWithMessage("Internal message should be empty, but was not")
82                  .that(internalMessages)
83                  .isEmpty();
84  
85          final File secondFile = new File("inputAbstractFileSetCheck.txt");
86          final List<String> lines = Arrays.asList("key=value", "ext=tmp");
87          final SortedSet<Violation> secondFileMessages =
88              check.process(secondFile, new FileText(secondFile, lines));
89  
90          assertWithMessage("Message should be empty, but was not")
91                  .that(secondFileMessages)
92                  .isEmpty();
93      }
94  
95      @Test
96      public void testNotProcessed() throws Exception {
97          final ExceptionFileSetCheck check = new ExceptionFileSetCheck();
98          check.setFileExtensions("java");
99          final File firstFile = new File("inputAbstractFileSetCheck.tmp");
100 
101         check.process(firstFile, new FileText(firstFile, Collections.emptyList()));
102 
103         final SortedSet<Violation> internalMessages =
104                 check.getViolations();
105         assertWithMessage("Internal message should be empty")
106                 .that(internalMessages)
107                 .isEmpty();
108     }
109 
110     @Test
111     public void testProcessException() throws Exception {
112         final ExceptionFileSetCheck check = new ExceptionFileSetCheck();
113         check.configure(new DefaultConfiguration("filesetcheck"));
114         check.setFileExtensions("tmp");
115         final File firstFile = new File("inputAbstractFileSetCheck.tmp");
116 
117         final FileText fileText = new FileText(firstFile, Collections.emptyList());
118         final IllegalArgumentException exc =
119                 getExpectedThrowable(IllegalArgumentException.class, () -> {
120                     check.process(firstFile, fileText);
121                 }, "Exception is expected");
122         assertWithMessage("Invalid exception message")
123                 .that(exc.getMessage())
124                 .isEqualTo("Test");
125 
126         final SortedSet<Violation> internalViolations =
127                 check.getViolations();
128         assertWithMessage("Internal violation should only have 1")
129                 .that(internalViolations)
130                 .hasSize(1);
131 
132         // again to prove only 1 violation exists
133         final File secondFile = new File("inputAbstractFileSetCheck.tmp");
134         final FileText fileText2 = new FileText(secondFile, Collections.emptyList());
135         final IllegalArgumentException exc2 =
136                 getExpectedThrowable(IllegalArgumentException.class, () -> {
137                     check.process(secondFile, fileText2);
138                 }, "Exception is expected");
139         assertWithMessage("Invalid exception message")
140                 .that(exc2.getMessage())
141                 .isEqualTo("Test");
142 
143         final SortedSet<Violation> internalViolations2 =
144             check.getViolations();
145         assertWithMessage("Internal violation should only have 1 again")
146                 .that(internalViolations2)
147                 .hasSize(1);
148     }
149 
150     @Test
151     public void testGetFileExtension() {
152         final DummyFileSetCheck check = new DummyFileSetCheck();
153         check.setFileExtensions("tmp", ".java");
154         final String[] expectedExtensions = {".tmp", ".java"};
155 
156         assertWithMessage("Invalid extensions")
157                 .that(check.getFileExtensions())
158                 .isEqualTo(expectedExtensions);
159     }
160 
161     /**
162      * This javadoc exists only to suppress IntelliJ IDEA inspection.
163      */
164     @Test
165     public void testSetExtensionThrowsExceptionWhenTheyAreNull() {
166         final DummyFileSetCheck check = new DummyFileSetCheck();
167         final IllegalArgumentException exception =
168                 getExpectedThrowable(IllegalArgumentException.class, () -> {
169                     check.setFileExtensions((String[]) null);
170                 }, "Expected exception.");
171         assertWithMessage("Invalid exception message")
172                 .that(exception.getMessage())
173                 .isEqualTo("Extensions array can not be null");
174     }
175 
176     @Test
177     public void testLineColumnLog() throws Exception {
178         final ViolationFileSetCheck check = new ViolationFileSetCheck();
179         check.configure(new DefaultConfiguration("filesetcheck"));
180         final File file = new File(getPath("InputAbstractFileSetLineColumn.java"));
181         final FileText theText = new FileText(file.getAbsoluteFile(),
182                 StandardCharsets.UTF_8.name());
183         final SortedSet<Violation> internalViolations = check.process(file, theText);
184 
185         assertWithMessage("Internal violation should only have 1")
186                 .that(internalViolations)
187                 .hasSize(1);
188 
189         final Violation violation = internalViolations.getFirst();
190         assertWithMessage("expected line")
191                 .that(violation.getLineNo())
192                 .isEqualTo(1);
193         assertWithMessage("expected column")
194                 .that(violation.getColumnNo())
195                 .isEqualTo(1);
196         assertWithMessage("expected severity")
197                 .that(violation.getSeverityLevel())
198                 .isEqualTo(SeverityLevel.ERROR);
199     }
200 
201     @Test
202     public void testGetMessageDispatcher() {
203         final DummyFileSetCheck check = new DummyFileSetCheck();
204         final Checker checker = new Checker();
205         check.setMessageDispatcher(checker);
206 
207         assertWithMessage("Invalid message dispatcher")
208                 .that(check.getMessageDispatcher())
209                 .isSameInstanceAs(checker);
210     }
211 
212     @Test
213     public void testCheck() throws Exception {
214         final String[] expected = {
215             "1:1: Violation.",
216         };
217         verifyWithInlineConfigParser(getPath("InputAbstractFileSetLineColumn.java"), expected);
218     }
219 
220     @Test
221     public void testMultiFileFireErrors() throws Exception {
222         final MultiFileViolationFileSetCheck check = new MultiFileViolationFileSetCheck();
223         check.configure(new DefaultConfiguration("filesetcheck"));
224         final ViolationDispatcher dispatcher = new ViolationDispatcher();
225         check.setMessageDispatcher(dispatcher);
226 
227         check.finishProcessing();
228 
229         assertWithMessage("Invalid fileName reported")
230                 .that(dispatcher.name)
231                 .isEqualTo("fileName");
232 
233         assertWithMessage("errors should only have 1")
234                 .that(dispatcher.errorList)
235                 .hasSize(1);
236 
237         final Violation violation = dispatcher.errorList.getFirst();
238         assertWithMessage("expected line")
239                 .that(violation.getLineNo())
240                 .isEqualTo(1);
241         assertWithMessage("expected column")
242                 .that(violation.getColumnNo())
243                 .isEqualTo(0);
244         assertWithMessage("expected severity")
245                 .that(violation.getSeverityLevel())
246                 .isEqualTo(SeverityLevel.ERROR);
247 
248         // re-running erases previous errors
249 
250         check.finishProcessing();
251 
252         assertWithMessage("errors should still have 1 after re-run")
253                 .that(dispatcher.errorList)
254                 .hasSize(1);
255         assertWithMessage("finishProcessing was called twice")
256                 .that(check.finishProcessingCount)
257                 .isEqualTo(2);
258     }
259 
260     /**
261      * S2384 - Mutable members should not be stored or returned directly.
262      * Inspection is valid, a pure unit test is required as this condition can't be recreated in
263      * a test with checks and input file as none of the checks try to modify the fileExtensions.
264      */
265     @Test
266     public void testCopiedArrayIsReturned() {
267         final DummyFileSetCheck check = new DummyFileSetCheck();
268         check.setFileExtensions(".tmp");
269         assertWithMessage("Extensions should be copied")
270             .that(check.getFileExtensions())
271             .isNotSameInstanceAs(check.getFileExtensions());
272     }
273 
274     public static class DummyFileSetCheck extends AbstractFileSetCheck {
275 
276         private static final String MSG_KEY = "File should not be empty.";
277 
278         @Override
279         protected void processFiltered(File file, FileText fileText) {
280             if (fileText.size() == 0) {
281                 log(1, MSG_KEY);
282             }
283         }
284 
285     }
286 
287     public static class ViolationFileSetCheck extends AbstractFileSetCheck {
288 
289         private static final String MSG_KEY = "Violation.";
290 
291         @Override
292         protected void processFiltered(File file, FileText fileText) {
293             log(1, 0, MSG_KEY);
294         }
295 
296     }
297 
298     public static class MultiFileViolationFileSetCheck extends AbstractFileSetCheck {
299 
300         private static final String MSG_KEY = "Violation.";
301         private int finishProcessingCount;
302 
303         @Override
304         protected void processFiltered(File file, FileText fileText) {
305             // no code needed
306         }
307 
308         @Override
309         public void finishProcessing() {
310             final String fileName = "fileName";
311 
312             log(1, MSG_KEY + finishProcessingCount);
313             fireErrors(fileName);
314 
315             finishProcessingCount++;
316         }
317 
318     }
319 
320     public static class ExceptionFileSetCheck extends AbstractFileSetCheck {
321 
322         private static final String MSG_KEY = "Test.";
323         private int count = 1;
324 
325         @Override
326         protected void processFiltered(File file, FileText fileText) {
327             log(count, MSG_KEY);
328             count++;
329             throw new IllegalArgumentException("Test");
330         }
331 
332     }
333 
334     public static class ViolationDispatcher implements MessageDispatcher {
335         private String name;
336         private SortedSet<Violation> errorList;
337 
338         @Override
339         public void fireFileStarted(String fileName) {
340             // no code needed
341         }
342 
343         @Override
344         public void fireFileFinished(String fileName) {
345             // no code needed
346         }
347 
348         @Override
349         public void fireErrors(String fileName, SortedSet<Violation> errors) {
350             name = fileName;
351             errorList = new TreeSet<>(errors);
352         }
353 
354     }
355 
356 }