View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.errorprone;
6   
7   import com.qulice.spi.Environment;
8   import com.qulice.spi.Violation;
9   import java.io.File;
10  import java.io.IOException;
11  import java.util.ArrayList;
12  import java.util.Collection;
13  import java.util.Collections;
14  import org.hamcrest.MatcherAssert;
15  import org.hamcrest.Matchers;
16  import org.junit.jupiter.api.Test;
17  
18  /**
19   * Test case for {@link ErrorProneValidator}'s source-level awareness of the
20   * stock {@code PatternMatchingInstanceof} check. The check suggests
21   * pattern-matching {@code instanceof}, a Java 16+ feature, so it must not fire
22   * when the project compiles at an older source level (issue #1716).
23   *
24   * @since 1.0
25   */
26  final class ErrorPronePatternMatchingInstanceofTest {
27  
28      /**
29       * Name of the ErrorProne check under test.
30       */
31      private static final String CHECK = "PatternMatchingInstanceof";
32  
33      /**
34       * Path of the sample file, relative to the base directory.
35       */
36      private static final String FILE = "src/main/java/foo/Sample.java";
37  
38      /**
39       * A class whose {@code instanceof} followed by a cast to the same type is
40       * exactly what {@code PatternMatchingInstanceof} suggests rewriting.
41       */
42      private static final String SOURCE = String.join(
43          System.lineSeparator(),
44          "package foo;",
45          "/**",
46          " * Sample.",
47          " * @since 1.0",
48          " */",
49          "public final class Sample {",
50          "    /**",
51          "     * Compare with another object.",
52          "     * @param other The other object",
53          "     * @return TRUE if the same",
54          "     */",
55          "    public boolean same(final Object other) {",
56          "        return other instanceof Sample",
57          "            && ((Sample) other).hashCode() == this.hashCode();",
58          "    }",
59          "}"
60      );
61  
62      @Test
63      void skipsPatternMatchingInstanceofOnSourceEight() throws Exception {
64          MatcherAssert.assertThat(
65              "PatternMatchingInstanceof must not fire when source is 8",
66              this.checks("8"),
67              Matchers.not(Matchers.hasItem(ErrorPronePatternMatchingInstanceofTest.CHECK))
68          );
69      }
70  
71      @Test
72      void skipsPatternMatchingInstanceofOnLegacySourceEight() throws Exception {
73          MatcherAssert.assertThat(
74              "PatternMatchingInstanceof must not fire when source is 1.8",
75              this.checks("1.8"),
76              Matchers.not(Matchers.hasItem(ErrorPronePatternMatchingInstanceofTest.CHECK))
77          );
78      }
79  
80      @Test
81      void reportsPatternMatchingInstanceofOnModernSource() throws Exception {
82          MatcherAssert.assertThat(
83              "PatternMatchingInstanceof must fire when source is 17",
84              this.checks("17"),
85              Matchers.hasItem(ErrorPronePatternMatchingInstanceofTest.CHECK)
86          );
87      }
88  
89      private Collection<String> checks(final String source) throws IOException {
90          final Environment.Mock mock = new Environment.Mock();
91          if (source != null) {
92              mock.withParam("maven.compiler.source", source);
93          }
94          final Environment env = mock.withFile(
95              ErrorPronePatternMatchingInstanceofTest.FILE,
96              ErrorPronePatternMatchingInstanceofTest.SOURCE
97          );
98          final Collection<Violation> violations = new ErrorProneValidator(env).validate(
99              Collections.singletonList(
100                 new File(env.basedir(), ErrorPronePatternMatchingInstanceofTest.FILE)
101             )
102         );
103         final Collection<String> names = new ArrayList<>(violations.size());
104         for (final Violation violation : violations) {
105             names.add(violation.name());
106         }
107         return names;
108     }
109 }