1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_EMPTY;
23  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_REGEXP_EXCEEDED;
24  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_REGEXP_MINIMUM;
25  import static com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector.MSG_STACKOVERFLOW;
26  
27  import java.io.File;
28  import java.nio.charset.StandardCharsets;
29  import java.nio.file.Files;
30  
31  import org.junit.Assert;
32  import org.junit.Rule;
33  import org.junit.Test;
34  import org.junit.rules.TemporaryFolder;
35  
36  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
37  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
38  import com.puppycrawl.tools.checkstyle.api.FileText;
39  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestLoggingReporter;
40  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
41  
42  public class RegexpMultilineCheckTest extends AbstractModuleTestSupport {
43  
44      @Rule
45      public final TemporaryFolder temporaryFolder = new TemporaryFolder();
46  
47      @Override
48      protected String getPackageLocation() {
49          return "com/puppycrawl/tools/checkstyle/checks/regexp/regexpmultiline";
50      }
51  
52      @Test
53      public void testIt() throws Exception {
54          final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
55          checkConfig.addAttribute("format", "System\\.(out)|(err)\\.print(ln)?\\(");
56          final String[] expected = {
57              "69: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "System\\.(out)|(err)\\.print(ln)?\\("),
58          };
59          verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
60      }
61  
62      @Test
63      public void testMessageProperty()
64              throws Exception {
65          final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
66          checkConfig.addAttribute("format", "System\\.(out)|(err)\\.print(ln)?\\(");
67          checkConfig.addAttribute("message", "Bad line :(");
68          final String[] expected = {
69              "69: " + "Bad line :(",
70          };
71          verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
72      }
73  
74      @Test
75      public void testIgnoreCaseTrue() throws Exception {
76          final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
77          checkConfig.addAttribute("format", "SYSTEM\\.(OUT)|(ERR)\\.PRINT(LN)?\\(");
78          checkConfig.addAttribute("ignoreCase", "true");
79          final String[] expected = {
80              "69: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "SYSTEM\\.(OUT)|(ERR)\\.PRINT(LN)?\\("),
81          };
82          verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
83      }
84  
85      @Test
86      public void testIgnoreCaseFalse() throws Exception {
87          final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
88          checkConfig.addAttribute("format", "SYSTEM\\.(OUT)|(ERR)\\.PRINT(LN)?\\(");
89          checkConfig.addAttribute("ignoreCase", "false");
90          final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
91          verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
92      }
93  
94      @Test
95      public void testIllegalFailBelowErrorLimit() throws Exception {
96          final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
97          checkConfig.addAttribute("format", "^import");
98          final String[] expected = {
99              "7: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "^import"),
100             "8: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "^import"),
101             "9: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "^import"),
102         };
103         verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
104     }
105 
106     @Test
107     public void testCarriageReturn() throws Exception {
108         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
109         checkConfig.addAttribute("format", "\\r");
110         checkConfig.addAttribute("maximum", "0");
111         final String[] expected = {
112             "1: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "\\r"),
113             "3: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "\\r"),
114         };
115 
116         final File file = temporaryFolder.newFile();
117         Files.write(file.toPath(),
118             "first line \r\n second line \n\r third line".getBytes(StandardCharsets.UTF_8));
119 
120         verify(checkConfig, file.getPath(), expected);
121     }
122 
123     @Test
124     public void testMaximum() throws Exception {
125         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
126         checkConfig.addAttribute("format", "\\r");
127         checkConfig.addAttribute("maximum", "1");
128         final String[] expected = {
129             "3: " + getCheckMessage(MSG_REGEXP_EXCEEDED, "\\r"),
130         };
131 
132         final File file = temporaryFolder.newFile();
133         Files.write(file.toPath(),
134                 "first line \r\n second line \n\r third line".getBytes(StandardCharsets.UTF_8));
135 
136         verify(checkConfig, file.getPath(), expected);
137     }
138 
139     /**
140      * Done as a UT cause new instance of Detector is created each time 'verify' executed.
141      * @throws Exception some Exception
142      */
143     @Test
144     public void testStateIsBeingReset() throws Exception {
145         final TestLoggingReporter reporter = new TestLoggingReporter();
146         final DetectorOptions detectorOptions = DetectorOptions.newBuilder()
147                 .reporter(reporter)
148                 .format("\\r")
149                 .maximum(1)
150                 .build();
151 
152         final MultilineDetector detector =
153                 new MultilineDetector(detectorOptions);
154         final File file = temporaryFolder.newFile();
155         Files.write(file.toPath(),
156                 "first line \r\n second line \n\r third line".getBytes(StandardCharsets.UTF_8));
157 
158         detector.processLines(new FileText(file, StandardCharsets.UTF_8.name()));
159         detector.processLines(new FileText(file, StandardCharsets.UTF_8.name()));
160         Assert.assertEquals("Logged unexpected amount of issues",
161                 2, reporter.getLogCount());
162     }
163 
164     @Test
165     public void testDefaultConfiguration() throws Exception {
166         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
167         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
168         verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
169     }
170 
171     @Test
172     public void testNullFormat() throws Exception {
173         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
174         checkConfig.addAttribute("format", null);
175         final String[] expected = {
176             "1: " + getCheckMessage(MSG_EMPTY),
177         };
178         verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
179     }
180 
181     @Test
182     public void testEmptyFormat() throws Exception {
183         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
184         checkConfig.addAttribute("format", "");
185         final String[] expected = {
186             "1: " + getCheckMessage(MSG_EMPTY),
187         };
188         verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
189     }
190 
191     @Test
192     public void testNoStackOverflowError() throws Exception {
193         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
194         // http://madbean.com/2004/mb2004-20/
195         checkConfig.addAttribute("format", "(x|y)*");
196 
197         final String[] expected = {
198             "1: " + getCheckMessage(MSG_STACKOVERFLOW),
199         };
200 
201         final File file = temporaryFolder.newFile();
202         Files.write(file.toPath(), makeLargeXyString().toString().getBytes(StandardCharsets.UTF_8));
203 
204         verify(checkConfig, file.getPath(), expected);
205     }
206 
207     @Test
208     public void testMinimum() throws Exception {
209         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
210         checkConfig.addAttribute("format", "\\r");
211         checkConfig.addAttribute("minimum", "5");
212         final String[] expected = {
213             "1: " + getCheckMessage(MSG_REGEXP_MINIMUM, "5", "\\r"),
214         };
215 
216         final File file = temporaryFolder.newFile();
217         Files.write(file.toPath(), "".getBytes(StandardCharsets.UTF_8));
218 
219         verify(checkConfig, file.getPath(), expected);
220     }
221 
222     @Test
223     public void testMinimumWithCustomMessage() throws Exception {
224         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
225         checkConfig.addAttribute("format", "\\r");
226         checkConfig.addAttribute("minimum", "5");
227         checkConfig.addAttribute("message", "some message");
228         final String[] expected = {
229             "1: some message",
230         };
231 
232         final File file = temporaryFolder.newFile();
233         Files.write(file.toPath(), "".getBytes(StandardCharsets.UTF_8));
234 
235         verify(checkConfig, file.getPath(), expected);
236     }
237 
238     private static CharSequence makeLargeXyString() {
239         // now needs 10'000 or 100'000, as just 1000 is no longer enough today to provoke the
240         // StackOverflowError
241         final int size = 100000;
242         final StringBuilder largeString = new StringBuilder(size);
243         for (int i = 0; i < size / 2; i++) {
244             largeString.append("xy");
245         }
246         return largeString;
247     }
248 
249     @Test
250     public void testGoodLimit() throws Exception {
251         final DefaultConfiguration checkConfig = createModuleConfig(RegexpMultilineCheck.class);
252         checkConfig.addAttribute("format", "^import");
253         checkConfig.addAttribute("maximum", "5000");
254         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
255         verify(checkConfig, getPath("InputRegexpMultilineSemantic.java"), expected);
256     }
257 
258 }