Skip to content

Commit 55bd9c9

Browse files
committed
Prevented race condition between disposing of WaitForHandler and starting timer. Tweaks to FixtureBase to provide better error messages
1 parent 968a632 commit 55bd9c9

2 files changed

Lines changed: 31 additions & 13 deletions

File tree

src/bunit.core/Extensions/WaitForHelpers/WaitForHelper.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,21 @@ protected WaitForHelper(IRenderedFragmentBase renderedFragment, Func<bool> compl
5555

5656
_renderedFragment.OnAfterRender += OnAfterRender;
5757
OnAfterRender();
58+
StartTimer(timeout);
59+
}
60+
61+
private void StartTimer(TimeSpan? timeout)
62+
{
63+
if (_isDisposed)
64+
return;
65+
66+
lock (_completionSouce)
67+
{
68+
if (_isDisposed)
69+
return;
5870

59-
if (!_isDisposed)
6071
_timer.Change(GetRuntimeTimeout(timeout), Timeout.InfiniteTimeSpan);
72+
}
6173
}
6274

6375
private void OnAfterRender()
@@ -123,11 +135,17 @@ public void Dispose()
123135
if (_isDisposed)
124136
return;
125137

126-
_isDisposed = true;
127-
_renderedFragment.OnAfterRender -= OnAfterRender;
128-
_timer.Dispose();
129-
_completionSouce.TrySetCanceled();
130-
_logger.LogDebug(new EventId(6, nameof(Dispose)), $"The state wait helper for component {_renderedFragment.ComponentId} disposed");
138+
lock (_completionSouce)
139+
{
140+
if (_isDisposed)
141+
return;
142+
143+
_isDisposed = true;
144+
_renderedFragment.OnAfterRender -= OnAfterRender;
145+
_timer.Dispose();
146+
_completionSouce.TrySetCanceled();
147+
_logger.LogDebug(new EventId(6, nameof(Dispose)), $"The state wait helper for component {_renderedFragment.ComponentId} disposed");
148+
}
131149
}
132150

133151
private static TimeSpan GetRuntimeTimeout(TimeSpan? timeout)

src/bunit.core/RazorTesting/FixtureBase.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ public override Task SetParametersAsync(ParameterView parameters)
7272
TestAsync = parameters.GetValueOrDefault<Func<TFixture, Task>>(nameof(TestAsync));
7373

7474
#pragma warning disable CS0618 // Type or member is obsolete
75-
if (parameters.TryGetValue<IReadOnlyCollection<Action<TFixture>>>("Tests", out var tests))
75+
if (parameters.TryGetValue<IReadOnlyCollection<Action<TFixture>>>(nameof(Tests), out var tests))
7676
Tests = tests;
77-
if (parameters.TryGetValue<IReadOnlyCollection<Func<TFixture, Task>>>("TestsAsync", out var asyncTests))
77+
if (parameters.TryGetValue<IReadOnlyCollection<Func<TFixture, Task>>>(nameof(TestsAsync), out var asyncTests))
7878
TestsAsync = asyncTests;
7979
#pragma warning restore CS0618 // Type or member is obsolete
8080

@@ -86,16 +86,16 @@ public override void Validate()
8686
{
8787
base.Validate();
8888
if (ChildContent is null)
89-
throw new ArgumentException($"No {nameof(ChildContent)} specified in the {GetType().Name} component.");
89+
throw new ArgumentException($"No '{nameof(ChildContent)}' specified in the {GetType().Name} component.");
9090
if (Test is null && TestAsync is null)
91-
throw new ArgumentException($"No test/assertions provided to the {GetType().Name} component.");
91+
throw new ArgumentException($"No test action provided via the '{nameof(Test)}' or '{nameof(TestAsync)}' parameters to the {GetType().Name} component.");
9292
if (Test is { } && TestAsync is { })
93-
throw new ArgumentException($"Only a single test method can be provided to the {GetType().Name} component at the time.");
93+
throw new ArgumentException($"Only one of the '{nameof(Test)}' or '{nameof(TestAsync)}' actions can be provided to the {GetType().Name} component at the same time.");
9494
#pragma warning disable CS0618 // Type or member is obsolete
9595
if (Tests is { })
96-
throw new ArgumentException($"The user of the Tests parameter has been obsoleted, and any methods assigned to it will not longer be invoked.");
96+
throw new ArgumentException($"The use of the '{nameof(Tests)}' parameter has been obsoleted, and any methods assigned to it will not longer be invoked.");
9797
if (TestsAsync is { })
98-
throw new ArgumentException($"The user of the TestsAsync parameter has been obsoleted, and any methods assigned to it will not longer be invoked.");
98+
throw new ArgumentException($"The use of the '{nameof(TestsAsync)}' parameter has been obsoleted, and any methods assigned to it will not longer be invoked.");
9999
#pragma warning restore CS0618 // Type or member is obsolete
100100
}
101101

0 commit comments

Comments
 (0)