Skip to content

Commit 0602840

Browse files
Tests and fixes
1 parent 6b9bef5 commit 0602840

File tree

21 files changed

+403
-196
lines changed

21 files changed

+403
-196
lines changed

src/Components/Components/perf/RenderTreeDiffBuilderBenchmark.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public RenderTreeDiffBuilderBenchmark()
7979
public void ComputeDiff_SingleFormField()
8080
{
8181
builder.ClearStateForCurrentBatch();
82-
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, builder, 0, modified.GetFrames(), original.GetFrames(), original.GetNamedEvents());
82+
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, builder, 0, modified.GetFrames(), original.GetFrames());
8383
GC.KeepAlive(diff);
8484
}
8585

src/Components/Components/src/PublicAPI.Unshipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ Microsoft.AspNetCore.Components.RenderTree.NamedEvent.NamedEvent() -> void
4141
Microsoft.AspNetCore.Components.RenderTree.NamedEvent.NamedEvent(int componentId, int frameIndex, string! eventType, string! assignedName) -> void
4242
Microsoft.AspNetCore.Components.RenderTree.RenderBatch.AddedNamedEvents.get -> Microsoft.AspNetCore.Components.RenderTree.ArrayRange<Microsoft.AspNetCore.Components.RenderTree.NamedEvent>?
4343
Microsoft.AspNetCore.Components.RenderTree.RenderBatch.RemovedNamedEvents.get -> Microsoft.AspNetCore.Components.RenderTree.ArrayRange<Microsoft.AspNetCore.Components.RenderTree.NamedEvent>?
44-
Microsoft.AspNetCore.Components.RenderTree.Renderer.GetCurrentRenderTreeFrames(int componentId, bool optional) -> Microsoft.AspNetCore.Components.RenderTree.ArrayRange<Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame>?
4544
Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame.ComponentFrameFlags.get -> Microsoft.AspNetCore.Components.RenderTree.ComponentFrameFlags
4645
Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType.ComponentRenderMode = 9 -> Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType
4746
Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType.NamedEvent = 10 -> Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrameType

src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
using System.Diagnostics;
77
using Microsoft.AspNetCore.Components.HotReload;
8-
using Microsoft.AspNetCore.Components.Reflection;
98
using Microsoft.AspNetCore.Components.Rendering;
109

1110
namespace Microsoft.AspNetCore.Components.RenderTree;

