Show a flattened tree in LinkGenerationDecisionTree's DebuggerDisplayString

[Fixes #636] Flatten the LinkGenerationDecisionTree to show as debugger display string
This commit is contained in:
Kiran Challa 2018-07-20 10:09:31 -07:00
parent 34499dbe24
commit 71cb933a08
2 changed files with 87 additions and 1 deletions

View File

@ -3,12 +3,16 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Routing.DecisionTree;
using Microsoft.AspNetCore.Routing.Tree;
namespace Microsoft.AspNetCore.Routing.Internal
{
// A decision tree that matches link generation entries based on route data.
[DebuggerDisplay("{DebuggerDisplayString,nq}")]
public class LinkGenerationDecisionTree
{
private readonly DecisionTreeNode<OutboundMatch> _root;
@ -160,5 +164,51 @@ namespace Microsoft.AspNetCore.Routing.Internal
y.Match.Entry.RouteTemplate.TemplateText);
}
}
// Example output:
//
// => action: Buy => controller: Store => version: V1(Matches: Store/Buy/V1)
// => action: Buy => controller: Store => version: V2(Matches: Store/Buy/V2)
// => action: Buy => controller: Store => area: Admin(Matches: Admin/Store/Buy)
// => action: Buy => controller: Products(Matches: Products/Buy)
// => action: Cart => controller: Store(Matches: Store/Cart)
internal string DebuggerDisplayString
{
get
{
var sb = new StringBuilder();
var branchStack = new Stack<string>();
branchStack.Push(string.Empty);
FlattenTree(_root, branchStack, sb);
return sb.ToString();
}
}
private void FlattenTree(DecisionTreeNode<OutboundMatch> node, Stack<string> branchStack, StringBuilder sb)
{
// leaf node
if (node.Criteria.Count == 0)
{
var temp = new StringBuilder();
foreach (var branch in branchStack)
{
temp.Insert(0, branch);
}
sb.Append(temp.ToString());
sb.Append(" (Matches: ");
sb.Append(string.Join(", ", node.Matches.Select(m => m.Entry.RouteTemplate.TemplateText)));
sb.AppendLine(")");
}
foreach (var criterion in node.Criteria)
{
foreach (var branch in criterion.Branches)
{
branchStack.Push($" => {criterion.Key}: {branch.Key}");
FlattenTree(branch.Value, branchStack, sb);
branchStack.Pop();
}
}
}
}
}

View File

@ -1,9 +1,11 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree;
using Xunit;
@ -318,11 +320,45 @@ namespace Microsoft.AspNetCore.Routing.Internal.Routing
Assert.Equal(entries, matches);
}
private OutboundMatch CreateMatch(object requiredValues)
[Fact]
public void ToDebuggerDisplayString_GivesAFlattenedTree()
{
// Arrange
var entries = new List<OutboundMatch>();
entries.Add(CreateMatch(new { action = "Buy", controller = "Store", version = "V1" }, "Store/Buy/V1"));
entries.Add(CreateMatch(new { action = "Buy", controller = "Store", area = "Admin" }, "Admin/Store/Buy"));
entries.Add(CreateMatch(new { action = "Buy", controller = "Products" }, "Products/Buy"));
entries.Add(CreateMatch(new { action = "Buy", controller = "Store", version = "V2" }, "Store/Buy/V2"));
entries.Add(CreateMatch(new { action = "Cart", controller = "Store" }, "Store/Cart"));
entries.Add(CreateMatch(new { action = "Index", controller = "Home" }, "Home/Index/{id?}"));
var tree = new LinkGenerationDecisionTree(entries);
var newLine = Environment.NewLine;
var expected =
" => action: Buy => controller: Store => version: V1 (Matches: Store/Buy/V1)" + newLine +
" => action: Buy => controller: Store => version: V2 (Matches: Store/Buy/V2)" + newLine +
" => action: Buy => controller: Store => area: Admin (Matches: Admin/Store/Buy)" + newLine +
" => action: Buy => controller: Products (Matches: Products/Buy)" + newLine +
" => action: Cart => controller: Store (Matches: Store/Cart)" + newLine +
" => action: Index => controller: Home (Matches: Home/Index/{id?})" + newLine;
// Act
var flattenedTree = tree.DebuggerDisplayString;
// Assert
Assert.Equal(expected, flattenedTree);
}
private OutboundMatch CreateMatch(object requiredValues, string routeTemplate = null)
{
var match = new OutboundMatch();
match.Entry = new OutboundRouteEntry();
match.Entry.RequiredLinkValues = new RouteValueDictionary(requiredValues);
if (!string.IsNullOrEmpty(routeTemplate))
{
match.Entry.RouteTemplate = new RouteTemplate(RoutePatternFactory.Parse(routeTemplate));
}
return match;
}