This commit is contained in:
Ryan Nowak 2018-06-07 21:59:45 -07:00 коммит произвёл Ryan Nowak
Родитель 1b470f3d3b
Коммит 08f12f2bfd
6 изменённых файлов: 226 добавлений и 1 удалений

Просмотреть файл

@ -41,7 +41,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
if (context.Handler != null)
{
httpContext.Features.Set<IEndpointFeature>(feature);
await context.Handler(httpContext);
}
}

Просмотреть файл

@ -3,12 +3,18 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public sealed class MatcherEndpoint : Endpoint
{
internal static readonly Func<RequestDelegate, RequestDelegate> EmptyInvoker = (next) =>
{
return (context) => Task.CompletedTask;
};
public MatcherEndpoint(
Func<RequestDelegate, RequestDelegate> invoker,
string template,

Просмотреть файл

@ -0,0 +1,54 @@
// 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.Linq;
using Xunit.Sdk;
namespace Microsoft.AspNetCore.Routing.Matchers
{
internal static class DispatcherAssert
{
public static void AssertMatch(IEndpointFeature feature, Endpoint expected)
{
AssertMatch(feature, expected, new RouteValueDictionary());
}
public static void AssertMatch(IEndpointFeature feature, Endpoint expected, RouteValueDictionary values)
{
if (feature.Endpoint == null)
{
throw new XunitException($"Was expected to match '{expected.DisplayName}' but did not match.");
}
if (!object.ReferenceEquals(expected, feature.Endpoint))
{
throw new XunitException(
$"Was expected to match '{expected.DisplayName}' but matched " +
$"'{feature.Endpoint.DisplayName}' with values: {FormatRouteValues(feature.Values)}.");
}
if (values.Count != feature.Values.Count ||
!values.OrderBy(kvp => kvp.Key).SequenceEqual(feature.Values.OrderBy(kvp => kvp.Key)))
{
throw new XunitException(
$"Was expected to match '{expected.DisplayName}' with values {FormatRouteValues(values)} but matched " +
$"values: {FormatRouteValues(feature.Values)}.");
}
}
public static void AssertNotMatch(IEndpointFeature feature)
{
if (feature.Endpoint != null)
{
throw new XunitException(
$"Was expected not to match '{feature.Endpoint.DisplayName}' " +
$"but matched with values: {FormatRouteValues(feature.Values)}.");
}
}
private static string FormatRouteValues(RouteValueDictionary values)
{
return "{" + string.Join(", ", values.Select(kvp => $"{kvp.Key} = '{kvp.Value}'")) + "}";
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public abstract class MatcherConformanceTest
{
internal abstract Matcher CreateMatcher(MatcherEndpoint endpoint);
[Fact]
public virtual async Task Match_SingleLiteralSegment_Success()
{
// Arrange
var (matcher, endpoint) = CreateMatcher("/simple");
var (httpContext, feature) = CreateContext("/simple");
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, endpoint);
}
[Theory]
[InlineData("simple")]
[InlineData("/simple")]
[InlineData("~/simple")]
public virtual async Task Match_Sanitizies_TemplatePrefix(string template)
{
// Arrange
var (matcher, endpoint) = CreateMatcher(template);
var (httpContext, feature) = CreateContext("/simple");
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, endpoint);
}
internal static (HttpContext httpContext, IEndpointFeature feature) CreateContext(string path)
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Method = "TEST";
httpContext.Request.Path = path;
httpContext.RequestServices = CreateServices();
var feature = new EndpointFeature();
httpContext.Features.Set<IEndpointFeature>(feature);
return (httpContext, feature);
}
// The older routing implementations retrieve services when they first execute.
internal static IServiceProvider CreateServices()
{
var services = new ServiceCollection();
services.AddLogging();
return services.BuildServiceProvider();
}
internal static MatcherEndpoint CreateEndpoint(string template)
{
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
template,
null,
0,
EndpointMetadataCollection.Empty, "endpoint: " + template);
}
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template)
{
var endpoint = CreateEndpoint(template);
return (CreateMatcher(endpoint), endpoint);
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Tree;
namespace Microsoft.AspNetCore.Routing.Matchers
{
// This is an adapter to use TreeRouter in the conformance tests
internal class TreeRouterMatcher : Matcher
{
private readonly TreeRouter _inner;
internal TreeRouterMatcher(TreeRouter inner)
{
_inner = inner;
}
public async override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
}
var context = new RouteContext(httpContext);
await _inner.RouteAsync(context);
if (context.Handler != null)
{
httpContext.Features.Get<IEndpointFeature>().Values = context.RouteData.Values;
await context.Handler(httpContext);
}
}
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public class TreeRouterMatcherConformanceTest : MatcherConformanceTest
{
internal override Matcher CreateMatcher(MatcherEndpoint endpoint)
{
var builder = new TreeRouteBuilder(
NullLoggerFactory.Instance,
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
var handler = new RouteHandler(c =>
{
var feature = c.Features.Get<IEndpointFeature>();
feature.Endpoint = endpoint;
feature.Invoker = MatcherEndpoint.EmptyInvoker;
return Task.CompletedTask;
});
builder.MapInbound(handler, TemplateParser.Parse(endpoint.Template), "default", 0);
return new TreeRouterMatcher(builder.Build());
}
}
}