using System.Threading.Tasks;
using Microsoft.Extensions.Time.Testing;
private const int RepeatTestCount = 5;
public static async Task Main()
await ShouldReturnInTime();
private static async Task ShouldReturnInTime()
for (int i = 0; i < RepeatTestCount; i++)
await TestWithTimeout(10, 11, false);
Console.WriteLine($"##### {nameof(ShouldReturnInTime)} passed! #####");
private static async Task ShouldTimeout()
for (int i = 0; i < RepeatTestCount; i++)
await TestWithTimeout(11, 10, true);
Console.WriteLine($"##### {nameof(ShouldTimeout)} passed! #####");
private static async Task TestWithTimeout(int longRunningTaskInMs, int waitTimeoutInMs, bool expectedWaitCallbackFired)
var timeProvider = new FakeTimeProvider();
var timeOutCallbackFired = false;
var actual = await Task.Run(async () =>
Console.WriteLine($" {DateTime.Now:O} long running task started...");
await Task.Delay(longRunningTaskInMs);
timeProvider.Advance(TimeSpan.FromMilliseconds(longRunningTaskInMs));
Console.WriteLine($" {DateTime.Now:O} long running task finished.");
return " returned in time";
}).WithTimeout(waitTimeoutInMs, timeProvider: timeProvider, onTimeout: () =>
Console.WriteLine(" timeout!");
timeOutCallbackFired = true;
Assert.That(actual, Is.EqualTo(expectedWaitCallbackFired ? " fired" : " returned in time"));
Assert.That(timeOutCallbackFired, Is.EqualTo(expectedWaitCallbackFired));
public static class TaskExtensions
public static async Task<T> WithTimeout<T>(this Task<T> task, int millisecondsTimeout, Func<T> onTimeout = null, TimeProvider timeProvider = null)
using (var timeoutCancellation = new CancellationTokenSource())
.WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout), timeProvider ?? TimeProvider.System, timeoutCancellation.Token)
catch (TimeoutException timeoutException)
return onTimeout != null ? onTimeout() : throw timeoutException;