View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.regexp;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_EMPTY;
24  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_REGEXP_EXCEEDED;
25  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_REGEXP_MINIMUM;
26  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_STACKOVERFLOW;
27  
28  import java.io.File;
29  import java.nio.charset.StandardCharsets;
30  import java.nio.file.Files;
31  
32  import org.junit.jupiter.api.Test;
33  import org.junit.jupiter.api.io.TempDir;
34  
35  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
36  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
37  import com.puppycrawl.tools.checkstyle.api.FileText;
38  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestLoggingReporter;
39  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
40  
41  public class RegexpMultilineCheckTest extends AbstractModuleTestSupport {
42  
43      @TempDir
44      public File temporaryFolder;
45  
46      @Override
47      protected String getPackageLocation() {
48          return "com/puppycrawl/tools/checkstyle/checks/regexp/regexpmultiline";
49      }
50  
51      @Test
52      public void testIt() throws Exception {
53          final String[] expected = {
54              "78: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "System\\.(out)|(err)\\.print(ln)?\\("),
55          };
56          verifyWithInlineConfigParser(
57                  getPath("InputRegexpMultilineSemantic.java"), expected);
58      }
59  
60      @Test
61      public void testMessageProperty()
62              throws Exception {
63          final String[] expected = {
64              "79: " + "Bad line :(",
65          };
66          verifyWithInlineConfigParser(
67                  getPath("InputRegexpMultilineSemantic2.java"), expected);
68      }
69  
70      @Test
71      public void testIgnoreCaseTrue() throws Exception {
72          final String[] expected = {
73              "79: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "SYSTEM\\.(OUT)|(ERR)\\.PRINT(LN)?\\("),
74          };
75          verifyWithInlineConfigParser(
76                  getPath("InputRegexpMultilineSemantic3.java"), expected);
77      }
78  
79      @Test
80      public void testIgnoreCaseFalse() throws Exception {
81          final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
82          verifyWithInlineConfigParser(
83                  getPath("InputRegexpMultilineSemantic4.java"), expected);
84      }
85  
86      @Test
87      public void testIllegalFailBelowErrorLimit() throws Exception {
88          final String[] expected = {
89              "16: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "^import"),
90              "17: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "^import"),
91              "18: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "^import"),
92          };
93          verifyWithInlineConfigParser(
94                  getPath("InputRegexpMultilineSemantic5.java"), expected);
95      }
96  
97      @Test
98      public void testCarriageReturn() throws Exception {
99          final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
100         checkConfig.addProperty("format", "\\r");
101         checkConfig.addProperty("maximum", "0");
102         final String[] expected = {
103             "1: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "\\r"),
104             "3: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "\\r"),
105         };
106 
107         final File file = File.createTempFile("junit", null, temporaryFolder);
108         Files.write(file.toPath(),
109             "first line \r\n second line \n\r third line".getBytes(StandardCharsets.UTF_8));
110 
111         verify(checkConfig, file.getPath(), expected);
112     }
113 
114     @Test
115     public void testMaximum() throws Exception {
116         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
117         checkConfig.addProperty("format", "\\r");
118         checkConfig.addProperty("maximum", "1");
119         final String[] expected = {
120             "3: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "\\r"),
121         };
122 
123         final File file = File.createTempFile("junit", null, temporaryFolder);
124         Files.write(file.toPath(),
125                 "first line \r\n second line \n\r third line".getBytes(StandardCharsets.UTF_8));
126 
127         verify(checkConfig, file.getPath(), expected);
128     }
129 
130     /**
131      * Done as a UT cause new instance of Detector is created each time 'verify' executed.
132      *
133      * @throws Exception some Exception
134      */
135     @Test
136     public void testStateIsBeingReset() throws Exception {
137         final TestLoggingReporter reporter = new TestLoggingReporter();
138         final DetectorOptions detectorOptions = DetectorOptions.newBuilder()
139                 .reporter(reporter)
140                 .format("\\r")
141                 .maximum(1)
142                 .build();
143 
144         final MultilineDetector detector =
145                 new MultilineDetector(detectorOptions);
146         final File file = File.createTempFile("junit", null, temporaryFolder);
147         Files.write(file.toPath(),
148                 "first line \r\n second line \n\r third line".getBytes(StandardCharsets.UTF_8));
149 
150         detector.processLines(new FileText(file, StandardCharsets.UTF_8.name()));
151         detector.processLines(new FileText(file, StandardCharsets.UTF_8.name()));
152         assertWithMessage("Logged unexpected amount of issues")
153                 .that(reporter.getLogCount())
154                 .isEqualTo(2);
155     }
156 
157     @Test
158     public void testDefaultConfiguration() throws Exception {
159         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
160         verifyWithInlineConfigParser(
161                 getPath("InputRegexpMultilineSemantic6.java"), expected);
162     }
163 
164     @Test
165     public void testNullFormat() throws Exception {
166         final String[] expected = {
167             "1: " + getCheckMessage(MSG_EMPTY),
168         };
169         verifyWithInlineConfigParser(
170                 getPath("InputRegexpMultilineSemantic7.java"), expected);
171     }
172 
173     @Test
174     public void testEmptyFormat() throws Exception {
175         final String[] expected = {
176             "1: " + getCheckMessage(MSG_EMPTY),
177         };
178         verifyWithInlineConfigParser(
179                 getPath("InputRegexpMultilineSemantic8.java"), expected);
180     }
181 
182     @Test
183     public void testNoStackOverflowError() throws Exception {
184         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
185         // http://madbean.com/2004/mb2004-20/
186         checkConfig.addProperty("format", "(x|y)*");
187 
188         final String[] expected = {
189             "1: " + getCheckMessage(MSG_STACKOVERFLOW, "(x|y)*"),
190         };
191 
192         final File file = File.createTempFile("junit", null, temporaryFolder);
193         Files.write(file.toPath(), makeLargeXyString().toString().getBytes(StandardCharsets.UTF_8));
194 
195         verify(checkConfig, file.getPath(), expected);
196     }
197 
198     @Test
199     public void testMinimum() throws Exception {
200         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
201         checkConfig.addProperty("format", "\\r");
202         checkConfig.addProperty("minimum", "5");
203         final String[] expected = {
204             "1: " + getCheckMessage(MSG_REGEXP_MINIMUM, "5", "\\r"),
205         };
206 
207         final File file = File.createTempFile("junit", null, temporaryFolder);
208         Files.write(file.toPath(), "".getBytes(StandardCharsets.UTF_8));
209 
210         verify(checkConfig, file.getPath(), expected);
211     }
212 
213     @Test
214     public void testMinimumWithCustomMessage() throws Exception {
215         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
216         checkConfig.addProperty("format", "\\r");
217         checkConfig.addProperty("minimum", "5");
218         checkConfig.addProperty("message", "some message");
219         final String[] expected = {
220             "1: some message",
221         };
222 
223         final File file = File.createTempFile("junit", null, temporaryFolder);
224         Files.write(file.toPath(), "".getBytes(StandardCharsets.UTF_8));
225 
226         verify(checkConfig, file.getPath(), expected);
227     }
228 
229     private static CharSequence makeLargeXyString() {
230         // now needs 10'000 or 100'000, as just 1000 is no longer enough today to provoke the
231         // StackOverflowError
232         final int size = 100_000;
233         return "xy".repeat(size / 2);
234     }
235 
236     @Test
237     public void testGoodLimit() throws Exception {
238         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
239         verifyWithInlineConfigParser(
240                 getPath("InputRegexpMultilineSemantic9.java"), expected);
241     }
242 
243     @Test
244     public void testMultilineSupport() throws Exception {
245         final String[] expected = {
246             "22: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "(a)bc.*def"),
247         };
248         verifyWithInlineConfigParser(
249                 getPath("InputRegexpMultilineMultilineSupport.java"), expected);
250     }
251 
252     @Test
253     public void testMultilineSupportNotGreedy() throws Exception {
254         final String[] expected = {
255             "22: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "(a)bc.*?def"),
256             "24: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "(a)bc.*?def"),
257         };
258         verifyWithInlineConfigParser(
259                 getPath("InputRegexpMultilineMultilineSupport2.java"), expected);
260     }
261 
262 }