1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertSame;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26
27 import java.io.File;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import java.nio.file.Files;
32 import java.nio.file.Paths;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Properties;
36
37 import org.junit.Test;
38 import org.powermock.reflect.Whitebox;
39 import org.xml.sax.InputSource;
40 import org.xml.sax.SAXException;
41
42 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
43 import com.puppycrawl.tools.checkstyle.api.Configuration;
44
45
46
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
67
68
69
70
71
72
73 private static Method getReplacePropertiesMethod() throws Exception {
74 final Class<?>[] params = new Class<?>[3];
75 params[0] = String.class;
76 params[1] = PropertyResolver.class;
77 params[2] = String.class;
78 final Class<ConfigurationLoader> configurationLoaderClass = ConfigurationLoader.class;
79 final Method replacePropertiesMethod =
80 configurationLoaderClass.getDeclaredMethod("replaceProperties", params);
81 replacePropertiesMethod.setAccessible(true);
82 return replacePropertiesMethod;
83 }
84
85 @Test
86 public void testResourceLoadConfiguration() throws Exception {
87 final Properties props = new Properties();
88 props.setProperty("checkstyle.basedir", "basedir");
89
90
91 final DefaultConfiguration config =
92 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
93 getPath("InputConfigurationLoaderChecks.xml"), new PropertiesExpander(props));
94
95
96 final Properties attributes = new Properties();
97 attributes.setProperty("tabWidth", "4");
98 attributes.setProperty("basedir", "basedir");
99 verifyConfigNode(config, "Checker", 3, attributes);
100 }
101
102 @Test
103 public void testResourceLoadConfigurationWithMultiThreadConfiguration() throws Exception {
104 final Properties props = new Properties();
105 props.setProperty("checkstyle.basedir", "basedir");
106
107 final PropertiesExpander propertiesExpander = new PropertiesExpander(props);
108 final String configPath = getPath("InputConfigurationLoaderChecks.xml");
109 final ThreadModeSettings multiThreadModeSettings =
110 new ThreadModeSettings(4, 2);
111
112 try {
113 ConfigurationLoader.loadConfiguration(
114 configPath, propertiesExpander, multiThreadModeSettings);
115 fail("An exception is expected");
116 }
117 catch (IllegalArgumentException ex) {
118 assertEquals("Invalid exception message",
119 "Multi thread mode for Checker module is not implemented",
120 ex.getMessage());
121 }
122 }
123
124 @Test
125 public void testResourceLoadConfigurationWithSingleThreadConfiguration() throws Exception {
126 final Properties props = new Properties();
127 props.setProperty("checkstyle.basedir", "basedir");
128
129 final PropertiesExpander propertiesExpander = new PropertiesExpander(props);
130 final String configPath = getPath("InputConfigurationLoaderChecks.xml");
131 final ThreadModeSettings singleThreadModeSettings =
132 ThreadModeSettings.SINGLE_THREAD_MODE_INSTANCE;
133
134 final DefaultConfiguration config =
135 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
136 configPath, propertiesExpander, singleThreadModeSettings);
137
138 final Properties attributes = new Properties();
139 attributes.setProperty("tabWidth", "4");
140 attributes.setProperty("basedir", "basedir");
141 verifyConfigNode(config, "Checker", 3, attributes);
142 }
143
144 @Test
145 public void testEmptyConfiguration() throws Exception {
146 final DefaultConfiguration config =
147 (DefaultConfiguration) loadConfiguration("InputConfigurationLoaderEmpty.xml");
148 verifyConfigNode(config, "Checker", 0, new Properties());
149 }
150
151 @Test
152 public void testEmptyModuleResolver() throws Exception {
153 final DefaultConfiguration config =
154 (DefaultConfiguration) loadConfiguration(
155 "InputConfigurationLoaderEmpty.xml", new Properties());
156 verifyConfigNode(config, "Checker", 0, new Properties());
157 }
158
159 @Test
160 public void testMissingPropertyName() throws Exception {
161 try {
162 loadConfiguration("InputConfigurationLoaderMissingPropertyName.xml");
163 fail("missing property name");
164 }
165 catch (CheckstyleException ex) {
166 assertTrue("Invalid exception message: " + ex.getMessage(),
167 ex.getMessage().contains("\"name\""));
168 assertTrue("Invalid exception message: " + ex.getMessage(),
169 ex.getMessage().contains("\"property\""));
170 assertTrue("Invalid exception message: " + ex.getMessage(),
171 ex.getMessage().endsWith(":8:41"));
172 }
173 }
174
175 @Test
176 public void testMissingPropertyNameInMethodWithBooleanParameter() throws Exception {
177 try {
178 final String fName = getPath("InputConfigurationLoaderMissingPropertyName.xml");
179 ConfigurationLoader.loadConfiguration(fName, new PropertiesExpander(new Properties()),
180 false);
181
182 fail("missing property name");
183 }
184 catch (CheckstyleException ex) {
185 assertTrue("Invalid exception message: " + ex.getMessage(),
186 ex.getMessage().contains("\"name\""));
187 assertTrue("Invalid exception message: " + ex.getMessage(),
188 ex.getMessage().contains("\"property\""));
189 assertTrue("Invalid exception message: " + ex.getMessage(),
190 ex.getMessage().endsWith(":8:41"));
191 }
192 }
193
194 @Test
195 public void testMissingPropertyValue() throws Exception {
196 try {
197 loadConfiguration("InputConfigurationLoaderMissingPropertyValue.xml");
198 fail("missing property value");
199 }
200 catch (CheckstyleException ex) {
201 assertTrue("Invalid exception message: " + ex.getMessage(),
202 ex.getMessage().contains("\"value\""));
203 assertTrue("Invalid exception message: " + ex.getMessage(),
204 ex.getMessage().contains("\"property\""));
205 assertTrue("Invalid exception message: " + ex.getMessage(),
206 ex.getMessage().endsWith(":8:43"));
207 }
208 }
209
210 @Test
211 public void testMissingConfigName() throws Exception {
212 try {
213 loadConfiguration("InputConfigurationLoaderMissingConfigName.xml");
214 fail("missing module name");
215 }
216 catch (CheckstyleException ex) {
217 assertTrue("Invalid exception message: " + ex.getMessage(),
218 ex.getMessage().contains("\"name\""));
219 assertTrue("Invalid exception message: " + ex.getMessage(),
220 ex.getMessage().contains("\"module\""));
221 assertTrue("Invalid exception message: " + ex.getMessage(),
222 ex.getMessage().endsWith(":7:23"));
223 }
224 }
225
226 @Test
227 public void testMissingConfigParent() throws Exception {
228 try {
229 loadConfiguration("InputConfigurationLoaderMissingConfigParent.xml");
230 fail("missing module parent");
231 }
232 catch (CheckstyleException ex) {
233 assertTrue("Invalid exception message: " + ex.getMessage(),
234 ex.getMessage().contains("\"property\""));
235 assertTrue("Invalid exception message: " + ex.getMessage(),
236 ex.getMessage().contains("\"module\""));
237 assertTrue("Invalid exception message: " + ex.getMessage(),
238 ex.getMessage().endsWith(":8:38"));
239 }
240 }
241
242 @Test
243 public void testCheckstyleChecks() throws Exception {
244 final Properties props = new Properties();
245 props.setProperty("checkstyle.basedir", "basedir");
246
247 final DefaultConfiguration config =
248 (DefaultConfiguration) loadConfiguration(
249 "InputConfigurationLoaderChecks.xml", props);
250
251
252 final Properties atts = new Properties();
253 atts.setProperty("tabWidth", "4");
254 atts.setProperty("basedir", "basedir");
255 verifyConfigNode(config, "Checker", 3, atts);
256
257
258 final Configuration[] children = config.getChildren();
259 atts.clear();
260 verifyConfigNode(
261 (DefaultConfiguration) children[1], "JavadocPackage", 0, atts);
262 verifyConfigNode(
263 (DefaultConfiguration) children[2], "Translation", 0, atts);
264 atts.setProperty("testName", "testValue");
265 verifyConfigNode(
266 (DefaultConfiguration) children[0],
267 "TreeWalker",
268 8,
269 atts);
270
271
272 final Configuration[] grandchildren = children[0].getChildren();
273 atts.clear();
274 verifyConfigNode(
275 (DefaultConfiguration) grandchildren[0],
276 "AvoidStarImport",
277 0,
278 atts);
279 atts.setProperty("format", "System.out.println");
280 verifyConfigNode(
281 (DefaultConfiguration) grandchildren[grandchildren.length - 1],
282 "GenericIllegalRegexp",
283 0,
284 atts);
285 atts.clear();
286 atts.setProperty("tokens", "DOT");
287 atts.setProperty("allowLineBreaks", "true");
288 verifyConfigNode(
289 (DefaultConfiguration) grandchildren[6],
290 "NoWhitespaceAfter",
291 0,
292 atts);
293 }
294
295 @Test
296 public void testCustomMessages() throws Exception {
297 final Properties props = new Properties();
298 props.setProperty("checkstyle.basedir", "basedir");
299
300 final DefaultConfiguration config =
301 (DefaultConfiguration) loadConfiguration(
302 "InputConfigurationLoaderCustomMessages.xml", props);
303
304 final Configuration[] children = config.getChildren();
305 final Configuration[] grandchildren = children[0].getChildren();
306
307 final String expectedKey = "name.invalidPattern";
308 assertTrue("Messages should contain key: " + expectedKey,
309 grandchildren[0].getMessages()
310 .containsKey(expectedKey));
311 }
312
313 private static void verifyConfigNode(
314 DefaultConfiguration config, String name, int childrenLength,
315 Properties atts) throws Exception {
316 assertEquals("name.", name, config.getName());
317 assertEquals(
318 "children.length.",
319 childrenLength,
320 config.getChildren().length);
321
322 final String[] attNames = config.getAttributeNames();
323 assertEquals("attributes.length", atts.size(), attNames.length);
324
325 for (String attName : attNames) {
326 assertEquals(
327 "attribute[" + attName + "]",
328 atts.getProperty(attName),
329 config.getAttribute(attName));
330 }
331 }
332
333 @Test
334 public void testReplacePropertiesNoReplace() throws Exception {
335 final String[] testValues = {null, "", "a", "$a", "{a",
336 "{a}", "a}", "$a}", "$", "a$b", };
337 final Properties props = initProperties();
338 for (String testValue : testValues) {
339 final String value = (String) getReplacePropertiesMethod().invoke(
340 null, testValue, new PropertiesExpander(props), null);
341 assertEquals("\"" + testValue + "\"", value, testValue);
342 }
343 }
344
345 @Test
346 public void testReplacePropertiesSyntaxError() throws Exception {
347 final Properties props = initProperties();
348 try {
349 final String value = (String) getReplacePropertiesMethod().invoke(
350 null, "${a", new PropertiesExpander(props), null);
351 fail("expected to fail, instead got: " + value);
352 }
353 catch (InvocationTargetException ex) {
354 assertEquals("Invalid exception cause message",
355 "Syntax error in property: ${a", ex.getCause().getMessage());
356 }
357 }
358
359 @Test
360 public void testReplacePropertiesMissingProperty() throws Exception {
361 final Properties props = initProperties();
362 try {
363 final String value = (String) getReplacePropertiesMethod().invoke(
364 null, "${c}", new PropertiesExpander(props), null);
365 fail("expected to fail, instead got: " + value);
366 }
367 catch (InvocationTargetException ex) {
368 assertEquals("Invalid exception cause message",
369 "Property ${c} has not been set", ex.getCause().getMessage());
370 }
371 }
372
373 @Test
374 public void testReplacePropertiesReplace() throws Exception {
375 final String[][] testValues = {
376 {"${a}", "A"},
377 {"x${a}", "xA"},
378 {"${a}x", "Ax"},
379 {"${a}${b}", "AB"},
380 {"x${a}${b}", "xAB"},
381 {"${a}x${b}", "AxB"},
382 {"${a}${b}x", "ABx"},
383 {"x${a}y${b}", "xAyB"},
384 {"${a}x${b}y", "AxBy"},
385 {"x${a}${b}y", "xABy"},
386 {"x${a}y${b}z", "xAyBz"},
387 {"$$", "$"},
388 };
389 final Properties props = initProperties();
390 for (String[] testValue : testValues) {
391 final String value = (String) getReplacePropertiesMethod().invoke(
392 null, testValue[0], new PropertiesExpander(props), null);
393 assertEquals("\"" + testValue[0] + "\"",
394 testValue[1], value);
395 }
396 }
397
398 private static Properties initProperties() {
399 final Properties props = new Properties();
400 props.setProperty("a", "A");
401 props.setProperty("b", "B");
402 return props;
403 }
404
405 @Test
406 public void testExternalEntity() throws Exception {
407 final Properties props = new Properties();
408 props.setProperty("checkstyle.basedir", "basedir");
409
410 System.setProperty(
411 XmlLoader.LoadExternalDtdFeatureProvider.ENABLE_EXTERNAL_DTD_LOAD, "true");
412
413 final DefaultConfiguration config =
414 (DefaultConfiguration) loadConfiguration(
415 "InputConfigurationLoaderExternalEntity.xml", props);
416
417 final Properties atts = new Properties();
418 atts.setProperty("tabWidth", "4");
419 atts.setProperty("basedir", "basedir");
420 verifyConfigNode(config, "Checker", 2, atts);
421 }
422
423 @Test
424 public void testExternalEntitySubdirectory() throws Exception {
425 final Properties props = new Properties();
426 props.setProperty("checkstyle.basedir", "basedir");
427
428 System.setProperty(
429 XmlLoader.LoadExternalDtdFeatureProvider.ENABLE_EXTERNAL_DTD_LOAD, "true");
430
431 final DefaultConfiguration config =
432 (DefaultConfiguration) loadConfiguration(
433 "subdir/InputConfigurationLoaderExternalEntitySubDir.xml", props);
434
435 final Properties attributes = new Properties();
436 attributes.setProperty("tabWidth", "4");
437 attributes.setProperty("basedir", "basedir");
438 verifyConfigNode(config, "Checker", 2, attributes);
439 }
440
441 @Test
442 public void testExternalEntityFromUri() throws Exception {
443 final Properties props = new Properties();
444 props.setProperty("checkstyle.basedir", "basedir");
445
446 System.setProperty(
447 XmlLoader.LoadExternalDtdFeatureProvider.ENABLE_EXTERNAL_DTD_LOAD, "true");
448
449 final File file = new File(
450 getPath("subdir/InputConfigurationLoaderExternalEntitySubDir.xml"));
451 final DefaultConfiguration config =
452 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
453 file.toURI().toString(), new PropertiesExpander(props));
454
455 final Properties atts = new Properties();
456 atts.setProperty("tabWidth", "4");
457 atts.setProperty("basedir", "basedir");
458 verifyConfigNode(config, "Checker", 2, atts);
459 }
460
461 @Test
462 public void testIncorrectTag() throws Exception {
463 final Class<?> aClassParent = ConfigurationLoader.class;
464 final Constructor<?> ctorParent = aClassParent.getDeclaredConstructor(
465 PropertyResolver.class, boolean.class, ThreadModeSettings.class);
466 ctorParent.setAccessible(true);
467 final Object objParent = ctorParent.newInstance(null, true, null);
468
469 final Class<?> aClass = Class.forName("com.puppycrawl.tools.checkstyle."
470 + "ConfigurationLoader$InternalLoader");
471 final Constructor<?> constructor = aClass.getDeclaredConstructor(objParent.getClass());
472 constructor.setAccessible(true);
473
474 final Object obj = constructor.newInstance(objParent);
475
476 try {
477 Whitebox.invokeMethod(obj, "startElement", "", "", "hello", null);
478
479 fail("Exception is expected");
480 }
481 catch (IllegalStateException ex) {
482 assertEquals("Invalid exception cause message",
483 "Unknown name:" + "hello" + ".", ex.getMessage());
484 }
485 }
486
487 @Test
488 public void testNonExistentPropertyName() throws Exception {
489 try {
490 loadConfiguration("InputConfigurationLoaderNonexistentProperty.xml");
491 fail("exception in expected");
492 }
493 catch (CheckstyleException ex) {
494 assertEquals("Invalid exception message",
495 "unable to parse configuration stream", ex.getMessage());
496 assertSame("Expected cause of type SAXException",
497 SAXException.class, ex.getCause().getClass());
498 assertSame("Expected cause of type CheckstyleException",
499 CheckstyleException.class, ex.getCause().getCause().getClass());
500 assertEquals("Invalid exception cause message",
501 "Property ${nonexistent} has not been set",
502 ex.getCause().getCause().getMessage());
503 }
504 }
505
506 @Test
507 public void testConfigWithIgnore() throws Exception {
508 final DefaultConfiguration config =
509 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
510 getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"),
511 new PropertiesExpander(new Properties()), true);
512
513 final Configuration[] children = config.getChildren();
514 assertEquals("Invalid children count", 0, children[0].getChildren().length);
515 }
516
517 @Test
518 public void testConfigWithIgnoreUsingInputSource() throws Exception {
519 final DefaultConfiguration config =
520 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(new InputSource(
521 new File(getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"))
522 .toURI().toString()),
523 new PropertiesExpander(new Properties()), true);
524
525 final Configuration[] children = config.getChildren();
526 assertEquals("Invalid children count", 0, children[0].getChildren().length);
527 }
528
529 @Test
530 public void testConfigCheckerWithIgnore() throws Exception {
531 final DefaultConfiguration config =
532 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
533 getPath("InputConfigurationLoaderCheckerIgnoreSeverity.xml"),
534 new PropertiesExpander(new Properties()), true);
535
536 final Configuration[] children = config.getChildren();
537 assertEquals("Invalid children count", 0, children.length);
538 }
539
540 @Test
541 public void testLoadConfigurationWrongUrl() {
542 try {
543 final DefaultConfiguration config =
544 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
545 ";InputConfigurationLoaderModuleIgnoreSeverity.xml",
546 new PropertiesExpander(new Properties()), true);
547
548 final Configuration[] children = config.getChildren();
549 assertEquals("Invalid children count", 0, children[0].getChildren().length);
550 fail("Exception is expected");
551 }
552 catch (CheckstyleException ex) {
553 assertEquals("Invalid exception message",
554 "Unable to find: ;InputConfigurationLoaderModuleIgnoreSeverity.xml",
555 ex.getMessage());
556 }
557 }
558
559 @Test
560 public void testLoadConfigurationDeprecated() throws Exception {
561 final DefaultConfiguration config =
562 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
563 Files.newInputStream(Paths.get(
564 getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"))),
565 new PropertiesExpander(new Properties()), true);
566
567 final Configuration[] children = config.getChildren();
568 assertEquals("Invalid children count",
569 0, children[0].getChildren().length);
570 }
571
572 @Test
573 public void testReplacePropertiesDefault() throws Exception {
574 final Properties props = new Properties();
575 final String defaultValue = "defaultValue";
576
577 final String value = (String) getReplacePropertiesMethod().invoke(
578 null, "${checkstyle.basedir}", new PropertiesExpander(props), defaultValue);
579
580 assertEquals("Invalid property value", defaultValue, value);
581 }
582
583 @Test
584 public void testLoadConfigurationFromClassPath() throws Exception {
585 final DefaultConfiguration config =
586 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
587 getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml"),
588 new PropertiesExpander(new Properties()), true);
589
590 final Configuration[] children = config.getChildren();
591 assertEquals("Invalid children count",
592 0, children[0].getChildren().length);
593 }
594
595 @Test
596 public void testParsePropertyString() throws Exception {
597 final List<String> propertyRefs = new ArrayList<>();
598 final List<String> fragments = new ArrayList<>();
599
600 Whitebox.invokeMethod(ConfigurationLoader.class,
601 "parsePropertyString", "$",
602 fragments, propertyRefs);
603 assertEquals("Fragments list has unexpected amount of items",
604 1, fragments.size());
605 }
606
607 @Test
608 public void testConstructors() throws Exception {
609 final Properties props = new Properties();
610 props.setProperty("checkstyle.basedir", "basedir");
611 final String fName = getPath("InputConfigurationLoaderChecks.xml");
612
613 final Configuration configuration = ConfigurationLoader.loadConfiguration(fName,
614 new PropertiesExpander(props), ConfigurationLoader.IgnoredModulesOptions.OMIT);
615 assertEquals("Name is not expected", "Checker", configuration.getName());
616
617 final DefaultConfiguration configuration1 =
618 (DefaultConfiguration) ConfigurationLoader.loadConfiguration(
619 new InputSource(Files.newInputStream(Paths.get(
620 getPath("InputConfigurationLoaderModuleIgnoreSeverity.xml")))),
621 new PropertiesExpander(new Properties()),
622 ConfigurationLoader.IgnoredModulesOptions.EXECUTE);
623
624 final Configuration[] children = configuration1.getChildren();
625 assertEquals("Unexpected children size", 1, children[0].getChildren().length);
626 }
627
628 }