Coverlet: Incorrect branch coverage with await using

Created on 29 Jul 2020  路  8Comments  路  Source: coverlet-coverage/coverlet

If use 'using' statement only branch coverate is 100%
image

If use 'await using' branch coverage is 50%
image
But they should be the same

bug tenet-coverage with repro

Most helpful comment

We have the same issue and I've prepared a simple repro. Hope it will be useful

    public class TestClass
    {
        public async Task Example1()
        {
            await using var trans = new MyTransaction();
        }

        public async Task Example2()
        {
            var trans = new MyTransaction();
            await trans.DisposeAsync();
        }
    }

    public class MyTransaction : IAsyncDisposable
    {
        public async ValueTask DisposeAsync()
        {
        }
    }

    public class UnitTest1
    {
        [Fact]
        public async Task Test1()
        {
            var c = new TestClass();
            await c.Example1().ConfigureAwait(false);
        }

        [Fact]
        public async Task Test2()
        {
            var c = new TestClass();
            await c.Example2().ConfigureAwait(false);
        }
    }

Expected behavior: treat TestClass as having 100% test coverage both by lines and by conditions.

Current behavior:
This line has 5 out of 10 branches covered:

            await using var trans = new MyTransaction();

This line has 1 of 2 branches covered:

            await trans.DisposeAsync();

The command line I use is

dotnet test --configuration Release /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=./TestResults/

Target framework is netcoreapp3.1
Coverlet.MsBuild version is 2.9.0

The coverage report is attached.
coverage.opencover.xml.log

All 8 comments

Which version are you using?

Out of curiosity, I checked whether my attempted fix of #907 would also address this. It appears the answer to that is "no". I started with this example:

public class AwaitUsing
{
    async public ValueTask AsyncAwait()
    {
        await using (var ms = new MemoryStream(System.Text.Encoding.ASCII.GetBytes("abc")))
        {
        }
    }
}

I tried this test (similar to what I added in #907, according to patterns of what was already there):

[Fact]
public void GetBranchPoints_IgnoresBranchesIn_AwaitUsing()
{
    // arrange
    var nestedName = typeof(AwaitUsing).GetNestedTypes(BindingFlags.NonPublic).First().Name;
    var type = _module.Types.FirstOrDefault(x => x.FullName == typeof(AwaitUsing).FullName);
    var nestedType = type.NestedTypes.FirstOrDefault(x => x.FullName.EndsWith(nestedName));
    var method = nestedType.Methods.First(x => x.FullName.EndsWith("::MoveNext()"));

    // act
    var points = _cecilSymbolHelper.GetBranchPoints(method);

    // assert
    Assert.Empty(points);
}

Without my #907 fix, there were eight branch points found. With my #907 fix, there were six branch points found.

So, at the very least, we know these are separate issues. Whether I'll be able to figure out what the issue _is_, well, that's a different story, but I'm just finding my way around this code base for the first time.

I took a crack at this one today, too. The #914 fix depends on the #907 fix, but I'll create a separate pull request for the #914 fix. (I've combined the two fixes into one pull request, since they're on the same branch.)

Working in Debug mode, but not Release mode. Live and learn! I'll investigate.

Fixed (I hope!) in 61e0d9f.

We have the same issue and I've prepared a simple repro. Hope it will be useful

    public class TestClass
    {
        public async Task Example1()
        {
            await using var trans = new MyTransaction();
        }

        public async Task Example2()
        {
            var trans = new MyTransaction();
            await trans.DisposeAsync();
        }
    }

    public class MyTransaction : IAsyncDisposable
    {
        public async ValueTask DisposeAsync()
        {
        }
    }

    public class UnitTest1
    {
        [Fact]
        public async Task Test1()
        {
            var c = new TestClass();
            await c.Example1().ConfigureAwait(false);
        }

        [Fact]
        public async Task Test2()
        {
            var c = new TestClass();
            await c.Example2().ConfigureAwait(false);
        }
    }

Expected behavior: treat TestClass as having 100% test coverage both by lines and by conditions.

Current behavior:
This line has 5 out of 10 branches covered:

            await using var trans = new MyTransaction();

This line has 1 of 2 branches covered:

            await trans.DisposeAsync();

The command line I use is

dotnet test --configuration Release /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=./TestResults/

Target framework is netcoreapp3.1
Coverlet.MsBuild version is 2.9.0

The coverage report is attached.
coverage.opencover.xml.log

Thanks for the repro

Any forecast when could this be fixed?

I've just literally stumbled upon same issue, that using await using creates multiple branches, and google brought me here :)

Was this page helpful?
0 / 5 - 0 ratings