Skip to content

Commit 844a72b

Browse files
committed
#762: Improved handling of generic classes in DynamicCodeCoverage files (generated by dotnet-coverage)
1 parent aa1d629 commit 844a72b

File tree

9 files changed

+1071
-511
lines changed

9 files changed

+1071
-511
lines changed

src/ReportGenerator.Core.Test/Parser/DynamicCodeCoverageClassNameParserTest.cs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,80 +10,66 @@ public class DynamicCodeCoverageClassNameParserTest
1010
"AbstractClass",
1111
"Test",
1212
"AbstractClass",
13-
"AbstractClass",
14-
null,
1513
"Test.AbstractClass",
1614
true)]
1715
[InlineData(
1816
"TestClass.NestedClass",
1917
"Test",
2018
"TestClass",
21-
"TestClass",
22-
null,
2319
"Test.TestClass",
2420
true)]
2521
[InlineData(
2622
"AsyncClass.<SendAsync>d__0",
2723
"Test",
2824
"AsyncClass",
29-
"AsyncClass",
30-
null,
3125
"Test.AsyncClass",
3226
true)]
3327
[InlineData(
3428
"GenericAsyncClass<T>",
3529
"Test",
3630
"GenericAsyncClass",
37-
"GenericAsyncClass<T>",
38-
"<T>",
39-
"Test.GenericAsyncClass<T>",
31+
"Test.GenericAsyncClass",
4032
true)]
4133
[InlineData(
4234
"ClassWithLocalFunctions.MyNestedClass<T1, T2>",
4335
"Test",
4436
"ClassWithLocalFunctions",
45-
"ClassWithLocalFunctions",
46-
null,
4737
"Test.ClassWithLocalFunctions",
4838
true)]
4939
[InlineData(
5040
"GenericAsyncClass.<MyAsyncMethod>d__1<T>",
5141
"Test",
5242
"GenericAsyncClass",
53-
"GenericAsyncClass<T>",
54-
"<T>",
55-
"Test.GenericAsyncClass<T>",
43+
"Test.GenericAsyncClass",
5644
true)]
5745
[InlineData(
5846
"Program.<CallAsyncMethod>d__1",
5947
"Test",
6048
"Program",
61-
"Program",
62-
null,
6349
"Test.Program",
6450
true)]
6551
[InlineData(
6652
"AutoMapperExtensions.<>c__1<TResult>",
6753
"Test",
6854
"AutoMapperExtensions",
69-
"AutoMapperExtensions",
70-
null,
7155
"Test.AutoMapperExtensions",
7256
true)]
57+
[InlineData(
58+
"TestClass.<GenericAsyncMethod>d__4<T>",
59+
"Test",
60+
"TestClass",
61+
"Test.TestClass",
62+
true)]
7363
public void ParseClassName(
7464
string rawName,
7565
string @namespace,
7666
string expectedName,
77-
string expectedDisplayName,
78-
string expectedGenericType,
7967
string expectedFullName,
8068
bool expectedInclude)
8169
{
8270
var result = DynamicCodeCoverageClassNameParser.ParseClassName(rawName, @namespace);
8371

8472
Assert.Equal(expectedName, result.Name);
85-
Assert.Equal(expectedDisplayName, result.DisplayName);
86-
Assert.Equal(expectedGenericType, result.GenericType);
8773
Assert.Equal(expectedFullName, result.FullName);
8874
Assert.Equal(expectedInclude, result.Include);
8975
}

src/ReportGenerator.Core/Parser/DynamicCodeCoverageClassNameParser.cs

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ internal static class DynamicCodeCoverageClassNameParser
1111
/// <summary>
1212
/// Regex to clean class names from compiler generated parts.
1313
/// </summary>
14-
private static readonly Regex CleanupRegex = new Regex("\\.<>\\w_?_?\\w*\\d*<.*>|\\.<.*>\\w_?_?\\w*\\d*", RegexOptions.Compiled);
14+
private static readonly Regex CleanupRegex = new Regex("<.*?>", RegexOptions.Compiled);
1515