src/Components/Components/src/RenderTree/RenderTreeFrame.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,12 @@ internal static RenderTreeFrame ElementReferenceCapture(int sequence, Action<Ele
414414
internal static RenderTreeFrame ComponentReferenceCapture(int sequence, Action<object> componentReferenceCaptureAction, int parentFrameIndex)
415415
=> new RenderTreeFrame(sequence, componentReferenceCaptureAction: componentReferenceCaptureAction, parentFrameIndex: parentFrameIndex);
416416

417+
internal static RenderTreeFrame NamedEvent(int sequence, string eventType, string assignedName)
418+
=> new RenderTreeFrame { SequenceField = sequence, FrameTypeField = RenderTreeFrameType.NamedEvent, NamedEventTypeField = eventType, NamedEventAssignedNameField = assignedName };
419+
420+
internal static RenderTreeFrame ComponentRenderModeFrame(int sequence, IComponentRenderMode renderMode)
421+
=> new RenderTreeFrame { SequenceField = sequence, FrameTypeField = RenderTreeFrameType.ComponentRenderMode, ComponentRenderModeField = renderMode };
422+
417423
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
418424
=> new RenderTreeFrame(SequenceField, elementSubtreeLength: elementSubtreeLength, ElementNameField, ElementKeyField);
419425

src/Components/Components/src/Rendering/RenderTreeBuilder.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public sealed class RenderTreeBuilder : IDisposable
2828
private bool _hasSeenAddMultipleAttributes;
2929
private Dictionary<string, int>? _seenAttributeNames;
3030
private IComponentRenderMode? _pendingComponentCallSiteRenderMode; // TODO: Remove when Razor compiler supports call-site @rendermode
31+
private (int Sequence, string AssignedName)? _pendingNamedSubmitEvent; // TODO: Remove when Razor compiler supports @onsubmit:name
3132

3233
/// <summary>
3334
/// The reserved parameter name used for supplying child content.
@@ -63,6 +64,12 @@ public void OpenElement(int sequence, string elementName)
6364
/// </summary>
6465
public void CloseElement()
6566
{
67+
if (_pendingNamedSubmitEvent is { } pendingNamedSubmitEvent)
68+
{
69+
AddNamedEvent(pendingNamedSubmitEvent.Sequence, "onsubmit", pendingNamedSubmitEvent.AssignedName);
70+
_pendingNamedSubmitEvent = default;
71+
}
72+
6673
var indexOfEntryBeingClosed = _openElementIndices.Pop();
6774

6875
// We might be closing an element with only attributes, run the duplicate cleanup pass
@@ -226,7 +233,7 @@ public void AddAttribute(int sequence, string name, string? value)
226233
// That should compile directly as a call to AddNamedValue.
227234
if (string.Equals(name, "@onsubmit:name", StringComparison.Ordinal) && _lastNonAttributeFrameType == RenderTreeFrameType.Element)
228235
{
229-
AddNamedEvent(sequence, "onsubmit", value!);
236+
_pendingNamedSubmitEvent = (sequence, value!);
230237
}
231238
else
232239
{
@@ -704,6 +711,9 @@ public void AddComponentRenderMode(int sequence, IComponentRenderMode renderMode
704711
/// <param name="assignedName">The application-assigned name.</param>
705712
public void AddNamedEvent(int sequence, string eventType, string assignedName)
706713
{
714+
ArgumentNullException.ThrowIfNull(eventType);
715+
ArgumentNullException.ThrowIfNull(assignedName);
716+
707717
// Note that we could trivially extend this to a generic concept of "named values" that exist within the rendertree
708718
// and are tracked when added, removed, or updated. Currently we don't need that generality, but if we ever do, we
709719
// can replace RenderTreeFrameType.NamedEvent with RenderTreeFrameType.NamedValue and use it to implement named events.

src/Components/Components/test/RenderTreeDiffBuilderTest.cs

Lines changed: 139 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ public void RecognizesComponentTypeChangesAtSameSequenceNumber()
816816
using var batchBuilder = new RenderBatchBuilder();
817817

818818
// Act
819-
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames(), newTree.GetNamedEvents());
819+
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames());
820820

821821
// Assert: We're going to dispose the old component and render the new one
822822
Assert.Equal(new[] { 0 }, batchBuilder.ComponentDisposalQueue);
@@ -1627,7 +1627,7 @@ public void RetainsChildComponentsForExistingFrames()
16271627

16281628
using var batchBuilder = new RenderBatchBuilder();
16291629
using var renderTreeBuilder = new RenderTreeBuilder();
1630-
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames(), oldTree.GetNamedEvents());
1630+
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames());
16311631
var originalFakeComponentInstance = oldTree.GetFrames().Array[2].Component;
16321632
var originalFakeComponent2Instance = oldTree.GetFrames().Array[3].Component;
16331633

@@ -1713,7 +1713,7 @@ public void SetsUpdatedParametersOnChildComponents()
17131713

17141714
using var batchBuilder = new RenderBatchBuilder();
17151715
using var renderTree = new RenderTreeBuilder();
1716-
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTree.GetFrames(), oldTree.GetFrames(), oldTree.GetNamedEvents());
1716+
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTree.GetFrames(), oldTree.GetFrames());
17171717
var originalComponentInstance = (FakeComponent)oldTree.GetFrames().Array[0].Component;
17181718

