1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.AbstractPathTestSupport.addEndOfLine;
24  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.isUtilsClassHasPrivateConstructor;
25  import static org.junit.jupiter.api.Assumptions.assumeTrue;
26  import static org.mockito.Mockito.mock;
27  import static org.mockito.Mockito.mockStatic;
28  import static org.mockito.Mockito.verify;
29  
30  import java.io.BufferedReader;
31  import java.io.ByteArrayOutputStream;
32  import java.io.File;
33  import java.io.IOException;
34  import java.io.PrintStream;
35  import java.io.Serial;
36  import java.nio.charset.StandardCharsets;
37  import java.nio.file.Files;
38  import java.nio.file.Path;
39  import java.util.ArrayList;
40  import java.util.List;
41  import java.util.Locale;
42  import java.util.logging.Handler;
43  import java.util.logging.Level;
44  import java.util.logging.Logger;
45  import java.util.regex.Pattern;
46  import java.util.stream.Collectors;
47  
48  import org.itsallcode.io.Capturable;
49  import org.itsallcode.junit.sysextensions.SystemErrGuard;
50  import org.itsallcode.junit.sysextensions.SystemErrGuard.SysErr;
51  import org.itsallcode.junit.sysextensions.SystemOutGuard;
52  import org.itsallcode.junit.sysextensions.SystemOutGuard.SysOut;
53  import org.junit.jupiter.api.BeforeEach;
54  import org.junit.jupiter.api.Test;
55  import org.junit.jupiter.api.extension.ExtendWith;
56  import org.junit.jupiter.api.io.TempDir;
57  import org.mockito.MockedStatic;
58  import org.mockito.Mockito;
59  
60  import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
61  import com.puppycrawl.tools.checkstyle.api.AuditListener;
62  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
63  import com.puppycrawl.tools.checkstyle.api.Violation;
64  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestRootModuleChecker;
65  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
66  import com.puppycrawl.tools.checkstyle.utils.ChainedPropertyUtil;
67  
68  @ExtendWith({SystemErrGuard.class, SystemOutGuard.class})
69  public class MainTest {
70  
71      private static final String SHORT_USAGE = String.format(Locale.ROOT,
72              "Usage: checkstyle [OPTIONS]... file(s) or folder(s) ...%n"
73              + "Try 'checkstyle --help' for more information.%n");
74  
75      private static final String USAGE = String.format(Locale.ROOT,
76            "Usage: checkstyle [-dEgGhjJtTV] [-b=<xpath>] [-c=<configurationFile>] "
77                    + "[-f=<format>]%n"
78                    + "                  [-o=<outputPath>] [-p=<propertiesFile>] "
79                    + "[-s=<suppressionLineColumnNumber>]%n"
80                    + "                  [-w=<tabWidth>] [-e=<exclude>]... [-x=<excludeRegex>]... "
81                    + "<files or folders>...%n"
82                    + "Checkstyle verifies that the specified source code files adhere to the"
83                    + " specified rules. By default,%n"
84                    + "violations are reported to standard out in plain format. Checkstyle requires"
85                    + " a configuration XML%n"
86                    + "file that configures the checks to apply.%n"
87                    + "      <files or folders>... One or more source files to verify%n"
88                    + "  -b, --branch-matching-xpath=<xpath>%n"
89                    + "                            Shows Abstract Syntax Tree(AST) branches that"
90                    + " match given XPath query.%n"
91                    + "  -c=<configurationFile>    Specifies the location of the file that defines"
92                    + " the configuration%n"
93                    + "                              modules. The location can either be a"
94                    + " filesystem location, or a name%n"
95                    + "                              passed to the ClassLoader.getResource()"
96                    + " method.%n"
97                    + "  -d, --debug               Prints all debug logging of CheckStyle utility.%n"
98                    + "  -e, --exclude=<exclude>   Directory/file to exclude from CheckStyle. The"
99                    + " path can be the full,%n"
100                   + "                              absolute path, or relative to the current"
101                   + " path. Multiple excludes are%n"
102                   + "                              allowed.%n"
103                   + "  -E, --executeIgnoredModules%n"
104                   + "                            Allows ignored modules to be run.%n"
105                   + "  -f=<format>               Specifies the output format. Valid values: "
106                   + "xml, sarif, plain for%n"
107                   + "                              XMLLogger, SarifLogger, and "
108                   + "DefaultLogger respectively. Defaults to%n"
109                   + "                              plain.%n"
110                   + "  -g, --generate-xpath-suppression%n"
111                   + "                            Generates to output a xpath suppression xml to use"
112                   + " to suppress all%n"
113                   + "                              violations from user's config. Instead of"
114                   + " printing every violation,%n"
115                   + "                              all violations will be catched and single"
116                   + " suppressions xml file will%n"
117                   + "                              be printed out. Used only with -c option. Output"
118                   + " location can be%n"
119                   + "                              specified with -o option.%n"
120                   + "  -G, --generate-checks-and-files-suppression%n"
121                   + "                            Generates to output a suppression xml that will"
122                   + " have suppress elements%n"
123                   + "                              with \"checks\" and \"files\" attributes only to"
124                   + " use to suppress all%n"
125                   + "                              violations from user's config. Instead of"
126                   + " printing every violation,%n"
127                   + "                              all violations will be catched and single"
128                   + " suppressions xml file will%n"
129                   + "                              be printed out. Used only with -c option. Output"
130                   + " location can be%n"
131                   + "                              specified with -o option.%n"
132                   + "  -h, --help                Show this help message and exit.%n"
133                   + "  -j, --javadocTree         This option is used to print the Parse Tree of"
134                   + " the Javadoc comment. The%n"
135                   + "                              file has to contain only Javadoc comment"
136                   + " content excluding '/**' and%n"
137                   + "                              '*/' at the beginning and at the end"
138                   + " respectively. It can only be%n"
139                   + "                              used on a single file and cannot be"
140                   + " combined with other options.%n"
141                   + "  -J, --treeWithJavadoc     This option is used to display the Abstract"
142                   + " Syntax Tree (AST) with%n"
143                   + "                              Javadoc nodes of the specified file. It can"
144                   + " only be used on a single%n"
145                   + "                              file and cannot be combined"
146                   + " with other options.%n"
147                   + "  -o=<outputPath>           Sets the output file. Defaults to stdout.%n"
148                   + "  -p=<propertiesFile>       Sets the property files to load.%n"
149                   + "  -s=<suppressionLineColumnNumber>%n"
150                   + "                            Prints xpath suppressions at the file's line and"
151                   + " column position.%n"
152                   + "                              Argument is the line and column number"
153                   + " (separated by a : ) in the%n"
154                   + "                              file that the suppression should be generated"
155                   + " for. The option cannot%n"
156                   + "                              be used with other options and requires exactly"
157                   + " one file to run on to%n"
158                   + "                              be specified. Note that the generated result"
159                   + " will have few queries,%n"
160                   + "                              joined by pipe(|). Together they will match all"
161                   + " AST nodes on%n"
162                   + "                              specified line and column. You need to choose"
163                   + " only one and recheck%n"
164                   + "                              that it works. Usage of all of them is also ok,"
165                   + " but might result in%n"
166                   + "                              undesirable matching and suppress other"
167                   + " issues.%n"
168                   + "  -t, --tree                This option is used to display the Abstract"
169                   + " Syntax Tree (AST) without%n"
170                   + "                              any comments of the specified file. It can"
171                   + " only be used on a single%n"
172                   + "                              file and cannot be combined with"
173                   + " other options.%n"
174                   + "  -T, --treeWithComments    This option is used to display the Abstract"
175                   + " Syntax Tree (AST) with%n"
176                   + "                              comment nodes excluding Javadoc of the"
177                   + " specified file. It can only be%n"
178                   + "                              used on a single file and cannot be combined"
179                   + " with other options.%n"
180                   + "  -V, --version             Print version information and exit.%n"
181                   + "  -w, --tabWidth=<tabWidth> Sets the length of the tab character. Used only"
182                   + " with -s option. Default%n"
183                   + "                              value is 8.%n"
184                   + "  -x, --exclude-regexp=<excludeRegex>%n"
185                   + "                            Directory/file pattern to exclude from CheckStyle."
186                   + " Multiple excludes%n"
187                   + "                              are allowed.%n");
188 
189     private static final Logger LOG = Logger.getLogger(MainTest.class.getName()).getParent();
190     private static final Handler[] HANDLERS = LOG.getHandlers();
191     private static final Level ORIGINAL_LOG_LEVEL = LOG.getLevel();
192 
193     private static final String EOL = System.lineSeparator();
194 
195     @TempDir
196     public File temporaryFolder;
197 
198     private final LocalizedMessage auditStartMessage = new LocalizedMessage(
199             Definitions.CHECKSTYLE_BUNDLE, getClass(),
200             "DefaultLogger.auditStarted");
201 
202     private final LocalizedMessage auditFinishMessage = new LocalizedMessage(
203             Definitions.CHECKSTYLE_BUNDLE, getClass(),
204             "DefaultLogger.auditFinished");
205 
206     private final String noViolationsOutput = auditStartMessage.getMessage() + EOL
207                     + auditFinishMessage.getMessage() + EOL;
208 
209     private static String getPath(String filename) {
210         return "src/test/resources/com/puppycrawl/tools/checkstyle/main/" + filename;
211     }
212 
213     private static String getNonCompilablePath(String filename) {
214         return "src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/main/" + filename;
215     }
216 
217     private static String getFilePath(String filename) throws IOException {
218         return new File(getPath(filename)).getCanonicalPath();
219     }
220 
221     
222 
223 
224 
225 
226 
227 
228 
229 
230 
231 
232     @BeforeEach
233     public void setUp(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
234         systemErr.captureMuted();
235         systemOut.captureMuted();
236 
237         System.setProperty("picocli.ansi", "false");
238 
239         LOG.setLevel(ORIGINAL_LOG_LEVEL);
240 
241         for (Handler handler : LOG.getHandlers()) {
242             boolean found = false;
243 
244             for (Handler savedHandler : HANDLERS) {
245                 if (handler == savedHandler) {
246                     found = true;
247                     break;
248                 }
249             }
250 
251             if (!found) {
252                 LOG.removeHandler(handler);
253             }
254         }
255     }
256 
257     @Test
258     public void testIsProperUtilsClass() throws ReflectiveOperationException {
259         assertWithMessage("Constructor is not private")
260                 .that(isUtilsClassHasPrivateConstructor(Main.class))
261                 .isTrue();
262     }
263 
264     @Test
265     public void testVersionPrint(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
266         assertMainReturnCode(0, "-V");
267         assertWithMessage("Unexpected output log")
268             .that(systemOut.getCapturedData())
269             .isEqualTo("Checkstyle version: null" + System.lineSeparator());
270         assertWithMessage("Unexpected system error log")
271             .that(systemErr.getCapturedData())
272             .isEqualTo("");
273     }
274 
275     @Test
276     public void testUsageHelpPrint(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
277         assertMainReturnCode(0, "-h");
278         assertWithMessage("Unexpected output log")
279             .that(systemOut.getCapturedData())
280             .isEqualTo(USAGE);
281         assertWithMessage("Unexpected system error log")
282             .that(systemErr.getCapturedData())
283             .isEqualTo("");
284     }
285 
286     @Test
287     public void testWrongArgument(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
288         
289         
290         
291         assertMainReturnCode(-1, "-q", "file");
292         final String usage = "Unknown option: '-q'" + EOL + SHORT_USAGE;
293         assertWithMessage("Unexpected output log")
294             .that(systemOut.getCapturedData())
295             .isEqualTo("");
296         assertWithMessage("Unexpected system error log")
297             .that(systemErr.getCapturedData())
298             .isEqualTo(usage);
299     }
300 
301     @Test
302     public void testWrongArgumentMissingFiles(@SysErr Capturable systemErr,
303             @SysOut Capturable systemOut) {
304         assertMainReturnCode(-1, "-q");
305         
306         
307         final String usage = "Missing required parameter: '<files or folders>'" + EOL + SHORT_USAGE;
308         assertWithMessage("Unexpected output log")
309             .that(systemOut.getCapturedData())
310             .isEqualTo("");
311         assertWithMessage("Unexpected system error log")
312             .that(systemErr.getCapturedData())
313             .isEqualTo(usage);
314     }
315 
316     @Test
317     public void testNoConfigSpecified(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
318         assertMainReturnCode(-1, getPath("InputMain.java"));
319         assertWithMessage("Unexpected output log")
320             .that(systemOut.getCapturedData())
321             .isEqualTo("Must specify a config XML file." + System.lineSeparator());
322         assertWithMessage("Unexpected system error log")
323             .that(systemErr.getCapturedData())
324             .isEqualTo("");
325     }
326 
327     @Test
328     public void testNonExistentTargetFile(@SysErr Capturable systemErr,
329             @SysOut Capturable systemOut) {
330         assertMainReturnCode(-1, "-c", "/google_checks.xml", "NonExistentFile.java");
331         assertWithMessage("Unexpected output log")
332             .that(systemOut.getCapturedData())
333             .isEqualTo("Files to process must be specified, found 0." + System.lineSeparator());
334         assertWithMessage("Unexpected system error log")
335             .that(systemErr.getCapturedData())
336             .isEqualTo("");
337     }
338 
339     @Test
340     public void testExistingTargetFileButWithoutReadAccess(
341             @SysErr Capturable systemErr, @SysOut Capturable systemOut) throws IOException {
342         final File file = Files.createTempFile(temporaryFolder.toPath(),
343                 "testExistingTargetFileButWithoutReadAccess", null).toFile();
344         
345         
346         assumeTrue(file.setReadable(false), "file is still readable");
347 
348         final String canonicalPath = file.getCanonicalPath();
349         assertMainReturnCode(-1, "-c", "/google_checks.xml", canonicalPath);
350         assertWithMessage("Unexpected output log")
351             .that(systemOut.getCapturedData())
352             .isEqualTo("Files to process must be specified, found 0." + System.lineSeparator());
353         assertWithMessage("Unexpected system error log")
354             .that(systemErr.getCapturedData())
355             .isEqualTo("");
356     }
357 
358     @Test
359     public void testCustomSeverityVariableForGoogleConfig(@SysOut Capturable systemOut) {
360         assertMainReturnCode(1, "-c", "/google_checks.xml",
361                 "-p", getPath("InputMainCustomSeverityForGoogleConfig.properties"),
362                 getPath("InputMainCustomSeverityForGoogleConfig.java"));
363 
364         final String expectedOutputStart = addEndOfLine(auditStartMessage.getMessage())
365             + "[ERROR] ";
366         final String expectedOutputEnd = addEndOfLine(
367                 "InputMainCustomSeverityForGoogleConfig.java:3:1:"
368                     + " Missing a Javadoc comment. [MissingJavadocType]",
369                 auditFinishMessage.getMessage());
370         assertWithMessage("Unexpected output log")
371             .that(systemOut.getCapturedData())
372             .startsWith(expectedOutputStart);
373         assertWithMessage("Unexpected output log")
374             .that(systemOut.getCapturedData())
375             .endsWith(expectedOutputEnd);
376     }
377 
378     @Test
379     public void testDefaultSeverityVariableForGoogleConfig(@SysOut Capturable systemOut) {
380         assertMainReturnCode(0, "-c", "/google_checks.xml",
381                 getPath("InputMainCustomSeverityForGoogleConfig.java"));
382 
383         final String expectedOutputStart = addEndOfLine(auditStartMessage.getMessage())
384                 + "[WARN] ";
385         final String expectedOutputEnd = addEndOfLine(
386                 "InputMainCustomSeverityForGoogleConfig.java:3:1:"
387                         + " Missing a Javadoc comment. [MissingJavadocType]",
388                 auditFinishMessage.getMessage());
389         assertWithMessage("Unexpected output log")
390                 .that(systemOut.getCapturedData())
391                 .startsWith(expectedOutputStart);
392         assertWithMessage("Unexpected output log")
393                 .that(systemOut.getCapturedData())
394                 .endsWith(expectedOutputEnd);
395     }
396 
397     @Test
398     public void testNonExistentConfigFile(@SysErr Capturable systemErr,
399             @SysOut Capturable systemOut) {
400         assertMainReturnCode(-1, "-c", "src/main/resources/non_existent_config.xml",
401                     getPath("InputMain.java"));
402         assertWithMessage("Unexpected output log")
403             .that(systemOut.getCapturedData())
404             .isEqualTo(addEndOfLine("Could not find config XML file "
405                     + "'src/main/resources/non_existent_config.xml'."));
406         assertWithMessage("Unexpected system error log")
407             .that(systemErr.getCapturedData())
408             .isEqualTo("");
409     }
410 
411     @Test
412     public void testNonExistentOutputFormat(@SysErr Capturable systemErr,
413             @SysOut Capturable systemOut) {
414         assertMainReturnCode(-1, "-c", "/google_checks.xml", "-f", "xmlp",
415                 getPath("InputMain.java"));
416         assertWithMessage("Unexpected output log")
417             .that(systemOut.getCapturedData())
418             .isEqualTo("");
419         assertWithMessage("Unexpected system error log")
420             .that(systemErr.getCapturedData())
421             .isEqualTo("Invalid value for option '-f': expected one of [XML, SARIF, PLAIN]"
422                     + " (case-insensitive) but was 'xmlp'" + EOL + SHORT_USAGE);
423     }
424 
425     @Test
426     public void testNonExistentClass(@SysErr Capturable systemErr) {
427         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-non-existent-classname.xml"),
428                     getPath("InputMain.java"));
429         final String cause = "com.puppycrawl.tools.checkstyle.api.CheckstyleException:"
430                 + " cannot initialize module TreeWalker - ";
431         assertWithMessage("Unexpected system error log")
432                 .that(systemErr.getCapturedData())
433                 .startsWith(cause);
434     }
435 
436     @Test
437     public void testExistingTargetFile(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
438         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"),
439                 getPath("InputMain.java"));
440         assertWithMessage("Unexpected output log")
441             .that(systemOut.getCapturedData())
442             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
443                 auditFinishMessage.getMessage()));
444         assertWithMessage("Unexpected system error log")
445             .that(systemErr.getCapturedData())
446             .isEqualTo("");
447     }
448 
449     @Test
450     public void testExistingTargetFileXmlOutput(@SysErr Capturable systemErr,
451             @SysOut Capturable systemOut) throws IOException {
452         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"), "-f", "xml",
453                 getPath("InputMain.java"));
454         final String expectedPath = getFilePath("InputMain.java");
455         final String version = Main.class.getPackage().getImplementationVersion();
456         assertWithMessage("Unexpected output log")
457             .that(systemOut.getCapturedData())
458             .isEqualTo(addEndOfLine(
459             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
460                 "<checkstyle version=\"" + version + "\">",
461                 "<file name=\"" + expectedPath + "\">",
462                 "</file>",
463                 "</checkstyle>"));
464         assertWithMessage("Unexpected system error log")
465             .that(systemErr.getCapturedData())
466             .isEqualTo("");
467     }
468 
469     
470 
471 
472 
473 
474 
475 
476 
477 
478     @Test
479     public void testNonClosedSystemStreams(@SysErr Capturable systemErr,
480            @SysOut Capturable systemOut) {
481         try (ShouldNotBeClosedStream stream = new ShouldNotBeClosedStream()) {
482             System.setOut(stream);
483             System.setErr(stream);
484             assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"), "-f", "xml",
485                     getPath("InputMain.java"));
486             assertWithMessage("stream should not be closed")
487                 .that(stream.isClosed)
488                 .isFalse();
489             assertWithMessage("System.err should be not used")
490                 .that(systemErr.getCapturedData())
491                 .isEmpty();
492             assertWithMessage("System.out should be not used")
493                 .that(systemOut.getCapturedData())
494                 .isEmpty();
495         }
496     }
497 
498     
499 
500 
501 
502 
503 
504 
505 
506 
507     @Test
508     public void testGetOutputStreamOptionsMethod() throws Exception {
509         final Path path = new File(getPath("InputMain.java")).toPath();
510         final OutputStreamOptions option =
511                 TestUtil.invokeStaticMethod(Main.class, "getOutputStreamOptions",
512                         OutputStreamOptions.class, path);
513         assertWithMessage("Main.getOutputStreamOptions return CLOSE on not null Path")
514                 .that(option)
515                 .isEqualTo(OutputStreamOptions.CLOSE);
516     }
517 
518     @Test
519     public void testExistingTargetFilePlainOutput(@SysErr Capturable systemErr,
520             @SysOut Capturable systemOut) {
521         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"), "-f", "plain",
522                 getPath("InputMain.java"));
523         assertWithMessage("Unexpected output log")
524             .that(systemOut.getCapturedData())
525             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
526                 auditFinishMessage.getMessage()));
527         assertWithMessage("Unexpected system error log")
528             .that(systemErr.getCapturedData())
529             .isEqualTo("");
530     }
531 
532     @Test
533     public void testExistingTargetFileWithViolations(@SysErr Capturable systemErr,
534             @SysOut Capturable systemOut) throws IOException {
535         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname2.xml"),
536                 getPath("InputMain.java"));
537         final Violation invalidPatternMessageMain = new Violation(1,
538                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
539                 "name.invalidPattern", new String[] {"InputMain", "^[a-z0-9]*$"},
540                 null, getClass(), null);
541         final Violation invalidPatternMessageMainInner = new Violation(1,
542                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
543                 "name.invalidPattern", new String[] {"InputMainInner", "^[a-z0-9]*$"},
544                 null, getClass(), null);
545         final String expectedPath = getFilePath("InputMain.java");
546         assertWithMessage("Unexpected output log")
547             .that(systemOut.getCapturedData())
548             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
549                     "[WARN] " + expectedPath + ":3:14: "
550                         + invalidPatternMessageMain.getViolation()
551                         + " [TypeName]",
552                     "[WARN] " + expectedPath + ":5:7: "
553                         + invalidPatternMessageMainInner.getViolation()
554                         + " [TypeName]",
555                     auditFinishMessage.getMessage()));
556         assertWithMessage("Unexpected system error log")
557             .that(systemErr.getCapturedData())
558             .isEqualTo("");
559     }
560 
561     @Test
562     public void testViolationsByGoogleAndXpathSuppressions(@SysErr Capturable systemErr,
563             @SysOut Capturable systemOut) {
564         System.setProperty("org.checkstyle.google.suppressionxpathfilter.config",
565                 getPath("InputMainViolationsForGoogleXpathSuppressions.xml"));
566         assertMainReturnCode(0, "-c", "/google_checks.xml",
567                 getPath("InputMainViolationsForGoogle.java"));
568         assertWithMessage("Unexpected output log")
569             .that(systemOut.getCapturedData())
570             .isEqualTo(noViolationsOutput);
571         assertWithMessage("Unexpected system error log")
572             .that(systemErr.getCapturedData())
573             .isEqualTo("");
574     }
575 
576     @Test
577     public void testViolationsByGoogleAndSuppressions(@SysErr Capturable systemErr,
578             @SysOut Capturable systemOut) {
579         System.setProperty("org.checkstyle.google.suppressionfilter.config",
580                 getPath("InputMainViolationsForGoogleSuppressions.xml"));
581         assertMainReturnCode(0, "-c", "/google_checks.xml",
582                 getPath("InputMainViolationsForGoogle.java"));
583         assertWithMessage("Unexpected output log")
584             .that(systemOut.getCapturedData())
585             .isEqualTo(noViolationsOutput);
586         assertWithMessage("Unexpected system error log")
587             .that(systemErr.getCapturedData())
588             .isEqualTo("");
589     }
590 
591     @Test
592     public void testExistingTargetFileWithError(@SysErr Capturable systemErr,
593             @SysOut Capturable systemOut) throws Exception {
594         assertMainReturnCode(2, "-c", getPath("InputMainConfig-classname2-error.xml"),
595                     getPath("InputMain.java"));
596         final Violation errorCounterTwoMessage = new Violation(1,
597                 Definitions.CHECKSTYLE_BUNDLE, Main.ERROR_COUNTER,
598                 new String[] {String.valueOf(2)}, null, getClass(), null);
599         final Violation invalidPatternMessageMain = new Violation(1,
600                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
601                 "name.invalidPattern", new String[] {"InputMain", "^[a-z0-9]*$"},
602                 null, getClass(), null);
603         final Violation invalidPatternMessageMainInner = new Violation(1,
604                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
605                 "name.invalidPattern", new String[] {"InputMainInner", "^[a-z0-9]*$"},
606                 null, getClass(), null);
607         final String expectedPath = getFilePath("InputMain.java");
608         assertWithMessage("Unexpected output log")
609             .that(systemOut.getCapturedData())
610             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
611                     "[ERROR] " + expectedPath + ":3:14: "
612                         + invalidPatternMessageMain.getViolation() + " [TypeName]",
613                     "[ERROR] " + expectedPath + ":5:7: "
614                         + invalidPatternMessageMainInner.getViolation() + " [TypeName]",
615                     auditFinishMessage.getMessage()));
616         assertWithMessage("Unexpected system error log")
617             .that(systemErr.getCapturedData())
618             .isEqualTo(addEndOfLine(errorCounterTwoMessage.getViolation()));
619     }
620 
621     
622 
623 
624 
625 
626 
627 
628     @Test
629     public void testExistingTargetFileWithOneError(@SysErr Capturable systemErr,
630             @SysOut Capturable systemOut) throws Exception {
631         assertMainReturnCode(1, "-c", getPath("InputMainConfig-classname2-error.xml"),
632                     getPath("InputMain1.java"));
633         final Violation errorCounterTwoMessage = new Violation(1,
634                 Definitions.CHECKSTYLE_BUNDLE, Main.ERROR_COUNTER,
635                 new String[] {String.valueOf(1)}, null, getClass(), null);
636         final Violation invalidPatternMessageMain = new Violation(1,
637                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
638                 "name.invalidPattern", new String[] {"InputMain1", "^[a-z0-9]*$"},
639                 null, getClass(), null);
640         final String expectedPath = getFilePath("InputMain1.java");
641         assertWithMessage("Unexpected output log")
642             .that(systemOut.getCapturedData())
643             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
644                     "[ERROR] " + expectedPath + ":3:14: "
645                         + invalidPatternMessageMain.getViolation() + " [TypeName]",
646                     auditFinishMessage.getMessage()));
647         assertWithMessage("Unexpected system error log")
648             .that(systemErr.getCapturedData())
649             .isEqualTo(addEndOfLine(errorCounterTwoMessage.getViolation()));
650     }
651 
652     @Test
653     public void testExistingTargetFileWithOneErrorAgainstSunCheck(@SysErr Capturable systemErr,
654             @SysOut Capturable systemOut) throws Exception {
655         assertMainReturnCode(1, "-c", "/sun_checks.xml", getPath("InputMain1.java"));
656         final Violation errorCounterTwoMessage = new Violation(1,
657                 Definitions.CHECKSTYLE_BUNDLE, Main.ERROR_COUNTER,
658                 new String[] {String.valueOf(1)}, null, getClass(), null);
659         final Violation message = new Violation(1,
660                 "com.puppycrawl.tools.checkstyle.checks.javadoc.messages",
661                 "javadoc.packageInfo", new String[] {},
662                 null, getClass(), null);
663         final String expectedPath = getFilePath("InputMain1.java");
664         assertWithMessage("Unexpected output log")
665             .that(systemOut.getCapturedData())
666             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
667                 "[ERROR] " + expectedPath + ":1: " + message.getViolation() + " [JavadocPackage]",
668                 auditFinishMessage.getMessage()));
669         assertWithMessage("Unexpected system error log")
670             .that(systemErr.getCapturedData())
671             .isEqualTo(addEndOfLine(errorCounterTwoMessage.getViolation()));
672     }
673 
674     @Test
675     public void testExistentTargetFilePlainOutputToNonExistentFile(@SysErr Capturable systemErr,
676             @SysOut Capturable systemOut) {
677         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"), "-f", "plain",
678                 "-o", temporaryFolder + "/output.txt", getPath("InputMain.java"));
679         assertWithMessage("Unexpected output log")
680             .that(systemOut.getCapturedData())
681             .isEqualTo("");
682         assertWithMessage("Unexpected system error log")
683             .that(systemErr.getCapturedData())
684             .isEqualTo("");
685     }
686 
687     @Test
688     public void testExistingTargetFilePlainOutputToFile(@SysErr Capturable systemErr,
689             @SysOut Capturable systemOut) throws Exception {
690         final String outputFile =
691                 Files.createTempFile(temporaryFolder.toPath(), "file", ".output").toFile()
692                     .getCanonicalPath();
693         assertWithMessage("File must exist")
694                 .that(new File(outputFile).exists())
695                 .isTrue();
696         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"), "-f", "plain",
697                 "-o", outputFile, getPath("InputMain.java"));
698         assertWithMessage("Unexpected output log")
699             .that(systemOut.getCapturedData())
700             .isEqualTo("");
701         assertWithMessage("Unexpected system error log")
702             .that(systemErr.getCapturedData())
703             .isEqualTo("");
704     }
705 
706     @Test
707     public void testCreateNonExistentOutputFile() throws IOException {
708         final String outputFile = new File(temporaryFolder, "nonexistent.out").getCanonicalPath();
709         assertWithMessage("File must not exist")
710                 .that(new File(outputFile).exists())
711                 .isFalse();
712         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname.xml"), "-f", "plain",
713                 "-o", outputFile, getPath("InputMain.java"));
714         assertWithMessage("File must exist")
715                 .that(new File(outputFile).exists())
716                 .isTrue();
717     }
718 
719     @Test
720     public void testExistingTargetFilePlainOutputProperties(@SysErr Capturable systemErr,
721             @SysOut Capturable systemOut) {
722         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname-prop.xml"),
723                 "-p", getPath("InputMainMycheckstyle.properties"), getPath("InputMain.java"));
724         assertWithMessage("Unexpected output log")
725             .that(systemOut.getCapturedData())
726             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
727                 auditFinishMessage.getMessage()));
728         assertWithMessage("Unexpected system error log")
729             .that(systemErr.getCapturedData())
730             .isEqualTo("");
731     }
732 
733     @Test
734     public void testPropertyFileWithPropertyChaining(@SysErr Capturable systemErr,
735             @SysOut Capturable systemOut) {
736         assertMainReturnCode(0, "-c", getPath("InputMainConfig-classname-prop.xml"),
737             "-p", getPath("InputMainPropertyChaining.properties"), getPath("InputMain.java"));
738 
739         assertWithMessage("Unexpected output log")
740             .that(systemOut.getCapturedData())
741             .isEqualTo(addEndOfLine(auditStartMessage.getMessage(),
742                 auditFinishMessage.getMessage()));
743         assertWithMessage("Unexpected system error log")
744             .that(systemErr.getCapturedData())
745             .isEqualTo("");
746     }
747 
748     @Test
749     public void testPropertyFileWithPropertyChainingUndefinedProperty(@SysErr Capturable systemErr,
750             @SysOut Capturable systemOut) {
751         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-classname-prop.xml"),
752                 "-p", getPath("InputMainPropertyChainingUndefinedProperty.properties"),
753                 getPath("InputMain.java"));
754 
755         assertWithMessage("Invalid error message")
756             .that(systemErr.getCapturedData())
757             .contains(ChainedPropertyUtil.UNDEFINED_PROPERTY_MESSAGE);
758         assertWithMessage("Unexpected output log")
759             .that(systemOut.getCapturedData())
760             .isEqualTo("");
761     }
762 
763     @Test
764     public void testExistingTargetFilePlainOutputNonexistentProperties(@SysErr Capturable systemErr,
765             @SysOut Capturable systemOut) {
766         assertMainReturnCode(-1, "-c", getPath("InputMainConfig-classname-prop.xml"),
767                     "-p", "nonexistent.properties", getPath("InputMain.java"));
768         assertWithMessage("Unexpected output log")
769             .that(systemOut.getCapturedData())
770             .isEqualTo("Could not find file 'nonexistent.properties'."
771                 + System.lineSeparator());
772         assertWithMessage("Unexpected system error log")
773             .that(systemErr.getCapturedData())
774             .isEqualTo("");
775     }
776 
777     @Test
778     public void testExistingIncorrectConfigFile(@SysErr Capturable systemErr) {
779         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-Incorrect.xml"),
780                 getPath("InputMain.java"));
781         final String errorOutput = "com.puppycrawl.tools.checkstyle.api."
782             + "CheckstyleException: unable to parse configuration stream - ";
783         assertWithMessage("Unexpected system error log")
784                 .that(systemErr.getCapturedData())
785                 .startsWith(errorOutput);
786     }
787 
788     @Test
789     public void testExistingIncorrectChildrenInConfigFile(@SysErr Capturable systemErr) {
790         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-incorrectChildren.xml"),
791                     getPath("InputMain.java"));
792         final String errorOutput = "com.puppycrawl.tools.checkstyle.api."
793                 + "CheckstyleException: cannot initialize module RegexpSingleline"
794                 + " - RegexpSingleline is not allowed as a child in RegexpSingleline";
795         assertWithMessage("Unexpected system error log")
796                 .that(systemErr.getCapturedData())
797                 .startsWith(errorOutput);
798     }
799 
800     @Test
801     public void testExistingIncorrectChildrenInConfigFile2(@SysErr Capturable systemErr) {
802         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-incorrectChildren2.xml"),
803                     getPath("InputMain.java"));
804         final String errorOutput = "com.puppycrawl.tools.checkstyle.api."
805                 + "CheckstyleException: cannot initialize module TreeWalker - "
806                 + "cannot initialize module JavadocMethod - "
807                 + "JavadocVariable is not allowed as a child in JavadocMethod";
808         assertWithMessage("Unexpected system error log")
809                 .that(systemErr.getCapturedData())
810                 .startsWith(errorOutput);
811     }
812 
813     @Test
814     public void testLoadPropertiesIoException() throws Exception {
815         final Class<?> cliOptionsClass = Class.forName(Main.class.getName());
816         try {
817             TestUtil.invokeVoidStaticMethod(cliOptionsClass,
818                     "loadProperties", new File("."));
819             assertWithMessage("Exception was expected").fail();
820         }
821         catch (ReflectiveOperationException exc) {
822             assertWithMessage("Invalid error cause")
823                     .that(exc)
824                     .hasCauseThat()
825                     .isInstanceOf(CheckstyleException.class);
826             
827             
828             
829             final Violation loadPropertiesMessage = new Violation(1,
830                     Definitions.CHECKSTYLE_BUNDLE, Main.LOAD_PROPERTIES_EXCEPTION,
831                     new String[] {""}, null, getClass(), null);
832             final String causeMessage = exc.getCause().getLocalizedMessage();
833             final String violation = loadPropertiesMessage.getViolation();
834             final boolean samePrefix = causeMessage.substring(0, causeMessage.indexOf(' '))
835                     .equals(violation
836                             .substring(0, violation.indexOf(' ')));
837             final boolean sameSuffix =
838                     causeMessage.substring(causeMessage.lastIndexOf(' '))
839                     .equals(violation
840                             .substring(violation.lastIndexOf(' ')));
841             assertWithMessage("Invalid violation")
842                     .that(samePrefix || sameSuffix)
843                     .isTrue();
844             assertWithMessage("Invalid violation")
845                     .that(causeMessage)
846                     .contains(".'");
847         }
848     }
849 
850     @Test
851     public void testExistingDirectoryWithViolations(@SysErr Capturable systemErr,
852             @SysOut Capturable systemOut) throws IOException {
853         
854         final String[][] outputValues = {
855                 {"InputMainComplexityOverflow", "1", "172"},
856         };
857 
858         final int allowedLength = 170;
859         final String msgKey = "maxLen.file";
860         final String bundle = "com.puppycrawl.tools.checkstyle.checks.sizes.messages";
861 
862         assertMainReturnCode(0, "-c", getPath("InputMainConfig-filelength.xml"),
863                 getPath(""));
864         final String expectedPath = getFilePath("") + File.separator;
865         final StringBuilder sb = new StringBuilder(28);
866         sb.append(auditStartMessage.getMessage())
867                 .append(EOL);
868         final String format = "[WARN] " + expectedPath + outputValues[0][0] + ".java:"
869                 + outputValues[0][1] + ": ";
870         for (String[] outputValue : outputValues) {
871             final String violation = new Violation(1, bundle,
872                     msgKey, new Integer[] {Integer.valueOf(outputValue[2]), allowedLength},
873                     null, getClass(), null).getViolation();
874             final String line = format + violation + " [FileLength]";
875             sb.append(line).append(EOL);
876         }
877         sb.append(auditFinishMessage.getMessage())
878                 .append(EOL);
879         assertWithMessage("Unexpected output log")
880             .that(systemOut.getCapturedData())
881             .isEqualTo(sb.toString());
882         assertWithMessage("Unexpected system error log")
883             .that(systemErr.getCapturedData())
884             .isEqualTo("");
885     }
886 
887     
888 
889 
890 
891 
892 
893 
894     @Test
895     public void testListFilesNotFile() throws Exception {
896         final File fileMock = new File("") {
897             @Serial
898             private static final long serialVersionUID = 1L;
899 
900             @Override
901             public boolean canRead() {
902                 return true;
903             }
904 
905             @Override
906             public boolean isDirectory() {
907                 return false;
908             }
909 
910             @Override
911             public boolean isFile() {
912                 return false;
913             }
914         };
915 
916         final List<File> result = TestUtil.invokeStaticMethodList(Main.class, "listFiles",
917                 fileMock, new ArrayList<>());
918         assertWithMessage("Invalid result size")
919             .that(result)
920             .isEmpty();
921     }
922 
923     
924 
925 
926 
927 
928 
929 
930     @Test
931     public void testListFilesDirectoryWithNull() throws Exception {
932         final File[] nullResult = null;
933         final File fileMock = new File("") {
934             @Serial
935             private static final long serialVersionUID = 1L;
936 
937             @Override
938             public boolean canRead() {
939                 return true;
940             }
941 
942             @Override
943             public boolean isDirectory() {
944                 return true;
945             }
946 
947             @Override
948             public File[] listFiles() {
949                 return nullResult;
950             }
951         };
952 
953         final List<File> result = TestUtil.invokeStaticMethodList(Main.class, "listFiles",
954                 fileMock, new ArrayList<>());
955         assertWithMessage("Invalid result size")
956             .that(result)
957             .isEmpty();
958     }
959 
960     @Test
961     public void testFileReferenceDuringException(@SysErr Capturable systemErr) {
962         
963         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-classname.xml"),
964                     getNonCompilablePath("InputMainIncorrectClass.java"));
965         final String exceptionMessage = addEndOfLine("com.puppycrawl.tools.checkstyle.api."
966                 + "CheckstyleException: Exception was thrown while processing "
967                 + new File(getNonCompilablePath("InputMainIncorrectClass.java")).getPath());
968         assertWithMessage("Unexpected system error log")
969                 .that(systemErr.getCapturedData())
970                 .contains(exceptionMessage);
971     }
972 
973     @Test
974     public void testRemoveLexerDefaultErrorListener(@SysErr Capturable systemErr) {
975         assertMainReturnCode(-2, "-t", getNonCompilablePath("InputMainIncorrectClass.java"));
976 
977         assertWithMessage("First line of exception message should not contain lexer error.")
978             .that(systemErr.getCapturedData().startsWith("line 2:2 token recognition error"))
979                 .isFalse();
980     }
981 
982     @Test
983     public void testRemoveParserDefaultErrorListener(@SysErr Capturable systemErr) {
984         assertMainReturnCode(-2, "-t", getNonCompilablePath("InputMainIncorrectClass.java"));
985         final String capturedData = systemErr.getCapturedData();
986 
987         assertWithMessage("First line of exception message should not contain parser error.")
988             .that(capturedData.startsWith("line 2:0 no viable alternative"))
989                 .isFalse();
990         assertWithMessage("Second line of exception message should not contain parser error.")
991             .that(capturedData.startsWith("line 2:0 no viable alternative",
992                     capturedData.indexOf('\n') + 1))
993                 .isFalse();
994     }
995 
996     @Test
997     public void testPrintTreeOnMoreThanOneFile(@SysErr Capturable systemErr,
998             @SysOut Capturable systemOut) {
999         assertMainReturnCode(-1, "-t", getPath(""));
1000         assertWithMessage("Unexpected output log")
1001             .that(systemOut.getCapturedData())
1002             .isEqualTo("Printing AST is allowed for only one file." + System.lineSeparator());
1003         assertWithMessage("Unexpected system error log")
1004             .that(systemErr.getCapturedData())
1005             .isEqualTo("");
1006     }
1007 
1008     @Test
1009     public void testPrintTreeOption(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1010         final String expected = addEndOfLine(
1011             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1012             "|--PACKAGE_DEF -> package [1:0]",
1013             "|   |--ANNOTATIONS -> ANNOTATIONS [1:39]",
1014             "|   |--DOT -> . [1:39]",
1015             "|   |   |--DOT -> . [1:28]",
1016             "|   |   |   |--DOT -> . [1:22]",
1017             "|   |   |   |   |--DOT -> . [1:11]",
1018             "|   |   |   |   |   |--IDENT -> com [1:8]",
1019             "|   |   |   |   |   `--IDENT -> puppycrawl [1:12]",
1020             "|   |   |   |   `--IDENT -> tools [1:23]",
1021             "|   |   |   `--IDENT -> checkstyle [1:29]",
1022             "|   |   `--IDENT -> main [1:40]",
1023             "|   `--SEMI -> ; [1:44]",
1024             "|--CLASS_DEF -> CLASS_DEF [3:0]",
1025             "|   |--MODIFIERS -> MODIFIERS [3:0]",
1026             "|   |   `--LITERAL_PUBLIC -> public [3:0]",
1027             "|   |--LITERAL_CLASS -> class [3:7]",
1028             "|   |--IDENT -> InputMain [3:13]",
1029             "|   `--OBJBLOCK -> OBJBLOCK [3:23]",
1030             "|       |--LCURLY -> { [3:23]",
1031             "|       `--RCURLY -> } [4:0]",
1032             "`--CLASS_DEF -> CLASS_DEF [5:0]",
1033             "    |--MODIFIERS -> MODIFIERS [5:0]",
1034             "    |--LITERAL_CLASS -> class [5:0]",
1035             "    |--IDENT -> InputMainInner [5:6]",
1036             "    `--OBJBLOCK -> OBJBLOCK [5:21]",
1037             "        |--LCURLY -> { [5:21]",
1038             "        `--RCURLY -> } [6:0]");
1039 
1040         assertMainReturnCode(0, "-t", getPath("InputMain.java"));
1041         assertWithMessage("Unexpected output log")
1042             .that(systemOut.getCapturedData())
1043             .isEqualTo(expected);
1044         assertWithMessage("Unexpected system error log")
1045             .that(systemErr.getCapturedData())
1046             .isEqualTo("");
1047     }
1048 
1049     @Test
1050     public void testPrintXpathOption(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1051         final String expected = addEndOfLine(
1052             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1053             "|--CLASS_DEF -> CLASS_DEF [3:0]",
1054             "|   `--OBJBLOCK -> OBJBLOCK [3:28]",
1055             "|       |--METHOD_DEF -> METHOD_DEF [4:4]",
1056             "|       |   `--SLIST -> { [4:20]",
1057             "|       |       |--VARIABLE_DEF -> VARIABLE_DEF [5:8]",
1058             "|       |       |   |--IDENT -> a [5:12]");
1059         assertMainReturnCode(0, "-b",
1060                 "/COMPILATION_UNIT/CLASS_DEF//METHOD_DEF[./IDENT[@text='methodOne']]"
1061                         + "//VARIABLE_DEF/IDENT",
1062                 getPath("InputMainXPath.java"));
1063         assertWithMessage("Unexpected output log")
1064             .that(systemOut.getCapturedData())
1065             .isEqualTo(expected);
1066         assertWithMessage("Unexpected system error log")
1067             .that(systemErr.getCapturedData())
1068             .isEqualTo("");
1069     }
1070 
1071     @Test
1072     public void testPrintXpathCommentNode(@SysErr Capturable systemErr,
1073             @SysOut Capturable systemOut) {
1074         final String expected = addEndOfLine(
1075             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1076             "`--CLASS_DEF -> CLASS_DEF [17:0]",
1077             "    `--OBJBLOCK -> OBJBLOCK [17:19]",
1078             "        |--CTOR_DEF -> CTOR_DEF [19:4]",
1079             "        |   |--BLOCK_COMMENT_BEGIN -> /* [18:4]");
1080         assertMainReturnCode(0, "-b", "/COMPILATION_UNIT/CLASS_DEF//BLOCK_COMMENT_BEGIN",
1081                 getPath("InputMainXPath.java"));
1082         assertWithMessage("Unexpected output log")
1083             .that(systemOut.getCapturedData())
1084             .isEqualTo(expected);
1085         assertWithMessage("Unexpected system error log")
1086             .that(systemErr.getCapturedData())
1087             .isEqualTo("");
1088     }
1089 
1090     @Test
1091     public void testPrintXpathNodeParentNull(@SysErr Capturable systemErr,
1092             @SysOut Capturable systemOut) {
1093         final String expected = addEndOfLine("COMPILATION_UNIT -> COMPILATION_UNIT [1:0]");
1094         assertMainReturnCode(0, "-b", "/COMPILATION_UNIT", getPath("InputMainXPath.java"));
1095         assertWithMessage("Unexpected output log")
1096             .that(systemOut.getCapturedData())
1097             .isEqualTo(expected);
1098         assertWithMessage("Unexpected system error log")
1099             .that(systemErr.getCapturedData())
1100             .isEqualTo("");
1101     }
1102 
1103     @Test
1104     public void testPrintXpathFullOption(
1105             @SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1106         final String expected = addEndOfLine(
1107             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1108             "|--CLASS_DEF -> CLASS_DEF [3:0]",
1109             "|   `--OBJBLOCK -> OBJBLOCK [3:28]",
1110             "|       |--METHOD_DEF -> METHOD_DEF [8:4]",
1111             "|       |   `--SLIST -> { [8:26]",
1112             "|       |       |--VARIABLE_DEF -> VARIABLE_DEF [9:8]",
1113             "|       |       |   |--IDENT -> a [9:12]");
1114         final String xpath = "/COMPILATION_UNIT/CLASS_DEF//METHOD_DEF[./IDENT[@text='method']]"
1115                 + "//VARIABLE_DEF/IDENT";
1116         assertMainReturnCode(0, "--branch-matching-xpath", xpath, getPath("InputMainXPath.java"));
1117         assertWithMessage("Unexpected output log")
1118             .that(systemOut.getCapturedData())
1119             .isEqualTo(expected);
1120         assertWithMessage("Unexpected system error log")
1121             .that(systemErr.getCapturedData())
1122             .isEqualTo("");
1123     }
1124 
1125     @Test
1126     public void testPrintXpathTwoResults(
1127             @SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1128         final String expected = addEndOfLine(
1129             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1130             "|--CLASS_DEF -> CLASS_DEF [12:0]",
1131             "|   `--OBJBLOCK -> OBJBLOCK [12:10]",
1132             "|       |--METHOD_DEF -> METHOD_DEF [13:4]",
1133             "---------",
1134             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1135             "|--CLASS_DEF -> CLASS_DEF [12:0]",
1136             "|   `--OBJBLOCK -> OBJBLOCK [12:10]",
1137             "|       |--METHOD_DEF -> METHOD_DEF [14:4]");
1138         assertMainReturnCode(0, "--branch-matching-xpath",
1139                 "/COMPILATION_UNIT/CLASS_DEF[./IDENT[@text='Two']]//METHOD_DEF",
1140                 getPath("InputMainXPath.java"));
1141         assertWithMessage("Unexpected output log")
1142             .that(systemOut.getCapturedData())
1143             .isEqualTo(expected);
1144         assertWithMessage("Unexpected system error log")
1145             .that(systemErr.getCapturedData())
1146             .isEqualTo("");
1147     }
1148 
1149     @Test
1150     public void testPrintXpathInvalidXpath(@SysErr Capturable systemErr) throws Exception {
1151         final String invalidXpath = "\\/COMPILATION_UNIT/CLASS_DEF[./IDENT[@text='Two']]"
1152                 + "//METHOD_DEF";
1153         final String filePath = getFilePath("InputMainXPath.java");
1154         assertMainReturnCode(-2, "--branch-matching-xpath", invalidXpath, filePath);
1155         final String exceptionFirstLine = addEndOfLine("com.puppycrawl.tools.checkstyle.api."
1156             + "CheckstyleException: Error during evaluation for xpath: " + invalidXpath
1157             + ", file: " + filePath);
1158         assertWithMessage("Unexpected system error log")
1159             .that(systemErr.getCapturedData())
1160             .startsWith(exceptionFirstLine);
1161     }
1162 
1163     @Test
1164     public void testPrintTreeCommentsOption(@SysErr Capturable systemErr,
1165             @SysOut Capturable systemOut) {
1166         final String expected = addEndOfLine(
1167             "COMPILATION_UNIT -> COMPILATION_UNIT [1:0]",
1168             "|--PACKAGE_DEF -> package [1:0]",
1169             "|   |--ANNOTATIONS -> ANNOTATIONS [1:39]",
1170             "|   |--DOT -> . [1:39]",
1171             "|   |   |--DOT -> . [1:28]",
1172             "|   |   |   |--DOT -> . [1:22]",
1173             "|   |   |   |   |--DOT -> . [1:11]",
1174             "|   |   |   |   |   |--IDENT -> com [1:8]",
1175             "|   |   |   |   |   `--IDENT -> puppycrawl [1:12]",
1176             "|   |   |   |   `--IDENT -> tools [1:23]",
1177             "|   |   |   `--IDENT -> checkstyle [1:29]",
1178             "|   |   `--IDENT -> main [1:40]",
1179             "|   `--SEMI -> ; [1:44]",
1180             "|--CLASS_DEF -> CLASS_DEF [3:0]",
1181             "|   |--MODIFIERS -> MODIFIERS [3:0]",
1182             "|   |   |--BLOCK_COMMENT_BEGIN -> /* [2:0]",
1183             "|   |   |   |--COMMENT_CONTENT -> comment [2:2]",
1184             "|   |   |   `--BLOCK_COMMENT_END -> */ [2:8]",
1185             "|   |   `--LITERAL_PUBLIC -> public [3:0]",
1186             "|   |--LITERAL_CLASS -> class [3:7]",
1187             "|   |--IDENT -> InputMain [3:13]",
1188             "|   `--OBJBLOCK -> OBJBLOCK [3:23]",
1189             "|       |--LCURLY -> { [3:23]",
1190             "|       `--RCURLY -> } [4:0]",
1191             "`--CLASS_DEF -> CLASS_DEF [5:0]",
1192             "    |--MODIFIERS -> MODIFIERS [5:0]",
1193             "    |--LITERAL_CLASS -> class [5:0]",
1194             "    |--IDENT -> InputMainInner [5:6]",
1195             "    `--OBJBLOCK -> OBJBLOCK [5:21]",
1196             "        |--LCURLY -> { [5:21]",
1197             "        `--RCURLY -> } [6:0]");
1198 
1199         assertMainReturnCode(0, "-T", getPath("InputMain.java"));
1200         assertWithMessage("Unexpected output log")
1201             .that(systemOut.getCapturedData())
1202             .isEqualTo(expected);
1203         assertWithMessage("Unexpected system error log")
1204             .that(systemErr.getCapturedData())
1205             .isEqualTo("");
1206     }
1207 
1208     
1209 
1210 
1211 
1212 
1213 
1214 
1215 
1216 
1217     @Test
1218     public void testPrintTreeJavadocOption(@SysErr Capturable systemErr,
1219             @SysOut Capturable systemOut) throws IOException {
1220         final String expected = Files.readString(Path.of(
1221             getPath("InputMainExpectedInputJavadocComment.txt")))
1222             .replaceAll("\\\\r\\\\n", "\\\\n").replaceAll("\r\n", "\n");
1223 
1224         assertMainReturnCode(0, "-j", getPath("InputMainJavadocComment.javadoc"));
1225         assertWithMessage("Unexpected output log")
1226             .that(systemOut.getCapturedData().replaceAll("\\\\r\\\\n", "\\\\n")
1227                         .replaceAll("\r\n", "\n"))
1228             .isEqualTo(expected);
1229         assertWithMessage("Unexpected system error log")
1230             .that(systemErr.getCapturedData())
1231             .isEqualTo("");
1232     }
1233 
1234     @Test
1235     public void testPrintSuppressionOption(@SysErr Capturable systemErr,
1236             @SysOut Capturable systemOut) {
1237         final String expected = addEndOfLine(
1238             "/COMPILATION_UNIT/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]",
1239                 "/COMPILATION_UNIT/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]"
1240                         + "/MODIFIERS",
1241                 "/COMPILATION_UNIT/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']"
1242                         + "]/LITERAL_CLASS");
1243 
1244         assertMainReturnCode(0, getPath("InputMainSuppressionsStringPrinter.java"), "-s", "3:1");
1245         assertWithMessage("Unexpected output log")
1246             .that(systemOut.getCapturedData())
1247             .isEqualTo(expected);
1248         assertWithMessage("Unexpected system error log")
1249             .that(systemErr.getCapturedData())
1250             .isEqualTo("");
1251     }
1252 
1253     @Test
1254     public void testPrintSuppressionAndTabWidthOption(@SysErr Capturable systemErr,
1255             @SysOut Capturable systemOut) {
1256         final String expected = addEndOfLine(
1257             "/COMPILATION_UNIT/CLASS_DEF"
1258                     + "[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
1259                     + "/METHOD_DEF[./IDENT[@text='getName']]"
1260                     + "/SLIST/VARIABLE_DEF[./IDENT[@text='var']]",
1261                 "/COMPILATION_UNIT/CLASS_DEF"
1262                     + "[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
1263                     + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST"
1264                     + "/VARIABLE_DEF[./IDENT[@text='var']]/MODIFIERS",
1265                 "/COMPILATION_UNIT/CLASS_DEF"
1266                     + "[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
1267                     + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST"
1268                     + "/VARIABLE_DEF[./IDENT[@text='var']]/TYPE",
1269                 "/COMPILATION_UNIT/CLASS_DEF"
1270                     + "[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
1271                     + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST"
1272                     + "/VARIABLE_DEF[./IDENT[@text='var']]/TYPE/LITERAL_INT");
1273 
1274         assertMainReturnCode(0, getPath("InputMainSuppressionsStringPrinter.java"),
1275                 "-s", "7:9", "--tabWidth", "2");
1276         assertWithMessage("Unexpected output log")
1277             .that(systemOut.getCapturedData())
1278             .isEqualTo(expected);
1279         assertWithMessage("Unexpected system error log")
1280             .that(systemErr.getCapturedData())
1281             .isEqualTo("");
1282     }
1283 
1284     @Test
1285     public void testPrintSuppressionConflictingOptionsTvsC(@SysErr Capturable systemErr,
1286             @SysOut Capturable systemOut) {
1287         assertMainReturnCode(-1, "-c", "/google_checks.xml", getPath(""), "-s", "2:4");
1288         assertWithMessage("Unexpected output log")
1289             .that(systemOut.getCapturedData())
1290             .isEqualTo("Option '-s' cannot be used with other options."
1291                 + System.lineSeparator());
1292         assertWithMessage("Unexpected system error log")
1293             .that(systemErr.getCapturedData())
1294             .isEqualTo("");
1295     }
1296 
1297     @Test
1298     public void testPrintSuppressionConflictingOptionsTvsP(@SysErr Capturable systemErr,
1299             @SysOut Capturable systemOut) {
1300         assertMainReturnCode(-1, "-p", getPath("InputMainMycheckstyle.properties"), "-s", "2:4",
1301                 getPath(""));
1302         assertWithMessage("Unexpected output log")
1303             .that(systemOut.getCapturedData())
1304             .isEqualTo("Option '-s' cannot be used with other options."
1305                 + System.lineSeparator());
1306         assertWithMessage("Unexpected system error log")
1307             .that(systemErr.getCapturedData())
1308             .isEqualTo("");
1309     }
1310 
1311     @Test
1312     public void testPrintSuppressionConflictingOptionsTvsF(@SysErr Capturable systemErr,
1313             @SysOut Capturable systemOut) {
1314         assertMainReturnCode(-1, "-f", "plain", "-s", "2:4", getPath(""));
1315         assertWithMessage("Unexpected output log")
1316             .that(systemOut.getCapturedData())
1317             .isEqualTo("Option '-s' cannot be used with other options."
1318                 + System.lineSeparator());
1319         assertWithMessage("Unexpected system error log")
1320             .that(systemErr.getCapturedData())
1321             .isEqualTo("");
1322     }
1323 
1324     @Test
1325     public void testPrintSuppressionConflictingOptionsTvsO(@SysErr Capturable systemErr,
1326             @SysOut Capturable systemOut) throws IOException {
1327         final String outputPath = new File(temporaryFolder, "file.output").getCanonicalPath();
1328 
1329         assertMainReturnCode(-1, "-o", outputPath, "-s", "2:4", getPath(""));
1330         assertWithMessage("Unexpected output log")
1331             .that(systemOut.getCapturedData())
1332             .isEqualTo("Option '-s' cannot be used with other options."
1333                 + System.lineSeparator());
1334         assertWithMessage("Unexpected system error log")
1335             .that(systemErr.getCapturedData())
1336             .isEqualTo("");
1337     }
1338 
1339     @Test
1340     public void testPrintSuppressionOnMoreThanOneFile(@SysErr Capturable systemErr,
1341             @SysOut Capturable systemOut) {
1342         assertMainReturnCode(-1, "-s", "2:4", getPath(""), getPath(""));
1343         assertWithMessage("Unexpected output log")
1344             .that(systemOut.getCapturedData())
1345             .isEqualTo("Printing xpath suppressions is allowed for only one file."
1346                 + System.lineSeparator());
1347         assertWithMessage("Unexpected system error log")
1348             .that(systemErr.getCapturedData())
1349             .isEqualTo("");
1350     }
1351 
1352     @Test
1353     public void testGenerateXpathSuppressionOptionOne(@SysErr Capturable systemErr,
1354             @SysOut Capturable systemOut) {
1355         final String expected = addEndOfLine(
1356             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1357                 "<!DOCTYPE suppressions PUBLIC",
1358                 "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
1359                     + "//EN\"",
1360                 "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">",
1361                 "<suppressions>",
1362                 "  <suppress-xpath",
1363                 "       files=\"InputMainComplexityOverflow.java\"",
1364                 "       checks=\"MissingJavadocMethodCheck\"",
1365                 "       query=\"/COMPILATION_UNIT/CLASS_DEF"
1366                     + "[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
1367                     + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]\"/>",
1368                 "  <suppress-xpath",
1369                 "       files=\"InputMainComplexityOverflow.java\"",
1370                 "       id=\"LeftCurlyEol\"",
1371                 "       query=\"/COMPILATION_UNIT/CLASS_DEF"
1372                     + "[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
1373                     + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST\"/>",
1374                 "</suppressions>");
1375 
1376         assertMainReturnCode(0, "-c", "/google_checks.xml", "--generate-xpath-suppression",
1377                 getPath("InputMainComplexityOverflow.java"));
1378         assertWithMessage("Unexpected output log")
1379             .that(systemOut.getCapturedData())
1380             .isEqualTo(expected);
1381         assertWithMessage("Unexpected system error log")
1382             .that(systemErr.getCapturedData())
1383             .isEqualTo("");
1384     }
1385 
1386     @Test
1387     public void testGenerateXpathSuppressionOptionTwo(@SysErr Capturable systemErr,
1388             @SysOut Capturable systemOut) {
1389         final String expected = addEndOfLine(
1390             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1391             "<!DOCTYPE suppressions PUBLIC",
1392             "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
1393                 + "//EN\"",
1394             "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">",
1395             "<suppressions>",
1396             "  <suppress-xpath",
1397             "       files=\"InputMainGenerateXpathSuppressions.java\"",
1398             "       checks=\"ExplicitInitializationCheck\"",
1399             "       query=\"/COMPILATION_UNIT/CLASS_DEF"
1400                 + "[./IDENT[@text='InputMainGenerateXpathSuppressions']]"
1401                 + "/OBJBLOCK/VARIABLE_DEF/IDENT[@text='low']\"/>",
1402             "  <suppress-xpath",
1403             "       files=\"InputMainGenerateXpathSuppressions.java\"",
1404             "       checks=\"IllegalThrowsCheck\"",
1405             "       query=\"/COMPILATION_UNIT/CLASS_DEF"
1406                 + "[./IDENT[@text='InputMainGenerateXpathSuppressions']]"
1407                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='test']]/LITERAL_THROWS"
1408                 + "/IDENT[@text='RuntimeException']\"/>",
1409             "  <suppress-xpath",
1410             "       files=\"InputMainGenerateXpathSuppressions.java\"",
1411             "       checks=\"NestedForDepthCheck\"",
1412             "       query=\"/COMPILATION_UNIT/CLASS_DEF"
1413                 + "[./IDENT[@text='InputMainGenerateXpathSuppressions']]"
1414                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='test']]/SLIST/LITERAL_FOR/SLIST"
1415                 + "/LITERAL_FOR/SLIST/LITERAL_FOR\"/>",
1416             "</suppressions>");
1417 
1418         assertMainReturnCode(0, "-c", getPath("InputMainConfig-xpath-suppressions.xml"),
1419                 "--generate-xpath-suppression",
1420                 getPath("InputMainGenerateXpathSuppressions.java"));
1421         assertWithMessage("Unexpected output log")
1422             .that(systemOut.getCapturedData())
1423             .isEqualTo(expected);
1424         assertWithMessage("Unexpected system error log")
1425             .that(systemErr.getCapturedData())
1426             .isEqualTo("");
1427     }
1428 
1429     @Test
1430     public void testGenerateXpathSuppressionOptionEmptyConfig(@SysErr Capturable systemErr,
1431             @SysOut Capturable systemOut) {
1432         final String expected = "";
1433 
1434         assertMainReturnCode(0, "-c", getPath("InputMainConfig-empty.xml"),
1435                 "--generate-xpath-suppression", getPath("InputMainComplexityOverflow.java"));
1436         assertWithMessage("Unexpected output log")
1437             .that(systemOut.getCapturedData())
1438             .isEqualTo(expected);
1439         assertWithMessage("Unexpected system error log")
1440             .that(systemErr.getCapturedData())
1441             .isEqualTo("");
1442     }
1443 
1444     @Test
1445     public void testGenerateXpathSuppressionOptionCustomOutput(@SysErr Capturable systemErr)
1446             throws IOException {
1447         final String expected = addEndOfLine(
1448             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1449                 "<!DOCTYPE suppressions PUBLIC",
1450                 "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
1451                     + "//EN\"",
1452                 "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">",
1453                 "<suppressions>",
1454                 "  <suppress-xpath",
1455                 "       files=\"InputMainGenerateXpathSuppressionsTabWidth.java\"",
1456                 "       checks=\"ExplicitInitializationCheck\"",
1457                 "       query=\"/COMPILATION_UNIT/CLASS_DEF[./IDENT["
1458                     + "@text='InputMainGenerateXpathSuppressionsTabWidth']]"
1459                     + "/OBJBLOCK/VARIABLE_DEF/IDENT[@text='low']\"/>",
1460                 "</suppressions>");
1461         final File file = new File(temporaryFolder, "file.output");
1462         assertMainReturnCode(0, "-c", getPath("InputMainConfig-xpath-suppressions.xml"), "-o",
1463                 file.getPath(), "--generate-xpath-suppression",
1464                 getPath("InputMainGenerateXpathSuppressionsTabWidth.java"));
1465         try (BufferedReader br = Files.newBufferedReader(file.toPath())) {
1466             final String fileContent = br.lines().collect(Collectors.joining(EOL, "", EOL));
1467             assertWithMessage("Unexpected output log")
1468                 .that(fileContent)
1469                 .isEqualTo(expected);
1470             assertWithMessage("Unexpected system error log")
1471                 .that(systemErr.getCapturedData())
1472                 .isEqualTo("");
1473         }
1474     }
1475 
1476     @Test
1477     public void testGenerateXpathSuppressionOptionDefaultTabWidth(@SysErr Capturable systemErr,
1478             @SysOut Capturable systemOut) {
1479         final String expected = addEndOfLine(
1480             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1481                 "<!DOCTYPE suppressions PUBLIC",
1482                 "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
1483                     + "//EN\"",
1484                 "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">",
1485                 "<suppressions>",
1486                 "  <suppress-xpath",
1487                 "       files=\"InputMainGenerateXpathSuppressionsTabWidth.java\"",
1488                 "       checks=\"ExplicitInitializationCheck\"",
1489                 "       query=\"/COMPILATION_UNIT/CLASS_DEF[./IDENT["
1490                     + "@text='InputMainGenerateXpathSuppressionsTabWidth']]"
1491                     + "/OBJBLOCK/VARIABLE_DEF/IDENT[@text='low']\"/>",
1492                 "</suppressions>");
1493 
1494         assertMainReturnCode(0, "-c", getPath("InputMainConfig-xpath-suppressions.xml"),
1495                 "--generate-xpath-suppression",
1496                 getPath("InputMainGenerateXpathSuppressionsTabWidth.java"));
1497         assertWithMessage("Unexpected output log")
1498             .that(systemOut.getCapturedData())
1499             .isEqualTo(expected);
1500         assertWithMessage("Unexpected system error log")
1501             .that(systemErr.getCapturedData())
1502             .isEqualTo("");
1503     }
1504 
1505     @Test
1506     public void testGenerateXpathSuppressionOptionCustomTabWidth(@SysErr Capturable systemErr,
1507             @SysOut Capturable systemOut) {
1508         final String expected = "";
1509 
1510         assertMainReturnCode(0, "-c", getPath("InputMainConfig-xpath-suppressions.xml"),
1511                 "--generate-xpath-suppression", "--tabWidth", "20",
1512                 getPath("InputMainGenerateXpathSuppressionsTabWidth.java"));
1513         assertWithMessage("Unexpected output log")
1514             .that(systemOut.getCapturedData())
1515             .isEqualTo(expected);
1516         assertWithMessage("Unexpected system error log")
1517             .that(systemErr.getCapturedData())
1518             .isEqualTo("");
1519     }
1520 
1521     @Test
1522     public void testGenerateChecksAndFilesSuppressionOptionOne(@SysErr Capturable systemErr,
1523             @SysOut Capturable systemOut) {
1524         final String expected = addEndOfLine(
1525             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1526                 "<!DOCTYPE suppressions PUBLIC",
1527                 "    \"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"",
1528                 "    \"https://checkstyle.org/dtds/suppressions_1_2.dtd\">",
1529                 "<suppressions>",
1530                 "  <suppress",
1531                 "      files=\"InputMainComplexityOverflow.java\"",
1532                 "      checks=\"MissingJavadocMethodCheck\"/>",
1533                 "  <suppress",
1534                 "      files=\"InputMainComplexityOverflow.java\"",
1535                 "      id=\"LeftCurlyEol\"/>",
1536                 "</suppressions>");
1537 
1538         assertMainReturnCode(0, "-c", "/google_checks.xml",
1539                 "--generate-checks-and-files-suppression",
1540                 getPath("InputMainComplexityOverflow.java"));
1541         assertWithMessage("Unexpected output log")
1542             .that(systemOut.getCapturedData())
1543             .isEqualTo(expected);
1544         assertWithMessage("Unexpected system error log")
1545             .that(systemErr.getCapturedData())
1546             .isEqualTo("");
1547     }
1548 
1549     @Test
1550     public void testGenerateChecksAndFilesSuppressionOptionTwo(@SysErr Capturable systemErr,
1551             @SysOut Capturable systemOut) {
1552         final String expected = addEndOfLine(
1553             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1554             "<!DOCTYPE suppressions PUBLIC",
1555             "    \"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"",
1556             "    \"https://checkstyle.org/dtds/suppressions_1_2.dtd\">",
1557             "<suppressions>",
1558             "  <suppress",
1559             "      files=\"InputMainGenerateChecksAndFilesSuppressions.java\"",
1560             "      id=\"InitializeViolation\"/>",
1561             "  <suppress",
1562             "      files=\"InputMainGenerateChecksAndFilesSuppressions.java\"",
1563             "      checks=\"IllegalThrowsCheck\"/>",
1564             "  <suppress",
1565             "      files=\"InputMainGenerateChecksAndFilesSuppressions.java\"",
1566             "      checks=\"NestedForDepthCheck\"/>",
1567             "  <suppress",
1568             "      files=\"InputMainGenerateChecksAndFilesSuppressions.java\"",
1569             "      id=\"MethodNaming\"/>",
1570             "</suppressions>");
1571 
1572         assertMainReturnCode(0, "-c", getPath("InputMainConfig-Checks-And-Files-suppressions.xml"),
1573                 "--generate-checks-and-files-suppression",
1574                 getPath("InputMainGenerateChecksAndFilesSuppressions.java"));
1575         assertWithMessage("Unexpected output log")
1576             .that(systemOut.getCapturedData())
1577             .isEqualTo(expected);
1578         assertWithMessage("Unexpected system error log")
1579             .that(systemErr.getCapturedData())
1580             .isEqualTo("");
1581     }
1582 
1583     @Test
1584     public void testGenerateChecksAndFilesSuppressionOptionEmptyConfig(@SysErr Capturable systemErr,
1585             @SysOut Capturable systemOut) {
1586         final String expected = "";
1587 
1588         assertMainReturnCode(0, "-c", getPath("InputMainConfig-empty.xml"),
1589                 "--generate-checks-and-files-suppression",
1590                 getPath("InputMainComplexityOverflow.java"));
1591         assertWithMessage("Unexpected output log")
1592             .that(systemOut.getCapturedData())
1593             .isEqualTo(expected);
1594         assertWithMessage("Unexpected system error log")
1595             .that(systemErr.getCapturedData())
1596             .isEqualTo("");
1597     }
1598 
1599     @Test
1600     public void testGenerateChecksAndFilesSuppressionOptionCustomOutput(
1601             @SysErr Capturable systemErr) throws IOException {
1602         final String expected = addEndOfLine(
1603             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1604                 "<!DOCTYPE suppressions PUBLIC",
1605                 "    \"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"",
1606                 "    \"https://checkstyle.org/dtds/suppressions_1_2.dtd\">",
1607                 "<suppressions>",
1608                 "  <suppress",
1609                 "      files=\"InputMainGenerateChecksAndFilesSuppressionsTabWidth.java\"",
1610                 "      id=\"InitializeViolation\"/>",
1611                 "</suppressions>");
1612         final File file = new File(temporaryFolder, "file.output");
1613         assertMainReturnCode(0, "-c", getPath("InputMainConfig-Checks-And-Files-suppressions.xml"),
1614                 "-o", file.getPath(), "--generate-checks-and-files-suppression",
1615                 getPath("InputMainGenerateChecksAndFilesSuppressionsTabWidth.java"));
1616         try (BufferedReader br = Files.newBufferedReader(file.toPath())) {
1617             final String fileContent = br.lines().collect(Collectors.joining(EOL, "", EOL));
1618             assertWithMessage("Unexpected output log")
1619                 .that(fileContent)
1620                 .isEqualTo(expected);
1621             assertWithMessage("Unexpected system error log")
1622                 .that(systemErr.getCapturedData())
1623                 .isEqualTo("");
1624         }
1625     }
1626 
1627     @Test
1628     public void testGenerateChecksAndFilesSuppressionOptionDefaultTabWidth(
1629             @SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1630         final String expected = addEndOfLine(
1631             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1632                 "<!DOCTYPE suppressions PUBLIC",
1633                 "    \"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"",
1634                 "    \"https://checkstyle.org/dtds/suppressions_1_2.dtd\">",
1635                 "<suppressions>",
1636                 "  <suppress",
1637                 "      files=\"InputMainGenerateChecksAndFilesSuppressionsTabWidth.java\"",
1638                 "      id=\"InitializeViolation\"/>",
1639                 "</suppressions>");
1640 
1641         assertMainReturnCode(0, "-c", getPath("InputMainConfig-Checks-And-Files-suppressions.xml"),
1642                 "--generate-checks-and-files-suppression",
1643                 getPath("InputMainGenerateChecksAndFilesSuppressionsTabWidth.java"));
1644         assertWithMessage("Unexpected output log")
1645             .that(systemOut.getCapturedData())
1646             .isEqualTo(expected);
1647         assertWithMessage("Unexpected system error log")
1648             .that(systemErr.getCapturedData())
1649             .isEqualTo("");
1650     }
1651 
1652     @Test
1653     public void testGenerateChecksAndFilesSuppressionOptionCustomTabWidth(
1654             @SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1655         final String expected = addEndOfLine(
1656             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
1657                 "<!DOCTYPE suppressions PUBLIC",
1658                 "    \"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"",
1659                 "    \"https://checkstyle.org/dtds/suppressions_1_2.dtd\">",
1660                 "<suppressions>",
1661                 "  <suppress",
1662                 "      files=\"InputMainGenerateChecksAndFilesSuppressionsTabWidth.java\"",
1663                 "      id=\"InitializeViolation\"/>",
1664                 "</suppressions>");
1665 
1666         assertMainReturnCode(0, "-c", getPath("InputMainConfig-Checks-And-Files-suppressions.xml"),
1667                 "--generate-checks-and-files-suppression", "--tabWidth", "20",
1668                 getPath("InputMainGenerateChecksAndFilesSuppressionsTabWidth.java"));
1669         assertWithMessage("Unexpected output log")
1670             .that(systemOut.getCapturedData())
1671             .isEqualTo(expected);
1672         assertWithMessage("Unexpected system error log")
1673             .that(systemErr.getCapturedData())
1674             .isEqualTo("");
1675     }
1676 
1677     
1678 
1679 
1680 
1681 
1682 
1683 
1684 
1685 
1686     @Test
1687     public void testPrintFullTreeOption(@SysErr Capturable systemErr, @SysOut Capturable systemOut)
1688             throws IOException {
1689         final String expected = Files.readString(Path.of(
1690             getPath("InputMainExpectedInputAstTreeStringPrinterJavadoc.txt")))
1691                 .replaceAll("\\\\r\\\\n", "\\\\n")
1692                 .replaceAll("\r\n", "\n");
1693 
1694         assertMainReturnCode(0, "-J", getPath("InputMainAstTreeStringPrinterJavadoc.java"));
1695         assertWithMessage("Unexpected output log")
1696             .that(systemOut.getCapturedData().replaceAll("\\\\r\\\\n", "\\\\n")
1697                         .replaceAll("\r\n", "\n"))
1698             .isEqualTo(expected);
1699         assertWithMessage("Unexpected system error log")
1700             .that(systemErr.getCapturedData())
1701             .isEqualTo("");
1702     }
1703 
1704     @Test
1705     public void testConflictingOptionsTvsC(@SysErr Capturable systemErr,
1706             @SysOut Capturable systemOut) {
1707         assertMainReturnCode(-1, "-c", "/google_checks.xml", "-t", getPath(""));
1708         assertWithMessage("Unexpected output log")
1709             .that(systemOut.getCapturedData())
1710             .isEqualTo("Option '-t' cannot be used with other options." + System.lineSeparator());
1711         assertWithMessage("Unexpected system error log")
1712             .that(systemErr.getCapturedData())
1713             .isEqualTo("");
1714     }
1715 
1716     @Test
1717     public void testConflictingOptionsTvsP(@SysErr Capturable systemErr,
1718             @SysOut Capturable systemOut) {
1719         assertMainReturnCode(-1, "-p", getPath("InputMainMycheckstyle.properties"), "-t",
1720                 getPath(""));
1721         assertWithMessage("Unexpected output log")
1722             .that(systemOut.getCapturedData())
1723             .isEqualTo("Option '-t' cannot be used with other options." + System.lineSeparator());
1724         assertWithMessage("Unexpected system error log")
1725             .that(systemErr.getCapturedData())
1726             .isEqualTo("");
1727     }
1728 
1729     @Test
1730     public void testConflictingOptionsTvsF(@SysErr Capturable systemErr,
1731             @SysOut Capturable systemOut) {
1732         assertMainReturnCode(-1, "-f", "plain", "-t", getPath(""));
1733         assertWithMessage("Unexpected output log")
1734             .that(systemOut.getCapturedData())
1735             .isEqualTo("Option '-t' cannot be used with other options." + System.lineSeparator());
1736         assertWithMessage("Unexpected system error log")
1737             .that(systemErr.getCapturedData())
1738             .isEqualTo("");
1739     }
1740 
1741     @Test
1742     public void testConflictingOptionsTvsS(@SysErr Capturable systemErr,
1743             @SysOut Capturable systemOut) throws IOException {
1744         final String outputPath = new File(temporaryFolder, "file.output").getCanonicalPath();
1745 
1746         assertMainReturnCode(-1, "-s", outputPath, "-t", getPath(""));
1747         assertWithMessage("Unexpected output log")
1748             .that(systemOut.getCapturedData())
1749             .isEqualTo("Option '-t' cannot be used with other options." + System.lineSeparator());
1750         assertWithMessage("Unexpected system error log")
1751             .that(systemErr.getCapturedData())
1752             .isEqualTo("");
1753     }
1754 
1755     @Test
1756     public void testConflictingOptionsTvsO(@SysErr Capturable systemErr,
1757             @SysOut Capturable systemOut) throws IOException {
1758         final String outputPath = new File(temporaryFolder, "file.output").getCanonicalPath();
1759 
1760         assertMainReturnCode(-1, "-o", outputPath, "-t", getPath(""));
1761         assertWithMessage("Unexpected output log")
1762             .that(systemOut.getCapturedData())
1763             .isEqualTo("Option '-t' cannot be used with other options." + System.lineSeparator());
1764         assertWithMessage("Unexpected system error log")
1765             .that(systemErr.getCapturedData())
1766             .isEqualTo("");
1767     }
1768 
1769     @Test
1770     public void testDebugOption(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1771         assertMainReturnCode(0, "-c", "/google_checks.xml", getPath("InputMain.java"), "-d");
1772         assertWithMessage("Unexpected system error log")
1773             .that(systemErr.getCapturedData())
1774             .contains("FINE: Checkstyle debug logging enabled");
1775         assertWithMessage("Unexpected system error log")
1776             .that(systemOut.getCapturedData())
1777             .contains("Audit done.");
1778 
1779     }
1780 
1781     @Test
1782     public void testExcludeOption(@SysErr Capturable systemErr, @SysOut Capturable systemOut)
1783             throws IOException {
1784         final String filePath = getFilePath("");
1785         assertMainReturnCode(-1, "-c", "/google_checks.xml", filePath, "-e", filePath);
1786         assertWithMessage("Unexpected output log")
1787             .that(systemOut.getCapturedData())
1788             .isEqualTo("Files to process must be specified, found 0." + System.lineSeparator());
1789         assertWithMessage("Unexpected system error log")
1790             .that(systemErr.getCapturedData())
1791             .isEqualTo("");
1792     }
1793 
1794     @Test
1795     public void testExcludeOptionFile(@SysErr Capturable systemErr, @SysOut Capturable systemOut)
1796             throws IOException {
1797         final String filePath = getFilePath("InputMain.java");
1798         assertMainReturnCode(-1, "-c", "/google_checks.xml", filePath, "-e", filePath);
1799         assertWithMessage("Unexpected output log")
1800             .that(systemOut.getCapturedData())
1801             .isEqualTo("Files to process must be specified, found 0." + System.lineSeparator());
1802         assertWithMessage("Unexpected system error log")
1803             .that(systemErr.getCapturedData())
1804             .isEqualTo("");
1805     }
1806 
1807     @Test
1808     public void testExcludeRegexpOption(@SysErr Capturable systemErr, @SysOut Capturable systemOut)
1809             throws IOException {
1810         final String filePath = getFilePath("");
1811         assertMainReturnCode(-1, "-c", "/google_checks.xml", filePath, "-x", ".");
1812         assertWithMessage("Unexpected output log")
1813             .that(systemOut.getCapturedData())
1814             .isEqualTo("Files to process must be specified, found 0." + System.lineSeparator());
1815         assertWithMessage("Unexpected output log")
1816             .that(systemErr.getCapturedData())
1817             .isEqualTo("");
1818     }
1819 
1820     @Test
1821     public void testExcludeRegexpOptionFile(@SysErr Capturable systemErr,
1822             @SysOut Capturable systemOut) throws IOException {
1823         final String filePath = getFilePath("InputMain.java");
1824         assertMainReturnCode(-1, "-c", "/google_checks.xml", filePath, "-x", ".");
1825         assertWithMessage("Unexpected output log")
1826             .that(systemOut.getCapturedData())
1827             .isEqualTo("Files to process must be specified, found 0." + System.lineSeparator());
1828         assertWithMessage("Unexpected output log")
1829             .that(systemErr.getCapturedData())
1830             .isEqualTo("");
1831     }
1832 
1833     @Test
1834     public void testExcludeDirectoryNotMatch() throws Exception {
1835         final Class<?> optionsClass = Class.forName(Main.class.getName());
1836         final List<Pattern> list = new ArrayList<>();
1837         list.add(Pattern.compile("BAD_PATH"));
1838 
1839         final List<File> result = TestUtil.invokeStaticMethodList(
1840                 optionsClass, "listFiles", new File(getFilePath("")), list);
1841         assertWithMessage("Invalid result size")
1842             .that(result)
1843             .isNotEmpty();
1844     }
1845 
1846     @Test
1847     public void testCustomRootModule(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1848         TestRootModuleChecker.reset();
1849 
1850         assertMainReturnCode(0, "-c", getPath("InputMainConfig-custom-root-module.xml"),
1851                 getPath("InputMain.java"));
1852         assertWithMessage("Unexpected output log")
1853             .that(systemOut.getCapturedData())
1854             .isEqualTo("");
1855         assertWithMessage("Unexpected system error log")
1856             .that(systemErr.getCapturedData())
1857             .isEqualTo("");
1858         assertWithMessage("Invalid Checker state")
1859                 .that(TestRootModuleChecker.isProcessed())
1860                 .isTrue();
1861         assertWithMessage("RootModule should be destroyed")
1862                 .that(TestRootModuleChecker.isDestroyed())
1863                 .isTrue();
1864     }
1865 
1866     @Test
1867     public void testCustomSimpleRootModule(@SysErr Capturable systemErr) {
1868         TestRootModuleChecker.reset();
1869         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-custom-simple-root-module.xml"),
1870                 getPath("InputMain.java"));
1871         final String checkstylePackage = "com.puppycrawl.tools.checkstyle.";
1872         final LocalizedMessage unableToInstantiateExceptionMessage = new LocalizedMessage(
1873                 Definitions.CHECKSTYLE_BUNDLE,
1874                 getClass(),
1875                 "PackageObjectFactory.unableToInstantiateExceptionMessage",
1876                 "TestRootModuleChecker",
1877                 checkstylePackage
1878                         + "TestRootModuleChecker, "
1879                         + "TestRootModuleCheckerCheck, " + checkstylePackage
1880                         + "TestRootModuleCheckerCheck");
1881         assertWithMessage(
1882                 "Unexpected system error log")
1883                         .that(systemErr.getCapturedData())
1884                         .startsWith(checkstylePackage + "api.CheckstyleException: "
1885                                 + unableToInstantiateExceptionMessage.getMessage());
1886         assertWithMessage("Invalid checker state")
1887                 .that(TestRootModuleChecker.isProcessed())
1888                 .isFalse();
1889     }
1890 
1891     @Test
1892     public void testExceptionOnExecuteIgnoredModuleWithUnknownModuleName(
1893             @SysErr Capturable systemErr) {
1894         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-non-existent-classname-ignore.xml"),
1895                     "--executeIgnoredModules", getPath("InputMain.java"));
1896         final String cause = "com.puppycrawl.tools.checkstyle.api.CheckstyleException:"
1897                 + " cannot initialize module TreeWalker - ";
1898         assertWithMessage("Unexpected system error log")
1899                 .that(systemErr.getCapturedData())
1900                 .startsWith(cause);
1901     }
1902 
1903     @Test
1904     public void testExceptionOnExecuteIgnoredModuleWithBadPropertyValue(
1905             @SysErr Capturable systemErr) {
1906         assertMainReturnCode(-2, "-c", getPath("InputMainConfig-TypeName-bad-value.xml"),
1907                     "--executeIgnoredModules", getPath("InputMain.java"));
1908         final String cause = "com.puppycrawl.tools.checkstyle.api.CheckstyleException:"
1909                 + " cannot initialize module TreeWalker - ";
1910         final String causeDetail = "it is not a boolean";
1911         assertWithMessage("Unexpected system error log")
1912                 .that(systemErr.getCapturedData())
1913                 .startsWith(cause);
1914         assertWithMessage("Unexpected system error log")
1915                 .that(systemErr.getCapturedData())
1916                 .contains(causeDetail);
1917     }
1918 
1919     @Test
1920     public void testNoProblemOnExecuteIgnoredModuleWithBadPropertyValue(
1921             @SysErr Capturable systemErr) {
1922         assertMainReturnCode(0, "-c", getPath("InputMainConfig-TypeName-bad-value.xml"),
1923                     "", getPath("InputMain.java"));
1924         assertWithMessage("Unexpected system error log")
1925             .that(systemErr.getCapturedData())
1926                 .isEmpty();
1927     }
1928 
1929     @Test
1930     public void testMissingFiles(@SysErr Capturable systemErr, @SysOut Capturable systemOut) {
1931         assertMainReturnCode(-1);
1932         final String usage = "Missing required parameter: '<files or folders>'" + EOL + SHORT_USAGE;
1933         assertWithMessage("Unexpected output log")
1934             .that(systemOut.getCapturedData())
1935             .isEqualTo("");
1936         assertWithMessage("Unexpected system error log")
1937             .that(systemErr.getCapturedData())
1938             .isEqualTo(usage);
1939     }
1940 
1941     @Test
1942     public void testOutputFormatToStringLowercase() {
1943         assertWithMessage("expected xml")
1944             .that(Main.OutputFormat.XML.toString())
1945             .isEqualTo("xml");
1946         assertWithMessage("expected plain")
1947             .that(Main.OutputFormat.PLAIN.toString())
1948             .isEqualTo("plain");
1949     }
1950 
1951     @Test
1952     public void testXmlOutputFormatCreateListener() throws IOException {
1953         final ByteArrayOutputStream out = new ByteArrayOutputStream();
1954         final AuditListener listener = Main.OutputFormat.XML.createListener(out,
1955                 OutputStreamOptions.CLOSE);
1956         assertWithMessage("listener is XMLLogger")
1957                 .that(listener)
1958                 .isInstanceOf(XMLLogger.class);
1959     }
1960 
1961     @Test
1962     public void testSarifOutputFormatCreateListener() throws IOException {
1963         final ByteArrayOutputStream out = new ByteArrayOutputStream();
1964         final AuditListener listener = Main.OutputFormat.SARIF.createListener(out,
1965                 OutputStreamOptions.CLOSE);
1966         assertWithMessage("listener is SarifLogger")
1967                 .that(listener)
1968                 .isInstanceOf(SarifLogger.class);
1969     }
1970 
1971     @Test
1972     public void testPlainOutputFormatCreateListener() throws IOException {
1973         final ByteArrayOutputStream out = new ByteArrayOutputStream();
1974         final AuditListener listener = Main.OutputFormat.PLAIN.createListener(out,
1975                 OutputStreamOptions.CLOSE);
1976         assertWithMessage("listener is DefaultLogger")
1977                 .that(listener)
1978                 .isInstanceOf(DefaultLogger.class);
1979     }
1980 
1981     
1982 
1983 
1984 
1985 
1986 
1987 
1988 
1989 
1990 
1991 
1992 
1993 
1994     private static void assertMainReturnCode(int expectedExitCode, String... arguments) {
1995         final Runtime mock = mock();
1996         try (MockedStatic<Runtime> runtime = mockStatic(Runtime.class)) {
1997             runtime.when(Runtime::getRuntime)
1998                     .thenReturn(mock);
1999             Main.main(arguments);
2000         }
2001         catch (IOException exception) {
2002             assertWithMessage("Unexpected exception: %s", exception).fail();
2003         }
2004         verify(mock).exit(expectedExitCode);
2005     }
2006 
2007     
2008 
2009 
2010 
2011     private static final class ShouldNotBeClosedStream extends PrintStream {
2012 
2013         private boolean isClosed;
2014 
2015         private ShouldNotBeClosedStream() {
2016             super(new ByteArrayOutputStream(), false, StandardCharsets.UTF_8);
2017         }
2018 
2019         @Override
2020         public void close() {
2021             isClosed = true;
2022             super.close();
2023         }
2024 
2025     }
2026 
2027 }