ITypeInfo.FullName should return the same value as TypeInfo.FullName

Fixes #523
This commit is contained in:
Pranav K 2015-09-18 17:35:08 -07:00
parent 388362245f
commit 9c456965a6
20 changed files with 292 additions and 220 deletions

View File

@ -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<IPropertyInfo> _properties;
/// <summary>
@ -128,6 +129,19 @@ namespace Microsoft.AspNet.Razor.Runtime.Precompilation
}
}
private string SanitizedFullName
{
get
{
if (_sanitizedFullName == null)
{
_sanitizedFullName = RuntimeTypeInfo.SanitizeFullName(FullName);
}
return _sanitizedFullName;
}
}
/// <inheritdoc />
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>()
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);
}
/// <inheritdoc />
public override int GetHashCode() => FullName.GetHashCode();
public override int GetHashCode() => SanitizedFullName.GetHashCode();
private static List<IPropertyInfo> 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("],");
}

View File

@ -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
/// <summary>
/// Contains type metadata.
/// </summary>
public interface ITypeInfo : IMemberInfo
public interface ITypeInfo : IMemberInfo, IEquatable<ITypeInfo>
{
/// <summary>
/// Fully qualified name of the type.
/// </summary>
/// <remarks>
/// 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 <see cref="FullName"/> includes the assembly
/// qualified name of generic parameters, FullNames would not match.
/// Use <see cref="IEquatable{ITypeInfo}.Equals(ITypeInfo)"/> to compare <see cref="ITypeInfo"/>s instead.
/// </remarks>
string FullName { get; }
/// <summary>

View File

@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private static readonly TypeInfo TagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
private IEnumerable<IPropertyInfo> _properties;
private string _sanitizedFullName;
/// <summary>
/// Initializes a new instance of <see cref="RuntimeTypeInfo"/>
@ -46,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
public string Name => TypeInfo.Name;
/// <inheritdoc />
public string FullName => SanitizeFullName(TypeInfo.FullName);
public string FullName => TypeInfo.FullName;
/// <inheritdoc />
public bool IsAbstract => TypeInfo.IsAbstract;
@ -78,6 +79,19 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <inheritdoc />
public bool IsTagHelper => TagHelperTypeInfo.IsAssignableFrom(TypeInfo);
private string SanitizedFullName
{
get
{
if (_sanitizedFullName == null)
{
_sanitizedFullName = SanitizeFullName(FullName);
}
return _sanitizedFullName;
}
}
/// <inheritdoc />
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>() where TAttribute : Attribute =>
TypeInfo.GetCustomAttributes<TAttribute>(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);
}
/// <inheritdoc />
public override int GetHashCode() => FullName.GetHashCode();
public override int GetHashCode() => SanitizedFullName.GetHashCode();
// Internal for unit testing
internal static string SanitizeFullName(string fullName)
/// <summary>
/// Removes assembly qualification from generic type parameters for the specified <paramref name="fullName"/>.
/// </summary>
/// <param name="fullName">Full name.</param>
/// <returns>Full name without fully qualified generic parameters.</returns>
/// <example>
/// <c>typeof(<see cref="List{string}"/>).FullName</c> is
/// List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]
/// <c>Sanitize(typeof(<see cref="List{string}"/>.FullName</c> returns
/// List`1[[System.String]
/// </example>
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;

View File

@ -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
};

View File

@ -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)

View File

@ -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);
}

View File

@ -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)

View File

@ -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)

View File

@ -1,7 +1,9 @@
{
"version": "4.0.0-*",
"shared": "*.cs",
"dependencies": { },
"dependencies": {
"xunit.assert": "2.1.0-*"
},
"frameworks": {
"net45": { },
"dotnet": {

View File

@ -46,15 +46,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers
public bool IsIndexer { get; set; }
/// <summary>
/// Gets an indication whether this property is of type <see cref="string"/> or, if <see cref="IsIndexer"/> is
/// <c>true</c>, whether the indexer's value is of type <see cref="string"/>.
/// Gets or sets an indication whether this property is of type <see cref="string"/> or, if
/// <see cref="IsIndexer"/> is <c>true</c>, whether the indexer's value is of type <see cref="string"/>.
/// </summary>
/// <value>
/// If <c>true</c> the <see cref="TypeName"/> is for <see cref="string"/>. This causes the Razor parser
/// to allow empty values for HTML attributes matching this <see cref="TagHelperAttributeDescriptor"/>. If
/// <c>false</c> empty values for such matching attributes lead to errors.
/// </value>
public bool IsStringProperty { get; private set; }
public bool IsStringProperty { get; set; }
/// <summary>
/// The HTML attribute name or, if <see cref="IsIndexer"/> is <c>true</c>, 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);
}
}

View File

@ -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<TagHelperAttributeDescriptor> 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);
}
}
}

View File

@ -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<IPropertyInfo>((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)

View File

@ -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": {

View File

@ -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<TagHelperAttributeDescriptor> 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

View File

@ -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<Type, string>
{
{ typeof(int), typeof(int).FullName },
{ typeof(string), typeof(string).FullName },
{ typeof(Tuple<>), typeof(Tuple<>).FullName },
{ typeof(Tuple<,>), typeof(Tuple<,>).FullName },
{
typeof(IDictionary<string, string>),
$"{typeof(IDictionary<,>).FullName}[[{StringFullName}],[{StringFullName}]]"
},
{
typeof(IDictionary<string, IDictionary<string, CustomType>>),
$"{typeof(IDictionary<,>).FullName}[[{StringFullName}],[{typeof(IDictionary<,>).FullName}" +
$"[[{StringFullName}],[{typeof(CustomType).FullName}]]]]"
},
{
typeof(IList<IReadOnlyList<IDictionary<List<string>, Tuple<CustomType, object[]>>>>),
$"{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<string>),
$"{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<string, string>))]
[InlineData(typeof(IDictionary<string, IDictionary<string, CustomType>>))]
[InlineData(typeof(IList<IReadOnlyList<IDictionary<List<string>, Tuple<CustomType, object[]>>>>))]
[InlineData(typeof(AbstractType))]
[InlineData(typeof(PrivateType))]
[InlineData(typeof(KnownKeyDictionary<>))]
[InlineData(typeof(KnownKeyDictionary<string>))]
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<string,string>),
RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary<string, string>).FullName)
typeof(IDictionary<string, string>).FullName
},
};
@ -580,6 +555,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}
}
public bool Equals(ITypeInfo other)
{
throw new NotImplementedException();
}
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>() where TAttribute : Attribute
{
throw new NotImplementedException();

View File

@ -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<string, string>).FullName)
TypeName = typeof(IDictionary<string, string>).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<string, string>).FullName)
TypeName = typeof(IDictionary<string, string>).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<string, object>).FullName)
TypeName = typeof(Dictionary<string, object>).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<object>).FullName)
TypeName = typeof(GenericDictionarySubclass<object>).FullName
},
new TagHelperAttributeDescriptor
{
Name = "valid-name5",
PropertyName = nameof(MultipleValidHtmlAttributePrefix.SortedDictionaryProperty),
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(SortedDictionary<string, int>).FullName)
TypeName = typeof(SortedDictionary<string, int>).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<TagHelperAttributeDescriptor> 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<string> 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
};
}
}

View File

@ -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<TagHelperDescriptor> tagHelperDescriptors,
IList<LineMapping> expectedDesignTimePragmas)
public void TagHelpers_GenerateExpectedDesignTimeOutput(
string testName,
string baseLineName,
IEnumerable<TagHelperDescriptor> tagHelperDescriptors,
IList<LineMapping> expectedDesignTimePragmas)
{
// Act & Assert
RunTagHelperTest(testName,

View File

@ -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<Type, bool, bool>
{
{ typeof(int), false, false },
{ typeof(string), false, true },
{ typeof(string), true, true },
{ typeof(object), false, false },
{ typeof(IEnumerable<string>), false, false },
{ typeof(IDictionary<string, string>), false, false },
{ typeof(IDictionary<string, string>), 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);
}
}
}

View File

@ -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
{

View File

@ -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