1616
/// <summary>
1717
/// Regex to analyze if a class name represents a generic class.
@@ -39,36 +39,10 @@ public static DynamicCodeCoverageClassNameParserResult ParseClassName(
3939

4040
if (nestedClassSeparatorIndex > -1)
4141
{
42-
string className = cleanedClassName.Substring(0, nestedClassSeparatorIndex);
43-
return new DynamicCodeCoverageClassNameParserResult(namespaceOfClass, className, className, IncludeClass(className));
42+
cleanedClassName = cleanedClassName.Substring(0, nestedClassSeparatorIndex);
4443
}
4544

46-
if (cleanedClassName.Contains("<"))
47-
{
48-
var match = GenericClassRegex.Match(cleanedClassName);
49-
50-
if (match.Success)
51-
{
52-
return new DynamicCodeCoverageClassNameParserResult(
53-
namespaceOfClass,
54-
match.Groups["ClassName"].Value,
55-
match.Groups["ClassName"].Value + match.Groups["GenericTypes"].Value,
56-
IncludeClass(match.Groups["ClassName"].Value))
57-
{
58-
GenericType = match.Groups["GenericTypes"].Value
59-
};
60-
}
61-
else
62-
{
63-
return new DynamicCodeCoverageClassNameParserResult(
64-
namespaceOfClass,
65-
cleanedClassName,
66-
cleanedClassName,
67-
IncludeClass(cleanedClassName));
68-
}
69-
}
70-
71-
return new DynamicCodeCoverageClassNameParserResult(namespaceOfClass, cleanedClassName, cleanedClassName, IncludeClass(cleanedClassName));
45+
return new DynamicCodeCoverageClassNameParserResult(namespaceOfClass, cleanedClassName, IncludeClass(rawName));
7246
}
7347

7448
/// <summary>
@@ -78,8 +52,7 @@ public static DynamicCodeCoverageClassNameParserResult ParseClassName(
7852
/// <returns>True if the class should be included; otherwise, false.</returns>
7953
private static bool IncludeClass(string name)
8054
{
81-
return !name.Contains("<>")
82-
&& !name.StartsWith("$", StringComparison.OrdinalIgnoreCase);
55+
return !name.StartsWith("$", StringComparison.OrdinalIgnoreCase);
8356
}
8457
}
8558
}

src/ReportGenerator.Core/Parser/DynamicCodeCoverageClassNameParserResult.cs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,14 @@ internal class DynamicCodeCoverageClassNameParserResult
1010
/// </summary>
1111
/// <param name="namespaceOfClass">The namespace.</param>
1212
/// <param name="name">The name.</param>
13-
/// <param name="displayName">The display name.</param>
1413
/// <param name="include">Indicates whether the class should be included in the report.</param>
1514
public DynamicCodeCoverageClassNameParserResult(
1615
string namespaceOfClass,
1716
string name,
18-
string displayName,
1917
bool include)
2018
{
2119
this.Namespace = namespaceOfClass;
2220
this.Name = name;
23-
this.DisplayName = displayName;
2421
this.Include = include;
2522
}
2623

