View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static org.mockito.Mockito.mockConstruction;
24  import static org.mockito.Mockito.when;
25  
26  import java.io.File;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Collections;
32  import java.util.List;
33  import java.util.Properties;
34  
35  import org.junit.jupiter.api.Test;
36  import org.mockito.MockedConstruction;
37  import org.xml.sax.InputSource;
38  import org.xml.sax.SAXException;
39  
40  import com.puppycrawl.tools.checkstyle.ConfigurationLoader.IgnoredModulesOptions;
41  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
42  import com.puppycrawl.tools.checkstyle.api.Configuration;
43  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
44  
45  /**
46   * Unit test for ConfigurationLoader.
47   */
48  public class ConfigurationLoaderTest extends AbstractPathTestSupport {
49  
50      @Override
51      protected String getPackageLocation() {
52          return "com/puppycrawl/tools/checkstyle/configurationloader";
53      }
54  
55      private Configuration loadConfiguration(String name) throws Exception {
56          return loadConfiguration(name, new Properties());
57      }
58  
59      private Configuration loadConfiguration(
60          String name, Properties props) throws Exception {
61          final String fName = getPath(name);
62  
63          return ConfigurationLoader.loadConfiguration(fName, new PropertiesExpander(props));
64      }
65  
66      private static Object getInternalLoaderInstance(PropertyResolver resolver)
67               throws Exception {
68  
69          final ConfigurationLoader loader = TestUtil.instantiate(ConfigurationLoader.class,
70                  resolver, false, ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE);
71  
72          return TestUtil.getInternalState(loader, "saxHandler", Object.class);
73      }
74  
75      private static String invokeReplacePropertiesMethod(
76              Object internalLoader, String value, String defaultValue)
77              throws ReflectiveOperationException {
78          return TestUtil.invokeMethod(internalLoader, "replaceProperties",
79                  String.class, value, defaultValue);
80      }
81  
82      private static void invokeParsePropertyStringMethod(
83              Object internalLoader,
84              String value,
85              Collection<String> fragments,
86              Collection<String> propertyRefs)
87              throws ReflectiveOperationException {
88          TestUtil.invokeVoidMethod(
89                  internalLoader, "parsePropertyString", value, fragments, propertyRefs);
90      }
91  
92      @Test
93      public void testReplacePropertiesNoReplace() throws Exception {
94          final String[] testValues = {"", "a", "$a", "{a",
95                                       "{a}", "a}", "$a}", "$", "a$b", };
96          final Properties props = initProperties();
97          final Object internalLoader = getInternalLoaderInstance(new PropertiesExpander(props));
98  
99          for (String testValue : testValues) {
100             final String value = invokeReplacePropertiesMethod(internalLoader, testValue, null);
101             assertWithMessage("\"" + testValue + "\"")
102                 .that(testValue)
103                 .isEqualTo(value);
104         }
105     }
106 
107     @Test
108     public void testReplacePropertiesSyntaxError() throws Exception {
109         final Properties props = initProperties();
110         final Object internalLoader = getInternalLoaderInstance(new PropertiesExpander(props));
111 
112         try {
113             final String value = invokeReplacePropertiesMethod(internalLoader, "${a", null);
114             assertWithMessage("expected to fail, instead got: " + value).fail();
115         }
116         catch (ReflectiveOperationException exc) {
117             assertWithMessage("Invalid exception cause message")
118                 .that(exc.getCause())
119                 .isInstanceOf(CheckstyleException.class);
120             assertWithMessage("Invalid exception message")
121                 .that(exc.getCause().getMessage())
122                 .isEqualTo("Syntax error in property: ${a");
123         }
124     }
125 
126     @Test
127     public void testReplacePropertiesMissingProperty() throws Exception {
128         final Properties props = initProperties();
129         final Object internalLoader = getInternalLoaderInstance(new PropertiesExpander(props));
130 
131         try {
132             final String value = invokeReplacePropertiesMethod(internalLoader, "${c}", null);
133             assertWithMessage("expected to fail, instead got: " + value).fail();
134         }
135         catch (ReflectiveOperationException exc) {
136             assertWithMessage("Invalid exception cause message")
137                 .that(exc.getCause())
138                 .isInstanceOf(CheckstyleException.class);
139             assertWithMessage("Invalid exception message")
140                 .that(exc.getCause().getMessage())
141                 .isEqualTo("Property ${c} has not been set");
142         }
143     }
144 
145     @Test
146     public void testReplacePropertiesReplace() throws Exception {
147         final String[][] testValues = {
148             {"${a}", "A"},
149             {"x${a}", "xA"},
150             {"${a}x", "Ax"},
151             {"${a}${b}", "AB"},
152             {"x${a}${b}", "xAB"},
153             {"${a}x${b}", "AxB"},
154             {"${a}${b}x", "ABx"},
155             {"x${a}y${b}", "xAyB"},
156             {"${a}x${b}y", "AxBy"},
157             {"x${a}${b}y", "xABy"},
158             {"x${a}y${b}z", "xAyBz"},
159             {"$$", "$"},
160         };
161         final Properties props = initProperties();
162         final Object internalLoader = getInternalLoaderInstance(new PropertiesExpander(props));
163 
164         for (String[] testValue : testValues) {
165             final String value = invokeReplacePropertiesMethod(internalLoader, testValue[0], null);
166             assertWithMessage("\"" + testValue[0] + "\"")
167                 .that(value)
168                 .isEqualTo(testValue[1]);
169         }
170     }
171 
172     @Test
173     public void testReplacePropertiesDefault() throws Exception {
174         final Properties props = new Properties();
175         final String defaultValue = "defaultValue";
176         final Object internalLoader = getInternalLoaderInstance(new PropertiesExpander(props));
177 
178         final String value =
179                 invokeReplacePropertiesMethod(
180                         internalLoader, "${checkstyle.basedir}", defaultValue);
181 
182         assertWithMessage("Invalid property value")
183             .that(value)
184             .isEqualTo(defaultValue);
185     }
186 
187     @Test
188     public void testParsePropertyString() throws Exception {
189         final Properties props = initProperties();
190         final Object internalLoader = getInternalLoaderInstance(new PropertiesExpander(props));
191 
192         final List<String> propertyRefs = new ArrayList<>();
193         final List<String> fragments = new ArrayList<>();
194 
195         invokeParsePropertyStringMethod(internalLoader, "$", fragments, propertyRefs);
196         assertWithMessage("Fragments list has unexpected amount of items")
197             .that(fragments)
198             .hasSize(1);
199     }
200 
201     private static Properties initProperties() {
202         final Properties props = new Properties();
203         props.setProperty("a", "A");
204         props.setProperty("b", "B");
205         return props;
206     }
207 
208     @Test
209     public void testResourceLoadConfiguration() throws Exception {
210         final Properties props = new Properties();
211         props.setProperty("checkstyle.basedir", "basedir");
212 
213         // load config that's only found in the classpath
214         final DefaultConfiguration config =
215             (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
216                 getPath("InputConfigurationLoaderChecks.xml"), new PropertiesExpander(props));
217 
218         // verify the root, and property substitution
219         final Properties attributes = new Properties();
220         attributes.setProperty("tabWidth", "4");
221         attributes.setProperty("basedir", "basedir");
222         verifyConfigNode(config, "Checker", 3, attributes);
223     }
224 
225     @Test
226     public void testResourceLoadConfigurationWithMultiThreadConfiguration() throws Exception {
227         final Properties props = new Properties();
228         props.setProperty("checkstyle.basedir", "basedir");
229 
230         final PropertiesExpander propertiesExpander = new PropertiesExpander(props);
231         final String configPath = getPath("InputConfigurationLoaderChecks.xml");
232         final ThreadModeSettings multiThreadModeSettings =
233             new ThreadModeSettings(4, 2);
234 
235         try {
236             ConfigurationLoader.loadConfiguration(
237                 configPath, propertiesExpander, multiThreadModeSettings);
238             assertWithMessage("An exception is expected").fail();
239         }
240         catch (IllegalArgumentException exc) {
241             assertWithMessage("Invalid exception message")
242                 .that(exc.getMessage())
243                 .isEqualTo("Multi thread mode for Checker module is not implemented");
244         }
245     }
246 
247     @Test
248     public void testResourceLoadConfigurationWithSingleThreadConfiguration() throws Exception {
249         final Properties props = new Properties();
250         props.setProperty("checkstyle.basedir", "basedir");
251 
252         final PropertiesExpander propertiesExpander = new PropertiesExpander(props);
253         final String configPath = getPath("InputConfigurationLoaderChecks.xml");
254         final ThreadModeSettings singleThreadModeSettings =
255             ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE;
256 
257         final DefaultConfiguration config =
258             (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
259                 configPath, propertiesExpander, singleThreadModeSettings);
260 
261         final Properties attributes = new Properties();
262         attributes.setProperty("tabWidth", "4");
263         attributes.setProperty("basedir", "basedir");
264         verifyConfigNode(config, "Checker", 3, attributes);
265     }
266 
267     @Test
268     public void testEmptyConfiguration() throws Exception {
269         final DefaultConfiguration config =
270             (DefaultConfiguration) loadConfiguration("InputConfigurationLoaderEmpty.xml");
271         verifyConfigNode(config, "Checker", 0, new Properties());
272     }
273 
274     @Test
275     public void testEmptyModuleResolver() throws Exception {
276         final DefaultConfiguration config =
277             (DefaultConfiguration) loadConfiguration(
278                 "InputConfigurationLoaderEmpty.xml", new Properties());
279         verifyConfigNode(config, "Checker", 0, new Properties());
280     }
281 
282     @Test
283     public void testMissingPropertyName() throws Exception {
284         try {
285             loadConfiguration("InputConfigurationLoaderMissingPropertyName.xml");
286             assertWithMessage("missing property name").fail();
287         }
288         catch (CheckstyleException exc) {
289             assertWithMessage("Invalid exception message: " + exc.getMessage())
290                     .that(exc.getMessage())
291                     .contains("\"name\"");
292             assertWithMessage("Invalid exception message: " + exc.getMessage())
293                     .that(exc.getMessage())
294                     .contains("\"property\"");
295             assertWithMessage("Invalid exception message: " + exc.getMessage())
296                     .that(exc.getMessage())
297                     .endsWith(":8:41");
298         }
299     }
300 
301     @Test
302     public void testMissingPropertyNameInMethodWithBooleanParameter() throws Exception {
303         try {
304             final String fName = getPath("InputConfigurationLoaderMissingPropertyName.xml");
305             ConfigurationLoader.loadConfiguration(fName, new PropertiesExpander(new Properties()),
306                     IgnoredModulesOptions.EXECUTE);
307 
308             assertWithMessage("missing property name").fail();
309         }
310         catch (CheckstyleException exc) {
311             assertWithMessage("Invalid exception message: " + exc.getMessage())
312                     .that(exc.getMessage())
313                     .contains("\"name\"");
314             assertWithMessage("Invalid exception message: " + exc.getMessage())
315                     .that(exc.getMessage())
316                     .contains("\"property\"");
317             assertWithMessage("Invalid exception message: " + exc.getMessage())
318                     .that(exc.getMessage())
319                     .endsWith(":8:41");
320         }
321     }
322 
323     @Test
324     public void testMissingPropertyValue() throws Exception {
325         try {
326             loadConfiguration("InputConfigurationLoaderMissingPropertyValue.xml");
327             assertWithMessage("missing property value").fail();
328         }
329         catch (CheckstyleException exc) {
330             assertWithMessage("Invalid exception message: " + exc.getMessage())
331                     .that(exc.getMessage())
332                     .contains("\"value\"");
333             assertWithMessage("Invalid exception message: " + exc.getMessage())
334                     .that(exc.getMessage())
335                     .contains("\"property\"");
336             assertWithMessage("Invalid exception message: " + exc.getMessage())
337                     .that(exc.getMessage())
338                     .endsWith(":8:43");
339         }
340     }
341 
342     @Test
343     public void testMissingConfigName() throws Exception {
344         try {
345             loadConfiguration("InputConfigurationLoaderMissingConfigName.xml");
346             assertWithMessage("missing module name").fail();
347         }
348         catch (CheckstyleException exc) {
349             assertWithMessage("Invalid exception message: " + exc.getMessage())
350                     .that(exc.getMessage())
351                     .contains("\"name\"");
352             assertWithMessage("Invalid exception message: " + exc.getMessage())
353                     .that(exc.getMessage())
354                     .contains("\"module\"");
355             assertWithMessage("Invalid exception message: " + exc.getMessage())
356                     .that(exc.getMessage())
357                     .endsWith(":7:23");
358         }
359     }
360 
361     @Test
362     public void testMissingConfigParent() throws Exception {
363         try {
364             loadConfiguration("InputConfigurationLoaderMissingConfigParent.xml");
365             assertWithMessage("missing module parent").fail();
366         }
367         catch (CheckstyleException exc) {
368             assertWithMessage("Invalid exception message: " + exc.getMessage())
369                     .that(exc.getMessage())
370                     .contains("\"property\"");
371             assertWithMessage("Invalid exception message: " + exc.getMessage())
372                     .that(exc.getMessage())
373                     .contains("\"module\"");
374             assertWithMessage("Invalid exception message: " + exc.getMessage())
375                     .that(exc.getMessage())
376                     .endsWith(":8:38");
377         }
378     }
379 
380     @Test
381     public void testCheckstyleChecks() throws Exception {
382         final Properties props = new Properties();
383         props.setProperty("checkstyle.basedir", "basedir");
384 
385         final DefaultConfiguration config =
386             (DefaultConfiguration) loadConfiguration(
387                 "InputConfigurationLoaderChecks.xml", props);
388 
389         // verify the root, and property substitution
390         final Properties atts = new Properties();
391         atts.setProperty("tabWidth", "4");
392         atts.setProperty("basedir", "basedir");
393         verifyConfigNode(config, "Checker", 3, atts);
394 
395         // verify children
396         final Configuration[] children = config.getChildren();
397         atts.clear();
398         verifyConfigNode(
399             (DefaultConfiguration) children[1], "JavadocPackage", 0, atts);
400         verifyConfigNode(
401             (DefaultConfiguration) children[2], "Translation", 0, atts);
402         atts.setProperty("testName", "testValue");
403         verifyConfigNode(
404             (DefaultConfiguration) children[0],
405             "TreeWalker",
406             8,
407             atts);
408 
409         // verify TreeWalker's first, last, NoWhitespaceAfterCheck
410         final Configuration[] grandchildren = children[0].getChildren();
411         atts.clear();
412         verifyConfigNode(
413             (DefaultConfiguration) grandchildren[0],
414             "AvoidStarImport",
415             0,
416             atts);
417         atts.setProperty("format", "System.out.println");
418         verifyConfigNode(
419             (DefaultConfiguration) grandchildren[grandchildren.length - 1],
420             "GenericIllegalRegexp",
421             0,
422             atts);
423         atts.clear();
424         atts.setProperty("tokens", "DOT");
425         atts.setProperty("allowLineBreaks", "true");
426         verifyConfigNode(
427             (DefaultConfiguration) grandchildren[6],
428             "NoWhitespaceAfter",
429             0,
430             atts);
431     }
432 
433     @Test
434     public void testCustomMessages() throws Exception {
435         final Properties props = new Properties();
436         props.setProperty("checkstyle.basedir", "basedir");
437 
438         final DefaultConfiguration config =
439             (DefaultConfiguration) loadConfiguration(
440                 "InputConfigurationLoaderCustomMessages.xml", props);
441 
442         final Configuration[] children = config.getChildren();
443         final Configuration[] grandchildren = children[0].getChildren();
444         final List<String> messages = new ArrayList<>(grandchildren[0].getMessages().values());
445         final String expectedKey = "name.invalidPattern";
446         final List<String> expectedMessages = Collections
447                 .singletonList("Member ''{0}'' must start with ''m'' (checked pattern ''{1}'').");
448         assertWithMessage("Messages should contain key: " + expectedKey)
449                 .that(grandchildren[0].getMessages())
450                 .containsKey(expectedKey);
451         assertWithMessage("Message is not expected")
452                 .that(messages)
453                 .isEqualTo(expectedMessages);
454     }
455 
456     private static void verifyConfigNode(
457         DefaultConfiguration config, String name, int childrenLength,
458         Properties atts) throws Exception {
459         assertWithMessage("name.")
460             .that(config.getName())
461             .isEqualTo(name);
462         assertWithMessage("children.length.")
463             .that(config.getChildren().length)
464             .isEqualTo(childrenLength);
465 
466         final String[] attNames = config.getPropertyNames();
467         assertWithMessage("attributes.length")
468             .that(attNames.length)
469             .isEqualTo(atts.size());
470 
471         for (String attName : attNames) {
472             final String attribute = config.getProperty(attName);
473             assertWithMessage("attribute[" + attName + "]")
474                 .that(attribute)
475                 .isEqualTo(atts.getProperty(attName));
476         }
477     }
478 
479     @Test
480     public void testSystemEntity() throws Exception {
481         final Properties props = new Properties();
482         props.setProperty("checkstyle.basedir", "basedir");
483 
484         final DefaultConfiguration config =
485             (DefaultConfiguration) loadConfiguration(
486                 "InputConfigurationLoaderSystemDoctype.xml", props);
487 
488         final Properties atts = new Properties();
489         atts.setProperty("tabWidth", "4");
490 
491         verifyConfigNode(config, "Checker", 0, atts);
492     }
493 
494     @Test
495     public void testExternalEntity() throws Exception {
496         final Properties props = new Properties();
497         props.setProperty("checkstyle.basedir", "basedir");
498 
499         System.setProperty(
500                 XmlLoader.LoadExternalDtdFeatureProvider.ENABLE_EXTERNAL_DTD_LOAD, "true");
501 
502         final DefaultConfiguration config =
503             (DefaultConfiguration) loadConfiguration(
504                 "InputConfigurationLoaderExternalEntity.xml", props);
505 
506         final Properties atts = new Properties();
507         atts.setProperty("tabWidth", "4");
508         atts.setProperty("basedir", "basedir");
509         verifyConfigNode(config, "Checker", 2, atts);
510     }
511 
512     @Test
513     public void testExternalEntitySubdirectory() throws Exception {
514         final Properties props = new Properties();
515         props.setProperty("checkstyle.basedir", "basedir");
516 
517         System.setProperty(
518                 XmlLoader.LoadExternalDtdFeatureProvider.ENABLE_EXTERNAL_DTD_LOAD, "true");
519 
520         final DefaultConfiguration config =
521             (DefaultConfiguration) loadConfiguration(
522                 "subdir/InputConfigurationLoaderExternalEntitySubDir.xml", props);
523 
524         final Properties attributes = new Properties();
525         attributes.setProperty("tabWidth", "4");
526         attributes.setProperty("basedir", "basedir");
527         verifyConfigNode(config, "Checker", 2, attributes);
528     }
529 
530     @Test
531     public void testExternalEntityFromUri() throws Exception {
532         final Properties props = new Properties();
533         props.setProperty("checkstyle.basedir", "basedir");
534 
535         System.setProperty(
536                 XmlLoader.LoadExternalDtdFeatureProvider.ENABLE_EXTERNAL_DTD_LOAD, "true");
537 
538         final File file = new File(
539                 getPath("subdir/InputConfigurationLoaderExternalEntitySubDir.xml"));
540         final DefaultConfiguration config =
541             (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
542                     file.toURI().toString(), new PropertiesExpander(props));
543 
544         final Properties atts = new Properties();
545         atts.setProperty("tabWidth", "4");
546         atts.setProperty("basedir", "basedir");
547         verifyConfigNode(config, "Checker", 2, atts);
548     }
549 
550     @Test
551     public void testIncorrectTag() throws Exception {
552         final Class<?> aClassParent = ConfigurationLoader.class;
553         final Object objParent = TestUtil.instantiate(aClassParent, null, true, null);
554 
555         final Class<?> aClass = Class.forName("com.puppycrawl.tools.checkstyle."
556                 + "ConfigurationLoader$InternalLoader");
557         final Object obj = TestUtil.instantiate(aClass, objParent);
558 
559         try {
560             TestUtil.invokeVoidMethod(obj, "startElement", "", "", "hello", null);
561 
562             assertWithMessage("InvocationTargetException is expected").fail();
563         }
564         catch (ReflectiveOperationException exc) {
565             assertWithMessage("Invalid exception cause message")
566                 .that(exc)
567                     .hasCauseThat()
568                         .hasMessageThat()
569                         .isEqualTo("Unknown name:" + "hello" + ".");
570         }
571     }
572 
573     @Test
574     public void testNonExistentPropertyName() throws Exception {
575         try {
576             loadConfiguration("InputConfigurationLoaderNonexistentProperty.xml");
577             assertWithMessage("exception in expected").fail();
578         }
579         catch (CheckstyleException exc) {
580             assertWithMessage("Invalid exception message")
581                 .that(exc.getMessage())
582                 .isEqualTo("unable to parse configuration stream");
583             assertWithMessage("Expected cause of type SAXException")
584                 .that(exc.getCause())
585                 .isInstanceOf(SAXException.class);
586             assertWithMessage("Expected cause of type CheckstyleException")
587                 .that(exc.getCause().getCause())
588                 .isInstanceOf(CheckstyleException.class);
589             assertWithMessage("Invalid exception cause message")
590                 .that(exc)
591                 .hasCauseThat()
592                 .hasCauseThat()
593                 .hasMessageThat()
594                 .isEqualTo("Property ${nonexistent} has not been set");
595         }
596     }
597 
598     @Test
599     public void testConfigWithIgnore() throws Exception {
600         final DefaultConfiguration config =
601                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
602                         getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"),
603                         new PropertiesExpander(new Properties()), IgnoredModulesOptions.OMIT);
604 
605         final Configuration[] children = config.getChildren();
606         final int length = children[0].getChildren().length;
607         assertWithMessage("Invalid children count")
608             .that(length)
609             .isEqualTo(0);
610     }
611 
612     @Test
613     public void testConfigWithIgnoreUsingInputSource() throws Exception {
614         final DefaultConfiguration config =
615                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(new InputSource(
616                         new File(getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"))
617                             .toURI().toString()),
618                         new PropertiesExpander(new Properties()), IgnoredModulesOptions.OMIT);
619 
620         final Configuration[] children = config.getChildren();
621         final int length = children[0].getChildren().length;
622         assertWithMessage("Invalid children count")
623             .that(length)
624             .isEqualTo(0);
625     }
626 
627     @Test
628     public void testConfigCheckerWithIgnore() throws Exception {
629         final DefaultConfiguration config =
630                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
631                         getPath("InputConfigurationLoaderCheckerIgnoreSeverity.xml"),
632                         new PropertiesExpander(new Properties()), IgnoredModulesOptions.OMIT);
633 
634         final Configuration[] children = config.getChildren();
635         assertWithMessage("Invalid children count")
636             .that(children.length)
637             .isEqualTo(0);
638     }
639 
640     @Test
641     public void testLoadConfigurationWrongUrl() {
642         try {
643             ConfigurationLoader.loadConfiguration(
644                     ";InputConfigurationLoaderModuleIgnoreSeverity.xml",
645                     new PropertiesExpander(new Properties()), IgnoredModulesOptions.OMIT);
646 
647             assertWithMessage("Exception is expected").fail();
648         }
649         catch (CheckstyleException exc) {
650             assertWithMessage("Invalid exception message")
651                 .that(exc.getMessage())
652                 .isEqualTo("Unable to find: ;InputConfigurationLoaderModuleIgnoreSeverity.xml");
653         }
654     }
655 
656     @Test
657     public void testLoadConfigurationDeprecated() throws Exception {
658         final DefaultConfiguration config =
659                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
660                         new InputSource(Files.newInputStream(Path.of(
661                             getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml")))),
662                         new PropertiesExpander(new Properties()), IgnoredModulesOptions.OMIT);
663 
664         final Configuration[] children = config.getChildren();
665         final int length = children[0].getChildren().length;
666         assertWithMessage("Invalid children count")
667             .that(length)
668             .isEqualTo(0);
669     }
670 
671     @Test
672     public void testLoadConfigurationFromClassPath() throws Exception {
673         final DefaultConfiguration config =
674                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
675                         getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"),
676                         new PropertiesExpander(new Properties()), IgnoredModulesOptions.OMIT);
677 
678         final Configuration[] children = config.getChildren();
679         final int length = children[0].getChildren().length;
680         assertWithMessage("Invalid children count")
681             .that(length)
682             .isEqualTo(0);
683     }
684 
685     @Test
686     public void testLoadConfigurationFromClassPathWithNonAsciiSymbolsInPath() throws Exception {
687         final DefaultConfiguration config =
688                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
689                     getResourcePath("棵¥/InputConfigurationLoaderDefaultProperty.xml"),
690                         new PropertiesExpander(new Properties()));
691 
692         final Properties expectedPropertyValues = new Properties();
693         expectedPropertyValues.setProperty("tabWidth", "2");
694         expectedPropertyValues.setProperty("basedir", ".");
695         // charset property uses 2 variables, one is not defined, so default becomes a result value
696         expectedPropertyValues.setProperty("charset", "ASCII");
697         verifyConfigNode(config, "Checker", 0, expectedPropertyValues);
698     }
699 
700     @Test
701     public void testConstructors() throws Exception {
702         final Properties props = new Properties();
703         props.setProperty("checkstyle.basedir", "basedir");
704         final String fName = getPath("InputConfigurationLoaderChecks.xml");
705 
706         final Configuration configuration = ConfigurationLoader.loadConfiguration(fName,
707                 new PropertiesExpander(props), ConfigurationLoader.IgnoredModulesOptions.OMIT);
708         assertWithMessage("Name is not expected")
709             .that(configuration.getName())
710             .isEqualTo("Checker");
711 
712         final DefaultConfiguration configuration1 =
713                 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
714                         new InputSource(Files.newInputStream(Path.of(
715                             getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml")))),
716                         new PropertiesExpander(new Properties()),
717                         ConfigurationLoader.IgnoredModulesOptions.EXECUTE);
718 
719         final Configuration[] children = configuration1.getChildren();
720         final int length = children[0].getChildren().length;
721         assertWithMessage("Unexpected children size")
722             .that(length)
723             .isEqualTo(1);
724     }
725 
726     @Test
727     public void testConfigWithIgnoreExceptionalAttributes() {
728         try (MockedConstruction<DefaultConfiguration> mocked = mockConstruction(
729                 DefaultConfiguration.class, (mock, context) -> {
730                     when(mock.getPropertyNames()).thenReturn(new String[] {"severity"});
731                     when(mock.getName()).thenReturn("MemberName");
732                     when(mock.getProperty("severity")).thenThrow(CheckstyleException.class);
733                 })) {
734             final CheckstyleException ex =
735                     TestUtil.getExpectedThrowable(CheckstyleException.class, () -> {
736                         ConfigurationLoader.loadConfiguration(
737                                 getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"),
738                                 new PropertiesExpander(
739                                         new Properties()), IgnoredModulesOptions.OMIT);
740                     });
741             final String expectedMessage =
742                 "Problem during accessing 'severity' attribute for MemberName";
743             assertWithMessage("Invalid exception cause message")
744                 .that(ex)
745                 .hasCauseThat()
746                     .hasMessageThat()
747                     .isEqualTo(expectedMessage);
748         }
749     }
750 
751     @Test
752     public void testLoadConfiguration3() throws Exception {
753         final String[] configFiles = {
754             "InputConfigurationLoaderOldConfig0.xml",
755             "InputConfigurationLoaderOldConfig1.xml",
756             "InputConfigurationLoaderOldConfig2.xml",
757             "InputConfigurationLoaderOldConfig3.xml",
758             "InputConfigurationLoaderOldConfig4.xml",
759             "InputConfigurationLoaderOldConfig5.xml",
760             "InputConfigurationLoaderOldConfig6.xml",
761             "InputConfigurationLoaderOldConfig7.xml",
762         };
763 
764         for (String configFile : configFiles) {
765             final DefaultConfiguration config =
766                     (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
767                             new InputSource(Files.newInputStream(Path.of(
768                                     getPath(configFile)))),
769                             new PropertiesExpander(new Properties()),
770                             IgnoredModulesOptions.OMIT);
771 
772             assertWithMessage("should have properties")
773                     .that(config.getPropertyNames()).asList()
774                     .contains("severity");
775 
776             assertWithMessage("should have properties")
777                     .that(config.getPropertyNames()).asList()
778                     .contains("fileExtensions");
779 
780             assertWithMessage("")
781                     .that(config.getAttribute("severity"))
782                     .isEqualTo("error");
783 
784             assertWithMessage("")
785                     .that(config.getAttribute("fileExtensions"))
786                     .isEqualTo("java, properties, xml");
787 
788             assertWithMessage("")
789                     .that(config.getChildren().length)
790                     .isEqualTo(1);
791 
792             final Configuration[] children = config.getChildren();
793             final Configuration[] grandchildren = children[0].getChildren();
794 
795             assertWithMessage("")
796                     .that(children[0].getPropertyNames()).asList()
797                     .contains("severity");
798 
799             assertWithMessage("")
800                     .that(grandchildren[0].getPropertyNames()).asList()
801                     .contains("query");
802         }
803     }
804 
805     @Test
806     public void testDefaultValuesForNonDefinedProperties() throws Exception {
807         final Properties props = new Properties();
808         props.setProperty("checkstyle.charset.base", "UTF");
809 
810         final File file = new File(
811                 getPath("InputConfigurationLoaderDefaultProperty.xml"));
812         final DefaultConfiguration config =
813             (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
814                     file.toURI().toString(), new PropertiesExpander(props));
815 
816         final Properties expectedPropertyValues = new Properties();
817         expectedPropertyValues.setProperty("tabWidth", "2");
818         expectedPropertyValues.setProperty("basedir", ".");
819         // charset property uses 2 variables, one is not defined, so default becomes a result value
820         expectedPropertyValues.setProperty("charset", "ASCII");
821         verifyConfigNode(config, "Checker", 0, expectedPropertyValues);
822     }
823 
824 }