Skip to content

Commit c77b85c

Browse files
Coverage for awaiting ValueTasks (#949)
Coverage for awaiting ValueTasks
1 parent fe6fee9 commit c77b85c

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

Diff for: src/coverlet.core/Symbols/CecilSymbolHelper.cs

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ instruction.Previous.Operand is MethodReference operand &&
172172
operand.Name == "get_IsCompleted" &&
173173
(
174174
operand.DeclaringType.FullName.StartsWith("System.Runtime.CompilerServices.TaskAwaiter") ||
175+
operand.DeclaringType.FullName.StartsWith("System.Runtime.CompilerServices.ValueTaskAwaiter") ||
175176
operand.DeclaringType.FullName.StartsWith("System.Runtime.CompilerServices.ConfiguredTaskAwaitable") ||
176177
operand.DeclaringType.FullName.StartsWith("System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable")
177178
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System.IO;
2+
using System.Threading.Tasks;
3+
4+
using Coverlet.Core.Samples.Tests;
5+
using Coverlet.Tests.Xunit.Extensions;
6+
using Xunit;
7+
8+
namespace Coverlet.Core.Tests
9+
{
10+
public partial class CoverageTests
11+
{
12+
[Fact]
13+
public void AsyncAwaitWithValueTask()
14+
{
15+
string path = Path.GetTempFileName();
16+
try
17+
{
18+
FunctionExecutor.Run(async (string[] pathSerialize) =>
19+
{
20+
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<AsyncAwaitValueTask>(async instance =>
21+
{
22+
instance.SyncExecution();
23+
24+
int res = await ((ValueTask<int>)instance.AsyncExecution(true)).ConfigureAwait(false);
25+
res = await ((ValueTask<int>)instance.AsyncExecution(1)).ConfigureAwait(false);
26+
res = await ((ValueTask<int>)instance.AsyncExecution(2)).ConfigureAwait(false);
27+
res = await ((ValueTask<int>)instance.AsyncExecution(3)).ConfigureAwait(false);
28+
res = await ((ValueTask<int>)instance.ConfigureAwait()).ConfigureAwait(false);
29+
res = ((Task<int>)instance.WrappingValueTaskAsTask()).ConfigureAwait(false).GetAwaiter().GetResult();
30+
}, persistPrepareResultToFile: pathSerialize[0]);
31+
return 0;
32+
}, new string[] { path });
33+
34+
TestInstrumentationHelper.GetCoverageResult(path)
35+
.Document("Instrumentation.AsyncAwaitValueTask.cs")
36+
.AssertLinesCovered(BuildConfiguration.Debug,
37+
// AsyncExecution(bool)
38+
(12, 1), (13, 1), (15, 1), (16, 1), (18, 1), (19, 1), (21, 1), (23, 1), (24, 0), (25, 0), (26, 0), (28, 1), (29, 1),
39+
// Async
40+
(32, 10), (33, 10), (34, 10), (35, 10), (36, 10),
41+
// AsyncExecution(int)
42+
(39, 3), (40, 3), (42, 3), (43, 3), (45, 3), (46, 3),
43+
(49, 1), (50, 1), (51, 1), (54, 1), (55, 1), (56, 1), (59, 1), (60, 1), (62, 1),
44+
(65, 0), (66, 0), (67, 0), (68, 0), (71, 0),
45+
// SyncExecution
46+
(77, 1), (78, 1), (79, 1),
47+
// Sync
48+
(82, 1), (83, 1), (84, 1),
49+
// ConfigureAwait
50+
(87, 1), (88, 1), (90, 1), (91, 1), (93, 1), (94, 1), (95, 1),
51+
// WrappingValueTaskAsTask
52+
(98, 1), (99, 1), (101, 1), (102, 1), (104, 1), (106, 1), (107, 1)
53+
)
54+
.AssertBranchesCovered(BuildConfiguration.Debug,
55+
// AsyncExecution(bool) if statement
56+
(23, 0, 0), (23, 1, 1),
57+
// AsyncExecution(int) switch statement
58+
(46, 0, 3), (46, 1, 1), (46, 2, 1), (46, 3, 1), (46, 4, 0)
59+
)
60+
.ExpectedTotalNumberOfBranches(BuildConfiguration.Debug, 2);
61+
}
62+
finally
63+
{
64+
File.Delete(path);
65+
}
66+
}
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Remember to use full name because adding new using directives change line numbers
2+
3+
using System;
4+
using System.IO;
5+
using System.Threading.Tasks;
6+
7+
namespace Coverlet.Core.Samples.Tests
8+
{
9+
public class AsyncAwaitValueTask
10+
{
11+
async public ValueTask<int> AsyncExecution(bool skipLast)
12+
{
13+
var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
14+
15+
var stream = new MemoryStream(bytes);
16+
stream.Position = 0;
17+
18+
int res = 0;
19+
res += await Async(stream);
20+
21+
res += await Async(stream);
22+
23+
if (!skipLast)
24+
{
25+
res += await Async(stream);
26+
}
27+
28+
return res;
29+
}
30+
31+
async public ValueTask<int> Async(System.IO.MemoryStream stream)
32+
{
33+
var buffer = new byte[4];
34+
await stream.ReadAsync(buffer.AsMemory()); // This overload of ReadAsync() returns a ValueTask<int>
35+
return buffer[0] + buffer[1] + buffer[2] + buffer[3];
36+
}
37+
38+
async public ValueTask<int> AsyncExecution(int val)
39+
{
40+
var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
41+
42+
var stream = new MemoryStream(bytes);
43+
stream.Position = 0;
44+
45+
int res = 0;
46+
switch (val)
47+
{
48+
case 1:
49+
{
50+
res += await Async(stream);
51+
break;
52+
}
53+
case 2:
54+
{
55+
res += await Async(stream) + await Async(stream);
56+
break;
57+
}
58+
case 3:
59+
{
60+
res += await Async(stream) + await Async(stream) +
61+
await Async(stream);
62+
break;
63+
}
64+
case 4:
65+
{
66+
res += await Async(stream) + await Async(stream) +
67+
await Async(stream) + await Async(stream);
68+
break;
69+
}
70+
default:
71+
break;
72+
}
73+
return res;
74+
}
75+
76+
async public ValueTask SyncExecution()
77+
{
78+
await Sync();
79+
}
80+
81+
public ValueTask Sync()
82+
{
83+
return default(ValueTask);
84+
}
85+
86+
async public ValueTask<int> ConfigureAwait()
87+
{
88+
var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
89+
90+
var stream = new MemoryStream(bytes);
91+
stream.Position = 0;
92+
93+
await Async(stream).ConfigureAwait(false);
94+
return 42;
95+
}
96+
97+
async public Task<int> WrappingValueTaskAsTask()
98+
{
99+
var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
100+
101+
var stream = new MemoryStream(bytes);
102+
stream.Position = 0;
103+
104+
var task = Async(stream).AsTask();
105+
106+
return await task;
107+
}
108+
}
109+
}

Diff for: test/coverlet.core.tests/Samples/Samples.cs

+8
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ async public Task AsyncAwait()
189189
}
190190
}
191191

192+
public class AsyncAwaitValueTaskStateMachine
193+
{
194+
async public ValueTask AsyncAwait()
195+
{
196+
await default(ValueTask);
197+
}
198+
}
199+
192200
[ExcludeFromCoverage]
193201
public class ClassExcludedByCoverletCodeCoverageAttr
194202
{

Diff for: test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,22 @@ public void GetBranchPoints_IgnoresBranchesIn_AsyncAwaitStateMachine()
294294
Assert.Empty(points);
295295
}
296296

297+
[Fact]
298+
public void GetBranchPoints_IgnoresBranchesIn_AsyncAwaitValueTaskStateMachine()
299+
{
300+
// arrange
301+
var nestedName = typeof(AsyncAwaitValueTaskStateMachine).GetNestedTypes(BindingFlags.NonPublic).First().Name;
302+
var type = _module.Types.FirstOrDefault(x => x.FullName == typeof(AsyncAwaitValueTaskStateMachine).FullName);
303+
var nestedType = type.NestedTypes.FirstOrDefault(x => x.FullName.EndsWith(nestedName));
304+
var method = nestedType.Methods.First(x => x.FullName.EndsWith("::MoveNext()"));
305+
306+
// act
307+
var points = _cecilSymbolHelper.GetBranchPoints(method);
308+
309+
// assert
310+
Assert.Empty(points);
311+
}
312+
297313
[Fact]
298314
public void GetBranchPoints_ExceptionFilter()
299315
{

0 commit comments

Comments
 (0)