View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.google.common.base.Splitter;
8   import com.puppycrawl.tools.checkstyle.Checker;
9   import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
10  import com.puppycrawl.tools.checkstyle.PropertiesExpander;
11  import com.puppycrawl.tools.checkstyle.api.AuditListener;
12  import java.io.File;
13  import java.nio.charset.StandardCharsets;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.List;
17  import java.util.Objects;
18  import java.util.Properties;
19  import java.util.stream.Stream;
20  import org.apache.commons.io.IOUtils;
21  import org.hamcrest.MatcherAssert;
22  import org.hamcrest.Matchers;
23  import org.junit.jupiter.params.ParameterizedTest;
24  import org.junit.jupiter.params.provider.Arguments;
25  import org.junit.jupiter.params.provider.MethodSource;
26  import org.xml.sax.InputSource;
27  
28  /**
29   * Integration test case for all checkstyle checks.
30   *
31   * @since 0.3
32   */
33  final class ChecksTest {
34  
35      /**
36       * Test checkstyle for true positive.
37       *
38       * @param dir Directory where test scripts are located
39       * @param name The name of the Invalid*.java file
40       * @throws Exception If something goes wrong
41       */
42      @ParameterizedTest
43      @MethodSource("invalids")
44      void testCheckstyleTruePositive(final String dir, final String name)
45          throws Exception {
46          final AuditCollector collector = new AuditCollector();
47          this.run(
48              dir, String.format("/%s", name),
49              new FakeAuditListener(collector)
50          );
51          final String[] violations = IOUtils.toString(
52              Objects.requireNonNull(
53                  this.getClass().getResourceAsStream(
54                      String.format(
55                          "%s/violations%s.txt",
56                          dir,
57                          name.substring(
58                              "Invalid".length(),
59                              name.length() - ".java".length()
60                          )
61                      )
62                  )
63              ),
64              StandardCharsets.UTF_8
65          ).split(String.valueOf('\n'));
66          MatcherAssert.assertThat(
67              String.format(
68                  "Expected exactly %d violations from %s/%s (%s)",
69                  violations.length, dir, name, collector.summary()
70              ),
71              collector.eventCount() == violations.length
72                  && Arrays.stream(violations).allMatch(
73                      line -> {
74                          final List<String> sectors =
75                              Splitter.on(':').splitToList(line);
76                          return collector.has(
77                              Integer.valueOf(sectors.get(0)),
78                              sectors.get(1).trim()
79                          );
80                      }
81                  ),
82              Matchers.is(true)
83          );
84      }
85  
86      /**
87       * Test checkstyle for true negative.
88       *
89       * @param dir Directory where test scripts are located
90       * @param name The name of the Valid*.java file
91       * @throws Exception If something goes wrong
92       */
93      @ParameterizedTest
94      @MethodSource("valids")
95      void testCheckstyleTrueNegative(final String dir, final String name)
96          throws Exception {
97          final AuditCollector collector = new AuditCollector();
98          this.run(
99              dir, String.format("/%s", name),
100             new FakeAuditListener(collector)
101         );
102         MatcherAssert.assertThat(
103             String.format("Log should be empty for valid file %s/%s", dir, name),
104             collector.summary(),
105             Matchers.equalTo("")
106         );
107     }
108 
109     private void run(
110         final String dir, final String name, final AuditListener listener
111     ) throws Exception {
112         final Checker checker = new Checker();
113         checker.setModuleClassLoader(
114             Thread.currentThread().getContextClassLoader()
115         );
116         checker.configure(
117             ConfigurationLoader.loadConfiguration(
118                 new InputSource(
119                     this.getClass().getResourceAsStream(
120                         String.format("%s/config.xml", dir)
121                     )
122                 ),
123                 new PropertiesExpander(new Properties()),
124                 ConfigurationLoader.IgnoredModulesOptions.OMIT
125             )
126         );
127         final List<File> files = new ArrayList<>(0);
128         files.add(
129             new File(
130                 this.getClass().getResource(
131                     String.format("%s%s", dir, name)
132                 ).getFile()
133             )
134         );
135         checker.addListener(listener);
136         checker.process(files);
137         checker.destroy();
138     }
139 
140     private static Stream<Arguments> invalids() {
141         return ChecksTest.checks().flatMap(dir -> ChecksTest.files(dir, "Invalid"));
142     }
143 
144     private static Stream<Arguments> valids() {
145         return ChecksTest.checks().flatMap(dir -> ChecksTest.files(dir, "Valid"));
146     }
147 
148     private static Stream<Arguments> files(
149         final String dir, final String prefix
150     ) {
151         final File directory = new File(
152             Objects.requireNonNull(
153                 ChecksTest.class.getResource(dir),
154                 String.format("Resource directory not found: %s", dir)
155             ).getFile()
156         );
157         final File[] found = directory.listFiles(
158             (parent, child) -> child.endsWith(".java")
159                 && (
160                     child.equals(String.format("%s.java", prefix))
161                         || child.startsWith(String.format("%s-", prefix))
162                 )
163         );
164         final Stream<Arguments> result;
165         if (found == null) {
166             result = Stream.empty();
167         } else {
168             result = Arrays.stream(found)
169                 .sorted()
170                 .map(file -> Arguments.of(dir, file.getName()));
171         }
172         return result;
173     }
174 
175     private static Stream<String> checks() {
176         return Stream.of(
177             "MethodsOrderCheck",
178             "MultilineJavadocTagsCheck",
179             "StringLiteralsConcatenationCheck",
180             "ProhibitLineSeparatorInStringsCheck",
181             "EmptyLinesCheck",
182             "EmptyLineBeforeFirstMemberCheck",
183             "ImportCohesionCheck",
184             "BracketsStructureCheck",
185             "NestedSwitchCheck",
186             "ProhibitTestExpectedCheck",
187             "ProhibitTestMethodNameCheck",
188             "MethodDeclarationLengthCheck",
189             "CurlyBracketsStructureCheck",
190             "JavadocLocationCheck",
191             "JavadocParameterOrderCheck",
192             "MethodBodyCommentsCheck",
193             "RequireThisCheck",
194             "ProtectedMethodInFinalClassCheck",
195             "NoJavadocForOverriddenMethodsCheck",
196             "NonStaticMethodCheck",
197             "ConstantUsageCheck",
198             "SingleUseConstantCheck",
199             "FullyQualifiedTypeCheck",
200             "ImplicitConstructorCheck",
201             "JavadocEmptyLineCheck",
202             "JavadocEmptyLineBeforeTagCheck",
203             "JavadocCompactParagraphCheck",
204             "JavadocStrayAsteriskCheck",
205             "JavadocUnclosedParagraphCheck",
206             "JavadocFirstLineCheck",
207             "JavadocNoIndentCheck",
208             "JavadocTagsCheck",
209             "JavadocTagsDotCheck",
210             "JavadocThrowsCheck",
211             "ProhibitNonFinalClassesCheck",
212             "QualifyInnerClassCheck",
213             "CommentCheck",
214             "ProhibitUnusedPrivateConstructorCheck",
215             "ConstructorsOrderCheck",
216             "ConstructorsCodeFreeCheck",
217             "RedundantSuperConstructorCheck",
218             "StaticAccessViaInstanceCheck",
219             "SingleSpaceSeparatorCheck",
220             "SimpleStringSplitCheck",
221             "ProhibitFieldsInTestClassesCheck",
222             "ProhibitStaticNestedClassesCheck",
223             "NoJavadocForPrivateMethodsCheck",
224             "ParameterNumberCheck"
225         ).map(s -> String.format("ChecksTest/%s", s));
226     }
227 }