Skip to content

Commit 721fa86

Browse files
authored
Fix ManagedNameHelper to support namespaceless methods. (#4003)
1 parent 796afa3 commit 721fa86

File tree

5 files changed

+249
-73
lines changed

5 files changed

+249
-73
lines changed

src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
7878
/// More information about <paramref name="managedTypeName"/> and <paramref name="managedMethodName"/> can be found in
7979
/// <see href="https://github.com/microsoft/vstest-docs/blob/main/RFCs/0017-Managed-TestCase-Properties.md">the RFC</see>.
8080
/// </remarks>
81-
public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName, out string[] hierarchyValues)
81+
public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName, out string?[] hierarchyValues)
8282
{
8383
GetManagedName(method, out managedTypeName, out managedMethodName);
8484
GetManagedNameAndHierarchy(method, true, out _, out _, out hierarchyValues);
@@ -102,14 +102,14 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
102102
/// <returns>
103103
/// The hierarchy values.
104104
/// </returns>
105-
public static string[] GetManagedHierarchy(MethodBase method)
105+
public static string?[] GetManagedHierarchy(MethodBase method)
106106
{
107107
GetManagedNameAndHierarchy(method, true, out _, out _, out var hierarchyValues);
108108

109109
return hierarchyValues;
110110
}
111111

112-
private static void GetManagedNameAndHierarchy(MethodBase method, bool useClosedTypes, out string managedTypeName, out string managedMethodName, out string[] hierarchyValues)
112+
private static void GetManagedNameAndHierarchy(MethodBase method, bool useClosedTypes, out string managedTypeName, out string managedMethodName, out string?[] hierarchyValues)
113113
{
114114
_ = method ?? throw new ArgumentNullException(nameof(method));
115115

@@ -191,8 +191,16 @@ private static void GetManagedNameAndHierarchy(MethodBase method, bool useClosed
191191

192192
hierarchyValues = new string[HierarchyConstants.Levels.TotalLevelCount];
193193
hierarchyValues[HierarchyConstants.Levels.TestGroupIndex] = managedMethodName.Substring(0, methodNameEndIndex);
194-
hierarchyValues[HierarchyConstants.Levels.ClassIndex] = managedTypeName.Substring(hierarchyPos[1] + 1, hierarchyPos[2] - hierarchyPos[1] - 1);
195-
hierarchyValues[HierarchyConstants.Levels.NamespaceIndex] = managedTypeName.Substring(hierarchyPos[0], hierarchyPos[1] - hierarchyPos[0]);
194+
if (hierarchyPos[1] == hierarchyPos[0]) // No namespace
195+
{
196+
hierarchyValues[HierarchyConstants.Levels.ClassIndex] = managedTypeName.Substring(0, hierarchyPos[2]);
197+
hierarchyValues[HierarchyConstants.Levels.NamespaceIndex] = null;
198+
}
199+
else
200+
{
201+
hierarchyValues[HierarchyConstants.Levels.ClassIndex] = managedTypeName.Substring(hierarchyPos[1] + 1, hierarchyPos[2] - hierarchyPos[1] - 1);
202+
hierarchyValues[HierarchyConstants.Levels.NamespaceIndex] = managedTypeName.Substring(hierarchyPos[0], hierarchyPos[1] - hierarchyPos[0]);
203+
}
196204
hierarchyValues[HierarchyConstants.Levels.ContainerIndex] = method.DeclaringType?.GetTypeInfo()?.Assembly?.GetName()?.Name ?? string.Empty;
197205
}
198206

@@ -328,10 +336,17 @@ bool Filter(MemberInfo mbr, object? param)
328336
hierarchies = new int[3];
329337
hierarchies[0] = b.Length;
330338

331-
AppendNamespace(b, type.Namespace);
332-
hierarchies[1] = b.Length;
339+
if (type.Namespace != null)
340+
{
341+
AppendNamespace(b, type.Namespace);
342+
hierarchies[1] = b.Length;
333343

334-
b.Append('.');
344+
b.Append('.');
345+
}
346+
else
347+
{
348+
hierarchies[1] = hierarchies[0];
349+
}
335350

336351
AppendNestedTypeName(b, type, closedType);
337352
if (closedType)

src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.GetHash() -> byte[]!
2626
Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.GetId() -> System.Guid
2727
Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.TestIdProvider() -> void
2828
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedName(System.Reflection.MethodBase! method, out string! managedTypeName, out string! managedMethodName) -> void
29-
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedName(System.Reflection.MethodBase! method, out string! managedTypeName, out string! managedMethodName, out string![]! hierarchyValues) -> void
30-
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedHierarchy(System.Reflection.MethodBase! method) -> string![]!
29+
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedName(System.Reflection.MethodBase! method, out string! managedTypeName, out string! managedMethodName, out string?[]! hierarchyValues) -> void
30+
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedHierarchy(System.Reflection.MethodBase! method) -> string?[]!
3131
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetMethod(System.Reflection.Assembly! assembly, string! managedTypeName, string! managedMethodName) -> System.Reflection.MethodBase!
3232
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameParser.ParseManagedMethodName(string! managedMethodName, out string! methodName, out int arity, out string![]? parameterTypes) -> void
3333
static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameParser.ParseManagedTypeName(string! managedTypeName, out string! namespaceName, out string! typeName) -> void
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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 Microsoft.VisualStudio.TestTools.UnitTesting;
5+
6+
namespace Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.UnitTests;
7+
8+
[TestClass]
9+
public class ManagedNameGeneratorTests
10+
{
11+
[TestMethod]
12+
public void Namespaceless_ClassMembers_ShouldNotReportANamespace()
13+
{
14+
// Arrange
15+
var methodBase = typeof(global::NamespacelessClass).GetMethod("Method0")!;
16+
17+
// Act
18+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName);
19+
20+
// Assert
21+
Assert.AreEqual("NamespacelessClass", managedTypeName);
22+
Assert.AreEqual("Method0", managedMethodName);
23+
}
24+
25+
[TestMethod]
26+
public void Namespaceless_RecordMembers_ShouldNotReportANamespace()
27+
{
28+
// Arrange
29+
var methodBase = typeof(global::NamespacelessRecord).GetMethod("Method0")!;
30+
31+
// Act
32+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName);
33+
34+
// Assert
35+
Assert.AreEqual("NamespacelessRecord", managedTypeName);
36+
Assert.AreEqual("Method0", managedMethodName);
37+
}
38+
39+
[TestMethod]
40+
public void Namespaceless_InnerClassMembers_ShouldNotReportANamespace()
41+
{
42+
// Arrange
43+
var methodBase = typeof(global::NamespacelessClass.Inner).GetMethod("Method0")!;
44+
45+
// Act
46+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName);
47+
48+
// Assert
49+
Assert.AreEqual("NamespacelessClass+Inner", managedTypeName);
50+
Assert.AreEqual("Method0", managedMethodName);
51+
}
52+
53+
[TestMethod]
54+
public void Namespaceless_InnerRecordMembers_ShouldNotReportANamespace()
55+
{
56+
// Arrange
57+
var methodBase = typeof(global::NamespacelessRecord.Inner).GetMethod("Method0")!;
58+
59+
// Act
60+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName);
61+
62+
// Assert
63+
Assert.AreEqual("NamespacelessRecord+Inner", managedTypeName);
64+
Assert.AreEqual("Method0", managedMethodName);
65+
}
66+
67+
[TestMethod]
68+
public void Namespaceless_ClassMembers_ShouldNotReportANamespace_InHierarchy()
69+
{
70+
// Arrange
71+
var methodBase = typeof(global::NamespacelessClass).GetMethod("Method0")!;
72+
73+
// Act
74+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues);
75+
76+
// Assert
77+
Assert.AreEqual("NamespacelessClass", managedTypeName);
78+
Assert.AreEqual("Method0", managedMethodName);
79+
Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]);
80+
}
81+
82+
[TestMethod]
83+
public void Namespaceless_RecordMembers_ShouldNotReportANamespace_InHierarch()
84+
{
85+
// Arrange
86+
var methodBase = typeof(global::NamespacelessRecord).GetMethod("Method0")!;
87+
88+
// Act
89+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues);
90+
91+
// Assert
92+
Assert.AreEqual("NamespacelessRecord", managedTypeName);
93+
Assert.AreEqual("Method0", managedMethodName);
94+
Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]);
95+
}
96+
97+
[TestMethod]
98+
public void Namespaceless_InnerClassMembers_ShouldNotReportANamespace_InHierarchy()
99+
{
100+
// Arrange
101+
var methodBase = typeof(global::NamespacelessClass.Inner).GetMethod("Method0")!;
102+
103+
// Act
104+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues);
105+
106+
// Assert
107+
Assert.AreEqual("NamespacelessClass+Inner", managedTypeName);
108+
Assert.AreEqual("Method0", managedMethodName);
109+
Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]);
110+
}
111+
112+
[TestMethod]
113+
public void Namespaceless_InnerRecordMembers_ShouldNotReportANamespace_InHierarchy()
114+
{
115+
// Arrange
116+
var methodBase = typeof(global::NamespacelessRecord.Inner).GetMethod("Method0")!;
117+
118+
// Act
119+
ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues);
120+
121+
// Assert
122+
Assert.AreEqual("NamespacelessRecord+Inner", managedTypeName);
123+
Assert.AreEqual("Method0", managedMethodName);
124+
Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]);
125+
}
126+
}

