]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/linter/code-path-analysis/code-path-analyzer.js
6460ba1b67682f12c08ff02a9795feb25b8dbfac
2 * @fileoverview Tests for CodePathAnalyzer.
3 * @author Toru Nagashima
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const assert
= require("assert"),
14 path
= require("path"),
15 { Linter
} = require("../../../../lib/linter"),
16 EventGeneratorTester
= require("../../../../tools/internal-testers/event-generator-tester"),
17 createEmitter
= require("../../../../lib/linter/safe-emitter"),
18 debug
= require("../../../../lib/linter/code-path-analysis/debug-helpers"),
19 CodePath
= require("../../../../lib/linter/code-path-analysis/code-path"),
20 CodePathAnalyzer
= require("../../../../lib/linter/code-path-analysis/code-path-analyzer"),
21 CodePathSegment
= require("../../../../lib/linter/code-path-analysis/code-path-segment"),
22 NodeEventGenerator
= require("../../../../lib/linter/node-event-generator");
24 //------------------------------------------------------------------------------
26 //------------------------------------------------------------------------------
28 const expectedPattern
= /\/\*expected\s+((?:.|[\r\n])+?)\s*\*\//gu;
29 const lineEndingPattern
= /\r?\n/gu;
30 const linter
= new Linter();
33 * Extracts the content of `/*expected` comments from a given source code.
34 * It's expected DOT arrows.
35 * @param {string} source A source code text.
36 * @returns {string[]} DOT arrows.
38 function getExpectedDotArrows(source
) {
39 expectedPattern
.lastIndex
= 0;
44 while ((m
= expectedPattern
.exec(source
)) !== null) {
45 retv
.push(m
[1].replace(lineEndingPattern
, "\n"));
51 //------------------------------------------------------------------------------
53 //------------------------------------------------------------------------------
55 describe("CodePathAnalyzer", () => {
56 EventGeneratorTester
.testEventGeneratorInterface(
57 new CodePathAnalyzer(new NodeEventGenerator(createEmitter()))
60 describe("interface of code paths", () => {
65 linter
.defineRule("test", () => ({
66 onCodePathStart(codePath
) {
67 actual
.push(codePath
);
71 "function foo(a) { if (a) return 0; else throw new Error(); }",
72 { rules
: { test
: 2 } }
76 it("should have `id` as unique string", () => {
77 assert(typeof actual
[0].id
=== "string");
78 assert(typeof actual
[1].id
=== "string");
79 assert(actual
[0].id
!== actual
[1].id
);
82 it("should have `upper` as CodePath", () => {
83 assert(actual
[0].upper
=== null);
84 assert(actual
[1].upper
=== actual
[0]);
87 it("should have `childCodePaths` as CodePath[]", () => {
88 assert(Array
.isArray(actual
[0].childCodePaths
));
89 assert(Array
.isArray(actual
[1].childCodePaths
));
90 assert(actual
[0].childCodePaths
.length
=== 1);
91 assert(actual
[1].childCodePaths
.length
=== 0);
92 assert(actual
[0].childCodePaths
[0] === actual
[1]);
95 it("should have `initialSegment` as CodePathSegment", () => {
96 assert(actual
[0].initialSegment
instanceof CodePathSegment
);
97 assert(actual
[1].initialSegment
instanceof CodePathSegment
);
98 assert(actual
[0].initialSegment
.prevSegments
.length
=== 0);
99 assert(actual
[1].initialSegment
.prevSegments
.length
=== 0);
102 it("should have `finalSegments` as CodePathSegment[]", () => {
103 assert(Array
.isArray(actual
[0].finalSegments
));
104 assert(Array
.isArray(actual
[1].finalSegments
));
105 assert(actual
[0].finalSegments
.length
=== 1);
106 assert(actual
[1].finalSegments
.length
=== 2);
107 assert(actual
[0].finalSegments
[0].nextSegments
.length
=== 0);
108 assert(actual
[1].finalSegments
[0].nextSegments
.length
=== 0);
109 assert(actual
[1].finalSegments
[1].nextSegments
.length
=== 0);
111 // finalSegments should include returnedSegments and thrownSegments.
112 assert(actual
[0].finalSegments
[0] === actual
[0].returnedSegments
[0]);
113 assert(actual
[1].finalSegments
[0] === actual
[1].returnedSegments
[0]);
114 assert(actual
[1].finalSegments
[1] === actual
[1].thrownSegments
[0]);
117 it("should have `returnedSegments` as CodePathSegment[]", () => {
118 assert(Array
.isArray(actual
[0].returnedSegments
));
119 assert(Array
.isArray(actual
[1].returnedSegments
));
120 assert(actual
[0].returnedSegments
.length
=== 1);
121 assert(actual
[1].returnedSegments
.length
=== 1);
122 assert(actual
[0].returnedSegments
[0] instanceof CodePathSegment
);
123 assert(actual
[1].returnedSegments
[0] instanceof CodePathSegment
);
126 it("should have `thrownSegments` as CodePathSegment[]", () => {
127 assert(Array
.isArray(actual
[0].thrownSegments
));
128 assert(Array
.isArray(actual
[1].thrownSegments
));
129 assert(actual
[0].thrownSegments
.length
=== 0);
130 assert(actual
[1].thrownSegments
.length
=== 1);
131 assert(actual
[1].thrownSegments
[0] instanceof CodePathSegment
);
134 it("should have `currentSegments` as CodePathSegment[]", () => {
135 assert(Array
.isArray(actual
[0].currentSegments
));
136 assert(Array
.isArray(actual
[1].currentSegments
));
137 assert(actual
[0].currentSegments
.length
=== 0);
138 assert(actual
[1].currentSegments
.length
=== 0);
140 // there is the current segment in progress.
141 linter
.defineRule("test", () => {
145 onCodePathStart(cp
) {
149 assert(codePath
.currentSegments
.length
=== 1);
150 assert(codePath
.currentSegments
[0] instanceof CodePathSegment
);
153 assert(codePath
.currentSegments
.length
=== 1);
154 assert(codePath
.currentSegments
[0] instanceof CodePathSegment
);
159 "function foo(a) { if (a) return 0; else throw new Error(); }",
160 { rules
: { test
: 2 } }
165 describe("interface of code path segments", () => {
170 linter
.defineRule("test", () => ({
171 onCodePathSegmentStart(segment
) {
172 actual
.push(segment
);
176 "function foo(a) { if (a) return 0; else throw new Error(); }",
177 { rules
: { test
: 2 } }
181 it("should have `id` as unique string", () => {
182 assert(typeof actual
[0].id
=== "string");
183 assert(typeof actual
[1].id
=== "string");
184 assert(typeof actual
[2].id
=== "string");
185 assert(typeof actual
[3].id
=== "string");
186 assert(actual
[0].id
!== actual
[1].id
);
187 assert(actual
[0].id
!== actual
[2].id
);
188 assert(actual
[0].id
!== actual
[3].id
);
189 assert(actual
[1].id
!== actual
[2].id
);
190 assert(actual
[1].id
!== actual
[3].id
);
191 assert(actual
[2].id
!== actual
[3].id
);
194 it("should have `nextSegments` as CodePathSegment[]", () => {
195 assert(Array
.isArray(actual
[0].nextSegments
));
196 assert(Array
.isArray(actual
[1].nextSegments
));
197 assert(Array
.isArray(actual
[2].nextSegments
));
198 assert(Array
.isArray(actual
[3].nextSegments
));
199 assert(actual
[0].nextSegments
.length
=== 0);
200 assert(actual
[1].nextSegments
.length
=== 2);
201 assert(actual
[2].nextSegments
.length
=== 0);
202 assert(actual
[3].nextSegments
.length
=== 0);
203 assert(actual
[1].nextSegments
[0] === actual
[2]);
204 assert(actual
[1].nextSegments
[1] === actual
[3]);
207 it("should have `allNextSegments` as CodePathSegment[]", () => {
208 assert(Array
.isArray(actual
[0].allNextSegments
));
209 assert(Array
.isArray(actual
[1].allNextSegments
));
210 assert(Array
.isArray(actual
[2].allNextSegments
));
211 assert(Array
.isArray(actual
[3].allNextSegments
));
212 assert(actual
[0].allNextSegments
.length
=== 0);
213 assert(actual
[1].allNextSegments
.length
=== 2);
214 assert(actual
[2].allNextSegments
.length
=== 1);
215 assert(actual
[3].allNextSegments
.length
=== 1);
216 assert(actual
[2].allNextSegments
[0].reachable
=== false);
217 assert(actual
[3].allNextSegments
[0].reachable
=== false);
220 it("should have `prevSegments` as CodePathSegment[]", () => {
221 assert(Array
.isArray(actual
[0].prevSegments
));
222 assert(Array
.isArray(actual
[1].prevSegments
));
223 assert(Array
.isArray(actual
[2].prevSegments
));
224 assert(Array
.isArray(actual
[3].prevSegments
));
225 assert(actual
[0].prevSegments
.length
=== 0);
226 assert(actual
[1].prevSegments
.length
=== 0);
227 assert(actual
[2].prevSegments
.length
=== 1);
228 assert(actual
[3].prevSegments
.length
=== 1);
229 assert(actual
[2].prevSegments
[0] === actual
[1]);
230 assert(actual
[3].prevSegments
[0] === actual
[1]);
233 it("should have `allPrevSegments` as CodePathSegment[]", () => {
234 assert(Array
.isArray(actual
[0].allPrevSegments
));
235 assert(Array
.isArray(actual
[1].allPrevSegments
));
236 assert(Array
.isArray(actual
[2].allPrevSegments
));
237 assert(Array
.isArray(actual
[3].allPrevSegments
));
238 assert(actual
[0].allPrevSegments
.length
=== 0);
239 assert(actual
[1].allPrevSegments
.length
=== 0);
240 assert(actual
[2].allPrevSegments
.length
=== 1);
241 assert(actual
[3].allPrevSegments
.length
=== 1);
244 it("should have `reachable` as boolean", () => {
245 assert(actual
[0].reachable
=== true);
246 assert(actual
[1].reachable
=== true);
247 assert(actual
[2].reachable
=== true);
248 assert(actual
[3].reachable
=== true);
252 describe("onCodePathStart", () => {
253 it("should be fired at the head of programs/functions", () => {
255 let lastCodePathNodeType
= null;
257 linter
.defineRule("test", () => ({
258 onCodePathStart(cp
, node
) {
260 lastCodePathNodeType
= node
.type
;
262 assert(cp
instanceof CodePath
);
264 assert(node
.type
=== "Program");
265 } else if (count
=== 2) {
266 assert(node
.type
=== "FunctionDeclaration");
267 } else if (count
=== 3) {
268 assert(node
.type
=== "FunctionExpression");
269 } else if (count
=== 4) {
270 assert(node
.type
=== "ArrowFunctionExpression");
274 assert(lastCodePathNodeType
=== "Program");
276 FunctionDeclaration() {
277 assert(lastCodePathNodeType
=== "FunctionDeclaration");
279 FunctionExpression() {
280 assert(lastCodePathNodeType
=== "FunctionExpression");
282 ArrowFunctionExpression() {
283 assert(lastCodePathNodeType
=== "ArrowFunctionExpression");
287 "foo(); function foo() {} var foo = function() {}; var foo = () => {};",
288 { rules
: { test
: 2 }, env
: { es6
: true } }
295 describe("onCodePathEnd", () => {
296 it("should be fired at the end of programs/functions", () => {
298 let lastNodeType
= null;
300 linter
.defineRule("test", () => ({
301 onCodePathEnd(cp
, node
) {
304 assert(cp
instanceof CodePath
);
306 assert(node
.type
=== "Program");
307 } else if (count
=== 1) {
308 assert(node
.type
=== "FunctionDeclaration");
309 } else if (count
=== 2) {
310 assert(node
.type
=== "FunctionExpression");
311 } else if (count
=== 3) {
312 assert(node
.type
=== "ArrowFunctionExpression");
314 assert(node
.type
=== lastNodeType
);
317 lastNodeType
= "Program";
319 "FunctionDeclaration:exit"() {
320 lastNodeType
= "FunctionDeclaration";
322 "FunctionExpression:exit"() {
323 lastNodeType
= "FunctionExpression";
325 "ArrowFunctionExpression:exit"() {
326 lastNodeType
= "ArrowFunctionExpression";
330 "foo(); function foo() {} var foo = function() {}; var foo = () => {};",
331 { rules
: { test
: 2 }, env
: { es6
: true } }
338 describe("onCodePathSegmentStart", () => {
339 it("should be fired at the head of programs/functions for the initial segment", () => {
341 let lastCodePathNodeType
= null;
343 linter
.defineRule("test", () => ({
344 onCodePathSegmentStart(segment
, node
) {
346 lastCodePathNodeType
= node
.type
;
348 assert(segment
instanceof CodePathSegment
);
350 assert(node
.type
=== "Program");
351 } else if (count
=== 2) {
352 assert(node
.type
=== "FunctionDeclaration");
353 } else if (count
=== 3) {
354 assert(node
.type
=== "FunctionExpression");
355 } else if (count
=== 4) {
356 assert(node
.type
=== "ArrowFunctionExpression");
360 assert(lastCodePathNodeType
=== "Program");
362 FunctionDeclaration() {
363 assert(lastCodePathNodeType
=== "FunctionDeclaration");
365 FunctionExpression() {
366 assert(lastCodePathNodeType
=== "FunctionExpression");
368 ArrowFunctionExpression() {
369 assert(lastCodePathNodeType
=== "ArrowFunctionExpression");
373 "foo(); function foo() {} var foo = function() {}; var foo = () => {};",
374 { rules
: { test
: 2 }, env
: { es6
: true } }
381 describe("onCodePathSegmentEnd", () => {
382 it("should be fired at the end of programs/functions for the final segment", () => {
384 let lastNodeType
= null;
386 linter
.defineRule("test", () => ({
387 onCodePathSegmentEnd(cp
, node
) {
390 assert(cp
instanceof CodePathSegment
);
392 assert(node
.type
=== "Program");
393 } else if (count
=== 1) {
394 assert(node
.type
=== "FunctionDeclaration");
395 } else if (count
=== 2) {
396 assert(node
.type
=== "FunctionExpression");
397 } else if (count
=== 3) {
398 assert(node
.type
=== "ArrowFunctionExpression");
400 assert(node
.type
=== lastNodeType
);
403 lastNodeType
= "Program";
405 "FunctionDeclaration:exit"() {
406 lastNodeType
= "FunctionDeclaration";
408 "FunctionExpression:exit"() {
409 lastNodeType
= "FunctionExpression";
411 "ArrowFunctionExpression:exit"() {
412 lastNodeType
= "ArrowFunctionExpression";
416 "foo(); function foo() {} var foo = function() {}; var foo = () => {};",
417 { rules
: { test
: 2 }, env
: { es6
: true } }
424 describe("onCodePathSegmentLoop", () => {
425 it("should be fired in `while` loops", () => {
428 linter
.defineRule("test", () => ({
429 onCodePathSegmentLoop(fromSegment
, toSegment
, node
) {
431 assert(fromSegment
instanceof CodePathSegment
);
432 assert(toSegment
instanceof CodePathSegment
);
433 assert(node
.type
=== "WhileStatement");
437 "while (a) { foo(); }",
438 { rules
: { test
: 2 } }
444 it("should be fired in `do-while` loops", () => {
447 linter
.defineRule("test", () => ({
448 onCodePathSegmentLoop(fromSegment
, toSegment
, node
) {
450 assert(fromSegment
instanceof CodePathSegment
);
451 assert(toSegment
instanceof CodePathSegment
);
452 assert(node
.type
=== "DoWhileStatement");
456 "do { foo(); } while (a);",
457 { rules
: { test
: 2 } }
463 it("should be fired in `for` loops", () => {
466 linter
.defineRule("test", () => ({
467 onCodePathSegmentLoop(fromSegment
, toSegment
, node
) {
469 assert(fromSegment
instanceof CodePathSegment
);
470 assert(toSegment
instanceof CodePathSegment
);
474 // connect path: "update" -> "test"
475 assert(node
.parent
.type
=== "ForStatement");
476 } else if (count
=== 2) {
477 assert(node
.type
=== "ForStatement");
482 "for (var i = 0; i < 10; ++i) { foo(); }",
483 { rules
: { test
: 2 } }
489 it("should be fired in `for-in` loops", () => {
492 linter
.defineRule("test", () => ({
493 onCodePathSegmentLoop(fromSegment
, toSegment
, node
) {
495 assert(fromSegment
instanceof CodePathSegment
);
496 assert(toSegment
instanceof CodePathSegment
);
500 // connect path: "right" -> "left"
501 assert(node
.parent
.type
=== "ForInStatement");
502 } else if (count
=== 2) {
503 assert(node
.type
=== "ForInStatement");
508 "for (var k in obj) { foo(); }",
509 { rules
: { test
: 2 } }
515 it("should be fired in `for-of` loops", () => {
518 linter
.defineRule("test", () => ({
519 onCodePathSegmentLoop(fromSegment
, toSegment
, node
) {
521 assert(fromSegment
instanceof CodePathSegment
);
522 assert(toSegment
instanceof CodePathSegment
);
526 // connect path: "right" -> "left"
527 assert(node
.parent
.type
=== "ForOfStatement");
528 } else if (count
=== 2) {
529 assert(node
.type
=== "ForOfStatement");
534 "for (var x of xs) { foo(); }",
535 { rules
: { test
: 2 }, env
: { es6
: true } }
542 describe("completed code paths are correct", () => {
543 const testDataDir
= path
.join(__dirname
, "../../../fixtures/code-path-analysis/");
544 const testDataFiles
= fs
.readdirSync(testDataDir
);
546 testDataFiles
.forEach(file
=> {
548 const source
= fs
.readFileSync(path
.join(testDataDir
, file
), { encoding
: "utf8" });
549 const expected
= getExpectedDotArrows(source
);
552 assert(expected
.length
> 0, "/*expected */ comments not found.");
554 linter
.defineRule("test", () => ({
555 onCodePathEnd(codePath
) {
556 actual
.push(debug
.makeDotArrows(codePath
));
559 const messages
= linter
.verify(source
, { rules
: { test
: 2 }, env
: { es6
: true } });
561 assert
.strictEqual(messages
.length
, 0);
562 assert
.strictEqual(actual
.length
, expected
.length
, "a count of code paths is wrong.");
564 for (let i
= 0; i
< actual
.length
; ++i
) {
565 assert
.strictEqual(actual
[i
], expected
[i
]);