17191719
// Act
@@ -1763,7 +1763,7 @@ public void SkipsUpdatingParametersOnChildComponentsIfAllAreDefinitelyImmutableA
17631763

17641764
using var batchBuilder = new RenderBatchBuilder();
17651765
using var renderTreeBuilder = new RenderTreeBuilder();
1766-
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames(), oldTree.GetNamedEvents());
1766+
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames());
17671767
var originalComponentInstance = (CaptureSetParametersComponent)oldTree.GetFrames().Array[0].Component;
17681768
Assert.Equal(1, originalComponentInstance.SetParametersCallCount);
17691769

@@ -1793,7 +1793,7 @@ public void AlwaysRegardsRenderFragmentAsPossiblyChanged()
17931793

17941794
using var batchBuilder = new RenderBatchBuilder();
17951795
using var renderTreeBuilder = new RenderTreeBuilder();
1796-
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames(), oldTree.GetNamedEvents());
1796+
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames());
17971797
var componentInstance = (CaptureSetParametersComponent)oldTree.GetFrames().Array[0].Component;
17981798
Assert.Equal(1, componentInstance.SetParametersCallCount);
17991799

@@ -1819,13 +1819,13 @@ public void QueuesRemovedChildComponentsForDisposal()
18191819

18201820
using var batchBuilder = new RenderBatchBuilder();
18211821
using var renderTree = new RenderTreeBuilder();
1822-
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTree.GetFrames(), oldTree.GetFrames(), oldTree.GetNamedEvents());
1822+
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTree.GetFrames(), oldTree.GetFrames());
18231823

18241824
// Act/Assert
18251825
// Note that we track NonDisposableComponent was disposed even though it's not IDisposable,
18261826
// because it's up to the upstream renderer to decide what "disposing" a component means
18271827
Assert.Empty(batchBuilder.ComponentDisposalQueue);
1828-
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames(), newTree.GetNamedEvents());
1828+
RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames());
18291829
Assert.Equal(new[] { 0, 1 }, batchBuilder.ComponentDisposalQueue);
18301830
}
18311831

@@ -2211,24 +2211,136 @@ public void CanChangeFrameTypeWithMatchingKey()
22112211
entry => AssertEdit(entry, RenderTreeEditType.RemoveFrame, 1));
22122212
}
22132213