test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/TestClasses.cs

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
using System;
55
using System.Collections.Generic;
66

7-
namespace TestClasses;
8-
97
#pragma warning disable IDE0060 // Remove unused parameter
108
#pragma warning disable CA1822 // Mark members as static
11-
internal class Outer
9+
#pragma warning disable IDE0161 // Convert to file-scoped namespace
10+
11+
internal class NamespacelessClass
1212
{
1313
public void Method0() { }
1414
public void Method1(int i) { }
@@ -23,83 +23,118 @@ public void Method3<U, T>(int i) { }
2323
}
2424
}
2525

26-
internal class OuterPrime : Outer { }
27-
28-
internal class Outer<T>
26+
internal record NamespacelessRecord
2927
{
3028
public void Method0() { }
31-
public void Method1(T t) { }
32-
public void Method2<U>(U[] u) { }
33-
public void Method3<U>(T t, U u) { }
29+
public void Method1(int i) { }
30+
public void Method2(List<string> ls) { }
31+
public void Method3(string p, int l) { }
32+
internal record Inner
33+
{
34+
public void Method0() { }
35+
public void Method1(int i) { }
36+
public void Method2<U>(int i) { }
37+
public void Method3<U, T>(int i) { }
38+
}
39+
}
40+
41+
namespace TestClasses
42+
{
43+
internal class Outer
44+
{
45+
public void Method0() { }
46+
public void Method1(int i) { }
47+
public void Method2(List<string> ls) { }
48+
public void Method3(string p, int l) { }
49+
internal class Inner
50+
{
51+
public void Method0() { }
52+
public void Method1(int i) { }
53+
public void Method2<U>(int i) { }
54+
public void Method3<U, T>(int i) { }
55+
}
56+
}
57+
58+
internal class OuterPrime : Outer { }
3459

35-
internal class Inner<V>
60+
internal class Outer<T>
3661
{
3762
public void Method0() { }
3863
public void Method1(T t) { }
39-
public void Method2(V v) { }
40-
public void Method3<U>(T t, U u, V v) { }
41-
public void Method4<U, X>(X x, U u) { }
42-
public void Method5<U, X>(List<X> x, U u) { }
64+
public void Method2<U>(U[] u) { }
65+
public void Method3<U>(T t, U u) { }
4366

44-
internal class MoreInner<I>
67+
internal class Inner<V>
4568
{
46-
public void Method0<U>(T t, V v, I i, U u) { }
69+
public void Method0() { }
70+
public void Method1(T t) { }
71+
public void Method2(V v) { }
72+
public void Method3<U>(T t, U u, V v) { }
73+
public void Method4<U, X>(X x, U u) { }
74+
public void Method5<U, X>(List<X> x, U u) { }
75+
76+
internal class MoreInner<I>
77+
{
78+
public void Method0<U>(T t, V v, I i, U u) { }
79+
}
4780
}
4881
}
49-
}
5082

51-
internal class OuterPrime<Z> : Outer<Z> { }
83+
internal class OuterPrime<Z> : Outer<Z> { }
5284

53-
internal class OuterPrime<Y, Z> : Outer<Z> { }
85+
internal class OuterPrime<Y, Z> : Outer<Z> { }
5486

55-
internal class OuterString : Outer<string> { }
87+
internal class OuterString : Outer<string> { }
5688

57-
internal interface IImplementation
58-
{
59-
void ImplMethod0();
60-
void ImplMethod1(int i);
61-
}
89+
internal interface IImplementation
90+
{
91+
void ImplMethod0();
92+
void ImplMethod1(int i);
93+
}
6294

63-
internal class Impl : IImplementation
64-
{
65-
void IImplementation.ImplMethod0() { }
66-
void IImplementation.ImplMethod1(int i) { }
67-
}
95+
internal class Impl : IImplementation
96+
{
97+
void IImplementation.ImplMethod0() { }
98+
void IImplementation.ImplMethod1(int i) { }
99+
}
68100

69-
internal interface IImplementation<T>
70-
{
71-
void ImplMethod0();
72-
void ImplMethod1(T t);
73-
void ImplMethod2<U>(T t, U u, string s);
74-
}
101+
internal interface IImplementation<T>
102+
{
103+
void ImplMethod0();
104+
void ImplMethod1(T t);
105+
void ImplMethod2<U>(T t, U u, string s);
106+
}
75107

