Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/internal/test_runner/coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,14 +263,16 @@ class TestCoverage {
ObjectAssign(range, mapRangeToLines(range, lines));

if (isBlockCoverage) {
const ignored = range.ignoredLines === range.lines.length;

ArrayPrototypePush(branchReports, {
__proto__: null,
line: range.lines[0]?.line,
count: range.count,
ignored,
});

if (range.count !== 0 ||
range.ignoredLines === range.lines.length) {
if (range.count !== 0 || ignored) {
branchesCovered++;
}

Expand Down
9 changes: 5 additions & 4 deletions lib/internal/test_runner/reporter/lcov.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class LcovReporter extends Transform {
// containing filename and coverage data:
// ## SF:\<path to the source file\>
lcov += `SF:${relative(workingDirectory, file.path)}\n`;
const branches = file.branches.filter((branch) => !branch.ignored);

// Following is a list of line numbers for each function name found in
// the source file:
Expand Down Expand Up @@ -68,15 +69,15 @@ class LcovReporter extends Transform {
// Taken is either '-' if the basic block containing the branch was
// never executed or a number indicating how often that branch was
// taken.
for (let j = 0; j < file.branches.length; j++) {
lcov += `BRDA:${file.branches[j].line},${j},0,${file.branches[j].count}\n`;
for (let j = 0; j < branches.length; j++) {
lcov += `BRDA:${branches[j].line},${j},0,${branches[j].count}\n`;
}

// Branch coverage summaries are stored in two lines:
// ## BRF:\<number of branches found\>
// ## BRH:\<number of branches hit\>
lcov += `BRF:${file.totalBranchCount}\n`;
lcov += `BRH:${file.coveredBranchCount}\n`;
lcov += `BRF:${branches.length}\n`;
lcov += `BRH:${branches.filter((branch) => branch.count !== 0).length}\n`;

// Then there is a list of execution counts for each instrumented line
// (i.e. a line which resulted in executable code):
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-runner-coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,11 @@ test('coverage reports on lines, functions, and branches', skipIfNoInspector, as
await t.test('reports on branch coverage', () => {
const uncalledBranch = file.branches.find((b) => b.line === 6);
assert.strictEqual(uncalledBranch.count, 0);
assert.strictEqual(uncalledBranch.ignored, true);

const calledTwice = file.branches.find((b) => b.line === 35);
assert.strictEqual(calledTwice.count, 2);
assert.strictEqual(calledTwice.ignored, false);
});

await t.test('reports on line coverage', () => {
Expand Down
42 changes: 42 additions & 0 deletions test/parallel/test-runner-reporters.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ require('../common');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { describe, it } = require('node:test');
const { once } = require('node:events');
const { spawnSync } = require('node:child_process');
const assert = require('node:assert');
const fs = require('node:fs');
const LcovReporter = require('internal/test_runner/reporter/lcov');

const testFile = fixtures.path('test-runner/reporters.js');
tmpdir.refresh();
Expand Down Expand Up @@ -105,6 +107,46 @@ describe('node:test reporters', { concurrency: true }, () => {
assert.match(file2Contents, / failing/);
});

it('should omit ignored branches from lcov output', async () => {
const reporter = new LcovReporter();
const chunks = [];
reporter.setEncoding('utf8');
reporter.on('data', common.mustCallAtLeast((chunk) => {
chunks.push(chunk);
}));

reporter.end({
type: 'test:coverage',
data: {
summary: {
workingDirectory: '/tmp/project',
files: [{
path: '/tmp/project/file.js',
functions: [],
totalFunctionCount: 0,
coveredFunctionCount: 0,
branches: [
{ line: 1, count: 0, ignored: true },
{ line: 2, count: 1, ignored: false },
],
totalBranchCount: 2,
coveredBranchCount: 1,
lines: [],
totalLineCount: 0,
coveredLineCount: 0,
}],
},
},
});

await once(reporter, 'finish');
const output = chunks.join('');
assert.match(output, /BRDA:2,0,0,1/);
assert(!output.includes('BRDA:1,0,0,0'));
assert.match(output, /BRF:1/);
assert.match(output, /BRH:1/);
});

['js', 'cjs', 'mjs'].forEach((ext) => {
it(`should support a '${ext}' file as a custom reporter`, async () => {
const filename = `custom.${ext}`;
Expand Down
Loading