Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions tests/TUnit.Assertions.Tests/ExecutionTimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ await Assert.That(

var duration = stopwatch.Elapsed;

await Assert.That(duration).IsLessThan(TimeSpan.FromSeconds(1));
// Well under the 10s action, so the 250ms limit must have cut it short, but loose enough
// for loaded CI runners where a sub-second budget is flaky (see #7169).
await Assert.That(duration).IsLessThan(TimeSpan.FromSeconds(5));
}

[Test]
Expand All @@ -52,6 +54,8 @@ await Assert.That(

var duration = stopwatch.Elapsed;

await Assert.That(duration).IsLessThan(TimeSpan.FromSeconds(1));
// Well under the 10s action, so the 250ms limit must have cut it short, but loose enough
// for loaded CI runners where a sub-second budget is flaky (see #7169).
await Assert.That(duration).IsLessThan(TimeSpan.FromSeconds(5));
}
}
29 changes: 21 additions & 8 deletions tests/TUnit.Assertions.Tests/WaitsForAssertionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ namespace TUnit.Assertions.Tests;
[NotInParallel]
public class WaitsForAssertionTests
{
// The cancellation tests use a long internal timeout so that "cancellation won" is never decided
// by wall-clock noise. If the supplied token were ignored, the internal timeout would produce an
// AssertionException (failing the Throws<OperationCanceledException> check) after 30s. The elapsed
// budget is a third of that: it still proves the timeout was short-circuited, while tolerating
// heavily loaded CI runners (see #7169), where sub-second budgets proved flaky.
private static readonly TimeSpan CancellationTestTimeout = TimeSpan.FromSeconds(30);
private static readonly TimeSpan CancellationElapsedBudget = TimeSpan.FromSeconds(10);

[Test]
public async Task WaitsFor_Passes_Immediately_When_Assertion_Succeeds()
{
Expand Down Expand Up @@ -419,14 +427,14 @@ public async Task WaitsFor_PropagatesExternalCancellation_Before_Internal_Timeou
var act = async () => await Assert.That(() => false)
.WaitsFor(
assert => assert.IsTrue(),
timeout: TimeSpan.FromSeconds(30),
timeout: CancellationTestTimeout,
cancellationToken: cts.Token);

await Assert.That(act).Throws<OperationCanceledException>();

stopwatch.Stop();

await Assert.That(stopwatch.Elapsed).IsLessThan(TimeSpan.FromSeconds(1));
await Assert.That(stopwatch.Elapsed).IsLessThan(CancellationElapsedBudget);
}

[Test]
Expand Down Expand Up @@ -454,14 +462,14 @@ public async Task WaitsFor_Honours_PreCancelled_Token_Before_First_Poll()
var act = async () => await Assert.That(() => false)
.WaitsFor(
assert => assert.IsTrue(),
timeout: TimeSpan.FromSeconds(5),
timeout: CancellationTestTimeout,
cancellationToken: cts.Token);

await Assert.That(act).Throws<OperationCanceledException>();

stopwatch.Stop();

await Assert.That(stopwatch.Elapsed).IsLessThan(TimeSpan.FromMilliseconds(500));
await Assert.That(stopwatch.Elapsed).IsLessThan(CancellationElapsedBudget);
}

[Test]
Expand All @@ -475,19 +483,24 @@ public async Task Eventually_PropagatesExternalCancellation_Before_Internal_Time
var act = async () => await Assert.That(() => false)
.Eventually(
assert => assert.IsTrue(),
timeout: TimeSpan.FromSeconds(30),
timeout: CancellationTestTimeout,
cancellationToken: cts.Token);

await Assert.That(act).Throws<OperationCanceledException>();

stopwatch.Stop();

await Assert.That(stopwatch.Elapsed).IsLessThan(TimeSpan.FromSeconds(1));
await Assert.That(stopwatch.Elapsed).IsLessThan(CancellationElapsedBudget);
}

[Test]
public async Task WaitsFor_Propagates_OCE_When_Predicate_Observes_Supplied_Token()
{
// The predicate and WaitsFor deliberately share one token. The Func source captures the
// predicate's OperationCanceledException as an evaluation exception, so it never escapes on
// its own; the OCE the caller sees comes from WaitsFor observing the same token. The test
// proves that a token-aware predicate does not turn cancellation into an AssertionException.
// With an independent token passed to WaitsFor, the loop would poll until its 30s timeout.
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(50));

Expand All @@ -502,14 +515,14 @@ public async Task WaitsFor_Propagates_OCE_When_Predicate_Observes_Supplied_Token
var act = async () => await Assert.That(tokenAwarePredicate)
.WaitsFor(
assert => assert.IsTrue(),
timeout: TimeSpan.FromSeconds(30),
timeout: CancellationTestTimeout,
cancellationToken: cts.Token);

await Assert.That(act).Throws<OperationCanceledException>();

stopwatch.Stop();

await Assert.That(stopwatch.Elapsed).IsLessThan(TimeSpan.FromMilliseconds(500));
await Assert.That(stopwatch.Elapsed).IsLessThan(CancellationElapsedBudget);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

// Helper class for testing complex objects
Expand Down
Loading