76-
internal class Impl<T> : IImplementation<T>
77-
{
78-
void IImplementation<T>.ImplMethod0() { }
79-
void IImplementation<T>.ImplMethod1(T t) { }
80-
void IImplementation<T>.ImplMethod2<U>(T t, U u, string s) { }
81-
}
108+
internal class Impl<T> : IImplementation<T>
109+
{
110+
void IImplementation<T>.ImplMethod0() { }
111+
void IImplementation<T>.ImplMethod1(T t) { }
112+
void IImplementation<T>.ImplMethod2<U>(T t, U u, string s) { }
113+
}
82114

83-
internal class Overloads
84-
{
85-
public void Overload0() { }
86-
public void Overload0(int i) { }
87-
public void Overload0(int i, Overloads c) { }
88-
public unsafe void Overload0(int* p) { }
89-
public void Overload0(dynamic d) { }
90-
public void Overload0<U>(U u) { }
91-
public void Overload0<U>() { }
92-
public void Overload0<U, T>() { }
93-
public void Overload0<U>(U[] u) { }
94-
public void Overload0<U>(U[][] u) { }
95-
public void Overload0<U>(U[,] u) { }
96-
public void Overload0<U>(U[,,] u) { }
97-
public void Overload0<U>(List<int> l) { }
98-
public void Overload0<U>(List<U> l) { }
99-
public void Overload0<U, V>(Tuple<U, V> t0, Tuple<V, U> t1) { }
100-
public void Overload0(Tuple<Tuple<string[,], int>> t0) { }
101-
public void Overload0(Tuple<Tuple<string>, Tuple<int>> t) { }
102-
public void Overload0<U>(Tuple<Tuple<Outer<U>.Inner<U>>> t) { }
115+
internal class Overloads
116+
{
117+
public void Overload0() { }
118+
public void Overload0(int i) { }
119+
public void Overload0(int i, Overloads c) { }
120+
public unsafe void Overload0(int* p) { }
121+
public void Overload0(dynamic d) { }
122+
public void Overload0<U>(U u) { }
123+
public void Overload0<U>() { }
124+
public void Overload0<U, T>() { }
125+
public void Overload0<U>(U[] u) { }
126+
public void Overload0<U>(U[][] u) { }
127+
public void Overload0<U>(U[,] u) { }
128+
public void Overload0<U>(U[,,] u) { }
129+
public void Overload0<U>(List<int> l) { }
130+
public void Overload0<U>(List<U> l) { }
131+
public void Overload0<U, V>(Tuple<U, V> t0, Tuple<V, U> t1) { }
132+
public void Overload0(Tuple<Tuple<string[,], int>> t0) { }
133+
public void Overload0(Tuple<Tuple<string>, Tuple<int>> t) { }
134+
public void Overload0<U>(Tuple<Tuple<Outer<U>.Inner<U>>> t) { }
135+
}
103136
}
137+
138+
#pragma warning restore IDE0161 // Convert to file-scoped namespace
104139
#pragma warning restore IDE0060 // Remove unused parameter
105140
#pragma warning restore CA1822 // Mark members as static

0 commit comments

Comments
 (0)