Skip to content

test_runner: source-mapped coverage marks an unexecuted return as covered #67099

Open

Description

@CaliLuke

Version

v26.12.1

Platform

Darwin arm64

Subsystem

test_runner coverage

Reproduction

Create these two files in an empty directory. No packages or module hooks are required. The inline source map contains its TypeScript source.

compiled.mjs

function choose(value) {
  if (value) {
    return "yes";
  }
  return "no";
}
export {
  choose
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiZXhhbXBsZS50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiZXhwb3J0IGZ1bmN0aW9uIGNob29zZSh2YWx1ZTogYm9vbGVhbik6IHN0cmluZyB7XG4gIGlmICh2YWx1ZSkge1xuICAgIHJldHVybiBcInllc1wiO1xuICB9XG4gIHJldHVybiBcIm5vXCI7XG59XG4iXSwKICAibWFwcGluZ3MiOiAiQUFBTyxTQUFTLE9BQU8sT0FBd0I7QUFDN0MsTUFBSSxPQUFPO0FBQ1QsV0FBTztBQUFBLEVBQ1Q7QUFDQSxTQUFPO0FBQ1Q7IiwKICAibmFtZXMiOiBbXQp9Cg==

compiled.test.mjs

import assert from "node:assert/strict";
import test from "node:test";
import { choose } from "./compiled.mjs";

test("branch", () => assert.equal(choose(true), "yes"));

Run:

node --enable-source-maps --experimental-test-coverage --test compiled.test.mjs

The embedded original source is:

export function choose(value: boolean): string {
  if (value) {
    return "yes";
  }
  return "no";
}

Expected behavior

The test only calls choose(true). Original line 5 (return "no") should be uncovered. With the source-map comment removed, the JavaScript report correctly shows line 5 uncovered.

Actual behavior

The source-mapped report shows example.ts at 100% lines, 66.67% branches, and 100% functions, with no uncovered lines. The lcov reporter also emits DA:5,1. This overstates execution of a branch that the test never takes.

This reproduces with a single test process and a precompiled module. The example was generated using esbuild 0.28.2, but the reproduction above does not require esbuild. Related source-map coverage reports include #65946 and #67077; those describe off-thread hooks or merging multiple test processes, while this case needs neither.

Activity

  1. fatihguzeldev commented on Oct 31, 2026

    @fatihguzeldev

    i've been looking into this and have a local change that fixes the repro. the mapped zero-count range stops before the end of the return line, so the line stays covered.

    i found the same problem with an uncalled function too, plus a few other boundary cases with sparse maps. trying to cover those also broke some reports that were previously correct.

    i'm still working out whether a small endpoint fix is enough here or the other boundary cases need to be addressed in the same change.

  2. added
    test_runnerIssues and PRs related to the test runner subsystem.
    on Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    test_runnerIssues and PRs related to the test runner subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions