Skip to content

Commit dd61e65

Browse files
committed
fix: Fix test timeout handling in WaitForConditionAsync helper
Fix TaskCanceledException in Skip_ShouldSkipEvents test by improving timeout handling in WaitForConditionAsync. The helper now properly distinguishes between timeout expiration and external cancellation, throwing TimeoutException with a descriptive message when conditions aren't met within the timeout period. Also update GitHub Actions workflow to not fail on empty test results.
1 parent f39fc18 commit dd61e65

2 files changed

Lines changed: 42 additions & 24 deletions

File tree

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
name: Test Results
5757
path: '**/*.trx'
5858
reporter: dotnet-trx
59-
fail-on-error: true
59+
fail-on-empty: false
6060

6161
- name: Upload test artifacts on failure
6262
if: failure()

tests/MultiLock.Tests/TestHelpers.cs

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,37 @@ public static class TestHelpers
1919
/// <exception cref="TimeoutException">Thrown when the condition is not met within the timeout period.</exception>
2020
public static async Task WaitForConditionAsync(Func<bool> condition, TimeSpan timeout, CancellationToken cancellationToken, object? lockObject = null)
2121
{
22-
DateTimeOffset deadline = DateTimeOffset.UtcNow + timeout;
23-
while (DateTimeOffset.UtcNow < deadline)
22+
using var timeoutCts = new CancellationTokenSource(timeout);
23+
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
24+
25+
try
2426
{
25-
bool conditionMet;
26-
if (lockObject != null)
27+
while (true)
2728
{
28-
lock (lockObject)
29+
bool conditionMet;
30+
if (lockObject != null)
31+
{
32+
lock (lockObject)
33+
{
34+
conditionMet = condition();
35+
}
36+
}
37+
else
2938
{
3039
conditionMet = condition();
3140
}
32-
}
33-
else
34-
{
35-
conditionMet = condition();
36-
}
3741

38-
if (conditionMet)
39-
{
40-
return;
41-
}
42+
if (conditionMet)
43+
{
44+
return;
45+
}
4246

43-
await Task.Delay(TimeSpan.FromMilliseconds(10), cancellationToken);
47+
await Task.Delay(TimeSpan.FromMilliseconds(10), linkedCts.Token);
48+
}
49+
}
50+
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
51+
{
52+
throw new TimeoutException($"Condition was not met within the timeout period of {timeout.TotalSeconds} seconds.");
4453
}
4554
}
4655

@@ -53,17 +62,26 @@ public static async Task WaitForConditionAsync(Func<bool> condition, TimeSpan ti
5362
/// <exception cref="TimeoutException">Thrown when the condition is not met within the timeout period.</exception>
5463
public static async Task WaitForConditionAsync(Func<Task<bool>> condition, TimeSpan timeout, CancellationToken cancellationToken)
5564
{
56-
DateTimeOffset deadline = DateTimeOffset.UtcNow + timeout;
57-
while (DateTimeOffset.UtcNow < deadline)
58-
{
59-
bool conditionMet = await condition();
65+
using var timeoutCts = new CancellationTokenSource(timeout);
66+
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
6067

61-
if (conditionMet)
68+
try
69+
{
70+
while (true)
6271
{
63-
return;
64-
}
72+
bool conditionMet = await condition();
6573

66-
await Task.Delay(TimeSpan.FromMilliseconds(10), cancellationToken);
74+
if (conditionMet)
75+
{
76+
return;
77+
}
78+
79+
await Task.Delay(TimeSpan.FromMilliseconds(10), linkedCts.Token);
80+
}
81+
}
82+
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
83+
{
84+
throw new TimeoutException($"Condition was not met within the timeout period of {timeout.TotalSeconds} seconds.");
6785
}
6886
}
6987

0 commit comments

Comments
 (0)