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