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.ant;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.net.URL;
29  import java.nio.file.Files;
30  import java.util.Arrays;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  import java.util.Optional;
35  import java.util.ResourceBundle;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  
39  import org.apache.tools.ant.BuildException;
40  import org.apache.tools.ant.Location;
41  import org.apache.tools.ant.Project;
42  import org.apache.tools.ant.types.FileSet;
43  import org.apache.tools.ant.types.Path;
44  import org.apache.tools.ant.types.resources.FileResource;
45  import org.junit.jupiter.api.Test;
46  import org.junit.jupiter.api.io.TempDir;
47  
48  import com.google.common.base.Splitter;
49  import com.google.common.collect.Iterables;
50  import com.google.common.truth.StandardSubjectBuilder;
51  import com.puppycrawl.tools.checkstyle.AbstractPathTestSupport;
52  import com.puppycrawl.tools.checkstyle.DefaultLogger;
53  import com.puppycrawl.tools.checkstyle.Definitions;
54  import com.puppycrawl.tools.checkstyle.SarifLogger;
55  import com.puppycrawl.tools.checkstyle.XMLLogger;
56  import com.puppycrawl.tools.checkstyle.internal.testmodules.CheckstyleAntTaskLogStub;
57  import com.puppycrawl.tools.checkstyle.internal.testmodules.CheckstyleAntTaskStub;
58  import com.puppycrawl.tools.checkstyle.internal.testmodules.MessageLevelPair;
59  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestRootModuleChecker;
60  
61  public class CheckstyleAntTaskTest extends AbstractPathTestSupport {
62  
63      private static final String FLAWLESS_INPUT =
64              "InputCheckstyleAntTaskFlawless.java";
65      private static final String VIOLATED_INPUT =
66              "InputCheckstyleAntTaskError.java";
67      private static final String WARNING_INPUT =
68              "InputCheckstyleAntTaskWarning.java";
69      private static final String CONFIG_FILE =
70              "InputCheckstyleAntTaskTestChecks.xml";
71      private static final String CUSTOM_ROOT_CONFIG_FILE =
72              "InputCheckstyleAntTaskConfigCustomRootModule.xml";
73      private static final String IGNORED_CHILD_CONFIG_FILE =
74              "InputCheckstyleAntTaskConfigIgnoredChild.xml";
75      private static final String NOT_EXISTING_FILE = "target/not_existing.xml";
76      private static final String FAILURE_PROPERTY_VALUE = "myValue";
77  
78      @TempDir
79      public File temporaryFolder;
80  
81      @Override
82      public String getPackageLocation() {
83          return "com/puppycrawl/tools/checkstyle/ant/checkstyleanttask/";
84      }
85  
86      private CheckstyleAntTask getCheckstyleAntTask() throws IOException {
87          return getCheckstyleAntTask(CONFIG_FILE);
88      }
89  
90      private CheckstyleAntTask getCheckstyleAntTask(String configFile) throws IOException {
91          final CheckstyleAntTask antTask = new CheckstyleAntTask();
92          antTask.setConfig(getPath(configFile));
93          antTask.setProject(new Project());
94          return antTask;
95      }
96  
97      @Test
98      public final void testDefaultFlawless() throws IOException {
99          TestRootModuleChecker.reset();
100         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
101         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
102         antTask.execute();
103 
104         assertWithMessage("Checker is not processed")
105                 .that(TestRootModuleChecker.isProcessed())
106                 .isTrue();
107     }
108 
109     @Test
110     public final void testPathsOneFile() throws IOException {
111         // given
112         TestRootModuleChecker.reset();
113 
114         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
115         final FileSet examinationFileSet = new FileSet();
116         examinationFileSet.setFile(new File(getPath(FLAWLESS_INPUT)));
117         final Path sourcePath = new Path(antTask.getProject());
118         sourcePath.addFileset(examinationFileSet);
119         antTask.addPath(sourcePath);
120 
121         // when
122         antTask.execute();
123 
124         // then
125         assertWithMessage("Checker is not processed")
126                 .that(TestRootModuleChecker.isProcessed())
127                 .isTrue();
128         final List<File> filesToCheck = TestRootModuleChecker.getFilesToCheck();
129         assertWithMessage("There are more files to check than expected")
130                 .that(filesToCheck)
131                 .hasSize(1);
132         assertWithMessage("The path of file differs from expected")
133                 .that(filesToCheck.getFirst().getAbsolutePath())
134                 .isEqualTo(getPath(FLAWLESS_INPUT));
135     }
136 
137     @Test
138     public final void testPathsFileWithLogVerification() throws IOException {
139         // given
140         TestRootModuleChecker.reset();
141         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
142         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
143         antTask.setProject(new Project());
144         final FileSet examinationFileSet = new FileSet();
145         examinationFileSet.setFile(new File(getPath(FLAWLESS_INPUT)));
146         final Path sourcePath = new Path(antTask.getProject());
147         sourcePath.addFileset(examinationFileSet);
148         antTask.addPath(sourcePath);
149         antTask.addPath(new Path(new Project()));
150 
151         // when
152         antTask.execute();
153 
154         // then
155         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
156 
157         assertWithMessage("Scanning path was not logged")
158                 .that(loggedMessages.stream().filter(
159                         msg -> msg.getMsg().startsWith("1) Scanning path")).count())
160                 .isEqualTo(1);
161 
162         assertWithMessage("Scanning path was not logged")
163                 .that(loggedMessages.stream().filter(
164                         msg -> msg.getMsg().startsWith("1) Adding 1 files from path")).count())
165                 .isEqualTo(1);
166 
167         assertWithMessage("Scanning empty was logged")
168                 .that(loggedMessages.stream().filter(
169                         msg -> msg.getMsg().startsWith("2) Adding 0 files from path ")).count())
170                 .isEqualTo(0);
171 
172         assertWithMessage("Checker is not processed")
173                 .that(TestRootModuleChecker.isProcessed())
174                 .isTrue();
175         final List<File> filesToCheck = TestRootModuleChecker.getFilesToCheck();
176         assertWithMessage("There are more files to check than expected")
177                 .that(filesToCheck)
178                 .hasSize(1);
179         assertWithMessage("The path of file differs from expected")
180                 .that(filesToCheck.getFirst().getAbsolutePath())
181                 .isEqualTo(getPath(FLAWLESS_INPUT));
182     }
183 
184     @Test
185     public final void testBaseDirPresence() throws IOException {
186         TestRootModuleChecker.reset();
187 
188         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
189         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
190 
191         final Project project = new Project();
192         project.setBaseDir(new File("."));
193         antTask.setProject(new Project());
194 
195         final FileSet fileSet = new FileSet();
196         fileSet.setFile(new File(getPath(FLAWLESS_INPUT)));
197         antTask.addFileset(fileSet);
198 
199         antTask.scanFileSets();
200 
201         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
202 
203         final String expectedPath = new File(getPath(".")).getAbsolutePath();
204         final boolean containsBaseDir = loggedMessages.stream()
205                 .anyMatch(msg -> msg.getMsg().contains(expectedPath));
206 
207         assertWithMessage("Base directory should be present in logs.")
208                 .that(containsBaseDir)
209                 .isTrue();
210     }
211 
212     @Test
213     public final void testPathsDirectoryWithNestedFile() throws IOException {
214         // given
215         TestRootModuleChecker.reset();
216 
217         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
218         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
219         antTask.setProject(new Project());
220 
221         final FileResource fileResource = new FileResource(
222             antTask.getProject(), getPath(""));
223         final Path sourcePath = new Path(antTask.getProject());
224         sourcePath.add(fileResource);
225         antTask.addPath(sourcePath);
226 
227         // when
228         antTask.execute();
229 
230         // then
231         assertWithMessage("Checker is not processed")
232                 .that(TestRootModuleChecker.isProcessed())
233                 .isTrue();
234         final List<File> filesToCheck = TestRootModuleChecker.getFilesToCheck();
235         assertWithMessage("There are more files to check than expected")
236                 .that(filesToCheck)
237                 .hasSize(11);
238         assertWithMessage("The path of file differs from expected")
239                 .that(filesToCheck.get(7).getAbsolutePath())
240                 .isEqualTo(getPath(FLAWLESS_INPUT));
241         assertWithMessage("Amount of logged messages in unexpected")
242                 .that(antTask.getLoggedMessages())
243                 .hasSize(8);
244     }
245 
246     @Test
247     public final void testCustomRootModule() throws IOException {
248         TestRootModuleChecker.reset();
249 
250         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
251         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
252         antTask.execute();
253 
254         assertWithMessage("Checker is not processed")
255                 .that(TestRootModuleChecker.isProcessed())
256                 .isTrue();
257     }
258 
259     @Test
260     public final void testFileSet() throws IOException {
261         TestRootModuleChecker.reset();
262         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
263         final FileSet examinationFileSet = new FileSet();
264         examinationFileSet.setFile(new File(getPath(FLAWLESS_INPUT)));
265         antTask.addFileset(examinationFileSet);
266         antTask.execute();
267 
268         assertWithMessage("Checker is not processed")
269                 .that(TestRootModuleChecker.isProcessed())
270                 .isTrue();
271         final List<File> filesToCheck = TestRootModuleChecker.getFilesToCheck();
272         assertWithMessage("There are more files to check than expected")
273                 .that(filesToCheck)
274                 .hasSize(1);
275         assertWithMessage("The path of file differs from expected")
276                 .that(filesToCheck.getFirst().getAbsolutePath())
277                 .isEqualTo(getPath(FLAWLESS_INPUT));
278     }
279 
280     @Test
281     public final void testNoConfigFile() throws IOException {
282         final CheckstyleAntTask antTask = new CheckstyleAntTask();
283         antTask.setProject(new Project());
284         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
285         final Location fileLocation = new Location("build.xml", 42, 10);
286         antTask.setLocation(fileLocation);
287 
288         final BuildException ex = getExpectedThrowable(BuildException.class,
289                 antTask::execute,
290                 "BuildException is expected");
291         assertWithMessage("Error message is unexpected")
292                 .that(ex.getMessage())
293                 .isEqualTo("Must specify 'config'.");
294         assertWithMessage("Location is missing in exception")
295                 .that(ex.getLocation())
296                 .isEqualTo(fileLocation);
297     }
298 
299     @Test
300     public void testNoFileOrPathSpecified() {
301         final CheckstyleAntTask antTask = new CheckstyleAntTask();
302         antTask.setProject(new Project());
303 
304         final Location fileLocation = new Location("build.xml", 42, 10);
305         antTask.setLocation(fileLocation);
306 
307         final BuildException ex = getExpectedThrowable(BuildException.class,
308                 antTask::execute,
309                 "BuildException is expected");
310 
311         assertWithMessage("Error message is unexpected")
312                 .that(ex.getMessage())
313                 .isEqualTo("Must specify at least one of 'file' or nested 'fileset' or 'path'.");
314         assertWithMessage("Location is missing in the exception")
315                 .that(ex.getLocation())
316                 .isEqualTo(fileLocation);
317     }
318 
319     @Test
320     public final void testNonExistentConfig() throws IOException {
321         final CheckstyleAntTask antTask = new CheckstyleAntTask();
322         antTask.setConfig(getPath(NOT_EXISTING_FILE));
323         antTask.setProject(new Project());
324         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
325         final BuildException ex = getExpectedThrowable(BuildException.class,
326                 antTask::execute,
327                 "BuildException is expected");
328         // Verify exact format of error message (testing String.format mutation)
329         final String expectedExceptionFormat = String.format(Locale.ROOT,
330                 "Unable to create Root Module: config {%s}.", getPath(NOT_EXISTING_FILE));
331         assertWithMessage("Error message is unexpected")
332                 .that(ex.getMessage())
333                 .isEqualTo(expectedExceptionFormat);
334     }
335 
336     @Test
337     public final void testEmptyConfigFile() throws IOException {
338         final CheckstyleAntTask antTask = new CheckstyleAntTask();
339         antTask.setConfig(getPath("InputCheckstyleAntTaskEmptyConfig.xml"));
340         antTask.setProject(new Project());
341         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
342         final BuildException ex = getExpectedThrowable(BuildException.class,
343                 antTask::execute,
344                 "BuildException is expected");
345         final String expectedMessage = String.format(Locale.ROOT,
346                 "Unable to create Root Module: config {%s}.",
347                 getPath("InputCheckstyleAntTaskEmptyConfig.xml"));
348         assertWithMessage("Error message is unexpected")
349                 .that(ex.getMessage())
350                 .isEqualTo(expectedMessage);
351     }
352 
353     @Test
354     public final void testNoFile() throws IOException {
355         final CheckstyleAntTask antTask = getCheckstyleAntTask();
356         final BuildException ex = getExpectedThrowable(BuildException.class,
357                 antTask::execute,
358                 "BuildException is expected");
359         assertWithMessage("Error message is unexpected")
360                 .that(ex.getMessage())
361                 .isEqualTo("Must specify at least one of 'file' or nested 'fileset' or 'path'.");
362     }
363 
364     @Test
365     public void testPackagePrefixResolution() throws IOException {
366         final CheckstyleAntTask antTask = new CheckstyleAntTask();
367         antTask.setProject(new Project());
368         antTask.setConfig(getPath("InputCheckstyleAntTaskPackagePrefix.xml"));
369         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
370 
371         final BuildException exception = getExpectedThrowable(BuildException.class,
372                 antTask::execute,
373                 "Should throw BuildException for non-existent module.");
374         final Throwable cause = exception.getCause();
375 
376         assertWithMessage("BuildException should have a cause")
377                 .that(cause)
378                 .isNotNull();
379         assertWithMessage("Error message should contain the correct package prefix")
380                 .that(cause.getMessage())
381                 .contains("com.puppycrawl.tools.checkstyle");
382     }
383 
384     @Test
385     public final void testMaxWarningExceeded() throws IOException {
386         final CheckstyleAntTask antTask = getCheckstyleAntTask();
387         antTask.setFile(new File(getPath(WARNING_INPUT)));
388         antTask.setMaxWarnings(0);
389         final Location fileLocation = new Location("build.xml", 42, 10);
390         antTask.setLocation(fileLocation);
391 
392         final BuildException ex = getExpectedThrowable(BuildException.class,
393                 antTask::execute,
394                 "BuildException is expected");
395         assertWithMessage("Error message is unexpected")
396                 .that(ex.getMessage())
397                 .isEqualTo("Got 0 errors (max allowed: 0) and 1 warnings.");
398         assertWithMessage("Location is missing in exception")
399                 .that(ex.getLocation())
400                 .isEqualTo(fileLocation);
401     }
402 
403     @Test
404     public final void testMaxErrorsExceeded() throws IOException {
405         final CheckstyleAntTask antTask = getCheckstyleAntTask();
406         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
407         antTask.setMaxErrors(1);
408 
409         final BuildException ex = getExpectedThrowable(BuildException.class,
410                 antTask::execute,
411                 "BuildException is expected");
412         assertWithMessage("Failure message should include maxErrors value")
413                 .that(ex.getMessage())
414                 .contains("max allowed: 1");
415     }
416 
417     @Test
418     public final void testMaxErrors() throws IOException {
419         TestRootModuleChecker.reset();
420 
421         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
422         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
423         antTask.setMaxErrors(2);
424         antTask.execute();
425 
426         assertWithMessage("Checker is not processed")
427                 .that(TestRootModuleChecker.isProcessed())
428                 .isTrue();
429     }
430 
431     @Test
432     public final void testFailureProperty() throws IOException {
433         final CheckstyleAntTask antTask = new CheckstyleAntTask();
434         antTask.setConfig(getPath(CONFIG_FILE));
435         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
436 
437         final Project project = new Project();
438         final String failurePropertyName = "myProperty";
439         project.setProperty(failurePropertyName, FAILURE_PROPERTY_VALUE);
440 
441         antTask.setProject(project);
442         antTask.setFailureProperty(failurePropertyName);
443         final BuildException ex = getExpectedThrowable(BuildException.class,
444                 antTask::execute,
445                 "BuildException is expected");
446         assertWithMessage("Error message is unexpected")
447                 .that(ex.getMessage())
448                 .isEqualTo("Got 2 errors (max allowed: 0) and 0 warnings.");
449         final Map<String, Object> hashtable = project.getProperties();
450         final Object propertyValue = hashtable.get(failurePropertyName);
451         assertWithMessage("Number of errors is unexpected")
452                 .that(propertyValue)
453                 .isEqualTo("Got 2 errors (max allowed: 0) and 0 warnings.");
454     }
455 
456     @Test
457     public final void testOverrideProperty() throws IOException {
458         TestRootModuleChecker.reset();
459 
460         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
461         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
462         final CheckstyleAntTask.Property property = new CheckstyleAntTask.Property();
463         property.setKey("lineLength.severity");
464         property.setValue("ignore");
465         antTask.addProperty(property);
466         antTask.execute();
467 
468         assertWithMessage("Property key should not be empty")
469                     .that(property.getKey())
470                     .isNotEmpty();
471         assertWithMessage("Checker is not processed")
472                 .that(TestRootModuleChecker.isProcessed())
473                 .isTrue();
474         assertWithMessage("Property should be passed to checker with correct value")
475             .that(TestRootModuleChecker.getProperty())
476             .isEqualTo("ignore");
477 
478     }
479 
480     @Test
481     public final void testExecuteIgnoredModulesKeepsIgnoredChild() throws IOException {
482         TestRootModuleChecker.reset();
483         final CheckstyleAntTask antTask = getCheckstyleAntTask(IGNORED_CHILD_CONFIG_FILE);
484         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
485         antTask.setExecuteIgnoredModules(true);
486         antTask.execute();
487 
488         assertWithMessage("Checker should process files")
489                 .that(TestRootModuleChecker.isProcessed())
490                 .isTrue();
491         assertWithMessage("executeIgnoredModules=true should keep severity=ignore children")
492                 .that(TestRootModuleChecker.getConfig().getChildren().length)
493                 .isEqualTo(1);
494     }
495 
496     @Test
497     public final void testOmitIgnoredModulesRemovesIgnoredChild() throws IOException {
498         TestRootModuleChecker.reset();
499         final CheckstyleAntTask antTask = getCheckstyleAntTask(IGNORED_CHILD_CONFIG_FILE);
500         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
501         antTask.execute();
502 
503         assertWithMessage("Checker should process files")
504                 .that(TestRootModuleChecker.isProcessed())
505                 .isTrue();
506         assertWithMessage("default OMIT should drop severity=ignore children")
507                 .that(TestRootModuleChecker.getConfig().getChildren().length)
508                 .isEqualTo(0);
509     }
510 
511     @Test
512     public void testScanPathLogsVerboseMessage() throws IOException {
513         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
514         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
515         antTask.setProject(new Project());
516 
517         final Path path = new Path(antTask.getProject());
518         path.setPath(getPath(FLAWLESS_INPUT));
519         antTask.addPath(path);
520 
521         antTask.execute();
522 
523         final String expectedLogFragment = ") Scanning path " + path;
524 
525         final boolean logFound = antTask.getLoggedMessages().stream()
526                 .anyMatch(pair -> {
527                     return pair.getMsg().contains(expectedLogFragment)
528                             && pair.getLevel() == Project.MSG_VERBOSE;
529                 });
530 
531         assertWithMessage("Verbose log should contain the scanning path")
532                 .that(logFound)
533                 .isTrue();
534     }
535 
536     @Test
537     public final void testConfigurationByUrl() throws IOException {
538         final CheckstyleAntTask antTask = new CheckstyleAntTask();
539         antTask.setProject(new Project());
540         final URL url = new File(getPath(CONFIG_FILE)).toURI().toURL();
541         antTask.setConfig(url.toString());
542         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
543 
544         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
545         final File outputFile = new File("target/ant_task_config_by_url.txt");
546         formatter.setTofile(outputFile);
547         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
548         formatterType.setValue("plain");
549         formatter.setType(formatterType);
550         formatter.createListener(null);
551         antTask.addFormatter(formatter);
552 
553         antTask.execute();
554 
555         final List<String> output = readWholeFile(outputFile);
556         final int sizeOfOutputWithNoViolations = 2;
557         assertWithMessage("No violations expected")
558                 .that(output)
559                 .hasSize(sizeOfOutputWithNoViolations);
560     }
561 
562     @Test
563     public final void testConfigurationByResource() throws IOException {
564         final CheckstyleAntTask antTask = new CheckstyleAntTask();
565         antTask.setProject(new Project());
566         antTask.setConfig(getPath(CONFIG_FILE));
567         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
568 
569         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
570         final File outputFile = new File("target/ant_task_config_by_url.txt");
571         formatter.setTofile(outputFile);
572         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
573         formatterType.setValue("plain");
574         formatter.setType(formatterType);
575         formatter.createListener(null);
576         antTask.addFormatter(formatter);
577 
578         antTask.execute();
579 
580         final List<String> output = readWholeFile(outputFile);
581         final int sizeOfOutputWithNoViolations = 2;
582         assertWithMessage("No violations expected")
583                 .that(output)
584                 .hasSize(sizeOfOutputWithNoViolations);
585     }
586 
587     @Test
588     public final void testSimultaneousConfiguration() throws IOException {
589         final File file = new File(getPath(CONFIG_FILE));
590         final URL url = file.toURI().toURL();
591 
592         final CheckstyleAntTask antTask = new CheckstyleAntTask();
593         antTask.setConfig(url.toString());
594         final BuildException ex = getExpectedThrowable(BuildException.class,
595                 () -> antTask.setConfig("Any string value"),
596                 "BuildException is expected");
597         final String expected = "Attribute 'config' has already been set";
598         assertWithMessage("Error message is unexpected")
599                 .that(ex.getMessage())
600                 .isEqualTo(expected);
601     }
602 
603     @Test
604     public final void testSetPropertiesFile() throws IOException {
605         TestRootModuleChecker.reset();
606 
607         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
608         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
609         antTask.setProperties(new File(getPath(
610                 "InputCheckstyleAntTaskCheckstyleAntTest.properties")));
611         antTask.execute();
612 
613         assertWithMessage("Property is not set")
614                 .that(TestRootModuleChecker.getProperty())
615                 .isEqualTo("ignore");
616     }
617 
618     @Test
619     public final void testSetPropertiesNonExistentFile() throws IOException {
620         final CheckstyleAntTask antTask = getCheckstyleAntTask();
621         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
622         final File propertiesFile = new File(getPath(NOT_EXISTING_FILE));
623         antTask.setProperties(propertiesFile);
624         final BuildException ex = getExpectedThrowable(BuildException.class,
625                 antTask::execute,
626                 "BuildException is expected");
627         assertWithMessage("Error message is unexpected")
628                 .that(ex.getMessage())
629                 .startsWith("Error loading Properties file");
630         assertWithMessage("Error message should contain file path")
631                 .that(ex.getMessage())
632                 .contains(propertiesFile.toPath().toString());
633         assertWithMessage("Exception should have a location")
634                 .that(ex.getLocation())
635                 .isNotNull();
636     }
637 
638     @Test
639     public final void testXmlOutput() throws IOException {
640         final CheckstyleAntTask antTask = getCheckstyleAntTask();
641         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
642         antTask.setFailOnViolation(false);
643         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
644         final File outputFile = new File("target/log.xml");
645         formatter.setTofile(outputFile);
646         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
647         formatterType.setValue("xml");
648         formatter.setType(formatterType);
649         antTask.addFormatter(formatter);
650         antTask.execute();
651 
652         final List<String> expected = readWholeFile(
653             new File(getPath("ExpectedCheckstyleAntTaskXmlOutput.xml")));
654         final List<String> actual = readWholeFile(outputFile);
655         for (int i = 0; i < expected.size(); i++) {
656             final String line = expected.get(i);
657             if (!line.startsWith("<checkstyle version") && !line.startsWith("<file")) {
658                 assertWithMessage("Content of file with violations differs from expected")
659                         .that(actual.get(i))
660                         .isEqualTo(line);
661             }
662         }
663     }
664 
665     @Test
666     public final void testSarifOutput() throws IOException {
667         final CheckstyleAntTask antTask = getCheckstyleAntTask();
668         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
669         antTask.setFailOnViolation(false);
670         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
671         final File outputFile = new File("target/log.sarif");
672         formatter.setTofile(outputFile);
673         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
674         formatterType.setValue("sarif");
675         formatter.setType(formatterType);
676         antTask.addFormatter(formatter);
677         antTask.execute();
678 
679         final List<String> expected = readWholeFile(
680                 new File(getPath("ExpectedCheckstyleAntTaskSarifOutput.sarif")));
681         final List<String> actual = readWholeFile(outputFile);
682         for (int lineNumber = 0; lineNumber < expected.size(); lineNumber++) {
683             final String line = expected.get(lineNumber);
684             final StandardSubjectBuilder assertWithMessage =
685                     assertWithMessage("Content of file with violations differs from expected");
686             if (line.trim().startsWith("\"uri\"")) {
687                 final String expectedPathEnd = Iterables.get(
688                         Splitter.on("**").split(line), 1);
689                 // normalize windows path
690                 final String actualLine = actual.get(lineNumber).replaceAll("\\\\", "/");
691                 assertWithMessage
692                         .that(actualLine)
693                         .endsWith(expectedPathEnd);
694             }
695             else {
696                 assertWithMessage
697                         .that(actual.get(lineNumber))
698                         .isEqualTo(line);
699             }
700         }
701     }
702 
703     @Test
704     public final void testCreateListenerException() throws IOException {
705         final CheckstyleAntTask antTask = getCheckstyleAntTask();
706         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
707         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
708         final File outputFile = new File("target/");
709         formatter.setTofile(outputFile);
710         antTask.addFormatter(formatter);
711         final BuildException ex = getExpectedThrowable(BuildException.class,
712                 antTask::execute,
713                 "BuildException is expected");
714         assertWithMessage("Error message is unexpected")
715                 .that(ex.getMessage())
716                 .isEqualTo("Unable to create listeners: formatters "
717                         + "{" + List.of(formatter) + "}.");
718     }
719 
720     @Test
721     public final void testCreateListenerExceptionWithXmlLogger() throws IOException {
722         final CheckstyleAntTask antTask = getCheckstyleAntTask();
723         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
724         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
725         final File outputFile = new File("target/");
726         formatter.setTofile(outputFile);
727         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
728         formatterType.setValue("xml");
729         formatter.setType(formatterType);
730         antTask.addFormatter(formatter);
731         final BuildException ex = getExpectedThrowable(BuildException.class,
732                 antTask::execute,
733                 "BuildException is expected");
734         assertWithMessage("Error message is unexpected")
735                 .that(ex.getMessage())
736                 .startsWith("Unable to create listeners: formatters");
737     }
738 
739     @Test
740     public final void testCreateListenerExceptionWithSarifLogger() throws IOException {
741         final CheckstyleAntTask antTask = getCheckstyleAntTask();
742         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
743         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
744         final File outputFile = new File("target/");
745         formatter.setTofile(outputFile);
746         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
747         formatterType.setValue("sarif");
748         formatter.setType(formatterType);
749         antTask.addFormatter(formatter);
750         final BuildException ex = getExpectedThrowable(BuildException.class,
751                 antTask::execute,
752                 "BuildException is expected");
753         assertWithMessage("Error message is unexpected")
754                 .that(ex.getMessage())
755                 .startsWith("Unable to create listeners: formatters");
756     }
757 
758     @Test
759     public void testSetInvalidType() {
760         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
761         final BuildException ex = getExpectedThrowable(BuildException.class,
762                 () -> formatterType.setValue("foo"),
763                 "BuildException is expected");
764         assertWithMessage("Error message is unexpected")
765                 .that(ex.getMessage())
766                 .isEqualTo("foo is not a legal value for this attribute");
767     }
768 
769     @Test
770     public void testSetFileValueByFile() throws IOException {
771         final String filename = getPath("InputCheckstyleAntTaskCheckstyleAntTest.properties");
772         final CheckstyleAntTask.Property property = new CheckstyleAntTask.Property();
773         property.setFile(new File(filename));
774         assertWithMessage("File path is unexpected")
775                 .that(new File(filename).getAbsolutePath())
776                 .isEqualTo(property.getValue());
777     }
778 
779     @Test
780     public void testDefaultLoggerListener() throws IOException {
781         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
782         formatter.setUseFile(false);
783         assertWithMessage("Listener instance has unexpected type")
784                 .that(formatter.createListener(null))
785                 .isInstanceOf(DefaultLogger.class);
786     }
787 
788     @Test
789     public void testDefaultLoggerListenerWithToFile() throws IOException {
790         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
791         formatter.setUseFile(false);
792         formatter.setTofile(new File("target/"));
793         assertWithMessage("Listener instance has unexpected type")
794                 .that(formatter.createListener(null))
795                 .isInstanceOf(DefaultLogger.class);
796     }
797 
798     @Test
799     public void testXmlLoggerListener() throws IOException {
800         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
801         formatterType.setValue("xml");
802         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
803         formatter.setType(formatterType);
804         formatter.setUseFile(false);
805         assertWithMessage("Listener instance has unexpected type")
806                 .that(formatter.createListener(null))
807                 .isInstanceOf(XMLLogger.class);
808     }
809 
810     @Test
811     public void testXmlLoggerListenerWithToFile() throws IOException {
812         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
813         formatterType.setValue("xml");
814         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
815         formatter.setType(formatterType);
816         formatter.setUseFile(false);
817         formatter.setTofile(new File("target/"));
818         assertWithMessage("Listener instance has unexpected type")
819                 .that(formatter.createListener(null))
820                 .isInstanceOf(XMLLogger.class);
821     }
822 
823     @Test
824     public void testDefaultLoggerWithNullToFile() throws IOException {
825         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
826         formatter.setTofile(null);
827         assertWithMessage("Listener instance has unexpected type")
828             .that(formatter.createListener(null))
829             .isInstanceOf(DefaultLogger.class);
830     }
831 
832     @Test
833     public void testXmlLoggerWithNullToFile() throws IOException {
834         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
835         formatterType.setValue("xml");
836         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
837         formatter.setType(formatterType);
838         formatter.setTofile(null);
839         assertWithMessage("Listener instance has unexpected type")
840             .that(formatter.createListener(null))
841             .isInstanceOf(XMLLogger.class);
842     }
843 
844     @Test
845     public void testSarifLoggerListener() throws IOException {
846         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
847         formatterType.setValue("sarif");
848         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
849         formatter.setType(formatterType);
850         formatter.setUseFile(false);
851         assertWithMessage("Listener instance has unexpected type")
852                 .that(formatter.createListener(null))
853                 .isInstanceOf(SarifLogger.class);
854     }
855 
856     @Test
857     public void testSarifLoggerListenerWithToFile() throws IOException {
858         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
859         formatterType.setValue("sarif");
860         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
861         formatter.setType(formatterType);
862         formatter.setUseFile(false);
863         formatter.setTofile(new File("target/"));
864         assertWithMessage("Listener instance has unexpected type")
865                 .that(formatter.createListener(null))
866                 .isInstanceOf(SarifLogger.class);
867     }
868 
869     @Test
870     public void testSarifLoggerWithNullToFile() throws IOException {
871         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
872         formatterType.setValue("sarif");
873         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
874         formatter.setType(formatterType);
875         formatter.setTofile(null);
876         assertWithMessage("Listener instance has unexpected type")
877                 .that(formatter.createListener(null))
878                 .isInstanceOf(SarifLogger.class);
879     }
880 
881     @Test
882     public void testDestroyed() throws IOException {
883         TestRootModuleChecker.reset();
884 
885         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
886         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
887         antTask.setMaxWarnings(0);
888         antTask.execute();
889 
890         assertWithMessage("Checker is not destroyed")
891                 .that(TestRootModuleChecker.isDestroyed())
892                 .isTrue();
893     }
894 
895     @Test
896     public void testMaxWarnings() throws IOException {
897         TestRootModuleChecker.reset();
898 
899         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
900         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
901         antTask.setMaxWarnings(0);
902         antTask.execute();
903 
904         assertWithMessage("Checker is not processed")
905                 .that(TestRootModuleChecker.isProcessed())
906                 .isTrue();
907     }
908 
909     @Test
910     public final void testExecuteLogOutput() throws Exception {
911         final URL url = new File(getPath(CONFIG_FILE)).toURI().toURL();
912         final ResourceBundle bundle = ResourceBundle.getBundle(
913                 Definitions.CHECKSTYLE_BUNDLE, Locale.ROOT);
914         final String auditStartedMessage = bundle.getString(DefaultLogger.AUDIT_STARTED_MESSAGE);
915         final String auditFinishedMessage = bundle.getString(DefaultLogger.AUDIT_FINISHED_MESSAGE);
916 
917         final List<MessageLevelPair> expectedList = Arrays.asList(
918                 new MessageLevelPair("checkstyle version .*", Project.MSG_VERBOSE),
919                 new MessageLevelPair("Adding standalone file for audit", Project.MSG_VERBOSE),
920                 new MessageLevelPair("To locate the files took \\d+ ms.", Project.MSG_VERBOSE),
921                 new MessageLevelPair("Running Checkstyle  on 1 files", Project.MSG_INFO),
922                 new MessageLevelPair("Using configuration file:.*", Project.MSG_VERBOSE),
923                 new MessageLevelPair(auditStartedMessage, Project.MSG_DEBUG),
924                 new MessageLevelPair(auditFinishedMessage, Project.MSG_DEBUG),
925                 new MessageLevelPair("To process the files took \\d+ ms.", Project.MSG_VERBOSE),
926                 new MessageLevelPair("Total execution took \\d+ ms.", Project.MSG_VERBOSE)
927         );
928 
929         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
930         antTask.setProject(new Project());
931         antTask.setConfig(url.toString());
932         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
933 
934         antTask.execute();
935 
936         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
937 
938         assertWithMessage("Amount of log messages is unexpected")
939                 .that(loggedMessages)
940                 .hasSize(expectedList.size());
941 
942         for (int i = 0; i < expectedList.size(); i++) {
943             final MessageLevelPair expected = expectedList.get(i);
944             final MessageLevelPair actual = loggedMessages.get(i);
945             assertWithMessage("Log messages should match")
946                     .that(actual.getMsg())
947                     .matches(expected.getMsg());
948             assertWithMessage("Log levels should be equal")
949                     .that(actual.getLevel())
950                     .isEqualTo(expected.getLevel());
951         }
952     }
953 
954     @Test
955     public void testCheckerException() throws IOException {
956         final CheckstyleAntTask antTask = new CheckstyleAntTaskStub();
957         antTask.setConfig(getPath(CONFIG_FILE));
958         antTask.setProject(new Project());
959         antTask.setFile(new File(""));
960         final BuildException ex = getExpectedThrowable(BuildException.class,
961                 antTask::execute,
962                 "BuildException is expected");
963         assertWithMessage("Error message is unexpected")
964                 .that(ex)
965                 .hasMessageThat()
966                         .startsWith("Unable to process files:");
967     }
968 
969     @Test
970     public void testLoggedTime() throws IOException {
971         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
972         antTask.setConfig(getPath(CONFIG_FILE));
973         antTask.setProject(new Project());
974         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
975         final long startTime = System.currentTimeMillis();
976         antTask.execute();
977         final long endTime = System.currentTimeMillis();
978         final long testingTime = endTime - startTime;
979         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
980 
981         assertLoggedTime(loggedMessages, testingTime, "Total execution");
982         assertLoggedTime(loggedMessages, testingTime, "To locate the files");
983         assertLoggedTime(loggedMessages, testingTime, "To process the files");
984     }
985 
986     private static void assertLoggedTime(List<MessageLevelPair> loggedMessages,
987                                          long testingTime, String expectedMsg) {
988 
989         final Optional<MessageLevelPair> optionalMessageLevelPair = loggedMessages.stream()
990             .filter(msg -> msg.getMsg().startsWith(expectedMsg))
991             .findFirst();
992 
993         assertWithMessage("Message should be present.")
994             .that(optionalMessageLevelPair.isPresent())
995             .isTrue();
996 
997         final long actualTime = getNumberFromLine(optionalMessageLevelPair.orElseThrow().getMsg());
998 
999         assertWithMessage("Logged time in '%s' must be less than the testing time", expectedMsg)
1000             .that(actualTime)
1001             .isAtMost(testingTime);
1002     }
1003 
1004     private static List<String> readWholeFile(File outputFile) throws IOException {
1005         return Files.readAllLines(outputFile.toPath());
1006     }
1007 
1008     private static long getNumberFromLine(String line) {
1009         final Matcher matcher = Pattern.compile("(\\d+)").matcher(line);
1010         matcher.find();
1011         return Long.parseLong(matcher.group(1));
1012     }
1013 
1014     @Test
1015     public void testMaxWarningDefault() throws IOException {
1016         final CheckstyleAntTask antTask = getCheckstyleAntTask();
1017         final File inputFile = new File(getPath(WARNING_INPUT));
1018         final Location fileLocation = new Location("build.xml", 42, 10);
1019 
1020         antTask.setFile(inputFile);
1021         antTask.setLocation(fileLocation);
1022         assertDoesNotThrow(antTask::execute, "BuildException is not expected");
1023     }
1024 
1025     @Test
1026     public void testMultipleFormattersProduceOutputs() throws IOException {
1027         final CheckstyleAntTask antTask = getCheckstyleAntTask();
1028         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
1029         antTask.setFailOnViolation(false);
1030 
1031         final File firstOutput = new File(temporaryFolder, "ant_task_multi_formatter_1.txt");
1032         final File secondOutput = new File(temporaryFolder, "ant_task_multi_formatter_2.txt");
1033 
1034         antTask.addFormatter(createPlainFormatter(firstOutput));
1035         antTask.addFormatter(createPlainFormatter(secondOutput));
1036 
1037         antTask.execute();
1038 
1039         assertWithMessage("First formatter output was not created")
1040                 .that(firstOutput.exists())
1041                 .isTrue();
1042         assertWithMessage("First formatter output is empty")
1043                 .that(firstOutput.length())
1044                 .isGreaterThan(0L);
1045         assertWithMessage("Second formatter output was not created")
1046                 .that(secondOutput.exists())
1047                 .isTrue();
1048         assertWithMessage("Second formatter output is empty")
1049                 .that(secondOutput.length())
1050                 .isGreaterThan(0L);
1051     }
1052 
1053     @Test
1054     public void testExceptionMessageContainsFileList() throws Exception {
1055         final CheckstyleAntTask antTask = new CheckstyleAntTaskStub();
1056         antTask.setConfig(getPath(CONFIG_FILE));
1057         antTask.setProject(new Project());
1058 
1059         final File file = new File(getPath(FLAWLESS_INPUT));
1060         antTask.setFile(file);
1061 
1062         final BuildException ex = getExpectedThrowable(
1063                 BuildException.class, antTask::execute, "BuildException is expected");
1064 
1065         assertWithMessage("Exception message must contain the file name")
1066                 .that(ex.getMessage())
1067                 .contains(file.getName());
1068     }
1069 
1070     @Test
1071     public void testAntProjectPropertyValueIsCopiedCorrectly() throws IOException {
1072         TestRootModuleChecker.reset();
1073 
1074         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
1075 
1076         final Project project = new Project();
1077         project.setProperty("lineLength.severity", "ignore");
1078         antTask.setProject(project);
1079 
1080         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
1081 
1082         antTask.execute();
1083 
1084         assertWithMessage("Failed to propagate Ant project property value correctly")
1085                 .that(TestRootModuleChecker.getProperty())
1086                 .isEqualTo("ignore");
1087     }
1088 
1089     @Test
1090     public final void testFileSetWithLogIndexVerification() throws IOException {
1091         // given
1092         TestRootModuleChecker.reset();
1093 
1094         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
1095         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
1096         antTask.setProject(new Project());
1097 
1098         final FileSet fileSet = new FileSet();
1099         fileSet.setFile(new File(getPath(FLAWLESS_INPUT)));
1100         antTask.addFileset(fileSet);
1101 
1102         // when
1103         antTask.scanFileSets();
1104 
1105         // then
1106         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
1107 
1108         assertWithMessage("Log message with correct index was not found")
1109                 .that(loggedMessages.stream().filter(
1110                         msg -> msg.getMsg().startsWith("0) Adding 1 files from directory")).count())
1111                 .isEqualTo(1);
1112     }
1113 
1114     private static CheckstyleAntTask.Formatter createPlainFormatter(File outputFile) {
1115         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
1116         formatter.setTofile(outputFile);
1117         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
1118         formatterType.setValue("plain");
1119         formatter.setType(formatterType);
1120         return formatter;
1121     }
1122 
1123 }