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