Implementation of exclusion (#1277)

implementation of exclusion
This commit is contained in:
David Müller
2022-01-14 11:25:21 +01:00
committed by GitHub
parent 0632e7a425
commit 84876b09c6
4 changed files with 95 additions and 1 deletions
+1
View File
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Fixed
-Fix wrong branch coverage with EnumeratorCancellation attribute [#1275](https://github.com/coverlet-coverage/coverlet/issues/1275)
-Fix negative coverage exceeding int.MaxValue [#1266](https://github.com/coverlet-coverage/coverlet/issues/1266)
-Fix summary output format for culture de-DE [#1263](https://github.com/coverlet-coverage/coverlet/issues/1263)
-Fix branch coverage issue for finally block with await [#1233](https://github.com/coverlet-coverage/coverlet/issues/1233)
+39 -1
View File
@@ -855,7 +855,6 @@ namespace Coverlet.Core.Symbols
return false;
}
static bool DisposeCheck(List<Instruction> instructions, Instruction instruction, int currentIndex)
{
// Within the compiler-generated async iterator, there are at least a
@@ -891,6 +890,40 @@ namespace Coverlet.Core.Symbols
}
}
private bool SkipGeneratedBranchesForEnumeratorCancellationAttribute(List<Instruction> instructions, Instruction instruction)
{
// For async-enumerable methods an additional cancellation token despite the default one can be passed.
// The EnumeratorCancellation attribute marks the parameter whose value is received by GetAsyncEnumerator(CancellationToken).
// Therefore the compiler generates the field x__combinedTokens and generates some additional branch points.
//
// IL_0118: ldarg.0
// IL_0119: ldfld class [System.Runtime]System.Threading.CancellationTokenSource Issue1275.AwaitForeachReproduction/'<AsyncEnumerable>d__1'::'<>x__combinedTokens'
// IL_011E: brfalse.s IL_0133
//
// We'll eliminate these wherever they appear. It's reasonable to just look for a "brfalse" or "brfalse.s" instruction, preceded
// immediately by "ldfld" of the compiler-generated "<>x__combinedTokens" field.
int branchIndex = instructions.BinarySearch(instruction, new InstructionByOffsetComparer());
if (instruction.OpCode != OpCodes.Brfalse &&
instruction.OpCode != OpCodes.Brfalse_S)
{
return false;
}
if (branchIndex >= 2 &&
instructions[branchIndex - 1].OpCode == OpCodes.Ldfld &&
instructions[branchIndex - 1].Operand is FieldDefinition field &&
field.FieldType.FullName.Equals("System.Threading.CancellationTokenSource") &&
field.FullName.EndsWith("x__combinedTokens") &&
(instructions[branchIndex - 2].OpCode == OpCodes.Ldarg ||
instructions[branchIndex - 2].OpCode == OpCodes.Ldarg_0))
{
return true;
}
return false;
}
// https://github.com/dotnet/roslyn/blob/master/docs/compilers/CSharp/Expression%20Breakpoints.md
private static bool SkipExpressionBreakpointsBranches(Instruction instruction) => instruction.Previous is not null && instruction.Previous.OpCode == OpCodes.Ldc_I4 &&
instruction.Previous.Operand is int operandValue && operandValue == 1 &&
@@ -973,6 +1006,11 @@ namespace Coverlet.Core.Symbols
}
}
if (SkipGeneratedBranchesForEnumeratorCancellationAttribute(instructions, instruction))
{
continue;
}
if (SkipBranchGeneratedExceptionFilter(instruction, methodDefinition))
{
continue;
@@ -1,5 +1,6 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Coverlet.Core.Samples.Tests;
@@ -182,5 +183,35 @@ namespace Coverlet.Core.Tests
File.Delete(path);
}
}
[Fact]
public void AsyncAwait_Issue_1275()
{
string path = Path.GetTempFileName();
try
{
FunctionExecutor.Run(async (string[] pathSerialize) =>
{
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<Issue_1275>(instance =>
{
var cts = new CancellationTokenSource();
((Task)instance.Execute(cts.Token)).ConfigureAwait(false).GetAwaiter().GetResult();
return Task.CompletedTask;
},
persistPrepareResultToFile: pathSerialize[0]);
return 0;
}, new string[] { path });
var document = TestInstrumentationHelper.GetCoverageResult(path).Document("Instrumentation.AsyncAwait.cs");
document.AssertLinesCoveredFromTo(BuildConfiguration.Debug, 170, 176);
document.AssertBranchesCovered(BuildConfiguration.Debug, (171, 0, 1), (171, 1, 1));
Assert.DoesNotContain(document.Branches, x => x.Key.Line == 176);
}
finally
{
File.Delete(path);
}
}
}
}
@@ -151,4 +151,28 @@ namespace Coverlet.Core.Samples.Tests
}
}
}
public class Issue_1275
{
public async Task<int> Execute(System.Threading.CancellationToken token)
{
int sum = 0;
await foreach (int result in AsyncEnumerable(token))
{
sum += result;
}
return sum;
}
async System.Collections.Generic.IAsyncEnumerable<int> AsyncEnumerable([System.Runtime.CompilerServices.EnumeratorCancellation] System.Threading.CancellationToken cancellationToken)
{
for (int i = 0; i < 1; i++)
{
await Task.Delay(1, cancellationToken);
yield return i;
}
}
}
}