Skip to content
Draft
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
21 changes: 2 additions & 19 deletions src/coverlet.core/Symbols/CecilSymbolHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,10 +1364,9 @@ private bool SkipExpressionBreakpointsSequences(MethodDefinition methodDefinitio

public bool SkipInlineAssignedAutoProperty(bool skipAutoProps, MethodDefinition methodDefinition, Instruction instruction)
{
if (!skipAutoProps || !methodDefinition.IsConstructor) return false;
if (!skipAutoProps) return false;

return SkipGeneratedBackingFieldAssignment(methodDefinition, instruction) ||
SkipDefaultInitializationSystemObject(instruction);
return SkipGeneratedBackingFieldAssignment(methodDefinition, instruction);
}

private static bool SkipGeneratedBackingFieldAssignment(MethodDefinition methodDefinition, Instruction instruction)
Expand Down Expand Up @@ -1401,22 +1400,6 @@ instance void .ctor () cil managed
autogeneratedBackingFields.Select(x => x.FullName).Contains(fr.FullName);
}

private static bool SkipDefaultInitializationSystemObject(Instruction instruction)
{
/*
A type always has a constructor with a default instantiation of System.Object. For record types these
instructions can have a own sequence point. This means that even the default constructor would be instrumented.
To skip this we search for call instructions with a method reference that declares System.Object.

IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: ret
*/
return instruction.OpCode == OpCodes.Ldarg &&
instruction.Next?.OpCode == OpCodes.Call &&
instruction.Next?.Operand is MethodReference mr && mr.DeclaringType.FullName.Equals(typeof(System.Object).FullName);
}

