coverlet icon indicating copy to clipboard operation
coverlet copied to clipboard

[BUG] Code coverage empty on NET8.0 WPF project

Open modmynitro opened this issue 1 year ago • 8 comments

Describe the bug Code Coverage on NET8.0 WPF project is empty.

To Reproduce Create a .NET8 WPF project and refrence an enum value out of 'PresentationFramework' as default value.

public class Class
{
    public void Method(MessageBoxButton button = MessageBoxButton.OK)
    {
    }
}

Create a test project with an arbitrary test and run dotnet test --configuration Debug --collect:"XPlat Code Coverage".

Expected behavior Code coverage should not be empty.

Actual behavior The resulting coverage is empty.

When executing: dotnet test .\TestProject.sln --configuration Debug --collect:"XPlat Code Coverage" --diag:log.txt

The log.datacollector.***.txt contains following exception:

Coverlet.Core.Exceptions.CecilAssemblyResolutionException: AssemblyResolutionException for 'PresentationFramework, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. Try to add <PreserveCompilationContext>true</PreserveCompilationContext> to test projects </PropertyGroup> or pass '/p:CopyLocalLockFileAssemblies=true' option to the 'dotnet test' command-line
 ---> Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'PresentationFramework, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
   --- End of inner exception stack trace ---
   at Coverlet.Core.Instrumentation.NetstandardAwareAssemblyResolver.TryWithCustomResolverOnDotNetCore(AssemblyNameReference name) in /_/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs:line 217
   at Coverlet.Core.Instrumentation.NetstandardAwareAssemblyResolver.Resolve(AssemblyNameReference name) in /_/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs:line 129
   at Mono.Cecil.MetadataResolver.Resolve(TypeReference type)
   at Mono.Cecil.ModuleDefinition.Resolve(TypeReference type)
   at Mono.Cecil.TypeReference.Resolve()
   at Mono.Cecil.Mixin.CheckedResolve(TypeReference self)
   at Mono.Cecil.MetadataBuilder.GetConstantType(TypeReference constant_type, Object constant)
   at Mono.Cecil.MetadataBuilder.AddConstant(IConstantProvider owner, TypeReference type)
   at Mono.Cecil.MetadataBuilder.AddParameter(UInt16 sequence, ParameterDefinition parameter, ParamTable table)
   at Mono.Cecil.MetadataBuilder.AddParameters(MethodDefinition method)
   at Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method)
   at Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type)
   at Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type)
   at Mono.Cecil.MetadataBuilder.AddTypes()
   at Mono.Cecil.MetadataBuilder.BuildTypes()
   at Mono.Cecil.MetadataBuilder.BuildModule()
   at Mono.Cecil.MetadataBuilder.BuildMetadata()
   at Mono.Cecil.ModuleWriter.<>c.<BuildMetadata>b__2_0(MetadataBuilder builder, MetadataReader _)
   at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read)
   at Mono.Cecil.ModuleWriter.BuildMetadata(ModuleDefinition module, MetadataBuilder metadata)
   at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
   at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
   at Mono.Cecil.ModuleDefinition.Write(Stream stream, WriterParameters parameters)
   at Coverlet.Core.Instrumentation.Instrumenter.InstrumentModule() in /_/src/coverlet.core/Instrumentation/Instrumenter.cs:line 325
   at Coverlet.Core.Instrumentation.Instrumenter.Instrument() in /_/src/coverlet.core/Instrumentation/Instrumenter.cs:line 148
   at Coverlet.Core.Coverage.PrepareModules() in /_/src/coverlet.core/Coverage.cs:line 134

Configuration (please complete the following information): Please provide more information on your .NET configuration: * Which coverlet package and version was used? => 6.0.2 also tested 6.0.3-preview.17.g302886a430 * Which version of .NET is the code running on? => net8.0-windows and net9.0-windows * What OS and version, and what distro if applicable? => Microsoft Windows 10 Enterprise 19045 * What is the architecture (x64, x86, ARM, ARM64)? => x64 * Do you know whether it is specific to that configuration? => no

Additional context Adding <PreserveCompilationContext>true</PreserveCompilationContext> did not help. Adding /p:CopyLocalLockFileAssemblies=true did also not help.

:exclamation: Please also read Known Issues

modmynitro avatar Nov 27 '24 11:11 modmynitro

Please provide a repo for analysis. I not able to reproduce the exception.

Bertk avatar Dec 07 '24 09:12 Bertk

I have created a repo for this issue. https://github.com/modmynitro/coverlet-test

modmynitro avatar Dec 09 '24 07:12 modmynitro

Thank you for the repo. I updated the target framework and used net48 which solved the problem.

steps:

  • dotnet build
  • dotnet publish -c debug
  • dotnet test --configuration Debug --collect:"XPlat Code Coverage" --diag:log.txt

TestProject\TestProject.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net48</TargetFramework>
        <UseWpf>true</UseWpf>
        <LangVersion>10.0</LangVersion>
    </PropertyGroup>

</Project>

TestProjectTests\TestProjectTests.csproj

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

  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
    <LangVersion>10.0</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\TestProject\TestProject.csproj"/>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="NUnit" Version="4.2.2"/>
    <PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
    <PackageReference Include="coverlet.collector" Version="6.0.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

coverage.cobertura.xml

