Skip to content

Commit 2e782ed

Browse files
committed
fix: block render updates and user access to TestRenderer's state after it has been disposed
1 parent 2d76c38 commit 2e782ed

1 file changed

Lines changed: 36 additions & 9 deletions

File tree

src/bunit.core/Rendering/TestRenderer.cs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class TestRenderer : Renderer, ITestRenderer
1414
private readonly List<RootComponent> rootComponents = new();
1515
private readonly ILogger<TestRenderer> logger;
1616
private readonly IRenderedComponentActivator activator;
17+
private bool disposed;
1718
private TaskCompletionSource<Exception> unhandledExceptionTsc = new(TaskCreationOptions.RunContinuationsAsynchronously);
1819
private Exception? capturedUnhandledException;
1920

@@ -81,6 +82,9 @@ public Task DispatchEventAsync(
8182
if (fieldInfo is null)
8283
throw new ArgumentNullException(nameof(fieldInfo));
8384

85+
if (disposed)
86+
throw new ObjectDisposedException(nameof(TestRenderer));
87+
8488
// Calling base.DispatchEventAsync updates the render tree
8589
// if the event contains associated data.
8690
lock (renderTreeUpdateLock)
@@ -134,6 +138,9 @@ public IReadOnlyList<IRenderedComponentBase<TComponent>> FindComponents<TCompone
134138
/// <inheritdoc />
135139
public void DisposeComponents()
136140
{
141+
if (disposed)
142+
throw new ObjectDisposedException(nameof(TestRenderer));
143+
137144
// The dispatcher will always return a completed task,
138145
// when dealing with an IAsyncDisposable.
139146
// Therefore checking for a completed task and awaiting it
@@ -160,6 +167,9 @@ public void DisposeComponents()
160167
/// <inheritdoc/>
161168
protected override void ProcessPendingRender()
162169
{
170+
if (disposed)
171+
return;
172+
163173
// Blocks updates to the renderers internal render tree
164174
// while the render tree is being read elsewhere.
165175
// base.ProcessPendingRender calls UpdateDisplayAsync,
@@ -173,6 +183,9 @@ protected override void ProcessPendingRender()
173183
/// <inheritdoc/>
174184
protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
175185
{
186+
if (disposed)
187+
return Task.CompletedTask;
188+
176189
if (usersSyncContext is not null && usersSyncContext != SynchronizationContext.Current)
177190
{
178191
// The users' sync context, typically one established by
@@ -243,23 +256,34 @@ private void UpdateDisplay(in RenderBatch renderBatch)
243256
/// <inheritdoc/>
244257
protected override void Dispose(bool disposing)
245258
{
246-
if (disposing)
259+
if (disposed)
260+
return;
261+
262+
disposed = true;
263+
264+
lock (renderTreeUpdateLock)
247265
{
248-
foreach (var rc in renderedComponents.Values)
266+
if (disposing)
249267
{
250-
rc.Dispose();
268+
foreach (var rc in renderedComponents.Values)
269+
{
270+
rc.Dispose();
271+
}
272+
273+
renderedComponents.Clear();
274+
unhandledExceptionTsc.TrySetCanceled();
251275
}
252276

253-
renderedComponents.Clear();
254-
unhandledExceptionTsc.TrySetCanceled();
277+
base.Dispose(disposing);
255278
}
256-
257-
base.Dispose(disposing);
258279
}
259280

260281
private TResult Render<TResult>(RenderFragment renderFragment, Func<int, TResult> activator)
261282
where TResult : IRenderedFragmentBase
262283
{
284+
if (disposed)
285+
throw new ObjectDisposedException(nameof(TestRenderer));
286+
263287
var renderTask = Dispatcher.InvokeAsync(() =>
264288
{
265289
ResetUnhandledException();
@@ -298,6 +322,9 @@ private IReadOnlyList<IRenderedComponentBase<TComponent>> FindComponents<TCompon
298322
if (parentComponent is null)
299323
throw new ArgumentNullException(nameof(parentComponent));
300324

325+
if (disposed)
326+
throw new ObjectDisposedException(nameof(TestRenderer));
327+
301328
var result = new List<IRenderedComponentBase<TComponent>>();
302329
var framesCollection = new RenderTreeFrameDictionary();
303330

@@ -387,7 +414,7 @@ private ArrayRange<RenderTreeFrame> GetOrLoadRenderTreeFrame(RenderTreeFrameDict
387414
/// <inheritdoc/>
388415
protected override void HandleException(Exception exception)
389416
{
390-
if (exception is null)
417+
if (exception is null || disposed)
391418
return;
392419

393420
logger.LogUnhandledException(exception);
@@ -411,7 +438,7 @@ private void ResetUnhandledException()
411438

412439
private void AssertNoUnhandledExceptions()
413440
{
414-
if (capturedUnhandledException is Exception unhandled)
441+
if (capturedUnhandledException is Exception unhandled && !disposed)
415442
{
416443
capturedUnhandledException = null;
417444

0 commit comments

Comments
 (0)