From 89317a4a90086d1eb0be68c6e85b1ca5458be693 Mon Sep 17 00:00:00 2001 From: wdsmini Date: Thu, 26 Mar 2026 16:50:20 +0800 Subject: [PATCH] test_runner: fix coverage ignore comments to exclude BRDA entries When using /* node:coverage ignore next */ comments, the test_runner correctly excluded DA (line coverage) entries from lcov output, but still included BRDA (branch coverage) entries as uncovered. This caused branch coverage to report lower percentages than expected. This fix ensures that branches completely covered by ignore comments are excluded from the branch coverage report, matching the behavior of c8 and user expectations. Fixes: https://github.com/nodejs/node/issues/61586 --- lib/internal/test_runner/coverage.js | 25 ++++++----- .../test-runner/coverage-ignore-branch.js | 13 ++++++ .../test-runner-coverage-ignore-branch.js | 43 +++++++++++++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/test-runner/coverage-ignore-branch.js create mode 100644 test/parallel/test-runner-coverage-ignore-branch.js diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 8fa9c872568d1e..8756d02b206f71 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -193,18 +193,23 @@ class TestCoverage { ObjectAssign(range, mapRangeToLines(range, lines)); if (isBlockCoverage) { - ArrayPrototypePush(branchReports, { - __proto__: null, - line: range.lines[0]?.line, - count: range.count, - }); - - if (range.count !== 0 || - range.ignoredLines === range.lines.length) { + // Skip branches that are completely ignored + if (range.ignoredLines === range.lines.length) { + totalBranches++; branchesCovered++; + } else { + ArrayPrototypePush(branchReports, { + __proto__: null, + line: range.lines[0]?.line, + count: range.count, + }); + + if (range.count !== 0) { + branchesCovered++; + } + + totalBranches++; } - - totalBranches++; } } diff --git a/test/fixtures/test-runner/coverage-ignore-branch.js b/test/fixtures/test-runner/coverage-ignore-branch.js new file mode 100644 index 00000000000000..097dd38a4cd71f --- /dev/null +++ b/test/fixtures/test-runner/coverage-ignore-branch.js @@ -0,0 +1,13 @@ +// Test fixture for issue #61586 +// Tests that node:coverage ignore next also excludes BRDA entries + +function getValue(condition) { + if (condition) { + return 'truthy'; + } + /* node:coverage ignore next */ + return 'falsy'; +} + +// Call only the truthy branch +getValue(true); diff --git a/test/parallel/test-runner-coverage-ignore-branch.js b/test/parallel/test-runner-coverage-ignore-branch.js new file mode 100644 index 00000000000000..f6b8e776a5fb53 --- /dev/null +++ b/test/parallel/test-runner-coverage-ignore-branch.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('node:assert'); +const { spawnSync } = require('node:child_process'); +const { test } = require('node:test'); +const fixtures = require('../common/fixtures'); +const skipIfNoInspector = { + skip: !process.features.inspector ? 'inspector disabled' : false +}; + +test('coverage ignore next excludes BRDA for ignored branches', skipIfNoInspector, async (t) => { + const fixture = fixtures.path('test-runner', 'coverage-ignore-branch.js'); + const args = [ + '--experimental-test-coverage', + '--test-reporter', 'lcov', + fixture, + ]; + const result = spawnSync(process.execPath, args); + const lcovOutput = result.stdout.toString(); + + // Parse the LCOV output + const lines = lcovOutput.split('\n'); + const brdaLines = lines.filter(l => l.startsWith('BRDA:')); + + // All branches should be covered (no uncovered branches) + // The branch leading to the ignored code should be excluded + const uncoveredBranches = brdaLines.filter(l => l.endsWith(',0')); + + assert.strictEqual(uncoveredBranches.length, 0, + `Expected no uncovered branches, but found: ${uncoveredBranches.join(', ')}`); + + // Verify branch coverage is 100% + const brfLine = lines.find(l => l.startsWith('BRF:')); + const brhLine = lines.find(l => l.startsWith('BRH:')); + assert(brfLine, 'Should have BRF line'); + assert(brhLine, 'Should have BRH line'); + + const brf = parseInt(brfLine.split(':')[1], 10); + const brh = parseInt(brhLine.split(':')[1], 10); + + assert.strictEqual(brf, brh, + `Expected branch coverage to be 100% (${brh}/${brf}), but found ${brh}/${brf}`); +});