From 231e8a9cf487597ebb1c4ad32823d6756219c276 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 10 Aug 2015 16:33:04 -0700 Subject: [PATCH] Create an abstraction model for TypeInfo for tag helper discovery. --- .../TagHelpers/IMemberInfo.cs | 30 ++ .../TagHelpers/IPropertyInfo.cs | 26 ++ .../TagHelpers/ITypeInfo.cs | 61 +++ .../TagHelpers/RuntimePropertyInfo.cs | 51 +++ .../TagHelpers/RuntimeTypeInfo.cs | 88 ++++ .../TagHelpers/TagHelperDescriptorFactory.cs | 93 +++-- .../TagHelpers/TagHelperTypeResolver.cs | 49 +-- .../DoesNotImplementRealITagHelper.cs | 9 + .../TagHelpers/IFakeTagHelper.cs | 14 + .../TagHelpers/ImplementsRealTagHelper.cs | 26 ++ .../TagHelpers/RuntimePropertyInfoTest.cs | 159 ++++++++ .../TagHelpers/RuntimeTypeInfoTest.cs | 375 ++++++++++++++++++ .../TagHelperDescriptorFactoryTest.cs | 55 +-- .../TagHelperDescriptorResolverTest.cs | 2 +- .../TagHelpers/TagHelperTypeResolverTest.cs | 12 +- 15 files changed, 958 insertions(+), 92 deletions(-) create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IMemberInfo.cs create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IPropertyInfo.cs create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimePropertyInfo.cs create mode 100644 src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs create mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/DoesNotImplementRealITagHelper.cs create mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/IFakeTagHelper.cs create mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/ImplementsRealTagHelper.cs create mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimePropertyInfoTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IMemberInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IMemberInfo.cs new file mode 100644 index 0000000000..6d569b0df1 --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IMemberInfo.cs @@ -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 System; +using System.Collections.Generic; + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + /// + /// Metadata common to types and properties. + /// + public interface IMemberInfo + { + /// + /// Gets the name. + /// + string Name { get; } + + /// + /// Retrieves a collection of custom s of type applied + /// to this instance of . + /// + /// The type of to search for. + /// A sequence of custom s of type + /// . + /// Result not include inherited s. + IEnumerable GetCustomAttributes() + where TAttribute : Attribute; + } +} diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IPropertyInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IPropertyInfo.cs new file mode 100644 index 0000000000..beb0840b57 --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/IPropertyInfo.cs @@ -0,0 +1,26 @@ +// 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. + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + /// + /// Contains property metadata. + /// + public interface IPropertyInfo : IMemberInfo + { + /// + /// Gets a value indicating whether this property has a public getter. + /// + bool HasPublicGetter { get; } + + /// + /// Gets a value indicating whether this property has a public setter. + /// + bool HasPublicSetter { get; } + + /// + /// Gets the of the property. + /// + ITypeInfo PropertyType { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs new file mode 100644 index 0000000000..294f247932 --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs @@ -0,0 +1,61 @@ +// 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.Collections.Generic; + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + /// + /// Contains type metadata. + /// + public interface ITypeInfo : IMemberInfo + { + /// + /// Fully qualified name of the type. + /// + string FullName { get; } + + /// + /// Gets s for all properties of the current type excluding indexers. + /// + /// + /// Indexers in this context refer to the CLR notion of an indexer (this [string name] + /// and does not overlap with the semantics of + /// . + /// + IEnumerable Properties { get; } + + /// + /// Gets a value indicating whether the type is public. + /// + bool IsPublic { get; } + + /// + /// Gets a value indicating whether the type is abstract or an interface. + /// + bool IsAbstract { get; } + + /// + /// Gets a value indicating whether the type is generic. + /// + bool IsGenericType { get; } + + /// + /// Gets a value indicating whether the type implements the interface. + /// + bool IsTagHelper { get; } + + /// + /// Gets the full names of the parameter types if the type implements + /// . + /// + /// + /// The full type names () of TKey and TValue + /// parameters if the type implements , otherwise null. + /// + /// + /// For open generic types, full type names for generic type parameters is null. + /// + string[] GetGenericDictionaryParameterNames(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimePropertyInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimePropertyInfo.cs new file mode 100644 index 0000000000..b79ff696d7 --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimePropertyInfo.cs @@ -0,0 +1,51 @@ +// 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.Reflection; +using Microsoft.AspNet.Razor.Runtime.TagHelpers; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Razor.Runtime +{ + /// + /// adapter for instances. + /// + public class RuntimePropertyInfo : IPropertyInfo + { + /// + /// Initializes a new instance of . + /// + /// The instance to adapt. + public RuntimePropertyInfo([NotNull] PropertyInfo propertyInfo) + { + Property = propertyInfo; + } + + /// + /// The instance. + /// + public PropertyInfo Property { get; } + + /// + public bool HasPublicGetter => Property.GetMethod != null && Property.GetMethod.IsPublic; + + /// + public bool HasPublicSetter => Property.SetMethod != null && Property.SetMethod.IsPublic; + + /// + public string Name => Property.Name; + + /// + public ITypeInfo PropertyType => new RuntimeTypeInfo(Property.PropertyType.GetTypeInfo()); + + /// + public IEnumerable GetCustomAttributes() where TAttribute : Attribute + => Property.GetCustomAttributes(inherit: false); + + /// + public override string ToString() => + Property.ToString(); + } +} diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs new file mode 100644 index 0000000000..dff34999c0 --- /dev/null +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs @@ -0,0 +1,88 @@ +// 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 System.Reflection; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + /// + /// adapter for instances. + /// + public class RuntimeTypeInfo : ITypeInfo + { + private static readonly TypeInfo TagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo(); + private IEnumerable _properties; + + /// + /// Initializes a new instance of + /// + /// The instance to adapt. + public RuntimeTypeInfo([NotNull] TypeInfo typeInfo) + { + TypeInfo = typeInfo; + } + + /// + /// The instance. + /// + public TypeInfo TypeInfo { get; } + + /// + public string Name => TypeInfo.Name; + + /// + public string FullName => TypeInfo.FullName; + + /// + public bool IsAbstract => TypeInfo.IsAbstract; + + /// + public bool IsGenericType => TypeInfo.IsGenericType; + + /// + public bool IsPublic => TypeInfo.IsPublic; + + /// + public IEnumerable Properties + { + get + { + if (_properties == null) + { + _properties = TypeInfo + .AsType() + .GetRuntimeProperties() + .Where(property => property.GetIndexParameters().Length == 0) + .Select(property => new RuntimePropertyInfo(property)); + } + + return _properties; + } + } + + /// + public bool IsTagHelper => TagHelperTypeInfo.IsAssignableFrom(TypeInfo); + + /// + public IEnumerable GetCustomAttributes() where TAttribute : Attribute => + TypeInfo.GetCustomAttributes(inherit: false); + + /// + public string[] GetGenericDictionaryParameterNames() + { + return ClosedGenericMatcher.ExtractGenericInterface( + TypeInfo.AsType(), + typeof(IDictionary<,>)) + ?.GenericTypeArguments + .Select(type => type.FullName) + .ToArray(); + } + + /// + public override string ToString() => TypeInfo.ToString(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs index 25195ddbff..c337ca89fc 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using System.Reflection; using System.Text.RegularExpressions; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.Framework.Internal; @@ -13,7 +12,7 @@ using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { /// - /// Factory for s from s. + /// Factory for s from s. /// public static class TagHelperDescriptorFactory { @@ -35,31 +34,30 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' }); /// - /// Creates a from the given . + /// Creates a from the given . /// /// The assembly name that contains . - /// The type to create a from. + /// The to create a from. + /// /// Indicates if the returned s should include /// design time specific information. /// The used to collect s encountered - /// when creating s for the given . + /// when creating s for the given . /// - /// A collection of s that describe the given . + /// A collection of s that describe the given . /// public static IEnumerable CreateDescriptors( string assemblyName, - [NotNull] Type type, + [NotNull] ITypeInfo typeInfo, bool designTime, [NotNull] ErrorSink errorSink) { - var typeInfo = type.GetTypeInfo(); - if (ShouldSkipDescriptorCreation(designTime, typeInfo)) { return Enumerable.Empty(); } - var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink); + var attributeDescriptors = GetAttributeDescriptors(typeInfo, designTime, errorSink); var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink); var allowedChildren = GetAllowedChildren(typeInfo, errorSink); @@ -76,16 +74,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } private static IEnumerable GetValidTargetElementAttributes( - TypeInfo typeInfo, + ITypeInfo typeInfo, ErrorSink errorSink) { - var targetElementAttributes = typeInfo.GetCustomAttributes(inherit: false); + var targetElementAttributes = typeInfo.GetCustomAttributes(); return targetElementAttributes.Where(attribute => ValidTargetElementAttributeNames(attribute, errorSink)); } private static IEnumerable BuildTagHelperDescriptors( - TypeInfo typeInfo, + ITypeInfo typeInfo, string assemblyName, IEnumerable attributeDescriptors, IEnumerable targetElementAttributes, @@ -97,7 +95,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers #if !DNXCORE50 if (designTime) { - typeDesignTimeDescriptor = TagHelperDesignTimeDescriptorFactory.CreateDescriptor(typeInfo.AsType()); + var runtimeTypeInfo = typeInfo as RuntimeTypeInfo; + if (runtimeTypeInfo != null) + { + typeDesignTimeDescriptor = + TagHelperDesignTimeDescriptorFactory.CreateDescriptor(runtimeTypeInfo.TypeInfo.AsType()); + } } #endif @@ -138,9 +141,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers typeDesignTimeDescriptor)); } - private static IEnumerable GetAllowedChildren(TypeInfo typeInfo, ErrorSink errorSink) + private static IEnumerable GetAllowedChildren(ITypeInfo typeInfo, ErrorSink errorSink) { - var restrictChildrenAttribute = typeInfo.GetCustomAttribute(inherit: false); + var restrictChildrenAttribute = typeInfo + .GetCustomAttributes() + .FirstOrDefault(); if (restrictChildrenAttribute == null) { return null; @@ -338,7 +343,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } private static IEnumerable GetAttributeDescriptors( - Type type, + ITypeInfo type, bool designTime, ErrorSink errorSink) { @@ -347,7 +352,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Keep indexer descriptors separate to avoid sorting the combined list later. var indexerDescriptors = new List(); - var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty); + var accessibleProperties = type.Properties.Where(IsAccessibleProperty); foreach (var property in accessibleProperties) { if (ShouldSkipDescriptorCreation(designTime, property)) @@ -355,13 +360,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers continue; } - var attributeNameAttribute = property.GetCustomAttribute(inherit: false); + var attributeNameAttribute = property + .GetCustomAttributes() + .FirstOrDefault(); var hasExplicitName = attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name); var attributeName = hasExplicitName ? attributeNameAttribute.Name : ToHtmlCase(property.Name); TagHelperAttributeDescriptor mainDescriptor = null; - if (property.SetMethod?.IsPublic == true) + if (property.HasPublicSetter) { mainDescriptor = ToAttributeDescriptor(property, attributeName, designTime); if (!ValidateTagHelperAttributeDescriptor(mainDescriptor, type, errorSink)) @@ -425,7 +432,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Internal for testing. internal static bool ValidateTagHelperAttributeDescriptor( TagHelperAttributeDescriptor attributeDescriptor, - Type parentType, + ITypeInfo parentType, ErrorSink errorSink) { string nameOrPrefix; @@ -457,13 +464,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers nameOrPrefix); } - private static bool ShouldSkipDescriptorCreation(bool designTime, MemberInfo memberInfo) + private static bool ShouldSkipDescriptorCreation(bool designTime, IMemberInfo memberInfo) { if (designTime) { - var editorBrowsableAttribute = memberInfo.GetCustomAttribute(inherit: false); + var editorBrowsableAttribute = memberInfo + .GetCustomAttributes() + .FirstOrDefault(); - return editorBrowsableAttribute != null && editorBrowsableAttribute.State == EditorBrowsableState.Never; + return editorBrowsableAttribute != null && + editorBrowsableAttribute.State == EditorBrowsableState.Never; } return false; @@ -471,7 +481,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers private static bool ValidateTagHelperAttributeNameOrPrefix( string attributeNameOrPrefix, - Type parentType, + ITypeInfo parentType, string propertyName, ErrorSink errorSink, string nameOrPrefix) @@ -540,7 +550,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } private static TagHelperAttributeDescriptor ToAttributeDescriptor( - PropertyInfo property, + IPropertyInfo property, string attributeName, bool designTime) { @@ -553,21 +563,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } private static TagHelperAttributeDescriptor ToIndexerAttributeDescriptor( - PropertyInfo property, + IPropertyInfo property, HtmlAttributeNameAttribute attributeNameAttribute, - Type parentType, + ITypeInfo parentType, ErrorSink errorSink, string defaultPrefix, bool designTime, out bool isInvalid) { isInvalid = false; - var hasPublicSetter = property.SetMethod?.IsPublic == true; - var dictionaryTypeArguments = ClosedGenericMatcher.ExtractGenericInterface( - property.PropertyType, - typeof(IDictionary<,>)) - ?.GenericTypeArguments; - if (dictionaryTypeArguments?[0] != typeof(string)) + var hasPublicSetter = property.HasPublicSetter; + var dictionaryTypeArguments = property.PropertyType.GetGenericDictionaryParameterNames(); + if (dictionaryTypeArguments?[0] != typeof(string).FullName) { if (attributeNameAttribute?.DictionaryAttributePrefix != null) { @@ -634,13 +641,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return ToAttributeDescriptor( property, attributeName: prefix, - typeName: dictionaryTypeArguments[1].FullName, + typeName: dictionaryTypeArguments[1], isIndexer: true, designTime: designTime); } private static TagHelperAttributeDescriptor ToAttributeDescriptor( - PropertyInfo property, + IPropertyInfo property, string attributeName, string typeName, bool isIndexer, @@ -651,8 +658,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers #if !DNXCORE50 if (designTime) { - propertyDesignTimeDescriptor = - TagHelperDesignTimeDescriptorFactory.CreateAttributeDescriptor(property); + var runtimeProperty = property as RuntimePropertyInfo; + if (runtimeProperty != null) + { + propertyDesignTimeDescriptor = + TagHelperDesignTimeDescriptorFactory.CreateAttributeDescriptor(runtimeProperty.Property); + } } #endif @@ -666,11 +677,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers }; } - private static bool IsAccessibleProperty(PropertyInfo property) + private static bool IsAccessibleProperty(IPropertyInfo property) { // Accessible properties are those with public getters and without [HtmlAttributeNotBound]. - return property.GetMethod?.IsPublic == true && - property.GetCustomAttribute(inherit: false) == null; + return property.HasPublicGetter && + property.GetCustomAttributes().FirstOrDefault() == null; } /// diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs index bc383d41ac..8a5ea0b43d 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs @@ -17,25 +17,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers private static readonly TypeInfo ITagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo(); /// - /// Instantiates a new instance of the class. - /// - public TagHelperTypeResolver() - { - } - - /// - /// Loads an using the given and resolves - /// all valid s. + /// Locates valid types from the named . /// /// The name of an to search. /// The of the associated /// responsible for the current call. /// /// The used to record errors found when resolving - /// s. - /// An of valid s. - /// - public IEnumerable Resolve( + /// types. + /// An of valid types. + public IEnumerable Resolve( string name, SourceLocation documentLocation, [NotNull] ErrorSink errorSink) @@ -48,15 +39,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull, errorLength); - return Type.EmptyTypes; + return Enumerable.Empty(); } var assemblyName = new AssemblyName(name); - IEnumerable libraryTypes; + IEnumerable libraryTypes; try { - libraryTypes = GetExportedTypes(assemblyName); + libraryTypes = GetTopLevelExportedTypes(assemblyName); } catch (Exception ex) { @@ -67,13 +58,26 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers ex.Message), name.Length); - return Type.EmptyTypes; + return Enumerable.Empty(); } - var validTagHelpers = libraryTypes.Where(IsTagHelper); + return libraryTypes.Where(IsTagHelper); + } - // Convert from TypeInfo[] to Type[] - return validTagHelpers.Select(type => type.AsType()); + /// + /// Returns all non-nested exported types from the given + /// + /// The to get s from. + /// + /// An of types exported from the given . + /// + protected virtual IEnumerable GetTopLevelExportedTypes([NotNull] AssemblyName assemblyName) + { + var exportedTypeInfos = GetExportedTypes(assemblyName); + + return exportedTypeInfos + .Where(typeInfo => !typeInfo.IsNested) + .Select(typeInfo => new RuntimeTypeInfo(typeInfo)); } /// @@ -91,13 +95,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } // Internal for testing. - internal virtual bool IsTagHelper(TypeInfo typeInfo) + internal virtual bool IsTagHelper(ITypeInfo typeInfo) { return typeInfo.IsPublic && !typeInfo.IsAbstract && !typeInfo.IsGenericType && - !typeInfo.IsNested && - ITagHelperTypeInfo.IsAssignableFrom(typeInfo); + typeInfo.IsTagHelper; } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/DoesNotImplementRealITagHelper.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/DoesNotImplementRealITagHelper.cs new file mode 100644 index 0000000000..b1ed2ee105 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/DoesNotImplementRealITagHelper.cs @@ -0,0 +1,9 @@ +// 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. + +namespace Microsoft.AspNet.Razor.Fake +{ + public class DoesNotImplementRealITagHelper : Microsoft.AspNet.Razor.Fake.ITagHelper + { + } +} diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/IFakeTagHelper.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/IFakeTagHelper.cs new file mode 100644 index 0000000000..1e9ef72e21 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/IFakeTagHelper.cs @@ -0,0 +1,14 @@ +// 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.AspNet.Razor.Runtime.TagHelpers; + +namespace Microsoft.AspNet.Razor.Fake +{ + public interface ITagHelper + { + + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/ImplementsRealTagHelper.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/ImplementsRealTagHelper.cs new file mode 100644 index 0000000000..b7d3a21a54 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/ImplementsRealTagHelper.cs @@ -0,0 +1,26 @@ +// 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.AspNet.Razor.Runtime.TagHelpers; + +namespace Microsoft.AspNet.Razor.Fake +{ + public class ImplementsRealITagHelper : Microsoft.AspNet.Razor.Runtime.TagHelpers.ITagHelper + { + public int Order + { + get + { + throw new NotImplementedException(); + } + } + + public Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + throw new NotImplementedException(); + } + } + +} diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimePropertyInfoTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimePropertyInfoTest.cs new file mode 100644 index 0000000000..78b1fb1cb7 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimePropertyInfoTest.cs @@ -0,0 +1,159 @@ +// 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.Linq; +using System.Reflection; +using Xunit; + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + public class RuntimePropertyInfoTest + { + [Fact] + public void PropertyInfo_ReturnsMetadataOfAdaptingProperty() + { + // Arrange + var property = GetPropertyInfo(nameof(TestType.Property)); + var runtimePropertyInfo = new RuntimePropertyInfo(property); + + // Act + var actual = runtimePropertyInfo.Property; + + // Assert + Assert.Same(property, actual); + var runtimeTypeInfo = Assert.IsType(runtimePropertyInfo.PropertyType); + Assert.Same(property.PropertyType, runtimeTypeInfo.TypeInfo); + } + + [Theory] + [InlineData(nameof(TestType.Property))] + [InlineData(nameof(TestType.PrivateSetter))] + [InlineData(nameof(TestType.PropertyWithoutSetter))] + public void HasPublicGetter_ReturnsTrueIfGetterExistsAndIsPublic(string propertyName) + { + // Arrange + var property = GetPropertyInfo(propertyName); + var runtimePropertyInfo = new RuntimePropertyInfo(property); + + // Act + var result = runtimePropertyInfo.HasPublicGetter; + + // Assert + Assert.True(result); + } + + [Theory] + [InlineData(nameof(TestType.PrivateGetter))] + [InlineData(nameof(TestType.PropertyWithoutGetter))] + [InlineData("ProtectedProperty")] + public void HasPublicGetter_ReturnsFalseIfGetterDoesNotExistOrIsNonPublic(string propertyName) + { + // Arrange + var property = GetPropertyInfo(propertyName); + var runtimePropertyInfo = new RuntimePropertyInfo(property); + + // Act + var result = runtimePropertyInfo.HasPublicGetter; + + // Assert + Assert.False(result); + } + + [Theory] + [InlineData(nameof(TestType.Property))] + [InlineData(nameof(TestType.PrivateGetter))] + [InlineData(nameof(TestType.PropertyWithoutGetter))] + public void HasPublicSetter_ReturnsTrueIfSetterExistsAndIsPublic(string propertyName) + { + // Arrange + var property = GetPropertyInfo(propertyName); + var runtimePropertyInfo = new RuntimePropertyInfo(property); + + // Act + var result = runtimePropertyInfo.HasPublicSetter; + + // Assert + Assert.True(result); + } + + [Theory] + [InlineData(nameof(TestType.PrivateSetter))] + [InlineData(nameof(TestType.PropertyWithoutSetter))] + [InlineData("ProtectedProperty")] + public void HasPublicSetter_ReturnsFalseIfGetterDoesNotExistOrIsNonPublic(string propertyName) + { + // Arrange + var property = GetPropertyInfo(propertyName); + var runtimePropertyInfo = new RuntimePropertyInfo(property); + + // Act + var result = runtimePropertyInfo.HasPublicSetter; + + // Assert + Assert.False(result); + } + + [Fact] + public void GetAttributes_ReturnsCustomAttributesOfSpecifiedType() + { + // Arrange + var property = GetPropertyInfo(nameof(TestType.PropertyWithAttributes)); + var runtimeProperty = new RuntimePropertyInfo(property); + + // Act + var attributes = property.GetCustomAttributes(); + + // Assert + var htmlAttributeName = Assert.Single(attributes); + Assert.Equal("somename", htmlAttributeName.Name); + } + + [Fact] + public void GetAttributes_DoesNotInheritAttributes() + { + // Arrange + var property = GetPropertyInfo(nameof(TestType.PropertyWithAttributes)); + var runtimeProperty = new RuntimePropertyInfo(property); + + // Act + var attributes = property.GetCustomAttributes(); + + // Assert + Assert.Empty(attributes); + } + + private static PropertyInfo GetPropertyInfo(string propertyName) + { + return typeof(TestType).GetRuntimeProperties() + .FirstOrDefault(p => p.Name == propertyName); + } + + public class BaseType + { + [HtmlAttributeNotBound] + public virtual string PropertyWithAttributes { get; } + } + + public class TestType : BaseType + { + public string Property { get; set; } + + public int PrivateSetter { get; private set; } + + public object PrivateGetter { private get; set; } + + protected DateTimeOffset ProtectedProperty { get; set; } + + public string PropertyWithoutGetter + { + set { } + } + + public int PropertyWithoutSetter => 0; + + [HtmlAttributeName("somename")] + public override string PropertyWithAttributes { get; } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs new file mode 100644 index 0000000000..8c4a3290e0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs @@ -0,0 +1,375 @@ +// 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; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNet.Razor.Runtime.TagHelpers; +using Xunit; + +namespace Microsoft.AspNet.Razor.Runtime.TagHelpers +{ + public class RuntimeTypeInfoTest + { + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(string))] + [InlineData(typeof(Tuple<,>))] + [InlineData(typeof(IDictionary))] + [InlineData(typeof(IDictionary>))] + [InlineData(typeof(AbstractType))] + [InlineData(typeof(PrivateType))] + [InlineData(typeof(KnownKeyDictionary<>))] + [InlineData(typeof(KnownKeyDictionary))] + public void RuntimeTypeInfo_ReturnsMetadataOfAdaptingType(Type type) + { + // Arrange + var typeInfo = type.GetTypeInfo(); + var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo); + + // Act and Assert + Assert.Same(runtimeTypeInfo.TypeInfo, typeInfo); + Assert.Equal(runtimeTypeInfo.Name, typeInfo.Name); + Assert.Equal(runtimeTypeInfo.FullName, typeInfo.FullName); + Assert.Equal(runtimeTypeInfo.IsAbstract, typeInfo.IsAbstract); + Assert.Equal(runtimeTypeInfo.IsGenericType, typeInfo.IsGenericType); + Assert.Equal(runtimeTypeInfo.IsPublic, typeInfo.IsPublic); + } + + [Fact] + public void Properties_ReturnsPublicPropertiesOfAdaptingType() + { + // Arrange + var typeInfo = typeof(SubType).GetTypeInfo(); + var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo); + + // Act and Assert + Assert.Collection(runtimeTypeInfo.Properties, + property => + { + Assert.IsType(property); + Assert.Equal("Property1", property.Name); + }, + property => + { + Assert.IsType(property); + Assert.Equal("Property2", property.Name); + }, + property => + { + Assert.IsType(property); + Assert.Equal("Property3", property.Name); + }, + property => + { + Assert.IsType(property); + Assert.Equal("Property4", property.Name); + }, + property => + { + Assert.IsType(property); + Assert.Equal("BaseTypeProperty", property.Name); + }, + property => + { + Assert.IsType(property); + Assert.Equal("ProtectedProperty", property.Name); + }); + } + + [Fact] + public void GetCustomAttributes_ReturnsAllAttributesOfType() + { + // Arrange + var typeInfo = typeof(TypeWithAttributes).GetTypeInfo(); + var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo); + var expected = typeInfo.GetCustomAttributes(); + + // Act + var actual = runtimeTypeInfo.GetCustomAttributes(); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void GetCustomAttributes_DoesNotInheritAttributesFromBaseType() + { + // Arrange + var typeInfo = typeof(SubType).GetTypeInfo(); + var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo); + + // Act + var actual = runtimeTypeInfo.GetCustomAttributes(); + + // Assert + Assert.Empty(actual); + } + + [Theory] + [InlineData(typeof(ITagHelper))] + [InlineData(typeof(TagHelper))] + [InlineData(typeof(ImplementsITagHelper))] + [InlineData(typeof(DerivesFromTagHelper))] + [InlineData(typeof(Fake.ImplementsRealITagHelper))] + public void IsTagHelper_ReturnsTrueIfTypeImplementsTagHelper(Type type) + { + // Arrange + var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo()); + + // Act + var result = runtimeTypeInfo.IsTagHelper; + + // Assert + Assert.True(result); + } + + [Theory] + [InlineData(typeof(string))] + [InlineData(typeof(SubType))] + [InlineData(typeof(Fake.DoesNotImplementRealITagHelper))] + public void IsTagHelper_ReturnsFalseIfTypeDoesNotImplementTagHelper(Type type) + { + // Arrange + var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo()); + + // Act + var result = runtimeTypeInfo.IsTagHelper; + + // Assert + Assert.False(result); + } + + [Theory] + [InlineData(typeof(Dictionary), new[] { typeof(string), typeof(string) })] + [InlineData(typeof(DerivesFromDictionary), new[] { typeof(int), typeof(object) })] + [InlineData(typeof(ImplementsIDictionary), new[] { typeof(List), typeof(string) })] + [InlineData(typeof(IDictionary>), + new[] { typeof(string), typeof(IDictionary) })] + [InlineData(typeof(Dictionary<,>), new Type[] { null, null })] + [InlineData(typeof(KnownKeyDictionary<>), new[] { typeof(string), null })] + [InlineData(typeof(KnownKeyDictionary), + new[] { typeof(string), typeof(ImplementsIDictionary) })] + public void GetGenericDictionaryParameterNames_ReturnsKeyAndValueParameterTypeNames( + Type type, + Type[] expectedTypes) + { + // Arrange + var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo()); + var expected = expectedTypes.Select(t => t?.FullName); + + // Act + var actual = runtimeTypeInfo.GetGenericDictionaryParameterNames(); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(string))] + [InlineData(typeof(List))] + [InlineData(typeof(IDictionary))] + [InlineData(typeof(ITagHelper))] + public void GetGenericDictionaryParameterNames_ReturnsNullIfTypeDoesNotImplementGenericDictionary(Type type) + { + // Arrange + var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo()); + + // Act + var actual = runtimeTypeInfo.GetGenericDictionaryParameterNames(); + + // Assert + Assert.Null(actual); + } + + public class AbstractType + { + } + + internal class InternalType + { + } + + private class PrivateType + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private class BaseType + { + public string this[string key] + { + get { return ""; } + set { } + } + + public string BaseTypeProperty { get; set; } + + protected int ProtectedProperty { get; set; } + } + + private class SubType : BaseType + { + public string Property1 { get; set; } + + public int Property2 { get; } + + public object Property3 { private get; set; } + + private int Property4 { get; set; } + } + + [TargetElement("test1")] + [TargetElement("test2")] + private class TypeWithAttributes + { + } + + private class DerivesFromTagHelper : TagHelper + { + } + + private class ImplementsITagHelper : ITagHelper + { + public int Order { get; } = 0; + + public Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + throw new NotImplementedException(); + } + } + + private class DerivesFromDictionary : Dictionary + { + } + + private class ImplementsIDictionary : IDictionary, string>, IReadOnlyList + { + public int this[int index] + { + get + { + throw new NotImplementedException(); + } + } + + public string this[List key] + { + get + { + throw new NotImplementedException(); + } + + set + { + throw new NotImplementedException(); + } + } + + public int Count + { + get + { + throw new NotImplementedException(); + } + } + + public bool IsReadOnly + { + get + { + throw new NotImplementedException(); + } + } + + public ICollection> Keys + { + get + { + throw new NotImplementedException(); + } + } + + public ICollection Values + { + get + { + throw new NotImplementedException(); + } + } + + public void Add(KeyValuePair, string> item) + { + throw new NotImplementedException(); + } + + public void Add(List key, string value) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair, string> item) + { + throw new NotImplementedException(); + } + + public bool ContainsKey(List key) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair, string>[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public IEnumerator, string>> GetEnumerator() + { + throw new NotImplementedException(); + } + + public bool Remove(KeyValuePair, string> item) + { + throw new NotImplementedException(); + } + + public bool Remove(List key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(List key, out string value) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public class KnownKeyDictionary : Dictionary + { + } + + private class CustomType + { + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs index db99a15e0b..95009711be 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs @@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime: false, errorSink: errorSink); @@ -175,7 +175,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime: false, errorSink: errorSink); @@ -256,7 +256,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime: true, errorSink: errorSink); @@ -493,7 +493,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime, errorSink); @@ -695,7 +695,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime: false, errorSink: errorSink); @@ -744,7 +744,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime: false, errorSink: errorSink); @@ -781,7 +781,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(OverriddenAttributeTagHelper), + GetTypeInfo(typeof(OverriddenAttributeTagHelper)), designTime: false, errorSink: errorSink); @@ -815,7 +815,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(InheritedOverriddenAttributeTagHelper), + GetTypeInfo(typeof(InheritedOverriddenAttributeTagHelper)), designTime: false, errorSink: errorSink); @@ -849,7 +849,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(InheritedNotOverriddenAttributeTagHelper), + GetTypeInfo(typeof(InheritedNotOverriddenAttributeTagHelper)), designTime: false, errorSink: errorSink); @@ -870,7 +870,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( objectAssemblyName, - typeof(object), + GetTypeInfo(typeof(object)), designTime: false, errorSink: errorSink); @@ -902,7 +902,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(InheritedSingleAttributeTagHelper), + GetTypeInfo(typeof(InheritedSingleAttributeTagHelper)), designTime: false, errorSink: errorSink); @@ -930,7 +930,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(SingleAttributeTagHelper), + GetTypeInfo(typeof(SingleAttributeTagHelper)), designTime: false, errorSink: new ErrorSink()); @@ -959,7 +959,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(MissingAccessorTagHelper), + GetTypeInfo(typeof(MissingAccessorTagHelper)), designTime: false, errorSink: errorSink); @@ -988,7 +988,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(NonPublicAccessorTagHelper), + GetTypeInfo(typeof(NonPublicAccessorTagHelper)), designTime: false, errorSink: errorSink); @@ -1020,7 +1020,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(NotBoundAttributeTagHelper), + GetTypeInfo(typeof(NotBoundAttributeTagHelper)), designTime: false, errorSink: errorSink); @@ -1039,7 +1039,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(DuplicateAttributeNameTagHelper), + GetTypeInfo(typeof(DuplicateAttributeNameTagHelper)), designTime: false, errorSink: errorSink); @@ -1086,7 +1086,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(MultiTagTagHelper), + GetTypeInfo(typeof(MultiTagTagHelper)), designTime: false, errorSink: errorSink); @@ -1118,7 +1118,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(InheritedMultiTagTagHelper), + GetTypeInfo(typeof(InheritedMultiTagTagHelper)), designTime: false, errorSink: errorSink); @@ -1148,7 +1148,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(DuplicateTagNameTagHelper), + GetTypeInfo(typeof(DuplicateTagNameTagHelper)), designTime: false, errorSink: errorSink); @@ -1178,7 +1178,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - typeof(OverrideNameTagHelper), + GetTypeInfo(typeof(OverrideNameTagHelper)), designTime: false, errorSink: errorSink); @@ -1366,7 +1366,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - type, + GetTypeInfo(type), designTime: false, errorSink: errorSink); @@ -1597,7 +1597,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, - tagHelperType, + GetTypeInfo(tagHelperType), designTime: false, errorSink: errorSink); @@ -1651,7 +1651,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( descriptor, - typeof(MultiTagTagHelper), + GetTypeInfo(typeof(MultiTagTagHelper)), errorSink); // Assert @@ -1693,7 +1693,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( descriptor, - typeof(MultiTagTagHelper), + GetTypeInfo(typeof(MultiTagTagHelper)), errorSink); // Assert @@ -1739,7 +1739,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( descriptor, - typeof(MultiTagTagHelper), + GetTypeInfo(typeof(MultiTagTagHelper)), errorSink); // Assert @@ -1794,7 +1794,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( descriptor, - typeof(MultiTagTagHelper), + GetTypeInfo(typeof(MultiTagTagHelper)), errorSink); // Assert @@ -2101,6 +2101,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers }; } + private static ITypeInfo GetTypeInfo(Type tagHelperType) => + new RuntimeTypeInfo(tagHelperType.GetTypeInfo()); + [RestrictChildren("p")] private class RestrictChildrenTagHelper { diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs index 4482690b2d..8973f5a39a 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs @@ -1575,7 +1575,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return types?.Select(type => type.GetTypeInfo()) ?? Enumerable.Empty(); } - internal override bool IsTagHelper(TypeInfo typeInfo) + internal override bool IsTagHelper(ITypeInfo typeInfo) { return true; } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs index 0ccf0daf79..d72266253f 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs @@ -62,7 +62,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var types = tagHelperTypeResolver.Resolve("Foo", SourceLocation.Zero, new ErrorSink()); // Assert - Assert.Equal(ValidTestableTagHelpers, types); + Assert.Collection(types, + type => + { + var typeInfo = Assert.IsType(type); + Assert.Equal(typeof(Valid_PlainTagHelper).GetTypeInfo(), typeInfo.TypeInfo); + }, + type => + { + var typeInfo = Assert.IsType(type); + Assert.Equal(typeof(Valid_InheritedTagHelper).GetTypeInfo(), typeInfo.TypeInfo); + }); } [Fact]