1
2
3
4
5 package com.qulice.pmd;
6
7 import com.google.common.base.Joiner;
8 import com.jcabi.matchers.RegexMatchers;
9 import com.qulice.pmd.rules.ProhibitPlainJunitAssertionsRule;
10 import com.qulice.spi.Environment;
11 import com.qulice.spi.Violation;
12 import java.io.File;
13 import java.util.Collections;
14 import org.hamcrest.MatcherAssert;
15 import org.hamcrest.Matchers;
16 import org.hamcrest.core.IsEqual;
17 import org.hamcrest.core.IsNot;
18 import org.hamcrest.core.StringContains;
19 import org.junit.jupiter.api.Assertions;
20 import org.junit.jupiter.api.Test;
21 import org.junit.jupiter.api.condition.EnabledForJreRange;
22 import org.junit.jupiter.api.condition.JRE;
23
24
25
26
27
28
29 @SuppressWarnings("PMD.TooManyMethods")
30 final class PmdValidatorTest {
31
32
33
34
35
36
37 private static final String STATIC_ACCESS =
38 "%s\\[\\d+-\\d+\\]: Static fields should be accessed in a static way \\[CLASS_NAME.FIELD_NAME\\]\\.";
39
40
41
42
43
44 private static final String STATIC_VIA_THIS =
45 "%s\\[\\d+-\\d+\\]: Static members should be accessed in a static way \\[CLASS_NAME.FIELD_NAME\\], not via instance reference.";
46
47
48
49
50
51 private static final String CODE_IN_CON =
52 "%s\\[\\d+-\\d+\\]: Only field initialization or call to other constructors in a constructor";
53
54
55
56
57 private static final String NO_CON_INIT =
58 "%s\\[\\d+-\\d+\\]: Avoid doing field initialization outside constructor.";
59
60
61
62
63 private static final String MULT_CON_INIT =
64 "%s\\[\\d+-\\d+\\]: Avoid field initialization in several constructors.";
65
66
67
68
69 private static final String BRACKETS = "(%s)";
70
71
72
73
74 private static final String PLAIN_ASSERTIONS =
75 "Avoid using Plain JUnit assertions";
76
77
78
79
80 private static final String STATIC_METHODS =
81 "Public static methods are prohibited";
82
83
84
85
86 private static final String FILES_CREATE_ERR =
87 "Files.createFile should not be used in tests, replace them with @Rule TemporaryFolder";
88
89
90
91
92
93
94 @Test
95 void findsProblemsInJavaFiles() throws Exception {
96 final String file = "src/main/java/Main.java";
97 final Environment env = new Environment.Mock()
98 .withFile(file, "class Main { int x = 0; }");
99 MatcherAssert.assertThat(
100 "Violations should be found",
101 new PmdValidator(env).validate(
102 Collections.singletonList(new File(env.basedir(), file))
103 ),
104 Matchers.not(Matchers.<Violation>empty())
105 );
106 }
107
108
109
110
111
112
113 @Test
114 void understandsMethodReferences() throws Exception {
115 final String file = "UnderstandsMethodReferences.java";
116 new PmdAssert(
117 file,
118 Matchers.is(true),
119 Matchers.not(
120 Matchers.containsString("(UnusedPrivateMethod)")
121 )
122 ).validate();
123 }
124
125
126
127
128
129
130
131 @Test
132 @SuppressWarnings("PMD.AvoidDuplicateLiterals")
133 void doesNotComplainAboutConstantsInInnerClasses() throws Exception {
134 final String file = "src/main/java/foo/Foo.java";
135 final Environment env = new Environment.Mock().withFile(
136 file,
137 Joiner.on('\n').join(
138 "package foo;",
139 "interface Foo {",
140 " final class Bar implements Foo {",
141 " private static final Pattern TEST =",
142 " Pattern.compile(\"hey\");",
143 " public String doSomething() {",
144 " return Foo.Bar.TEST.toString();",
145 " }",
146 " }",
147 "}"
148 )
149 );
150 MatcherAssert.assertThat(
151 "Private constant in inner class is not a violation",
152 new PmdValidator(env).validate(
153 Collections.singletonList(new File(env.basedir(), file))
154 ),
155 Matchers.<Violation>empty()
156 );
157 }
158
159
160
161
162
163
164 @Test
165 void allowsFieldInitializationWhenConstructorIsMissing()
166 throws Exception {
167 final String file = "FieldInitNoConstructor.java";
168 new PmdAssert(
169 file,
170 Matchers.is(true),
171 Matchers.not(
172 RegexMatchers.containsPattern(
173 String.format(
174 PmdValidatorTest.NO_CON_INIT,
175 file
176 )
177 )
178 )
179 ).validate();
180 }
181
182
183
184
185
186
187 @Test
188 void forbidsFieldInitializationWhenConstructorExists()
189 throws Exception {
190 final String file = "FieldInitConstructor.java";
191 new PmdAssert(
192 file,
193 Matchers.is(false),
194 RegexMatchers.containsPattern(
195 String.format(
196 PmdValidatorTest.NO_CON_INIT,
197 file
198 )
199 )
200 ).validate();
201 }
202
203
204
205
206
207
208
209 @Test
210 void allowsStaticFieldInitializationWhenConstructorExists()
211 throws Exception {
212 final String file = "StaticFieldInitConstructor.java";
213 new PmdAssert(
214 file,
215 Matchers.is(true),
216 Matchers.not(
217 RegexMatchers.containsPattern(
218 String.format(
219 PmdValidatorTest.NO_CON_INIT,
220 file
221 )
222 )
223 )
224 ).validate();
225 }
226
227
228
229
230
231
232
233 @Test
234 void forbidsFieldInitializationInSeveralConstructors()
235 throws Exception {
236 final String file = "FieldInitSeveralConstructors.java";
237 new PmdAssert(
238 file,
239 Matchers.is(false),
240 RegexMatchers.containsPattern(
241 String.format(
242 PmdValidatorTest.MULT_CON_INIT,
243 file
244 )
245 )
246 ).validate();
247 }
248
249
250
251
252
253
254
255 @Test
256 void allowsFieldInitializationInOneConstructor()
257 throws Exception {
258 final String file = "FieldInitOneConstructor.java";
259 new PmdAssert(
260 file,
261 Matchers.is(true),
262 Matchers.not(
263 RegexMatchers.containsPattern(
264 String.format(
265 PmdValidatorTest.MULT_CON_INIT,
266 file
267 )
268 )
269 )
270 ).validate();
271 }
272
273
274
275
276
277
278 @Test
279 void forbidsUnnecessaryFinalModifier()
280 throws Exception {
281 final String file = "UnnecessaryFinalModifier.java";
282 new PmdAssert(
283 file,
284 Matchers.is(false),
285 Matchers.containsString("Unnecessary modifier 'final'")
286 ).validate();
287 }
288
289
290
291
292
293
294 @Test
295 void forbidsUselessParentheses()
296 throws Exception {
297 final String file = "UselessParentheses.java";
298 new PmdAssert(
299 file,
300 Matchers.is(false),
301 Matchers.containsString("Useless parentheses")
302 ).validate();
303 }
304
305
306
307
308
309
310
311 @Test
312 void forbidsCodeInConstructor()
313 throws Exception {
314 final String file = "CodeInConstructor.java";
315 new PmdAssert(
316 file,
317 Matchers.is(false),
318 RegexMatchers.containsPattern(
319 String.format(
320 PmdValidatorTest.CODE_IN_CON,
321 file
322 )
323 )
324 ).validate();
325 }
326
327
328
329
330
331
332 @Test
333 void allowsLambdaInConstructor()
334 throws Exception {
335 final String file = "LambdaInConstructor.java";
336 new PmdAssert(
337 file,
338 new IsEqual<>(true),
339 new IsNot<>(
340 RegexMatchers.containsPattern(
341 String.format(
342 PmdValidatorTest.CODE_IN_CON,
343 file
344 )
345 )
346 )
347 ).validate();
348 }
349
350
351
352
353
354
355 @Test
356 void forbidsFilesCreateFileInTests() throws Exception {
357 new PmdAssert(
358 "FilesCreateFileTest.java",
359 Matchers.is(false),
360 Matchers.containsString(
361 PmdValidatorTest.FILES_CREATE_ERR
362 )
363 ).validate();
364 }
365
366
367
368
369
370
371 @Test
372 void forbidsFilesCreateFileOutsideOfTests() throws Exception {
373 new PmdAssert(
374 "FilesCreateFileOther.java",
375 Matchers.is(true),
376 Matchers.not(
377 Matchers.containsString(
378 PmdValidatorTest.FILES_CREATE_ERR
379 )
380 )
381 ).validate();
382 }
383
384
385
386
387
388
389
390 @Test
391 void acceptsCallToConstructorInConstructor()
392 throws Exception {
393 final String file = "CallToConstructorInConstructor.java";
394 new PmdAssert(
395 file,
396 Matchers.is(true),
397 Matchers.not(
398 RegexMatchers.containsPattern(
399 String.format(
400 PmdValidatorTest.CODE_IN_CON,
401 file
402 )
403 )
404 )
405 ).validate();
406 }
407
408
409
410
411
412
413 @Test
414 void acceptsCallToStaticFieldsInStaticWay()
415 throws Exception {
416 final String file = "StaticAccessToStaticFields.java";
417 new PmdAssert(
418 file,
419 Matchers.is(true),
420 Matchers.allOf(
421 Matchers.not(
422 RegexMatchers.containsPattern(
423 String.format(
424 PmdValidatorTest.STATIC_ACCESS,
425 file
426 )
427 )
428 ),
429 Matchers.not(
430 RegexMatchers.containsPattern(
431 String.format(
432 PmdValidatorTest.STATIC_VIA_THIS,
433 file
434 )
435 )
436 )
437 )
438 ).validate();
439 }
440
441
442
443
444
445
446
447 @Test
448 void forbidsCallToStaticFieldsDirectly()
449 throws Exception {
450 final String file = "DirectAccessToStaticFields.java";
451 new PmdAssert(
452 file,
453 Matchers.is(false),
454 RegexMatchers.containsPattern(
455 String.format(
456 PmdValidatorTest.STATIC_ACCESS,
457 file
458 )
459 )
460 ).validate();
461 }
462
463
464
465
466
467
468
469 @Test
470 void forbidsCallToStaticFieldsViaThis()
471 throws Exception {
472 final String file = "AccessToStaticFieldsViaThis.java";
473 new PmdAssert(
474 file,
475 Matchers.is(false),
476 RegexMatchers.containsPattern(
477 String.format(
478 PmdValidatorTest.STATIC_VIA_THIS,
479 file
480 )
481 )
482 ).validate();
483 }
484
485
486
487
488
489
490
491 @Test
492 void forbidsCallToStaticMethodsViaThis()
493 throws Exception {
494 final String file = "AccessToStaticMethodsViaThis.java";
495 new PmdAssert(
496 file,
497 Matchers.is(false),
498 RegexMatchers.containsPattern(
499 String.format(
500 PmdValidatorTest.STATIC_VIA_THIS,
501 file
502 )
503 )
504 ).validate();
505 }
506
507
508
509
510
511
512
513 @Test
514 void forbidsNonPublicCloneMethod() throws Exception {
515 new PmdAssert(
516 "CloneMethodMustBePublic.java",
517 Matchers.is(false),
518 Matchers.containsString(
519 String.format(
520 PmdValidatorTest.BRACKETS,
521 "CloneMethodMustBePublic"
522 )
523 )
524 ).validate();
525 }
526
527
528
529
530
531
532
533
534 @Test
535 void forbidsCloneMethodReturnTypeNotMatchingClassName()
536 throws Exception {
537 new PmdAssert(
538 "CloneMethodReturnTypeMustMatchClassName.java",
539 Matchers.is(false),
540 Matchers.containsString(
541 String.format(
542 PmdValidatorTest.BRACKETS,
543 "CloneMethodReturnTypeMustMatchClassName"
544 )
545 )
546 ).validate();
547 }
548
549
550
551
552
553
554
555 @Test
556 void forbidsNonSimplifiedTernaryOperators()
557 throws Exception {
558 new PmdAssert(
559 "SimplifiedTernary.java",
560 Matchers.is(false),
561 Matchers.containsString(
562 String.format(
563 PmdValidatorTest.BRACKETS,
564 "SimplifiedTernary"
565 )
566 )
567 ).validate();
568 }
569
570
571
572
573
574
575
576
577
578
579 @Test
580 void prohibitsStaticImportsPlainAssertionsInTests()
581 throws Exception {
582 final String file = "PlainJUnitAssertionStaticImportBlock.java";
583 new PmdAssert(
584 file,
585 Matchers.is(false),
586 Matchers.containsString(
587 PmdValidatorTest.PLAIN_ASSERTIONS
588 )
589 ).validate();
590 }
591
592
593
594
595
596
597
598
599
600 @Test
601 void prohibitsPlainJunitAssertionsInTestMethods()
602 throws Exception {
603 final String file = "PlainJUnitAssertionTestMethod.java";
604 new PmdAssert(
605 file,
606 Matchers.is(false),
607 Matchers.containsString(
608 PmdValidatorTest.PLAIN_ASSERTIONS
609 )
610 ).validate();
611 }
612
613
614
615
616
617
618
619
620 @Test
621 void allowsAssertFail()
622 throws Exception {
623 final String file = "AllowAssertFail.java";
624 new PmdAssert(
625 file,
626 Matchers.is(false),
627 Matchers.allOf(
628 Matchers.not(
629 Matchers.containsString(
630 PmdValidatorTest.PLAIN_ASSERTIONS
631 )
632 ),
633 Matchers.containsString("UnitTestContainsTooManyAsserts")
634 )
635 ).validate();
636 }
637
638
639
640
641
642
643 @Test
644 void allowsNonTransientFields() throws Exception {
645 final String file = "AllowNonTransientFields.java";
646 new PmdAssert(
647 file,
648 Matchers.is(true),
649 Matchers.not(
650 Matchers.containsString(
651 "Found non-transient, non-static member."
652 )
653 )
654 ).validate();
655 }
656
657
658
659
660
661
662 @Test
663 void prohibitsPublicStaticMethods() throws Exception {
664 new PmdAssert(
665 "StaticPublicMethod.java",
666 Matchers.is(false),
667 Matchers.containsString(PmdValidatorTest.STATIC_METHODS)
668 ).validate();
669 }
670
671
672
673
674
675
676 @Test
677 void allowsPublicStaticMainMethod() throws Exception {
678 new PmdAssert(
679 "StaticPublicVoidMainMethod.java",
680 Matchers.is(true),
681 Matchers.not(
682 Matchers.containsString(PmdValidatorTest.STATIC_METHODS)
683 )
684 ).validate();
685 }
686
687
688
689
690
691
692
693
694 @Test
695 void allowsJunitFrameworkPublicStaticMethods() throws Exception {
696 new PmdAssert(
697 "JunitStaticPublicMethods.java",
698 Matchers.is(false),
699 Matchers.allOf(
700 Matchers.not(
701 Matchers.containsString(PmdValidatorTest.STATIC_METHODS)
702 ),
703 Matchers.containsString("UnitTestShouldIncludeAssert")
704 )
705 ).validate();
706 }
707
708
709
710
711
712
713 @Test
714 void allowsDuplicateLiteralsInAnnotations() throws Exception {
715 new PmdAssert(
716 "AllowsDuplicateLiteralsInAnnotations.java",
717 Matchers.is(true),
718 Matchers.not(
719 Matchers.containsString("AvoidDuplicateLiterals")
720 )
721 ).validate();
722 }
723
724
725
726
727
728
729 @Test
730 void testShouldBePackagePrivate() throws Exception {
731 new PmdAssert(
732 "TestShouldBePackagePrivate.java",
733 Matchers.is(false),
734 Matchers.containsString("JUnit5TestShouldBePackagePrivate")
735 ).validate();
736 }
737
738
739
740
741
742
743 @Test
744 void allowJunitThirdTestClassToBeFinal() throws Exception {
745 new PmdAssert(
746 "Junit3TestClassShouldBeFinal.java",
747 Matchers.is(false),
748 Matchers.containsString("JUnitTestClassShouldBeFinal")
749 ).validate();
750 }
751
752
753
754
755
756
757 @Test
758 void allowJunitFourthTestClassToBeFinal() throws Exception {
759 new PmdAssert(
760 "Junit4TestClassShouldBeFinal.java",
761 Matchers.is(false),
762 Matchers.containsString("JUnitTestClassShouldBeFinal")
763 ).validate();
764 }
765
766
767
768
769
770
771 @Test
772 void allowJunitFifthTestClassToBeFinal() throws Exception {
773 new PmdAssert(
774 "Junit5TestClassShouldBeFinal.java",
775 Matchers.is(false),
776 Matchers.containsString("JUnitTestClassShouldBeFinal")
777 ).validate();
778 }
779
780
781
782
783
784
785 @Test
786 void allowJunitTestClassToBeFinal() throws Exception {
787 new PmdAssert(
788 "JunitTestClassIsFinal.java",
789 Matchers.is(false),
790 Matchers.allOf(
791 Matchers.not(
792 Matchers.containsString("JUnitTestClassShouldBeFinal")
793 ),
794 Matchers.containsString("UnitTestShouldIncludeAssert")
795 )
796 ).validate();
797 }
798
799
800
801
802
803
804 @Test
805 @EnabledForJreRange(min = JRE.JAVA_21, max = JRE.JAVA_25)
806 void allowRecordClasses() throws Exception {
807 new PmdAssert(
808 "RecordParsed.java",
809 Matchers.is(true),
810 Matchers.not(
811 Matchers.containsString(PmdValidatorTest.STATIC_METHODS)
812 )
813 ).validate();
814 }
815
816
817
818
819
820
821 @Test
822 void allowsSwaggerAnnotations() throws Exception {
823 new PmdAssert(
824 "SwaggerApi.java",
825 Matchers.is(true),
826 Matchers.not(
827 Matchers.containsString("RuleSetReferenceId")
828 )
829 ).validate();
830 }
831
832
833
834
835
836
837 @Test
838 void prohibitsUnicodeCharactersInMethodNames() throws Exception {
839 new PmdAssert(
840 "UnicodeCharactersInMethodNames.java",
841 Matchers.is(false),
842 Matchers.containsString("MethodNamingConventions")
843 ).validate();
844 }
845
846
847
848
849
850 @Test
851 void notThrowsAnNullPointerExceptionOnPatternMatching() {
852 Assertions.assertDoesNotThrow(
853 () -> new PmdAssert(
854 "UseStringIsEmptyRuleFailsOnPatternMatching.java",
855 new IsEqual<>(false),
856 new StringContains("UnusedLocalVariable")
857 ).validate()
858 );
859 }
860 }