Skip to content

Commit bd822c7

Browse files
authored
Refactor GetTestFromMethod (#4279)
1 parent 7b2656e commit bd822c7

File tree

4 files changed

+174
-72
lines changed

4 files changed

+174
-72
lines changed

src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs

+28-14
Original file line numberDiff line numberDiff line change
@@ -201,29 +201,43 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT
201201

202202
testElement.Traits = traits.ToArray();
203203

204-
if (_reflectHelper.GetFirstDerivedAttributeOrDefault<CssIterationAttribute>(method, inherit: true) is CssIterationAttribute cssIteration)
205-
{
206-
testElement.CssIteration = cssIteration.CssIteration;
207-
}
204+
Attribute[] attributes = _reflectHelper.GetCustomAttributesCached(method, inherit: true);
205+
TestMethodAttribute? testMethodAttribute = null;
208206

209-
if (_reflectHelper.GetFirstDerivedAttributeOrDefault<CssProjectStructureAttribute>(method, inherit: true) is CssProjectStructureAttribute cssProjectStructure)
207+
// Backward looping for backcompat. This used to be calls to _reflectHelper.GetFirstDerivedAttributeOrDefault
208+
// So, to make sure the first attribute always wins, we loop from end to start.
209+
for (int i = attributes.Length - 1; i >= 0; i--)
210210
{
211-
testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure;
212-
}
213-
214-
if (_reflectHelper.GetFirstDerivedAttributeOrDefault<DescriptionAttribute>(method, inherit: true) is DescriptionAttribute descriptionAttribute)
215-
{
216-
testElement.Description = descriptionAttribute.Description;
211+
if (attributes[i] is TestMethodAttribute tma)
212+
{
213+
testMethodAttribute = tma;
214+
}
215+
else if (attributes[i] is CssIterationAttribute cssIteration)
216+
{
217+
testElement.CssIteration = cssIteration.CssIteration;
218+
}
219+
else if (attributes[i] is CssProjectStructureAttribute cssProjectStructure)
220+
{
221+
testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure;
222+
}
223+
else if (attributes[i] is DescriptionAttribute descriptionAttribute)
224+
{
225+
testElement.Description = descriptionAttribute.Description;
226+
}
217227
}
218228

219-
WorkItemAttribute[] workItemAttributes = _reflectHelper.GetDerivedAttributes<WorkItemAttribute>(method, inherit: true).ToArray();
220-
if (workItemAttributes.Length != 0)
229+
IEnumerable<WorkItemAttribute> workItemAttributes = attributes.OfType<WorkItemAttribute>();
230+
if (workItemAttributes.Any())
221231
{
222232
testElement.WorkItemIds = workItemAttributes.Select(x => x.Id.ToString(CultureInfo.InvariantCulture)).ToArray();
223233
}
224234

235+
// In production, we always have a TestMethod attribute because GetTestFromMethod is called under IsValidTestMethod
236+
// In unit tests, we may not have the test to have TestMethodAttribute.
237+
// TODO: Adjust all unit tests to properly have the attribute and uncomment the assert.
238+
// DebugEx.Assert(testMethodAttribute is not null, "Expected to find a 'TestMethod' attribute.");
239+
225240
// get DisplayName from TestMethodAttribute (or any inherited attribute)
226-
TestMethodAttribute? testMethodAttribute = _reflectHelper.GetFirstDerivedAttributeOrDefault<TestMethodAttribute>(method, inherit: true);
227241
testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name;
228242

229243
return testElement;

src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ internal virtual IEnumerable<Trait> GetTestPropertiesAsTraits(MemberInfo testPro
428428
/// <param name="attributeProvider">The member to inspect.</param>
429429
/// <param name="inherit">Look at inheritance chain.</param>
430430
/// <returns>attributes defined.</returns>
431-
private Attribute[] GetCustomAttributesCached(ICustomAttributeProvider attributeProvider, bool inherit)
431+
internal Attribute[] GetCustomAttributesCached(ICustomAttributeProvider attributeProvider, bool inherit)
432432
{
433433
// If the information is cached, then use it otherwise populate the cache using
434434
// the reflection APIs.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Globalization;
5+
using System.Reflection;
6+
7+
#if !NET6_0_OR_GREATER
8+
using Polyfills;
9+
#endif
10+
11+
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery;
12+
13+
public partial class TypeEnumeratorTests
14+
{
15+
private sealed class MockedMethodInfoWithExtraAttributes : MethodInfo
16+
{
17+
private readonly MethodInfo _original;
18+
private readonly Attribute[] _extraAttributes;
19+
20+
public MockedMethodInfoWithExtraAttributes(MethodInfo original, params Attribute[] extraAttributes)
21+
{
22+
_original = original;
23+
_extraAttributes = extraAttributes;
24+
}
25+
26+
public override ICustomAttributeProvider ReturnTypeCustomAttributes => _original.ReturnTypeCustomAttributes;
27+
28+
public override RuntimeMethodHandle MethodHandle => _original.MethodHandle;
29+
30+
public override MethodAttributes Attributes => _original.Attributes;
31+
32+
public override string Name => _original.Name;
33+
34+
public override Type DeclaringType => _original.DeclaringType;
35+
36+
public override Type ReflectedType => _original.ReflectedType;
37+
38+
public override IEnumerable<CustomAttributeData> CustomAttributes => _original.CustomAttributes;
39+
40+
public override int MetadataToken => _original.MetadataToken;
41+
42+
public override Module Module => _original.Module;
43+
44+
public override MethodImplAttributes MethodImplementationFlags => _original.MethodImplementationFlags;
45+
46+
public override CallingConventions CallingConvention => _original.CallingConvention;
47+
48+
public override bool IsGenericMethodDefinition => _original.IsGenericMethodDefinition;
49+
50+
public override bool ContainsGenericParameters => _original.ContainsGenericParameters;
51+
52+
public override bool IsGenericMethod => _original.IsGenericMethod;
53+
54+
public override bool IsSecurityCritical => _original.IsSecurityCritical;
55+
56+
public override bool IsSecuritySafeCritical => _original.IsSecuritySafeCritical;
57+
58+
public override bool IsSecurityTransparent => _original.IsSecurityTransparent;
59+
60+
public override MemberTypes MemberType => _original.MemberType;
61+
62+
public override Type ReturnType => _original.ReturnType;
63+
64+
public override ParameterInfo ReturnParameter => _original.ReturnParameter;
65+
66+
public override Delegate CreateDelegate(Type delegateType) => _original.CreateDelegate(delegateType);
67+
68+
public override Delegate CreateDelegate(Type delegateType, object target) => _original.CreateDelegate(delegateType, target);
69+
70+
public override MethodInfo GetBaseDefinition() => _original.GetBaseDefinition();
71+
72+
public override object[] GetCustomAttributes(bool inherit) => _original.GetCustomAttributes().Concat(_extraAttributes).ToArray();
73+
74+
public override object[] GetCustomAttributes(Type attributeType, bool inherit) => _original.GetCustomAttributes().Concat(_extraAttributes.Where(a => a.GetType().IsAssignableTo(attributeType))).ToArray();
75+
76+
public override IList<CustomAttributeData> GetCustomAttributesData() => _original.GetCustomAttributesData();
77+
78+
public override Type[] GetGenericArguments() => _original.GetGenericArguments();
79+
80+
public override MethodInfo GetGenericMethodDefinition() => _original.GetGenericMethodDefinition();
81+
82+
public override MethodBody GetMethodBody() => _original.GetMethodBody();
83+
84+
public override MethodImplAttributes GetMethodImplementationFlags() => _original.GetMethodImplementationFlags();
85+
86+
public override ParameterInfo[] GetParameters() => _original.GetParameters();
87+
88+
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
89+
=> _original.Invoke(obj, invokeAttr, binder, parameters, culture);
90+
91+
public override bool IsDefined(Type attributeType, bool inherit)
92+
=> _original.IsDefined(attributeType, inherit) || _extraAttributes.Any(a => a.GetType().IsAssignableTo(attributeType));
93+
94+
public override MethodInfo MakeGenericMethod(params Type[] typeArguments) => _original.MakeGenericMethod(typeArguments);
95+
96+
public override string ToString() => _original.ToString();
97+
}
98+
}

0 commit comments

Comments
 (0)