1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23
24 import java.net.URI;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.regex.Pattern;
28 import java.util.stream.Collectors;
29
30 import org.apache.commons.beanutils.ConversionException;
31 import org.apache.commons.beanutils.ConvertUtilsBean;
32 import org.apache.commons.beanutils.Converter;
33 import org.junit.jupiter.api.Test;
34
35 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
36 import com.puppycrawl.tools.checkstyle.api.Scope;
37 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
38 import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
39 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
40
41 public class AbstractAutomaticBeanTest {
42 @Test
43 public void testConfigureNoSuchAttribute() {
44 final TestBean testBean = new TestBean();
45 final DefaultConfiguration conf = new DefaultConfiguration("testConf");
46 conf.addProperty("NonExistent", "doesn't matter");
47 try {
48 testBean.configure(conf);
49 assertWithMessage("Exception is expected")
50 .fail();
51 }
52 catch (CheckstyleException ex) {
53 assertWithMessage("Exceptions cause should be null")
54 .that(ex)
55 .hasCauseThat()
56 .isNull();
57 assertWithMessage("Invalid exception message")
58 .that(ex)
59 .hasMessageThat()
60 .isEqualTo("Property 'NonExistent' does not exist,"
61 + " please check the documentation");
62 }
63 }
64
65 @Test
66 public void testConfigureNoSuchAttribute2() {
67 final TestBean testBean = new TestBean();
68 final DefaultConfiguration conf = new DefaultConfiguration("testConf");
69 conf.addProperty("privateField", "doesn't matter");
70 try {
71 testBean.configure(conf);
72 assertWithMessage("Exception is expected")
73 .fail();
74 }
75 catch (CheckstyleException ex) {
76 assertWithMessage("Exceptions cause should be null")
77 .that(ex)
78 .hasCauseThat()
79 .isNull();
80 assertWithMessage("Invalid exception message")
81 .that(ex)
82 .hasMessageThat()
83 .isEqualTo("Property 'privateField' does not exist,"
84 + " please check the documentation");
85 }
86 }
87
88 @Test
89 public void testSetupChildFromBaseClass() throws CheckstyleException {
90 final TestBean testBean = new TestBean();
91 testBean.configure(new DefaultConfiguration("bean config"));
92 testBean.setupChild(null);
93 try {
94 testBean.setupChild(new DefaultConfiguration("dummy"));
95 assertWithMessage("Exception is expected")
96 .fail();
97 }
98 catch (CheckstyleException ex) {
99 final String expectedMessage = "dummy is not allowed as a child in bean config. "
100 + "Please review 'Parent Module' section for this Check"
101 + " in web documentation if Check is standard.";
102 assertWithMessage("Invalid exception message")
103 .that(ex)
104 .hasMessageThat()
105 .isEqualTo(expectedMessage);
106 }
107 }
108
109 @Test
110 public void testSetupInvalidChildFromBaseClass() {
111 final TestBean testBean = new TestBean();
112 final DefaultConfiguration parentConf = new DefaultConfiguration("parentConf");
113 final DefaultConfiguration childConf = new DefaultConfiguration("childConf");
114 TestUtil.setInternalState(testBean, "configuration", parentConf);
115
116 try {
117 testBean.setupChild(childConf);
118 assertWithMessage("expecting checkstyle exception")
119 .fail();
120 }
121 catch (CheckstyleException ex) {
122 assertWithMessage("Invalid exception message")
123 .that(ex)
124 .hasMessageThat()
125 .isEqualTo("childConf is not allowed as a "
126 + "child in parentConf. Please review 'Parent Module' section "
127 + "for this Check in web documentation if Check is standard.");
128 }
129 }
130
131 @Test
132 public void testContextualizeInvocationTargetException() {
133 final TestBean testBean = new TestBean();
134 final DefaultContext context = new DefaultContext();
135 context.add("exceptionalMethod", 123.0f);
136 try {
137 testBean.contextualize(context);
138 assertWithMessage("InvocationTargetException is expected")
139 .fail();
140 }
141 catch (CheckstyleException ex) {
142 final String expected = "Cannot set property 'exceptionalMethod' to '123.0'";
143 assertWithMessage("Invalid exception cause, should be: ReflectiveOperationException")
144 .that(ex)
145 .hasCauseThat()
146 .isInstanceOf(ReflectiveOperationException.class);
147 assertWithMessage("Invalid exception message, should start with: " + expected)
148 .that(ex)
149 .hasMessageThat()
150 .isEqualTo(expected);
151 }
152 }
153
154 @Test
155 public void testContextualizeConversionException() {
156 final TestBean testBean = new TestBean();
157 final DefaultContext context = new DefaultContext();
158 context.add("val", "some string");
159 try {
160 testBean.contextualize(context);
161 assertWithMessage("InvocationTargetException is expected")
162 .fail();
163 }
164 catch (CheckstyleException ex) {
165 final String expected = "illegal value ";
166 assertWithMessage("Invalid exception cause, should be: ConversionException")
167 .that(ex)
168 .hasCauseThat()
169 .isInstanceOf(ConversionException.class);
170 assertWithMessage("Invalid exception message, should start with: " + expected)
171 .that(ex)
172 .hasMessageThat()
173 .startsWith(expected);
174 }
175 }
176
177 @Test
178 public void testTestBean() {
179 final TestBean testBean = new TestBean();
180 testBean.setVal(0);
181 testBean.setWrong("wrongVal");
182 testBean.assignPrivateFieldSecretly(null);
183 try {
184 testBean.setExceptionalMethod("someValue");
185 assertWithMessage("exception expected")
186 .fail();
187 }
188 catch (IllegalStateException ex) {
189 assertWithMessage("Invalid exception message")
190 .that(ex)
191 .hasMessageThat()
192 .isEqualTo("null,wrongVal,0,someValue");
193 }
194 }
195
196 @Test
197 public void testRegisterIntegralTypes() throws Exception {
198 final ConvertUtilsBeanStub convertUtilsBean = new ConvertUtilsBeanStub();
199 TestUtil.invokeStaticMethod(AbstractAutomaticBean.class,
200 "registerIntegralTypes", convertUtilsBean);
201 assertWithMessage("Number of converters registered differs from expected")
202 .that(convertUtilsBean.getRegisterCount())
203 .isEqualTo(81);
204 }
205
206 @Test
207 public void testBeanConverters() throws Exception {
208 final ConverterBean bean = new ConverterBean();
209
210
211 bean.setStrings("BAD");
212 bean.setPattern(null);
213 bean.setSeverityLevel(null);
214 bean.setScope(null);
215 bean.setUri(null);
216 bean.setAccessModifiers(AccessModifierOption.PACKAGE);
217
218 final DefaultConfiguration config = new DefaultConfiguration("bean");
219 config.addProperty("strings", "a, b, c");
220 config.addProperty("pattern", ".*");
221 config.addProperty("severityLevel", "error");
222 config.addProperty("scope", "public");
223 config.addProperty("uri", "http://github.com");
224 config.addProperty("accessModifiers", "public, private");
225 bean.configure(config);
226
227 final String message = "invalid result";
228 assertWithMessage(message)
229 .that(bean.strings)
230 .asList()
231 .containsExactly("a", "b", "c")
232 .inOrder();
233 assertWithMessage(message)
234 .that(bean.pattern.pattern())
235 .isEqualTo(".*");
236 assertWithMessage(message)
237 .that(bean.severityLevel)
238 .isEqualTo(SeverityLevel.ERROR);
239 assertWithMessage(message)
240 .that(bean.scope)
241 .isEqualTo(Scope.PUBLIC);
242 assertWithMessage(message)
243 .that(bean.uri)
244 .isEqualTo(new URI("http://github.com"));
245 assertWithMessage(message)
246 .that(bean.accessModifiers)
247 .asList()
248 .containsExactly(AccessModifierOption.PUBLIC, AccessModifierOption.PRIVATE)
249 .inOrder();
250 }
251
252 @Test
253 public void testBeanConvertersUri2() throws Exception {
254 final ConverterBean bean = new ConverterBean();
255 final DefaultConfiguration config = new DefaultConfiguration("bean");
256 config.addProperty("uri", "");
257 bean.configure(config);
258
259 assertWithMessage("invalid result")
260 .that(bean.uri)
261 .isNull();
262 }
263
264 @Test
265 public void testBeanConvertersUri3() {
266 final ConverterBean bean = new ConverterBean();
267 final DefaultConfiguration config = new DefaultConfiguration("bean");
268 config.addProperty("uri", "BAD");
269
270 try {
271 bean.configure(config);
272 assertWithMessage("Exception is expected")
273 .fail();
274 }
275 catch (CheckstyleException ex) {
276 assertWithMessage("Error message is not expected")
277 .that(ex)
278 .hasMessageThat()
279 .isEqualTo("illegal value 'BAD' for property 'uri'");
280 }
281 }
282
283 @Test
284 public void testBeanConverterPatternArray() throws Exception {
285 final ConverterBean bean = new ConverterBean();
286 final DefaultConfiguration config = new DefaultConfiguration("bean");
287 final String patternString = "^a*$ , ^b*$ , ^c*$ ";
288 final List<String> expectedPatternStrings = Arrays.asList("^a*$", "^b*$", "^c*$");
289 config.addProperty("patterns", patternString);
290 bean.configure(config);
291
292 final List<String> actualPatternStrings = Arrays.stream(bean.patterns)
293 .map(Pattern::pattern)
294 .collect(Collectors.toUnmodifiableList());
295
296 assertWithMessage("invalid size of result")
297 .that(bean.patterns)
298 .hasLength(3);
299 assertWithMessage("invalid result")
300 .that(actualPatternStrings)
301 .containsExactlyElementsIn(expectedPatternStrings);
302 }
303
304 @Test
305 public void testBeanConverterPatternArraySingleElement() throws Exception {
306 final ConverterBean bean = new ConverterBean();
307 final DefaultConfiguration config = new DefaultConfiguration("bean");
308 final String patternString = "^a*$";
309 final List<String> expectedPatternStrings = List.of("^a*$");
310 config.addProperty("patterns", patternString);
311 bean.configure(config);
312
313 final List<String> actualPatternStrings = Arrays.stream(bean.patterns)
314 .map(Pattern::pattern)
315 .collect(Collectors.toUnmodifiableList());
316
317 assertWithMessage("invalid size of result")
318 .that(bean.patterns)
319 .hasLength(1);
320 assertWithMessage("invalid result")
321 .that(actualPatternStrings)
322 .containsExactlyElementsIn(expectedPatternStrings);
323 }
324
325 @Test
326 public void testBeanConverterPatternArrayEmptyString() throws Exception {
327 final ConverterBean bean = new ConverterBean();
328 final DefaultConfiguration config = new DefaultConfiguration("bean");
329 config.addProperty("patterns", "");
330 bean.configure(config);
331
332 assertWithMessage("invalid size of result")
333 .that(bean.patterns)
334 .hasLength(0);
335 }
336
337 private static final class ConvertUtilsBeanStub extends ConvertUtilsBean {
338
339 private int registerCount;
340
341 @Override
342 public void register(Converter converter, Class<?> clazz) {
343 super.register(converter, clazz);
344 if (converter != null) {
345 registerCount++;
346 }
347 }
348
349 public int getRegisterCount() {
350 return registerCount;
351 }
352
353 }
354
355 public static final class TestBean extends AbstractAutomaticBean {
356
357 private String privateField;
358
359 private String wrong;
360
361 private int val;
362
363 public void setWrong(String wrong) {
364 this.wrong = wrong;
365 }
366
367 public void setVal(int val) {
368 this.val = val;
369 }
370
371 public void assignPrivateFieldSecretly(String input) {
372 privateField = input;
373 }
374
375 public void setExceptionalMethod(String value) {
376 throw new IllegalStateException(privateField + "," + wrong + "," + val + "," + value);
377 }
378
379 @Override
380 protected void finishLocalSetup() {
381
382 }
383
384 }
385
386
387
388
389 public static class ConverterBean extends AbstractAutomaticBean {
390
391 private String[] strings;
392 private Pattern pattern;
393 private SeverityLevel severityLevel;
394 private Scope scope;
395 private URI uri;
396 private AccessModifierOption[] accessModifiers;
397 private Pattern[] patterns;
398
399
400
401
402
403
404 public void setStrings(String... strings) {
405 this.strings = Arrays.copyOf(strings, strings.length);
406 }
407
408
409
410
411
412
413 public void setPattern(Pattern pattern) {
414 this.pattern = pattern;
415 }
416
417
418
419
420
421
422 public void setSeverityLevel(SeverityLevel severityLevel) {
423 this.severityLevel = severityLevel;
424 }
425
426
427
428
429
430
431 public void setScope(Scope scope) {
432 this.scope = scope;
433 }
434
435
436
437
438
439
440 public void setUri(URI uri) {
441 this.uri = uri;
442 }
443
444
445
446
447
448
449 public void setAccessModifiers(AccessModifierOption... accessModifiers) {
450 this.accessModifiers = Arrays.copyOf(accessModifiers,
451 accessModifiers.length);
452 }
453
454
455
456
457
458
459 public void setPatterns(Pattern... patterns) {
460 this.patterns = Arrays.copyOf(patterns, patterns.length);
461 }
462
463 @Override
464 protected void finishLocalSetup() {
465
466 }
467
468 }
469
470 }