View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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;
21  
22  import static com.puppycrawl.tools.checkstyle.checks.AvoidEscapedUnicodeCharactersCheck.MSG_KEY;
23  import static org.junit.Assert.assertArrayEquals;
24  import static org.junit.Assert.assertEquals;
25  
26  import java.lang.reflect.Method;
27  import java.util.Arrays;
28  import java.util.regex.Pattern;
29  import java.util.stream.IntStream;
30  
31  import org.junit.Test;
32  import org.powermock.reflect.Whitebox;
33  
34  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
35  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
36  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
37  
38  public class AvoidEscapedUnicodeCharactersCheckTest extends AbstractModuleTestSupport {
39  
40      // C0 (ASCII and derivatives)
41      // https://en.wiktionary.org/wiki/Appendix:Control_characters#C0_.28ASCII_and_derivatives.29
42      private static final int[] C0_CONTROL_CHARACTER_INDICES = {
43          0x0000,
44          0x0001,
45          0x0002,
46          0x0003,
47          0x0004,
48          0x0005,
49          0x0006,
50          0x0007,
51          0x0008,
52          0x0009,
53          0x000a,
54          0x000b,
55          0x000c,
56          0x000d,
57          0x000e,
58          0x000f,
59          0x0010,
60          0x0011,
61          0x0012,
62          0x0013,
63          0x0014,
64          0x0015,
65          0x0016,
66          0x0017,
67          0x0018,
68          0x0019,
69          0x001a,
70          0x001b,
71          0x001c,
72          0x001d,
73          0x001e,
74          0x001f,
75      };
76  
77      // C1 set
78      // https://en.wiktionary.org/wiki/Appendix:Control_characters#C1_set
79      private static final int[] C1_CONTROL_CHARACTER_INDICES = {
80          0x0080,
81          0x0081,
82          0x0082,
83          0x0083,
84          0x0084,
85          0x0085,
86          0x0086,
87          0x0087,
88          0x0088,
89          0x0089,
90          0x008a,
91          0x008b,
92          0x008c,
93          0x008d,
94          0x008e,
95          0x008f,
96          0x0090,
97          0x0091,
98          0x0092,
99          0x0093,
100         0x0094,
101         0x0095,
102         0x0096,
103         0x0097,
104         0x0098,
105         0x0099,
106         0x009a,
107         0x009b,
108         0x009c,
109         0x009d,
110         0x009e,
111         0x009f,
112     };
113 
114     // Other control characters which do not occur in the C0 or C1 sets
115     // https://en.wiktionary.org/wiki/Appendix:Control_characters#Unicode_control_characters
116     private static final int[] OTHER_CONTROL_CHARACTER_INDICES = {
117         0x00ad,
118         0x034f,
119         0x070f,
120         0x180e,
121         0x200b,
122         0x200c,
123         0x200d,
124         0x200e,
125         0x200f,
126         0x202a,
127         0x202b,
128         0x202c,
129         0x202d,
130         0x202e,
131         0x2060,
132         0x2061,
133         0x2062,
134         0x2063,
135         0x2064,
136         0x206a,
137         0x206b,
138         0x206c,
139         0x206d,
140         0x206e,
141         0x206f,
142         0xfeff,
143         0xfff9,
144         0xfffa,
145         0xfffb,
146     };
147 
148     @Override
149     protected String getPackageLocation() {
150         return "com/puppycrawl/tools/checkstyle/checks/avoidescapedunicodecharacters";
151     }
152 
153     @Test
154     public void testGetRequiredTokens() {
155         final AvoidEscapedUnicodeCharactersCheck checkObj =
156             new AvoidEscapedUnicodeCharactersCheck();
157         final int[] expected = {
158             TokenTypes.STRING_LITERAL,
159             TokenTypes.CHAR_LITERAL,
160         };
161         assertArrayEquals("Required tokens differ from expected",
162                 expected, checkObj.getRequiredTokens());
163     }
164 
165     @Test
166     public void testDefault() throws Exception {
167         final DefaultConfiguration checkConfig =
168                 createModuleConfig(AvoidEscapedUnicodeCharactersCheck.class);
169         final String[] expected = {
170             "7: " + getCheckMessage(MSG_KEY),
171             "9: " + getCheckMessage(MSG_KEY),
172             "11: " + getCheckMessage(MSG_KEY),
173             "15: " + getCheckMessage(MSG_KEY),
174             "16: " + getCheckMessage(MSG_KEY),
175             "20: " + getCheckMessage(MSG_KEY),
176             "24: " + getCheckMessage(MSG_KEY),
177             "25: " + getCheckMessage(MSG_KEY),
178             "27: " + getCheckMessage(MSG_KEY),
179             "31: " + getCheckMessage(MSG_KEY),
180             "32: " + getCheckMessage(MSG_KEY),
181             "33: " + getCheckMessage(MSG_KEY),
182             "34: " + getCheckMessage(MSG_KEY),
183             "42: " + getCheckMessage(MSG_KEY),
184             "59: " + getCheckMessage(MSG_KEY),
185             "60: " + getCheckMessage(MSG_KEY),
186             "61: " + getCheckMessage(MSG_KEY),
187             "62: " + getCheckMessage(MSG_KEY),
188             "72: " + getCheckMessage(MSG_KEY),
189             "73: " + getCheckMessage(MSG_KEY),
190             "74: " + getCheckMessage(MSG_KEY),
191             "75: " + getCheckMessage(MSG_KEY),
192             "76: " + getCheckMessage(MSG_KEY),
193             "77: " + getCheckMessage(MSG_KEY),
194             "79: " + getCheckMessage(MSG_KEY),
195             "82: " + getCheckMessage(MSG_KEY),
196             "86: " + getCheckMessage(MSG_KEY),
197             "87: " + getCheckMessage(MSG_KEY),
198             "88: " + getCheckMessage(MSG_KEY),
199             "89: " + getCheckMessage(MSG_KEY),
200             "92: " + getCheckMessage(MSG_KEY),
201             "93: " + getCheckMessage(MSG_KEY),
202             "94: " + getCheckMessage(MSG_KEY),
203             "98: " + getCheckMessage(MSG_KEY),
204             "104: " + getCheckMessage(MSG_KEY),
205         };
206         verify(checkConfig, getPath("InputAvoidEscapedUnicodeCharacters.java"), expected);
207     }
208 
209     @Test
210     public void testAllowEscapesForControlCharacterSet() throws Exception {
211         final DefaultConfiguration checkConfig =
212                 createModuleConfig(AvoidEscapedUnicodeCharactersCheck.class);
213         checkConfig.addAttribute("allowEscapesForControlCharacters", "true");
214         final String[] expected = {
215             "7: " + getCheckMessage(MSG_KEY),
216             "9: " + getCheckMessage(MSG_KEY),
217             "11: " + getCheckMessage(MSG_KEY),
218             "15: " + getCheckMessage(MSG_KEY),
219             "16: " + getCheckMessage(MSG_KEY),
220             "24: " + getCheckMessage(MSG_KEY),
221             "25: " + getCheckMessage(MSG_KEY),
222             "31: " + getCheckMessage(MSG_KEY),
223             "32: " + getCheckMessage(MSG_KEY),
224             "33: " + getCheckMessage(MSG_KEY),
225             "34: " + getCheckMessage(MSG_KEY),
226             "42: " + getCheckMessage(MSG_KEY),
227             "59: " + getCheckMessage(MSG_KEY),
228             "60: " + getCheckMessage(MSG_KEY),
229             "61: " + getCheckMessage(MSG_KEY),
230             "62: " + getCheckMessage(MSG_KEY),
231             "73: " + getCheckMessage(MSG_KEY),
232             "74: " + getCheckMessage(MSG_KEY),
233             "75: " + getCheckMessage(MSG_KEY),
234             "76: " + getCheckMessage(MSG_KEY),
235             "77: " + getCheckMessage(MSG_KEY),
236             "79: " + getCheckMessage(MSG_KEY),
237             "82: " + getCheckMessage(MSG_KEY),
238             "86: " + getCheckMessage(MSG_KEY),
239             "87: " + getCheckMessage(MSG_KEY),
240             "88: " + getCheckMessage(MSG_KEY),
241             "89: " + getCheckMessage(MSG_KEY),
242             "92: " + getCheckMessage(MSG_KEY),
243             "94: " + getCheckMessage(MSG_KEY),
244             "98: " + getCheckMessage(MSG_KEY),
245             "104: " + getCheckMessage(MSG_KEY),
246         };
247         verify(checkConfig, getPath("InputAvoidEscapedUnicodeCharacters.java"), expected);
248     }
249 
250     @Test
251     public void testAllowByTailComment() throws Exception {
252         final DefaultConfiguration checkConfig =
253                 createModuleConfig(AvoidEscapedUnicodeCharactersCheck.class);
254         checkConfig.addAttribute("allowByTailComment", "true");
255         final String[] expected = {
256             "7: " + getCheckMessage(MSG_KEY),
257             "15: " + getCheckMessage(MSG_KEY),
258             "24: " + getCheckMessage(MSG_KEY),
259             "31: " + getCheckMessage(MSG_KEY),
260             "33: " + getCheckMessage(MSG_KEY),
261             "34: " + getCheckMessage(MSG_KEY),
262             "59: " + getCheckMessage(MSG_KEY),
263             "60: " + getCheckMessage(MSG_KEY),
264             "61: " + getCheckMessage(MSG_KEY),
265             "62: " + getCheckMessage(MSG_KEY),
266             "73: " + getCheckMessage(MSG_KEY),
267             "74: " + getCheckMessage(MSG_KEY),
268             "75: " + getCheckMessage(MSG_KEY),
269             "76: " + getCheckMessage(MSG_KEY),
270             "77: " + getCheckMessage(MSG_KEY),
271             "79: " + getCheckMessage(MSG_KEY),
272             "82: " + getCheckMessage(MSG_KEY),
273             "92: " + getCheckMessage(MSG_KEY),
274             "98: " + getCheckMessage(MSG_KEY),
275             "104: " + getCheckMessage(MSG_KEY),
276         };
277         verify(checkConfig, getPath("InputAvoidEscapedUnicodeCharacters.java"), expected);
278     }
279 
280     @Test
281     public void testAllowAllCharactersEscaped() throws Exception {
282         final DefaultConfiguration checkConfig =
283                 createModuleConfig(AvoidEscapedUnicodeCharactersCheck.class);
284         checkConfig.addAttribute("allowIfAllCharactersEscaped", "true");
285         final String[] expected = {
286             "7: " + getCheckMessage(MSG_KEY),
287             "9: " + getCheckMessage(MSG_KEY),
288             "11: " + getCheckMessage(MSG_KEY),
289             "15: " + getCheckMessage(MSG_KEY),
290             "16: " + getCheckMessage(MSG_KEY),
291             "31: " + getCheckMessage(MSG_KEY),
292             "32: " + getCheckMessage(MSG_KEY),
293             "33: " + getCheckMessage(MSG_KEY),
294             "42: " + getCheckMessage(MSG_KEY),
295             "86: " + getCheckMessage(MSG_KEY),
296             "87: " + getCheckMessage(MSG_KEY),
297             "88: " + getCheckMessage(MSG_KEY),
298             "89: " + getCheckMessage(MSG_KEY),
299             "98: " + getCheckMessage(MSG_KEY),
300         };
301         verify(checkConfig, getPath("InputAvoidEscapedUnicodeCharacters.java"), expected);
302     }
303 
304     @Test
305     public void allowNonPrintableEscapes() throws Exception {
306         final DefaultConfiguration checkConfig =
307                 createModuleConfig(AvoidEscapedUnicodeCharactersCheck.class);
308         checkConfig.addAttribute("allowNonPrintableEscapes", "true");
309         final String[] expected = {
310             "7: " + getCheckMessage(MSG_KEY),
311             "9: " + getCheckMessage(MSG_KEY),
312             "11: " + getCheckMessage(MSG_KEY),
313             "15: " + getCheckMessage(MSG_KEY),
314             "16: " + getCheckMessage(MSG_KEY),
315             "24: " + getCheckMessage(MSG_KEY),
316             "25: " + getCheckMessage(MSG_KEY),
317             "31: " + getCheckMessage(MSG_KEY),
318             "32: " + getCheckMessage(MSG_KEY),
319             "33: " + getCheckMessage(MSG_KEY),
320             "34: " + getCheckMessage(MSG_KEY),
321             "42: " + getCheckMessage(MSG_KEY),
322             "86: " + getCheckMessage(MSG_KEY),
323             "87: " + getCheckMessage(MSG_KEY),
324             "88: " + getCheckMessage(MSG_KEY),
325             "89: " + getCheckMessage(MSG_KEY),
326             "93: " + getCheckMessage(MSG_KEY),
327             "94: " + getCheckMessage(MSG_KEY),
328             "98: " + getCheckMessage(MSG_KEY),
329             "104: " + getCheckMessage(MSG_KEY),
330         };
331         verify(checkConfig, getPath("InputAvoidEscapedUnicodeCharacters.java"), expected);
332     }
333 
334     @Test
335     public void testGetAcceptableTokens() {
336         final AvoidEscapedUnicodeCharactersCheck check = new AvoidEscapedUnicodeCharactersCheck();
337         final int[] actual = check.getAcceptableTokens();
338         final int[] expected = {TokenTypes.STRING_LITERAL, TokenTypes.CHAR_LITERAL };
339         assertArrayEquals("Acceptable tokens differ from expected",
340                 expected, actual);
341     }
342 
343     @Test
344     public void testAllowEscapesForControlCharacterSetForAllCharacters() throws Exception {
345         final DefaultConfiguration checkConfig =
346                 createModuleConfig(AvoidEscapedUnicodeCharactersCheck.class);
347         checkConfig.addAttribute("allowEscapesForControlCharacters", "true");
348 
349         final int indexOfStartLineInInputFile = 6;
350         final String message = getCheckMessage(MSG_KEY);
351         final String[] expected = IntStream.rangeClosed(0, 0xffff)
352                 .parallel()
353                 .filter(val -> !isControlCharacter(val))
354                 .mapToObj(msg -> indexOfStartLineInInputFile + msg + ": " + message)
355                 .toArray(String[]::new);
356         verify(checkConfig,
357                 getPath("InputAvoidEscapedUnicodeCharactersAllEscapedUnicodeCharacters.java"),
358                 expected);
359     }
360 
361     /**
362      * Method countMatches is used only inside isOnlyUnicodeValidChars method, and when
363      * pitest mutates 316:13 countMatches++ to countMatches-- it makes no difference for
364      * isOnlyUnicodeValidChars method as it applies countMatches to both cases in comparison.
365      * It is possible to kill mutation in countMatches method by changing code in
366      * isOnlyUnicodeValidChars, but it creates new uncoverable mutations and makes code harder
367      * to understand.
368      *
369      * @throws Exception when code tested throws some exception
370      */
371     @Test
372     public void testCountMatches() throws Exception {
373         final Method countMatches = Whitebox.getMethod(AvoidEscapedUnicodeCharactersCheck.class,
374                 "countMatches", Pattern.class, String.class);
375         final AvoidEscapedUnicodeCharactersCheck check = new AvoidEscapedUnicodeCharactersCheck();
376         final int actual = (int) countMatches.invoke(check,
377                 Pattern.compile("\\\\u[a-fA-F0-9]{4}"), "\\u1234");
378         assertEquals("Unexpected matches count", 1, actual);
379     }
380 
381     private static boolean isControlCharacter(final int character) {
382         return Arrays.binarySearch(C0_CONTROL_CHARACTER_INDICES, character) >= 0
383                 || Arrays.binarySearch(C1_CONTROL_CHARACTER_INDICES, character) >= 0
384                 || Arrays.binarySearch(OTHER_CONTROL_CHARACTER_INDICES, character) >= 0;
385     }
386 }