aspnetcore/test/Microsoft.AspNet.Mvc.ViewFe.../ViewComponents/ViewComponentMethodSelector...

283 lines
10 KiB
C#

// 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.Globalization;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNet.Mvc.ViewComponents
{
public class ViewComponentMethodSelectorTest
{
[Theory]
[InlineData(typeof(ViewComponentWithSyncInvoke), new object[] { "" })]
[InlineData(typeof(ViewComponentWithAsyncInvoke), new object[] { 42, false })]
[InlineData(typeof(ViewComponentWithNonPublicNonInstanceInvokes), new object[] { })]
[InlineData(typeof(ViewComponentWithNonPublicNonInstanceInvokes), new object[] { "" })]
public void FindAsyncMethod_ReturnsNull_IfMatchCannotBeFound(Type type, object[] args)
{
// Arrange
var typeInfo = type.GetTypeInfo();
// Act
var method = ViewComponentMethodSelector.FindAsyncMethod(typeInfo, args);
// Assert
Assert.Null(method);
}
[Theory]
[InlineData(typeof(ViewComponentWithAsyncInvoke), new object[0])]
[InlineData(typeof(ViewComponentWithSyncInvoke), new object[] { "" })]
[InlineData(typeof(ViewComponentWithAsyncInvoke), new object[] { "" })]
[InlineData(typeof(ViewComponentWithSyncInvoke), new object[] { 42 })]
[InlineData(typeof(ViewComponentWithAsyncInvoke), new object[] { "", 42 })]
[InlineData(typeof(ViewComponentWithNonPublicNonInstanceInvokes), new object[] { })]
[InlineData(typeof(ViewComponentWithNonPublicNonInstanceInvokes), new object[] { "" })]
[InlineData(typeof(BaseClass), new object[] { })]
public void FindSyncMethod_ReturnsNull_IfMatchCannotBeFound(Type type, object[] args)
{
// Arrange
var typeInfo = type.GetTypeInfo();
// Act
var method = ViewComponentMethodSelector.FindSyncMethod(typeInfo, args);
// Assert
Assert.Null(method);
}
[Theory]
[InlineData(new object[] { new object[] { "Hello" } })]
[InlineData(new object[] { new object[] { 4 } })]
[InlineData(new object[] { new object[] { "", 5 } })]
public void FindAsyncMethod_ThrowsIfInvokeAsyncDoesNotHaveCorrectReturnType(object[] args)
{
// Arrange
var typeInfo = typeof(TypeWithInvalidInvokeAsync).GetTypeInfo();
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(
() => ViewComponentMethodSelector.FindAsyncMethod(typeInfo, args));
Assert.Equal("The async view component method 'InvokeAsync' should be declared to return Task<T>.",
ex.Message);
}
[Fact]
public void FindSyncMethod_ThrowsIfInvokeSyncIsAVoidMethod()
{
// Arrange
var expectedMessage = "The view component method 'Invoke' should be declared to return a value.";
var typeInfo = typeof(TypeWithInvalidInvokeSync).GetTypeInfo();
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(
() => ViewComponentMethodSelector.FindSyncMethod(typeInfo, new object[] { 4 }));
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public void FindSyncMethod_ThrowsIfInvokeSyncReturnsTask()
{
// Arrange
var expectedMessage = "The view component method 'Invoke' cannot return a Task.";
var typeInfo = typeof(TypeWithInvalidInvokeSync).GetTypeInfo();
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(
() => ViewComponentMethodSelector.FindSyncMethod(typeInfo, new object[] { "" }));
Assert.Equal(expectedMessage, ex.Message);
}
public static TheoryData FindAsyncMethod_ReturnsMethodMatchingParametersData
{
get
{
var derivedClass = new DerivedClass();
return new TheoryData<Type, object[], string>
{
{ typeof(ViewComponentWithAsyncInvoke), new object[] { "", }, "1" },
{ typeof(ViewComponentWithAsyncInvoke), new object[] { "", 2 }, "2" },
{ typeof(ViewComponentWithAsyncInvoke), new object[] { "", 0, 1 }, "3" },
{ typeof(ViewComponentWithAsyncInvoke), new object[] { 1, false, 1 }, "4" },
{ typeof(MethodsWithValueConversions), new object[] { 2, (byte)1, (byte)2 }, "2" },
{ typeof(MethodsWithValueConversions), new object[] { derivedClass, derivedClass }, "4" },
{ typeof(MethodsWithValueConversions), new object[] { CultureInfo.InvariantCulture }, "6" },
};
}
}
[Theory]
[MemberData(nameof(FindAsyncMethod_ReturnsMethodMatchingParametersData))]
public void FindAsyncMethod_ReturnsMethodMatchingParameters(Type type, object[] args, string expectedId)
{
// Arrange
var typeInfo = type.GetTypeInfo();
// Act
var method = ViewComponentMethodSelector.FindAsyncMethod(typeInfo, args);
// Assert
Assert.NotNull(method);
var data = method.GetCustomAttribute<MethodDataAttribute>();
Assert.Equal(expectedId, data.Data);
}
public static TheoryData FindSyncMethod_ReturnsMethodMatchingParametersData
{
get
{
var derivedClass = new DerivedAgain();
return new TheoryData<Type, object[], string>
{
{ typeof(ViewComponentWithSyncInvoke), new object[] { }, "1" },
{ typeof(ViewComponentWithSyncInvoke), new object[] { 2, 3 }, "2" },
{ typeof(ViewComponentWithSyncInvoke), new object[] { "", 0, true }, "3" },
{ typeof(MethodsWithValueConversions), new object[] { 1, (byte)2, 3.0f }, "1" },
{ typeof(MethodsWithValueConversions), new object[] { derivedClass, derivedClass }, "3" },
{ typeof(MethodsWithValueConversions), new object[] { "Hello world" }, "5" },
{ typeof(DerivedClass), new object[] { }, "Derived1" },
#if !DNXCORE50
{ typeof(DerivedAgain), new object[] { "" }, "Derived2" },
#endif
};
}
}
[Theory]
[MemberData(nameof(FindSyncMethod_ReturnsMethodMatchingParametersData))]
public void FindSyncMethod_ReturnsMethodMatchingParameters(Type type, object[] args, string expectedId)
{
// Arrange
var typeInfo = type.GetTypeInfo();
// Act
var method = ViewComponentMethodSelector.FindSyncMethod(typeInfo, args);
// Assert
Assert.NotNull(method);
var data = method.GetCustomAttribute<MethodDataAttribute>();
Assert.Equal(expectedId, data.Data);
}
private class ViewComponentWithSyncInvoke
{
[MethodData("1")]
public int Invoke() => 3;
[MethodData("2")]
public int Invoke(int a, int? b) => a + b.Value;
[MethodData("3")]
public int Invoke(string a, int b, bool? c) => 3;
}
private class ViewComponentWithAsyncInvoke
{
[MethodData("1")]
public Task<string> InvokeAsync(string value) => Task.FromResult(value.ToUpperInvariant());
[MethodData("2")]
public Task<string> InvokeAsync(string a, int b) => Task.FromResult(a + b);
[MethodData("3")]
public Task<string> InvokeAsync(string a, int? b, int c) => Task.FromResult(a + b + c);
[MethodData("4")]
public Task<string> InvokeAsync(int? a, bool? b, int c) => Task.FromResult(a.ToString() + b + c);
[MethodData("5")]
public Task<string> InvokeAsync(object value) => Task.FromResult(value.ToString());
}
public class MethodsWithValueConversions
{
[MethodData("1")]
public int Invoke(long a, char b, double c) => 1;
[MethodData("2")]
public Task<int> InvokeAsync(float a, float b, byte c) => Task.FromResult(1);
[MethodData("3")]
public int Invoke(BaseClass a, DerivedClass b) => 1;
[MethodData("4")]
public Task<int> InvokeAsync(BaseClass a, DerivedClass b) => Task.FromResult(1);
[MethodData("5")]
public int Invoke(IEnumerable<char> value) => 1;
[MethodData("6")]
public Task<int> InvokeAsync(IFormatProvider formatProvider) => Task.FromResult(1);
}
private class ViewComponentWithNonPublicNonInstanceInvokes
{
public static int Invoke() => 1;
private int Invoke(string a) => 2;
public static Task<int> InvokeAsync() => Task.FromResult(3);
protected Task<string> InvokeAsync(string a) => Task.FromResult(a);
}
public class BaseClass
{
[MethodData("Base")]
public static int Invoke() => 1;
}
public class DerivedClass : BaseClass
{
[MethodData("Derived1")]
public new int Invoke() => 1;
[MethodData("Derived2")]
public int Invoke(string x) => 2;
}
public class DerivedAgain : DerivedClass
{
[MethodData("DerivedAgain")]
public new static int Invoke(string x) => 2;
}
private class TypeWithInvalidInvokeAsync
{
public Task InvokeAsync(string value) => Task.FromResult(value);
public void InvokeAsync(int value)
{
}
public long InvokeAsync(string a, int b) => b;
}
private class TypeWithInvalidInvokeSync
{
public Task Invoke(string value) => Task.FromResult(value);
public void Invoke(int value)
{
}
}
private class MethodDataAttribute : Attribute
{
public MethodDataAttribute(string data)
{
Data = data;
}
public string Data { get; }
}
}
}