-
Notifications
You must be signed in to change notification settings - Fork 292
Expand file tree
/
Copy pathTestArgumentsManager.cs
More file actions
154 lines (126 loc) · 6.89 KB
/
TestArgumentsManager.cs
File metadata and controls
154 lines (126 loc) · 6.89 KB
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
146
147
148
149
150
151
152
153
154
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under dual-license. See LICENSE.PLATFORMTOOLS.txt file in the project root for full license information.
using Microsoft.Testing.Platform;
namespace Microsoft.Testing.Framework;
internal sealed class TestArgumentsManager : ITestArgumentsManager
{
private readonly Dictionary<TestNodeUid, Func<TestArgumentsContext, ITestArgumentsEntry>> _testArgumentsEntryProviders = [];
private bool _isRegistrationFrozen;
public void RegisterTestArgumentsEntryProvider<TArguments>(
TestNodeUid testNodeStableUid,
Func<TestArgumentsContext, InternalUnsafeTestArgumentsEntry<TArguments>> argumentPropertiesProviderCallback)
{
if (_isRegistrationFrozen)
{
throw new InvalidOperationException("Cannot register TestArgumentsEntry provider after registration is frozen.");
}
if (!_testArgumentsEntryProviders.TryAdd(testNodeStableUid, argumentPropertiesProviderCallback))
{
throw new InvalidOperationException($"TestArgumentsEntry provider is already registered for test node with UID '{testNodeStableUid}'.");
}
}
internal void FreezeRegistration() => _isRegistrationFrozen = true;
internal static bool IsExpandableTestNode(TestNode testNode)
=> testNode is IExpandableTestNode
&& !testNode.Properties.OfType<FrameworkEngineMetadataProperty>().SingleOrDefault().PreventArgumentsExpansion;
internal async Task<TestNode> ExpandTestNodeAsync(TestNode currentNode)
{
RoslynDebug.Assert(IsExpandableTestNode(currentNode), "Test node is not expandable");
int argumentsRowIndex = -1;
bool isIndexArgumentPropertiesProvider = false;
if (!_testArgumentsEntryProviders.TryGetValue(
currentNode.StableUid,
out Func<TestArgumentsContext, ITestArgumentsEntry>? argumentPropertiesProvider))
{
isIndexArgumentPropertiesProvider = true;
argumentPropertiesProvider = argument =>
{
string fragment = $"[{argumentsRowIndex}]";
return new InternalUnsafeTestArgumentsEntry<object>(argument.Arguments, fragment, fragment);
};
}
HashSet<TestNodeUid> expandedTestNodeUids = [];
List<TestNode> expandedTestNodes = [.. currentNode.Tests];
switch (currentNode)
{
case IParameterizedTestNode parameterizedTestNode:
foreach (object? arguments in parameterizedTestNode.GetArguments())
{
ExpandNodeWithArguments(currentNode, arguments, ref argumentsRowIndex, expandedTestNodes,
expandedTestNodeUids, argumentPropertiesProvider, isIndexArgumentPropertiesProvider);
}
break;
case ITaskParameterizedTestNode parameterizedTestNode:
foreach (object? arguments in await parameterizedTestNode.GetArguments().ConfigureAwait(false))
{
ExpandNodeWithArguments(currentNode, arguments, ref argumentsRowIndex, expandedTestNodes,
expandedTestNodeUids, argumentPropertiesProvider, isIndexArgumentPropertiesProvider);
}
break;
#if NET
case IAsyncParameterizedTestNode parameterizedTestNode:
await foreach (object? arguments in parameterizedTestNode.GetArguments().ConfigureAwait(false))
{
ExpandNodeWithArguments(currentNode, arguments, ref argumentsRowIndex, expandedTestNodes,
expandedTestNodeUids, argumentPropertiesProvider, isIndexArgumentPropertiesProvider);
}
break;
#endif
default:
throw new InvalidOperationException($"Unexpected parameterized test node type: '{currentNode.GetType()}'");
}
// When the node is expandable, we need to create a new node that is not an action node, but a container
// node that contains the expanded nodes. This is this node that will be executed.
TestNode expandedNode = new()
{
StableUid = currentNode.StableUid,
DisplayName = currentNode.DisplayName,
OverriddenEdgeName = currentNode.OverriddenEdgeName,
Properties = currentNode.Properties,
Tests = [.. expandedTestNodes],
};
return expandedNode;
// Local functions
static void ExpandNodeWithArguments(TestNode testNode, object arguments, ref int argumentsRowIndex, List<TestNode> expandedTestNodes,
HashSet<TestNodeUid> expandedTestNodeUids, Func<TestArgumentsContext, ITestArgumentsEntry> argumentPropertiesProvider,
bool isIndexArgumentPropertiesProvider)
{
// We need to increase the index before calling the argumentPropertiesProvider, because it is capturing it's value
argumentsRowIndex++;
bool shouldWrapInParenthesis = true;
if (arguments is not ITestArgumentsEntry testArgumentsEntry)
{
shouldWrapInParenthesis = !isIndexArgumentPropertiesProvider;
testArgumentsEntry = argumentPropertiesProvider(new(arguments, testNode));
}
string argumentFragmentUid = GetArgumentFragmentUid(testArgumentsEntry, shouldWrapInParenthesis);
string argumentFragmentDisplayName = GetArgumentFragmentDisplayName(testArgumentsEntry, shouldWrapInParenthesis);
TestNode expandedTestNode = ((IExpandableTestNode)testNode).GetExpandedTestNode(arguments, argumentFragmentUid,
argumentFragmentDisplayName);
if (!expandedTestNodeUids.Add(expandedTestNode.StableUid))
{
throw new InvalidOperationException(
$"Expanded test node with UID '{expandedTestNode.StableUid}' is not unique for test node '{testNode.StableUid}'.");
}
expandedTestNodes.Add(expandedTestNode);
}
static string GetArgumentFragmentUid(ITestArgumentsEntry testArgumentsEntry, bool shouldWrapInParenthesis)
=> CreateWrappedName(testArgumentsEntry.UidFragment, shouldWrapInParenthesis);
static string GetArgumentFragmentDisplayName(ITestArgumentsEntry testArgumentsEntry, bool shouldWrapInParenthesis)
=> CreateWrappedName(testArgumentsEntry.DisplayNameFragment ?? testArgumentsEntry.UidFragment, shouldWrapInParenthesis);
static string CreateWrappedName(string name, bool shouldWrapInParenthesis)
{
StringBuilder displayNameBuilder = new();
if (shouldWrapInParenthesis)
{
displayNameBuilder.Append('(');
}
displayNameBuilder.Append(name);
if (shouldWrapInParenthesis)
{
displayNameBuilder.Append(')');
}
return displayNameBuilder.ToString();
}
}
}