diff --git a/Src/FluentAssertions/AsyncAssertionsExtensions.cs b/Src/FluentAssertions/AsyncAssertionsExtensions.cs index 4935b97a50..53c6c349b9 100644 --- a/Src/FluentAssertions/AsyncAssertionsExtensions.cs +++ b/Src/FluentAssertions/AsyncAssertionsExtensions.cs @@ -18,6 +18,12 @@ public static class AsyncAssertionsExtensions /// /// Zero or more objects to format using the placeholders in . /// + /// + /// Please note that this assertion cannot identify whether the previous assertion was successful or not. + /// In case it was not successful and it is running within an active + /// there is no current result to compare with. + /// So, this extension will compare with the default value. + /// public static async Task, T>> WithResult( this Task, T>> task, T expected, string because = "", params object[] becauseArgs) diff --git a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs index 3a46130f8e..0b48d6213f 100644 --- a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs @@ -56,13 +56,13 @@ public GenericAsyncFunctionAssertions(Func> subject, IExtractExcep if (success) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime); - Execute.Assertion + success = Execute.Assertion .ForCondition(completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); } - TResult result = task.IsCompleted ? task.Result : default; + TResult result = success ? task.Result : default; return new AndWhichConstraint, TResult>(this, result); } diff --git a/Tests/FluentAssertions.Specs/FakeClock.cs b/Tests/FluentAssertions.Specs/FakeClock.cs index 4d8a95be91..3ecdfd3afd 100644 --- a/Tests/FluentAssertions.Specs/FakeClock.cs +++ b/Tests/FluentAssertions.Specs/FakeClock.cs @@ -9,7 +9,7 @@ namespace FluentAssertions.Specs; /// Implementation of for testing purposes only. /// /// -/// It allows you to control the "current" time. +/// It allows you to control the "current" date and time for test purposes. /// internal class FakeClock : IClock { @@ -25,17 +25,26 @@ Task IClock.DelayAsync(TimeSpan delay, CancellationToken cancellationToken) public ITimer StartTimer() => new TestTimer(() => elapsedTime); + /// + /// Advances the internal clock. + /// public void Delay(TimeSpan timeToDelay) { elapsedTime += timeToDelay; } + /// + /// Simulates the completion of the pending delay task. + /// public void Complete() { // the value is not relevant delayTask.SetResult(true); } + /// + /// Simulates the completion of the pending delay task after the internal clock has been advanced. + /// public void CompleteAfter(TimeSpan timeSpan) { Delay(timeSpan); diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs index 33f930ce5c..279dd2ac08 100644 --- a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using FluentAssertions.Execution; using FluentAssertions.Extensions; @@ -165,6 +164,28 @@ public async Task When_task_completes_late_it_in_assertion_scope_should_fail() await action.Should().ThrowAsync(); } + [Fact] + public async Task When_task_does_not_complete_the_result_extension_does_not_hang() + { + // Arrange + var timer = new FakeClock(); + var taskFactory = new TaskCompletionSource(); + + // Act + Func action = () => + { + Func> func = () => taskFactory.Task; + using var _ = new AssertionScope(); + return func.Should(timer).CompleteWithinAsync(100.Milliseconds()).WithResult(2); + }; + timer.Complete(); + + // Assert + var assertionTask = action.Should().ThrowAsync() + .WithMessage("Expected*to complete within 100ms.*Expected return*to be 2, but found 0."); + await Awaiting(() => assertionTask).Should().CompleteWithinAsync(200.Seconds()); + } + [Fact] public async Task When_task_consumes_time_in_sync_portion_it_should_fail() { diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index 1571d3542b..829a6b3b6a 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -12,6 +12,7 @@ sidebar: ### What's new ### Fixes +* Fixed hanging of `CompleteWithinAsync` when used with `WithResult` and `AssertionScope` - [#2101](https://github.com/fluentassertions/fluentassertions/pull/2101) ## 6.9.0