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.Joiner;
8   import com.qulice.spi.Environment;
9   import com.qulice.spi.Violation;
10  import java.io.File;
11  import java.io.IOException;
12  import java.util.Collection;
13  import org.cactoos.io.ResourceOf;
14  import org.cactoos.text.FormattedText;
15  import org.cactoos.text.IoCheckedText;
16  import org.cactoos.text.TextOf;
17  import org.hamcrest.MatcherAssert;
18  import org.hamcrest.Matchers;
19  import org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.Assumptions;
21  import org.junit.jupiter.api.Test;
22  
23  /**
24   * Test case for general {@link CheckstyleValidator} behavior that
25   * is not tied to a single check: method references, record/default
26   * methods, lambda and generics at end of line, chained lambdas
27   * passed as arguments, literal comparisons, and the imports-only
28   * regression guard. Per-check coverage lives in the dedicated
29   * {@code *CheckTest.java} and {@code Checkstyle*Test.java} files
30   * in this package.
31   *
32   * @since 0.3
33   */
34  final class CheckstyleValidatorTest {
35  
36      @Test
37      void acceptsInstanceMethodReferences() throws Exception {
38          Assertions.assertDoesNotThrow(
39              () -> this.runValidation("InstanceMethodRef.java", true)
40          );
41      }
42  
43      @Test
44      void acceptsIdAsMethodName() throws Exception {
45          Assertions.assertDoesNotThrow(
46              () -> this.runValidation("IdMethodName.java", true)
47          );
48      }
49  
50      @Test
51      void acceptsDefaultMethodsWithFinalModifiers() throws Exception {
52          Assertions.assertDoesNotThrow(
53              () -> this.runValidation("DefaultMethods.java", true)
54          );
55      }
56  
57      @Test
58      void supportsRecordTypes() throws Exception {
59          Assertions.assertDoesNotThrow(
60              () -> this.runValidation("ValidRecord.java", true)
61          );
62      }
63  
64      @Test
65      void acceptsLambdaAndGenericsAtEndOfLine() throws Exception {
66          Assertions.assertDoesNotThrow(
67              () -> this.runValidation("ValidLambdaAndGenericsAtEndOfLine.java", true)
68          );
69      }
70  
71      @Test
72      void acceptsChainedLambdaAsArgument() throws Exception {
73          Assertions.assertDoesNotThrow(
74              () -> this.runValidation("ValidLambdaIndentation.java", true)
75          );
76      }
77  
78      @Test
79      void allowsStringLiteralsOnBothSideInComparisons() throws Exception {
80          Assertions.assertDoesNotThrow(
81              () -> this.runValidation("ValidLiteralComparisonCheck.java", true)
82          );
83      }
84  
85      @Test
86      void countsRulesItApplies() throws Exception {
87          MatcherAssert.assertThat(
88              "Checkstyle cannot stay silent about how many rules it applies",
89              new CheckstyleValidator(new Environment.Mock()).rules(),
90              Matchers.greaterThan(100)
91          );
92      }
93  
94      @Test
95      void failsClearlyWhenCacheParentIsNotWritable() throws Exception {
96          final Environment.Mock mock = new Environment.Mock();
97          final File parent = new File(mock.tempdir(), "checkstyle");
98          Assumptions.assumeTrue(
99              parent.mkdirs() || parent.isDirectory(),
100             "Parent directory must exist for the regression scenario"
101         );
102         try {
103             Assumptions.assumeTrue(
104                 parent.setReadOnly(),
105                 "Filesystem does not support marking a directory read-only"
106             );
107             Assumptions.assumeFalse(
108                 parent.canWrite(),
109                 "Skipped: current user can write despite read-only attribute"
110             );
111             final Environment env = mock.withFile(
112                 "src/main/java/foo/Foo.java",
113                 "package foo; class Foo {}"
114             );
115             MatcherAssert.assertThat(
116                 "Validation must fail fast with a clear writability error",
117                 Assertions.assertThrows(
118                     IllegalStateException.class,
119                     () -> new CheckstyleValidator(env).validate(env.files("Foo.java"))
120                 ).getMessage(),
121                 Matchers.allOf(
122                     Matchers.containsString("write"),
123                     Matchers.containsString(parent.getAbsolutePath())
124                 )
125             );
126         } finally {
127             parent.setWritable(true, false);
128         }
129     }
130 
131     @Test
132     void doesNotThrowExceptionIfImportsOnly() throws Exception {
133         final Environment.Mock mock = new Environment.Mock();
134         final String crlf = String.valueOf('\r').concat(String.valueOf('\n'));
135         final String name = "Foo.java";
136         final Environment env = mock.withParam(
137             "license",
138             String.format(
139                 "file:%s",
140                 new License().savePackageInfo(
141                     new File(mock.basedir(), "src/main/java/foo")
142                 ).withLines("License-1.", "", "License-2.")
143                     .withEol(String.valueOf('\n'))
144                     .file()
145             )
146         ).withFile(
147             String.format("src/main/java/foo/%s", name),
148             Joiner.on(crlf).join(
149                 "package com.google;",
150                 crlf,
151                 "import java.util.*;"
152             )
153         );
154         MatcherAssert.assertThat(
155             "Validation error should exist",
156             new CheckstyleValidator(env).validate(env.files(name)),
157             Matchers.not(Matchers.<Violation>empty())
158         );
159     }
160 
161     private Collection<Violation> runValidation(final String file,
162         final boolean passes) throws IOException {
163         final Environment.Mock mock = new Environment.Mock();
164         final Environment env = mock.withParam(
165             "license",
166             String.format(
167                 "file:%s",
168                 new License().savePackageInfo(
169                     new File(mock.basedir(), "src/main/java/foo")
170                 ).withLines("Hello.")
171                     .withEol(String.valueOf('\n')).file()
172             )
173         ).withFile(
174             String.format("src/main/java/foo/%s", file),
175             new IoCheckedText(
176                 new TextOf(
177                     new ResourceOf(
178                         new FormattedText("com/qulice/checkstyle/%s", file)
179                     )
180                 )
181             ).asString()
182         );
183         final Collection<Violation> results =
184             new CheckstyleValidator(env).validate(env.files(file));
185         MatcherAssert.assertThat(
186             "validation result should match expected state",
187             results.isEmpty(),
188             Matchers.is(passes)
189         );
190         return results;
191     }
192 }