@@ -34,20 +31,10 @@ public DynamicCodeCoverageClassNameParserResult(
3431
/// </summary>
3532
public string Name { get; }
3633

37-
/// <summary>
38-
/// Gets the display name.
39-
/// </summary>
40-
public string DisplayName { get; }
41-
42-
/// <summary>
43-
/// Gets or sets the generic type.
44-
/// </summary>
45-
public string GenericType { get; set; }
46-
4734
/// <summary>
4835
/// Gets the full name.
4936
/// </summary>
50-
public string FullName => this.Namespace == null ? this.DisplayName : $"{this.Namespace}.{this.DisplayName}";
37+
public string FullName => this.Namespace == null ? this.Name : $"{this.Namespace}.{this.Name}";
5138

5239
/// <summary>
5340
/// Gets a value indicating whether the class should be included in the report.
@@ -57,7 +44,7 @@ public DynamicCodeCoverageClassNameParserResult(
5744
/// <inheritdoc />
5845
public override string ToString()
5946
{
60-
return this.DisplayName;
47+
return this.Name;
6148
}
6249

6350
/// <inheritdoc />
@@ -71,12 +58,11 @@ public override bool Equals(object obj)
7158
{
7259
var classNameParserResult = (DynamicCodeCoverageClassNameParserResult)obj;
7360
return string.Equals(classNameParserResult.Name, this.Name)
74-
&& string.Equals(classNameParserResult.DisplayName, this.DisplayName)
7561
&& string.Equals(classNameParserResult.Namespace, this.Namespace);
7662
}
7763
}
7864

7965
/// <inheritdoc />
80-
public override int GetHashCode() => this.Name.GetHashCode() + this.DisplayName.GetHashCode() + (this.Namespace?.GetHashCode()).GetValueOrDefault();
66+
public override int GetHashCode() => this.Name.GetHashCode() + (this.Namespace?.GetHashCode()).GetValueOrDefault();
8167
}
8268
}

src/Testprojects/CSharp/Project_DotNetCore/Test/AutoMapperExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ namespace Test
55
{
66
public static class AutoMapperExtensions
77
{
8-
public static TResult MergeInto<TResult>(this IMapper mapper, object item)
8+
public static TResult MergeIntoSingle<TResult>(this IMapper mapper, object item)
99
{
1010
return mapper.Map<TResult>(item);
1111
}
1212

13-
public static TResult MergeInto<TResult>(this IMapper mapper, params object?[] objects)
13+
public static TResult MergeIntoSeveral<TResult>(this IMapper mapper, params object?[] objects)
1414
{
1515
var res = mapper.Map<TResult>(objects.First());
1616
return objects.Skip(1).Where(x => x is not null).Aggregate(res, (r, obj) => mapper.Map(obj, r));

src/Testprojects/CSharp/Project_DotNetCore/Test/GenericAsyncClass.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,59 @@ public void DoWork()
1010
Console.WriteLine("BaseService DoWork");
1111
}
1212

13+
public void MyMethod()
14+
{
15+
}
16+
1317
public async Task MyAsyncMethod()
1418
{
1519
}
20+
21+
public async Task<T> MyAsyncGenericMethod<T1>(T1 something)
22+
{
23+
return default;
24+
}
25+
1626
public async Task<T> MyAsyncMethod(T something)
1727
{
1828
return something;
1929
}
30+
31+
public class InnerGenericClass<U>
32+
{
33+
public void MyNestedMethod()
34+
{
35+
}
36+
37+
public async Task MyNestedAsyncMethod()
38+
{
39+
}
40+
41+
public async Task<T> MyNestedAsyncGenericMethod<U1>(U1 something)
42+
{
43+
return default;
44+
}
45+
46+
public async Task<U> MyNestedAsyncMethod(U something)
47+
{
48+
return something;
49+
}
50+
}
51+
52+
public class InnerNonGenericClass
53+
{
54+
public void MyNestedMethod()
55+
{
56+
}
57+
58+
public async Task MyNestedAsyncMethod()
59+
{
60+
}
61+
62+
public async Task<T> MyNestedAsyncGenericMethod<U1>(U1 something)
63+
{
64+
return default;
65+
}
66+
}
2067
}
2168
}

src/Testprojects/CSharp/Project_DotNetCore/Test/TestClass.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Linq;
3+
using System.Threading.Tasks;
34

45
namespace Test
56
{
@@ -46,6 +47,17 @@ public void MethodWithLambda()
4647
var chars2 = "abc".Where(lambda).ToArray();
4748
}
4849

50+
51+
public void GenericMethod<T>(T messageObject)
52+
{
53+
54+
}
55+
56+
public async Task GenericAsyncMethod<T>(T messageObject)
57+
{
58+
59+
}
60+
4961
public class NestedClass
5062
{
5163
public void SampleFunction()

0 commit comments

Comments
 (0)