coverlet-coverage / coverlet

Cross platform code coverage for .NET
MIT License
2.93k stars 385 forks source link

[BUG] Records Result in No Coverage #1607

Closed aolszowka closed 4 months ago

aolszowka commented 5 months ago

Describe the bug Maybe a duplicate of #1576 ? We've been seeing this since we really started looking at our coverage last December but finally got around to a bug report today...

It appears that no coverage is detected for record types?

To Reproduce Consider the following toy program (Including Azure DevOps Pipeline):

SingleFieldRecord.cs

namespace CodeCoverageSingleFieldNotCovered
{
    public record SingleFieldRecord
    {
        public int Field { get; init; }
    }
}

SingleFieldRecordTests.cs

using Xunit;

namespace CodeCoverageSingleFieldNotCovered
{
    public class SingleFieldRecordTests
    {
        [Fact]
        public void SingleFieldRecordTest()
        {
            var record = new SingleFieldRecord { Field = 1 };
            Assert.Equal(1, record.Field);
        }
    }
}

CodeCoverageSingleFieldNotCovered.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="coverlet.collector" Version="6.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
    <PackageReference Include="xunit" Version="2.6.6" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

CodeCoverageSingleFieldNotCovered.sln


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCoverageSingleFieldNotCovered", "CodeCoverageSingleFieldNotCovered.csproj", "{D4A76F12-5C84-40C5-A40F-24BE789A45C6}"
EndProject
Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
                Release|Any CPU = Release|Any CPU
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
                {D4A76F12-5C84-40C5-A40F-24BE789A45C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {D4A76F12-5C84-40C5-A40F-24BE789A45C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {D4A76F12-5C84-40C5-A40F-24BE789A45C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {D4A76F12-5C84-40C5-A40F-24BE789A45C6}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {E42E1129-8625-4A6D-903E-31E57FA31E6F}
        EndGlobalSection
EndGlobal

azurepipeline.yml

trigger:
  branches:
    include:
    - main

pool:
  vmImage: ubuntu-latest

steps:
  - task: DotNetCoreCLI@2
    displayName: 'dotnet test'
    inputs:
      command: 'test'
      projects: CodeCoverageSingleFieldNotCovered.sln
      arguments: '--collect:"XPlat Code Coverage"'
      publishTestResults: true

  - task: PublishCodeCoverageResults@1
    displayName: 'publish code coverage (dotnet)'
    inputs:
      codeCoverageTool: 'Cobertura'
      summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
      failIfCoverageEmpty: true

Expected behavior When you run this in Azure DevOps Pipeline you'd expect the coverage to get published and it show as 100%

Actual behavior Running the above in Azure DevOps the publish task will fail.

If you crack open the coverage.cobertura.xml you'll see that no coverage was reported:

<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="0" branch-rate="0" version="1.9" timestamp="1706904136" lines-covered="0" lines-valid="0" branches-covered="0" branches-valid="0">
  <sources />
  <packages />
</coverage>

You can reproduce this locally (No need for Azure DevOps Pipeline) with:

dotnet test --collect:"XPlat Code Coverage"
daveMueller commented 5 months ago

Not sure, at least in your example I'm pretty sure it is something different. You have the unit test and the record under test in the same assembly. This doesn't work. coverlet excludes the test assembly by default and thus in your example the coverage report is completely empty. When you separate the record into another assembly, the current result with coverlet version 6.0.0 and the most current compiler looks like this:

grafik

The roslyn team changed something in the compiler for records with the release of net8 and that's why in my screenshot you see this uncovered line. If you run an older version of the compiler you probably don't see this. However we already fixed this with this PR https://github.com/coverlet-coverage/coverlet/pull/1575 which can be consumed with our nightly build.

grafik

aolszowka commented 5 months ago

@daveMueller Yikes, sorry in my haste to create a minimum viable reproducing test case I didn't realize that coverlet excludes the test assembly.

The second part of your comment (the uncovered line) is exactly what we're seeing "in production" and why we were reporting. The fix with #1575 looks like what we want to do, on Monday I'll upgrade to the nightly build and see if that fixes our wagon.

Thank you so much for following up on this so quickly!

aolszowka commented 4 months ago

@daveMueller I can confirm that the latest nightly fixes the issue "in production" we'll look forward to the latest NuGet Release, thank you again.

Closing as resolved.