Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partially covered await foreach statement #1104

Closed
meggima opened this issue Mar 2, 2021 · 4 comments
Closed

Partially covered await foreach statement #1104

meggima opened this issue Mar 2, 2021 · 4 comments
Labels
tenet-coverage Issue related to possible incorrect coverage

Comments

@meggima
Copy link

meggima commented Mar 2, 2021

Hello

In the following code the await foreach as well as the for and the yield statements are marked as partially covered due to the hidden async/await logic of the compiler. Can this be reported as covered by coverlet?

Reproduction class:

public class AwaitForeachReproduction
{
    public async Task<int> Execute()
    {
        int sum = 0;

        await foreach (int result in AsyncEnumerable())
        {
            sum += result;
        }

        return sum;
    }

    private async IAsyncEnumerable<int> AsyncEnumerable()
    {
        for(int i = 0; i < 100; i++)
        {
            await Task.Delay(10);
            yield return i;
        }
    }
}

Test Case for it:

public class AwaitForeachReproductionFixture
{
    [Fact]
    public async Task Execute_ShouldWork()
    {
        // Arrange
        var sut = new AwaitForeachReproduction();

        // Act
        int result = await sut.Execute();

        // Assert
        Assert.Equal(4950, result);
    }
}

Reported coverage:
image

Coverlet version: 3.0.3

@meggima
Copy link
Author

meggima commented Mar 2, 2021

You can find the full reproduction code (and also for some other cases) in this repository: https://github.com/meggima/coverlet-reproductions

@daveMueller daveMueller added the untriaged To be investigated label Mar 3, 2021
alexthornton1 added a commit to alexthornton1/coverlet that referenced this issue Mar 4, 2021
This commit attempts to fix code coverage for the "await foreach"
loop introduced in C# 8.  The presence of an "await foreach" loop
causes four new kinds of branch patterns to be generated in the
async state machine.  Three of these are to be eliminated, while
the fourth is actually the "should I stay in the loop or not?"
branch and is best left in a code coverage report.

CecilSymbolHelper now eliminates the three branch patterns that
need to be eliminated.  There are tests both in CecilSymbolHelperTests
as well as a new set of coverage tests in CoverageTests.AsyncForeach.cs.
alexthornton1 added a commit to alexthornton1/coverlet that referenced this issue Mar 4, 2021
This commit attempts to fix code coverage for the "await foreach"
loop introduced in C# 8.  The presence of an "await foreach" loop
causes four new kinds of branch patterns to be generated in the
async state machine.  Three of these are to be eliminated, while
the fourth is actually the "should I stay in the loop or not?"
branch and is best left in a code coverage report.

CecilSymbolHelper now eliminates the three branch patterns that
need to be eliminated.  There are tests both in CecilSymbolHelperTests
as well as a new set of coverage tests in CoverageTests.AsyncForeach.cs.
alexthornton1 added a commit to alexthornton1/coverlet that referenced this issue Mar 6, 2021
Fixes coverage for compiler-generated async iterators that arise from
async methods that use the "yield" statement to return one element
at a time asynchronously via an IAsyncEnumerable<T>.  In combination
with the previous commit that handles the "async foreach" loop, this
should address issue coverlet-coverage#1104.
@MarcoRossignoli MarcoRossignoli added tenet-coverage Issue related to possible incorrect coverage and removed untriaged To be investigated labels Mar 6, 2021
@MarcoRossignoli
Copy link
Collaborator

assigned to @alexthornton1

MarcoRossignoli pushed a commit that referenced this issue Mar 6, 2021
…tors (issue #1104) (#1107)

Coverage for "await foreach" loops and compiler-generated async iterators (issue #1104) (#1107)
@MarcoRossignoli
Copy link
Collaborator

Closed in #1107

pbmiguel pushed a commit to pbmiguel/coverlet that referenced this issue Mar 17, 2021
@ipetrushevskiy
Copy link

ipetrushevskiy commented Jan 5, 2022

If you change the AsyncEnumerable method in the original example like that (by adding attribute for cancellation Token support - code below), it will be still partially covered.

private async IAsyncEnumerable<int> AsyncEnumerable([EnumeratorCancellation] CancellationToken cancellationToken)
{
    for(int i = 0; i < 100; i++)
    {
        await Task.Delay(10, cancellationToken);
        yield return i;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tenet-coverage Issue related to possible incorrect coverage
Projects
None yet
Development

No branches or pull requests

4 participants