Skip to content

Coverage map issued on switch statements without scoped case clauses incorrectly report an uncovered block #53652

Open
@ericmorand

Description

Version

v20.13.0

Platform

Linux 6.5.0-35-generic #35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May  7 09:00:52 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

Consider the following scripts:

  • lib.mjs
export const foo = (value) => {
    switch (value) {
        case true:
            const result = 5;
            return result;
        default:
            return 10;
    }
};
  • index.mjs
import {foo} from "./lib.mjs";

foo(false);
foo(true);

Executing the index.mjs script with node alongside the NODE_V8_COVERAGE environment variable, the output coverage map incorrectly shows the line 9 of lib.mjs (};) as uncovered.

NODE_V8_COVERAGE=./coverage node index.mjs

The coverage map file, with only the two relevant scripts for readability:

{
  "result": [
    {
      "scriptId": "100",
      "url": "file:///home/ericmorand/Projects/one-hundred-cases/src/switch-without-scoped-cases/index.mjs",
      "functions": [
        {
          "functionName": "",
          "ranges": [
            {
              "startOffset": 0,
              "endOffset": 54,
              "count": 1
            }
          ],
          "isBlockCoverage": true
        }
      ]
    },
    {
      "scriptId": "101",
      "url": "file:///home/ericmorand/Projects/one-hundred-cases/src/switch-without-scoped-cases/lib.mjs",
      "functions": [
        {
          "functionName": "",
          "ranges": [
            {
              "startOffset": 0,
              "endOffset": 178,
              "count": 1
            }
          ],
          "isBlockCoverage": true
        },
        {
          "functionName": "foo",
          "ranges": [
            {
              "startOffset": 19,
              "endOffset": 176,
              "count": 2
            },
            {
              "startOffset": 61,
              "endOffset": 128,
              "count": 1
            },
            {
              "startOffset": 137,
              "endOffset": 168,
              "count": 1
            },
            {
              "startOffset": 174,
              "endOffset": 175,
              "count": 0
            }
          ],
          "isBlockCoverage": true
        }
      ]
    }
  ]
}

How often does it reproduce? Is there a required condition?

It happens only when using either:

  • a variable assignment that is immediately returned

The following variant of lib.mjs is not impacted:

export const foo = (value) => {
    switch (value) {
        case true:
            return 5;
        default:
            return 10;
    }
};
  • unscoped case clauses

The following variant of lib.mjs is not impacted:

export const foo = (value) => {
    switch (value) {
        case true: {
            const result = 5;
            return result;
        }
        default: {
            return 10;
        }
    }
};

What is the expected behavior? Why is that the expected behavior?

The entirety of lib.mjs should be shown as covered.

What do you see instead?

The range 174:175 is considered as uncovered:

{
  "startOffset": 174,
  "endOffset": 175,
  "count": 0
}

Additional information

This issues has been impacting c8, among other code coverage tools:

bcoe/c8#497

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    coverageIssues and PRs related to native coverage support.v8 engineIssues and PRs related to the V8 dependency.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions