diff --git a/src/Microsoft.AspNet.Razor.Runtime.Precompilation/CodeAnalysisSymbolBasedTypeInfo.cs b/src/Microsoft.AspNet.Razor.Runtime.Precompilation/CodeAnalysisSymbolBasedTypeInfo.cs index 3865b6e04b..81da1bbedc 100644 --- a/src/Microsoft.AspNet.Razor.Runtime.Precompilation/CodeAnalysisSymbolBasedTypeInfo.cs +++ b/src/Microsoft.AspNet.Razor.Runtime.Precompilation/CodeAnalysisSymbolBasedTypeInfo.cs @@ -28,6 +28,7 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation private readonly ITypeSymbol _type; private readonly ITypeSymbol _underlyingType; private string _fullName; + private string _sanitizedFullName; private List _properties; /// @@ -128,6 +129,19 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation } } + private string SanitizedFullName + { + get + { + if (_sanitizedFullName == null) + { + _sanitizedFullName = RuntimeTypeInfo.SanitizeFullName(FullName); + } + + return _sanitizedFullName; + } + } + /// public IEnumerable GetCustomAttributes() where TAttribute : Attribute @@ -222,11 +236,14 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation return otherSymbolBasedType.TypeSymbol == TypeSymbol; } - return string.Equals(FullName, other.FullName, StringComparison.Ordinal); + return string.Equals( + SanitizedFullName, + RuntimeTypeInfo.SanitizeFullName(other.FullName), + StringComparison.Ordinal); } /// - public override int GetHashCode() => FullName.GetHashCode(); + public override int GetHashCode() => SanitizedFullName.GetHashCode(); private static List GetProperties(ITypeSymbol typeSymbol) { @@ -276,7 +293,7 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation if (typeSymbol.TypeKind == TypeKind.Array) { var arrayType = (IArrayTypeSymbol)typeSymbol; - GetFullName(nameBuilder, arrayType.ElementType); + GetAssemblyQualifiedName(nameBuilder, arrayType.ElementType); nameBuilder.Append("[]"); return; } @@ -293,7 +310,7 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation foreach (var typeArgument in namedSymbol.TypeArguments) { nameBuilder.Append('['); - GetFullName(nameBuilder, typeArgument); + GetAssemblyQualifiedName(nameBuilder, typeArgument); nameBuilder.Append("],"); } diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs index 9a4cc17e3b..f2b2f78a65 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ITypeInfo.cs @@ -1,6 +1,7 @@ // 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 @@ -8,11 +9,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// /// Contains type metadata. /// - public interface ITypeInfo : IMemberInfo + public interface ITypeInfo : IMemberInfo, IEquatable { /// /// Fully qualified name of the type. /// + /// + /// On CoreCLR, some BCL types get type forwarded to the full desktop framework implementations at + /// runtime. For e.g. we compile against System.String in System.Runtime which is type forwarded to + /// mscorlib at runtime. Consequently for generic types where the includes the assembly + /// qualified name of generic parameters, FullNames would not match. + /// Use to compare s instead. + /// string FullName { get; } /// diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs index c67d732d9b..6205ad5364 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RuntimeTypeInfo.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers private static readonly TypeInfo TagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo(); private IEnumerable _properties; + private string _sanitizedFullName; /// /// Initializes a new instance of @@ -46,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public string Name => TypeInfo.Name; /// - public string FullName => SanitizeFullName(TypeInfo.FullName); + public string FullName => TypeInfo.FullName; /// public bool IsAbstract => TypeInfo.IsAbstract; @@ -78,6 +79,19 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// public bool IsTagHelper => TagHelperTypeInfo.IsAssignableFrom(TypeInfo); + private string SanitizedFullName + { + get + { + if (_sanitizedFullName == null) + { + _sanitizedFullName = SanitizeFullName(FullName); + } + + return _sanitizedFullName; + } + } + /// public IEnumerable GetCustomAttributes() where TAttribute : Attribute => TypeInfo.GetCustomAttributes(inherit: false); @@ -116,14 +130,27 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return otherRuntimeType.TypeInfo == TypeInfo; } - return string.Equals(FullName, other.FullName, StringComparison.Ordinal); + return string.Equals( + SanitizedFullName, + SanitizeFullName(other.FullName), + StringComparison.Ordinal); } /// - public override int GetHashCode() => FullName.GetHashCode(); + public override int GetHashCode() => SanitizedFullName.GetHashCode(); - // Internal for unit testing - internal static string SanitizeFullName(string fullName) + /// + /// Removes assembly qualification from generic type parameters for the specified . + /// + /// Full name. + /// Full name without fully qualified generic parameters. + /// + /// typeof().FullName is + /// List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] + /// Sanitize(typeof(.FullName returns + /// List`1[[System.String] + /// + public static string SanitizeFullName(string fullName) { // In CoreCLR, some types (such as System.String) are type forwarded from System.Runtime // to mscorlib at runtime. Type names of generic type parameters includes the assembly qualified name; diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs index 7a91562a44..06b07b2b00 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs @@ -579,6 +579,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers attributeName, property.PropertyType.FullName, isIndexer: false, + isStringProperty: StringTypeInfo.Equals(property.PropertyType), designTime: designTime); } @@ -663,6 +664,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers attributeName: prefix, typeName: dictionaryTypeArguments[1].FullName, isIndexer: true, + isStringProperty: StringTypeInfo.Equals(dictionaryTypeArguments[1]), designTime: designTime); } @@ -671,6 +673,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers string attributeName, string typeName, bool isIndexer, + bool isStringProperty, bool designTime) { TagHelperAttributeDesignTimeDescriptor propertyDesignTimeDescriptor = null; @@ -692,6 +695,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Name = attributeName, PropertyName = property.Name, TypeName = typeName, + IsStringProperty = isStringProperty, IsIndexer = isIndexer, DesignTimeDescriptor = propertyDesignTimeDescriptor }; diff --git a/src/Microsoft.AspNet.Razor.Test.Sources/CaseSensitiveTagHelperDescriptorComparer.cs b/src/Microsoft.AspNet.Razor.Test.Sources/CaseSensitiveTagHelperDescriptorComparer.cs index b462ddb152..c01092ef36 100644 --- a/src/Microsoft.AspNet.Razor.Test.Sources/CaseSensitiveTagHelperDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor.Test.Sources/CaseSensitiveTagHelperDescriptorComparer.cs @@ -2,9 +2,11 @@ // 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.AspNet.Razor.TagHelpers; using Microsoft.Framework.Internal; +using Xunit; namespace Microsoft.AspNet.Razor.Test.Internal { @@ -25,26 +27,29 @@ namespace Microsoft.AspNet.Razor.Test.Internal return true; } - return base.Equals(descriptorX, descriptorY) && - // Normal comparer doesn't care about the case, required attribute order, allowed children order, - // attributes or prefixes. In tests we do. - string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) && - string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.Ordinal) && - Enumerable.SequenceEqual( - descriptorX.RequiredAttributes, - descriptorY.RequiredAttributes, - StringComparer.Ordinal) && - (descriptorX.AllowedChildren == descriptorY.AllowedChildren || - Enumerable.SequenceEqual( - descriptorX.AllowedChildren, - descriptorY.AllowedChildren, - StringComparer.Ordinal)) && - descriptorX.Attributes.SequenceEqual( - descriptorY.Attributes, - TagHelperAttributeDescriptorComparer.Default) && - TagHelperDesignTimeDescriptorComparer.Default.Equals( - descriptorX.DesignTimeDescriptor, - descriptorY.DesignTimeDescriptor); + Assert.True(base.Equals(descriptorX, descriptorY)); + + // Normal comparer doesn't care about the case, required attribute order, allowed children order, + // attributes or prefixes. In tests we do. + Assert.Equal(descriptorX.TagName, descriptorY.TagName, StringComparer.Ordinal); + Assert.Equal(descriptorX.Prefix, descriptorY.Prefix, StringComparer.Ordinal); + Assert.Equal(descriptorX.RequiredAttributes, descriptorY.RequiredAttributes, StringComparer.Ordinal); + + if (descriptorX.AllowedChildren != descriptorY.AllowedChildren) + { + Assert.Equal(descriptorX.AllowedChildren, descriptorY.AllowedChildren, StringComparer.Ordinal); + } + + Assert.Equal( + descriptorX.Attributes, + descriptorY.Attributes, + TagHelperAttributeDescriptorComparer.Default); + Assert.Equal( + descriptorX.DesignTimeDescriptor, + descriptorY.DesignTimeDescriptor, + TagHelperDesignTimeDescriptorComparer.Default); + + return true; } public override int GetHashCode(TagHelperDescriptor descriptor) diff --git a/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDescriptorComparer.cs b/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDescriptorComparer.cs index 92fa2dc4d3..a97d389be8 100644 --- a/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDescriptorComparer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.Framework.Internal; +using Xunit; namespace Microsoft.AspNet.Razor.Test.Internal { @@ -24,14 +25,15 @@ namespace Microsoft.AspNet.Razor.Test.Internal return true; } - return descriptorX != null && - descriptorY != null && - descriptorX.IsIndexer == descriptorY.IsIndexer && - string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal) && - string.Equals(descriptorX.PropertyName, descriptorY.PropertyName, StringComparison.Ordinal) && - string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) && - descriptorX.IsStringProperty == descriptorY.IsStringProperty && - TagHelperAttributeDesignTimeDescriptorComparer.Default.Equals( + Assert.NotNull(descriptorX); + Assert.NotNull(descriptorY); + Assert.Equal(descriptorX.IsIndexer, descriptorY.IsIndexer); + Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal); + Assert.Equal(descriptorX.PropertyName, descriptorY.PropertyName, StringComparer.Ordinal); + Assert.Equal(descriptorX.TypeName, descriptorY.TypeName, StringComparer.Ordinal); + Assert.Equal(descriptorX.IsStringProperty, descriptorY.IsStringProperty); + + return TagHelperAttributeDesignTimeDescriptorComparer.Default.Equals( descriptorX.DesignTimeDescriptor, descriptorY.DesignTimeDescriptor); } diff --git a/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDesignTimeDescriptorComparer.cs b/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDesignTimeDescriptorComparer.cs index 60aeafe3e8..5fb63ebbcb 100644 --- a/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDesignTimeDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperAttributeDesignTimeDescriptorComparer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.Framework.Internal; +using Xunit; namespace Microsoft.AspNet.Razor.Test.Internal { @@ -27,10 +28,12 @@ namespace Microsoft.AspNet.Razor.Test.Internal return true; } - return descriptorX != null && - descriptorY != null && - string.Equals(descriptorX.Summary, descriptorY.Summary, StringComparison.Ordinal) && - string.Equals(descriptorX.Remarks, descriptorY.Remarks, StringComparison.Ordinal); + Assert.NotNull(descriptorX); + Assert.NotNull(descriptorY); + Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal); + Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal); + + return true; } public int GetHashCode(TagHelperAttributeDesignTimeDescriptor descriptor) diff --git a/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperDesignTimeDescriptorComparer.cs b/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperDesignTimeDescriptorComparer.cs index a24d02b2b0..c9f20181a0 100644 --- a/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperDesignTimeDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor.Test.Sources/TagHelperDesignTimeDescriptorComparer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.Framework.Internal; +using Xunit; namespace Microsoft.AspNet.Razor.Test.Internal { @@ -24,11 +25,13 @@ namespace Microsoft.AspNet.Razor.Test.Internal return true; } - return descriptorX != null && - descriptorY != null && - string.Equals(descriptorX.Summary, descriptorY.Summary, StringComparison.Ordinal) && - string.Equals(descriptorX.Remarks, descriptorY.Remarks, StringComparison.Ordinal) && - string.Equals(descriptorX.OutputElementHint, descriptorY.OutputElementHint, StringComparison.Ordinal); + Assert.NotNull(descriptorX); + Assert.NotNull(descriptorY); + Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal); + Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal); + Assert.Equal(descriptorX.OutputElementHint, descriptorY.OutputElementHint, StringComparer.Ordinal); + + return true; } public int GetHashCode(TagHelperDesignTimeDescriptor descriptor) diff --git a/src/Microsoft.AspNet.Razor.Test.Sources/project.json b/src/Microsoft.AspNet.Razor.Test.Sources/project.json index 2212737bd8..91408b685c 100644 --- a/src/Microsoft.AspNet.Razor.Test.Sources/project.json +++ b/src/Microsoft.AspNet.Razor.Test.Sources/project.json @@ -1,7 +1,9 @@ { "version": "4.0.0-*", "shared": "*.cs", - "dependencies": { }, + "dependencies": { + "xunit.assert": "2.1.0-*" + }, "frameworks": { "net45": { }, "dotnet": { diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs index 8fc2983c1d..8d7ee4ea18 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeDescriptor.cs @@ -46,15 +46,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers public bool IsIndexer { get; set; } /// - /// Gets an indication whether this property is of type or, if is - /// true, whether the indexer's value is of type . + /// Gets or sets an indication whether this property is of type or, if + /// is true, whether the indexer's value is of type . /// /// /// If true the is for . This causes the Razor parser /// to allow empty values for HTML attributes matching this . If /// false empty values for such matching attributes lead to errors. /// - public bool IsStringProperty { get; private set; } + public bool IsStringProperty { get; set; } /// /// The HTML attribute name or, if is true, the prefix for matching attribute @@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers } _typeName = value; - IsStringProperty = string.Equals(_typeName, typeof(string).FullName, StringComparison.Ordinal); + IsStringProperty = string.Equals(TypeName, typeof(string).FullName, StringComparison.Ordinal); } } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperDescriptorFactoryTest.cs index 5389451074..436307b06a 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperDescriptorFactoryTest.cs @@ -27,5 +27,54 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation return Assert.Single(typeResolver.GetExportedTypes(CompilationUtility.GeneratedAssemblyName), generatedType => string.Equals(generatedType.FullName, tagHelperType.FullName, StringComparison.Ordinal)); } + + [Theory] + [MemberData(nameof(TagHelperWithPrefixData))] + public void CreateDescriptors_WithPrefixes_ReturnsExpectedAttributeDescriptors( + Type tagHelperType, + IEnumerable expectedAttributeDescriptors, + string[] expectedErrorMessages) + { + // Arrange + var errorSink = new ErrorSink(); + + // Act + var descriptors = TagHelperDescriptorFactory.CreateDescriptors( + AssemblyName, + GetTypeInfo(tagHelperType), + designTime: false, + errorSink: errorSink); + + // Assert + var errors = errorSink.Errors.ToArray(); + Assert.Equal(expectedErrorMessages.Length, errors.Length); + + for (var i = 0; i < errors.Length; i++) + { + Assert.Equal(0, errors[i].Length); + Assert.Equal(SourceLocation.Zero, errors[i].Location); + Assert.Equal(expectedErrorMessages[i], errors[i].Message, StringComparer.Ordinal); + } + + var descriptor = Assert.Single(descriptors); +#if DNXCORE50 + // In CoreCLR type forwarding of System.Runtime types causes issues with comparing FullNames for generic types. + // We'll work around this by sanitizing the type names so that the assembly qualification is removed. + foreach (var attributeDescriptor in expectedAttributeDescriptors) + { + attributeDescriptor.TypeName = RuntimeTypeInfo.SanitizeFullName(attributeDescriptor.TypeName); + } + + foreach (var attributeDescriptor in descriptor.Attributes) + { + attributeDescriptor.TypeName = RuntimeTypeInfo.SanitizeFullName(attributeDescriptor.TypeName); + } +#endif + + Assert.Equal( + expectedAttributeDescriptors, + descriptor.Attributes, + TagHelperAttributeDescriptorComparer.Default); + } } } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperTypeResolverTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperTypeResolverTest.cs index 23643654af..de8ae6d5cf 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperTypeResolverTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/PrecompilationTagHelperTypeResolverTest.cs @@ -511,14 +511,11 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation runtimeType.TypeInfo.Assembly.GetName().Name); Assert.Equal(expected.Name, actual.Name); - if (expected.FullName != actualFullName) - { - Console.WriteLine("!!!"); - Console.WriteLine(runtimeType.TypeInfo.FullName); - Console.WriteLine(actualFullName); - } - - Assert.Equal(expected.FullName, actualFullName); +#if DNXCORE50 + Assert.Equal( + RuntimeTypeInfo.SanitizeFullName(expected.FullName), + RuntimeTypeInfo.SanitizeFullName(actualFullName)); +#endif Assert.Equal(expected.IsPublic, actual.IsPublic); Assert.Equal(expected.IsAbstract, actual.IsAbstract); Assert.Equal(expected.IsGenericType, actual.IsGenericType); @@ -535,6 +532,10 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation actual.Properties.OrderBy(p => p.Name), new DelegateAssertion((x, y) => AssertEqual(x, y))); } + + Assert.True(actual.Equals(expected)); + Assert.True(expected.Equals(actual)); + Assert.Equal(expected.GetHashCode(), actual.GetHashCode()); } private static void AssertEqual(IPropertyInfo expected, IPropertyInfo actual) diff --git a/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/project.json b/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/project.json index 394d81aee8..0e06470e22 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/project.json +++ b/test/Microsoft.AspNet.Razor.Runtime.Precompilation.Test/project.json @@ -23,11 +23,11 @@ }, "frameworks": { "dnx451": { + "frameworkAssemblies": { + "System.Text.Encoding": "" + }, "dependencies": { "Moq": "4.2.1312.1622" - }, - "frameworkAssemblies": { - "System.Text.Encoding": "4.0.0.0" } }, "dnxcore50": { diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTagHelperDescriptorFactoryTest.cs index 3515afe2bd..4335f09df4 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTagHelperDescriptorFactoryTest.cs @@ -38,6 +38,41 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default); } + [Theory] + [MemberData(nameof(TagHelperWithPrefixData))] + public void CreateDescriptors_WithPrefixes_ReturnsExpectedAttributeDescriptors( + Type tagHelperType, + IEnumerable expectedAttributeDescriptors, + string[] expectedErrorMessages) + { + // Arrange + var errorSink = new ErrorSink(); + + // Act + var descriptors = TagHelperDescriptorFactory.CreateDescriptors( + AssemblyName, + GetTypeInfo(tagHelperType), + designTime: false, + errorSink: errorSink); + + // Assert + var errors = errorSink.Errors.ToArray(); + Assert.Equal(expectedErrorMessages.Length, errors.Length); + + for (var i = 0; i < errors.Length; i++) + { + Assert.Equal(0, errors[i].Length); + Assert.Equal(SourceLocation.Zero, errors[i].Location); + Assert.Equal(expectedErrorMessages[i], errors[i].Message, StringComparer.Ordinal); + } + + var descriptor = Assert.Single(descriptors); + Assert.Equal( + expectedAttributeDescriptors, + descriptor.Attributes, + TagHelperAttributeDescriptorComparer.Default); + } + // TagHelperDesignTimeDescriptors are not created in CoreCLR. #if !DNXCORE50 public static TheoryData OutputElementHintData diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs index e63d4816b6..c84d6e611b 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/RuntimeTypeInfoTest.cs @@ -13,44 +13,19 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { public class RuntimeTypeInfoTest { - private static readonly string StringFullName = typeof(string).FullName; - private static readonly string CollectionsNamespace = typeof(IDictionary<,>).Namespace; - - public static TheoryData RuntimeTypeInfo_ReturnsMetadataOfAdaptingTypeData => - new TheoryData - { - { typeof(int), typeof(int).FullName }, - { typeof(string), typeof(string).FullName }, - { typeof(Tuple<>), typeof(Tuple<>).FullName }, - { typeof(Tuple<,>), typeof(Tuple<,>).FullName }, - { - typeof(IDictionary), - $"{typeof(IDictionary<,>).FullName}[[{StringFullName}],[{StringFullName}]]" - }, - { - typeof(IDictionary>), - $"{typeof(IDictionary<,>).FullName}[[{StringFullName}],[{typeof(IDictionary<,>).FullName}" + - $"[[{StringFullName}],[{typeof(CustomType).FullName}]]]]" - }, - { - typeof(IList, Tuple>>>), - $"{typeof(IList<>).FullName}[[{typeof(IReadOnlyList<>).FullName}[[{typeof(IDictionary<,>).FullName}[[" + - $"{typeof(List<>).FullName}[[{StringFullName}]]],[{typeof(Tuple<,>).FullName}[[{typeof(CustomType).FullName}]," + - $"[{typeof(object).FullName}[]]]]]]]]]" - }, - { typeof(AbstractType), typeof(AbstractType).FullName }, - { typeof(PrivateType), typeof(PrivateType).FullName }, - { typeof(KnownKeyDictionary<>), typeof(KnownKeyDictionary<>).FullName }, - { - typeof(KnownKeyDictionary), - $"{typeof(KnownKeyDictionary<>).Namespace}" + - $".RuntimeTypeInfoTest+KnownKeyDictionary`1[[{StringFullName}]]" - } - }; - [Theory] - [MemberData(nameof(RuntimeTypeInfo_ReturnsMetadataOfAdaptingTypeData))] - public void RuntimeTypeInfo_ReturnsMetadataOfAdaptingType(Type type, string expectedFullName) + [InlineData(typeof(int))] + [InlineData(typeof(string))] + [InlineData(typeof(Tuple<>))] + [InlineData(typeof(Tuple<>))] + [InlineData(typeof(IDictionary))] + [InlineData(typeof(IDictionary>))] + [InlineData(typeof(IList, Tuple>>>))] + [InlineData(typeof(AbstractType))] + [InlineData(typeof(PrivateType))] + [InlineData(typeof(KnownKeyDictionary<>))] + [InlineData(typeof(KnownKeyDictionary))] + public void RuntimeTypeInfo_ReturnsMetadataOfAdaptingType(Type type) { // Arrange var typeInfo = type.GetTypeInfo(); @@ -59,7 +34,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Act and Assert Assert.Same(typeInfo, runtimeTypeInfo.TypeInfo); Assert.Equal(typeInfo.Name, runtimeTypeInfo.Name); - Assert.Equal(expectedFullName, runtimeTypeInfo.FullName); + Assert.Equal(type.FullName, runtimeTypeInfo.FullName); Assert.Equal(typeInfo.IsAbstract, runtimeTypeInfo.IsAbstract); Assert.Equal(typeInfo.IsGenericType, runtimeTypeInfo.IsGenericType); Assert.Equal(typeInfo.IsPublic, runtimeTypeInfo.IsPublic); @@ -282,7 +257,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { typeof(IDictionary<,>), typeof(IDictionary<,>).FullName }, { typeof(IDictionary), - RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary).FullName) + typeof(IDictionary).FullName }, }; @@ -580,6 +555,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } } + public bool Equals(ITypeInfo other) + { + throw new NotImplementedException(); + } + public IEnumerable GetCustomAttributes() where TAttribute : Attribute { throw new NotImplementedException(); diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs index bde6e31eef..4fe8252beb 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs @@ -963,7 +963,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { Name = "valid-attribute", PropertyName = nameof(MultiTagTagHelper.ValidAttribute), - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } }), CreateTagHelperDescriptor( @@ -976,7 +977,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { Name = "valid-attribute", PropertyName = nameof(MultiTagTagHelper.ValidAttribute), - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } }) }; @@ -1309,7 +1311,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { Name = "dictionary-property", PropertyName = nameof(DefaultValidHtmlAttributePrefix.DictionaryProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary).FullName) + TypeName = typeof(IDictionary).FullName }, new TagHelperAttributeDescriptor { @@ -1329,7 +1331,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { Name = "valid-name", PropertyName = nameof(SingleValidHtmlAttributePrefix.DictionaryProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary).FullName) + TypeName = typeof(IDictionary).FullName }, new TagHelperAttributeDescriptor { @@ -1349,37 +1351,38 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { Name = "valid-name1", PropertyName = nameof(MultipleValidHtmlAttributePrefix.DictionaryProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(Dictionary).FullName) + TypeName = typeof(Dictionary).FullName }, new TagHelperAttributeDescriptor { Name = "valid-name2", PropertyName = nameof(MultipleValidHtmlAttributePrefix.DictionarySubclassProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(DictionarySubclass).FullName) + TypeName = typeof(DictionarySubclass).FullName }, new TagHelperAttributeDescriptor { Name = "valid-name3", PropertyName = nameof(MultipleValidHtmlAttributePrefix.DictionaryWithoutParameterlessConstructorProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(DictionaryWithoutParameterlessConstructor).FullName) + TypeName = typeof(DictionaryWithoutParameterlessConstructor).FullName }, new TagHelperAttributeDescriptor { Name = "valid-name4", PropertyName = nameof(MultipleValidHtmlAttributePrefix.GenericDictionarySubclassProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(GenericDictionarySubclass).FullName) + TypeName = typeof(GenericDictionarySubclass).FullName }, new TagHelperAttributeDescriptor { Name = "valid-name5", PropertyName = nameof(MultipleValidHtmlAttributePrefix.SortedDictionaryProperty), - TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(SortedDictionary).FullName) + TypeName = typeof(SortedDictionary).FullName }, new TagHelperAttributeDescriptor { Name = "valid-name6", PropertyName = nameof(MultipleValidHtmlAttributePrefix.StringProperty), - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true, }, new TagHelperAttributeDescriptor { @@ -1482,41 +1485,6 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } } - [Theory] - [MemberData(nameof(TagHelperWithPrefixData))] - public void CreateDescriptors_WithPrefixes_ReturnsExpectedAttributeDescriptors( - Type tagHelperType, - IEnumerable expectedAttributeDescriptors, - string[] expectedErrorMessages) - { - // Arrange - var errorSink = new ErrorSink(); - - // Act - var descriptors = TagHelperDescriptorFactory.CreateDescriptors( - AssemblyName, - GetTypeInfo(tagHelperType), - designTime: false, - errorSink: errorSink); - - // Assert - var errors = errorSink.Errors.ToArray(); - Assert.Equal(expectedErrorMessages.Length, errors.Length); - - for (var i = 0; i < errors.Length; i++) - { - Assert.Equal(0, errors[i].Length); - Assert.Equal(SourceLocation.Zero, errors[i].Location); - Assert.Equal(expectedErrorMessages[i], errors[i].Message, StringComparer.Ordinal); - } - - var descriptor = Assert.Single(descriptors); - Assert.Equal( - expectedAttributeDescriptors, - descriptor.Attributes, - TagHelperAttributeDescriptorComparer.Default); - } - public static TheoryData ValidAttributeNameData { get @@ -1995,7 +1963,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { Name = name, PropertyName = propertyInfo.Name, - TypeName = propertyInfo.PropertyType.FullName + TypeName = propertyInfo.PropertyType.FullName, + IsStringProperty = propertyInfo.PropertyType.FullName == typeof(string).FullName }; } } diff --git a/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs b/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs index 42739d0399..fc4d1062dc 100644 --- a/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs @@ -37,7 +37,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator { Name = "catchall-bound-string", PropertyName = "BoundRequiredString", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } }, RequiredAttributes = new[] { "catchall-unbound-required" }, @@ -53,13 +54,15 @@ namespace Microsoft.AspNet.Razor.Test.Generator { Name = "input-bound-required-string", PropertyName = "BoundRequiredString", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true }, new TagHelperAttributeDescriptor { Name = "input-bound-string", PropertyName = "BoundString", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } }, RequiredAttributes = new[] { "input-bound-required-string", "input-unbound-required" }, @@ -85,7 +88,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator { Name = "bound", PropertyName = "Bound", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } } } @@ -237,7 +241,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator { Name = "string-prefix-grabber", PropertyName = "StringProperty", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true }, new TagHelperAttributeDescriptor { @@ -251,7 +256,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator Name = "string-prefix-", PropertyName = "StringDictionaryProperty", TypeName = typeof(string).FullName, - IsIndexer = true + IsIndexer = true, + IsStringProperty = true } } }, @@ -286,7 +292,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator Name = "string-prefix-", PropertyName = "StringDictionaryProperty", TypeName = typeof(string).FullName, - IsIndexer = true + IsIndexer = true, + IsStringProperty = true } } } @@ -1517,10 +1524,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator [Theory] [MemberData(nameof(DesignTimeTagHelperTestData))] - public void TagHelpers_GenerateExpectedDesignTimeOutput(string testName, - string baseLineName, - IEnumerable tagHelperDescriptors, - IList expectedDesignTimePragmas) + public void TagHelpers_GenerateExpectedDesignTimeOutput( + string testName, + string baseLineName, + IEnumerable tagHelperDescriptors, + IList expectedDesignTimePragmas) { // Act & Assert RunTagHelperTest(testName, diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperAttributeDescriptorTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperAttributeDescriptorTest.cs deleted file mode 100644 index 5957dcfc15..0000000000 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperAttributeDescriptorTest.cs +++ /dev/null @@ -1,51 +0,0 @@ -// 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 Microsoft.AspNet.Razor.TagHelpers; -using Xunit; - -namespace Microsoft.AspNet.Razor.Test.TagHelpers -{ - public class TagHelperAttributeDescriptorTest - { - public static TheoryData IsStringPropertyData - { - get - { - // attributeType, isIndexer, expectedIsStringProperty - return new TheoryData - { - { typeof(int), false, false }, - { typeof(string), false, true }, - { typeof(string), true, true }, - { typeof(object), false, false }, - { typeof(IEnumerable), false, false }, - { typeof(IDictionary), false, false }, - { typeof(IDictionary), true, false }, - }; - } - } - - [Theory] - [MemberData(nameof(IsStringPropertyData))] - public void TagHelperAttributeDescriptor_IsStringPropertySetCorrectly( - Type attributeType, - bool isIndexer, - bool expectedIsStringProperty) - { - // Arrange - var attributeDescriptor = new TagHelperAttributeDescriptor - { - Name = "someAttribute", - PropertyName = "someProperty", - TypeName = attributeType.FullName, - IsIndexer = isIndexer - }; - - // Assert - Assert.Equal(expectedIsStringProperty, attributeDescriptor.IsStringProperty); - } - } -} diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs index 4656299e28..6ce6c69bfa 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs @@ -1078,7 +1078,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "name", PropertyName = "Name", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } } } @@ -2121,7 +2122,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "name", PropertyName = "Name", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } } } @@ -3761,7 +3763,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "bound-required-string", PropertyName = "BoundRequiredString", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } }, RequiredAttributes = new[] { "unbound-required" } @@ -3777,7 +3780,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "bound-required-string", PropertyName = "BoundRequiredString", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true } }, RequiredAttributes = new[] { "bound-required-string" } @@ -3829,7 +3833,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers Name = "string-prefix-", PropertyName = "DictionaryOfStringProperty", TypeName = typeof(string).FullName, - IsIndexer = true + IsIndexer = true, + IsStringProperty = true } } }, @@ -3844,7 +3849,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "bound-string", PropertyName = "BoundRequiredString", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true }, new TagHelperAttributeDescriptor { diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorTest.cs index 00498e8010..cdce725540 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorTest.cs @@ -77,7 +77,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "attribute two", PropertyName = "property name", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true }, }, TagStructure = TagStructure.NormalOrSelfClosing @@ -138,7 +139,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers Name = "attribute two", PropertyName = "property name", TypeName = typeof(string).FullName, - IsIndexer = true + IsIndexer = true, + IsStringProperty = true }, }, AllowedChildren = new[] { "allowed child one", "allowed child two" } @@ -273,7 +275,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers { Name = "attribute two", PropertyName = "property name", - TypeName = typeof(string).FullName + TypeName = typeof(string).FullName, + IsStringProperty = true }, }, AllowedChildren = new[] { "allowed child one", "allowed child two" } @@ -340,7 +343,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers Name = "attribute two", PropertyName = "property name", TypeName = typeof(string).FullName, - IsIndexer = true + IsIndexer = true, + IsStringProperty = true } }, TagStructure = TagStructure.NormalOrSelfClosing