eth-brownie / brownie

A Python-based development and testing framework for smart contracts targeting the Ethereum Virtual Machine.
https://eth-brownie.readthedocs.io
MIT License
2.63k stars 549 forks source link

Coverage reports do not cover all contracts #1087

Open Quaid-Doug opened 3 years ago

Quaid-Doug commented 3 years ago

Environment information

What was wrong?

Please include information like:

I have 3 contracts that work in interaction with each others.

My testing script is on one file.

When I run brownie test --coverage I get a coverage report for only one of the contracts. I'd like to get it for all of the contracts.

bainjamain commented 3 years ago

I found this issue to be quite random. Sometimes the coverages covers all my contracts, but more often than not, it covers only a small subset of the contracts tested, sometimes none.

igponce commented 3 years ago

Same issue here with:

I've got coverage for the fist contract I deploy. I tried changing the contract names to see if alphabetical of contract names was a factor, but no.

evoh-nft commented 3 years ago

Same problem here. I've noticed that when using solc 0.8.3 I get a normal coverage report, but with 0.8.4 it comes back empty.

Psirex commented 3 years ago

I found, that it happens with contracts that use the immutable modifier. Maybe because of this:

"The contract creation code generated by the compiler will modify the contract’s runtime code before it is returned by replacing all references to immutables by the values assigned to them. This is important if you are comparing the runtime code generated by the compiler with the one actually stored in the blockchain."

as mentioned in the docs here. But I'm not sure that it's the only reason which causes such behavior.

vsmelov commented 1 year ago

This bug is still active? I cannot use immutable in my contracts if I want to check test coverage?

iurii2002 commented 1 year ago

Yes, the bug is still active

FlavorlessQuark commented 1 year ago

I'm having the same issue. I'm using two contracts, and while they both show up in the coverage, it doesn't show all the functions in the contracts.

Bulalu commented 1 year ago

still having the same issue 😢

jacekv commented 1 year ago

I ran into the same issue using Brownie v1.18.1.

What @Psirex wrote seems to be the only problem. I have some contracts where only one state variable is set to immutable, which leads to an empty coverage report.

As soon I remove immutable from the state variable, it works.

jacekv commented 1 year ago

Actually I was wrong. I had been playing with it a bit more and realized that something seems to be broken.

I tried it with an example project: brownie bake token The coverage report says 100% but does not identify all functions. The approve is not part of the report for some reason.

jacekv commented 1 year ago

I spend some time looking into why the immutable keyword is causing problems, but I was not able to find a solution to the problem, since it would take me more time and resources to understand how browie works under the hood.

I was able to see that there seems to be an issue in the brownie//network/transaction.py - _expand_trace function.

If I saw it correct, brownie takes the local compiled files, execute the tests, and trace based on the opcodes.

But there is the following try-except block:

try:
    pc = last["pc_map"][trace[i]["pc"]]
except (KeyError, TypeError):
   # we don't have enough information about this contract
   continue

I was wondering why but I believe that the difference is the bytecode. If you deploy the following contract to chain:

pragma solidity ^0.8.12;

contract Contract1 {
    uint private i1;

    constructor(uint _i1){
        i1=_i1;
    }

    function getValue() public view returns (uint) {
        return i1;
    }
}

You will be able to get the bytecode from the compiler. But if you use the immutable keyword in the code:

pragma solidity ^0.8.12;

contract Contract1 {
    uint private immutable i1;

    constructor(uint _i1){
        i1=_i1;
    }

    function getValue() public view returns (uint) {
        return i1;
    }
}

the bytecode deployed as contract differs from the one you get from the Solidity compiler.

If I am wrong about it, let me know :)

Hope it might help the next dev to go deeper.