From 8beed5095a3dd43264215fbd8961f1a988825b82 Mon Sep 17 00:00:00 2001 From: meowbmw Date: Wed, 12 Feb 2025 17:34:53 +0800 Subject: [PATCH 1/4] Implement uncovered branch highlight --- assembly/diytest.ts | 24 ++++++++++++++ src/generator/html-generator/genCode.ts | 14 ++++++--- src/interface.ts | 3 ++ src/parser/singleFileAnalysis.ts | 6 ++-- src/parser/singleFunctionAnalysis.ts | 17 ++++++++++ tests-as/diytest.test.ts | 31 +++++++++++++++++++ .../parser/__snapshots__/parser.test.ts.snap | 13 ++++++++ .../test/parser/singleFileAnalysis.test.ts | 9 ++++-- 8 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 assembly/diytest.ts create mode 100644 tests-as/diytest.test.ts diff --git a/assembly/diytest.ts b/assembly/diytest.ts new file mode 100644 index 0000000..9756be3 --- /dev/null +++ b/assembly/diytest.ts @@ -0,0 +1,24 @@ +export function mycmp(a: T, b: T): bool { + if (a > b) { + return true; + } else { + return false; + } +} + +export function instancecmp(a: T, b: T): bool { + if (isInteger()) { + if (a > b) { + return true; + } else { + return false; + } + } else if (isFloat()) { + if (a > b) { + return true; + } else { + return false; + } + } + return false; +} diff --git a/src/generator/html-generator/genCode.ts b/src/generator/html-generator/genCode.ts index 983068f..3d32201 100644 --- a/src/generator/html-generator/genCode.ts +++ b/src/generator/html-generator/genCode.ts @@ -23,10 +23,16 @@ function generateLineCoverage(codes: CodeCoverage[]): string { return str.join("\n"); } -function generateSource(codes: CodeCoverage[]): string { +function generateSource(codes: CodeCoverage[], result: FileCoverageResult): string { const str: string[] = []; - for (const code of codes) { - str.push(escape(code.source)); + for (const [index, code] of codes.entries()) { + if (result.linesToHighlight.has(index + 1)) { + // IMPORTANT! to add "nocode" here to preventing prettify from adding unwanted pln class + str.push('!' + escape(code.source)); + } + else { + str.push(escape(code.source)); + } } return str.join("\n"); } @@ -36,7 +42,7 @@ export function generateCodeHtml(relativePathofRoot: string, result: FileCoverag const lineCoutHtml = generateLineCount(codes.length); const lineCov = generateLineCoverage(codes); - const lineSource = generateSource(codes); + const lineSource = generateSource(codes, result); return ` diff --git a/src/interface.ts b/src/interface.ts index 15921fd..4fc4cc6 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -87,11 +87,13 @@ export class FileCoverageResult { functionCoverageRate: Rate = new Rate(); lineCoverageRate: Rate = new Rate(); sourceUsedCount: CodeCoverage[] = []; + linesToHighlight: Set = new Set(); } export class FunctionCoverageResult { constructor(public functionName: string) {} branchCoverageRate: Rate = new Rate(); + linesToHighlight: Set = new Set(); lineRange: [number, number] = [Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER]; /** * first means lineIndex; @@ -111,6 +113,7 @@ export class FunctionCoverageResult { ]; result.branchCoverageRate = Rate.summarize(infos.map((info) => info.branchCoverageRate)); for (const info of infos) { + info.linesToHighlight.forEach(line => result.linesToHighlight.add(line)); for (const [lineIndex, count] of info.sourceUsedCount.entries()) { const srcLineUsedCount = result.sourceUsedCount.get(lineIndex); result.sourceUsedCount.set(lineIndex, srcLineUsedCount === undefined ? count : srcLineUsedCount + count); diff --git a/src/parser/singleFileAnalysis.ts b/src/parser/singleFileAnalysis.ts index 6990f89..a2b38f1 100644 --- a/src/parser/singleFileAnalysis.ts +++ b/src/parser/singleFileAnalysis.ts @@ -19,7 +19,7 @@ export class SingleFileCoverageAnalysis { for (let index = startLine - 1; index < endLine; index++) { const codeCoverage = this.result.sourceUsedCount[index]; if (codeCoverage === undefined) { - throw new Error(`unknowm error: There is no ${index} Line in file ${this.result.filename}`); + throw new Error(`unknown error: There is no ${index} Line in file ${this.result.filename}`); } codeCoverage.usedCount = 0; } @@ -27,13 +27,15 @@ export class SingleFileCoverageAnalysis { } merge(results: FunctionCoverageResult[]) { + // SingleFileCoverageAnalysis contains FileCoverageResult if (results.length === 0) return; for (const functionCovResult of results) { + functionCovResult.linesToHighlight.forEach(line => this.result.linesToHighlight.add(line)); for (const [lineIndex, count] of functionCovResult.sourceUsedCount.entries()) { const srcLineUsedCount = this.result.sourceUsedCount[lineIndex - 1]; if (srcLineUsedCount === undefined) { throw new Error( - `unknowm error: There is not Line ${lineIndex} in ${JSON.stringify(this.result.sourceUsedCount)}` + `unknown error: There is not Line ${lineIndex} in ${JSON.stringify(this.result.sourceUsedCount)}` ); } if (srcLineUsedCount.usedCount === CodeCoverage.default) { diff --git a/src/parser/singleFunctionAnalysis.ts b/src/parser/singleFunctionAnalysis.ts index e850a93..48f68b3 100644 --- a/src/parser/singleFunctionAnalysis.ts +++ b/src/parser/singleFunctionAnalysis.ts @@ -6,6 +6,7 @@ type BranchGraph = Map>; export class SingleFunctionCoverageAnalysis { result: FunctionCoverageResult; branchGraph: BranchGraph = new Map(); + notFullyCoveredBasicBlock: Set = new Set(); constructor( public covInfo: CovInfo, name: string @@ -72,6 +73,14 @@ export class SingleFunctionCoverageAnalysis { toNodes.set(second, true); } } + for (const toNodes of this.branchGraph) { + let [currentBasicBlock, branchesForThatBasicBlock] = toNodes; + for (const isCovered of branchesForThatBasicBlock.values()) { + if (!isCovered) { + this.notFullyCoveredBasicBlock.add(currentBasicBlock); + } + } + } for (const toNodes of this.branchGraph.values()) { let used = 0; for (const toNode of toNodes.values()) { @@ -79,5 +88,13 @@ export class SingleFunctionCoverageAnalysis { } this.result.branchCoverageRate.used += used; } + // console.log(this.notFullyCoveredBasicBlock); + for (const block of this.notFullyCoveredBasicBlock) { + const lineInfo = this.covInfo.lineInfo.get(block); + if (lineInfo !== undefined && lineInfo.size > 0) { + this.result.linesToHighlight.add(Math.max(...lineInfo)); + } + } + // console.log(this.result.functionName, this.result.lineRange, this.result.linesToHighlight); } } diff --git a/tests-as/diytest.test.ts b/tests-as/diytest.test.ts new file mode 100644 index 0000000..17b2db8 --- /dev/null +++ b/tests-as/diytest.test.ts @@ -0,0 +1,31 @@ +import { describe, endTest, expect, test } from "../assembly"; +import { mycmp, instancecmp } from "../assembly/diytest" + +describe("cmp test", () => { + // 要考虑到流图可能不同,导致总branch数可能不同,所以计算branch覆盖率只能进行加权平均 + test("a>b", () => { + let a: i32 = 1; + let b: i32 = 0; + expect(mycmp(a, b)).equal(true); + }); + test("a { + let a: f32 = 0; + let b: f32 = 0.5; + expect(mycmp(a, b)).equal(false); + }); +}); + +describe("instance test", () => { + test("a>b", () => { + let a: i32 = 1; + let b: i32 = 0; + expect(instancecmp(a, b)).equal(true); + }); + test("a { + let a: f32 = 0; + let b: f32 = 0.5; + expect(instancecmp(a, b)).equal(false); + }); +}); + +endTest(); diff --git a/tests-ts/test/parser/__snapshots__/parser.test.ts.snap b/tests-ts/test/parser/__snapshots__/parser.test.ts.snap index 3bb5987..5035c41 100644 --- a/tests-ts/test/parser/__snapshots__/parser.test.ts.snap +++ b/tests-ts/test/parser/__snapshots__/parser.test.ts.snap @@ -16,6 +16,7 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 18, "used": 9, }, + "linesToHighlight": Set {}, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -241,6 +242,7 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 1, "used": 1, }, + "linesToHighlight": Set {}, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -466,6 +468,9 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 13, "used": 9, }, + "linesToHighlight": Set { + 25, + }, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -691,6 +696,7 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 2, "used": 2, }, + "linesToHighlight": Set {}, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -917,6 +923,7 @@ exports[`Parser generateFunctionCoverage 1`] = ` 39, 39, ], + "linesToHighlight": Set {}, "sourceUsedCount": Map { 39 => 3, }, @@ -931,6 +938,7 @@ exports[`Parser generateFunctionCoverage 1`] = ` 16, 24, ], + "linesToHighlight": Set {}, "sourceUsedCount": Map { 16 => 3, 17 => 3, @@ -952,6 +960,7 @@ exports[`Parser generateFunctionCoverage 1`] = ` 45, 45, ], + "linesToHighlight": Set {}, "sourceUsedCount": Map { 45 => 4, }, @@ -966,6 +975,9 @@ exports[`Parser generateFunctionCoverage 1`] = ` 10, 29, ], + "linesToHighlight": Set { + 25, + }, "sourceUsedCount": Map { 10 => 2, 11 => 2, @@ -989,6 +1001,7 @@ exports[`Parser generateFunctionCoverage 1`] = ` 10, 11, ], + "linesToHighlight": Set {}, "sourceUsedCount": Map { 10 => 2, 11 => 2, diff --git a/tests-ts/test/parser/singleFileAnalysis.test.ts b/tests-ts/test/parser/singleFileAnalysis.test.ts index 2609ca7..886419d 100644 --- a/tests-ts/test/parser/singleFileAnalysis.test.ts +++ b/tests-ts/test/parser/singleFileAnalysis.test.ts @@ -36,6 +36,7 @@ describe("singleFileAnalysis", () => { functionName: "A", lineRange: [6, 9], branchCoverageRate: rate_A, + linesToHighlight: new Set([]), sourceUsedCount: new Map([ [6, 3], [7, 0], @@ -49,6 +50,7 @@ describe("singleFileAnalysis", () => { functionName: "B", lineRange: [10, 14], branchCoverageRate: rate_B, + linesToHighlight: new Set([]), sourceUsedCount: new Map([ [10, 2], [11, 0], @@ -73,8 +75,8 @@ describe("singleFileAnalysis", () => { test("setUnTestedFunction error", () => { const analyzer = new SingleFileCoverageAnalysis("main", source); - expect(() => analyzer.setUnTestedFunction([[30, 31]])).toThrowError( - "unknowm error: There is no 29 Line in file main" + expect(() => analyzer.setUnTestedFunction([[30, 31]])).toThrow( + "unknown error: There is no 29 Line in file main" ); }); @@ -88,12 +90,13 @@ describe("singleFileAnalysis", () => { functionName: "A", lineRange: [6, 30], branchCoverageRate: rate, + linesToHighlight: new Set([]), sourceUsedCount: new Map([ [6, 3], [7, 0], [30, 3], ]), }; - expect(() => analyzer.merge([funcResult])).toThrowError(); + expect(() => analyzer.merge([funcResult])).toThrow(); }); }); From 667c94c2221bc13c0d9539b9fe781b5c43c8596b Mon Sep 17 00:00:00 2001 From: meowbmw Date: Thu, 13 Feb 2025 17:29:50 +0800 Subject: [PATCH 2/4] Address review comments; still need to add more test --- assembly/diytest.ts | 24 -------------- src/generator/html-generator/genCode.ts | 8 ++--- src/interface.ts | 8 +++-- src/parser/singleFileAnalysis.ts | 2 +- src/parser/singleFunctionAnalysis.ts | 20 +++++------- tests-as/diytest.test.ts | 31 ------------------- .../parser/__snapshots__/parser.test.ts.snap | 26 ++++++++-------- .../test/parser/singleFileAnalysis.test.ts | 6 ++-- 8 files changed, 33 insertions(+), 92 deletions(-) delete mode 100644 assembly/diytest.ts delete mode 100644 tests-as/diytest.test.ts diff --git a/assembly/diytest.ts b/assembly/diytest.ts deleted file mode 100644 index 9756be3..0000000 --- a/assembly/diytest.ts +++ /dev/null @@ -1,24 +0,0 @@ -export function mycmp(a: T, b: T): bool { - if (a > b) { - return true; - } else { - return false; - } -} - -export function instancecmp(a: T, b: T): bool { - if (isInteger()) { - if (a > b) { - return true; - } else { - return false; - } - } else if (isFloat()) { - if (a > b) { - return true; - } else { - return false; - } - } - return false; -} diff --git a/src/generator/html-generator/genCode.ts b/src/generator/html-generator/genCode.ts index 3d32201..695fa0e 100644 --- a/src/generator/html-generator/genCode.ts +++ b/src/generator/html-generator/genCode.ts @@ -1,4 +1,4 @@ -import { CodeCoverage, FileCoverageResult, OrganizationName, Repository } from "../../interface.js"; +import { CodeCoverage, FileCoverageResult, UncoveredLines, OrganizationName, Repository } from "../../interface.js"; import { escape } from "../../utils/escape.js"; function generateLineCount(totalLines: number): string { @@ -23,10 +23,10 @@ function generateLineCoverage(codes: CodeCoverage[]): string { return str.join("\n"); } -function generateSource(codes: CodeCoverage[], result: FileCoverageResult): string { +function generateSource(codes: CodeCoverage[], uncoveredlines: UncoveredLines): string { const str: string[] = []; for (const [index, code] of codes.entries()) { - if (result.linesToHighlight.has(index + 1)) { + if (uncoveredlines.has(index + 1)) { // IMPORTANT! to add "nocode" here to preventing prettify from adding unwanted pln class str.push('!' + escape(code.source)); } @@ -42,7 +42,7 @@ export function generateCodeHtml(relativePathofRoot: string, result: FileCoverag const lineCoutHtml = generateLineCount(codes.length); const lineCov = generateLineCoverage(codes); - const lineSource = generateSource(codes, result); + const lineSource = generateSource(codes, result.uncoveredlines); return ` diff --git a/src/interface.ts b/src/interface.ts index 4fc4cc6..650b038 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -16,6 +16,8 @@ export type FunctionIndex = number; export type LineIndex = number; export type ColumnIndex = number; export type FileIndex = number; +export type UncoveredBasicBlocks = Set; +export type UncoveredLines = Set; // input cov export type BranchInfo = [CodeSnippetIndex, CodeSnippetIndex]; @@ -87,13 +89,13 @@ export class FileCoverageResult { functionCoverageRate: Rate = new Rate(); lineCoverageRate: Rate = new Rate(); sourceUsedCount: CodeCoverage[] = []; - linesToHighlight: Set = new Set(); + uncoveredlines: Set = new Set(); } export class FunctionCoverageResult { constructor(public functionName: string) {} branchCoverageRate: Rate = new Rate(); - linesToHighlight: Set = new Set(); + uncoveredlines: UncoveredLines = new Set(); lineRange: [number, number] = [Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER]; /** * first means lineIndex; @@ -113,7 +115,7 @@ export class FunctionCoverageResult { ]; result.branchCoverageRate = Rate.summarize(infos.map((info) => info.branchCoverageRate)); for (const info of infos) { - info.linesToHighlight.forEach(line => result.linesToHighlight.add(line)); + info.uncoveredlines.forEach(line => result.uncoveredlines.add(line)); for (const [lineIndex, count] of info.sourceUsedCount.entries()) { const srcLineUsedCount = result.sourceUsedCount.get(lineIndex); result.sourceUsedCount.set(lineIndex, srcLineUsedCount === undefined ? count : srcLineUsedCount + count); diff --git a/src/parser/singleFileAnalysis.ts b/src/parser/singleFileAnalysis.ts index a2b38f1..4cf082a 100644 --- a/src/parser/singleFileAnalysis.ts +++ b/src/parser/singleFileAnalysis.ts @@ -30,7 +30,7 @@ export class SingleFileCoverageAnalysis { // SingleFileCoverageAnalysis contains FileCoverageResult if (results.length === 0) return; for (const functionCovResult of results) { - functionCovResult.linesToHighlight.forEach(line => this.result.linesToHighlight.add(line)); + functionCovResult.uncoveredlines.forEach(line => this.result.uncoveredlines.add(line)); for (const [lineIndex, count] of functionCovResult.sourceUsedCount.entries()) { const srcLineUsedCount = this.result.sourceUsedCount[lineIndex - 1]; if (srcLineUsedCount === undefined) { diff --git a/src/parser/singleFunctionAnalysis.ts b/src/parser/singleFunctionAnalysis.ts index 48f68b3..8259bdc 100644 --- a/src/parser/singleFunctionAnalysis.ts +++ b/src/parser/singleFunctionAnalysis.ts @@ -1,12 +1,12 @@ import assert from "node:assert"; -import { CodeSnippetIndex, CovInfo, FunctionCoverageResult } from "../interface.js"; +import { CodeSnippetIndex, CovInfo, FunctionCoverageResult, UncoveredBasicBlocks } from "../interface.js"; type BranchGraph = Map>; export class SingleFunctionCoverageAnalysis { result: FunctionCoverageResult; branchGraph: BranchGraph = new Map(); - notFullyCoveredBasicBlock: Set = new Set(); + notFullyCoveredBasicBlock: UncoveredBasicBlocks = new Set(); constructor( public covInfo: CovInfo, name: string @@ -73,28 +73,22 @@ export class SingleFunctionCoverageAnalysis { toNodes.set(second, true); } } - for (const toNodes of this.branchGraph) { - let [currentBasicBlock, branchesForThatBasicBlock] = toNodes; + for (const [currentBasicBlock, branchesForThatBasicBlock] of this.branchGraph) { + let used = 0; for (const isCovered of branchesForThatBasicBlock.values()) { if (!isCovered) { this.notFullyCoveredBasicBlock.add(currentBasicBlock); + } else { + used++; } } - } - for (const toNodes of this.branchGraph.values()) { - let used = 0; - for (const toNode of toNodes.values()) { - if (toNode) used++; - } this.result.branchCoverageRate.used += used; } - // console.log(this.notFullyCoveredBasicBlock); for (const block of this.notFullyCoveredBasicBlock) { const lineInfo = this.covInfo.lineInfo.get(block); if (lineInfo !== undefined && lineInfo.size > 0) { - this.result.linesToHighlight.add(Math.max(...lineInfo)); + this.result.uncoveredlines.add(Math.max(...lineInfo)); } } - // console.log(this.result.functionName, this.result.lineRange, this.result.linesToHighlight); } } diff --git a/tests-as/diytest.test.ts b/tests-as/diytest.test.ts deleted file mode 100644 index 17b2db8..0000000 --- a/tests-as/diytest.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, endTest, expect, test } from "../assembly"; -import { mycmp, instancecmp } from "../assembly/diytest" - -describe("cmp test", () => { - // 要考虑到流图可能不同,导致总branch数可能不同,所以计算branch覆盖率只能进行加权平均 - test("a>b", () => { - let a: i32 = 1; - let b: i32 = 0; - expect(mycmp(a, b)).equal(true); - }); - test("a { - let a: f32 = 0; - let b: f32 = 0.5; - expect(mycmp(a, b)).equal(false); - }); -}); - -describe("instance test", () => { - test("a>b", () => { - let a: i32 = 1; - let b: i32 = 0; - expect(instancecmp(a, b)).equal(true); - }); - test("a { - let a: f32 = 0; - let b: f32 = 0.5; - expect(instancecmp(a, b)).equal(false); - }); -}); - -endTest(); diff --git a/tests-ts/test/parser/__snapshots__/parser.test.ts.snap b/tests-ts/test/parser/__snapshots__/parser.test.ts.snap index 5035c41..bc8fbbf 100644 --- a/tests-ts/test/parser/__snapshots__/parser.test.ts.snap +++ b/tests-ts/test/parser/__snapshots__/parser.test.ts.snap @@ -16,7 +16,6 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 18, "used": 9, }, - "linesToHighlight": Set {}, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -227,6 +226,7 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 18, "used": 9, }, + "uncoveredlines": Set {}, }, FileCoverageResult { "branchCoverageRate": Rate { @@ -242,7 +242,6 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 1, "used": 1, }, - "linesToHighlight": Set {}, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -453,6 +452,7 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 1, "used": 1, }, + "uncoveredlines": Set {}, }, FileCoverageResult { "branchCoverageRate": Rate { @@ -468,9 +468,6 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 13, "used": 9, }, - "linesToHighlight": Set { - 25, - }, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -681,6 +678,9 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 13, "used": 9, }, + "uncoveredlines": Set { + 25, + }, }, FileCoverageResult { "branchCoverageRate": Rate { @@ -696,7 +696,6 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 2, "used": 2, }, - "linesToHighlight": Set {}, "sourceUsedCount": [ CodeCoverage { "source": "", @@ -907,6 +906,7 @@ exports[`Parser generateFileCoverage 1`] = ` "total": 2, "used": 2, }, + "uncoveredlines": Set {}, }, ] `; @@ -923,10 +923,10 @@ exports[`Parser generateFunctionCoverage 1`] = ` 39, 39, ], - "linesToHighlight": Set {}, "sourceUsedCount": Map { 39 => 3, }, + "uncoveredlines": Set {}, }, FunctionCoverageResult { "branchCoverageRate": Rate { @@ -938,7 +938,6 @@ exports[`Parser generateFunctionCoverage 1`] = ` 16, 24, ], - "linesToHighlight": Set {}, "sourceUsedCount": Map { 16 => 3, 17 => 3, @@ -949,6 +948,7 @@ exports[`Parser generateFunctionCoverage 1`] = ` 22 => 1, 24 => 2, }, + "uncoveredlines": Set {}, }, FunctionCoverageResult { "branchCoverageRate": Rate { @@ -960,10 +960,10 @@ exports[`Parser generateFunctionCoverage 1`] = ` 45, 45, ], - "linesToHighlight": Set {}, "sourceUsedCount": Map { 45 => 4, }, + "uncoveredlines": Set {}, }, FunctionCoverageResult { "branchCoverageRate": Rate { @@ -975,9 +975,6 @@ exports[`Parser generateFunctionCoverage 1`] = ` 10, 29, ], - "linesToHighlight": Set { - 25, - }, "sourceUsedCount": Map { 10 => 2, 11 => 2, @@ -990,6 +987,9 @@ exports[`Parser generateFunctionCoverage 1`] = ` 26 => 1, 29 => 0, }, + "uncoveredlines": Set { + 25, + }, }, FunctionCoverageResult { "branchCoverageRate": Rate { @@ -1001,11 +1001,11 @@ exports[`Parser generateFunctionCoverage 1`] = ` 10, 11, ], - "linesToHighlight": Set {}, "sourceUsedCount": Map { 10 => 2, 11 => 2, }, + "uncoveredlines": Set {}, }, ] `; diff --git a/tests-ts/test/parser/singleFileAnalysis.test.ts b/tests-ts/test/parser/singleFileAnalysis.test.ts index 886419d..0d59f53 100644 --- a/tests-ts/test/parser/singleFileAnalysis.test.ts +++ b/tests-ts/test/parser/singleFileAnalysis.test.ts @@ -36,7 +36,7 @@ describe("singleFileAnalysis", () => { functionName: "A", lineRange: [6, 9], branchCoverageRate: rate_A, - linesToHighlight: new Set([]), + uncoveredlines: new Set([]), sourceUsedCount: new Map([ [6, 3], [7, 0], @@ -50,7 +50,7 @@ describe("singleFileAnalysis", () => { functionName: "B", lineRange: [10, 14], branchCoverageRate: rate_B, - linesToHighlight: new Set([]), + uncoveredlines: new Set([]), sourceUsedCount: new Map([ [10, 2], [11, 0], @@ -90,7 +90,7 @@ describe("singleFileAnalysis", () => { functionName: "A", lineRange: [6, 30], branchCoverageRate: rate, - linesToHighlight: new Set([]), + uncoveredlines: new Set([]), sourceUsedCount: new Map([ [6, 3], [7, 0], From b5b1401d888a900400d299c4cfbe3b78e0b4cb02 Mon Sep 17 00:00:00 2001 From: meowbmw Date: Mon, 17 Feb 2025 13:59:14 +0800 Subject: [PATCH 3/4] fix lint & prettifier error --- src/generator/html-generator/genCode.ts | 3 +-- src/interface.ts | 2 +- src/parser/singleFileAnalysis.ts | 2 +- src/parser/singleFunctionAnalysis.ts | 6 +++--- tests-ts/test/parser/singleFileAnalysis.test.ts | 4 +--- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/generator/html-generator/genCode.ts b/src/generator/html-generator/genCode.ts index 695fa0e..c61d79f 100644 --- a/src/generator/html-generator/genCode.ts +++ b/src/generator/html-generator/genCode.ts @@ -29,8 +29,7 @@ function generateSource(codes: CodeCoverage[], uncoveredlines: UncoveredLines): if (uncoveredlines.has(index + 1)) { // IMPORTANT! to add "nocode" here to preventing prettify from adding unwanted pln class str.push('!' + escape(code.source)); - } - else { + } else { str.push(escape(code.source)); } } diff --git a/src/interface.ts b/src/interface.ts index 650b038..e664323 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -115,7 +115,7 @@ export class FunctionCoverageResult { ]; result.branchCoverageRate = Rate.summarize(infos.map((info) => info.branchCoverageRate)); for (const info of infos) { - info.uncoveredlines.forEach(line => result.uncoveredlines.add(line)); + for (const line of info.uncoveredlines) result.uncoveredlines.add(line); for (const [lineIndex, count] of info.sourceUsedCount.entries()) { const srcLineUsedCount = result.sourceUsedCount.get(lineIndex); result.sourceUsedCount.set(lineIndex, srcLineUsedCount === undefined ? count : srcLineUsedCount + count); diff --git a/src/parser/singleFileAnalysis.ts b/src/parser/singleFileAnalysis.ts index 4cf082a..5c783b9 100644 --- a/src/parser/singleFileAnalysis.ts +++ b/src/parser/singleFileAnalysis.ts @@ -30,7 +30,7 @@ export class SingleFileCoverageAnalysis { // SingleFileCoverageAnalysis contains FileCoverageResult if (results.length === 0) return; for (const functionCovResult of results) { - functionCovResult.uncoveredlines.forEach(line => this.result.uncoveredlines.add(line)); + for (const line of functionCovResult.uncoveredlines) this.result.uncoveredlines.add(line); for (const [lineIndex, count] of functionCovResult.sourceUsedCount.entries()) { const srcLineUsedCount = this.result.sourceUsedCount[lineIndex - 1]; if (srcLineUsedCount === undefined) { diff --git a/src/parser/singleFunctionAnalysis.ts b/src/parser/singleFunctionAnalysis.ts index 8259bdc..d707220 100644 --- a/src/parser/singleFunctionAnalysis.ts +++ b/src/parser/singleFunctionAnalysis.ts @@ -76,10 +76,10 @@ export class SingleFunctionCoverageAnalysis { for (const [currentBasicBlock, branchesForThatBasicBlock] of this.branchGraph) { let used = 0; for (const isCovered of branchesForThatBasicBlock.values()) { - if (!isCovered) { - this.notFullyCoveredBasicBlock.add(currentBasicBlock); - } else { + if (isCovered) { used++; + } else { + this.notFullyCoveredBasicBlock.add(currentBasicBlock); } } this.result.branchCoverageRate.used += used; diff --git a/tests-ts/test/parser/singleFileAnalysis.test.ts b/tests-ts/test/parser/singleFileAnalysis.test.ts index 0d59f53..bbd7399 100644 --- a/tests-ts/test/parser/singleFileAnalysis.test.ts +++ b/tests-ts/test/parser/singleFileAnalysis.test.ts @@ -75,9 +75,7 @@ describe("singleFileAnalysis", () => { test("setUnTestedFunction error", () => { const analyzer = new SingleFileCoverageAnalysis("main", source); - expect(() => analyzer.setUnTestedFunction([[30, 31]])).toThrow( - "unknown error: There is no 29 Line in file main" - ); + expect(() => analyzer.setUnTestedFunction([[30, 31]])).toThrow("unknown error: There is no 29 Line in file main"); }); test("merge error", () => { From 56a37f7df3432b31f3de85a3e2de30b3b21b3490 Mon Sep 17 00:00:00 2001 From: meowbmw Date: Mon, 17 Feb 2025 16:30:14 +0800 Subject: [PATCH 4/4] add test case for uncoveredlines --- package.json | 2 +- .../parser/singleFunctionAnalysis.test.ts | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ebbcb7a..afdfe77 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "build": "tsc --build ./transform/tsconfig.json && node ./build/esbuild.js", "test": "node bin/as-test.js && cross-env NODE_OPTIONS=--experimental-vm-modules jest", "lint": "eslint src assembly tests-ts/test --max-warnings=0 && prettier -c .", - "lint:fix": "eslint src assembly --fix", + "lint:fix": "eslint src assembly --fix && npx prettier --write .", "example": "node bin/as-test.js --config example/as-test.config.cjs ; node bin/as-test.js --config example/as-test.config.js" }, "dependencies": { diff --git a/tests-ts/test/parser/singleFunctionAnalysis.test.ts b/tests-ts/test/parser/singleFunctionAnalysis.test.ts index b55bfe6..0847ae9 100644 --- a/tests-ts/test/parser/singleFunctionAnalysis.test.ts +++ b/tests-ts/test/parser/singleFunctionAnalysis.test.ts @@ -58,6 +58,83 @@ describe("singleFunctionAnalysis", () => { ]) ); }); + + test("forLoop", () => { + const covInfo: CovInfo = { + branchInfo: [ + [1, 2], + [1, 3], + ], + lineInfo: new Map([ + [0, new Set([17, 18])], + [1, new Set([18])], + [2, new Set([18, 20])], + [3, new Set([])], + [4, new Set([22])], + ]), + }; + const traceInfo = [0, 1, 3, 4]; + const analyzer = new SingleFunctionCoverageAnalysis(covInfo, "main"); + const result = analyzer.update(traceInfo); + expect(result.uncoveredlines).toEqual(new Set([18])); + }); + + test("ifWithoutElse", () => { + const covInfo: CovInfo = { + branchInfo: [ + [0, 1], + [0, 2], + ], + lineInfo: new Map([ + [0, new Set([2])], + [1, new Set([3])], + [2, new Set([5])], + ]), + }; + const traceInfo = [0, 1]; + const analyzer = new SingleFunctionCoverageAnalysis(covInfo, "main"); + const result = analyzer.update(traceInfo); + expect(result.uncoveredlines).toEqual(new Set([2])); + }); + + test("threeOperandOperator", () => { + const covInfo: CovInfo = { + branchInfo: [ + [0, 1], + [0, 2], + ], + lineInfo: new Map([ + [0, new Set([26])], + [1, new Set([26])], + [2, new Set([26])], + [3, new Set([26])], + ]), + }; + const traceInfo = [3, 0, 1]; + const analyzer = new SingleFunctionCoverageAnalysis(covInfo, "main"); + const result = analyzer.update(traceInfo); + expect(result.uncoveredlines).toEqual(new Set([26])); + }); + + test("whileLoop", () => { + const covInfo: CovInfo = { + branchInfo: [ + [1, 2], + [1, 3], + ], + lineInfo: new Map([ + [0, new Set([9])], + [1, new Set([10])], + [2, new Set([11])], + [3, new Set([])], + [4, new Set([10, 13])], + ]), + }; + const traceInfo = [0, 1, 3, 4]; + const analyzer = new SingleFunctionCoverageAnalysis(covInfo, "main"); + const result = analyzer.update(traceInfo); + expect(result.uncoveredlines).toEqual(new Set([10])); + }); }); test("mergeFromGeneric()", () => {