2214+
[Fact]
2215+
public void RecognizesNamedEventBeingAdded()
2216+
{
2217+
oldTree.OpenElement(0, "existing");
2218+
oldTree.AddAttribute(1, "attr1", "unrelated val1");
2219+
oldTree.CloseElement();
2220+
2221+
newTree.OpenElement(0, "existing");
2222+
newTree.AddAttribute(1, "attr1", "unrelated val1");
2223+
newTree.AddNamedEvent(2, "someevent1", "added to existing element");
2224+
newTree.CloseElement();
2225+
newTree.OpenElement(3, "new element");
2226+
newTree.AddNamedEvent(4, "someevent2", "added with new element");
2227+
newTree.CloseElement();
2228+
2229+
// Act
2230+
var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(componentId: 123);
2231+
2232+
// Assert
2233+
Assert.Collection(result.Edits,
2234+
entry =>
2235+
{
2236+
AssertEdit(entry, RenderTreeEditType.PrependFrame, 1);
2237+
Assert.Equal(0, entry.ReferenceFrameIndex);
2238+
Assert.Equal("new element", referenceFrames[entry.ReferenceFrameIndex].ElementName);
2239+
});
2240+
Assert.Collection(batch.AddedNamedEvents.Value.AsEnumerable(),
2241+
entry => AssertNamedEvent(entry, 123, 2, "someevent1", "added to existing element"),
2242+
entry => AssertNamedEvent(entry, 123, 4, "someevent2", "added with new element"));
2243+
Assert.False(batch.RemovedNamedEvents.HasValue);
2244+
}
2245+
2246+
[Fact]
2247+
public void RecognizesNamedEventBeingRemoved()
2248+
{
2249+
oldTree.OpenElement(0, "retaining");
2250+
oldTree.AddAttribute(1, "attr1", "unrelated val1");
2251+
oldTree.AddNamedEvent(2, "someevent1", "removing from retained element");
2252+
oldTree.CloseElement();
2253+
oldTree.OpenElement(3, "removing");
2254+
oldTree.AddNamedEvent(4, "someevent2", "removed because element was removed");
2255+
oldTree.CloseElement();
2256+
2257+
newTree.OpenElement(0, "retaining");
2258+
newTree.AddAttribute(1, "attr1", "unrelated val1");
2259+
newTree.CloseElement();
2260+
2261+
// Act
2262+
var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(componentId: 123);
2263+
2264+
// Assert
2265+
Assert.Collection(result.Edits,
2266+
entry => AssertEdit(entry, RenderTreeEditType.RemoveFrame, 1));
2267+
Assert.False(batch.AddedNamedEvents.HasValue);
2268+
Assert.Collection(batch.RemovedNamedEvents.Value.AsEnumerable(),
2269+
entry => AssertNamedEvent(entry, 123, 2, "someevent1", "removing from retained element"),
2270+
entry => AssertNamedEvent(entry, 123, 4, "someevent2", "removed because element was removed"));
2271+
}
2272+
2273+
[Fact]
2274+
public void RecognizesNamedEventBeingMoved()
2275+
{
2276+
oldTree.OpenElement(0, "elem");
2277+
oldTree.AddNamedEvent(2, "eventname", "assigned name");
2278+
oldTree.CloseElement();
2279+
2280+
newTree.OpenElement(0, "elem");
2281+
newTree.AddAttribute(1, "attr1", "unrelated val1");
2282+
newTree.AddNamedEvent(2, "eventname", "assigned name");
2283+
newTree.CloseElement();
2284+
2285+
// Act
2286+
var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(componentId: 123);
2287+
2288+
// Assert
2289+
Assert.Collection(result.Edits,
2290+
entry =>
2291+
{
2292+
AssertEdit(entry, RenderTreeEditType.SetAttribute, 0);
2293+
Assert.Equal(0, entry.ReferenceFrameIndex);
2294+
Assert.Equal("attr1", referenceFrames[entry.ReferenceFrameIndex].AttributeName);
2295+
});
2296+
Assert.Collection(batch.RemovedNamedEvents.Value.AsEnumerable(),
2297+
entry => AssertNamedEvent(entry, 123, 1, "eventname", "assigned name"));
2298+
Assert.Collection(batch.AddedNamedEvents.Value.AsEnumerable(),
2299+
entry => AssertNamedEvent(entry, 123, 2, "eventname", "assigned name"));
2300+
}
2301+
2302+
[Fact]
2303+
public void RecognizesNamedEventChangingAssignedName()
2304+
{
2305+
oldTree.OpenElement(0, "elem");
2306+
oldTree.AddNamedEvent(1, "eventname1", "original name");
2307+
oldTree.AddNamedEvent(2, "eventname2", "will be left unchanged");
2308+
oldTree.CloseElement();
2309+
2310+
newTree.OpenElement(0, "elem");
2311+
newTree.AddNamedEvent(1, "eventname1", "changed name");
2312+
newTree.AddNamedEvent(2, "eventname2", "will be left unchanged");
2313+
newTree.CloseElement();
2314+
2315+
// Act
2316+
var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(componentId: 123);
2317+
2318+
// Assert
2319+
Assert.Empty(result.Edits);
2320+
Assert.Collection(batch.RemovedNamedEvents.Value.AsEnumerable(),
2321+
entry => AssertNamedEvent(entry, 123, 1, "eventname1", "original name"));
2322+
Assert.Collection(batch.AddedNamedEvents.Value.AsEnumerable(),
2323+
entry => AssertNamedEvent(entry, 123, 1, "eventname1", "changed name"));
2324+
}
2325+
22142326
private (RenderTreeDiff, RenderTreeFrame[]) GetSingleUpdatedComponent(bool initializeFromFrames = false)
22152327
{
22162328
var result = GetSingleUpdatedComponentWithBatch(initializeFromFrames);
22172329
return (result.Item1, result.Item2);
22182330
}
22192331

