foundry icon indicating copy to clipboard operation
foundry copied to clipboard

bug(forge): coverage does not work for enum assignments

Open PaulRBerg opened this issue 2 years ago • 1 comments

Component

Forge

Have you ensured that all of these are up to date?

  • [X] Foundry
  • [X] Foundryup

What version of Foundry are you on?

forge 0.2.0 (249538f 2023-02-08T00:12:05.805004Z)

What command(s) is the bug in?

forge coverage

Operating System

macOS (Apple Silicon)

Describe the bug

Take the following contract (hidden by default for brevity reasons, click the toggle below to collapse it):

Click me to toggle the contract
contract Foo {
    enum Status {
        NULL,
        OPEN,
        CLOSED
    }

    struct Item {
        Status status;
        uint256 value;
    }

    mapping(uint256 => Item) internal items;
    uint256 public nextId = 1;

    function getItem(uint256 id) public view returns (Item memory item) {
        item = items[id];
    }

    function addItem(uint256 value) public returns (uint256 id) {
        id = nextId;
        items[id] = Item(Status.OPEN, value);
        nextId++;
    }

    function closeIfEqValue(uint256 id, uint256 value) public {
        if (items[id].value == value) {
            items[id].status = Status.CLOSED;
        }
    }

    function incrementIfEqValue(uint256 id, uint256 value) public {
        if (items[id].value == value) {
            items[id].value = value + 1;
        }
    }
}

And the following tests (again, hidden for brevity):

Click me to toggle the tests
contract FooTest is Test {
    Foo internal foo = new Foo();

    function test_AddItem() external {
        uint256 value = 42;
        uint256 id = foo.addItem(value);
        assertEq(id, 1);
        assertEq(foo.nextId(), 2);

        Foo.Item memory item = foo.getItem(id);
        assertEq(uint8(item.status), uint8(Foo.Status.OPEN));
        assertEq(item.value, value);
    }

    function test_CloseIfEqValue_NotEq() external {
        uint256 value = 42;
        uint256 id = foo.addItem(value);
        foo.closeIfEqValue(id, 903);

        Foo.Item memory item = foo.getItem(id);
        assertEq(uint8(item.status), uint8(Foo.Status.OPEN));
    }

    function test_CloseIfEqValue_Eq() external {
        uint256 value = 42;
        uint256 id = foo.addItem(value);
        foo.closeIfEqValue(id, 42);

        Foo.Item memory item = foo.getItem(id);
        assertEq(uint8(item.status), uint8(Foo.Status.CLOSED));
    }

    function test_IncrementIfEqValue_NotEq() external {
        uint256 value = 42;
        uint256 id = foo.addItem(value);
        foo.incrementIfEqValue(id, 903);

        Foo.Item memory item = foo.getItem(id);
        assertEq(item.value, 42);
    }

    function test_IncrementIfEqValue_Eq() external {
        uint256 value = 42;
        uint256 id = foo.addItem(value);
        foo.incrementIfEqValue(id, 42);

        Foo.Item memory item = foo.getItem(id);
        assertEq(item.value, 43);
    }
}

Now, run forge coverage. You will get this report:

| File        | % Lines       | % Statements  | % Branches   | % Funcs       |
|-------------|---------------|---------------|--------------|---------------|
| src/Foo.sol | 100.00% (8/8) | 100.00% (8/8) | 75.00% (3/4) | 100.00% (4/4) |
| Total       | 100.00% (8/8) | 100.00% (8/8) | 75.00% (3/4) | 100.00% (4/4) |

Notice that the branch coverage is 75% even if we do have full coverage of all the possible branches of the closeIfEqValue function:

Screenshot 2023-02-09 at 11 05 52 AM

By contrast, the coverage for the incrementIfEqValue function is 100%, which shows that this bug is specifically about enum assignments. closeIfEqValue and incrementIfEqValue are fully equivalent, except for the enum assignment.

PaulRBerg avatar Feb 09 '23 09:02 PaulRBerg

Able to reproduce, now still yields:

| File        | % Lines       | % Statements  | % Branches   | % Funcs       |
|-------------|---------------|---------------|--------------|---------------|
| src/Foo.sol | 100.00% (8/8) | 100.00% (8/8) | 75.00% (3/4) | 100.00% (4/4) |
| Total       | 100.00% (8/8) | 100.00% (8/8) | 75.00% (3/4) | 100.00% (4/4) |

zerosnacks avatar Jun 28 '24 15:06 zerosnacks