In tree diffing, better handle scenario where both old and new sequences loop back but to different locations

This commit is contained in:
Steve Sanderson 2018-01-21 19:37:34 +00:00
parent 6b37494e7c
commit 7a1abbaca3
2 changed files with 67 additions and 0 deletions

View File

@ -63,12 +63,20 @@ namespace Microsoft.Blazor.RenderTree
// preordered merge join (picking from whichever side brings us closer to being
// back in sync)
treatAsInsert = newSeq < oldSeq;
if (oldLoopedBack)
{
// If both old and new have now looped back, we must reset their 'looped back'
// tracker so we can treat them as proceeding through the same loop block
prevOldSeq = prevNewSeq = -1;
}
}
else if (oldLoopedBack)
{
// Old sequence looped back but new one didn't
// The new sequence either has some extra trailing elements in the current loop block
// which we should insert, or omits some old trailing loop blocks which we should delete
// TODO: Find a way of not recomputing this next flag on every iteration
var newLoopsBackLater = false;
for (var testIndex = newStartIndex + 1; testIndex < newEndIndexExcl; testIndex++)
{
@ -91,6 +99,7 @@ namespace Microsoft.Blazor.RenderTree
// The old sequence either has some extra trailing elements in the current loop block
// which we should delete, or the new sequence has extra trailing loop blocks which we
// should insert
// TODO: Find a way of not recomputing this next flag on every iteration
var oldLoopsBackLater = false;
for (var testIndex = oldStartIndex + 1; testIndex < oldEndIndexExcl; testIndex++)
{

View File

@ -212,6 +212,64 @@ namespace Microsoft.Blazor.Test
});
}
[Fact]
public void RecognizesLeadingLoopBlockItemsBeingAdded()
{
// Arrange
var oldTree = new RenderTreeBuilder(new FakeRenderer());
var newTree = new RenderTreeBuilder(new FakeRenderer());
var diff = new RenderTreeDiff();
oldTree.AddText(2, "x");
oldTree.AddText(2, "x"); // Note that the '0' and '1' items are not present on this iteration
newTree.AddText(2, "x");
newTree.AddText(0, "x");
newTree.AddText(1, "x");
newTree.AddText(2, "x");
// Act
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
entry =>
{
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
Assert.Equal(1, entry.NewTreeIndex);
},
entry =>
{
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
Assert.Equal(2, entry.NewTreeIndex);
},
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
}
[Fact]
public void RecognizesLeadingLoopBlockItemsBeingRemoved()
{
// Arrange
var oldTree = new RenderTreeBuilder(new FakeRenderer());
var newTree = new RenderTreeBuilder(new FakeRenderer());
var diff = new RenderTreeDiff();
oldTree.AddText(2, "x");
oldTree.AddText(0, "x");
oldTree.AddText(1, "x");
oldTree.AddText(2, "x");
newTree.AddText(2, "x");
newTree.AddText(2, "x"); // Note that the '0' and '1' items are not present on this iteration
// Act
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
// Assert
Assert.Collection(result,
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
}
[Fact]
public void HandlesAdjacentItemsBeingRemovedAndInsertedAtOnce()
{