Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

using System.Security;

using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
Expand Down Expand Up @@ -34,7 +34,10 @@ internal TestAssemblySettings GetSettings(string source)
// Load the source.
Assembly testAssembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(source);

ParallelizeAttribute? parallelizeAttribute = ReflectHelper.GetParallelizeAttribute(testAssembly);
IReflectionOperations reflectionOperations = PlatformServiceProvider.Instance.ReflectionOperations;
ParallelizeAttribute? parallelizeAttribute = reflectionOperations.GetCustomAttributes(testAssembly, typeof(ParallelizeAttribute))
.OfType<ParallelizeAttribute>()
.FirstOrDefault();

if (parallelizeAttribute != null)
{
Expand All @@ -47,7 +50,7 @@ internal TestAssemblySettings GetSettings(string source)
}
}

testAssemblySettings.CanParallelizeAssembly = !ReflectHelper.IsDoNotParallelizeSet(testAssembly);
testAssemblySettings.CanParallelizeAssembly = reflectionOperations.GetCustomAttributes(testAssembly, typeof(DoNotParallelizeAttribute)).Length == 0;

return testAssemblySettings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ internal TestMethodInfo(MethodInfo testMethod, TestClassInfo parent)
/// </summary>
/// <returns>An array of the attributes.</returns>
public Attribute[]? GetAllAttributes()
=> [.. ReflectHelper.Instance.GetAttributes<Attribute>(MethodInfo)];
=> [.. PlatformServiceProvider.Instance.ReflectionOperations.GetAttributes<Attribute>(MethodInfo)];

/// <summary>
/// Gets all attributes of the test method.
Expand All @@ -111,7 +111,7 @@ internal TestMethodInfo(MethodInfo testMethod, TestClassInfo parent)
/// <returns>An array of the attributes.</returns>
public TAttributeType[] GetAttributes<TAttributeType>()
where TAttributeType : Attribute
=> [.. ReflectHelper.Instance.GetAttributes<TAttributeType>(MethodInfo)];
=> [.. PlatformServiceProvider.Instance.ReflectionOperations.GetAttributes<TAttributeType>(MethodInfo)];

/// <summary>
/// Execute test method. Capture failures, handle async and return result.
Expand Down Expand Up @@ -246,7 +246,7 @@ public virtual async Task<TestResult> InvokeAsync(object?[]? arguments)
private TimeoutInfo GetTestTimeout()
{
DebugEx.Assert(MethodInfo != null, "TestMethod should be non-null");
TimeoutAttribute? timeoutAttribute = ReflectHelper.Instance.GetFirstAttributeOrDefault<TimeoutAttribute>(MethodInfo);
TimeoutAttribute? timeoutAttribute = PlatformServiceProvider.Instance.ReflectionOperations.GetFirstAttributeOrDefault<TimeoutAttribute>(MethodInfo);
if (timeoutAttribute is null)
{
return TimeoutInfo.FromTestTimeoutSettings();
Expand All @@ -269,7 +269,7 @@ private TestMethodAttribute GetTestMethodAttribute()
{
// Get the derived TestMethod attribute from reflection.
// It should be non-null as it was already validated by IsValidTestMethod.
TestMethodAttribute testMethodAttribute = ReflectHelper.Instance.GetSingleAttributeOrDefault<TestMethodAttribute>(MethodInfo)!;
TestMethodAttribute testMethodAttribute = PlatformServiceProvider.Instance.ReflectionOperations.GetSingleAttributeOrDefault<TestMethodAttribute>(MethodInfo)!;

// Get the derived TestMethod attribute from Extended TestClass Attribute
// If the extended TestClass Attribute doesn't have extended TestMethod attribute then base class returns back the original testMethod Attribute
Expand All @@ -285,7 +285,7 @@ private TestMethodAttribute GetTestMethodAttribute()
/// </returns>
private RetryBaseAttribute? GetRetryAttribute()
{
IEnumerable<RetryBaseAttribute> attributes = ReflectHelper.Instance.GetAttributes<RetryBaseAttribute>(MethodInfo);
IEnumerable<RetryBaseAttribute> attributes = PlatformServiceProvider.Instance.ReflectionOperations.GetAttributes<RetryBaseAttribute>(MethodInfo);
using IEnumerator<RetryBaseAttribute> enumerator = attributes.GetEnumerator();
if (!enumerator.MoveNext())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;

using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
Expand Down Expand Up @@ -191,7 +190,7 @@ private async Task<bool> TryExecuteDataSourceBasedTestsAsync(List<TestResult> re
private async Task<bool> TryExecuteFoldedDataDrivenTestsAsync(List<TestResult> results)
{
bool hasTestDataSource = false;
foreach (Attribute attribute in ReflectHelper.Instance.GetCustomAttributesCached(_testMethodInfo.MethodInfo))
foreach (Attribute attribute in PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributesCached(_testMethodInfo.MethodInfo))
{
if (attribute is not UTF.ITestDataSource testDataSource)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ internal static class AttributeExtensions
{
public static bool IsIgnored(this ICustomAttributeProvider type, out string? ignoreMessage)
{
IEnumerable<ConditionBaseAttribute> attributes = ReflectHelper.Instance.GetAttributes<ConditionBaseAttribute>(type);
IEnumerable<ConditionBaseAttribute> attributes = PlatformServiceProvider.Instance.ReflectionOperations.GetAttributes<ConditionBaseAttribute>(type);
IEnumerable<IGrouping<string, ConditionBaseAttribute>> groups = attributes.GroupBy(attr => attr.GroupName);
foreach (IGrouping<string, ConditionBaseAttribute>? group in groups)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;

/// <summary>
/// Helper methods for extracting test metadata from reflection operations.
/// </summary>
internal static class ReflectionHelper
{
/// <summary>
/// Get categories applied to the test method.
/// </summary>
/// <param name="reflectionOperations">The reflection operations to use.</param>
/// <param name="categoryAttributeProvider">The member to inspect.</param>
/// <param name="owningType">The reflected type that owns <paramref name="categoryAttributeProvider"/>.</param>
/// <returns>Categories defined.</returns>
public static string[] GetTestCategories(this IReflectionOperations reflectionOperations, MemberInfo categoryAttributeProvider, Type owningType)
{
IEnumerable<TestCategoryBaseAttribute> methodCategories = reflectionOperations.GetAttributes<TestCategoryBaseAttribute>(categoryAttributeProvider);
IEnumerable<TestCategoryBaseAttribute> typeCategories = reflectionOperations.GetAttributes<TestCategoryBaseAttribute>(owningType);
IEnumerable<TestCategoryBaseAttribute> assemblyCategories = reflectionOperations.GetAttributes<TestCategoryBaseAttribute>(owningType.Assembly);

return [.. methodCategories.Concat(typeCategories).Concat(assemblyCategories).SelectMany(c => c.TestCategories)];
}

/// <summary>
/// KeyValue pairs that are provided by TestPropertyAttributes of the given test method.
/// </summary>
/// <param name="reflectionOperations">The reflection operations to use.</param>
/// <param name="testPropertyProvider">The member to inspect.</param>
/// <returns>List of traits.</returns>
public static Trait[] GetTestPropertiesAsTraits(this IReflectionOperations reflectionOperations, MethodInfo testPropertyProvider)
{
Attribute[] attributesFromMethod = reflectionOperations.GetCustomAttributesCached(testPropertyProvider);
Attribute[] attributesFromClass = testPropertyProvider.ReflectedType is { } testClass ? reflectionOperations.GetCustomAttributesCached(testClass) : [];
int countTestPropertyAttribute = 0;
foreach (Attribute attribute in attributesFromMethod)
{
if (attribute is TestPropertyAttribute)
{
countTestPropertyAttribute++;
}
}

foreach (Attribute attribute in attributesFromClass)
{
if (attribute is TestPropertyAttribute)
{
countTestPropertyAttribute++;
}
}

if (countTestPropertyAttribute == 0)
{
// This is the common case that we optimize for. This method used to be an iterator (uses yield return) which is allocating unnecessarily in common cases.
return [];
}

var traits = new Trait[countTestPropertyAttribute];
int index = 0;
foreach (Attribute attribute in attributesFromMethod)
{
if (attribute is TestPropertyAttribute testProperty)
{
traits[index++] = new Trait(testProperty.Name, testProperty.Value);
}
}

foreach (Attribute attribute in attributesFromClass)
{
if (attribute is TestPropertyAttribute testProperty)
{
traits[index++] = new Trait(testProperty.Name, testProperty.Value);
}
}

return traits;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,60 @@ internal interface IReflectionOperations
Type? GetType(Assembly assembly, string typeName);

object? CreateInstance(Type type, object?[] parameters);

/// <summary>
/// Checks to see if a member or type is decorated with the given attribute, or an attribute that derives from it.
/// </summary>
/// <typeparam name="TAttribute">Attribute to search for.</typeparam>
/// <param name="memberInfo">Member to inspect for attributes.</param>
/// <returns>True if the attribute of the specified type is defined on this member or a class.</returns>
bool IsAttributeDefined<TAttribute>(MemberInfo memberInfo)
where TAttribute : Attribute;

/// <summary>
/// Gets first attribute that matches the type.
/// Use this together with attribute that does not allow multiple and is sealed. In such case there cannot be more attributes, and this will avoid the cost of
/// checking for more than one attribute.
/// </summary>
/// <typeparam name="TAttribute">Type of the attribute to find.</typeparam>
/// <param name="attributeProvider">The type, assembly or method.</param>
/// <returns>The attribute that is found or null.</returns>
TAttribute? GetFirstAttributeOrDefault<TAttribute>(ICustomAttributeProvider attributeProvider)
where TAttribute : Attribute;

/// <summary>
/// Gets first attribute that matches the type or is derived from it.
/// Use this together with attribute that does not allow multiple. In such case there cannot be more attributes, and this will avoid the cost of
/// checking for more than one attribute.
/// </summary>
/// <typeparam name="TAttribute">Type of the attribute to find.</typeparam>
/// <param name="attributeProvider">The type, assembly or method.</param>
/// <returns>The attribute that is found or null.</returns>
/// <exception cref="InvalidOperationException">Throws when multiple attributes are found (the attribute must allow multiple).</exception>
TAttribute? GetSingleAttributeOrDefault<TAttribute>(ICustomAttributeProvider attributeProvider)
where TAttribute : Attribute;

/// <summary>
/// Get attribute defined on a member which is of given type of subtype of given type.
/// </summary>
/// <typeparam name="TAttributeType">The attribute type.</typeparam>
/// <param name="attributeProvider">The member to inspect.</param>
/// <returns>An instance of the attribute.</returns>
IEnumerable<TAttributeType> GetAttributes<TAttributeType>(ICustomAttributeProvider attributeProvider)
where TAttributeType : Attribute;

/// <summary>
/// Gets and caches the attributes for the given type, or method.
/// </summary>
/// <param name="attributeProvider">The member to inspect.</param>
/// <returns>Attributes defined.</returns>
Attribute[] GetCustomAttributesCached(ICustomAttributeProvider attributeProvider);

/// <summary>
/// Checks whether the declaring type of the method is declared in the same assembly as the given type.
/// </summary>
/// <param name="method">The method to check.</param>
/// <param name="type">The type whose assembly to compare against.</param>
/// <returns>True if the method's declaring type is in the same assembly as <paramref name="type"/>.</returns>
bool IsMethodDeclaredInSameAssemblyAsType(MethodInfo method, Type type);
}
Loading
Loading