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