Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with primary constructors in records #1575

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Documentation/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased

### Fixed
-Uncovered lines in .NET 8 for inheriting records [#1555](https://github.com/coverlet-coverage/coverlet/issues/1555)
-Fix record constructors not covered when SkipAutoProps is true [#1561](https://github.com/coverlet-coverage/coverlet/issues/1561)
-Fix ExcludeFromCodeCoverage does not exclude method in a partial class [#1548](https://github.com/coverlet-coverage/coverlet/issues/1548)
-Fix ExcludeFromCodeCoverage does not exclude F# task [#1547](https://github.com/coverlet-coverage/coverlet/issues/1547)
-Fix issues where ExcludeFromCodeCoverage ignored [#1431](https://github.com/coverlet-coverage/coverlet/issues/1431)
Expand Down
11 changes: 8 additions & 3 deletions src/coverlet.core/Instrumentation/Instrumenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ private void InstrumentType(TypeDefinition type)

if (actualMethod.IsGetter || actualMethod.IsSetter)
{
if (_parameters.SkipAutoProps && actualMethod.CustomAttributes.Any(ca => ca.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName))
if (_parameters.SkipAutoProps && IsCompilerGenerated(actualMethod))
{
continue;
}
Expand Down Expand Up @@ -507,13 +507,18 @@ private void InstrumentType(TypeDefinition type)
IEnumerable<MethodDefinition> ctors = type.GetConstructors();
foreach (MethodDefinition ctor in ctors)
{
if (!ctor.CustomAttributes.Any(IsExcludeAttribute))
if (!ctor.CustomAttributes.Any(IsExcludeAttribute) && !IsCompilerGenerated(ctor))
{
InstrumentMethod(ctor);
}
}
}

private static bool IsCompilerGenerated(IMemberDefinition member)
{
return member.CustomAttributes.Any(ca => ca.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName);
}

private void InstrumentMethod(MethodDefinition method)
{
string sourceFile = method.DebugInformation.SequencePoints.Select(s => _sourceRootTranslator.ResolveFilePath(s.Document.Url)).FirstOrDefault();
Expand Down Expand Up @@ -679,7 +684,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
}
);

if (method.DeclaringType.CustomAttributes.Any(x => x.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName))
if (IsCompilerGenerated(method.DeclaringType))
{
if (_branchesInCompiledGeneratedClass == null)
{
Expand Down
54 changes: 48 additions & 6 deletions test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void SkipAutoPropsInRecords(bool skipAutoProps)

if (skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path).GenerateReport(show: true)
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 23, 24)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 23, 24)
Expand All @@ -106,7 +106,7 @@ public void SkipAutoPropsInRecords(bool skipAutoProps)
}
}

[Theory(Skip = "fails reason unknown (no session debug possible)")]
[Theory]
[InlineData(true)]
[InlineData(false)]
public void SkipRecordWithProperties(bool skipAutoProps)
Expand All @@ -116,7 +116,7 @@ public void SkipRecordWithProperties(bool skipAutoProps)
{
FunctionExecutor.Run(async (string[] parameters) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithAutoRecordProperties>(instance =>
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithRecordsAutoProperties>(instance =>
{
return Task.CompletedTask;
},
Expand All @@ -133,14 +133,56 @@ public void SkipRecordWithProperties(bool skipAutoProps)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 29, 29)
.AssertLinesCovered(BuildConfiguration.Debug, (32, 1), (33, 1), (34, 1))
.AssertLinesCovered(BuildConfiguration.Release, (33, 1));

}
else
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCovered(BuildConfiguration.Debug, (29, 3), (31, 1), (32, 1), (33, 1), (34, 1))
.AssertLinesCovered(BuildConfiguration.Release, (29, 3), (31, 1), (33, 1));
.AssertLinesCovered(BuildConfiguration.Debug, (29, 1), (31, 1), (32, 1), (33, 1), (34, 1))
.AssertLinesCovered(BuildConfiguration.Release, (29, 1), (31, 1), (33, 1));
}
}
finally
{
File.Delete(path);
}
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void SkipInheritingRecordsWithProperties(bool skipAutoProps)
{
string path = Path.GetTempFileName();
try
{
FunctionExecutor.Run(async (string[] parameters) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithInheritingRecordsAndAutoProperties>(instance =>
{
return Task.CompletedTask;
},
persistPrepareResultToFile: parameters[0], skipAutoProps: bool.Parse(parameters[1]));

return 0;
}, new string[] { path, skipAutoProps.ToString() });

if (skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 39, 39)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 39, 39)
.AssertLinesCovered(BuildConfiguration.Debug, (41, 1), (44, 1), (45, 1), (46, 1))
.AssertLinesCovered(BuildConfiguration.Release, (45, 1));

}
else
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCovered(BuildConfiguration.Debug, (39, 1), (41, 1), (44, 1), (45, 1), (46, 1))
.AssertLinesCovered(BuildConfiguration.Release, (39, 1), (41, 1), (45, 1));
}
}
finally
Expand Down
22 changes: 18 additions & 4 deletions test/coverlet.core.tests/Samples/Instrumentation.AutoProps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,27 @@ public RecordWithPropertyInit()
public string RecordAutoPropsInit { get; set; } = string.Empty;
}

public class ClassWithAutoRecordProperties
public class ClassWithRecordsAutoProperties
{
record AutoRecordWithProperties(string Prop1, string Prop2);
record RecordWithPrimaryConstructor(string Prop1, string Prop2);

public ClassWithAutoRecordProperties()
public ClassWithRecordsAutoProperties()
{
var record = new AutoRecordWithProperties(string.Empty, string.Empty);
var record = new RecordWithPrimaryConstructor(string.Empty, string.Empty);
}
}

public class ClassWithInheritingRecordsAndAutoProperties
{
record BaseRecord(int A);

record InheritedRecord(int A) : BaseRecord(A);

public ClassWithInheritingRecordsAndAutoProperties()
{
var record = new InheritedRecord(1);
}
}


}