<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="1" branch-rate="1" version="1.9" timestamp="1733822473" lines-covered="2" lines-valid="2" branches-covered="0" branches-valid="0">
  <sources>
    <source>C:\</source>
  </sources>
  <packages>
    <package name="TestProject" line-rate="1" branch-rate="1" complexity="1">
      <classes>
        <class name="TestProject.Class" filename="GitHub\coverlet-issue-1713\TestProject\Class.cs" line-rate="1" branch-rate="1" complexity="1">
          <methods>
            <method name="Method" signature="(System.Windows.MessageBoxButton)" line-rate="1" branch-rate="1" complexity="1">
              <lines>
                <line number="8" hits="1" branch="False" />
                <line number="9" hits="1" branch="False" />
              </lines>
            </method>
          </methods>
          <lines>
            <line number="8" hits="1" branch="False" />
            <line number="9" hits="1" branch="False" />
          </lines>
        </class>
      </classes>
    </package>
  </packages>
</coverage>

Bertk avatar Dec 10 '24 09:12 Bertk

I want to use net8.0 not .NET Framework so this does not help in my situation.

modmynitro avatar Dec 10 '24 10:12 modmynitro

OK. I will investigate (debug) this issue in the next days.

Bertk avatar Dec 11 '24 09:12 Bertk

The test project targeting net8.0-windows need the property <SelfContained>true</SelfContained> which resolves the CecilAssemblyResolutionException.

Unfortunately this is not documented anywhere but a hint shall be added for coverlet.msbuild and coverlet.collector.

Solution:

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

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <LangVersion>10.0</LangVersion>
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <SelfContained>true</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\TestProject\TestProject.csproj"/>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="NUnit" Version="4.2.2"/>
    <PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
    <PackageReference Include="coverlet.collector" Version="6.0.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

Results:

C:\GitHub\coverlet-issue-1713>dotnet build
  Determining projects to restore...
  All projects are up-to-date for restore.
  TestProject -> C:\GitHub\coverlet-issue-1713\TestProject\bin\Debug\net8.0-windows\TestProject.dll
  wpf-template -> C:\GitHub\coverlet-issue-1713\wpf-template\bin\Debug\net8.0-windows\wpf-template.dll
  TestProjectTests -> C:\GitHub\coverlet-issue-1713\TestProjectTests\bin\Debug\net8.0-windows\win-x64\TestProjectTests.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:04.31

C:\GitHub\coverlet-issue-1713>dotnet test --configuration Debug --collect:"XPlat Code Coverage" --diag:log.txt
  Determining projects to restore...
  All projects are up-to-date for restore.
  TestProject -> C:\GitHub\coverlet-issue-1713\TestProject\bin\Debug\net8.0-windows\TestProject.dll
  TestProjectTests -> C:\GitHub\coverlet-issue-1713\TestProjectTests\bin\Debug\net8.0-windows\win-x64\TestProjectTests.dll
Test run for C:\GitHub\coverlet-issue-1713\TestProjectTests\bin\Debug\net8.0-windows\win-x64\TestProjectTests.dll (.NETCoreApp,Version=v8.0)
VSTest version 17.11.1-release-24455-02 (x64)

Starting test execution, please wait...
Logging Vstest Diagnostics in file: C:\GitHub\coverlet-issue-1713\log.txt
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: 6 ms - TestProjectTests.dll (net8.0)

Attachments:
  C:\GitHub\coverlet-issue-1713\TestProjectTests\TestResults\abf916bc-6eea-4b68-9a2f-7430eea846e1\coverage.cobertura.xml

C:\GitHub\coverlet-issue-1713>type C:\GitHub\coverlet-issue-1713\TestProjectTests\TestResults\abf916bc-6eea-4b68-9a2f-7430eea846e1\coverage.cobertura.xml
<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="1" branch-rate="1" version="1.9" timestamp="1734340041" lines-covered="2" lines-valid="2" branches-covered="0" branches-valid="0">
  <sources>
    <source>C:\</source>
  </sources>
  <packages>
    <package name="TestProject" line-rate="1" branch-rate="1" complexity="1">
      <classes>
        <class name="TestProject.Class" filename="GitHub\coverlet-issue-1713\TestProject\Class.cs" line-rate="1" branch-rate="1" complexity="1">
          <methods>
            <method name="Method" signature="(System.Windows.MessageBoxButton)" line-rate="1" branch-rate="1" complexity="1">
              <lines>
                <line number="11" hits="1" branch="False" />
                <line number="12" hits="1" branch="False" />
              </lines>
            </method>
          </methods>
          <lines>
            <line number="11" hits="1" branch="False" />
            <line number="12" hits="1" branch="False" />
          </lines>
        </class>
      </classes>
    </package>
  </packages>
</coverage>
C:\GitHub\coverlet-issue-1713>

Bertk avatar Dec 16 '24 09:12 Bertk

I can use this solution as workaround, but we only use dotnet test on our gitlab runners and this solution forces any local build to be self contained.

I would expect the runtime assemblies to be found automatically.

modmynitro avatar Dec 17 '24 11:12 modmynitro

Does this work around work for custom control libraries? My understanding of the <SelfContained/> tag is that it is only for application projects to bundle all the dependencies into a single executable. Which isn't relevant if just writing a custom control library for WPF with tests.

This seems like something that should work, without having to change the project file in anyway. @Bertk The project file you posted in your solution is very different to the one provided by @modmynitro.

ChrisVicary avatar Aug 09 '25 05:08 ChrisVicary