1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.isUtilsClassHasPrivateConstructor;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNotEquals;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.io.ByteArrayOutputStream;
30  import java.io.File;
31  import java.io.IOException;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.nio.charset.StandardCharsets;
35  import java.nio.file.Files;
36  import java.nio.file.Paths;
37  import java.util.ArrayList;
38  import java.util.List;
39  import java.util.Locale;
40  import java.util.logging.Handler;
41  import java.util.logging.Level;
42  import java.util.logging.Logger;
43  import java.util.regex.Pattern;
44  
45  import org.junit.Before;
46  import org.junit.Rule;
47  import org.junit.Test;
48  import org.junit.contrib.java.lang.system.ExpectedSystemExit;
49  import org.junit.contrib.java.lang.system.SystemErrRule;
50  import org.junit.contrib.java.lang.system.SystemOutRule;
51  import org.junit.rules.TemporaryFolder;
52  import org.powermock.reflect.Whitebox;
53  
54  import com.puppycrawl.tools.checkstyle.api.AuditListener;
55  import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
56  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
57  import com.puppycrawl.tools.checkstyle.api.Configuration;
58  import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
59  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestRootModuleChecker;
60  
61  public class MainTest {
62  
63      private static final String SHORT_USAGE = String.format(Locale.ROOT,
64              "Usage: checkstyle [OPTIONS]... FILES...%n"
65              + "Try 'checkstyle --help' for more information.%n");
66  
67      private static final String USAGE = String.format(Locale.ROOT,
68            "Usage: checkstyle [-dghjJtTV] [--executeIgnoredModules] [--tabWidth=<tabWidth>]%n"
69            + "                  [-c=<configurationFile>] [-C=<checkerThreadsNumber>]"
70            + " [-f=<format>]%n"
71            + "                  [-o=<outputPath>] [-p=<propertiesFile>]"
72            + " [-s=<suppressionLineColumnNumber>]%n"
73            + "                  [-W=<treeWalkerThreadsNumber>] [-e=<exclude>]..."
74            + " [-x=<excludeRegex>]... <files>...%n"
75            + "Checkstyle verifies that the specified source code files adhere to the specified"
76            + " rules. By default%n"
77            + "errors are reported to standard out in plain format. Checkstyle requires a"
78            + " configuration XML file%n"
79            + "that configures the checks to apply.%n"
80            + "      <files>...            One or more source files to verify%n"
81            + "      --executeIgnoredModules%n"
82            + "                            Allows ignored modules to be run.%n"
83            + "      --tabWidth=<tabWidth> Sets the length of the tab character. Used only with"
84            + " \"-s\" option. Default%n"
85            + "                              value is 8%n"
86            + "  -c=<configurationFile>    Sets the check configuration file to use.%n"
87            + "  -C, --checker-threads-number=<checkerThreadsNumber>%n"
88            + "                            (experimental) The number of Checker threads (must be"
89            + " greater than zero)%n"
90            + "  -d, --debug               Print all debug logging of CheckStyle utility%n"
91            + "  -e, --exclude=<exclude>   Directory/File path to exclude from CheckStyle%n"
92            + "  -f=<format>               Sets the output format. Valid values: xml, plain."
93            + " Defaults to plain%n"
94            + "  -g, --generate-xpath-suppression%n"
95            + "                            Generates to output a suppression.xml to use to suppress"
96            + " all violations from%n"
97            + "                              user's config%n"
98            + "  -h, --help                Show this help message and exit.%n"
99            + "  -j, --javadocTree         Print Parse tree of the Javadoc comment%n"
100           + "  -J, --treeWithJavadoc     Print full Abstract Syntax Tree of the file%n"
101           + "  -o=<outputPath>           Sets the output file. Defaults to stdout%n"
102           + "  -p=<propertiesFile>       Loads the properties file%n"
103           + "  -s=<suppressionLineColumnNumber>%n"
104           + "                            Print xpath suppressions at the file's line and column"
105           + " position. Argument is%n"
106           + "                              the line and column number (separated by a : ) in the"
107           + " file that the%n"
108           + "                              suppression should be generated for%n"
109           + "  -t, --tree                Print Abstract Syntax Tree(AST) of the file%n"
110           + "  -T, --treeWithComments    Print Abstract Syntax Tree(AST) of the file including"
111           + " comments%n"
112           + "  -V, --version             Print version information and exit.%n"
113           + "  -W, --tree-walker-threads-number=<treeWalkerThreadsNumber>%n"
114           + "                            (experimental) The number of TreeWalker threads (must be"
115           + " greater than zero)%n"
116           + "  -x, --exclude-regexp=<excludeRegex>%n"
117           + "                            Regular expression of directory/file to exclude from"
118           + " CheckStyle%n");
119 
120     private static final Logger LOG = Logger.getLogger(MainTest.class.getName()).getParent();
121     private static final Handler[] HANDLERS = LOG.getHandlers();
122     private static final Level ORIGINAL_LOG_LEVEL = LOG.getLevel();
123 
124     private static final String EOL = System.getProperty("line.separator");
125 
126     @Rule
127     public final TemporaryFolder temporaryFolder = new TemporaryFolder();
128     @Rule
129     public final ExpectedSystemExit exit = ExpectedSystemExit.none();
130     @Rule
131     public final SystemErrRule systemErr = new SystemErrRule().enableLog().mute();
132     @Rule
133     public final SystemOutRule systemOut = new SystemOutRule().enableLog().mute();
134 
135     private final LocalizedMessage auditStartMessage = new LocalizedMessage(1,
136             Definitions.CHECKSTYLE_BUNDLE, "DefaultLogger.auditStarted", null, null,
137             getClass(), null);
138 
139     private final LocalizedMessage auditFinishMessage = new LocalizedMessage(1,
140             Definitions.CHECKSTYLE_BUNDLE, "DefaultLogger.auditFinished", null, null,
141             getClass(), null);
142 
143     private final LocalizedMessage errorCounterOneMessage = new LocalizedMessage(1,
144             Definitions.CHECKSTYLE_BUNDLE, Main.ERROR_COUNTER,
145             new String[] {String.valueOf(1)}, null, getClass(), null);
146 
147     private static String getPath(String filename) {
148         return "src/test/resources/com/puppycrawl/tools/checkstyle/main/" + filename;
149     }
150 
151     private static String getNonCompilablePath(String filename) {
152         return "src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/main/" + filename;
153     }
154 
155     private static String getFilePath(String filename) throws IOException {
156         return new File(getPath(filename)).getCanonicalPath();
157     }
158 
159     @Before
160     public void setUp() {
161         // restore original logging level and HANDLERS to prevent bleeding into other tests
162 
163         LOG.setLevel(ORIGINAL_LOG_LEVEL);
164 
165         for (Handler handler : LOG.getHandlers()) {
166             boolean found = false;
167 
168             for (Handler savedHandler : HANDLERS) {
169                 if (handler == savedHandler) {
170                     found = true;
171                     break;
172                 }
173             }
174 
175             if (!found) {
176                 LOG.removeHandler(handler);
177             }
178         }
179     }
180 
181     @Test
182     public void testIsProperUtilsClass() throws ReflectiveOperationException {
183         assertTrue("Constructor is not private",
184                 isUtilsClassHasPrivateConstructor(Main.class, false));
185     }
186 
187     @Test
188     public void testVersionPrint()
189             throws Exception {
190         exit.checkAssertionAfterwards(() -> {
191             assertEquals("Unexpected output log",
192                     "Checkstyle version: null" + System.lineSeparator(),
193                     systemOut.getLog());
194             assertEquals("Unexpected system error log", "", systemErr.getLog());
195         });
196         Main.main("-V");
197     }
198 
199     @Test
200     public void testUsageHelpPrint()
201             throws Exception {
202         exit.checkAssertionAfterwards(() -> {
203             assertEquals("Unexpected output log",
204                     USAGE,
205                     systemOut.getLog());
206             assertEquals("Unexpected system error log", "", systemErr.getLog());
207         });
208         Main.main("-h");
209     }
210 
211     @Test
212     public void testWrongArgument()
213             throws Exception {
214         exit.expectSystemExitWithStatus(-1);
215         exit.checkAssertionAfterwards(() -> {
216             final String usage = "Unknown option: -w" + EOL
217                     + SHORT_USAGE;
218             assertEquals("Unexpected output log", "", systemOut.getLog());
219             assertEquals("Unexpected system error log", usage, systemErr.getLog());
220         });
221         // need to specify a file:
222         // <files> is defined as a required positional param;
223         // picocli verifies required parameters before checking unknown options
224         Main.main("-w", "file");
225     }
226 
227     @Test
228     public void testWrongArgumentMissingFiles()
229             throws Exception {
230         exit.expectSystemExitWithStatus(-1);
231         exit.checkAssertionAfterwards(() -> {
232             // files is defined as a required positional param;
233             // picocli verifies required parameters before checking unknown options
234             final String usage = "Missing required parameter: <files>" + EOL
235                     + SHORT_USAGE;
236             assertEquals("Unexpected output log", "", systemOut.getLog());
237             assertEquals("Unexpected system error log", usage, systemErr.getLog());
238         });
239         Main.main("-w");
240     }
241 
242     @Test
243     public void testNoConfigSpecified()
244             throws Exception {
245         exit.expectSystemExitWithStatus(-1);
246         exit.checkAssertionAfterwards(() -> {
247             assertEquals("Unexpected output log",
248                     "Must specify a config XML file." + System.lineSeparator(),
249                     systemOut.getLog());
250             assertEquals("Unexpected system error log", "", systemErr.getLog());
251         });
252         Main.main(getPath("InputMain.java"));
253     }
254 
255     @Test
256     public void testNonExistentTargetFile()
257             throws Exception {
258         exit.expectSystemExitWithStatus(-1);
259         exit.checkAssertionAfterwards(() -> {
260             assertEquals("Unexpected output log", "Files to process must be specified, found 0."
261                 + System.lineSeparator(), systemOut.getLog());
262             assertEquals("Unexpected system error log", "", systemErr.getLog());
263         });
264         Main.main("-c", "/google_checks.xml", "NonExistentFile.java");
265     }
266 
267     @Test
268     public void testNonExistentConfigFile()
269             throws Exception {
270         exit.expectSystemExitWithStatus(-1);
271         exit.checkAssertionAfterwards(() -> {
272             assertEquals("Unexpected output log", "Could not find config XML file "
273                         + "'src/main/resources/non_existent_config.xml'." + EOL,
274                     systemOut.getLog());
275             assertEquals("Unexpected system error log", "", systemErr.getLog());
276         });
277         Main.main("-c", "src/main/resources/non_existent_config.xml",
278                 getPath("InputMain.java"));
279     }
280 
281     @Test
282     public void testNonExistentOutputFormat() throws Exception {
283         exit.expectSystemExitWithStatus(-1);
284         exit.checkAssertionAfterwards(() -> {
285             assertEquals("Unexpected output log", "", systemOut.getLog());
286             assertEquals("Unexpected system error log",
287                     "Invalid value for option '-f': expected one of [XML, PLAIN]"
288                         + " (case-insensitive) but was 'xmlp'"
289                     + EOL + SHORT_USAGE, systemErr.getLog());
290         });
291         Main.main("-c", "/google_checks.xml", "-f", "xmlp",
292                 getPath("InputMain.java"));
293     }
294 
295     @Test
296     public void testNonExistentClass() throws Exception {
297         exit.expectSystemExitWithStatus(-2);
298         exit.checkAssertionAfterwards(() -> {
299             final String expectedExceptionMessage = errorCounterOneMessage.getMessage() + EOL;
300             assertEquals("Unexpected output log", expectedExceptionMessage, systemOut.getLog());
301 
302             final String cause = "com.puppycrawl.tools.checkstyle.api.CheckstyleException:"
303                     + " cannot initialize module TreeWalker - ";
304             assertTrue("Unexpected system error log", systemErr.getLog().startsWith(cause));
305         });
306 
307         Main.main("-c", getPath("InputMainConfig-non-existent-classname.xml"),
308             getPath("InputMain.java"));
309     }
310 
311     @Test
312     public void testExistingTargetFile() throws Exception {
313         exit.checkAssertionAfterwards(() -> {
314             assertEquals("Unexpected output log", auditStartMessage.getMessage() + EOL
315                     + auditFinishMessage.getMessage() + EOL,
316                     systemOut.getLog());
317             assertEquals("Unexpected system error log", "", systemErr.getLog());
318         });
319         Main.main("-c", getPath("InputMainConfig-classname.xml"),
320                 getPath("InputMain.java"));
321     }
322 
323     @Test
324     public void testExistingTargetFileXmlOutput() throws Exception {
325         exit.checkAssertionAfterwards(() -> {
326             final String expectedPath = getFilePath("InputMain.java");
327             final String version = Main.class.getPackage().getImplementationVersion();
328             assertEquals("Unexpected output log", "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
329                     + "<checkstyle version=\"" + version + "\">" + EOL
330                     + "<file name=\"" + expectedPath + "\">" + EOL
331                     + "</file>" + EOL
332                     + "</checkstyle>" + EOL, systemOut.getLog());
333             assertEquals("Unexpected system error log", "", systemErr.getLog());
334         });
335         Main.main("-c", getPath("InputMainConfig-classname.xml"),
336                 "-f", "xml",
337                 getPath("InputMain.java"));
338     }
339 
340     @Test
341     public void testExistingTargetFilePlainOutput() throws Exception {
342         exit.checkAssertionAfterwards(() -> {
343             assertEquals("Unexpected output log", auditStartMessage.getMessage() + EOL
344                     + auditFinishMessage.getMessage() + EOL, systemOut.getLog());
345             assertEquals("Unexpected system error log", "", systemErr.getLog());
346         });
347         Main.main("-c", getPath("InputMainConfig-classname.xml"),
348                 "-f", "plain",
349                 getPath("InputMain.java"));
350     }
351 
352     @Test
353     public void testExistingTargetFileWithViolations() throws Exception {
354         exit.checkAssertionAfterwards(() -> {
355             final LocalizedMessage invalidPatternMessageMain = new LocalizedMessage(1,
356                     "com.puppycrawl.tools.checkstyle.checks.naming.messages",
357                     "name.invalidPattern", new String[] {"InputMain", "^[a-z0-9]*$"},
358                     null, getClass(), null);
359             final LocalizedMessage invalidPatternMessageMainInner = new LocalizedMessage(1,
360                     "com.puppycrawl.tools.checkstyle.checks.naming.messages",
361                     "name.invalidPattern", new String[] {"InputMainInner", "^[a-z0-9]*$"},
362                     null, getClass(), null);
363             final String expectedPath = getFilePath("InputMain.java");
364             assertEquals("Unexpected output log", auditStartMessage.getMessage() + EOL
365                             + "[WARN] " + expectedPath + ":3:14: "
366                             + invalidPatternMessageMain.getMessage()
367                             + " [TypeName]" + EOL
368                             + "[WARN] " + expectedPath + ":5:7: "
369                             + invalidPatternMessageMainInner.getMessage()
370                             + " [TypeName]" + EOL
371                             + auditFinishMessage.getMessage() + EOL, systemOut.getLog());
372             assertEquals("Unexpected system error log", "", systemErr.getLog());
373         });
374         Main.main("-c", getPath("InputMainConfig-classname2.xml"),
375                 getPath("InputMain.java"));
376     }
377 
378     @Test
379     public void testExistingTargetFileWithError()
380             throws Exception {
381         exit.expectSystemExitWithStatus(2);
382         exit.checkAssertionAfterwards(() -> {
383             final LocalizedMessage errorCounterTwoMessage = new LocalizedMessage(1,
384                     Definitions.CHECKSTYLE_BUNDLE, Main.ERROR_COUNTER,
385                     new String[] {String.valueOf(2)}, null, getClass(), null);
386             final LocalizedMessage invalidPatternMessageMain = new LocalizedMessage(1,
387                     "com.puppycrawl.tools.checkstyle.checks.naming.messages",
388                     "name.invalidPattern", new String[] {"InputMain", "^[a-z0-9]*$"},
389                     null, getClass(), null);
390             final LocalizedMessage invalidPatternMessageMainInner = new LocalizedMessage(1,
391                     "com.puppycrawl.tools.checkstyle.checks.naming.messages",
392                     "name.invalidPattern", new String[] {"InputMainInner", "^[a-z0-9]*$"},
393                     null, getClass(), null);
394             final String expectedPath = getFilePath("InputMain.java");
395             assertEquals("Unexpected output log", auditStartMessage.getMessage() + EOL
396                     + "[ERROR] " + expectedPath + ":3:14: "
397                     + invalidPatternMessageMain.getMessage() + " [TypeName]" + EOL
398                     + "[ERROR] " + expectedPath + ":5:7: "
399                     + invalidPatternMessageMainInner.getMessage() + " [TypeName]" + EOL
400                     + auditFinishMessage.getMessage() + EOL
401                     + errorCounterTwoMessage.getMessage() + EOL, systemOut.getLog());
402             assertEquals("Unexpected system error log", "", systemErr.getLog());
403         });
404         Main.main("-c",
405                 getPath("InputMainConfig-classname2-error.xml"),
406                 getPath("InputMain.java"));
407     }
408 
409     /**
410      * Similar test to {@link #testExistingTargetFileWithError}, but for PIT mutation tests:
411      * this test fails if the boundary condition is changed from {@code if (exitStatus > 0)}
412      * to {@code if (exitStatus > 1)}.
413      * @throws Exception should not throw anything
414      */
415     @Test
416     public void testExistingTargetFileWithOneError()
417             throws Exception {
418         exit.expectSystemExitWithStatus(1);
419         exit.checkAssertionAfterwards(() -> {
420             final LocalizedMessage errorCounterTwoMessage = new LocalizedMessage(1,
421                     Definitions.CHECKSTYLE_BUNDLE, Main.ERROR_COUNTER,
422                     new String[] {String.valueOf(1)}, null, getClass(), null);
423             final LocalizedMessage invalidPatternMessageMain = new LocalizedMessage(1,
424                     "com.puppycrawl.tools.checkstyle.checks.naming.messages",
425                     "name.invalidPattern", new String[] {"InputMain1", "^[a-z0-9]*$"},
426                     null, getClass(), null);
427             final String expectedPath = getFilePath("InputMain1.java");
428             assertEquals("Unexpected output log", auditStartMessage.getMessage() + EOL
429                     + "[ERROR] " + expectedPath + ":3:14: "
430                     + invalidPatternMessageMain.getMessage() + " [TypeName]" + EOL
431                     + auditFinishMessage.getMessage() + EOL
432                     + errorCounterTwoMessage.getMessage() + EOL, systemOut.getLog());
433             assertEquals("Unexpected system error log", "", systemErr.getLog());
434         });
435         Main.main("-c",
436                 getPath("InputMainConfig-classname2-error.xml"),
437                 getPath("InputMain1.java"));
438     }
439 
440     @Test
441     public void testExistentTargetFilePlainOutputToNonExistentFile()
442             throws Exception {
443         exit.checkAssertionAfterwards(() -> {
444             assertEquals("Unexpected output log", "", systemOut.getLog());
445             assertEquals("Unexpected system error log", "", systemErr.getLog());
446         });
447         Main.main("-c", getPath("InputMainConfig-classname.xml"),
448                 "-f", "plain",
449                 "-o", temporaryFolder.getRoot() + "/output.txt",
450                 getPath("InputMain.java"));
451     }
452 
453     @Test
454     public void testExistingTargetFilePlainOutputToFile()
455             throws Exception {
456         final File file = temporaryFolder.newFile("file.output");
457         exit.checkAssertionAfterwards(() -> {
458             assertEquals("Unexpected output log", "", systemOut.getLog());
459             assertEquals("Unexpected system error log", "", systemErr.getLog());
460         });
461         Main.main("-c", getPath("InputMainConfig-classname.xml"),
462                 "-f", "plain",
463                 "-o", file.getCanonicalPath(),
464                 getPath("InputMain.java"));
465     }
466 
467     @Test
468     public void testCreateNonExistentOutputFile() throws Exception {
469         final String outputFile = temporaryFolder.getRoot().getCanonicalPath() + "nonexistent.out";
470         assertFalse("File must not exist", new File(outputFile).exists());
471         Main.main("-c", getPath("InputMainConfig-classname.xml"),
472                 "-f", "plain",
473                 "-o", outputFile,
474                 getPath("InputMain.java"));
475         assertTrue("File must exist", new File(outputFile).exists());
476     }
477 
478     @Test
479     public void testExistingTargetFilePlainOutputProperties() throws Exception {
480         //exit.expectSystemExitWithStatus(0);
481         exit.checkAssertionAfterwards(() -> {
482             assertEquals("Unexpected output log", auditStartMessage.getMessage() + EOL
483                     + auditFinishMessage.getMessage() + EOL, systemOut.getLog());
484             assertEquals("Unexpected system error log", "", systemErr.getLog());
485         });
486         Main.main("-c", getPath("InputMainConfig-classname-prop.xml"),
487                 "-p", getPath("InputMainMycheckstyle.properties"),
488                 getPath("InputMain.java"));
489     }
490 
491     @Test
492     public void testExistingTargetFilePlainOutputNonexistentProperties()
493             throws Exception {
494         exit.expectSystemExitWithStatus(-1);
495         exit.checkAssertionAfterwards(() -> {
496             assertEquals("Unexpected output log", "Could not find file 'nonexistent.properties'."
497                     + System.lineSeparator(), systemOut.getLog());
498             assertEquals("Unexpected system error log", "", systemErr.getLog());
499         });
500         Main.main("-c", getPath("InputMainConfig-classname-prop.xml"),
501                 "-p", "nonexistent.properties",
502                 getPath("InputMain.java"));
503     }
504 
505     @Test
506     public void testExistingIncorrectConfigFile()
507             throws Exception {
508         exit.expectSystemExitWithStatus(-2);
509         exit.checkAssertionAfterwards(() -> {
510             final String output = errorCounterOneMessage.getMessage() + EOL;
511             assertEquals("Unexpected output log", output, systemOut.getLog());
512             final String errorOutput = "com.puppycrawl.tools.checkstyle.api."
513                 + "CheckstyleException: unable to parse configuration stream - ";
514             assertTrue("Unexpected system error log", systemErr.getLog().startsWith(errorOutput));
515         });
516         Main.main("-c", getPath("InputMainConfig-Incorrect.xml"),
517             getPath("InputMain.java"));
518     }
519 
520     @Test
521     public void testExistingIncorrectChildrenInConfigFile()
522             throws Exception {
523         exit.expectSystemExitWithStatus(-2);
524         exit.checkAssertionAfterwards(() -> {
525             final String output = errorCounterOneMessage.getMessage() + EOL;
526             assertEquals("Unexpected output log", output, systemOut.getLog());
527             final String errorOutput = "com.puppycrawl.tools.checkstyle.api."
528                     + "CheckstyleException: cannot initialize module RegexpSingleline"
529                     + " - RegexpSingleline is not allowed as a child in RegexpSingleline";
530             assertTrue("Unexpected system error log", systemErr.getLog().startsWith(errorOutput));
531         });
532         Main.main("-c", getPath("InputMainConfig-incorrectChildren.xml"),
533             getPath("InputMain.java"));
534     }
535 
536     @Test
537     public void testExistingIncorrectChildrenInConfigFile2()
538             throws Exception {
539         exit.expectSystemExitWithStatus(-2);
540         exit.checkAssertionAfterwards(() -> {
541             final String output = errorCounterOneMessage.getMessage() + EOL;
542             assertEquals("Unexpected output log", output, systemOut.getLog());
543             final String errorOutput = "com.puppycrawl.tools.checkstyle.api."
544                     + "CheckstyleException: cannot initialize module TreeWalker - "
545                     + "cannot initialize module JavadocMethod - "
546                     + "JavadocVariable is not allowed as a child in JavadocMethod";
547             assertTrue("Unexpected system error log", systemErr.getLog().startsWith(errorOutput));
548         });
549         Main.main("-c", getPath("InputMainConfig-incorrectChildren2.xml"),
550             getPath("InputMain.java"));
551     }
552 
553     @Test
554     public void testLoadPropertiesIoException() throws Exception {
555         final Class<?>[] param = new Class<?>[1];
556         param[0] = File.class;
557         final Class<?> cliOptionsClass = Class.forName(Main.class.getName());
558         final Method method = cliOptionsClass.getDeclaredMethod("loadProperties", param);
559         method.setAccessible(true);
560         try {
561             method.invoke(null, new File("."));
562             fail("Exception was expected");
563         }
564         catch (InvocationTargetException ex) {
565             assertTrue("Invalid error cause",
566                     ex.getCause() instanceof CheckstyleException);
567             // We do separate validation for message as in Windows
568             // disk drive letter appear in message,
569             // so we skip that drive letter for compatibility issues
570             final LocalizedMessage loadPropertiesMessage = new LocalizedMessage(1,
571                     Definitions.CHECKSTYLE_BUNDLE, Main.LOAD_PROPERTIES_EXCEPTION,
572                     new String[] {""}, null, getClass(), null);
573             final String causeMessage = ex.getCause().getLocalizedMessage();
574             final String localizedMessage = loadPropertiesMessage.getMessage();
575             final boolean samePrefix = causeMessage.substring(0, causeMessage.indexOf(' '))
576                     .equals(localizedMessage
577                             .substring(0, localizedMessage.indexOf(' ')));
578             final boolean sameSuffix =
579                     causeMessage.substring(causeMessage.lastIndexOf(' '))
580                     .equals(localizedMessage
581                             .substring(localizedMessage.lastIndexOf(' ')));
582             assertTrue("Invalid error message", samePrefix || sameSuffix);
583             assertTrue("Invalid error message", causeMessage.contains(".'"));
584         }
585     }
586 
587     @Test
588     public void testExistingDirectoryWithViolations() throws Exception {
589         // we just reference there all violations
590         final String[][] outputValues = {
591                 {"InputMainComplexityOverflow", "1", "172"},
592         };
593 
594         final int allowedLength = 170;
595         final String msgKey = "maxLen.file";
596         final String bundle = "com.puppycrawl.tools.checkstyle.checks.sizes.messages";
597 
598         exit.checkAssertionAfterwards(() -> {
599             final String expectedPath = getFilePath("") + File.separator;
600             final StringBuilder sb = new StringBuilder(28);
601             sb.append(auditStartMessage.getMessage())
602                     .append(EOL);
603             final String format = "[WARN] " + expectedPath + outputValues[0][0] + ".java:"
604                     + outputValues[0][1] + ": ";
605             for (String[] outputValue : outputValues) {
606                 final String localizedMessage = new LocalizedMessage(1, bundle,
607                         msgKey, new Integer[] {Integer.valueOf(outputValue[2]), allowedLength},
608                         null, getClass(), null).getMessage();
609                 final String line = format + localizedMessage + " [FileLength]";
610                 sb.append(line).append(EOL);
611             }
612             sb.append(auditFinishMessage.getMessage())
613                     .append(EOL);
614             assertEquals("Unexpected output log", sb.toString(), systemOut.getLog());
615             assertEquals("Unexpected system error log", "", systemErr.getLog());
616         });
617 
618         Main.main("-c", getPath("InputMainConfig-filelength.xml"),
619                 getPath(""));
620     }
621 
622     /**
623      * Test doesn't need to be serialized.
624      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
625      */
626     @Test
627     public void testListFilesNotFile() throws Exception {
628         final File fileMock = new File("") {
629             private static final long serialVersionUID = 1L;
630 
631             @Override
632             public boolean canRead() {
633                 return true;
634             }
635 
636             @Override
637             public boolean isDirectory() {
638                 return false;
639             }
640 
641             @Override
642             public boolean isFile() {
643                 return false;
644             }
645         };
646 
647         final List<File> result = Whitebox.invokeMethod(Main.class, "listFiles",
648                 fileMock, new ArrayList<Pattern>());
649         assertEquals("Invalid result size", 0, result.size());
650     }
651 
652     /**
653      * Test doesn't need to be serialized.
654      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
655      */
656     @Test
657     public void testListFilesDirectoryWithNull() throws Exception {
658         final File[] nullResult = null;
659         final File fileMock = new File("") {
660             private static final long serialVersionUID = 1L;
661 
662             @Override
663             public boolean canRead() {
664                 return true;
665             }
666 
667             @Override
668             public boolean isDirectory() {
669                 return true;
670             }
671 
672             @Override
673             public File[] listFiles() {
674                 return nullResult;
675             }
676         };
677 
678         final List<File> result = Whitebox.invokeMethod(Main.class, "listFiles",
679                 fileMock, new ArrayList<Pattern>());
680         assertEquals("Invalid result size", 0, result.size());
681     }
682 
683     @Test
684     public void testFileReferenceDuringException() throws Exception {
685         exit.expectSystemExitWithStatus(-2);
686         exit.checkAssertionAfterwards(() -> {
687             final String expectedExceptionMessage = auditStartMessage.getMessage() + EOL
688                             + errorCounterOneMessage.getMessage() + EOL;
689             assertEquals("Unexpected output log", expectedExceptionMessage, systemOut.getLog());
690 
691             final String exceptionFirstLine = "com.puppycrawl.tools.checkstyle.api."
692                     + "CheckstyleException: Exception was thrown while processing "
693                     + new File(getNonCompilablePath("InputMainIncorrectClass.java")).getPath()
694                     + EOL;
695             assertTrue("Unexpected system error log",
696                     systemErr.getLog().startsWith(exceptionFirstLine));
697         });
698 
699         // We put xml as source to cause parse exception
700         Main.main("-c", getPath("InputMainConfig-classname.xml"),
701                 getNonCompilablePath("InputMainIncorrectClass.java"));
702     }
703 
704     @Test
705     public void testPrintTreeOnMoreThanOneFile() throws Exception {
706         exit.expectSystemExitWithStatus(-1);
707         exit.checkAssertionAfterwards(() -> {
708             assertEquals("Unexpected output log", "Printing AST is allowed for only one file."
709                 + System.lineSeparator(), systemOut.getLog());
710             assertEquals("Unexpected system error log", "", systemErr.getLog());
711         });
712 
713         Main.main("-t", getPath(""));
714     }
715 
716     @Test
717     public void testPrintTreeOption() throws Exception {
718         final String expected = "PACKAGE_DEF -> package [1:0]" + EOL
719             + "|--ANNOTATIONS -> ANNOTATIONS [1:39]" + EOL
720             + "|--DOT -> . [1:39]" + EOL
721             + "|   |--DOT -> . [1:28]" + EOL
722             + "|   |   |--DOT -> . [1:22]" + EOL
723             + "|   |   |   |--DOT -> . [1:11]" + EOL
724             + "|   |   |   |   |--IDENT -> com [1:8]" + EOL
725             + "|   |   |   |   `--IDENT -> puppycrawl [1:12]" + EOL
726             + "|   |   |   `--IDENT -> tools [1:23]" + EOL
727             + "|   |   `--IDENT -> checkstyle [1:29]" + EOL
728             + "|   `--IDENT -> main [1:40]" + EOL
729             + "`--SEMI -> ; [1:44]" + EOL
730             + "CLASS_DEF -> CLASS_DEF [3:0]" + EOL
731             + "|--MODIFIERS -> MODIFIERS [3:0]" + EOL
732             + "|   `--LITERAL_PUBLIC -> public [3:0]" + EOL
733             + "|--LITERAL_CLASS -> class [3:7]" + EOL
734             + "|--IDENT -> InputMain [3:13]" + EOL
735             + "`--OBJBLOCK -> OBJBLOCK [3:23]" + EOL
736             + "    |--LCURLY -> { [3:23]" + EOL
737             + "    `--RCURLY -> } [4:0]" + EOL
738             + "CLASS_DEF -> CLASS_DEF [5:0]" + EOL
739             + "|--MODIFIERS -> MODIFIERS [5:0]" + EOL
740             + "|--LITERAL_CLASS -> class [5:0]" + EOL
741             + "|--IDENT -> InputMainInner [5:6]" + EOL
742             + "`--OBJBLOCK -> OBJBLOCK [5:21]" + EOL
743             + "    |--LCURLY -> { [5:21]" + EOL
744             + "    `--RCURLY -> } [6:0]" + EOL;
745 
746         exit.checkAssertionAfterwards(() -> {
747             assertEquals("Unexpected output log", expected, systemOut.getLog());
748             assertEquals("Unexpected system error log", "", systemErr.getLog());
749         });
750         Main.main("-t", getPath("InputMain.java"));
751     }
752 
753     @Test
754     public void testPrintTreeCommentsOption() throws Exception {
755         final String expected = "PACKAGE_DEF -> package [1:0]" + EOL
756             + "|--ANNOTATIONS -> ANNOTATIONS [1:39]" + EOL
757             + "|--DOT -> . [1:39]" + EOL
758             + "|   |--DOT -> . [1:28]" + EOL
759             + "|   |   |--DOT -> . [1:22]" + EOL
760             + "|   |   |   |--DOT -> . [1:11]" + EOL
761             + "|   |   |   |   |--IDENT -> com [1:8]" + EOL
762             + "|   |   |   |   `--IDENT -> puppycrawl [1:12]" + EOL
763             + "|   |   |   `--IDENT -> tools [1:23]" + EOL
764             + "|   |   `--IDENT -> checkstyle [1:29]" + EOL
765             + "|   `--IDENT -> main [1:40]" + EOL
766             + "`--SEMI -> ; [1:44]" + EOL
767             + "CLASS_DEF -> CLASS_DEF [3:0]" + EOL
768             + "|--MODIFIERS -> MODIFIERS [3:0]" + EOL
769             + "|   |--BLOCK_COMMENT_BEGIN -> /* [2:0]" + EOL
770             + "|   |   |--COMMENT_CONTENT -> comment [2:2]" + EOL
771             + "|   |   `--BLOCK_COMMENT_END -> */ [2:8]" + EOL
772             + "|   `--LITERAL_PUBLIC -> public [3:0]" + EOL
773             + "|--LITERAL_CLASS -> class [3:7]" + EOL
774             + "|--IDENT -> InputMain [3:13]" + EOL
775             + "`--OBJBLOCK -> OBJBLOCK [3:23]" + EOL
776             + "    |--LCURLY -> { [3:23]" + EOL
777             + "    `--RCURLY -> } [4:0]" + EOL
778             + "CLASS_DEF -> CLASS_DEF [5:0]" + EOL
779             + "|--MODIFIERS -> MODIFIERS [5:0]" + EOL
780             + "|--LITERAL_CLASS -> class [5:0]" + EOL
781             + "|--IDENT -> InputMainInner [5:6]" + EOL
782             + "`--OBJBLOCK -> OBJBLOCK [5:21]" + EOL
783             + "    |--LCURLY -> { [5:21]" + EOL
784             + "    `--RCURLY -> } [6:0]" + EOL;
785 
786         exit.checkAssertionAfterwards(() -> {
787             assertEquals("Unexpected output log", expected, systemOut.getLog());
788             assertEquals("Unexpected system error log", "", systemErr.getLog());
789         });
790         Main.main("-T", getPath("InputMain.java"));
791     }
792 
793     @Test
794     public void testPrintTreeJavadocOption() throws Exception {
795         final String expected = new String(Files.readAllBytes(Paths.get(
796             getPath("InputMainExpectedInputJavadocComment.txt"))), StandardCharsets.UTF_8)
797             .replaceAll("\\\\r\\\\n", "\\\\n").replaceAll("\r\n", "\n");
798 
799         exit.checkAssertionAfterwards(() -> {
800             assertEquals("Unexpected output log",
801                     expected, systemOut.getLog().replaceAll("\\\\r\\\\n", "\\\\n")
802                             .replaceAll("\r\n", "\n"));
803             assertEquals("Unexpected system error log",
804                     "", systemErr.getLog());
805         });
806         Main.main("-j", getPath("InputMainJavadocComment.javadoc"));
807     }
808 
809     @Test
810     public void testPrintSuppressionOption() throws Exception {
811         final String expected = "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]"
812                 + EOL
813                 + "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]/MODIFIERS" + EOL
814                 + "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]/LITERAL_CLASS"
815                 + EOL;
816 
817         exit.checkAssertionAfterwards(() -> {
818             assertEquals("Unexpected output log",
819                     expected, systemOut.getLog());
820             assertEquals("Unexpected system error log",
821                     "", systemErr.getLog());
822         });
823         Main.main(getPath("InputMainSuppressionsStringPrinter.java"),
824                 "-s", "3:1");
825     }
826 
827     @Test
828     public void testPrintSuppressionAndTabWidthOption() throws Exception {
829         final String expected = "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]"
830                 + "/OBJBLOCK"
831                 + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST/VARIABLE_DEF[./IDENT[@text='var']]"
832                 + EOL
833                 + "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
834                 + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST/VARIABLE_DEF[./IDENT[@text='var']]"
835                 + "/MODIFIERS" + EOL
836                 + "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
837                 + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST/VARIABLE_DEF[./IDENT[@text='var']]"
838                 + "/TYPE" + EOL
839                 + "/CLASS_DEF[./IDENT[@text='InputMainSuppressionsStringPrinter']]/OBJBLOCK"
840                 + "/METHOD_DEF[./IDENT[@text='getName']]/SLIST/VARIABLE_DEF[./IDENT[@text='var']]"
841                 + "/TYPE/LITERAL_INT"
842                 + EOL;
843 
844         exit.checkAssertionAfterwards(() -> {
845             assertEquals("Unexpected output log",
846                     expected, systemOut.getLog());
847             assertEquals("Unexpected system error log",
848                     "", systemErr.getLog());
849         });
850         Main.main(getPath("InputMainSuppressionsStringPrinter.java"),
851                 "-s", "7:9", "--tabWidth", "2");
852     }
853 
854     @Test
855     public void testPrintSuppressionConflictingOptionsTvsC() throws Exception {
856         exit.expectSystemExitWithStatus(-1);
857         exit.checkAssertionAfterwards(() -> {
858             assertEquals("Unexpected output log", "Option '-s' cannot be used with other options."
859                     + System.lineSeparator(), systemOut.getLog());
860             assertEquals("Unexpected system error log", "", systemErr.getLog());
861         });
862 
863         Main.main("-c", "/google_checks.xml",
864                 getPath(""), "-s", "2:4");
865     }
866 
867     @Test
868     public void testPrintSuppressionConflictingOptionsTvsP() throws Exception {
869         exit.expectSystemExitWithStatus(-1);
870         exit.checkAssertionAfterwards(() -> {
871             assertEquals("Unexpected output log", "Option '-s' cannot be used with other options."
872                     + System.lineSeparator(), systemOut.getLog());
873             assertEquals("Unexpected system error log", "", systemErr.getLog());
874         });
875 
876         Main.main("-p", getPath("InputMainMycheckstyle.properties"), "-s", "2:4", getPath(""));
877     }
878 
879     @Test
880     public void testPrintSuppressionConflictingOptionsTvsF() throws Exception {
881         exit.expectSystemExitWithStatus(-1);
882         exit.checkAssertionAfterwards(() -> {
883             assertEquals("Unexpected output log", "Option '-s' cannot be used with other options."
884                     + System.lineSeparator(), systemOut.getLog());
885             assertEquals("Unexpected system error log", "", systemErr.getLog());
886         });
887 
888         Main.main("-f", "plain", "-s", "2:4", getPath(""));
889     }
890 
891     @Test
892     public void testPrintSuppressionConflictingOptionsTvsO() throws Exception {
893         final File file = temporaryFolder.newFile("file.output");
894 
895         exit.expectSystemExitWithStatus(-1);
896         exit.checkAssertionAfterwards(() -> {
897             assertEquals("Unexpected output log", "Option '-s' cannot be used with other options."
898                     + System.lineSeparator(), systemOut.getLog());
899             assertEquals("Unexpected system error log", "", systemErr.getLog());
900         });
901 
902         Main.main("-o", file.getCanonicalPath(), "-s", "2:4", getPath(""));
903     }
904 
905     @Test
906     public void testPrintSuppressionOnMoreThanOneFile() throws Exception {
907         exit.expectSystemExitWithStatus(-1);
908         exit.checkAssertionAfterwards(() -> {
909             assertEquals("Unexpected output log", "Printing xpath suppressions is allowed for "
910                     + "only one file."
911                     + System.lineSeparator(), systemOut.getLog());
912             assertEquals("Unexpected system error log", "", systemErr.getLog());
913         });
914 
915         Main.main("-s", "2:4", getPath(""), getPath(""));
916     }
917 
918     @Test
919     public void testGenerateXpathSuppressionOptionOne() throws Exception {
920         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
921                 + "<!DOCTYPE suppressions PUBLIC" + EOL
922                 + "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
923                 + "//EN\"" + EOL
924                 + "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">"
925                 + EOL
926                 + "<suppressions>" + EOL
927                 + "<suppress-xpath" + EOL
928                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
929                 + "       checks=\"JavadocMethodCheck\"" + EOL
930                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
931                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]\"/>" + EOL
932                 + "<suppress-xpath" + EOL
933                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
934                 + "       checks=\"LeftCurlyCheck\"" + EOL
935                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
936                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST\"/>" + EOL
937                 + "<suppress-xpath" + EOL
938                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
939                 + "       checks=\"EmptyBlockCheck\"" + EOL
940                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
941                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
942                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST"
943                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
944                 + "<suppress-xpath" + EOL
945                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
946                 + "       checks=\"EmptyBlockCheck\"" + EOL
947                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
948                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
949                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
950                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
951                 + "<suppress-xpath" + EOL
952                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
953                 + "       checks=\"EmptyBlockCheck\"" + EOL
954                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
955                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
956                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST"
957                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
958                 + "<suppress-xpath" + EOL
959                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
960                 + "       checks=\"EmptyBlockCheck\"" + EOL
961                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
962                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
963                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
964                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
965                 + "<suppress-xpath" + EOL
966                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
967                 + "       checks=\"EmptyBlockCheck\"" + EOL
968                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
969                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
970                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
971                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
972                 + "<suppress-xpath" + EOL
973                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
974                 + "       checks=\"EmptyBlockCheck\"" + EOL
975                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
976                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
977                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
978                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
979                 + "<suppress-xpath" + EOL
980                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
981                 + "       checks=\"EmptyBlockCheck\"" + EOL
982                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
983                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
984                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
985                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
986                 + "<suppress-xpath" + EOL
987                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
988                 + "       checks=\"EmptyBlockCheck\"" + EOL
989                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
990                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
991                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
992                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
993                 + "<suppress-xpath" + EOL
994                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
995                 + "       checks=\"EmptyBlockCheck\"" + EOL
996                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
997                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
998                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
999                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
1000                 + "<suppress-xpath" + EOL
1001                 + "       files=\"InputMainComplexityOverflow.java\"" + EOL
1002                 + "       checks=\"EmptyBlockCheck\"" + EOL
1003                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainComplexityOverflow']]/OBJBLOCK"
1004                 + "/METHOD_DEF[./IDENT[@text='provokeNpathIntegerOverflow']]/SLIST/LITERAL_IF/SLIST"
1005                 + "/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST/LITERAL_IF"
1006                 + "/SLIST/LITERAL_IF/SLIST/LITERAL_IF/SLIST\"/>" + EOL
1007                 + "</suppressions>" + EOL;
1008 
1009         exit.checkAssertionAfterwards(() -> {
1010             assertEquals("Unexpected output log",
1011                     expected, systemOut.getLog());
1012             assertEquals("Unexpected system error log",
1013                     "", systemErr.getLog());
1014         });
1015         Main.main("-c", "/google_checks.xml", "--generate-xpath-suppression",
1016                 getPath("InputMainComplexityOverflow.java"));
1017     }
1018 
1019     @Test
1020     public void testGenerateXpathSuppressionOptionTwo() throws Exception {
1021         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
1022             + "<!DOCTYPE suppressions PUBLIC" + EOL
1023             + "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
1024             + "//EN\"" + EOL
1025             + "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">" + EOL
1026             + "<suppressions>" + EOL
1027             + "<suppress-xpath" + EOL
1028             + "       files=\"InputMainGenerateXpathSuppressions.java\"" + EOL
1029             + "       checks=\"ExplicitInitializationCheck\"" + EOL
1030             + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainGenerateXpathSuppressions']]"
1031             + "/OBJBLOCK/VARIABLE_DEF/IDENT[@text='low']\"/>" + EOL
1032             + "<suppress-xpath" + EOL
1033             + "       files=\"InputMainGenerateXpathSuppressions.java\"" + EOL
1034             + "       checks=\"IllegalThrowsCheck\"" + EOL
1035             + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainGenerateXpathSuppressions']]"
1036             + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='test']]/LITERAL_THROWS"
1037             + "/IDENT[@text='RuntimeException']\"/>" + EOL
1038             + "<suppress-xpath" + EOL
1039             + "       files=\"InputMainGenerateXpathSuppressions.java\"" + EOL
1040             + "       checks=\"NestedForDepthCheck\"" + EOL
1041             + "       query=\"/CLASS_DEF[./IDENT[@text='InputMainGenerateXpathSuppressions']]"
1042             + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='test']]/SLIST/LITERAL_FOR/SLIST"
1043             + "/LITERAL_FOR/SLIST"
1044             + "/LITERAL_FOR\"/>" + EOL
1045             + "</suppressions>" + EOL;
1046 
1047         exit.checkAssertionAfterwards(() -> {
1048             assertEquals("Unexpected output log",
1049                     expected, systemOut.getLog());
1050             assertEquals("Unexpected system error log",
1051                     "", systemErr.getLog());
1052         });
1053         Main.main("-c", getPath("InputMainConfig-xpath-suppressions.xml"),
1054                 "--generate-xpath-suppression",
1055                 getPath("InputMainGenerateXpathSuppressions.java"));
1056     }
1057 
1058     @Test
1059     public void testGenerateXpathSuppressionOptionEmptyConfig() throws Exception {
1060         final String expected = "";
1061 
1062         exit.checkAssertionAfterwards(() -> {
1063             assertEquals("Unexpected output log",
1064                     expected, systemOut.getLog());
1065             assertEquals("Unexpected system error log",
1066                     "", systemErr.getLog());
1067         });
1068         Main.main("-c", getPath("InputMainConfig-empty.xml"), "--generate-xpath-suppression",
1069                 getPath("InputMainComplexityOverflow.java"));
1070     }
1071 
1072     @Test
1073     public void testGenerateXpathSuppressionOptionDefaultTabWidth() throws Exception {
1074         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
1075                 + "<!DOCTYPE suppressions PUBLIC" + EOL
1076                 + "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
1077                 + "//EN\"" + EOL
1078                 + "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">"
1079                 + EOL
1080                 + "<suppressions>" + EOL
1081                 + "<suppress-xpath" + EOL
1082                 + "       files=\"InputMainGenerateXpathSuppressionsTabWidth.java\"" + EOL
1083                 + "       checks=\"ExplicitInitializationCheck\"" + EOL
1084                 + "       query=\"/CLASS_DEF[./IDENT["
1085                 + "@text='InputMainGenerateXpathSuppressionsTabWidth']]"
1086                 + "/OBJBLOCK/VARIABLE_DEF/IDENT[@text='low']\"/>" + EOL
1087                 + "</suppressions>" + EOL;
1088 
1089         exit.checkAssertionAfterwards(() -> {
1090             assertEquals("Unexpected output log",
1091                     expected, systemOut.getLog());
1092             assertEquals("Unexpected system error log",
1093                     "", systemErr.getLog());
1094         });
1095         Main.main("-c", getPath("InputMainConfig-xpath-suppressions.xml"),
1096                 "--generate-xpath-suppression",
1097                 getPath("InputMainGenerateXpathSuppressionsTabWidth.java"));
1098     }
1099 
1100     @Test
1101     public void testGenerateXpathSuppressionOptionCustomTabWidth() throws Exception {
1102         final String expected = "";
1103 
1104         exit.checkAssertionAfterwards(() -> {
1105             assertEquals("Unexpected output log",
1106                     expected, systemOut.getLog());
1107             assertEquals("Unexpected system error log",
1108                     "", systemErr.getLog());
1109         });
1110         Main.main("-c", getPath("InputMainConfig-xpath-suppressions.xml"),
1111                 "--generate-xpath-suppression",
1112                 "--tabWidth", "20",
1113                 getPath("InputMainGenerateXpathSuppressionsTabWidth.java"));
1114     }
1115 
1116     @Test
1117     public void testPrintFullTreeOption() throws Exception {
1118         final String expected = new String(Files.readAllBytes(Paths.get(
1119             getPath("InputMainExpectedInputAstTreeStringPrinterJavadoc.txt"))),
1120             StandardCharsets.UTF_8).replaceAll("\\\\r\\\\n", "\\\\n")
1121                 .replaceAll("\r\n", "\n");
1122 
1123         exit.checkAssertionAfterwards(() -> {
1124             assertEquals("Unexpected output log",
1125                     expected, systemOut.getLog().replaceAll("\\\\r\\\\n", "\\\\n")
1126                             .replaceAll("\r\n", "\n"));
1127             assertEquals("Unexpected system error log", "", systemErr.getLog());
1128         });
1129         Main.main("-J", getPath("InputMainAstTreeStringPrinterJavadoc.java"));
1130     }
1131 
1132     @Test
1133     public void testConflictingOptionsTvsC() throws Exception {
1134         exit.expectSystemExitWithStatus(-1);
1135         exit.checkAssertionAfterwards(() -> {
1136             assertEquals("Unexpected output log", "Option '-t' cannot be used with other options."
1137                 + System.lineSeparator(), systemOut.getLog());
1138             assertEquals("Unexpected system error log", "", systemErr.getLog());
1139         });
1140 
1141         Main.main("-c", "/google_checks.xml", "-t", getPath(""));
1142     }
1143 
1144     @Test
1145     public void testConflictingOptionsTvsP() throws Exception {
1146         exit.expectSystemExitWithStatus(-1);
1147         exit.checkAssertionAfterwards(() -> {
1148             assertEquals("Unexpected output log", "Option '-t' cannot be used with other options."
1149                 + System.lineSeparator(), systemOut.getLog());
1150             assertEquals("Unexpected system error log", "", systemErr.getLog());
1151         });
1152 
1153         Main.main("-p", getPath("InputMainMycheckstyle.properties"), "-t", getPath(""));
1154     }
1155 
1156     @Test
1157     public void testConflictingOptionsTvsF() throws Exception {
1158         exit.expectSystemExitWithStatus(-1);
1159         exit.checkAssertionAfterwards(() -> {
1160             assertEquals("Unexpected output log", "Option '-t' cannot be used with other options."
1161                 + System.lineSeparator(), systemOut.getLog());
1162             assertEquals("Unexpected system error log", "", systemErr.getLog());
1163         });
1164 
1165         Main.main("-f", "plain", "-t", getPath(""));
1166     }
1167 
1168     @Test
1169     public void testConflictingOptionsTvsS() throws Exception {
1170         final File file = temporaryFolder.newFile("file.output");
1171 
1172         exit.expectSystemExitWithStatus(-1);
1173         exit.checkAssertionAfterwards(() -> {
1174             assertEquals("Unexpected output log", "Option '-t' cannot be used with other options."
1175                     + System.lineSeparator(), systemOut.getLog());
1176             assertEquals("Unexpected system error log", "", systemErr.getLog());
1177         });
1178 
1179         Main.main("-s", file.getCanonicalPath(), "-t", getPath(""));
1180     }
1181 
1182     @Test
1183     public void testConflictingOptionsTvsO() throws Exception {
1184         final File file = temporaryFolder.newFile("file.output");
1185 
1186         exit.expectSystemExitWithStatus(-1);
1187         exit.checkAssertionAfterwards(() -> {
1188             assertEquals("Unexpected output log", "Option '-t' cannot be used with other options."
1189                 + System.lineSeparator(), systemOut.getLog());
1190             assertEquals("Unexpected system error log", "", systemErr.getLog());
1191         });
1192 
1193         Main.main("-o", file.getCanonicalPath(), "-t", getPath(""));
1194     }
1195 
1196     @Test
1197     public void testDebugOption() throws Exception {
1198         exit.checkAssertionAfterwards(() -> assertNotEquals("Unexpected system error log",
1199                         "", systemErr.getLog()));
1200         Main.main("-c", "/google_checks.xml", getPath("InputMain.java"), "-d");
1201     }
1202 
1203     @Test
1204     public void testExcludeOption() throws Exception {
1205         exit.expectSystemExitWithStatus(-1);
1206         exit.checkAssertionAfterwards(() -> {
1207             assertEquals("Unexpected output log", "Files to process must be specified, found 0."
1208                 + System.lineSeparator(), systemOut.getLog());
1209             assertEquals("Unexpected system error log", "", systemErr.getLog());
1210         });
1211         Main.main("-c", "/google_checks.xml", getFilePath(""), "-e", getFilePath(""));
1212     }
1213 
1214     @Test
1215     public void testExcludeOptionFile() throws Exception {
1216         exit.expectSystemExitWithStatus(-1);
1217         exit.checkAssertionAfterwards(() -> {
1218             assertEquals("Unexpected output log", "Files to process must be specified, found 0."
1219                 + System.lineSeparator(), systemOut.getLog());
1220             assertEquals("Unexpected system error log", "", systemErr.getLog());
1221         });
1222         Main.main("-c", "/google_checks.xml", getFilePath("InputMain.java"), "-e",
1223                 getFilePath("InputMain.java"));
1224     }
1225 
1226     @Test
1227     public void testExcludeRegexpOption() throws Exception {
1228         exit.expectSystemExitWithStatus(-1);
1229         exit.checkAssertionAfterwards(() -> {
1230             assertEquals("Unexpected output log", "Files to process must be specified, found 0."
1231                 + System.lineSeparator(), systemOut.getLog());
1232             assertEquals("Unexpected output log", "", systemErr.getLog());
1233         });
1234         Main.main("-c", "/google_checks.xml", getFilePath(""), "-x", ".");
1235     }
1236 
1237     @Test
1238     public void testExcludeRegexpOptionFile() throws Exception {
1239         exit.expectSystemExitWithStatus(-1);
1240         exit.checkAssertionAfterwards(() -> {
1241             assertEquals("Unexpected output log", "Files to process must be specified, found 0."
1242                 + System.lineSeparator(), systemOut.getLog());
1243             assertEquals("Unexpected output log", "", systemErr.getLog());
1244         });
1245         Main.main("-c", "/google_checks.xml", getFilePath("InputMain.java"), "-x", ".");
1246     }
1247 
1248     @Test
1249     @SuppressWarnings("unchecked")
1250     public void testExcludeDirectoryNotMatch() throws Exception {
1251         final Class<?> optionsClass = Class.forName(Main.class.getName());
1252         final Method method = optionsClass.getDeclaredMethod("listFiles", File.class, List.class);
1253         method.setAccessible(true);
1254         final List<Pattern> list = new ArrayList<>();
1255         list.add(Pattern.compile("BAD_PATH"));
1256 
1257         final List<File> result = (List<File>) method.invoke(null, new File(getFilePath("")),
1258                 list);
1259         assertNotEquals("Invalid result size", 0, result.size());
1260     }
1261 
1262     @Test
1263     public void testCustomRootModule() throws Exception {
1264         TestRootModuleChecker.reset();
1265 
1266         exit.checkAssertionAfterwards(() -> {
1267             assertEquals("Unexpected output log", "", systemOut.getLog());
1268             assertEquals("Unexpected system error log", "", systemErr.getLog());
1269             assertTrue("Invalid Checker state", TestRootModuleChecker.isProcessed());
1270         });
1271         Main.main("-c", getPath("InputMainConfig-custom-root-module.xml"),
1272                 getPath("InputMain.java"));
1273         assertTrue("RootModule should be destroyed", TestRootModuleChecker.isDestroyed());
1274     }
1275 
1276     @Test
1277     public void testCustomSimpleRootModule() throws Exception {
1278         TestRootModuleChecker.reset();
1279         exit.expectSystemExitWithStatus(-2);
1280         exit.checkAssertionAfterwards(() -> {
1281             final String checkstylePackage = "com.puppycrawl.tools.checkstyle.";
1282             final LocalizedMessage unableToInstantiateExceptionMessage = new LocalizedMessage(1,
1283                     Definitions.CHECKSTYLE_BUNDLE,
1284                     "PackageObjectFactory.unableToInstantiateExceptionMessage",
1285                     new String[] {"TestRootModuleChecker", checkstylePackage
1286                             + "TestRootModuleChecker, "
1287                             + "TestRootModuleCheckerCheck, " + checkstylePackage
1288                             + "TestRootModuleCheckerCheck"},
1289                     null, getClass(), null);
1290             assertEquals("Unexpected output log", errorCounterOneMessage.getMessage() + EOL,
1291                     systemOut.getLog());
1292             assertTrue("Unexpected system error log",
1293                     systemErr.getLog().startsWith(checkstylePackage + "api.CheckstyleException: "
1294                     + unableToInstantiateExceptionMessage.getMessage()));
1295             assertFalse("Invalid checker state", TestRootModuleChecker.isProcessed());
1296         });
1297         Main.main("-c", getPath("InputMainConfig-custom-simple-root-module.xml"),
1298                 getPath("InputMain.java"));
1299     }
1300 
1301     @Test
1302     public void testExecuteIgnoredModule() throws Exception {
1303         exit.expectSystemExitWithStatus(-2);
1304         exit.checkAssertionAfterwards(() -> {
1305             final String expectedExceptionMessage = errorCounterOneMessage.getMessage() + EOL;
1306             assertEquals("Unexpected output log", expectedExceptionMessage, systemOut.getLog());
1307 
1308             final String cause = "com.puppycrawl.tools.checkstyle.api.CheckstyleException:"
1309                     + " cannot initialize module TreeWalker - ";
1310             assertTrue("Unexpected system error log", systemErr.getLog().startsWith(cause));
1311         });
1312 
1313         Main.main("-c", getPath("InputMainConfig-non-existent-classname-ignore.xml"),
1314                 "--executeIgnoredModules",
1315                 getPath("InputMain.java"));
1316     }
1317 
1318     @Test
1319     public void testInvalidCheckerThreadsNumber() throws Exception {
1320         exit.expectSystemExitWithStatus(-1);
1321         exit.checkAssertionAfterwards(() -> {
1322             assertEquals("Unexpected output log", "", systemOut.getLog());
1323             assertEquals("Unexpected system error log",
1324                     "Invalid value for option '--checker-threads-number': 'invalid' is not an int"
1325                     + EOL + SHORT_USAGE, systemErr.getLog());
1326         });
1327         Main.main("-C", "invalid", "-c", "/google_checks.xml", getPath("InputMain.java"));
1328     }
1329 
1330     @Test
1331     public void testInvalidTreeWalkerThreadsNumber() throws Exception {
1332         exit.expectSystemExitWithStatus(-1);
1333         exit.checkAssertionAfterwards(() -> {
1334             assertEquals("Unexpected output log", "", systemOut.getLog());
1335             assertEquals("Unexpected system error log",
1336                     "Invalid value for option '--tree-walker-threads-number': "
1337                     + "'invalid' is not an int" + EOL + SHORT_USAGE, systemErr.getLog());
1338         });
1339         Main.main("-W", "invalid", "-c", "/google_checks.xml", getPath("InputMain.java"));
1340     }
1341 
1342     @Test
1343     public void testZeroCheckerThreadsNumber() throws Exception {
1344         exit.expectSystemExitWithStatus(-1);
1345         exit.checkAssertionAfterwards(() -> {
1346             assertEquals("Unexpected output log", "Checker threads number must be greater than zero"
1347                 + System.lineSeparator(), systemOut.getLog());
1348             assertEquals("Unexpected system error log", "", systemErr.getLog());
1349         });
1350         Main.main("-C", "0", "-c", "/google_checks.xml", getPath("InputMain.java"));
1351     }
1352 
1353     @Test
1354     public void testZeroTreeWalkerThreadsNumber() throws Exception {
1355         exit.expectSystemExitWithStatus(-1);
1356         exit.checkAssertionAfterwards(() -> {
1357             assertEquals("Unexpected output log",
1358                     "TreeWalker threads number must be greater than zero"
1359                 + System.lineSeparator(), systemOut.getLog());
1360             assertEquals("Unexpected system error log", "", systemErr.getLog());
1361         });
1362         Main.main("-W", "0", "-c", "/google_checks.xml", getPath("InputMain.java"));
1363     }
1364 
1365     @Test
1366     public void testCheckerThreadsNumber() throws Exception {
1367         TestRootModuleChecker.reset();
1368 
1369         exit.checkAssertionAfterwards(() -> {
1370             assertEquals("Unexpected output log", "", systemOut.getLog());
1371             assertEquals("Unexpected system error log", "", systemErr.getLog());
1372             assertTrue("Invalid checker state", TestRootModuleChecker.isProcessed());
1373             final DefaultConfiguration config =
1374                     (DefaultConfiguration) TestRootModuleChecker.getConfig();
1375             final ThreadModeSettings multiThreadModeSettings = config.getThreadModeSettings();
1376             assertEquals("Invalid checker thread number",
1377                     4, multiThreadModeSettings.getCheckerThreadsNumber());
1378             assertEquals("Invalid checker thread number",
1379                     1, multiThreadModeSettings.getTreeWalkerThreadsNumber());
1380         });
1381         Main.main("-C", "4", "-c", getPath("InputMainConfig-custom-root-module.xml"),
1382             getPath("InputMain.java"));
1383     }
1384 
1385     @Test
1386     public void testTreeWalkerThreadsNumber() throws Exception {
1387         TestRootModuleChecker.reset();
1388 
1389         exit.checkAssertionAfterwards(() -> {
1390             assertEquals("Unexpected output log", "", systemOut.getLog());
1391             assertEquals("Unexpected system error log", "", systemErr.getLog());
1392             assertTrue("Invalid checker state", TestRootModuleChecker.isProcessed());
1393             final DefaultConfiguration config =
1394                     (DefaultConfiguration) TestRootModuleChecker.getConfig();
1395             final ThreadModeSettings multiThreadModeSettings = config.getThreadModeSettings();
1396             assertEquals("Invalid checker thread number",
1397                     1, multiThreadModeSettings.getCheckerThreadsNumber());
1398             assertEquals("Invalid checker thread number",
1399                     4, multiThreadModeSettings.getTreeWalkerThreadsNumber());
1400         });
1401         Main.main("-W", "4", "-c", getPath("InputMainConfig-custom-root-module.xml"),
1402             getPath("InputMain.java"));
1403     }
1404 
1405     @Test
1406     public void testModuleNameInSingleThreadMode() throws Exception {
1407         TestRootModuleChecker.reset();
1408 
1409         exit.checkAssertionAfterwards(() -> {
1410             assertEquals("Unexpected output log", "", systemOut.getLog());
1411             assertEquals("Unexpected system error log", "", systemErr.getLog());
1412             assertTrue("Invalid checker state", TestRootModuleChecker.isProcessed());
1413             final DefaultConfiguration config =
1414                     (DefaultConfiguration) TestRootModuleChecker.getConfig();
1415             final ThreadModeSettings multiThreadModeSettings =
1416                 config.getThreadModeSettings();
1417             assertEquals("Invalid checker thread number",
1418                     1, multiThreadModeSettings.getCheckerThreadsNumber());
1419             assertEquals("Invalid checker thread number",
1420                     1, multiThreadModeSettings.getTreeWalkerThreadsNumber());
1421             final Configuration checkerConfiguration = config
1422                 .getChildren()[0];
1423             assertEquals("Invalid checker name", "Checker", checkerConfiguration.getName());
1424             final Configuration treeWalkerConfig = checkerConfiguration.getChildren()[0];
1425             assertEquals("Invalid checker children name", "TreeWalker", treeWalkerConfig.getName());
1426         });
1427         Main.main("-C", "1", "-W", "1", "-c", getPath("InputMainConfig-multi-thread-mode.xml"),
1428             getPath("InputMain.java"));
1429     }
1430 
1431     @Test
1432     public void testModuleNameInMultiThreadMode() throws Exception {
1433         TestRootModuleChecker.reset();
1434 
1435         try {
1436             Main.main("-C", "4", "-W", "4", "-c", getPath("InputMainConfig-multi-thread-mode.xml"),
1437                 getPath("InputMain.java"));
1438             fail("An exception is expected");
1439         }
1440         catch (IllegalArgumentException ex) {
1441             assertEquals("Invalid error message",
1442                     "Multi thread mode for Checker module is not implemented",
1443                 ex.getMessage());
1444         }
1445     }
1446 
1447     @Test
1448     public void testMissingFiles() throws Exception {
1449         exit.expectSystemExitWithStatus(-1);
1450         exit.checkAssertionAfterwards(() -> {
1451             final String usage = "Missing required parameter: <files>" + EOL + SHORT_USAGE;
1452             assertEquals("Unexpected output log", "", systemOut.getLog());
1453             assertEquals("Unexpected system error log", usage, systemErr.getLog());
1454         });
1455         Main.main();
1456     }
1457 
1458     @Test
1459     public void testOutputFormatToStringLowercase() {
1460         assertEquals("expected xml", "xml", Main.OutputFormat.XML.toString());
1461         assertEquals("expected plain", "plain", Main.OutputFormat.PLAIN.toString());
1462     }
1463 
1464     @Test
1465     public void testXmlOutputFormatCreateListener() {
1466         final ByteArrayOutputStream out = new ByteArrayOutputStream();
1467         final AuditListener listener = Main.OutputFormat.XML.createListener(out,
1468                 AutomaticBean.OutputStreamOptions.CLOSE);
1469         assertTrue("listener is XMLLogger", listener instanceof XMLLogger);
1470     }
1471 
1472     @Test
1473     public void testPlainOutputFormatCreateListener() {
1474         final ByteArrayOutputStream out = new ByteArrayOutputStream();
1475         final AuditListener listener = Main.OutputFormat.PLAIN.createListener(out,
1476                 AutomaticBean.OutputStreamOptions.CLOSE);
1477         assertTrue("listener is DefaultLogger", listener instanceof DefaultLogger);
1478     }
1479 
1480 }