Add tests to validate TagHelperDescriptorResolver.

- Tested the descriptor resolver and its underlying dependencies (TagHelperTypeResolver and TagHelperDescriptorFactory).

#99
#158
This commit is contained in:
NTaylorMullen 2014-09-28 20:53:14 -07:00 committed by N. Taylor Mullen
parent f9c70a0644
commit b37b11d4b6
8 changed files with 522 additions and 2 deletions

View File

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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
{
public class Valid_PlainTagHelper : ITagHelper
{
}
public class Valid_InheritedTagHelper : Valid_PlainTagHelper
{
}
public class SingleAttributeTagHelper : ITagHelper
{
public int IntAttribute { get; set; }
}
public class MissingAccessorTagHelper : ITagHelper
{
public string ValidAttribute { get; set; }
public string InvalidNoGetAttribute { set { } }
public string InvalidNoSetAttribute { get { return string.Empty; } }
}
public class PrivateAccessorTagHelper : ITagHelper
{
public string ValidAttribute { get; set; }
public string InvalidPrivateSetAttribute { get; private set; }
public string InvalidPrivateGetAttribute { private get; set; }
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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;
using System.Linq;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class CompleteTagHelperDescriptorComparer : TagHelperDescriptorComparer
{
public new static readonly CompleteTagHelperDescriptorComparer Default =
new CompleteTagHelperDescriptorComparer();
private CompleteTagHelperDescriptorComparer()
{
}
public new bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
return base.Equals(descriptorX, descriptorY) &&
descriptorX.Attributes.SequenceEqual(descriptorY.Attributes,
CompleteTagHelperAttributeDescriptorComparer.Default);
}
public new int GetHashCode(TagHelperDescriptor descriptor)
{
return HashCodeCombiner.Start()
.Add(base.GetHashCode())
.Add(descriptor.Attributes)
.CombinedHash;
}
private class CompleteTagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
{
public static readonly CompleteTagHelperAttributeDescriptorComparer Default =
new CompleteTagHelperAttributeDescriptorComparer();
private CompleteTagHelperAttributeDescriptorComparer()
{
}
public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
{
return descriptorX.AttributeName == descriptorY.AttributeName &&
descriptorX.AttributePropertyName == descriptorY.AttributePropertyName;
}
public int GetHashCode(TagHelperAttributeDescriptor descriptor)
{
return HashCodeCombiner.Start()
.Add(descriptor.AttributeName)
.Add(descriptor.AttributePropertyName)
.CombinedHash;
}
}
}
}

View File

@ -0,0 +1,87 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.TagHelpers;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class TagHelperDescriptorFactoryTest
{
[Fact]
public void DescriptorFactory_BuildsDescriptorsFromSimpleTypes()
{
// Arrange
var expectedDescriptor = new TagHelperDescriptor("Object", "System.Object", ContentBehavior.None);
// Act
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(object));
// Assert
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
public void DescriptorFactory_BuildsDescriptorsWithConventionNames()
{
// Arrange
var intProperty = typeof(SingleAttributeTagHelper).GetProperty(nameof(SingleAttributeTagHelper.IntAttribute));
var expectedDescriptor = new TagHelperDescriptor(
"SingleAttribute",
typeof(SingleAttributeTagHelper).FullName,
ContentBehavior.None,
new[] {
new TagHelperAttributeDescriptor(nameof(SingleAttributeTagHelper.IntAttribute), intProperty)
});
// Act
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(SingleAttributeTagHelper));
// Assert
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
public void DescriptorFactory_OnlyAcceptsPropertiesWithGetAndSet()
{
// Arrange
var validProperty = typeof(MissingAccessorTagHelper).GetProperty(
nameof(MissingAccessorTagHelper.ValidAttribute));
var expectedDescriptor = new TagHelperDescriptor(
"MissingAccessor",
typeof(MissingAccessorTagHelper).FullName,
ContentBehavior.None,
new[] {
new TagHelperAttributeDescriptor(nameof(MissingAccessorTagHelper.ValidAttribute), validProperty)
});
// Act
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(MissingAccessorTagHelper));
// Assert
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
public void DescriptorFactory_OnlyAcceptsPropertiesWithPublicGetAndSet()
{
// Arrange
var validProperty = typeof(PrivateAccessorTagHelper).GetProperty(
nameof(PrivateAccessorTagHelper.ValidAttribute));
var expectedDescriptor = new TagHelperDescriptor(
"PrivateAccessor",
typeof(PrivateAccessorTagHelper).FullName,
ContentBehavior.None,
new[] {
new TagHelperAttributeDescriptor(
nameof(PrivateAccessorTagHelper.ValidAttribute), validProperty)
});
// Act
var descriptor = TagHelperDescriptorFactory.CreateDescriptor(typeof(PrivateAccessorTagHelper));
// Assert
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
}
}
}

View File

@ -0,0 +1,190 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.TagHelpers;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class TagHelperDescriptorResolverTest : TagHelperTypeResolverTest
{
[Fact]
public void DescriptorResolver_DoesNotReturnInvalidTagHelpersWhenSpecified()
{
// Arrange
var tagHelperDescriptorResolver =
new TagHelperDescriptorResolver(
new TestTagHelperTypeResolver(TestableTagHelpers));
// Act
var descriptors = tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_AbstractTagHelper, MyAssembly");
descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_GenericTagHelper`, MyAssembly"));
descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_NestedPublicTagHelper, MyAssembly"));
descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_NestedInternalTagHelper, MyAssembly"));
descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_PrivateTagHelper, MyAssembly"));
descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_ProtectedTagHelper, MyAssembly"));
descriptors = descriptors.Concat(tagHelperDescriptorResolver.Resolve(
"Microsoft.AspNet.Razor.Runtime.Test.TagHelpers.Invalid_InternalTagHelper, MyAssembly"));
// Assert
Assert.Empty(descriptors);
}
[Theory]
[InlineData("Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper,MyAssembly")]
[InlineData(" Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper,MyAssembly")]
[InlineData("Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper ,MyAssembly")]
[InlineData(" Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper ,MyAssembly")]
[InlineData("Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper, MyAssembly")]
[InlineData("Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper,MyAssembly ")]
[InlineData("Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper, MyAssembly ")]
[InlineData(" Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper, MyAssembly ")]
[InlineData(" Microsoft.AspNet.Razor.Runtime.TagHelpers.Valid_PlainTagHelper , MyAssembly ")]
public void DescriptorResolver_IgnoresSpaces(string lookupText)
{
// Arrange
var tagHelperTypeResolver = new TestTagHelperTypeResolver(TestableTagHelpers)
{
OnGetLibraryDefinedTypes = (assemblyName) =>
{
Assert.Equal("MyAssembly", assemblyName.Name);
}
};
var tagHelperDescriptorResolver = new TagHelperDescriptorResolver(tagHelperTypeResolver);
var expectedDescriptor = new TagHelperDescriptor("Valid_Plain",
typeof(Valid_PlainTagHelper).FullName,
ContentBehavior.None);
// Act
var descriptors = tagHelperDescriptorResolver.Resolve(lookupText);
// Assert
var descriptor = Assert.Single(descriptors);
Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
public void DescriptorResolver_ResolvesOnlyTypeResolverProvidedTypes()
{
// Arrange
var resolver = new TagHelperDescriptorResolver(
new LookupBasedTagHelperTypeResolver(
new Dictionary<string, IEnumerable<Type>>(StringComparer.OrdinalIgnoreCase)
{
{ "lookupText1", ValidTestableTagHelpers },
{ "lookupText2", new Type[]{ typeof(Valid_PlainTagHelper) } }
}));
var expectedDescriptor = new TagHelperDescriptor("Valid_Plain",
typeof(Valid_PlainTagHelper).FullName,
ContentBehavior.None);
// Act
var descriptors = resolver.Resolve("lookupText2");
// Assert
var descriptor = Assert.Single(descriptors);
Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
public void DescriptorResolver_ResolvesMultipleTypes()
{
// Arrange
var resolver = new TagHelperDescriptorResolver(
new LookupBasedTagHelperTypeResolver(
new Dictionary<string, IEnumerable<Type>>(StringComparer.OrdinalIgnoreCase)
{
{ "lookupText", new Type[]{ typeof(Valid_PlainTagHelper), typeof(Valid_InheritedTagHelper) } },
}));
var expectedDescriptors = new TagHelperDescriptor[]
{
new TagHelperDescriptor("Valid_Plain",
typeof(Valid_PlainTagHelper).FullName,
ContentBehavior.None),
new TagHelperDescriptor("Valid_Inherited",
typeof(Valid_InheritedTagHelper).FullName,
ContentBehavior.None)
};
// Act
var descriptors = resolver.Resolve("lookupText").ToArray();
// Assert
Assert.Equal(descriptors.Length, 2);
Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
public void DescriptorResolver_DoesNotResolveTypesForNoTypeResolvingLookupText()
{
// Arrange
var resolver = new TagHelperDescriptorResolver(
new LookupBasedTagHelperTypeResolver(
new Dictionary<string, IEnumerable<Type>>(StringComparer.OrdinalIgnoreCase)
{
{ "lookupText1", ValidTestableTagHelpers },
{ "lookupText2", new Type[]{ typeof(Valid_PlainTagHelper) } }
}));
// Act
var descriptors = resolver.Resolve("lookupText").ToArray();
// Assert
Assert.Empty(descriptors);
}
[Theory]
[InlineData("")]
[InlineData(null)]
public void DescriptorResolver_ResolveThrowsIfNullOrEmptyLookupText(string lookupText)
{
// Arrange
var tagHelperDescriptorResolver =
new TagHelperDescriptorResolver(
new TestTagHelperTypeResolver(InvalidTestableTagHelpers));
var expectedMessage =
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText) +
Environment.NewLine +
"Parameter name: lookupText";
// Act & Assert
var ex = Assert.Throws<ArgumentException>(nameof(lookupText),
() =>
{
tagHelperDescriptorResolver.Resolve(lookupText);
});
Assert.Equal(expectedMessage, ex.Message);
}
private class LookupBasedTagHelperTypeResolver : TagHelperTypeResolver
{
private Dictionary<string, IEnumerable<Type>> _lookupValues;
public LookupBasedTagHelperTypeResolver(Dictionary<string, IEnumerable<Type>> lookupValues)
{
_lookupValues = lookupValues;
}
internal override IEnumerable<TypeInfo> GetLibraryDefinedTypes(AssemblyName assemblyName)
{
IEnumerable<Type> types;
_lookupValues.TryGetValue(assemblyName.Name, out types);
return types?.Select(type => type.GetTypeInfo()) ?? Enumerable.Empty<TypeInfo>();
}
}
}
}

View File

@ -0,0 +1,153 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class TagHelperTypeResolverTest
{
protected static readonly Type[] ValidTestableTagHelpers = new[]
{
typeof(Valid_PlainTagHelper),
typeof(Valid_InheritedTagHelper)
};
protected static readonly Type[] InvalidTestableTagHelpers = new[]
{
typeof(Invalid_AbstractTagHelper),
typeof(Invalid_GenericTagHelper<>),
typeof(Invalid_NestedPublicTagHelper),
typeof(Invalid_NestedInternalTagHelper),
typeof(Invalid_PrivateTagHelper),
typeof(Invalid_ProtectedTagHelper),
typeof(Invalid_InternalTagHelper)
};
protected static readonly Type[] TestableTagHelpers =
ValidTestableTagHelpers.Concat(InvalidTestableTagHelpers).ToArray();
[Fact]
public void TypeResolver_ThrowsWhenCannotResolveAssembly()
{
// Arrange
var tagHelperTypeResolver = new TagHelperTypeResolver();
var expectedErrorMessage = string.Format(
CultureInfo.InvariantCulture,
"Cannot resolve TagHelper containing assembly '{0}'. Error: '{1}'.",
"abcd",
"Could not load file or assembly 'abcd' or one of its dependencies. " +
"The system cannot find the file specified.");
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() =>
{
tagHelperTypeResolver.Resolve("abcd");
});
Assert.Equal(expectedErrorMessage, ex.Message, StringComparer.OrdinalIgnoreCase);
}
[Fact]
public void TypeResolver_OnlyReturnsValidTagHelpersForAssemblyLookup()
{
// Arrange
var tagHelperTypeResolver = new TestTagHelperTypeResolver(TestableTagHelpers);
// Act
var types = tagHelperTypeResolver.Resolve("Foo");
// Assert
Assert.Equal(ValidTestableTagHelpers, types);
}
[Fact]
public void TypeResolver_ReturnsEmptyEnumerableIfNoValidTagHelpersFound()
{
// Arrange
var tagHelperTypeResolver = new TestTagHelperTypeResolver(InvalidTestableTagHelpers);
// Act
var types = tagHelperTypeResolver.Resolve("Foo");
// Assert
Assert.Empty(types);
}
[Theory]
[InlineData("")]
[InlineData(null)]
public void TypeResolver_ResolveThrowsIfEmptyOrNullLookupText(string name)
{
// Arrange
var tagHelperTypeResolver = new TestTagHelperTypeResolver(InvalidTestableTagHelpers);
var expectedMessage = "Tag helper directive assembly name cannot be null or empty." +
Environment.NewLine +
"Parameter name: name";
// Act & Assert
var ex = Assert.Throws<ArgumentException>(nameof(name),
() =>
{
tagHelperTypeResolver.Resolve(name);
});
Assert.Equal(expectedMessage, ex.Message);
}
protected class TestTagHelperTypeResolver : TagHelperTypeResolver
{
private IEnumerable<TypeInfo> _assemblyTypeInfos;
public TestTagHelperTypeResolver(IEnumerable<Type> assemblyTypes)
{
_assemblyTypeInfos = assemblyTypes.Select(type => type.GetTypeInfo());
OnGetLibraryDefinedTypes = (_) => { };
}
public Action<AssemblyName> OnGetLibraryDefinedTypes { get; set; }
internal override IEnumerable<TypeInfo> GetLibraryDefinedTypes(AssemblyName assemblyName)
{
OnGetLibraryDefinedTypes(assemblyName);
return _assemblyTypeInfos;
}
}
public class Invalid_NestedPublicTagHelper : ITagHelper
{
}
internal class Invalid_NestedInternalTagHelper : ITagHelper
{
}
private class Invalid_PrivateTagHelper : ITagHelper
{
}
protected class Invalid_ProtectedTagHelper : ITagHelper
{
}
}
// These tag helper types must be unnested and public to potentially be valid tag helpers.
// In this case they do not fulfill other TagHelper requirements.
public abstract class Invalid_AbstractTagHelper : ITagHelper
{
}
public class Invalid_GenericTagHelper<T> : ITagHelper
{
}
internal class Invalid_InternalTagHelper : ITagHelper
{
}
}

View File

@ -1,5 +1,6 @@
{
"dependencies": {
"Microsoft.AspNet.Razor.Runtime": "",
"Microsoft.AspNet.Razor.Test": ""
},
"commands": {

View File

@ -8,7 +8,6 @@ using System.Linq;
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.TagHelpers;
using Xunit;
namespace Microsoft.AspNet.Razor.Test.Generator

View File

@ -5,7 +5,6 @@ using System;
using System.IO;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Test.Framework;
using Xunit;