public async Task TimeoutAfter_ShouldThrow_TimeoutException_WhenActionTimedOut()
        {
            // Arrange
            var breakpoint = new Breakpoint();

            async Task Action(CancellationToken ct)
            {
                await breakpoint.Hit().WaitForContinue();

                ct.ThrowIfCancellationRequested();
            }

            // Act
            var task = TaskExtensions.TimeoutAfter(Action, TimeSpan.Zero, CancellationToken.None);
            var hit  = await breakpoint.WaitForHit().TimeoutAfter(TestTimeout);

            await Task.Delay(TimeSpan.FromMilliseconds(100));

            hit.Continue();

            Func <Task> action = async() => await task;

            // Assert
            await action.Should().ThrowAsync <TimeoutException>();
        }
        public async Task TimeoutAfter_ShouldThrow_WhenActionThrows()
        {
            // Arrange
            Task Action(CancellationToken ct)
            {
                throw new Exception();
            }

            // Act
            Func <Task> action = async() => await TaskExtensions.TimeoutAfter(Action, TimeSpan.FromSeconds(10), CancellationToken.None);

            // Assert
            await action.Should().ThrowAsync <Exception>();
        }
        public async Task TimeoutAfter_ShouldSucceed_WhenActionSucceeds()
        {
            // Arrange
            Task Action(CancellationToken ct)
            {
                ct.ThrowIfCancellationRequested();
                return(Task.CompletedTask);
            }

            // Act
            Func <Task> action = async() => await TaskExtensions.TimeoutAfter(Action, TimeSpan.FromSeconds(10), CancellationToken.None);

            // Assert
            await action.Should().NotThrowAsync();
        }
        public async Task TimeoutAfter_ShouldThrow_TaskCanceledException_WhenActionCancelled()
        {
            // Arrange
            var breakpoint         = new Breakpoint();
            var cancellationSource = new CancellationTokenSource();

            async Task Action(CancellationToken ct)
            {
                await breakpoint.Hit().WaitForContinue();

                ct.ThrowIfCancellationRequested();
            }

            // Act
            var task = TaskExtensions.TimeoutAfter(Action, TimeSpan.Zero, cancellationSource.Token);
            var hit  = await breakpoint.WaitForHit().TimeoutAfter(TestTimeout);

            cancellationSource.Cancel();
            hit.Continue();
            Func <Task> action = async() => await task.TimeoutAfter(TestTimeout);

            // Assert
            await action.Should().ThrowAsync <OperationCanceledException>();
        }