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