Create collections on DfaNode as needed (#779)

This commit is contained in:
James Newton-King 2018-09-06 12:58:57 +12:00 committed by GitHub
parent a777a4cdd5
commit 0f5d471dfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 224 additions and 104 deletions

View File

@ -64,9 +64,12 @@ namespace Microsoft.AspNetCore.Routing.Internal
// We can safely index into visited because this is a post-order traversal, // We can safely index into visited because this is a post-order traversal,
// all of the children of this node are already in the dictionary. // all of the children of this node are already in the dictionary.
foreach (var literal in node.Literals) if (node.Literals != null)
{ {
writer.WriteLine($"{label} -> {visited[literal.Value]} [label=\"/{literal.Key}\"]"); foreach (var literal in node.Literals)
{
writer.WriteLine($"{label} -> {visited[literal.Value]} [label=\"/{literal.Key}\"]");
}
} }
if (node.Parameters != null) if (node.Parameters != null)
@ -79,9 +82,12 @@ namespace Microsoft.AspNetCore.Routing.Internal
writer.WriteLine($"{label} -> {visited[node.CatchAll]} [label=\"/**\"]"); writer.WriteLine($"{label} -> {visited[node.CatchAll]} [label=\"/**\"]");
} }
foreach (var policy in node.PolicyEdges) if (node.PolicyEdges != null)
{ {
writer.WriteLine($"{label} -> {visited[policy.Value]} [label=\"{policy.Key}\"]"); foreach (var policy in node.PolicyEdges)
{
writer.WriteLine($"{label} -> {visited[policy.Value]} [label=\"{policy.Key}\"]");
}
} }
writer.WriteLine($"{label} [label=\"{node.Label}\"]"); writer.WriteLine($"{label} [label=\"{node.Label}\"]");

View File

