Skip to content

Unexpected branch coverage with using statement and several awaits #1176

@strout

Description

@strout

I've encountered this with stable coverlet.collector as well as nightly 3.0.4-preview.31.g4902c245c8. I'm using .NET SDK 5.0.300 and running dotnet test --collect:"XPlat Code Coverage".

This code has 3 out of 6 blocks covered despite having no branches apart from awaits. Removing or reordering any of the statements results in 100% coverage and cyclomatic complexity of 1 instead of 6. I would expect this code to also have cyclomatic complexity of 1 and to be fully covered.

using System.IO;
using System.Threading.Tasks;

namespace Lib
{
    public class Foo
    {
        public static async Task Bar()
        {
            await Task.CompletedTask;
            using var _ = new MemoryStream();
            await Task.CompletedTask;
            await Task.CompletedTask;
            await Task.CompletedTask;
        }
    }
}

The report shows the using statement has 1 of 4 branches covered.

image

<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="1" branch-rate="0.5" version="1.9" timestamp="1623164584" lines-covered="7" lines-valid="7" branches-covered="3" branches-valid="6">
  <sources>
    <source>C:\</source>
  </sources>
  <packages>
    <package name="Lib" line-rate="1" branch-rate="0.5" complexity="6">
      <classes>
        <class name="Lib.Foo/&lt;Bar&gt;d__0" filename="\tmp\Lib\Foo.cs" line-rate="1" branch-rate="0.5" complexity="6">
          <methods>
            <method name="MoveNext" signature="()" line-rate="1" branch-rate="0.5" complexity="6">
              <lines>
                <line number="9" hits="1" branch="True" condition-coverage="100% (2/2)">
                  <conditions>
                    <condition number="16" type="jump" coverage="100%" />
                  </conditions>
                </line>
                <line number="10" hits="1" branch="False" />
                <line number="11" hits="1" branch="True" condition-coverage="25% (1/4)">
                  <conditions>
                    <condition number="135" type="switch" coverage="25%" />
                  </conditions>
                </line>
                <line number="12" hits="1" branch="False" />
                <line number="13" hits="1" branch="False" />
                <line number="14" hits="1" branch="False" />
                <line number="15" hits="1" branch="False" />
              </lines>
            </method>
          </methods>
          <lines>
            <line number="9" hits="1" branch="True" condition-coverage="100% (2/2)">
              <conditions>
                <condition number="16" type="jump" coverage="100%" />
              </conditions>
            </line>
            <line number="10" hits="1" branch="False" />
            <line number="11" hits="1" branch="True" condition-coverage="25% (1/4)">
              <conditions>
                <condition number="135" type="switch" coverage="25%" />
              </conditions>
            </line>
            <line number="12" hits="1" branch="False" />
            <line number="13" hits="1" branch="False" />
            <line number="14" hits="1" branch="False" />
            <line number="15" hits="1" branch="False" />
          </lines>
        </class>
      </classes>
    </package>
  </packages>
</coverage>

I used this test class.

using System.Threading.Tasks;
using Xunit;

namespace Test
{
    public class FooTest
    {
        [Fact]
        public async Task Test()
        {
            await Lib.Foo.Bar();
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    tenet-coverageIssue related to possible incorrect coverageuntriagedTo be investigated

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions