Add some tests for CodeAnalysisExtensions

This commit is contained in:
Pranav K 2018-06-06 12:08:08 -07:00
parent b7064c576d
commit f2eb6f8d37
17 changed files with 464 additions and 5 deletions

View File

@ -11,6 +11,14 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public static bool HasAttribute(this ITypeSymbol typeSymbol, ITypeSymbol attribute, bool inherit)
{
Debug.Assert(typeSymbol != null);
Debug.Assert(attribute != null);
if (!inherit)
{
return HasAttribute(typeSymbol, attribute);
}
foreach (var type in typeSymbol.GetTypeHierarchy())
{
if (type.HasAttribute(attribute))
@ -22,17 +30,47 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
return false;
}
public static bool HasAttribute(this ISymbol symbol, ITypeSymbol attribute)
public static bool HasAttribute(this IMethodSymbol methodSymbol, ITypeSymbol attribute, bool inherit)
{
Debug.Assert(symbol != null);
Debug.Assert(methodSymbol != null);
Debug.Assert(attribute != null);
foreach (var declaredAttribute in symbol.GetAttributes())
if (!inherit)
{
if (declaredAttribute.AttributeClass == attribute)
return HasAttribute(methodSymbol, attribute);
}
while (methodSymbol != null)
{
if (methodSymbol.HasAttribute(attribute))
{
return true;
}
methodSymbol = methodSymbol.IsOverride ? methodSymbol.OverriddenMethod : null;
}
return false;
}
public static bool HasAttribute(this IPropertySymbol propertySymbol, ITypeSymbol attribute, bool inherit)
{
Debug.Assert(propertySymbol != null);
Debug.Assert(attribute != null);
if (!inherit)
{
return HasAttribute(propertySymbol, attribute);
}
while (propertySymbol != null)
{
if (propertySymbol.HasAttribute(attribute))
{
return true;
}
propertySymbol = propertySymbol.IsOverride ? propertySymbol.OverriddenProperty : null;
}
return false;
@ -67,6 +105,19 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
return false;
}
private static bool HasAttribute(this ISymbol symbol, ITypeSymbol attribute)
{
foreach (var declaredAttribute in symbol.GetAttributes())
{
if (attribute.IsAssignableFrom(declaredAttribute.AttributeClass))
{
return true;
}
}
return false;
}
private static IEnumerable<ITypeSymbol> GetTypeHierarchy(this ITypeSymbol typeSymbol)
{
while (typeSymbol != null)

View File

@ -0,0 +1,6 @@
// 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Analyzers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,243 @@
// 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.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class CodeAnalysisExtensionsTest
{
[Fact]
public async Task HasAttribute_ReturnsFalseIfSymbolDoesNotHaveAttribute()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttributeTest");
var testMethod = (IMethodSymbol)testClass.GetMembers("SomeMethod").First();
var testProperty = (IPropertySymbol)testClass.GetMembers("SomeProperty").First();
// Act
var classHasAttribute = CodeAnalysisExtensions.HasAttribute(testClass, attribute, inherit: false);
var methodHasAttribute = CodeAnalysisExtensions.HasAttribute(testMethod, attribute, inherit: false);
var propertyHasAttribute = CodeAnalysisExtensions.HasAttribute(testProperty, attribute, inherit: false);
// AssertControllerAttribute
Assert.False(classHasAttribute);
Assert.False(methodHasAttribute);
Assert.False(propertyHasAttribute);
}
[Fact]
public async Task HasAttribute_ReturnsTrueIfTypeHasAttribute()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Mvc.ControllerAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.{nameof(HasAttribute_ReturnsTrueIfTypeHasAttribute)}");
// Act
var hasAttribute = CodeAnalysisExtensions.HasAttribute(testClass, attribute, inherit: false);
// Assert
Assert.True(hasAttribute);
}
[Fact]
public async Task HasAttribute_ReturnsTrueIfBaseTypeHasAttribute()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Mvc.ControllerAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.{nameof(HasAttribute_ReturnsTrueIfBaseTypeHasAttribute)}");
// Act
var hasAttributeWithoutInherit = CodeAnalysisExtensions.HasAttribute(testClass, attribute, inherit: false);
var hasAttributeWithInherit = CodeAnalysisExtensions.HasAttribute(testClass, attribute, inherit: true);
// Assert
Assert.False(hasAttributeWithoutInherit);
Assert.True(hasAttributeWithInherit);
}
[Fact]
public async Task HasAttribute_ReturnsTrueForInterfaceContractOnAttribute()
{
// Arrange
var compilation = await GetCompilation();
var @interface = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IHasAttribute_ReturnsTrueForInterfaceContractOnAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForInterfaceContractOnAttributeTest");
var derivedClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForInterfaceContractOnAttributeDerived");
// Act
var hasAttribute = CodeAnalysisExtensions.HasAttribute(testClass, @interface, inherit: true);
var hasAttributeOnDerived = CodeAnalysisExtensions.HasAttribute(testClass, @interface, inherit: true);
// Assert
Assert.True(hasAttribute);
Assert.True(hasAttributeOnDerived);
}
[Fact]
public async Task HasAttribute_ReturnsTrueForAttributesOnMethods()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnMethodsAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnMethodsTest");
var method = (IMethodSymbol)testClass.GetMembers("SomeMethod").First();
// Act
var hasAttribute = CodeAnalysisExtensions.HasAttribute(method, attribute, inherit: false);
// Assert
Assert.True(hasAttribute);
}
[Fact]
public async Task HasAttribute_ReturnsTrueForAttributesOnOverriddenMethods()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsTest");
var method = (IMethodSymbol)testClass.GetMembers("SomeMethod").First();
// Act
var hasAttributeWithoutInherit = CodeAnalysisExtensions.HasAttribute(method, attribute, inherit: false);
var hasAttributeWithInherit = CodeAnalysisExtensions.HasAttribute(method, attribute, inherit: true);
// Assert
Assert.False(hasAttributeWithoutInherit);
Assert.True(hasAttributeWithInherit);
}
[Fact]
public async Task HasAttribute_ReturnsTrueForAttributesOnProperties()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnPropertiesAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnProperties");
var property = (IPropertySymbol)testClass.GetMembers("SomeProperty").First();
// Act
var hasAttribute = CodeAnalysisExtensions.HasAttribute(property, attribute, inherit: false);
// Assert
Assert.True(hasAttribute);
}
[Fact]
public async Task HasAttribute_ReturnsTrueForAttributesOnOverridenProperties()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesAttribute");
var testClass = compilation.GetTypeByMetadataName($"{GetType().Namespace}.HasAttribute_ReturnsTrueForAttributesOnOverriddenProperties");
var property = (IPropertySymbol)testClass.GetMembers("SomeProperty").First();
// Act
var hasAttributeWithoutInherit = CodeAnalysisExtensions.HasAttribute(property, attribute, inherit: false);
var hasAttributeWithInherit = CodeAnalysisExtensions.HasAttribute(property, attribute, inherit: true);
// Assert
Assert.False(hasAttributeWithoutInherit);
Assert.True(hasAttributeWithInherit);
}
[Fact]
public async Task IsAssignable_ReturnsFalseForDifferentTypes()
{
// Arrange
var compilation = await GetCompilation();
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsFalseForDifferentTypesA");
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsFalseForDifferentTypesB");
// Act
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
// Assert
Assert.False(isAssignableFrom);
}
[Fact]
public async Task IsAssignable_ReturnsFalseIfTypeDoesNotImplementInterface()
{
// Arrange
var compilation = await GetCompilation(nameof(IsAssignable_ReturnsFalseForDifferentTypes));
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsFalseForDifferentTypesA");
var target = compilation.GetTypeByMetadataName($"System.IDisposable");
// Act
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
// Assert
Assert.False(isAssignableFrom);
}
[Fact]
public async Task IsAssignable_ReturnsTrueIfTypesAreExact()
{
// Arrange
var compilation = await GetCompilation();
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypesAreExact");
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypesAreExact");
// Act
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
// Assert
Assert.True(isAssignableFrom);
}
[Fact]
public async Task IsAssignable_ReturnsTrueIfTypeImplementsInterface()
{
// Arrange
var compilation = await GetCompilation();
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypeImplementsInterface");
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfTypeImplementsInterfaceTest");
// Act
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
var isAssignableFromDerived = CodeAnalysisExtensions.IsAssignableFrom(target, source);
// Assert
Assert.True(isAssignableFrom);
Assert.False(isAssignableFromDerived); // Inverse shouldn't be true
}
[Fact]
public async Task IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterface()
{
// Arrange
var compilation = await GetCompilation();
var source = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterface");
var target = compilation.GetTypeByMetadataName($"{GetType().Namespace}.IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceTest");
// Act
var isAssignableFrom = CodeAnalysisExtensions.IsAssignableFrom(source, target);
var isAssignableFromDerived = CodeAnalysisExtensions.IsAssignableFrom(target, source);
// Assert
Assert.True(isAssignableFrom);
Assert.False(isAssignableFromDerived); // Inverse shouldn't be true
}
private Task<Compilation> GetCompilation([CallerMemberName] string testMethod = "")
{
var testSource = MvcTestSource.Read(GetType().Name, testMethod);
var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source });
return project.GetCompilationAsync();
}
}
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Microsoft.AspNetCore.Mvc.Analyzers.Test
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class DiagnosticsAreReturned_IfAllowAnonymousIsAppliedToPageHandlerMethod : PageModel
{

View File

@ -0,0 +1,19 @@
using System;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttribute : Attribute { }
[Controller]
public class HasAttribute_ReturnsFalseIfTypeDoesNotHaveAttributeTest
{
[NonAction]
public void SomeMethod()
{
}
[BindProperty]
public string SomeProperty { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class HasAttribute_ReturnsTrueForAttributesOnMethodsAttribute : Attribute { }
public class HasAttribute_ReturnsTrueForAttributesOnMethodsTest
{
[HasAttribute_ReturnsTrueForAttributesOnMethodsAttribute]
public void SomeMethod() { }
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsAttribute : Attribute { }
public class HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsBase
{
[HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsAttribute]
public virtual void SomeMethod() { }
}
public class HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsTest : HasAttribute_ReturnsTrueForAttributesOnOverriddenMethodsBase
{
public override void SomeMethod() { }
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesAttribute : Attribute { }
public class HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesBase
{
[HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesAttribute]
public virtual string SomeProperty { get; set; }
}
public class HasAttribute_ReturnsTrueForAttributesOnOverriddenProperties : HasAttribute_ReturnsTrueForAttributesOnOverriddenPropertiesBase
{
public override string SomeProperty { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class HasAttribute_ReturnsTrueForAttributesOnPropertiesAttribute : Attribute { }
public class HasAttribute_ReturnsTrueForAttributesOnProperties
{
[HasAttribute_ReturnsTrueForAttributesOnPropertiesAttribute]
public string SomeProperty { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public interface IHasAttribute_ReturnsTrueForInterfaceContractOnAttribute { }
public class HasAttribute_ReturnsTrueForInterfaceContractOnAttribute : Attribute, IHasAttribute_ReturnsTrueForInterfaceContractOnAttribute { }
[HasAttribute_ReturnsTrueForInterfaceContractOnAttribute]
public class HasAttribute_ReturnsTrueForInterfaceContractOnAttributeTest
{
}
public class HasAttribute_ReturnsTrueForInterfaceContractOnAttributeDerived : HasAttribute_ReturnsTrueForInterfaceContractOnAttributeTest
{
}
}

View File

@ -0,0 +1,7 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[Controller]
public class HasAttribute_ReturnsTrueIfBaseTypeHasAttributeBase { }
public class HasAttribute_ReturnsTrueIfBaseTypeHasAttribute : HasAttribute_ReturnsTrueIfBaseTypeHasAttributeBase { }
}

View File

@ -0,0 +1,5 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[Controller]
public class HasAttribute_ReturnsTrueIfTypeHasAttribute { }
}

View File

@ -0,0 +1,10 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class IsAssignable_ReturnsFalseForDifferentTypesA
{
}
public class IsAssignable_ReturnsFalseForDifferentTypesB
{
}
}

View File

@ -0,0 +1,18 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public interface IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterface
{
}
public class IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceA : IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterface
{
}
public class IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceB : IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceA
{
}
public class IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceTest : IsAssignable_ReturnsTrueIfAncestorTypeImplementsInterfaceB
{
}
}

View File

@ -0,0 +1,9 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public interface IsAssignable_ReturnsTrueIfTypeImplementsInterface
{
}
public class IsAssignable_ReturnsTrueIfTypeImplementsInterfaceTest : IsAssignable_ReturnsTrueIfTypeImplementsInterface { }
}

View File

@ -0,0 +1,10 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class IsAssignable_ReturnsTrueIfTypeIsBaseClassBase
{
}
public class IsAssignable_ReturnsTrueIfTypeIsBaseClass : IsAssignable_ReturnsTrueIfTypeIsBaseClassBase
{
}
}

View File

@ -0,0 +1,6 @@
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class IsAssignable_ReturnsTrueIfTypesAreExact
{
}
}