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