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