danielpalme / ReportGenerator

ReportGenerator converts coverage reports generated by coverlet, OpenCover, dotCover, Visual Studio, NCover, Cobertura, JaCoCo, Clover, gcov or lcov into human readable reports in various formats.
https://reportgenerator.io
Apache License 2.0
2.62k stars 283 forks source link

Async methods show `MoveNext()` instead of the real method name #590

Closed bramborman closed 1 year ago

bramborman commented 1 year ago

When using either dotnet-coverage or dotnet test --collect "Code Coverage" with output format Cobertura, I get MoveNext() method name instead of the real name for async methods when generating the report.

I digged into this and found those tools use a slightly different syntax for the class name, which the ReportGenerator does not expect, and thus it fails to parse it:

Coverlet output:

MyNamespace.MyClass/<MyAsyncMethod>d__5

Output of these tools:

MyNamespace.MyClass.<MyAsyncMethod>d__5

Note the dot instead of a slash after MyClass

So basically, if it wouldn't break anything else, the ReGex here should accept a dot as well, and it would work: https://github.com/danielpalme/ReportGenerator/blob/f396a7440f73215e7daa97309972f742ae005878/src/ReportGenerator.Core/Parser/CoberturaParser.cs#L39

Sample commands for repro with each tool:

dotnet test --collect "Code Coverage;Format=Cobertura"
dotnet coverage collect -f cobertura dotnet test

Please note that the --collect "Code Coverage" way is now fully cross-platform, so I believe its use will increase over time, as it's built-in the .NET SDK.

I'm using dotnet-reportgenerator-globaltool v5.1.17.

danielpalme commented 1 year ago

Thanks for your issue. Will have a look next week.

bramborman commented 1 year ago

Hmm, and there's also a difference in how it reports generics, which also mess up with the async methods... So even when I tried replacing dots with slashes in the output XML, I didn't get the correct results.

The Coverlet output:

MyNamespace.MyClass`1/<MyAsyncMethod>d__2

The dotnet-coverage output:

MyNamespace.MyClass.<MyAsyncMethod>d__2<TMyArgument>

This is the corresponding C# code:

namespace MyNamespace {
    public class MyClass<TMyArgument> {
        public async Task MyAsyncMethod() {
        }
    }
}

Note that, while the class, not the async method, is generic, the generic argument is put after the generated class name, while its parent class seems to not be generic from the XML.

danielpalme commented 1 year ago

Could you please try again with the new release 5.1.18?

Your use cases should now be supported.