Implement ParameterCollection

This commit is contained in:
Steve Sanderson 2018-02-13 13:14:55 +00:00
parent 695ddc0fd6
commit 37217db73a
4 changed files with 238 additions and 0 deletions

View File

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.RenderTree;
namespace Microsoft.AspNetCore.Blazor.Components
{
/// <summary>
/// Represents a single parameter supplied to an <see cref="IComponent"/>
/// by its parent in the render tree.
/// </summary>
public readonly struct Parameter
{
private readonly RenderTreeFrame[] _frames;
private readonly int _frameIndex;
internal Parameter(RenderTreeFrame[] frames, int currentIndex)
{
_frames = frames;
_frameIndex = currentIndex;
}
/// <summary>
/// Gets the name of the parameter.
/// </summary>
public string Name
=> _frames[_frameIndex].AttributeName;
/// <summary>
/// Gets the value of the parameter.
/// </summary>
public object Value
=> _frames[_frameIndex].AttributeValue;
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.RenderTree;
namespace Microsoft.AspNetCore.Blazor.Components
{
/// <summary>
/// Represents a collection of parameters supplied to an <see cref="IComponent"/>
/// by its parent in the render tree.
/// </summary>
public readonly struct ParameterCollection
{
private readonly RenderTreeFrame[] _frames;
private readonly int _ownerIndex;
internal ParameterCollection(RenderTreeFrame[] frames, int ownerIndex)
{
_frames = frames;
_ownerIndex = ownerIndex;
}
/// <summary>
/// Returns an enumerator that iterates through the <see cref="ParameterCollection"/>.
/// </summary>
/// <returns>The enumerator.</returns>
public ParameterEnumerator GetEnumerator()
=> new ParameterEnumerator(_frames, _ownerIndex);
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.RenderTree;
using System;
namespace Microsoft.AspNetCore.Blazor.Components
{
/// <summary>
/// An enumerator that iterates through a <see cref="ParameterCollection"/>.
/// </summary>
public struct ParameterEnumerator
{
private readonly RenderTreeFrame[] _frames;
private readonly int _ownerIndex;
private readonly int _ownerDescendantsEndIndexExcl;
private int _currentIndex;
internal ParameterEnumerator(RenderTreeFrame[] frames, int ownerIndex)
{
_frames = frames;
_ownerIndex = ownerIndex;
_ownerDescendantsEndIndexExcl = ownerIndex + _frames[ownerIndex].ElementSubtreeLength;
_currentIndex = ownerIndex;
}
/// <summary>
/// Gets the current value of the enumerator.
/// </summary>
public Parameter Current
=> _currentIndex > _ownerIndex
? new Parameter(_frames, _currentIndex)
: throw new InvalidOperationException("Iteration has not yet started.");
/// <summary>
/// Instructs the enumerator to move to the next value in the sequence.
/// </summary>
/// <returns></returns>
public bool MoveNext()
{
// Stop iteration if you get to the end of the owner's descendants...
var nextIndex = _currentIndex + 1;
if (nextIndex == _ownerDescendantsEndIndexExcl)
{
return false;
}
// ... or if you get to its first non-attribute descendant (because attributes
// are always before any other type of descendant)
if (_frames[nextIndex].FrameType != RenderTreeFrameType.Attribute)
{
return false;
}
_currentIndex = nextIndex;
return true;
}
}
}

View File

@ -0,0 +1,114 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.RenderTree;
using System;
using System.Collections.Generic;
using Xunit;
namespace Microsoft.AspNetCore.Blazor.Test
{
public class ParameterCollectionTest
{
[Fact]
public void CanInitializeUsingComponentWithNoDescendants()
{
// Arrange
var frames = new[]
{
RenderTreeFrame.ChildComponent<FakeComponent>(0).WithComponentSubtreeLength(1)
};
var parameterCollection = new ParameterCollection(frames, 0);
// Assert
Assert.Empty(ToEnumerable(parameterCollection));
}
[Fact]
public void CanInitializeUsingElementWithNoDescendants()
{
// Arrange
var frames = new[]
{
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(1)
};
var parameterCollection = new ParameterCollection(frames, 0);
// Assert
Assert.Empty(ToEnumerable(parameterCollection));
}
[Fact]
public void EnumerationStopsAtEndOfOwnerDescendants()
{
// Arrange
var attribute1Value = new object();
var attribute2Value = new object();
var frames = new[]
{
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(3),
RenderTreeFrame.Attribute(1, "attribute 1", attribute1Value),
RenderTreeFrame.Attribute(2, "attribute 2", attribute2Value),
// Although RenderTreeBuilder doesn't let you add orphaned attributes like this,
// still want to verify that ParameterCollection doesn't attempt to read past the
// end of the owner's descendants
RenderTreeFrame.Attribute(3, "orphaned attribute", "value")
};
var parameterCollection = new ParameterCollection(frames, 0);
// Assert
Assert.Collection(ToEnumerable(parameterCollection),
AssertParameter("attribute 1", attribute1Value),
AssertParameter("attribute 2", attribute2Value));
}
[Fact]
public void EnumerationStopsAtEndOfOwnerAttributes()
{
// Arrange
var attribute1Value = new object();
var attribute2Value = new object();
var frames = new[]
{
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(3),
RenderTreeFrame.Attribute(1, "attribute 1", attribute1Value),
RenderTreeFrame.Attribute(2, "attribute 2", attribute2Value),
RenderTreeFrame.Element(3, "child element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(4, "child attribute", "some value")
};
var parameterCollection = new ParameterCollection(frames, 0);
// Assert
Assert.Collection(ToEnumerable(parameterCollection),
AssertParameter("attribute 1", attribute1Value),
AssertParameter("attribute 2", attribute2Value));
}
private Action<Parameter> AssertParameter(string expectedName, object expectedValue)
{
return parameter =>
{
Assert.Equal(expectedName, parameter.Name);
Assert.Same(expectedValue, parameter.Value);
};
}
public IEnumerable<Parameter> ToEnumerable(ParameterCollection parameterCollection)
{
foreach (var item in parameterCollection)
{
yield return item;
}
}
private class FakeComponent : IComponent
{
public void Init(RenderHandle renderHandle)
=> throw new NotImplementedException();
public void BuildRenderTree(RenderTreeBuilder builder)
=> throw new NotImplementedException();
}
}
}