From 99c0cc9d5e9351b475235234a7a419796864ef7d Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 6 Aug 2019 17:34:46 +0100 Subject: [PATCH] Ensure render batches aren't started from inside each other --- .../Components/src/Rendering/Renderer.cs | 6 +++ .../Components/test/RendererTest.cs | 40 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/Components/Components/src/Rendering/Renderer.cs b/src/Components/Components/src/Rendering/Renderer.cs index 048ba9a868ad..ed3d075f0aa0 100644 --- a/src/Components/Components/src/Rendering/Renderer.cs +++ b/src/Components/Components/src/Rendering/Renderer.cs @@ -409,6 +409,12 @@ protected virtual void ProcessPendingRender() private void ProcessRenderQueue() { EnsureSynchronizationContext(); + + if (_isBatchInProgress) + { + throw new InvalidOperationException("Cannot start a batch when one is already in progress."); + } + _isBatchInProgress = true; var updateDisplayTask = Task.CompletedTask; diff --git a/src/Components/Components/test/RendererTest.cs b/src/Components/Components/test/RendererTest.cs index f895c47f10d6..8d3ce2357000 100644 --- a/src/Components/Components/test/RendererTest.cs +++ b/src/Components/Components/test/RendererTest.cs @@ -3395,6 +3395,27 @@ public void EventFieldInfoWorksWhenEventHandlerIdWasSuperseded() } } + [Fact] + public void CannotStartOverlappingBatches() + { + // Arrange + var renderer = new InvalidRecursiveRenderer(); + var component = new CallbackOnRenderComponent(() => + { + // The renderer disallows one batch to be started inside another, because that + // would violate all kinds of state tracking invariants. It's not something that + // would ever happen except if you subclass the renderer and do something unsupported + // that commences batches from inside each other. + renderer.ProcessPendingRender(); + }); + var componentId = renderer.AssignRootComponentId(component); + + // Act/Assert + var ex = Assert.Throws( + () => renderer.RenderRootComponent(componentId)); + Assert.Contains("Cannot start a batch when one is already in progress.", ex.Message); + } + private class NoOpRenderer : Renderer { public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance) @@ -4109,5 +4130,24 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) private class DerivedEventArgs : EventArgs { } + + class CallbackOnRenderComponent : AutoRenderComponent + { + private readonly Action _callback; + + public CallbackOnRenderComponent(Action callback) + { + _callback = callback; + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + => _callback(); + } + + class InvalidRecursiveRenderer : TestRenderer + { + public new void ProcessPendingRender() + => base.ProcessPendingRender(); + } } }