@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
for (var j = 0; j < parents.Count; j++) for (var j = 0; j < parents.Count; j++)
{ {
var parent = parents[j]; var parent = parents[j];
parent.Matches.Add(endpoint); parent.AddMatch(endpoint);
} }
} }
@ -139,15 +139,17 @@ namespace Microsoft.AspNetCore.Routing.Matching
var part = segment.Parts[0]; var part = segment.Parts[0];
if (segment.IsSimple && part is RoutePatternLiteralPart literalPart) if (segment.IsSimple && part is RoutePatternLiteralPart literalPart)
{ {
DfaNode next = null;
var literal = literalPart.Content; var literal = literalPart.Content;
if (!parent.Literals.TryGetValue(literal, out var next)) if (parent.Literals == null ||
!parent.Literals.TryGetValue(literal, out next))
{ {
next = new DfaNode() next = new DfaNode()
{ {
PathDepth = parent.PathDepth + 1, PathDepth = parent.PathDepth + 1,
Label = parent.Label + literal + "/", Label = parent.Label + literal + "/",
}; };
parent.Literals.Add(literal, next); parent.AddLiteral(literal, next);
} }
nextParents.Add(next); nextParents.Add(next);
@ -157,7 +159,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
// A catch all should traverse all literal nodes as well as parameter nodes // A catch all should traverse all literal nodes as well as parameter nodes
// we don't need to create the parameter node here because of ordering // we don't need to create the parameter node here because of ordering
// all catchalls will be processed after all parameters. // all catchalls will be processed after all parameters.
nextParents.AddRange(parent.Literals.Values); if (parent.Literals != null)
{
nextParents.AddRange(parent.Literals.Values);
}
if (parent.Parameters != null) if (parent.Parameters != null)
{ {
nextParents.Add(parent.Parameters); nextParents.Add(parent.Parameters);
@ -181,7 +186,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
parent.CatchAll.CatchAll = parent.CatchAll; parent.CatchAll.CatchAll = parent.CatchAll;
} }
parent.CatchAll.Matches.Add(endpoint); parent.CatchAll.AddMatch(endpoint);
} }
else if (segment.IsSimple && part.IsParameter) else if (segment.IsSimple && part.IsParameter)
{ {
@ -195,7 +200,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
// A parameter should traverse all literal nodes as well as the parameter node // A parameter should traverse all literal nodes as well as the parameter node
nextParents.AddRange(parent.Literals.Values); if (parent.Literals != null)
{
nextParents.AddRange(parent.Literals.Values);
}
nextParents.Add(parent.Parameters); nextParents.Add(parent.Parameters);
} }
else else
@ -213,7 +221,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
}; };
} }
nextParents.AddRange(parent.Literals.Values); if (parent.Literals != null)
{
nextParents.AddRange(parent.Literals.Values);
}
nextParents.Add(parent.Parameters); nextParents.Add(parent.Parameters);
} }
} }
@ -311,7 +322,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
List<DfaState> states, List<DfaState> states,
List<(JumpTableBuilder pathBuilder, PolicyJumpTableBuilder policyBuilder)> tableBuilders) List<(JumpTableBuilder pathBuilder, PolicyJumpTableBuilder policyBuilder)> tableBuilders)
{ {
node.Matches.Sort(_comparer); node.Matches?.Sort(_comparer);
var stateIndex = states.Count; var stateIndex = states.Count;
@ -321,15 +332,18 @@ namespace Microsoft.AspNetCore.Routing.Matching
var pathBuilder = new JumpTableBuilder(); var pathBuilder = new JumpTableBuilder();
tableBuilders.Add((pathBuilder, null)); tableBuilders.Add((pathBuilder, null));
foreach (var kvp in node.Literals) if (node.Literals != null)
{ {
if (kvp.Key == null) foreach (var kvp in node.Literals)
{ {
continue; if (kvp.Key == null)
} {
continue;
}
var transition = Transition(kvp.Value); var transition = Transition(kvp.Value);
pathBuilder.AddEntry(kvp.Key, transition); pathBuilder.AddEntry(kvp.Key, transition);
}
} }
if (node.Parameters != null && if (node.Parameters != null &&
@ -360,7 +374,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
pathBuilder.ExitDestination = pathBuilder.DefaultDestination; pathBuilder.ExitDestination = pathBuilder.DefaultDestination;
} }
if (node.PolicyEdges.Count > 0) if (node.PolicyEdges != null && node.PolicyEdges.Count > 0)
{ {
var policyBuilder = new PolicyJumpTableBuilder(node.NodeBuilder); var policyBuilder = new PolicyJumpTableBuilder(node.NodeBuilder);
tableBuilders[stateIndex] = (pathBuilder, policyBuilder); tableBuilders[stateIndex] = (pathBuilder, policyBuilder);
@ -384,7 +398,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// endpoint. // endpoint.
internal Candidate[] CreateCandidates(IReadOnlyList<Endpoint> endpoints) internal Candidate[] CreateCandidates(IReadOnlyList<Endpoint> endpoints)
{ {
if (endpoints.Count == 0) if (endpoints == null || endpoints.Count == 0)
{ {
return Array.Empty<Candidate>(); return Array.Empty<Candidate>();
} }
@ -502,7 +516,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
private int[] GetGroupLengths(DfaNode node) private int[] GetGroupLengths(DfaNode node)
{ {
if (node.Matches.Count == 0) var nodeMatches = node.Matches;
if (nodeMatches == null || nodeMatches.Count == 0)
{ {
return Array.Empty<int>(); return Array.Empty<int>();
} }
@ -510,16 +525,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
var groups = new List<int>(); var groups = new List<int>();
var length = 1; var length = 1;
var exemplar = node.Matches[0]; var exemplar = nodeMatches[0];
for (var i = 1; i < node.Matches.Count; i++) for (var i = 1; i < nodeMatches.Count; i++)
{ {
if (!_comparer.Equals(exemplar, node.Matches[i])) if (!_comparer.Equals(exemplar, nodeMatches[i]))
{ {
groups.Add(length); groups.Add(length);
length = 0; length = 0;
exemplar = node.Matches[i]; exemplar = nodeMatches[i];
} }
length++; length++;
@ -561,7 +576,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
private void ApplyPolicies(DfaNode node) private void ApplyPolicies(DfaNode node)
{ {
if (node.Matches.Count == 0) if (node.Matches == null || node.Matches.Count == 0)
{ {
return; return;
} }
@ -578,7 +593,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
for (var j = 0; j < work.Count; j++) for (var j = 0; j < work.Count; j++)
{ {
var parent = work[j]; var parent = work[j];
if (!nodeBuilder.AppliesToNode(parent.Matches)) if (!nodeBuilder.AppliesToNode(parent.Matches ?? (IReadOnlyList<Endpoint>)Array.Empty<Endpoint>()))
{ {
// This node-builder doesn't care about this node, so add it to the list // This node-builder doesn't care about this node, so add it to the list
// to be processed by the next node-builder. // to be processed by the next node-builder.
@ -588,7 +603,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// This node-builder does apply to this node, so we need to create new nodes for each edge, // This node-builder does apply to this node, so we need to create new nodes for each edge,
// and then attach them to the parent. // and then attach them to the parent.
var edges = nodeBuilder.GetEdges(parent.Matches); var edges = nodeBuilder.GetEdges(parent.Matches ?? (IReadOnlyList<Endpoint>)Array.Empty<Endpoint>());
for (var k = 0; k < edges.Count; k++) for (var k = 0; k < edges.Count; k++)
{ {
var edge = edges[k]; var edge = edges[k];
@ -598,17 +613,20 @@ namespace Microsoft.AspNetCore.Routing.Matching
Label = parent.Label + " " + edge.State.ToString(), Label = parent.Label + " " + edge.State.ToString(),
}; };
next.Matches.AddRange(edge.Endpoints); if (edge.Endpoints.Count > 0)
{
next.AddMatches(edge.Endpoints);
}
nextWork.Add(next); nextWork.Add(next);
parent.PolicyEdges.Add(edge.State, next); parent.AddPolicyEdge(edge.State, next);
} }
// Associate the node-builder so we can build a jump table later. // Associate the node-builder so we can build a jump table later.
parent.NodeBuilder = nodeBuilder; parent.NodeBuilder = nodeBuilder;
// The parent no longer has matches, it's not considered a terminal node. // The parent no longer has matches, it's not considered a terminal node.
parent.Matches.Clear(); parent.Matches?.Clear();
} }
work = nextWork; work = nextWork;

View File

@ -14,13 +14,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
[DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerDisplay("{DebuggerToString(),nq}")]
internal class DfaNode internal class DfaNode
{ {
public DfaNode()
{
Literals = new Dictionary<string, DfaNode>(StringComparer.OrdinalIgnoreCase);
Matches = new List<Endpoint>();
PolicyEdges = new Dictionary<object, DfaNode>();
}
// The depth of the node. The depth indicates the number of segments // The depth of the node. The depth indicates the number of segments
// that must be processed to arrive at this node. // that must be processed to arrive at this node.
// //
@ -30,9 +23,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Just for diagnostics and debugging // Just for diagnostics and debugging
public string Label { get; set; } public string Label { get; set; }
public List<Endpoint> Matches { get; } public List<Endpoint> Matches { get; private set; }
public Dictionary<string, DfaNode> Literals { get; } public Dictionary<string, DfaNode> Literals { get; private set; }
public DfaNode Parameters { get; set; } public DfaNode Parameters { get; set; }
@ -40,13 +33,58 @@ namespace Microsoft.AspNetCore.Routing.Matching
public INodeBuilderPolicy NodeBuilder { get; set; } public INodeBuilderPolicy NodeBuilder { get; set; }
public Dictionary<object, DfaNode> PolicyEdges { get; } public Dictionary<object, DfaNode> PolicyEdges { get; private set; }
public void AddPolicyEdge(object state, DfaNode node)
{
if (PolicyEdges == null)
{
PolicyEdges = new Dictionary<object, DfaNode>();
}
PolicyEdges.Add(state, node);
}
public void AddLiteral(string literal, DfaNode node)
{
if (Literals == null)
{
Literals = new Dictionary<string, DfaNode>(StringComparer.OrdinalIgnoreCase);
}
Literals.Add(literal, node);
}
public void AddMatch(Endpoint endpoint)
{
if (Matches == null)
{
Matches = new List<Endpoint>();
}
Matches.Add(endpoint);
}
public void AddMatches(IEnumerable<Endpoint> endpoints)
{
if (Matches == null)
{
Matches = new List<Endpoint>(endpoints);
}
else
{
Matches.AddRange(endpoints);
}
}
public void Visit(Action<DfaNode> visitor) public void Visit(Action<DfaNode> visitor)
{ {
foreach (var kvp in Literals) if (Literals != null)
{ {
kvp.Value.Visit(visitor); foreach (var kvp in Literals)
{
kvp.Value.Visit(visitor);
}
} }
// Break cycles // Break cycles
@ -61,9 +99,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
CatchAll.Visit(visitor); CatchAll.Visit(visitor);
} }
foreach (var kvp in PolicyEdges) if (PolicyEdges != null)
{ {
kvp.Value.Visit(visitor); foreach (var kvp in PolicyEdges)
{
kvp.Value.Visit(visitor);
}
} }
visitor(this); visitor(this);
@ -76,9 +117,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
builder.Append(" d:"); builder.Append(" d:");
builder.Append(PathDepth); builder.Append(PathDepth);
builder.Append(" m:"); builder.Append(" m:");
builder.Append(Matches.Count); builder.Append(Matches?.Count ?? 0);
builder.Append(" c: "); builder.Append(" c: ");
builder.Append(string.Join(", ", Literals.Select(kvp => $"{kvp.Key}->({FormatNode(kvp.Value)})"))); if (Literals != null)
{
builder.Append(string.Join(", ", Literals.Select(kvp => $"{kvp.Key}->({FormatNode(kvp.Value)})")));
}
return builder.ToString(); return builder.ToString();
// DfaNodes can be self-referential, don't traverse cycles. // DfaNodes can be self-referential, don't traverse cycles.

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Assert // Assert
Assert.Same(endpoint, Assert.Single(root.Matches)); Assert.Same(endpoint, Assert.Single(root.Matches));
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
Assert.Empty(root.Literals); Assert.Null(root.Literals);
} }
[Fact] [Fact]
@ -46,21 +46,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
Assert.Equal("a", next.Key); Assert.Equal("a", next.Key);
var a = next.Value; var a = next.Value;
Assert.Empty(a.Matches); Assert.Null(a.Matches);
Assert.Null(a.Parameters); Assert.Null(a.Parameters);
next = Assert.Single(a.Literals); next = Assert.Single(a.Literals);
Assert.Equal("b", next.Key); Assert.Equal("b", next.Key);
var b = next.Value; var b = next.Value;
Assert.Empty(b.Matches); Assert.Null(b.Matches);
Assert.Null(b.Parameters); Assert.Null(b.Parameters);
next = Assert.Single(b.Literals); next = Assert.Single(b.Literals);
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var c = next.Value; var c = next.Value;
Assert.Same(endpoint, Assert.Single(c.Matches)); Assert.Same(endpoint, Assert.Single(c.Matches));
Assert.Null(c.Parameters); Assert.Null(c.Parameters);
Assert.Empty(c.Literals); Assert.Null(c.Literals);
} }
[Fact] [Fact]
@ -85,21 +85,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Empty(root.Literals); Assert.Null(root.Literals);
var a = root.Parameters; var a = root.Parameters;
Assert.Empty(a.Matches); Assert.Null(a.Matches);
Assert.Empty(a.Literals); Assert.Null(a.Literals);
var b = a.Parameters; var b = a.Parameters;
Assert.Empty(b.Matches); Assert.Null(b.Matches);
Assert.Empty(b.Literals); Assert.Null(b.Literals);
var c = b.Parameters; var c = b.Parameters;
Assert.Same(endpoint, Assert.Single(c.Matches)); Assert.Same(endpoint, Assert.Single(c.Matches));
Assert.Null(c.Parameters); Assert.Null(c.Parameters);
Assert.Empty(c.Literals); Assert.Null(c.Literals);
} }
[Fact] [Fact]
@ -115,21 +115,21 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Empty(root.Literals); Assert.Null(root.Literals);
var a = root.Parameters; var a = root.Parameters;
// The catch all can match a path like '/a' // The catch all can match a path like '/a'
Assert.Same(endpoint, Assert.Single(a.Matches)); Assert.Same(endpoint, Assert.Single(a.Matches));
Assert.Empty(a.Literals); Assert.Null(a.Literals);
Assert.Null(a.Parameters); Assert.Null(a.Parameters);
// Catch-all nodes include an extra transition that loops to process // Catch-all nodes include an extra transition that loops to process
// extra segments. // extra segments.
var catchAll = a.CatchAll; var catchAll = a.CatchAll;
Assert.Same(endpoint, Assert.Single(catchAll.Matches)); Assert.Same(endpoint, Assert.Single(catchAll.Matches));
Assert.Empty(catchAll.Literals); Assert.Null(catchAll.Literals);
Assert.Same(catchAll, catchAll.Parameters); Assert.Same(catchAll, catchAll.Parameters);
Assert.Same(catchAll, catchAll.CatchAll); Assert.Same(catchAll, catchAll.CatchAll);
} }
@ -148,13 +148,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Assert // Assert
Assert.Same(endpoint, Assert.Single(root.Matches)); Assert.Same(endpoint, Assert.Single(root.Matches));
Assert.Empty(root.Literals); Assert.Null(root.Literals);
// Catch-all nodes include an extra transition that loops to process // Catch-all nodes include an extra transition that loops to process
// extra segments. // extra segments.
var catchAll = root.CatchAll; var catchAll = root.CatchAll;
Assert.Same(endpoint, Assert.Single(catchAll.Matches)); Assert.Same(endpoint, Assert.Single(catchAll.Matches));
Assert.Empty(catchAll.Literals); Assert.Null(catchAll.Literals);
Assert.Same(catchAll, catchAll.Parameters); Assert.Same(catchAll, catchAll.Parameters);
} }
@ -174,19 +174,19 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
Assert.Equal("a", next.Key); Assert.Equal("a", next.Key);
var a = next.Value; var a = next.Value;
Assert.Empty(a.Matches); Assert.Null(a.Matches);
Assert.Equal(2, a.Literals.Count); Assert.Equal(2, a.Literals.Count);
var b1 = a.Literals["b1"]; var b1 = a.Literals["b1"];
Assert.Empty(b1.Matches); Assert.Null(b1.Matches);
Assert.Null(b1.Parameters); Assert.Null(b1.Parameters);
next = Assert.Single(b1.Literals); next = Assert.Single(b1.Literals);
@ -195,10 +195,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
var c1 = next.Value; var c1 = next.Value;
Assert.Same(endpoint1, Assert.Single(c1.Matches)); Assert.Same(endpoint1, Assert.Single(c1.Matches));
Assert.Null(c1.Parameters); Assert.Null(c1.Parameters);
Assert.Empty(c1.Literals); Assert.Null(c1.Literals);
var b2 = a.Literals["b2"]; var b2 = a.Literals["b2"];
Assert.Empty(b2.Matches); Assert.Null(b2.Matches);
Assert.Null(b2.Parameters); Assert.Null(b2.Parameters);
next = Assert.Single(b2.Literals); next = Assert.Single(b2.Literals);
@ -207,7 +207,59 @@ namespace Microsoft.AspNetCore.Routing.Matching
var c2 = next.Value; var c2 = next.Value;
Assert.Same(endpoint2, Assert.Single(c2.Matches)); Assert.Same(endpoint2, Assert.Single(c2.Matches));
Assert.Null(c2.Parameters); Assert.Null(c2.Parameters);
Assert.Empty(c2.Literals); Assert.Null(c2.Literals);
}
[Fact]
public void BuildDfaTree_MultipleEndpoint_LiteralDifferentCase()
{
// Arrange
var builder = CreateDfaMatcherBuilder();
var endpoint1 = CreateEndpoint("a/b1/c");
builder.AddEndpoint(endpoint1);
var endpoint2 = CreateEndpoint("A/b2/c");
builder.AddEndpoint(endpoint2);
// Act
var root = builder.BuildDfaTree();
// Assert
Assert.Null(root.Matches);
Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals);
Assert.Equal("a", next.Key);
var a = next.Value;
Assert.Null(a.Matches);
Assert.Equal(2, a.Literals.Count);
var b1 = a.Literals["b1"];
Assert.Null(b1.Matches);
Assert.Null(b1.Parameters);
next = Assert.Single(b1.Literals);
Assert.Equal("c", next.Key);
var c1 = next.Value;
Assert.Same(endpoint1, Assert.Single(c1.Matches));
Assert.Null(c1.Parameters);
Assert.Null(c1.Literals);
var b2 = a.Literals["b2"];
Assert.Null(b2.Matches);
Assert.Null(b2.Parameters);
next = Assert.Single(b2.Literals);
Assert.Equal("c", next.Key);
var c2 = next.Value;
Assert.Same(endpoint2, Assert.Single(c2.Matches));
Assert.Null(c2.Parameters);
Assert.Null(c2.Literals);
} }
[Fact] [Fact]
@ -226,20 +278,20 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
Assert.Equal("a", next.Key); Assert.Equal("a", next.Key);
var a = next.Value; var a = next.Value;
Assert.Empty(a.Matches); Assert.Null(a.Matches);
next = Assert.Single(a.Literals); next = Assert.Single(a.Literals);
Assert.Equal("b", next.Key); Assert.Equal("b", next.Key);
var b = next.Value; var b = next.Value;
Assert.Empty(b.Matches); Assert.Null(b.Matches);
Assert.Null(b.Parameters); Assert.Null(b.Parameters);
next = Assert.Single(b.Literals); next = Assert.Single(b.Literals);
@ -251,10 +303,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
e => Assert.Same(endpoint1, e), e => Assert.Same(endpoint1, e),
e => Assert.Same(endpoint2, e)); e => Assert.Same(endpoint2, e));
Assert.Null(c1.Parameters); Assert.Null(c1.Parameters);
Assert.Empty(c1.Literals); Assert.Null(c1.Literals);
var b2 = a.Parameters; var b2 = a.Parameters;
Assert.Empty(b2.Matches); Assert.Null(b2.Matches);
Assert.Null(b2.Parameters); Assert.Null(b2.Parameters);
next = Assert.Single(b2.Literals); next = Assert.Single(b2.Literals);
@ -263,7 +315,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var c2 = next.Value; var c2 = next.Value;
Assert.Same(endpoint2, Assert.Single(c2.Matches)); Assert.Same(endpoint2, Assert.Single(c2.Matches));
Assert.Null(c2.Parameters); Assert.Null(c2.Parameters);
Assert.Empty(c2.Literals); Assert.Null(c2.Literals);
} }
[Fact] [Fact]
@ -282,18 +334,18 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
Assert.Equal("a", next.Key); Assert.Equal("a", next.Key);
var a = next.Value; var a = next.Value;
Assert.Empty(a.Matches); Assert.Null(a.Matches);
Assert.Empty(a.Literals); Assert.Null(a.Literals);
var b = a.Parameters; var b = a.Parameters;
Assert.Empty(b.Matches); Assert.Null(b.Matches);
Assert.Null(b.Parameters); Assert.Null(b.Parameters);
next = Assert.Single(b.Literals); next = Assert.Single(b.Literals);
@ -305,7 +357,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
e => Assert.Same(endpoint1, e), e => Assert.Same(endpoint1, e),
e => Assert.Same(endpoint2, e)); e => Assert.Same(endpoint2, e));
Assert.Null(c.Parameters); Assert.Null(c.Parameters);
Assert.Empty(c.Literals); Assert.Null(c.Literals);
} }
[Fact] [Fact]
@ -324,7 +376,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -349,7 +401,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
e => Assert.Same(endpoint1, e), e => Assert.Same(endpoint1, e),
e => Assert.Same(endpoint2, e)); e => Assert.Same(endpoint2, e));
Assert.Null(c1.Parameters); Assert.Null(c1.Parameters);
Assert.Empty(c1.Literals); Assert.Null(c1.Literals);
var catchAll = a.CatchAll; var catchAll = a.CatchAll;
Assert.Same(endpoint2, Assert.Single(catchAll.Matches)); Assert.Same(endpoint2, Assert.Single(catchAll.Matches));
@ -373,7 +425,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -381,7 +433,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var a = next.Value; var a = next.Value;
Assert.Same(endpoint2, Assert.Single(a.Matches)); Assert.Same(endpoint2, Assert.Single(a.Matches));
Assert.Empty(a.Literals); Assert.Null(a.Literals);
var b1 = a.Parameters; var b1 = a.Parameters;
Assert.Same(endpoint2, Assert.Single(a.Matches)); Assert.Same(endpoint2, Assert.Single(a.Matches));
@ -396,7 +448,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
e => Assert.Same(endpoint1, e), e => Assert.Same(endpoint1, e),
e => Assert.Same(endpoint2, e)); e => Assert.Same(endpoint2, e));
Assert.Null(c1.Parameters); Assert.Null(c1.Parameters);
Assert.Empty(c1.Literals); Assert.Null(c1.Literals);
var catchAll = a.CatchAll; var catchAll = a.CatchAll;
Assert.Same(endpoint2, Assert.Single(catchAll.Matches)); Assert.Same(endpoint2, Assert.Single(catchAll.Matches));
@ -417,7 +469,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -440,7 +492,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var test2_true = test1_0.PolicyEdges[true]; var test2_true = test1_0.PolicyEdges[true];
Assert.Same(endpoint1, Assert.Single(test2_true.Matches)); Assert.Same(endpoint1, Assert.Single(test2_true.Matches));
Assert.Null(test2_true.NodeBuilder); Assert.Null(test2_true.NodeBuilder);
Assert.Empty(test2_true.PolicyEdges); Assert.Null(test2_true.PolicyEdges);
} }
[Fact] [Fact]
@ -462,7 +514,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -486,7 +538,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var test2_true = test1_0.PolicyEdges[true]; var test2_true = test1_0.PolicyEdges[true];
Assert.Same(endpoint1, Assert.Single(test2_true.Matches)); Assert.Same(endpoint1, Assert.Single(test2_true.Matches));
Assert.Null(test2_true.NodeBuilder); Assert.Null(test2_true.NodeBuilder);
Assert.Empty(test2_true.PolicyEdges); Assert.Null(test2_true.PolicyEdges);
var test1_1 = a.PolicyEdges[1]; var test1_1 = a.PolicyEdges[1];
Assert.Empty(test1_1.Matches); Assert.Empty(test1_1.Matches);
@ -499,12 +551,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
test2_true = test1_1.PolicyEdges[true]; test2_true = test1_1.PolicyEdges[true];
Assert.Same(endpoint2, Assert.Single(test2_true.Matches)); Assert.Same(endpoint2, Assert.Single(test2_true.Matches));
Assert.Null(test2_true.NodeBuilder); Assert.Null(test2_true.NodeBuilder);
Assert.Empty(test2_true.PolicyEdges); Assert.Null(test2_true.PolicyEdges);
var test2_false = test1_1.PolicyEdges[false]; var test2_false = test1_1.PolicyEdges[false];
Assert.Same(endpoint3, Assert.Single(test2_false.Matches)); Assert.Same(endpoint3, Assert.Single(test2_false.Matches));
Assert.Null(test2_false.NodeBuilder); Assert.Null(test2_false.NodeBuilder);
Assert.Empty(test2_false.PolicyEdges); Assert.Null(test2_false.PolicyEdges);
} }
[Fact] [Fact]
@ -526,7 +578,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -543,12 +595,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
var test2_true = a.PolicyEdges[true]; var test2_true = a.PolicyEdges[true];
Assert.Equal(new[] { endpoint1, endpoint2, }, test2_true.Matches); Assert.Equal(new[] { endpoint1, endpoint2, }, test2_true.Matches);
Assert.Null(test2_true.NodeBuilder); Assert.Null(test2_true.NodeBuilder);
Assert.Empty(test2_true.PolicyEdges); Assert.Null(test2_true.PolicyEdges);
var test2_false = a.PolicyEdges[false]; var test2_false = a.PolicyEdges[false];
Assert.Equal(new[] { endpoint3, }, test2_false.Matches); Assert.Equal(new[] { endpoint3, }, test2_false.Matches);
Assert.Null(test2_false.NodeBuilder); Assert.Null(test2_false.NodeBuilder);
Assert.Empty(test2_false.PolicyEdges); Assert.Null(test2_false.PolicyEdges);
} }
[Fact] [Fact]
@ -570,7 +622,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -587,12 +639,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
var test1_0 = a.PolicyEdges[0]; var test1_0 = a.PolicyEdges[0];
Assert.Equal(new[] { endpoint1, }, test1_0.Matches); Assert.Equal(new[] { endpoint1, }, test1_0.Matches);
Assert.Null(test1_0.NodeBuilder); Assert.Null(test1_0.NodeBuilder);
Assert.Empty(test1_0.PolicyEdges); Assert.Null(test1_0.PolicyEdges);
var test1_1 = a.PolicyEdges[1]; var test1_1 = a.PolicyEdges[1];
Assert.Equal(new[] { endpoint2, endpoint3, }, test1_1.Matches); Assert.Equal(new[] { endpoint2, endpoint3, }, test1_1.Matches);
Assert.Null(test1_1.NodeBuilder); Assert.Null(test1_1.NodeBuilder);
Assert.Empty(test1_1.PolicyEdges); Assert.Null(test1_1.PolicyEdges);
} }
[Fact] [Fact]
@ -614,7 +666,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -632,12 +684,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
var test1_0 = a.PolicyEdges[0]; var test1_0 = a.PolicyEdges[0];
Assert.Equal(new[] { endpoint1, }, test1_0.Matches); Assert.Equal(new[] { endpoint1, }, test1_0.Matches);
Assert.Null(test1_0.NodeBuilder); Assert.Null(test1_0.NodeBuilder);
Assert.Empty(test1_0.PolicyEdges); Assert.Null(test1_0.PolicyEdges);
var test1_1 = a.PolicyEdges[1]; var test1_1 = a.PolicyEdges[1];
Assert.Equal(new[] { endpoint2, endpoint3, }, test1_1.Matches); Assert.Equal(new[] { endpoint2, endpoint3, }, test1_1.Matches);
Assert.Null(test1_1.NodeBuilder); Assert.Null(test1_1.NodeBuilder);
Assert.Empty(test1_1.PolicyEdges); Assert.Null(test1_1.PolicyEdges);
var nonRouteEndpoint = a.PolicyEdges[int.MaxValue]; var nonRouteEndpoint = a.PolicyEdges[int.MaxValue];
Assert.Equal("MaxValueEndpoint", Assert.Single(nonRouteEndpoint.Matches).DisplayName); Assert.Equal("MaxValueEndpoint", Assert.Single(nonRouteEndpoint.Matches).DisplayName);
@ -662,7 +714,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var root = builder.BuildDfaTree(); var root = builder.BuildDfaTree();
// Assert // Assert
Assert.Empty(root.Matches); Assert.Null(root.Matches);
Assert.Null(root.Parameters); Assert.Null(root.Parameters);
var next = Assert.Single(root.Literals); var next = Assert.Single(root.Literals);
@ -671,7 +723,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var a = next.Value; var a = next.Value;
Assert.Equal(new[] { endpoint1, endpoint2, endpoint3, }, a.Matches); Assert.Equal(new[] { endpoint1, endpoint2, endpoint3, }, a.Matches);
Assert.Null(a.NodeBuilder); Assert.Null(a.NodeBuilder);
Assert.Empty(a.PolicyEdges); Assert.Null(a.PolicyEdges);
} }
[Fact] [Fact]