From a test ergonomics point of view, having the backward-compatible TimeProvider extensions methods run continuations asynchronously is bad for business.
I have a ManualTimeProvider implementation I am currently using included in https://github.com/egil/TimeScheduler, where I ported the TimeProvider type to .NET 6 as well as the TimeProviderTaskExtensions type. However, in my implementation, I changed the following:
|
public DelayState() : base(TaskCreationOptions.RunContinuationsAsynchronously) {} |
to
public DelayState() : base() { }
This allows me to write the following test:
[Fact]
public void Callbacks_runs_synchronously()
{
// arrange
var sut = new ManualTimeProvider();
var callbackCount = 0;
_ = Continuation(sut, () => callbackCount++);
// act
sut.ForwardTime(TimeSpan.FromSeconds(10));
// assert
callbackCount.Should().Be(1);
static async Task Continuation(TimeProvider timeProvider, Action callback)
{
await timeProvider.Delay(TimeSpan.FromSeconds(10));
callback();
}
}
With the original version that uses TaskCreationOptions.RunContinuationsAsynchronously, I have to add a Task.Yield() or similar into the test after forwarding time, otherwise the test will fail.
[Fact]
public async void Callbacks_runs_asynchronously()
{
// arrange
var sut = new ManualTimeProvider();
var callbackCount = 0;
_ = Continuation(sut, () => callbackCount++);
// act
sut.ForwardTime(TimeSpan.FromSeconds(10));
+ await Task.Yield();
// assert
callbackCount.Should().Be(1);
static async Task Continuation(TimeProvider timeProvider, Action callback)
{
await timeProvider.Delay(TimeSpan.FromSeconds(10));
callback();
}
}
It is a small change, but it does make the test less readable, adds noise, and more importantly, makes the test non-deterministic.
cc. @tarekgh.
From a test ergonomics point of view, having the backward-compatible
TimeProviderextensions methods run continuations asynchronously is bad for business.I have a
ManualTimeProviderimplementation I am currently using included in https://github.com/egil/TimeScheduler, where I ported theTimeProvidertype to .NET 6 as well as theTimeProviderTaskExtensionstype. However, in my implementation, I changed the following:runtime/src/libraries/Microsoft.Bcl.TimeProvider/src/System/Threading/Tasks/TimeProviderTaskExtensions.cs
Line 18 in 277a28d
to
This allows me to write the following test:
With the original version that uses
TaskCreationOptions.RunContinuationsAsynchronously, I have to add aTask.Yield()or similar into the test after forwarding time, otherwise the test will fail.[Fact] public async void Callbacks_runs_asynchronously() { // arrange var sut = new ManualTimeProvider(); var callbackCount = 0; _ = Continuation(sut, () => callbackCount++); // act sut.ForwardTime(TimeSpan.FromSeconds(10)); + await Task.Yield(); // assert callbackCount.Should().Be(1); static async Task Continuation(TimeProvider timeProvider, Action callback) { await timeProvider.Delay(TimeSpan.FromSeconds(10)); callback(); } }It is a small change, but it does make the test less readable, adds noise, and more importantly, makes the test non-deterministic.
cc. @tarekgh.