1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.metrics;
21
22 import static com.puppycrawl.tools.checkstyle.checks.metrics.NPathComplexityCheck.MSG_KEY;
23
24 import java.io.File;
25 import java.util.Collection;
26 import java.util.Optional;
27 import java.util.SortedSet;
28
29 import org.junit.Assert;
30 import org.junit.Test;
31
32 import antlr.CommonHiddenStreamToken;
33 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
34 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
35 import com.puppycrawl.tools.checkstyle.JavaParser;
36 import com.puppycrawl.tools.checkstyle.api.Context;
37 import com.puppycrawl.tools.checkstyle.api.DetailAST;
38 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
39 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
40 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
41 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
42
43
44 public class NPathComplexityCheckTest extends AbstractModuleTestSupport {
45
46 @Override
47 protected String getPackageLocation() {
48 return "com/puppycrawl/tools/checkstyle/checks/metrics/npathcomplexity";
49 }
50
51 @Test
52 public void testCalculation() throws Exception {
53 final DefaultConfiguration checkConfig =
54 createModuleConfig(NPathComplexityCheck.class);
55
56 checkConfig.addAttribute("max", "0");
57 final String[] expected = {
58 "5:5: " + getCheckMessage(MSG_KEY, 2, 0),
59 "10:17: " + getCheckMessage(MSG_KEY, 2, 0),
60 "22:5: " + getCheckMessage(MSG_KEY, 10, 0),
61 "35:5: " + getCheckMessage(MSG_KEY, 3, 0),
62 "45:5: " + getCheckMessage(MSG_KEY, 7, 0),
63 "63:5: " + getCheckMessage(MSG_KEY, 3, 0),
64 "76:5: " + getCheckMessage(MSG_KEY, 3, 0),
65 "88:5: " + getCheckMessage(MSG_KEY, 3, 0),
66 "104:13: " + getCheckMessage(MSG_KEY, 2, 0),
67 "113:5: " + getCheckMessage(MSG_KEY, 48, 0),
68 "123:5: " + getCheckMessage(MSG_KEY, 1, 0),
69 "124:5: " + getCheckMessage(MSG_KEY, 1, 0),
70 "130:17: " + getCheckMessage(MSG_KEY, 3, 0),
71 "144:21: " + getCheckMessage(MSG_KEY, 3, 0),
72 };
73
74 verify(checkConfig, getPath("InputNPathComplexityDefault.java"), expected);
75 }
76
77 @Test
78 public void testCalculation2() throws Exception {
79 final DefaultConfiguration checkConfig =
80 createModuleConfig(NPathComplexityCheck.class);
81
82 checkConfig.addAttribute("max", "0");
83 final String[] expected = {
84 "5:5: " + getCheckMessage(MSG_KEY, 5, 0),
85 "11:5: " + getCheckMessage(MSG_KEY, 5, 0),
86 "18:5: " + getCheckMessage(MSG_KEY, 4, 0),
87 "33:5: " + getCheckMessage(MSG_KEY, 4, 0),
88 "49:5: " + getCheckMessage(MSG_KEY, 6, 0),
89 "65:5: " + getCheckMessage(MSG_KEY, 15, 0),
90 "90:5: " + getCheckMessage(MSG_KEY, 11, 0),
91 "100:5: " + getCheckMessage(MSG_KEY, 8, 0),
92 "113:5: " + getCheckMessage(MSG_KEY, 120, 0),
93 "125:5: " + getCheckMessage(MSG_KEY, 6, 0),
94 "135:5: " + getCheckMessage(MSG_KEY, 21, 0),
95 "148:5: " + getCheckMessage(MSG_KEY, 35, 0),
96 "156:5: " + getCheckMessage(MSG_KEY, 25, 0),
97 "171:5: " + getCheckMessage(MSG_KEY, 2, 0),
98 };
99
100 verify(checkConfig, getPath("InputNPathComplexity.java"), expected);
101 }
102
103 @Test
104 public void testCalculation3() throws Exception {
105 final DefaultConfiguration checkConfig =
106 createModuleConfig(NPathComplexityCheck.class);
107
108 checkConfig.addAttribute("max", "0");
109 final String[] expected = {
110 "4:5: " + getCheckMessage(MSG_KEY, 64, 0),
111 };
112
113 verify(checkConfig, getNonCompilablePath("InputNPathComplexityDefault2.java"), expected);
114 }
115
116 @Test
117 public void testIntegerOverflow() throws Exception {
118 final DefaultConfiguration checkConfig =
119 createModuleConfig(NPathComplexityCheck.class);
120
121 checkConfig.addAttribute("max", "0");
122
123 final long largerThanMaxInt = 3_486_784_401L;
124
125 final String[] expected = {
126 "13:5: " + getCheckMessage(MSG_KEY, largerThanMaxInt, 0),
127 };
128
129 verify(checkConfig, getPath("InputNPathComplexityOverflow.java"), expected);
130 }
131
132 @Test
133 @SuppressWarnings("unchecked")
134 public void testStatefulFieldsClearedOnBeginTree1() throws Exception {
135 final DetailAST ast = new DetailAST();
136 ast.setType(TokenTypes.LITERAL_ELSE);
137
138 final NPathComplexityCheck check = new NPathComplexityCheck();
139 Assert.assertTrue("Stateful field is not cleared after beginTree",
140 TestUtil.isStatefulFieldClearedDuringBeginTree(check, ast, "rangeValues",
141 rangeValues -> ((Collection<Context>) rangeValues).isEmpty()));
142 Assert.assertTrue("Stateful field is not cleared after beginTree",
143 TestUtil.isStatefulFieldClearedDuringBeginTree(check, ast, "expressionValues",
144 expressionValues -> ((Collection<Context>) expressionValues).isEmpty()));
145 }
146
147 @Test
148 @SuppressWarnings("unchecked")
149 public void testStatefulFieldsClearedOnBeginTree2() throws Exception {
150 final DetailAST ast = new DetailAST();
151 ast.setType(TokenTypes.LITERAL_RETURN);
152 ast.setLineNo(5);
153 final DetailAST child = new DetailAST();
154 child.setType(TokenTypes.SEMI);
155 ast.addChild(child);
156
157 final NPathComplexityCheck check = new NPathComplexityCheck();
158 Assert.assertTrue("Stateful field is not cleared after beginTree",
159 TestUtil.isStatefulFieldClearedDuringBeginTree(check, ast, "afterValues",
160 isAfterValues -> ((Collection<Context>) isAfterValues).isEmpty()));
161 }
162
163 @Test
164 public void testStatefulFieldsClearedOnBeginTree3() throws Exception {
165 final NPathComplexityCheck check = new NPathComplexityCheck();
166 final Optional<DetailAST> question = TestUtil.findTokenInAstByPredicate(
167 JavaParser.parseFile(new File(getPath("InputNPathComplexity.java")),
168 JavaParser.Options.WITHOUT_COMMENTS),
169 ast -> ast.getType() == TokenTypes.QUESTION);
170
171 Assert.assertTrue("Ast should contain QUESTION", question.isPresent());
172
173 Assert.assertTrue("State is not cleared on beginTree",
174 TestUtil.isStatefulFieldClearedDuringBeginTree(
175 check,
176 question.get(),
177 "processingTokenEnd",
178 processingTokenEnd -> {
179 try {
180 return (Integer) TestUtil.getClassDeclaredField(
181 processingTokenEnd.getClass(), "endLineNo").get(
182 processingTokenEnd) == 0
183 && (Integer) TestUtil.getClassDeclaredField(
184 processingTokenEnd.getClass(), "endColumnNo").get(
185 processingTokenEnd) == 0;
186 }
187 catch (IllegalAccessException | NoSuchFieldException ex) {
188 throw new IllegalStateException(ex);
189 }
190 }));
191 }
192
193 @Test
194 public void testDefaultConfiguration() throws Exception {
195 final DefaultConfiguration checkConfig =
196 createModuleConfig(NPathComplexityCheck.class);
197
198 createChecker(checkConfig);
199 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
200 verify(checkConfig, getPath("InputNPathComplexityDefault.java"), expected);
201 }
202
203 @Test
204 public void testGetAcceptableTokens() {
205 final NPathComplexityCheck npathComplexityCheckObj = new NPathComplexityCheck();
206 final int[] actual = npathComplexityCheckObj.getAcceptableTokens();
207 final int[] expected = {
208 TokenTypes.CTOR_DEF,
209 TokenTypes.METHOD_DEF,
210 TokenTypes.STATIC_INIT,
211 TokenTypes.INSTANCE_INIT,
212 TokenTypes.LITERAL_WHILE,
213 TokenTypes.LITERAL_DO,
214 TokenTypes.LITERAL_FOR,
215 TokenTypes.LITERAL_IF,
216 TokenTypes.LITERAL_ELSE,
217 TokenTypes.LITERAL_SWITCH,
218 TokenTypes.CASE_GROUP,
219 TokenTypes.LITERAL_TRY,
220 TokenTypes.LITERAL_CATCH,
221 TokenTypes.QUESTION,
222 TokenTypes.LITERAL_RETURN,
223 TokenTypes.LITERAL_DEFAULT,
224 };
225 Assert.assertNotNull("Acceptable tokens should not be null", actual);
226 Assert.assertArrayEquals("Invalid acceptable tokens", expected, actual);
227 }
228
229 @Test
230 public void testGetRequiredTokens() {
231 final NPathComplexityCheck npathComplexityCheckObj = new NPathComplexityCheck();
232 final int[] actual = npathComplexityCheckObj.getRequiredTokens();
233 final int[] expected = {
234 TokenTypes.CTOR_DEF,
235 TokenTypes.METHOD_DEF,
236 TokenTypes.STATIC_INIT,
237 TokenTypes.INSTANCE_INIT,
238 TokenTypes.LITERAL_WHILE,
239 TokenTypes.LITERAL_DO,
240 TokenTypes.LITERAL_FOR,
241 TokenTypes.LITERAL_IF,
242 TokenTypes.LITERAL_ELSE,
243 TokenTypes.LITERAL_SWITCH,
244 TokenTypes.CASE_GROUP,
245 TokenTypes.LITERAL_TRY,
246 TokenTypes.LITERAL_CATCH,
247 TokenTypes.QUESTION,
248 TokenTypes.LITERAL_RETURN,
249 TokenTypes.LITERAL_DEFAULT,
250 };
251 Assert.assertNotNull("Required tokens should not be null", actual);
252 Assert.assertArrayEquals("Invalid required tokens", expected, actual);
253 }
254
255 @Test
256 public void testDefaultHooks() {
257 final NPathComplexityCheck npathComplexityCheckObj = new NPathComplexityCheck();
258 final DetailAST ast = new DetailAST();
259 ast.initialize(new CommonHiddenStreamToken(TokenTypes.INTERFACE_DEF, "interface"));
260
261 npathComplexityCheckObj.visitToken(ast);
262 final SortedSet<LocalizedMessage> messages1 = npathComplexityCheckObj.getMessages();
263
264 Assert.assertEquals("No exception messages expected", 0, messages1.size());
265
266 npathComplexityCheckObj.leaveToken(ast);
267 final SortedSet<LocalizedMessage> messages2 = npathComplexityCheckObj.getMessages();
268
269 Assert.assertEquals("No exception messages expected", 0, messages2.size());
270 }
271
272
273
274
275
276
277
278
279
280
281
282
283 @Test
284 public void testTokenEndIsAfterSameLineColumn() throws Exception {
285 final NPathComplexityCheck check = new NPathComplexityCheck();
286 final Object tokenEnd = TestUtil.getClassDeclaredField(NPathComplexityCheck.class,
287 "processingTokenEnd").get(check);
288 final DetailAST token = new DetailAST();
289 token.setLineNo(0);
290 token.setColumnNo(0);
291
292 Assert.assertTrue("isAfter must be true for same line/column",
293 (Boolean) TestUtil.getClassDeclaredMethod(tokenEnd.getClass(), "isAfter")
294 .invoke(tokenEnd, token));
295 }
296
297 @Test
298 public void testVisitTokenBeforeExpressionRange() {
299
300 final DetailAST astIf = mockAST(TokenTypes.LITERAL_IF, "if", "mockfile", 2, 2);
301 final DetailAST astIfLeftParen = mockAST(TokenTypes.LPAREN, "(", "mockfile", 3, 3);
302 astIf.addChild(astIfLeftParen);
303 final DetailAST astIfTrue =
304 mockAST(TokenTypes.LITERAL_TRUE, "true", "mockfile", 3, 3);
305 astIf.addChild(astIfTrue);
306 final DetailAST astIfRightParen = mockAST(TokenTypes.RPAREN, ")", "mockfile", 4, 4);
307 astIf.addChild(astIfRightParen);
308
309 final DetailAST astTernary = mockAST(TokenTypes.QUESTION, "?", "mockfile", 1, 1);
310 final DetailAST astTernaryTrue =
311 mockAST(TokenTypes.LITERAL_TRUE, "true", "mockfile", 1, 2);
312 astTernary.addChild(astTernaryTrue);
313
314 final NPathComplexityCheck npathComplexityCheckObj = new NPathComplexityCheck();
315
316
317 npathComplexityCheckObj.visitToken(astIf);
318 final SortedSet<LocalizedMessage> messages1 = npathComplexityCheckObj.getMessages();
319
320 Assert.assertEquals("No exception messages expected", 0, messages1.size());
321
322
323 npathComplexityCheckObj.visitToken(astTernary);
324 final SortedSet<LocalizedMessage> messages2 = npathComplexityCheckObj.getMessages();
325
326 Assert.assertEquals("No exception messages expected", 0, messages2.size());
327 }
328
329
330
331
332
333
334
335
336
337
338 private static DetailAST mockAST(final int tokenType, final String tokenText,
339 final String tokenFileName, final int tokenRow, final int tokenColumn) {
340 final CommonHiddenStreamToken tokenImportSemi = new CommonHiddenStreamToken();
341 tokenImportSemi.setType(tokenType);
342 tokenImportSemi.setText(tokenText);
343 tokenImportSemi.setLine(tokenRow);
344 tokenImportSemi.setColumn(tokenColumn);
345 tokenImportSemi.setFilename(tokenFileName);
346 final DetailAST astSemi = new DetailAST();
347 astSemi.initialize(tokenImportSemi);
348 return astSemi;
349 }
350
351 }