From 6d26b060666a47c7272951b0825133e44d4c2026 Mon Sep 17 00:00:00 2001 From: adekunle Date: Wed, 6 May 2026 18:33:11 +0100 Subject: [PATCH] test_runner: exclude ignored branches from lcov output --- lib/internal/test_runner/reporter/lcov.js | 29 +++++++++++++++-- test/parallel/test-runner-coverage.js | 39 ++++++++++++++++++++++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/lib/internal/test_runner/reporter/lcov.js b/lib/internal/test_runner/reporter/lcov.js index 698913d79dec02..45c2410b91d205 100644 --- a/lib/internal/test_runner/reporter/lcov.js +++ b/lib/internal/test_runner/reporter/lcov.js @@ -68,15 +68,38 @@ 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. + let branchCount = 0; + let coveredBranchCount = 0; + for (let j = 0; j < file.branches.length; j++) { - lcov += `BRDA:${file.branches[j].line},${j},0,${file.branches[j].count}\n`; + const branch = file.branches[j]; + let lineReported = false; + + for (let k = 0; k < file.lines.length; k++) { + if (file.lines[k].line === branch.line) { + lineReported = true; + break; + } + } + + if (!lineReported) { + continue; + } + + lcov += `BRDA:${branch.line},${branchCount},0,${branch.count}\n`; + + branchCount++; + + if (branch.count !== 0) { + coveredBranchCount++; + } } // Branch coverage summaries are stored in two lines: // ## BRF:\ // ## BRH:\ - lcov += `BRF:${file.totalBranchCount}\n`; - lcov += `BRH:${file.coveredBranchCount}\n`; + lcov += `BRF:${branchCount}\n`; + lcov += `BRH:${coveredBranchCount}\n`; // Then there is a list of execution counts for each instrumented line // (i.e. a line which resulted in executable code): diff --git a/test/parallel/test-runner-coverage.js b/test/parallel/test-runner-coverage.js index 5a8f3d743538cb..ba653530c39c9f 100644 --- a/test/parallel/test-runner-coverage.js +++ b/test/parallel/test-runner-coverage.js @@ -2,7 +2,7 @@ const common = require('../common'); const assert = require('node:assert'); const { spawnSync } = require('node:child_process'); -const { readdirSync } = require('node:fs'); +const { readdirSync, writeFileSync } = require('node:fs'); const { test } = require('node:test'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); @@ -77,6 +77,43 @@ function getSpecCoverageFixtureReport() { return report; } +test('lcov reporter excludes BRDA entries for ignored lines', skipIfNoInspector, () => { + const fixture = tmpdir.resolve('lcov-ignore-branch.test.js'); + + writeFileSync(fixture, ` +'use strict'; + +const test = require('node:test'); + +test('ignored branch', () => { + // node:coverage ignore next + if (false) { + throw new Error('ignored'); + } + + if (true) { + // Covered branch to ensure LCOV still reports branch data. + } +}); +`); + + const child = spawnSync(process.execPath, [ + '--test', + '--experimental-test-coverage', + '--test-reporter', + 'lcov', + fixture, + ]); + + assert.strictEqual(child.stderr.toString(), ''); + assert.strictEqual(child.status, 0); + + const stdout = child.stdout.toString(); + + assert(!stdout.includes('BRDA:8,')); + assert.match(stdout, /BRDA:/); +}); + test('test coverage report', async (t) => { await t.test('handles the inspector not being available', (t) => { if (process.features.inspector) {