From 5d81305c4e59942dd6fcdd10251c236a0af01692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Tue, 2 Jan 2024 00:52:05 +0100 Subject: [PATCH 1/3] fix issues with primary constuctors in records --- Documentation/Changelog.md | 2 + .../Instrumentation/Instrumenter.cs | 11 ++-- .../Coverage/CoverageTests.AutoProps.cs | 54 ++++++++++++++++--- .../Samples/Instrumentation.AutoProps.cs | 22 ++++++-- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 45fdb19da..f82005c5d 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -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) diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index f6f5f9379..a1dc22f51 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -468,7 +468,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; } @@ -505,13 +505,18 @@ private void InstrumentType(TypeDefinition type) IEnumerable 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(); @@ -677,7 +682,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) { diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs index fc3cf6587..91a8dae62 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs @@ -83,7 +83,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) @@ -115,7 +115,7 @@ public void SkipRecordWithProperties(bool skipAutoProps) { FunctionExecutor.Run(async (string[] parameters) => { - CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run(instance => + CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run(instance => { return Task.CompletedTask; }, @@ -126,20 +126,62 @@ public void SkipRecordWithProperties(bool skipAutoProps) if (skipAutoProps) { - TestInstrumentationHelper.GetCoverageResult(path) + TestInstrumentationHelper.GetCoverageResult(path).GenerateReport(show: true) .Document("Instrumentation.AutoProps.cs") .AssertNonInstrumentedLines(BuildConfiguration.Debug, 29, 29) .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(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).GenerateReport(show:true) + .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 diff --git a/test/coverlet.core.tests/Samples/Instrumentation.AutoProps.cs b/test/coverlet.core.tests/Samples/Instrumentation.AutoProps.cs index 852fcb182..abf2321ac 100644 --- a/test/coverlet.core.tests/Samples/Instrumentation.AutoProps.cs +++ b/test/coverlet.core.tests/Samples/Instrumentation.AutoProps.cs @@ -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); + } + } + + } From c816d035bfc1998bc7f0053b1e0581daac015e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Tue, 2 Jan 2024 01:14:23 +0100 Subject: [PATCH 2/3] nit --- test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs index 91a8dae62..11b75f7ed 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs @@ -126,7 +126,7 @@ public void SkipRecordWithProperties(bool skipAutoProps) if (skipAutoProps) { - TestInstrumentationHelper.GetCoverageResult(path).GenerateReport(show: true) + TestInstrumentationHelper.GetCoverageResult(path) .Document("Instrumentation.AutoProps.cs") .AssertNonInstrumentedLines(BuildConfiguration.Debug, 29, 29) .AssertNonInstrumentedLines(BuildConfiguration.Release, 29, 29) @@ -178,7 +178,7 @@ public void SkipInheritingRecordsWithProperties(bool skipAutoProps) } else { - TestInstrumentationHelper.GetCoverageResult(path).GenerateReport(show:true) + 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)); From d88519cdf3c0797cfb43b139b371c2337685ffa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Sat, 6 Jan 2024 00:32:27 +0100 Subject: [PATCH 3/3] nit --- test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs index 95a9a7902..6c7c56ab7 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.AutoProps.cs @@ -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)