View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.ant;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.net.URL;
29  import java.nio.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      public 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.getFirst().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.getFirst().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(10);
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.getFirst().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 void testPackagePrefixResolution() throws IOException {
365         final CheckstyleAntTask antTask = new CheckstyleAntTask();
366         antTask.setProject(new Project());
367         antTask.setConfig(getPath("InputCheckstyleAntTaskPackagePrefix.xml"));
368         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
369 
370         try {
371             antTask.execute();
372             assertWithMessage("Should throw BuildException for non-existent module.")
373                     .fail();
374         }
375         catch (final BuildException exception) {
376             final Throwable cause = exception.getCause();
377 
378             assertWithMessage("BuildException should have a cause")
379                     .that(cause)
380                     .isNotNull();
381             assertWithMessage("Error message should contain the correct package prefix")
382                     .that(cause.getMessage())
383                     .contains("com.puppycrawl.tools.checkstyle");
384         }
385     }
386 
387     @Test
388     public final void testMaxWarningExceeded() throws IOException {
389         final CheckstyleAntTask antTask = getCheckstyleAntTask();
390         antTask.setFile(new File(getPath(WARNING_INPUT)));
391         antTask.setMaxWarnings(0);
392         final Location fileLocation = new Location("build.xml", 42, 10);
393         antTask.setLocation(fileLocation);
394 
395         final BuildException ex = getExpectedThrowable(BuildException.class,
396                 antTask::execute,
397                 "BuildException is expected");
398         assertWithMessage("Error message is unexpected")
399                 .that(ex.getMessage())
400                 .isEqualTo("Got 0 errors (max allowed: 0) and 1 warnings.");
401         assertWithMessage("Location is missing in exception")
402                 .that(ex.getLocation())
403                 .isEqualTo(fileLocation);
404     }
405 
406     @Test
407     public final void testMaxErrorsExceeded() throws IOException {
408         final CheckstyleAntTask antTask = getCheckstyleAntTask();
409         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
410         antTask.setMaxErrors(1);
411 
412         final BuildException ex = getExpectedThrowable(BuildException.class,
413                 antTask::execute,
414                 "BuildException is expected");
415         assertWithMessage("Failure message should include maxErrors value")
416                 .that(ex.getMessage())
417                 .contains("max allowed: 1");
418     }
419 
420     @Test
421     public final void testMaxErrors() throws IOException {
422         TestRootModuleChecker.reset();
423 
424         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
425         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
426         antTask.setMaxErrors(2);
427         antTask.execute();
428 
429         assertWithMessage("Checker is not processed")
430                 .that(TestRootModuleChecker.isProcessed())
431                 .isTrue();
432     }
433 
434     @Test
435     public final void testFailureProperty() throws IOException {
436         final CheckstyleAntTask antTask = new CheckstyleAntTask();
437         antTask.setConfig(getPath(CONFIG_FILE));
438         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
439 
440         final Project project = new Project();
441         final String failurePropertyName = "myProperty";
442         project.setProperty(failurePropertyName, FAILURE_PROPERTY_VALUE);
443 
444         antTask.setProject(project);
445         antTask.setFailureProperty(failurePropertyName);
446         final BuildException ex = getExpectedThrowable(BuildException.class,
447                 antTask::execute,
448                 "BuildException is expected");
449         assertWithMessage("Error message is unexpected")
450                 .that(ex.getMessage())
451                 .isEqualTo("Got 2 errors (max allowed: 0) and 0 warnings.");
452         final Map<String, Object> hashtable = project.getProperties();
453         final Object propertyValue = hashtable.get(failurePropertyName);
454         assertWithMessage("Number of errors is unexpected")
455                 .that(propertyValue)
456                 .isEqualTo("Got 2 errors (max allowed: 0) and 0 warnings.");
457     }
458 
459     @Test
460     public final void testOverrideProperty() throws IOException {
461         TestRootModuleChecker.reset();
462 
463         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
464         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
465         final CheckstyleAntTask.Property property = new CheckstyleAntTask.Property();
466         property.setKey("lineLength.severity");
467         property.setValue("ignore");
468         antTask.addProperty(property);
469         antTask.execute();
470 
471         assertWithMessage("Property key should not be empty")
472                     .that(property.getKey())
473                     .isNotEmpty();
474         assertWithMessage("Checker is not processed")
475                 .that(TestRootModuleChecker.isProcessed())
476                 .isTrue();
477         assertWithMessage("Property should be passed to checker with correct value")
478             .that(TestRootModuleChecker.getProperty())
479             .isEqualTo("ignore");
480 
481     }
482 
483     @Test
484     public final void testExecuteIgnoredModules() throws IOException {
485         final CheckstyleAntTask antTask = getCheckstyleAntTask();
486         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
487         antTask.setFailOnViolation(false);
488         antTask.setExecuteIgnoredModules(true);
489 
490         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
491         final File outputFile = new File("target/ant_task_plain_output.txt");
492         formatter.setTofile(outputFile);
493         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
494         formatterType.setValue("plain");
495         formatter.setType(formatterType);
496         formatter.createListener(null);
497 
498         antTask.addFormatter(formatter);
499         antTask.execute();
500 
501         final ResourceBundle bundle = ResourceBundle.getBundle(
502                 Definitions.CHECKSTYLE_BUNDLE, Locale.ROOT);
503         final String auditStartedMessage = bundle.getString(DefaultLogger.AUDIT_STARTED_MESSAGE);
504         final String auditFinishedMessage = bundle.getString(DefaultLogger.AUDIT_FINISHED_MESSAGE);
505         final List<String> output = readWholeFile(outputFile);
506         final String errorMessage = "Content of file with violations differs from expected";
507         assertWithMessage(errorMessage)
508                 .that(output.getFirst())
509                 .isEqualTo(auditStartedMessage);
510         assertWithMessage(errorMessage)
511                 .that(output.get(1))
512                 .matches("^\\[WARN].*InputCheckstyleAntTaskError.java:4: .*"
513                         + "@incomplete=Some javadoc \\[WriteTag]");
514         assertWithMessage(errorMessage)
515                 .that(output.get(2))
516                 .matches("^\\[ERROR].*InputCheckstyleAntTaskError.java:7: "
517                         + "Line is longer than 70 characters \\(found 80\\). \\[LineLength]");
518         assertWithMessage(errorMessage)
519                 .that(output.get(3))
520                 .matches("^\\[ERROR].*InputCheckstyleAntTaskError.java:9: "
521                         + "Line is longer than 70 characters \\(found 81\\). \\[LineLength]");
522         assertWithMessage(errorMessage)
523                 .that(output.get(4))
524                 .isEqualTo(auditFinishedMessage);
525     }
526 
527     @Test
528     public void testScanPathLogsVerboseMessage() throws IOException {
529         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
530         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
531         antTask.setProject(new Project());
532 
533         final Path path = new Path(antTask.getProject());
534         path.setPath(getPath(FLAWLESS_INPUT));
535         antTask.addPath(path);
536 
537         antTask.execute();
538 
539         final String expectedLogFragment = ") Scanning path " + path;
540 
541         final boolean logFound = antTask.getLoggedMessages().stream()
542                 .anyMatch(pair -> {
543                     return pair.getMsg().contains(expectedLogFragment)
544                             && pair.getLevel() == Project.MSG_VERBOSE;
545                 });
546 
547         assertWithMessage("Verbose log should contain the scanning path")
548                 .that(logFound)
549                 .isTrue();
550     }
551 
552     @Test
553     public final void testConfigurationByUrl() throws IOException {
554         final CheckstyleAntTask antTask = new CheckstyleAntTask();
555         antTask.setProject(new Project());
556         final URL url = new File(getPath(CONFIG_FILE)).toURI().toURL();
557         antTask.setConfig(url.toString());
558         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
559 
560         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
561         final File outputFile = new File("target/ant_task_config_by_url.txt");
562         formatter.setTofile(outputFile);
563         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
564         formatterType.setValue("plain");
565         formatter.setType(formatterType);
566         formatter.createListener(null);
567         antTask.addFormatter(formatter);
568 
569         antTask.execute();
570 
571         final List<String> output = readWholeFile(outputFile);
572         final int sizeOfOutputWithNoViolations = 2;
573         assertWithMessage("No violations expected")
574                 .that(output)
575                 .hasSize(sizeOfOutputWithNoViolations);
576     }
577 
578     @Test
579     public final void testConfigurationByResource() throws IOException {
580         final CheckstyleAntTask antTask = new CheckstyleAntTask();
581         antTask.setProject(new Project());
582         antTask.setConfig(getPath(CONFIG_FILE));
583         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
584 
585         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
586         final File outputFile = new File("target/ant_task_config_by_url.txt");
587         formatter.setTofile(outputFile);
588         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
589         formatterType.setValue("plain");
590         formatter.setType(formatterType);
591         formatter.createListener(null);
592         antTask.addFormatter(formatter);
593 
594         antTask.execute();
595 
596         final List<String> output = readWholeFile(outputFile);
597         final int sizeOfOutputWithNoViolations = 2;
598         assertWithMessage("No violations expected")
599                 .that(output)
600                 .hasSize(sizeOfOutputWithNoViolations);
601     }
602 
603     @Test
604     public final void testSimultaneousConfiguration() throws IOException {
605         final File file = new File(getPath(CONFIG_FILE));
606         final URL url = file.toURI().toURL();
607 
608         final CheckstyleAntTask antTask = new CheckstyleAntTask();
609         antTask.setConfig(url.toString());
610         final BuildException ex = getExpectedThrowable(BuildException.class,
611                 () -> antTask.setConfig("Any string value"),
612                 "BuildException is expected");
613         final String expected = "Attribute 'config' has already been set";
614         assertWithMessage("Error message is unexpected")
615                 .that(ex.getMessage())
616                 .isEqualTo(expected);
617     }
618 
619     @Test
620     public final void testSetPropertiesFile() throws IOException {
621         TestRootModuleChecker.reset();
622 
623         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
624         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
625         antTask.setProperties(new File(getPath(
626                 "InputCheckstyleAntTaskCheckstyleAntTest.properties")));
627         antTask.execute();
628 
629         assertWithMessage("Property is not set")
630                 .that(TestRootModuleChecker.getProperty())
631                 .isEqualTo("ignore");
632     }
633 
634     @Test
635     public final void testSetPropertiesNonExistentFile() throws IOException {
636         final CheckstyleAntTask antTask = getCheckstyleAntTask();
637         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
638         final File propertiesFile = new File(getPath(NOT_EXISTING_FILE));
639         antTask.setProperties(propertiesFile);
640         final BuildException ex = getExpectedThrowable(BuildException.class,
641                 antTask::execute,
642                 "BuildException is expected");
643         assertWithMessage("Error message is unexpected")
644                 .that(ex.getMessage())
645                 .startsWith("Error loading Properties file");
646         assertWithMessage("Error message should contain file path")
647                 .that(ex.getMessage())
648                 .contains(propertiesFile.toPath().toString());
649         assertWithMessage("Exception should have a location")
650                 .that(ex.getLocation())
651                 .isNotNull();
652     }
653 
654     @Test
655     public final void testXmlOutput() throws IOException {
656         final CheckstyleAntTask antTask = getCheckstyleAntTask();
657         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
658         antTask.setFailOnViolation(false);
659         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
660         final File outputFile = new File("target/log.xml");
661         formatter.setTofile(outputFile);
662         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
663         formatterType.setValue("xml");
664         formatter.setType(formatterType);
665         antTask.addFormatter(formatter);
666         antTask.execute();
667 
668         final List<String> expected = readWholeFile(
669             new File(getPath("ExpectedCheckstyleAntTaskXmlOutput.xml")));
670         final List<String> actual = readWholeFile(outputFile);
671         for (int i = 0; i < expected.size(); i++) {
672             final String line = expected.get(i);
673             if (!line.startsWith("<checkstyle version") && !line.startsWith("<file")) {
674                 assertWithMessage("Content of file with violations differs from expected")
675                         .that(actual.get(i))
676                         .isEqualTo(line);
677             }
678         }
679     }
680 
681     @Test
682     public final void testSarifOutput() throws IOException {
683         final CheckstyleAntTask antTask = getCheckstyleAntTask();
684         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
685         antTask.setFailOnViolation(false);
686         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
687         final File outputFile = new File("target/log.sarif");
688         formatter.setTofile(outputFile);
689         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
690         formatterType.setValue("sarif");
691         formatter.setType(formatterType);
692         antTask.addFormatter(formatter);
693         antTask.execute();
694 
695         final List<String> expected = readWholeFile(
696                 new File(getPath("ExpectedCheckstyleAntTaskSarifOutput.sarif")));
697         final List<String> actual = readWholeFile(outputFile);
698         for (int lineNumber = 0; lineNumber < expected.size(); lineNumber++) {
699             final String line = expected.get(lineNumber);
700             final StandardSubjectBuilder assertWithMessage =
701                     assertWithMessage("Content of file with violations differs from expected");
702             if (line.trim().startsWith("\"uri\"")) {
703                 final String expectedPathEnd = Iterables.get(
704                         Splitter.on("**").split(line), 1);
705                 // normalize windows path
706                 final String actualLine = actual.get(lineNumber).replaceAll("\\\\", "/");
707                 assertWithMessage
708                         .that(actualLine)
709                         .endsWith(expectedPathEnd);
710             }
711             else {
712                 assertWithMessage
713                         .that(actual.get(lineNumber))
714                         .isEqualTo(line);
715             }
716         }
717     }
718 
719     @Test
720     public final void testCreateListenerException() throws IOException {
721         final CheckstyleAntTask antTask = getCheckstyleAntTask();
722         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
723         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
724         final File outputFile = new File("target/");
725         formatter.setTofile(outputFile);
726         antTask.addFormatter(formatter);
727         final BuildException ex = getExpectedThrowable(BuildException.class,
728                 antTask::execute,
729                 "BuildException is expected");
730         assertWithMessage("Error message is unexpected")
731                 .that(ex.getMessage())
732                 .isEqualTo("Unable to create listeners: formatters "
733                         + "{" + List.of(formatter) + "}.");
734     }
735 
736     @Test
737     public final void testCreateListenerExceptionWithXmlLogger() throws IOException {
738         final CheckstyleAntTask antTask = getCheckstyleAntTask();
739         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
740         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
741         final File outputFile = new File("target/");
742         formatter.setTofile(outputFile);
743         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
744         formatterType.setValue("xml");
745         formatter.setType(formatterType);
746         antTask.addFormatter(formatter);
747         final BuildException ex = getExpectedThrowable(BuildException.class,
748                 antTask::execute,
749                 "BuildException is expected");
750         assertWithMessage("Error message is unexpected")
751                 .that(ex.getMessage())
752                 .startsWith("Unable to create listeners: formatters");
753     }
754 
755     @Test
756     public final void testCreateListenerExceptionWithSarifLogger() throws IOException {
757         final CheckstyleAntTask antTask = getCheckstyleAntTask();
758         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
759         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
760         final File outputFile = new File("target/");
761         formatter.setTofile(outputFile);
762         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
763         formatterType.setValue("sarif");
764         formatter.setType(formatterType);
765         antTask.addFormatter(formatter);
766         final BuildException ex = getExpectedThrowable(BuildException.class,
767                 antTask::execute,
768                 "BuildException is expected");
769         assertWithMessage("Error message is unexpected")
770                 .that(ex.getMessage())
771                 .startsWith("Unable to create listeners: formatters");
772     }
773 
774     @Test
775     public void testSetInvalidType() {
776         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
777         final BuildException ex = getExpectedThrowable(BuildException.class,
778                 () -> formatterType.setValue("foo"),
779                 "BuildException is expected");
780         assertWithMessage("Error message is unexpected")
781                 .that(ex.getMessage())
782                 .isEqualTo("foo is not a legal value for this attribute");
783     }
784 
785     @Test
786     public void testSetFileValueByFile() throws IOException {
787         final String filename = getPath("InputCheckstyleAntTaskCheckstyleAntTest.properties");
788         final CheckstyleAntTask.Property property = new CheckstyleAntTask.Property();
789         property.setFile(new File(filename));
790         assertWithMessage("File path is unexpected")
791                 .that(new File(filename).getAbsolutePath())
792                 .isEqualTo(property.getValue());
793     }
794 
795     @Test
796     public void testDefaultLoggerListener() throws IOException {
797         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
798         formatter.setUseFile(false);
799         assertWithMessage("Listener instance has unexpected type")
800                 .that(formatter.createListener(null))
801                 .isInstanceOf(DefaultLogger.class);
802     }
803 
804     @Test
805     public void testDefaultLoggerListenerWithToFile() throws IOException {
806         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
807         formatter.setUseFile(false);
808         formatter.setTofile(new File("target/"));
809         assertWithMessage("Listener instance has unexpected type")
810                 .that(formatter.createListener(null))
811                 .isInstanceOf(DefaultLogger.class);
812     }
813 
814     @Test
815     public void testXmlLoggerListener() throws IOException {
816         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
817         formatterType.setValue("xml");
818         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
819         formatter.setType(formatterType);
820         formatter.setUseFile(false);
821         assertWithMessage("Listener instance has unexpected type")
822                 .that(formatter.createListener(null))
823                 .isInstanceOf(XMLLogger.class);
824     }
825 
826     @Test
827     public void testXmlLoggerListenerWithToFile() throws IOException {
828         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
829         formatterType.setValue("xml");
830         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
831         formatter.setType(formatterType);
832         formatter.setUseFile(false);
833         formatter.setTofile(new File("target/"));
834         assertWithMessage("Listener instance has unexpected type")
835                 .that(formatter.createListener(null))
836                 .isInstanceOf(XMLLogger.class);
837     }
838 
839     @Test
840     public void testDefaultLoggerWithNullToFile() throws IOException {
841         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
842         formatter.setTofile(null);
843         assertWithMessage("Listener instance has unexpected type")
844             .that(formatter.createListener(null))
845             .isInstanceOf(DefaultLogger.class);
846     }
847 
848     @Test
849     public void testXmlLoggerWithNullToFile() throws IOException {
850         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
851         formatterType.setValue("xml");
852         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
853         formatter.setType(formatterType);
854         formatter.setTofile(null);
855         assertWithMessage("Listener instance has unexpected type")
856             .that(formatter.createListener(null))
857             .isInstanceOf(XMLLogger.class);
858     }
859 
860     @Test
861     public void testSarifLoggerListener() throws IOException {
862         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
863         formatterType.setValue("sarif");
864         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
865         formatter.setType(formatterType);
866         formatter.setUseFile(false);
867         assertWithMessage("Listener instance has unexpected type")
868                 .that(formatter.createListener(null))
869                 .isInstanceOf(SarifLogger.class);
870     }
871 
872     @Test
873     public void testSarifLoggerListenerWithToFile() throws IOException {
874         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
875         formatterType.setValue("sarif");
876         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
877         formatter.setType(formatterType);
878         formatter.setUseFile(false);
879         formatter.setTofile(new File("target/"));
880         assertWithMessage("Listener instance has unexpected type")
881                 .that(formatter.createListener(null))
882                 .isInstanceOf(SarifLogger.class);
883     }
884 
885     @Test
886     public void testSarifLoggerWithNullToFile() throws IOException {
887         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
888         formatterType.setValue("sarif");
889         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
890         formatter.setType(formatterType);
891         formatter.setTofile(null);
892         assertWithMessage("Listener instance has unexpected type")
893                 .that(formatter.createListener(null))
894                 .isInstanceOf(SarifLogger.class);
895     }
896 
897     @Test
898     public void testDestroyed() throws IOException {
899         TestRootModuleChecker.reset();
900 
901         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
902         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
903         antTask.setMaxWarnings(0);
904         antTask.execute();
905 
906         assertWithMessage("Checker is not destroyed")
907                 .that(TestRootModuleChecker.isDestroyed())
908                 .isTrue();
909     }
910 
911     @Test
912     public void testMaxWarnings() throws IOException {
913         TestRootModuleChecker.reset();
914 
915         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
916         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
917         antTask.setMaxWarnings(0);
918         antTask.execute();
919 
920         assertWithMessage("Checker is not processed")
921                 .that(TestRootModuleChecker.isProcessed())
922                 .isTrue();
923     }
924 
925     @Test
926     public final void testExecuteLogOutput() throws Exception {
927         final URL url = new File(getPath(CONFIG_FILE)).toURI().toURL();
928         final ResourceBundle bundle = ResourceBundle.getBundle(
929                 Definitions.CHECKSTYLE_BUNDLE, Locale.ROOT);
930         final String auditStartedMessage = bundle.getString(DefaultLogger.AUDIT_STARTED_MESSAGE);
931         final String auditFinishedMessage = bundle.getString(DefaultLogger.AUDIT_FINISHED_MESSAGE);
932 
933         final List<MessageLevelPair> expectedList = Arrays.asList(
934                 new MessageLevelPair("checkstyle version .*", Project.MSG_VERBOSE),
935                 new MessageLevelPair("Adding standalone file for audit", Project.MSG_VERBOSE),
936                 new MessageLevelPair("To locate the files took \\d+ ms.", Project.MSG_VERBOSE),
937                 new MessageLevelPair("Running Checkstyle  on 1 files", Project.MSG_INFO),
938                 new MessageLevelPair("Using configuration file:.*", Project.MSG_VERBOSE),
939                 new MessageLevelPair(auditStartedMessage, Project.MSG_DEBUG),
940                 new MessageLevelPair(auditFinishedMessage, Project.MSG_DEBUG),
941                 new MessageLevelPair("To process the files took \\d+ ms.", Project.MSG_VERBOSE),
942                 new MessageLevelPair("Total execution took \\d+ ms.", Project.MSG_VERBOSE)
943         );
944 
945         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
946         antTask.setProject(new Project());
947         antTask.setConfig(url.toString());
948         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
949 
950         antTask.execute();
951 
952         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
953 
954         assertWithMessage("Amount of log messages is unexpected")
955                 .that(loggedMessages)
956                 .hasSize(expectedList.size());
957 
958         for (int i = 0; i < expectedList.size(); i++) {
959             final MessageLevelPair expected = expectedList.get(i);
960             final MessageLevelPair actual = loggedMessages.get(i);
961             assertWithMessage("Log messages should match")
962                     .that(actual.getMsg())
963                     .matches(expected.getMsg());
964             assertWithMessage("Log levels should be equal")
965                     .that(actual.getLevel())
966                     .isEqualTo(expected.getLevel());
967         }
968     }
969 
970     @Test
971     public void testCheckerException() throws IOException {
972         final CheckstyleAntTask antTask = new CheckstyleAntTaskStub();
973         antTask.setConfig(getPath(CONFIG_FILE));
974         antTask.setProject(new Project());
975         antTask.setFile(new File(""));
976         final BuildException ex = getExpectedThrowable(BuildException.class,
977                 antTask::execute,
978                 "BuildException is expected");
979         assertWithMessage("Error message is unexpected")
980                 .that(ex)
981                 .hasMessageThat()
982                         .startsWith("Unable to process files:");
983     }
984 
985     @Test
986     public void testLoggedTime() throws IOException {
987         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
988         antTask.setConfig(getPath(CONFIG_FILE));
989         antTask.setProject(new Project());
990         antTask.setFile(new File(getPath(FLAWLESS_INPUT)));
991         final long startTime = System.currentTimeMillis();
992         antTask.execute();
993         final long endTime = System.currentTimeMillis();
994         final long testingTime = endTime - startTime;
995         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
996 
997         assertLoggedTime(loggedMessages, testingTime, "Total execution");
998         assertLoggedTime(loggedMessages, testingTime, "To locate the files");
999         assertLoggedTime(loggedMessages, testingTime, "To process the files");
1000     }
1001 
1002     private static void assertLoggedTime(List<MessageLevelPair> loggedMessages,
1003                                          long testingTime, String expectedMsg) {
1004 
1005         final Optional<MessageLevelPair> optionalMessageLevelPair = loggedMessages.stream()
1006             .filter(msg -> msg.getMsg().startsWith(expectedMsg))
1007             .findFirst();
1008 
1009         assertWithMessage("Message should be present.")
1010             .that(optionalMessageLevelPair.isPresent())
1011             .isTrue();
1012 
1013         final long actualTime = getNumberFromLine(optionalMessageLevelPair.orElseThrow().getMsg());
1014 
1015         assertWithMessage("Logged time in '%s' must be less than the testing time", expectedMsg)
1016             .that(actualTime)
1017             .isAtMost(testingTime);
1018     }
1019 
1020     private static List<String> readWholeFile(File outputFile) throws IOException {
1021         return Files.readAllLines(outputFile.toPath(), StandardCharsets.UTF_8);
1022     }
1023 
1024     private static long getNumberFromLine(String line) {
1025         final Matcher matcher = Pattern.compile("(\\d+)").matcher(line);
1026         matcher.find();
1027         return Long.parseLong(matcher.group(1));
1028     }
1029 
1030     @Test
1031     public void testMaxWarningDefault() throws IOException {
1032         final CheckstyleAntTask antTask = getCheckstyleAntTask();
1033         final File inputFile = new File(getPath(WARNING_INPUT));
1034         final Location fileLocation = new Location("build.xml", 42, 10);
1035 
1036         antTask.setFile(inputFile);
1037         antTask.setLocation(fileLocation);
1038         assertDoesNotThrow(antTask::execute, "BuildException is not expected");
1039     }
1040 
1041     @Test
1042     public void testMultipleFormattersProduceOutputs() throws IOException {
1043         final CheckstyleAntTask antTask = getCheckstyleAntTask();
1044         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
1045         antTask.setFailOnViolation(false);
1046 
1047         final File firstOutput = new File(temporaryFolder, "ant_task_multi_formatter_1.txt");
1048         final File secondOutput = new File(temporaryFolder, "ant_task_multi_formatter_2.txt");
1049 
1050         antTask.addFormatter(createPlainFormatter(firstOutput));
1051         antTask.addFormatter(createPlainFormatter(secondOutput));
1052 
1053         antTask.execute();
1054 
1055         assertWithMessage("First formatter output was not created")
1056                 .that(firstOutput.exists())
1057                 .isTrue();
1058         assertWithMessage("First formatter output is empty")
1059                 .that(firstOutput.length())
1060                 .isGreaterThan(0L);
1061         assertWithMessage("Second formatter output was not created")
1062                 .that(secondOutput.exists())
1063                 .isTrue();
1064         assertWithMessage("Second formatter output is empty")
1065                 .that(secondOutput.length())
1066                 .isGreaterThan(0L);
1067     }
1068 
1069     @Test
1070     public void testExceptionMessageContainsFileList() throws Exception {
1071         final CheckstyleAntTask antTask = new CheckstyleAntTaskStub();
1072         antTask.setConfig(getPath(CONFIG_FILE));
1073         antTask.setProject(new Project());
1074 
1075         final File file = new File(getPath(FLAWLESS_INPUT));
1076         antTask.setFile(file);
1077 
1078         final BuildException ex = getExpectedThrowable(
1079                 BuildException.class, antTask::execute, "BuildException is expected");
1080 
1081         assertWithMessage("Exception message must contain the file name")
1082                 .that(ex.getMessage())
1083                 .contains(file.getName());
1084     }
1085 
1086     @Test
1087     public void testAntProjectPropertyValueIsCopiedCorrectly() throws IOException {
1088         TestRootModuleChecker.reset();
1089 
1090         final CheckstyleAntTask antTask = getCheckstyleAntTask(CUSTOM_ROOT_CONFIG_FILE);
1091 
1092         final Project project = new Project();
1093         project.setProperty("lineLength.severity", "ignore");
1094         antTask.setProject(project);
1095 
1096         antTask.setFile(new File(getPath(VIOLATED_INPUT)));
1097 
1098         antTask.execute();
1099 
1100         assertWithMessage("Failed to propagate Ant project property value correctly")
1101                 .that(TestRootModuleChecker.getProperty())
1102                 .isEqualTo("ignore");
1103     }
1104 
1105     @Test
1106     public final void testFileSetWithLogIndexVerification() throws IOException {
1107         // given
1108         TestRootModuleChecker.reset();
1109 
1110         final CheckstyleAntTaskLogStub antTask = new CheckstyleAntTaskLogStub();
1111         antTask.setConfig(getPath(CUSTOM_ROOT_CONFIG_FILE));
1112         antTask.setProject(new Project());
1113 
1114         final FileSet fileSet = new FileSet();
1115         fileSet.setFile(new File(getPath(FLAWLESS_INPUT)));
1116         antTask.addFileset(fileSet);
1117 
1118         // when
1119         antTask.scanFileSets();
1120 
1121         // then
1122         final List<MessageLevelPair> loggedMessages = antTask.getLoggedMessages();
1123 
1124         assertWithMessage("Log message with correct index was not found")
1125                 .that(loggedMessages.stream().filter(
1126                         msg -> msg.getMsg().startsWith("0) Adding 1 files from directory")).count())
1127                 .isEqualTo(1);
1128     }
1129 
1130     private static CheckstyleAntTask.Formatter createPlainFormatter(File outputFile) {
1131         final CheckstyleAntTask.Formatter formatter = new CheckstyleAntTask.Formatter();
1132         formatter.setTofile(outputFile);
1133         final CheckstyleAntTask.FormatterType formatterType = new CheckstyleAntTask.FormatterType();
1134         formatterType.setValue("plain");
1135         formatter.setType(formatterType);
1136         return formatter;
1137     }
1138 
1139 }