SteveGilham / altcover

Cross-platform coverage gathering and processing tool set for dotnet/.Net Framework and Mono
MIT License
494 stars 17 forks source link

Generic methods share the same name and signature #222

Closed kert-stratec closed 1 month ago

kert-stratec commented 2 months ago

Hi,

when there are two methods just differentiating based on their derived types, they share the same name and signature in the cobertura output.

Here is an example:

public static void AddSingleton<T1, T2, T>(this IServiceCollection services, Func<IServiceProvider, T> implementationFactory)
  where T : class, T1, T2 where T1 : class where T2 : class
{
  services.AddSingleton<T2, T>(implementationFactory);
  services.AddSingleton<T1, T>(x => (T)x.GetService<T2>()!);
}

public static void AddSingleton<T1, T2, T3, T>(this IServiceCollection services, Func<IServiceProvider, T> implementationFactory)
  where T : class, T1, T2, T3 where T1 : class where T2 : class where T3 : class
{
  services.AddSingleton<T2, T>(implementationFactory);
  services.AddSingleton<T1, T>(x => (T)x.GetService<T2>()!);
  services.AddSingleton<T3, T>(x => (T)x.GetService<T2>()!);
}

This results in a cobertura.xml that looks like this:

<class name="Extensions.DependencyInjectionExtensions" filename="...\Extensions\DependencyInjectionExtensions.cs" line-rate="0" branch-rate="0" complexity="2.6">
  <methods>
    <method name="AddSingleton" signature="System.Void (Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Func`2&lt;System.IServiceProvider,T&gt;)" line-rate="0" branch-rate="0" complexity="2">
      <lines>
        <line number="37" hits="0" branch="false" />
      </lines>
    </method>
    <method name="AddSingleton" signature="System.Void (Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Func`2&lt;System.IServiceProvider,T&gt;)" line-rate="0" branch-rate="0" complexity="3">
      <lines>
        <line number="60" hits="0" branch="false" />
      </lines>
    </method>
  </methods>
</class>

I noticed the issue when I tried to include the output into a Jenkins CI. The coverage plugin threw an Exception, that there is already an item with the key. Just as context, here is the exception thrown by Jenkins:

java.lang.IllegalArgumentException: There is already a child [METHOD] AddSingletonSystem.Void (Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Func`2<System.IServiceProvider,T>) <0> with the name AddSingletonSystem.Void (Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Func`2<System.IServiceProvider,T>) in [CLASS] Extensions.DependencyInjectionExtensions <3, LINE: 0.00% (0/12)>
SteveGilham commented 2 months ago

It's not just cobertura (which is a downstream format) that does not handle this edge case; the base OpenCover format doesn't record the information, so has two method entries of the same name, differing by metadata token. AltCover's JSON format even merges the two methods into one keyed on the non-generic signature. Of all the formats, it's only the ancient NCover that retains the information as it currently stands.

SteveGilham commented 2 months ago

Well, it seems that AltCover perfectly emulates OpenCover for that format, and coverlet for the json format. The latter also merges the two methods in cobertura format, rather than having two records with the same method signature as in this issue.

Time to improve on that.

SteveGilham commented 1 month ago

Should be resolved in release v8.8.74.

SteveGilham commented 1 month ago

Assuming silence as assent; closing.

kretzlaff commented 2 weeks ago

Sorry for the very late reply. Can confirm it is fixed now! Thank you!