2220-
private (RenderTreeDiff, RenderTreeFrame[], RenderBatch) GetSingleUpdatedComponentWithBatch(bool initializeFromFrames = false)
2332+
private (RenderTreeDiff, RenderTreeFrame[], RenderBatch) GetSingleUpdatedComponentWithBatch(bool initializeFromFrames = false, int componentId = 0)
22212333
{
2222-
var batch = GetRenderedBatch(initializeFromFrames);
2334+
var batch = GetRenderedBatch(initializeFromFrames, componentId);
22232335
var diffsInBatch = batch.UpdatedComponents;
22242336
Assert.Equal(1, diffsInBatch.Count);
22252337
return (diffsInBatch.Array[0], batch.ReferenceFrames.AsEnumerable().ToArray(), batch);
22262338
}
22272339

2228-
private RenderBatch GetRenderedBatch(bool initializeFromFrames = false)
2229-
=> GetRenderedBatch(oldTree, newTree, initializeFromFrames);
2340+
private RenderBatch GetRenderedBatch(bool initializeFromFrames = false, int componentId = 0)
2341+
=> GetRenderedBatch(oldTree, newTree, initializeFromFrames, componentId);
22302342

2231-
private RenderBatch GetRenderedBatch(RenderTreeBuilder from, RenderTreeBuilder to, bool initializeFromFrames)
2343+
private RenderBatch GetRenderedBatch(RenderTreeBuilder from, RenderTreeBuilder to, bool initializeFromFrames, int componentId = 0)
22322344
{
22332345
if (initializeFromFrames)
22342346
{
@@ -2238,14 +2350,14 @@ private RenderBatch GetRenderedBatch(RenderTreeBuilder from, RenderTreeBuilder t
22382350
var emptyFrames = renderTreeBuilder.GetFrames();
22392351
var oldFrames = from.GetFrames();
22402352

2241-
RenderTreeDiffBuilder.ComputeDiff(renderer, initializeBatchBuilder, 0, emptyFrames, oldFrames, from.GetNamedEvents());
2353+
RenderTreeDiffBuilder.ComputeDiff(renderer, initializeBatchBuilder, 0, emptyFrames, oldFrames);
22422354
}
22432355

22442356
batchBuilder?.Dispose();
22452357
// This gets disposed as part of the test type's Dispose
22462358
batchBuilder = new RenderBatchBuilder();
22472359

2248-
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, from.GetFrames(), to.GetFrames(), to.GetNamedEvents());
2360+
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, componentId, from.GetFrames(), to.GetFrames());
22492361
batchBuilder.UpdatedComponentDiffs.Append(diff);
22502362
return batchBuilder.ToBatch();
22512363
}
@@ -2373,4 +2485,17 @@ private static void AssertPermutationListEntry(
23732485
Assert.Equal(fromSiblingIndex, edit.SiblingIndex);
23742486
Assert.Equal(toSiblingIndex, edit.MoveToSiblingIndex);
23752487
}
2488+
2489+
private static void AssertNamedEvent(
2490+
NamedEvent namedEvent,
2491+
int componentId,
2492+
int frameIndex,
2493+
string eventType,
2494+
string assignedName)
2495+
{
2496+
Assert.Equal(componentId, namedEvent.ComponentId);
2497+
Assert.Equal(frameIndex, namedEvent.FrameIndex);
2498+
Assert.Equal(eventType, namedEvent.EventType);
2499+
Assert.Equal(assignedName, namedEvent.AssignedName);
2500+
}
23762501
}

0 commit comments

Comments
 (0)