-
Notifications
You must be signed in to change notification settings - Fork 269
/
Copy pathTestMethodFilter.cs
145 lines (130 loc) · 6.18 KB
/
TestMethodFilter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
internal class TestMethodFilter
{
/// <summary>
/// Supported properties for filtering.
/// </summary>
private readonly Dictionary<string, TestProperty> _supportedProperties;
internal TestMethodFilter() => _supportedProperties = new Dictionary<string, TestProperty>(StringComparer.OrdinalIgnoreCase)
{
[Constants.TestCategoryProperty.Label] = Constants.TestCategoryProperty,
[Constants.PriorityProperty.Label] = Constants.PriorityProperty,
[TestCaseProperties.FullyQualifiedName.Label] = TestCaseProperties.FullyQualifiedName,
[TestCaseProperties.DisplayName.Label] = TestCaseProperties.DisplayName,
[TestCaseProperties.Id.Label] = TestCaseProperties.Id,
[Constants.TestClassNameProperty.Label] = Constants.TestClassNameProperty,
};
/// <summary>
/// Returns ITestCaseFilterExpression for TestProperties supported by adapter.
/// </summary>
/// <param name="context">The current context of the run.</param>
/// <param name="logger">Handler to report test messages/start/end and results.</param>
/// <param name="filterHasError">Indicates that the filter is unsupported/has an error.</param>
/// <returns>A filter expression.</returns>
internal ITestCaseFilterExpression? GetFilterExpression(IDiscoveryContext? context, IMessageLogger logger, out bool filterHasError)
{
filterHasError = false;
if (context == null)
{
return null;
}
ITestCaseFilterExpression? filter = null;
try
{
filter = context is IRunContext runContext
? GetTestCaseFilterFromRunContext(runContext)
: GetTestCaseFilterFromDiscoveryContext(context, logger);
}
catch (TestPlatformFormatException ex)
{
filterHasError = true;
logger.SendMessage(TestMessageLevel.Error, ex.Message);
}
return filter;
}
/// <summary>
/// Provides TestProperty for property name 'propertyName' as used in filter.
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <returns>a TestProperty instance.</returns>
internal TestProperty PropertyProvider(string propertyName)
{
_supportedProperties.TryGetValue(propertyName, out TestProperty? testProperty);
DebugEx.Assert(testProperty != null, "Invalid property queried");
return testProperty;
}
/// <summary>
/// Provides value of TestProperty corresponding to property name 'propertyName' as used in filter.
/// </summary>
/// <param name="currentTest">The current test case.</param>
/// <param name="propertyName">Property name.</param>
/// <returns>The property value.</returns>
internal object? PropertyValueProvider(TestCase? currentTest, string? propertyName)
{
if (currentTest != null && propertyName != null)
{
if (_supportedProperties.TryGetValue(propertyName, out TestProperty? testProperty))
{
// Test case might not have defined this property. In that case GetPropertyValue()
// would return default value. For filtering, if property is not defined return null.
if (currentTest.Properties.Contains(testProperty))
{
return currentTest.GetPropertyValue(testProperty);
}
}
else
{
// Everything that it's not a supported property we use traits
foreach (Trait trait in currentTest.Traits)
{
if (trait.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
{
return trait.Value;
}
}
}
}
return null;
}
/// <summary>
/// Gets filter expression from run context.
/// </summary>
/// <param name="context">Run context.</param>
/// <returns>Filter expression.</returns>
private ITestCaseFilterExpression? GetTestCaseFilterFromRunContext(IRunContext context) => context.GetTestCaseFilter(_supportedProperties.Keys, PropertyProvider);
/// <summary>
/// Gets filter expression from discovery context.
/// </summary>
/// <param name="context">Discovery context.</param>
/// <param name="logger">The logger to log exception messages too.</param>
/// <returns>Filter expression.</returns>
[UnconditionalSuppressMessage("Aot", "IL2072:DoNotUseDynamicMembers", Justification = "Tested it, it works.")]
private ITestCaseFilterExpression? GetTestCaseFilterFromDiscoveryContext(IDiscoveryContext context, IMessageLogger logger)
{
try
{
// GetTestCaseFilter is present in DiscoveryContext but not in IDiscoveryContext interface.
MethodInfo? methodGetTestCaseFilter = context.GetType().GetRuntimeMethod("GetTestCaseFilter", [typeof(IEnumerable<string>), typeof(Func<string, TestProperty>)]);
return (ITestCaseFilterExpression?)methodGetTestCaseFilter?.Invoke(context, [_supportedProperties.Keys, (Func<string, TestProperty>)PropertyProvider]);
}
catch (Exception ex)
{
// In case of UWP .Net Native Tool Chain compilation. Invoking methods via Reflection doesn't work, hence discovery always fails.
// Hence throwing exception only if it is of type TargetInvocationException(i.e. Method got invoked but something went wrong in GetTestCaseFilter Method)
if (ex is TargetInvocationException)
{
throw ex.InnerException!;
}
logger.SendMessage(TestMessageLevel.Warning, ex.Message);
}
return null;
}
}