Skip to content

Commit

Permalink
feat (coverage): Load branch coverage from coverage files (#389)
Browse files Browse the repository at this point in the history
This commit teaches the LCOV parser to also read branch coverage data.

Works towards #362
  • Loading branch information
vogelsgesang committed May 5, 2024
1 parent 60051c5 commit e5c95e4
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 5 deletions.
59 changes: 56 additions & 3 deletions src/test-explorer/lcov_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export function parseLcov(
class FileCoverageInfo {
functionsByLine: Map<number, vscode.DeclarationCoverage> = new Map();
lineCoverage: Map<number, vscode.StatementCoverage> = new Map();
coverageByLineAndBranch: Map<number, Map<string, vscode.BranchCoverage>> =
new Map();
}
const infosByFile: Map<string, FileCoverageInfo> = new Map();
for (const block of lcov.split(/end_of_record(\n|$)/)) {
Expand Down Expand Up @@ -191,12 +193,54 @@ export function parseLcov(
// Ignored. Reconstructed from DA entries
break;
case "BRDA": {
// branch coverage: <line_number>,[<exception>]<block>,<branch>,<taken>
// branch coverage: <line_number>,[<exception>]<block>,<branch>,<hitCount>
// Note that the <branch> might contain commas, which requires being
// a bit careful while parsing.
const match = value.match(/(\d+),(e?)(\d+),(.+)/);
if (!match) {
throw new Error(`Invalid FNDA entry`);
}
// TODO: Add support for branch coverage
const lineNumber = Number.parseInt(match[1], 10) - 1;
if (lineNumber < 0) {
throw new Error("Negative line number in DA entry");
}
const isException = match[2] === "e";
const blockId = Number.parseInt(match[3], 10);
const rest = match[4];
const commaOffset = rest.lastIndexOf(",");
if (commaOffset === undefined) {
throw new Error(`Invalid FNDA entry`);
}
const label = rest.substring(0, commaOffset);
const hitCountStr = rest.substring(commaOffset + 1);
const hitCount =
hitCountStr === "-" ? 0 : Number.parseInt(hitCountStr, 10);
if (hitCount < 0) {
throw new Error("Negative hit count in DA entry");
}

if (info === undefined) {
throw new Error(`Missing filename`);
}

// We don't want to display coverage for exception edges.
if (isException) break;

// Insert into `branchByLineAndBranch`
if (!info.coverageByLineAndBranch.has(lineNumber)) {
info.coverageByLineAndBranch.set(lineNumber, new Map());
}
const coverageByBranch = info.coverageByLineAndBranch.get(lineNumber);
const branchId = `${blockId}:${label}`;
if (!coverageByBranch.has(branchId)) {
coverageByBranch.set(
branchId,
new vscode.BranchCoverage(0, undefined, label),
);
}
const branchCoverage = coverageByBranch.get(branchId);
assert(typeof branchCoverage.executed == "number");
branchCoverage.executed += hitCount;
break;
}
case "BRF": // branches found
Expand All @@ -216,7 +260,16 @@ export function parseLcov(
Array.from(info.functionsByLine.values()),
);
detailedCoverage = detailedCoverage.concat(
Array.from(info.lineCoverage.values()),
Array.from(info.lineCoverage.values()).map((c) => {
assert("line" in c.location);
const branchCoverage = info.coverageByLineAndBranch.get(
c.location.line,
);
if (branchCoverage) {
c.branches = Array.from(branchCoverage.values());
}
return c;
}),
);
fileCoverages.push(
BazelFileCoverage.fromDetails(
Expand Down
25 changes: 23 additions & 2 deletions test/lcov_parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ describe("The lcov parser", () => {
assert.strictEqual(fileCov.statementCoverage.covered, 11);
});
it("branch coverage", () => {
assert(fileCov.branchCoverage === undefined);
assert(fileCov.branchCoverage !== undefined);
assert.strictEqual(fileCov.branchCoverage.total, 6);
assert.strictEqual(fileCov.branchCoverage.covered, 3);
});
it("function coverage details", () => {
const initFunc = getFunctionByLine(fileCov, 24);
Expand All @@ -109,6 +111,12 @@ describe("The lcov parser", () => {
assert.equal(getLineCoverageForLine(fileCov, 38).executed, 0);
assert.equal(getLineCoverageForLine(fileCov, 40).executed, 1);
});
it("branch coverage data", () => {
const branchCoverage = getLineCoverageForLine(fileCov, 37).branches;
assert.equal(branchCoverage.length, 2);
assert.equal(branchCoverage[0].executed, 1);
assert.equal(branchCoverage[1].executed, 0);
});
});

describe("parses C++ coverage data", () => {
Expand All @@ -128,7 +136,9 @@ describe("The lcov parser", () => {
assert.strictEqual(fileCov.statementCoverage.covered, 505);
});
it("branch coverage", () => {
assert(fileCov.branchCoverage === undefined);
assert(fileCov.branchCoverage !== undefined);
assert.strictEqual(fileCov.branchCoverage.total, 2560);
assert.strictEqual(fileCov.branchCoverage.covered, 843);
});
it("function coverage details", () => {
const initFunc = getFunctionByLine(fileCov, 71);
Expand All @@ -141,6 +151,14 @@ describe("The lcov parser", () => {
assert.equal(getLineCoverageForLine(fileCov, 178).executed, 0);
assert.equal(getLineCoverageForLine(fileCov, 193).executed, 4);
});
it("branch coverage data", () => {
const branchCoverage = getLineCoverageForLine(fileCov, 479).branches;
assert.equal(branchCoverage.length, 2);
assert.equal(branchCoverage[0].executed, 1);
assert.equal(branchCoverage[1].executed, 0);
const branchCoverage2 = getLineCoverageForLine(fileCov, 481).branches;
assert.equal(branchCoverage2.length, 12);
});
});

describe("parses Rust coverage data", () => {
Expand All @@ -161,6 +179,8 @@ describe("The lcov parser", () => {
assert.strictEqual(fileCov.statementCoverage.covered, 426);
});
it("branch coverage", () => {
// Rust has no branch coverage data, as of writing this test case.
// Also see https://github.com/rust-lang/rust/issues/79649
assert(fileCov.branchCoverage === undefined);
});
it("function coverage details", () => {
Expand Down Expand Up @@ -194,6 +214,7 @@ describe("The lcov parser", () => {
assert.strictEqual(fileCov.statementCoverage.covered, 133);
});
it("branch coverage", () => {
// Go has no branch coverage data.
assert(fileCov.branchCoverage === undefined);
});
it("line coverage details", () => {
Expand Down

0 comments on commit e5c95e4

Please sign in to comment.