Skip to content

Commit e4de7a0

Browse files
committed
Feedback: Add more non-trivial cases tests.
1 parent a2c79c0 commit e4de7a0

File tree

1 file changed

+90
-8
lines changed

1 file changed

+90
-8
lines changed

src/Components/Components/test/OwningComponentBaseTest.cs

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,13 @@ public async Task DisposeAsync_ThenDispose_IsIdempotent()
141141

142142
_ = component.MyService;
143143

144-
// First disposal via DisposeAsync
145144
await ((IAsyncDisposable)component).DisposeAsync();
146145
var firstCallCount = component.DisposeCallCount;
147146
Assert.Equal(1, counter.DisposedCount);
148147

149-
// Second disposal via Dispose - user override is called but base class prevents double-disposal
150148
((IDisposable)component).Dispose();
151-
// User override is called again, but base.Dispose() returns early due to IsDisposed check
152-
Assert.True(component.DisposeCallCount >= firstCallCount); // Override may be called, but...
153-
Assert.Equal(1, counter.DisposedCount); // ...service should only be disposed once
149+
Assert.True(component.DisposeCallCount >= firstCallCount);
150+
Assert.Equal(1, counter.DisposedCount);
154151
}
155152

156153
[Fact]
@@ -166,11 +163,8 @@ public async Task DisposeAsyncCore_Override_WithException_StillCallsDispose()
166163

167164
_ = component.MyService;
168165

169-
// Even if DisposeAsyncCore throws, Dispose(true) should still be called
170-
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
171166
await ((IAsyncDisposable)component).DisposeAsync());
172167

173-
// Dispose should have been called due to try-finally
174168
Assert.True(component.DisposingParameter);
175169
Assert.True(component.IsDisposedPublic);
176170
}
@@ -215,4 +209,92 @@ private class MyOwningComponent : OwningComponentBase<MyService>
215209
// Expose IsDisposed for testing
216210
public bool IsDisposedPublic => IsDisposed;
217211
}
212+
213+
[Fact]
214+
public async Task ComplexComponent_DisposesResourcesOnlyWhenDisposingIsTrue()
215+
{
216+
var services = new ServiceCollection();
217+
services.AddSingleton<Counter>();
218+
services.AddTransient<MyService>();
219+
var serviceProvider = services.BuildServiceProvider();
220+
221+
var renderer = new TestRenderer(serviceProvider);
222+
var component = (ComplexComponent)renderer.InstantiateComponent<ComplexComponent>();
223+
224+
_ = component.MyService;
225+
226+
await ((IAsyncDisposable)component).DisposeAsync();
227+
228+
// Verify all managed resources were disposed because disposing=true
229+
Assert.True(component.TimerDisposed);
230+
Assert.True(component.CancellationTokenSourceDisposed);
231+
Assert.True(component.EventUnsubscribed);
232+
Assert.Equal(1, component.ManagedResourcesCleanedUpCount);
233+
}
234+
235+
[Fact]
236+
public void ComplexComponent_WithDisposingFalse_SkipsManagedResourceCleanup()
237+
{
238+
var services = new ServiceCollection();
239+
services.AddSingleton<Counter>();
240+
services.AddTransient<MyService>();
241+
var serviceProvider = services.BuildServiceProvider();
242+
243+
var renderer = new TestRenderer(serviceProvider);
244+
var component = (ComplexComponent)renderer.InstantiateComponent<ComplexComponent>();
245+
246+
_ = component.MyService;
247+
248+
component.TestDisposeWithFalse();
249+
250+
Assert.False(component.TimerDisposed);
251+
Assert.False(component.CancellationTokenSourceDisposed);
252+
Assert.False(component.EventUnsubscribed);
253+
Assert.Equal(0, component.ManagedResourcesCleanedUpCount);
254+
}
255+
256+
private class ComplexComponent : OwningComponentBase<MyService>
257+
{
258+
private readonly System.Threading.Timer _timer;
259+
private readonly CancellationTokenSource _cts;
260+
private bool _eventSubscribed;
261+
262+
public MyService MyService => Service;
263+
public bool TimerDisposed { get; private set; }
264+
public bool CancellationTokenSourceDisposed { get; private set; }
265+
public bool EventUnsubscribed { get; private set; }
266+
public int ManagedResourcesCleanedUpCount { get; private set; }
267+
268+
public ComplexComponent()
269+
{
270+
_timer = new System.Threading.Timer(_ => { }, null, Timeout.Infinite, Timeout.Infinite);
271+
_cts = new CancellationTokenSource();
272+
_eventSubscribed = true;
273+
}
274+
275+
public void TestDisposeWithFalse() => Dispose(disposing: false);
276+
277+
protected override void Dispose(bool disposing)
278+
{
279+
if (disposing)
280+
{
281+
_timer?.Dispose();
282+
TimerDisposed = true;
283+
284+
_cts?.Cancel();
285+
_cts?.Dispose();
286+
CancellationTokenSourceDisposed = true;
287+
288+
if (_eventSubscribed)
289+
{
290+
EventUnsubscribed = true;
291+
_eventSubscribed = false;
292+
}
293+
294+
ManagedResourcesCleanedUpCount++;
295+
}
296+
297+
base.Dispose(disposing);
298+
}
299+
}
218300
}

0 commit comments

Comments
 (0)