]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Tests for CodePath. | |
3 | * @author Toru Nagashima | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | //------------------------------------------------------------------------------ | |
9 | // Requirements | |
10 | //------------------------------------------------------------------------------ | |
11 | ||
12 | const assert = require("assert"), | |
13 | { Linter } = require("../../../../lib/linter"); | |
14 | const linter = new Linter(); | |
15 | ||
16 | //------------------------------------------------------------------------------ | |
17 | // Helpers | |
18 | //------------------------------------------------------------------------------ | |
19 | ||
20 | /** | |
21 | * Gets the code path of a given source code. | |
22 | * @param {string} code A source code. | |
23 | * @returns {CodePath[]} A list of created code paths. | |
24 | */ | |
25 | function parseCodePaths(code) { | |
26 | const retv = []; | |
27 | ||
28 | linter.defineRule("test", () => ({ | |
29 | onCodePathStart(codePath) { | |
30 | retv.push(codePath); | |
31 | } | |
32 | })); | |
33 | linter.verify(code, { rules: { test: 2 } }); | |
34 | ||
35 | return retv; | |
36 | } | |
37 | ||
38 | /** | |
39 | * Traverses a given code path then returns the order of traversing. | |
40 | * @param {CodePath} codePath A code path to traverse. | |
41 | * @param {Object|undefined} [options] The option object of | |
42 | * `codePath.traverseSegments()` method. | |
43 | * @param {Function|undefined} [callback] The callback function of | |
44 | * `codePath.traverseSegments()` method. | |
45 | * @returns {string[]} The list of segment's ids in the order traversed. | |
46 | */ | |
47 | function getOrderOfTraversing(codePath, options, callback) { | |
48 | const retv = []; | |
49 | ||
50 | codePath.traverseSegments(options, (segment, controller) => { | |
51 | retv.push(segment.id); | |
52 | if (callback) { | |
53 | callback(segment, controller); // eslint-disable-line callback-return | |
54 | } | |
55 | }); | |
56 | ||
57 | return retv; | |
58 | } | |
59 | ||
60 | //------------------------------------------------------------------------------ | |
61 | // Tests | |
62 | //------------------------------------------------------------------------------ | |
63 | ||
64 | describe("CodePathAnalyzer", () => { | |
65 | describe(".traverseSegments()", () => { | |
66 | describe("should traverse segments from the first to the end:", () => { | |
67 | /* eslint-disable internal-rules/multiline-comment-style */ | |
68 | it("simple", () => { | |
69 | const codePath = parseCodePaths("foo(); bar(); baz();")[0]; | |
70 | const order = getOrderOfTraversing(codePath); | |
71 | ||
72 | assert.deepStrictEqual(order, ["s1_1"]); | |
73 | ||
74 | /* | |
75 | digraph { | |
76 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
77 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
78 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
79 | s1_1[label="Program\nExpressionStatement\nCallExpression\nIdentifier (foo)\nExpressionStatement\nCallExpression\nIdentifier (bar)\nExpressionStatement\nCallExpression\nIdentifier (baz)"]; | |
80 | initial->s1_1->final; | |
81 | } | |
82 | */ | |
83 | }); | |
84 | ||
85 | it("if", () => { | |
86 | const codePath = parseCodePaths("if (a) foo(); else bar(); baz();")[0]; | |
87 | const order = getOrderOfTraversing(codePath); | |
88 | ||
89 | assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4"]); | |
90 | ||
91 | /* | |
92 | digraph { | |
93 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
94 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
95 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
96 | s1_1[label="Program\nIfStatement\nIdentifier (a)"]; | |
97 | s1_2[label="ExpressionStatement\nCallExpression\nIdentifier (foo)"]; | |
98 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (baz)"]; | |
99 | s1_3[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
100 | initial->s1_1->s1_2->s1_4; | |
101 | s1_1->s1_3->s1_4->final; | |
102 | } | |
103 | */ | |
104 | }); | |
105 | ||
106 | it("switch", () => { | |
107 | const codePath = parseCodePaths("switch (a) { case 0: foo(); break; case 1: bar(); } baz();")[0]; | |
108 | const order = getOrderOfTraversing(codePath); | |
109 | ||
110 | assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_4", "s1_5", "s1_6"]); | |
111 | ||
112 | /* | |
113 | digraph { | |
114 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
115 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
116 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
117 | s1_1[label="Program\nSwitchStatement\nIdentifier (a)\nSwitchCase\nLiteral (0)"]; | |
118 | s1_2[label="ExpressionStatement\nCallExpression\nIdentifier (foo)\nBreakStatement"]; | |
119 | s1_3[style="rounded,dashed,filled",fillcolor="#FF9800",label="<<unreachable>>\nSwitchCase:exit"]; | |
120 | s1_5[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
121 | s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (baz)"]; | |
122 | s1_4[label="SwitchCase\nLiteral (1)"]; | |
123 | initial->s1_1->s1_2->s1_3->s1_5->s1_6; | |
124 | s1_1->s1_4->s1_5; | |
125 | s1_2->s1_6; | |
126 | s1_4->s1_6->final; | |
127 | } | |
128 | */ | |
129 | }); | |
130 | ||
131 | it("while", () => { | |
132 | const codePath = parseCodePaths("while (a) foo(); bar();")[0]; | |
133 | const order = getOrderOfTraversing(codePath); | |
134 | ||
135 | assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4"]); | |
136 | ||
137 | /* | |
138 | digraph { | |
139 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
140 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
141 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
142 | s1_1[label="Program\nWhileStatement"]; | |
143 | s1_2[label="Identifier (a)"]; | |
144 | s1_3[label="ExpressionStatement\nCallExpression\nIdentifier (foo)"]; | |
145 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
146 | initial->s1_1->s1_2->s1_3->s1_2->s1_4->final; | |
147 | } | |
148 | */ | |
149 | }); | |
150 | ||
151 | it("for", () => { | |
152 | const codePath = parseCodePaths("for (var i = 0; i < 10; ++i) foo(i); bar();")[0]; | |
153 | const order = getOrderOfTraversing(codePath); | |
154 | ||
155 | assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4", "s1_5"]); | |
156 | ||
157 | /* | |
158 | digraph { | |
159 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
160 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
161 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
162 | s1_1[label="Program\nForStatement\nVariableDeclaration\nVariableDeclarator\nIdentifier (i)\nLiteral (0)"]; | |
163 | s1_2[label="BinaryExpression\nIdentifier (i)\nLiteral (10)"]; | |
164 | s1_3[label="ExpressionStatement\nCallExpression\nIdentifier (foo)\nIdentifier (i)"]; | |
165 | s1_4[label="UpdateExpression\nIdentifier (i)"]; | |
166 | s1_5[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
167 | initial->s1_1->s1_2->s1_3->s1_4->s1_2->s1_5->final; | |
168 | } | |
169 | */ | |
170 | }); | |
171 | ||
172 | it("for-in", () => { | |
173 | const codePath = parseCodePaths("for (var key in obj) foo(key); bar();")[0]; | |
174 | const order = getOrderOfTraversing(codePath); | |
175 | ||
176 | assert.deepStrictEqual(order, ["s1_1", "s1_3", "s1_2", "s1_4", "s1_5"]); | |
177 | ||
178 | /* | |
179 | digraph { | |
180 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
181 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
182 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
183 | s1_1[label="Program\nForInStatement"]; | |
184 | s1_3[label="Identifier (obj)"]; | |
185 | s1_2[label="VariableDeclaration\nVariableDeclarator\nIdentifier (key)"]; | |
186 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (foo)\nIdentifier (key)"]; | |
187 | s1_5[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
188 | initial->s1_1->s1_3->s1_2->s1_4->s1_2; | |
189 | s1_3->s1_5; | |
190 | s1_4->s1_5->final; | |
191 | } | |
192 | */ | |
193 | }); | |
194 | ||
195 | it("try-catch", () => { | |
196 | const codePath = parseCodePaths("try { foo(); } catch (e) { bar(); } baz();")[0]; | |
197 | const order = getOrderOfTraversing(codePath); | |
198 | ||
199 | assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_3", "s1_4"]); | |
200 | ||
201 | /* | |
202 | digraph { | |
203 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
204 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
205 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
206 | s1_1[label="Program\nTryStatement\nBlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"]; | |
207 | s1_2[label="CallExpression:exit\nExpressionStatement:exit\nBlockStatement:exit"]; | |
208 | s1_3[label="CatchClause\nIdentifier (e)\nBlockStatement\nExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
209 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (baz)"]; | |
210 | initial->s1_1->s1_2->s1_3->s1_4; | |
211 | s1_1->s1_3; | |
212 | s1_2->s1_4->final; | |
213 | } | |
214 | */ | |
215 | }); | |
216 | }); | |
217 | ||
218 | it("should traverse segments from `options.first` to `options.last`.", () => { | |
219 | const codePath = parseCodePaths("if (a) { if (b) { foo(); } bar(); } else { out1(); } out2();")[0]; | |
220 | const order = getOrderOfTraversing(codePath, { | |
221 | first: codePath.initialSegment.nextSegments[0], | |
222 | last: codePath.initialSegment.nextSegments[0].nextSegments[1] | |
223 | }); | |
224 | ||
225 | assert.deepStrictEqual(order, ["s1_2", "s1_3", "s1_4"]); | |
226 | ||
227 | /* | |
228 | digraph { | |
229 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
230 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
231 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
232 | s1_1[label="Program\nIfStatement\nIdentifier (a)"]; | |
233 | s1_2[label="BlockStatement\nIfStatement\nIdentifier (b)"]; | |
234 | s1_3[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"]; | |
235 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
236 | s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (out2)"]; | |
237 | s1_5[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (out1)"]; | |
238 | initial->s1_1->s1_2->s1_3->s1_4->s1_6; | |
239 | s1_1->s1_5->s1_6; | |
240 | s1_2->s1_4; | |
241 | s1_6->final; | |
242 | } | |
243 | */ | |
244 | }); | |
245 | ||
246 | it("should stop immediately when 'controller.break()' was called.", () => { | |
247 | const codePath = parseCodePaths("if (a) { if (b) { foo(); } bar(); } else { out1(); } out2();")[0]; | |
248 | const order = getOrderOfTraversing(codePath, null, (segment, controller) => { | |
249 | if (segment.id === "s1_2") { | |
250 | controller.break(); | |
251 | } | |
252 | }); | |
253 | ||
254 | assert.deepStrictEqual(order, ["s1_1", "s1_2"]); | |
255 | ||
256 | /* | |
257 | digraph { | |
258 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
259 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
260 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
261 | s1_1[label="Program\nIfStatement\nIdentifier (a)"]; | |
262 | s1_2[label="BlockStatement\nIfStatement\nIdentifier (b)"]; | |
263 | s1_3[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"]; | |
264 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
265 | s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (out2)"]; | |
266 | s1_5[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (out1)"]; | |
267 | initial->s1_1->s1_2->s1_3->s1_4->s1_6; | |
268 | s1_1->s1_5->s1_6; | |
269 | s1_2->s1_4; | |
270 | s1_6->final; | |
271 | } | |
272 | */ | |
273 | }); | |
274 | ||
275 | it("should skip the current branch when 'controller.skip()' was called.", () => { | |
276 | const codePath = parseCodePaths("if (a) { if (b) { foo(); } bar(); } else { out1(); } out2();")[0]; | |
277 | const order = getOrderOfTraversing(codePath, null, (segment, controller) => { | |
278 | if (segment.id === "s1_2") { | |
279 | controller.skip(); | |
280 | } | |
281 | }); | |
282 | ||
283 | assert.deepStrictEqual(order, ["s1_1", "s1_2", "s1_5", "s1_6"]); | |
284 | ||
285 | /* | |
286 | digraph { | |
287 | node[shape=box,style="rounded,filled",fillcolor=white]; | |
288 | initial[label="",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
289 | final[label="",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25]; | |
290 | s1_1[label="Program\nIfStatement\nIdentifier (a)"]; | |
291 | s1_2[label="BlockStatement\nIfStatement\nIdentifier (b)"]; | |
292 | s1_3[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (foo)"]; | |
293 | s1_4[label="ExpressionStatement\nCallExpression\nIdentifier (bar)"]; | |
294 | s1_6[label="ExpressionStatement\nCallExpression\nIdentifier (out2)"]; | |
295 | s1_5[label="BlockStatement\nExpressionStatement\nCallExpression\nIdentifier (out1)"]; | |
296 | initial->s1_1->s1_2->s1_3->s1_4->s1_6; | |
297 | s1_1->s1_5->s1_6; | |
298 | s1_2->s1_4; | |
299 | s1_6->final; | |
300 | } | |
301 | */ | |
302 | }); | |
303 | ||
304 | /* eslint-enable internal-rules/multiline-comment-style */ | |
305 | }); | |
306 | }); |