Skip to content

Commit

Permalink
Merge pull request #2101 from lg2de/WithResult-non-blocking
Browse files Browse the repository at this point in the history
Fix timeout check in WithResult extension
  • Loading branch information
jnyrup committed Jan 17, 2023
2 parents 6644745 + efb158f commit 1f6238f
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 8 deletions.
6 changes: 6 additions & 0 deletions Src/FluentAssertions/AsyncAssertionsExtensions.cs
Expand Up @@ -18,6 +18,12 @@ public static class AsyncAssertionsExtensions
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because"/>.
/// </param>
/// <remarks>
/// 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 <see cref="FluentAssertions.Execution.AssertionScope"/>
/// there is no current result to compare with.
/// So, this extension will compare with the default value.
/// </remarks>
public static async Task<AndWhichConstraint<GenericAsyncFunctionAssertions<T>, T>> WithResult<T>(
this Task<AndWhichConstraint<GenericAsyncFunctionAssertions<T>, T>> task,
T expected, string because = "", params object[] becauseArgs)
Expand Down
Expand Up @@ -51,13 +51,13 @@ public GenericAsyncFunctionAssertions(Func<Task<TResult>> subject, IExtractExcep
if (success)
{
bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime);
Execute.Assertion
.ForCondition(completesWithinTimeout)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan);
}
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<GenericAsyncFunctionAssertions<TResult>, TResult>(this, result);
}

Expand Down
11 changes: 10 additions & 1 deletion Tests/FluentAssertions.Specs/FakeClock.cs
Expand Up @@ -9,7 +9,7 @@ namespace FluentAssertions.Specs;
/// Implementation of <see cref="IClock"/> for testing purposes only.
/// </summary>
/// <remarks>
/// It allows you to control the "current" time.
/// It allows you to control the "current" date and time for test purposes.
/// </remarks>
internal class FakeClock : IClock
{
Expand All @@ -25,17 +25,26 @@ Task IClock.DelayAsync(TimeSpan delay, CancellationToken cancellationToken)

public ITimer StartTimer() => new TestTimer(() => elapsedTime);

/// <summary>
/// Advances the internal clock.
/// </summary>
public void Delay(TimeSpan timeToDelay)
{
elapsedTime += timeToDelay;
}

/// <summary>
/// Simulates the completion of the pending delay task.
/// </summary>
public void Complete()
{
// the value is not relevant
delayTask.SetResult(true);
}

/// <summary>
/// Simulates the completion of the pending delay task after the internal clock has been advanced.
/// </summary>
public void CompleteAfter(TimeSpan timeSpan)
{
Delay(timeSpan);
Expand Down
@@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using FluentAssertions.Execution;
using FluentAssertions.Extensions;
Expand All @@ -10,6 +9,8 @@
using Xunit;
using Xunit.Sdk;

using static FluentAssertions.FluentActions;

namespace FluentAssertions.Specs.Specialized;

public static class TaskOfTAssertionSpecs
Expand Down Expand Up @@ -165,6 +166,28 @@ public async Task When_task_completes_late_it_in_assertion_scope_should_fail()
await action.Should().ThrowAsync<XunitException>();
}

[Fact]
public async Task When_task_does_not_complete_the_result_extension_does_not_hang()
{
// Arrange
var timer = new FakeClock();
var taskFactory = new TaskCompletionSource<int>();

// Act
Func<Task> action = () =>
{
Func<Task<int>> func = () => taskFactory.Task;
using var _ = new AssertionScope();
return func.Should(timer).CompleteWithinAsync(100.Milliseconds()).WithResult(2);
};
timer.Complete();

// Assert
var assertionTask = action.Should().ThrowAsync<XunitException>()
.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()
{
Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -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)

* `BeEquivalentTo` no longer crashes on fields hiding base-class fields - [#1990](https://github.com/fluentassertions/fluentassertions/pull/1990)

Expand Down

0 comments on commit 1f6238f

Please sign in to comment.