private static bool SkipBranchGeneratedExceptionFilter(Instruction branchInstruction, MethodDefinition methodDefinition)
{
if (!methodDefinition.Body.HasExceptionHandlers)
Expand Down
138 changes: 91 additions & 47 deletions test/coverlet.core.coverage.tests/Coverage/CoverageTests.AutoProps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ public partial class CoverageTests
[Theory]
[InlineData(true)]
[InlineData(false)]
public void SkipAutoProps(bool skipAutoProps)
public void SkipClassWithAutoProps(bool skipAutoProps)
{
string path = Path.GetTempFileName();
try
{
FunctionExecutor.Run(async (string[] parameters) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<AutoProps>(instance =>
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithAutoProps>(instance =>
{
instance.AutoPropsNonInit = 10;
instance.AutoPropsInit = 20;
int readValue = instance.AutoPropsNonInit;
readValue = instance.AutoPropsInit;
readValue = instance.AutoPropsInitKeyword;
return Task.CompletedTask;
},
persistPrepareResultToFile: parameters[0], skipAutoProps: bool.Parse(parameters[1]));
Expand All @@ -40,8 +41,8 @@ public void SkipAutoProps(bool skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 12, 13)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 12, 13)
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 12, 14)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 12, 14)
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 9, 11)
.AssertLinesCovered(BuildConfiguration.Debug, (7, 1))
.AssertLinesCovered(BuildConfiguration.Release, (10, 1));
Expand All @@ -50,9 +51,9 @@ public void SkipAutoProps(bool skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 7, 13)
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 7, 14)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 10, 10)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 12, 13);
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 12, 14);
}
}
finally
Expand All @@ -64,42 +65,39 @@ public void SkipAutoProps(bool skipAutoProps)
[Theory]
[InlineData(true)]
[InlineData(false)]
public void SkipAutoPropsInRecords(bool skipAutoProps)
public void SkipClassWithAutoPropsPrimaryConstructor(bool skipAutoProps)
{
string path = Path.GetTempFileName();
try
{
FunctionExecutor.Run(async (string[] parameters) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<RecordWithPropertyInit>(instance =>
{
instance.RecordAutoPropsNonInit = string.Empty;
instance.RecordAutoPropsInit = string.Empty;
string readValue = instance.RecordAutoPropsInit;
readValue = instance.RecordAutoPropsNonInit;
return Task.CompletedTask;
},
persistPrepareResultToFile: parameters[0], skipAutoProps: bool.Parse(parameters[1]));
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithAutoPropsPrimaryConstructor>(instance =>
{
return Task.CompletedTask;
},
persistPrepareResultToFile: parameters[0], skipAutoProps: bool.Parse(parameters[1]));

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

if (skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 23, 24)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 23, 24)
.AssertLinesCovered(BuildConfiguration.Debug, (18, 1), (20, 1), (21, 1), (22, 1))
.AssertLinesCovered(BuildConfiguration.Release, (21, 1));
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 28, 28)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 28, 28)
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 30, 31, 32)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 30, 31, 32);
}
else
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 18, 24)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 21, 21)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 23, 24);
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 28, 28)
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 30, 32)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 28, 28)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 30, 32);
}
}
finally
Expand All @@ -111,37 +109,44 @@ public void SkipAutoPropsInRecords(bool skipAutoProps)
[Theory]
[InlineData(true)]
[InlineData(false)]
public void SkipRecordWithProperties(bool skipAutoProps)
public void SkipRecordWithAutoProps(bool skipAutoProps)
{
string path = Path.GetTempFileName();
try
{
FunctionExecutor.Run(async (string[] parameters) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithRecordsAutoProperties>(instance =>
{
return Task.CompletedTask;
},
persistPrepareResultToFile: parameters[0], skipAutoProps: bool.Parse(parameters[1]));
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<RecordWithAutoProps>(instance =>
{
instance.AutoPropsNonInit = 10;
instance.AutoPropsInit = 20;
int readValue = instance.AutoPropsNonInit;
readValue = instance.AutoPropsInit;
readValue = instance.AutoPropsInitKeyword;
return Task.CompletedTask;
},
persistPrepareResultToFile: parameters[0], skipAutoProps: bool.Parse(parameters[1]));

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

if (skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path)
.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));
.Document("Instrumentation.AutoProps.cs")
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 43, 45)
.AssertNonInstrumentedLines(BuildConfiguration.Release, 43, 45)
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 40, 42)
.AssertLinesCovered(BuildConfiguration.Debug, (39, 1))
.AssertLinesCovered(BuildConfiguration.Release, (39, 1));
}
else
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCovered(BuildConfiguration.Debug, (29, 1), (31, 1), (32, 1), (33, 1), (34, 1))
.AssertLinesCovered(BuildConfiguration.Release, (29, 1), (31, 1), (33, 1));
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 38, 45) // go on here
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 39, 39)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 41, 45);
}
}
finally
Expand All @@ -153,14 +158,14 @@ public void SkipRecordWithProperties(bool skipAutoProps)
[Theory]
[InlineData(true)]
[InlineData(false)]
public void SkipInheritingRecordsWithProperties(bool skipAutoProps)
public void SkipRecordWithAutoPropsPrimaryConstructor(bool skipAutoProps)
{
string path = Path.GetTempFileName();
try
{
FunctionExecutor.Run(async (string[] parameters) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<ClassWithInheritingRecordsAndAutoProperties>(instance =>
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<RecordsWithPrimaryConstructor>(instance =>
{
return Task.CompletedTask;
},
Expand All @@ -173,18 +178,57 @@ public void SkipInheritingRecordsWithProperties(bool 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));
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 50, 50)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 50, 50);
}
else
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 50, 50)
.AssertLinesCoveredFromTo(BuildConfiguration.Release, 50, 50);
}
}
finally
{
File.Delete(path);
}
}

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

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

if (skipAutoProps)
{
TestInstrumentationHelper.GetCoverageResult(path)
.Document("Instrumentation.AutoProps.cs")
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 52, 55)
.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 52, 55);
}
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));
.AssertLinesCovered(BuildConfiguration.Debug, 52, 55)
.AssertLinesNotCovered(BuildConfiguration.Debug, 53, 54)
.AssertLinesCovered(BuildConfiguration.Release, 52, 55)
.AssertLinesNotCovered(BuildConfiguration.Release, 53, 54);
}
}
finally
Expand Down
Loading