From b14ac5d5b6173459c2a9a35e3012667bdb6543cd Mon Sep 17 00:00:00 2001 From: Medeni Baykal <433724+Haplois@users.noreply.github.com> Date: Thu, 8 Sep 2022 22:37:30 +0200 Subject: [PATCH] Fix ManagedNameHelper to support namespaceless methods. --- .../ManagedNameHelper.Reflection.cs | 31 +++- .../PublicAPI/PublicAPI.Shipped.txt | 4 +- .../ManagedNameGeneratorTests.cs | 126 ++++++++++++++ .../TestClasses.cs | 159 +++++++++++------- test/TestAssets/CILProject/CILProject.proj | 2 +- 5 files changed, 249 insertions(+), 73 deletions(-) create mode 100644 test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameGeneratorTests.cs diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs index f7c42c9f42..b5d0c32227 100644 --- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs +++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs @@ -78,7 +78,7 @@ public static void GetManagedName(MethodBase method, out string managedTypeName, /// More information about and can be found in /// the RFC. /// - public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName, out string[] hierarchyValues) + public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName, out string?[] hierarchyValues) { GetManagedName(method, out managedTypeName, out managedMethodName); GetManagedNameAndHierarchy(method, true, out _, out _, out hierarchyValues); @@ -102,14 +102,14 @@ public static void GetManagedName(MethodBase method, out string managedTypeName, /// /// The hierarchy values. /// - public static string[] GetManagedHierarchy(MethodBase method) + public static string?[] GetManagedHierarchy(MethodBase method) { GetManagedNameAndHierarchy(method, true, out _, out _, out var hierarchyValues); return hierarchyValues; } - private static void GetManagedNameAndHierarchy(MethodBase method, bool useClosedTypes, out string managedTypeName, out string managedMethodName, out string[] hierarchyValues) + private static void GetManagedNameAndHierarchy(MethodBase method, bool useClosedTypes, out string managedTypeName, out string managedMethodName, out string?[] hierarchyValues) { _ = method ?? throw new ArgumentNullException(nameof(method)); @@ -191,8 +191,16 @@ private static void GetManagedNameAndHierarchy(MethodBase method, bool useClosed hierarchyValues = new string[HierarchyConstants.Levels.TotalLevelCount]; hierarchyValues[HierarchyConstants.Levels.TestGroupIndex] = managedMethodName.Substring(0, methodNameEndIndex); - hierarchyValues[HierarchyConstants.Levels.ClassIndex] = managedTypeName.Substring(hierarchyPos[1] + 1, hierarchyPos[2] - hierarchyPos[1] - 1); - hierarchyValues[HierarchyConstants.Levels.NamespaceIndex] = managedTypeName.Substring(hierarchyPos[0], hierarchyPos[1] - hierarchyPos[0]); + if (hierarchyPos[1] == hierarchyPos[0]) // No namespace + { + hierarchyValues[HierarchyConstants.Levels.ClassIndex] = managedTypeName.Substring(0, hierarchyPos[2]); + hierarchyValues[HierarchyConstants.Levels.NamespaceIndex] = null; + } + else + { + hierarchyValues[HierarchyConstants.Levels.ClassIndex] = managedTypeName.Substring(hierarchyPos[1] + 1, hierarchyPos[2] - hierarchyPos[1] - 1); + hierarchyValues[HierarchyConstants.Levels.NamespaceIndex] = managedTypeName.Substring(hierarchyPos[0], hierarchyPos[1] - hierarchyPos[0]); + } hierarchyValues[HierarchyConstants.Levels.ContainerIndex] = method.DeclaringType?.GetTypeInfo()?.Assembly?.GetName()?.Name ?? string.Empty; } @@ -328,10 +336,17 @@ bool Filter(MemberInfo mbr, object? param) hierarchies = new int[3]; hierarchies[0] = b.Length; - AppendNamespace(b, type.Namespace); - hierarchies[1] = b.Length; + if (type.Namespace != null) + { + AppendNamespace(b, type.Namespace); + hierarchies[1] = b.Length; - b.Append('.'); + b.Append('.'); + } + else + { + hierarchies[1] = hierarchies[0]; + } AppendNestedTypeName(b, type, closedType); if (closedType) diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt index a758469bac..6defd02fc2 100644 --- a/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt @@ -26,8 +26,8 @@ Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.GetHash() -> byte[]! Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.GetId() -> System.Guid Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.TestIdProvider() -> void static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedName(System.Reflection.MethodBase! method, out string! managedTypeName, out string! managedMethodName) -> void -static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedName(System.Reflection.MethodBase! method, out string! managedTypeName, out string! managedMethodName, out string![]! hierarchyValues) -> void -static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedHierarchy(System.Reflection.MethodBase! method) -> string![]! +static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedName(System.Reflection.MethodBase! method, out string! managedTypeName, out string! managedMethodName, out string?[]! hierarchyValues) -> void +static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetManagedHierarchy(System.Reflection.MethodBase! method) -> string?[]! static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameHelper.GetMethod(System.Reflection.Assembly! assembly, string! managedTypeName, string! managedMethodName) -> System.Reflection.MethodBase! static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameParser.ParseManagedMethodName(string! managedMethodName, out string! methodName, out int arity, out string![]? parameterTypes) -> void static Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameParser.ParseManagedTypeName(string! managedTypeName, out string! namespaceName, out string! typeName) -> void diff --git a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameGeneratorTests.cs b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameGeneratorTests.cs new file mode 100644 index 0000000000..01406c4f6f --- /dev/null +++ b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ManagedNameUtilities/ManagedNameGeneratorTests.cs @@ -0,0 +1,126 @@ +// 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.TestTools.UnitTesting; + +namespace Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.UnitTests; + +[TestClass] +public class ManagedNameGeneratorTests +{ + [TestMethod] + public void Namespaceless_ClassMembers_ShouldNotReportANamespace() + { + // Arrange + var methodBase = typeof(global::NamespacelessClass).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName); + + // Assert + Assert.AreEqual("NamespacelessClass", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + } + + [TestMethod] + public void Namespaceless_RecordMembers_ShouldNotReportANamespace() + { + // Arrange + var methodBase = typeof(global::NamespacelessRecord).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName); + + // Assert + Assert.AreEqual("NamespacelessRecord", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + } + + [TestMethod] + public void Namespaceless_InnerClassMembers_ShouldNotReportANamespace() + { + // Arrange + var methodBase = typeof(global::NamespacelessClass.Inner).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName); + + // Assert + Assert.AreEqual("NamespacelessClass+Inner", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + } + + [TestMethod] + public void Namespaceless_InnerRecordMembers_ShouldNotReportANamespace() + { + // Arrange + var methodBase = typeof(global::NamespacelessRecord.Inner).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName); + + // Assert + Assert.AreEqual("NamespacelessRecord+Inner", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + } + + [TestMethod] + public void Namespaceless_ClassMembers_ShouldNotReportANamespace_InHierarchy() + { + // Arrange + var methodBase = typeof(global::NamespacelessClass).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues); + + // Assert + Assert.AreEqual("NamespacelessClass", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]); + } + + [TestMethod] + public void Namespaceless_RecordMembers_ShouldNotReportANamespace_InHierarch() + { + // Arrange + var methodBase = typeof(global::NamespacelessRecord).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues); + + // Assert + Assert.AreEqual("NamespacelessRecord", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]); + } + + [TestMethod] + public void Namespaceless_InnerClassMembers_ShouldNotReportANamespace_InHierarchy() + { + // Arrange + var methodBase = typeof(global::NamespacelessClass.Inner).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues); + + // Assert + Assert.AreEqual("NamespacelessClass+Inner", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]); + } + + [TestMethod] + public void Namespaceless_InnerRecordMembers_ShouldNotReportANamespace_InHierarchy() + { + // Arrange + var methodBase = typeof(global::NamespacelessRecord.Inner).GetMethod("Method0")!; + + // Act + ManagedNameHelper.GetManagedName(methodBase, out var managedTypeName, out var managedMethodName, out var hierarchyValues); + + // Assert + Assert.AreEqual("NamespacelessRecord+Inner", managedTypeName); + Assert.AreEqual("Method0", managedMethodName); + Assert.IsNull(hierarchyValues[HierarchyConstants.Levels.NamespaceIndex]); + } +} diff --git a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/TestClasses.cs b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/TestClasses.cs index 77d0a713ce..44c8f3f1ca 100644 --- a/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/TestClasses.cs +++ b/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/TestClasses.cs @@ -4,11 +4,11 @@ using System; using System.Collections.Generic; -namespace TestClasses; - #pragma warning disable IDE0060 // Remove unused parameter #pragma warning disable CA1822 // Mark members as static -internal class Outer +#pragma warning disable IDE0161 // Convert to file-scoped namespace + +internal class NamespacelessClass { public void Method0() { } public void Method1(int i) { } @@ -23,83 +23,118 @@ public void Method3(int i) { } } } -internal class OuterPrime : Outer { } - -internal class Outer +internal record NamespacelessRecord { public void Method0() { } - public void Method1(T t) { } - public void Method2(U[] u) { } - public void Method3(T t, U u) { } + public void Method1(int i) { } + public void Method2(List ls) { } + public void Method3(string p, int l) { } + internal record Inner + { + public void Method0() { } + public void Method1(int i) { } + public void Method2(int i) { } + public void Method3(int i) { } + } +} + +namespace TestClasses +{ + internal class Outer + { + public void Method0() { } + public void Method1(int i) { } + public void Method2(List ls) { } + public void Method3(string p, int l) { } + internal class Inner + { + public void Method0() { } + public void Method1(int i) { } + public void Method2(int i) { } + public void Method3(int i) { } + } + } + + internal class OuterPrime : Outer { } - internal class Inner + internal class Outer { public void Method0() { } public void Method1(T t) { } - public void Method2(V v) { } - public void Method3(T t, U u, V v) { } - public void Method4(X x, U u) { } - public void Method5(List x, U u) { } + public void Method2(U[] u) { } + public void Method3(T t, U u) { } - internal class MoreInner + internal class Inner { - public void Method0(T t, V v, I i, U u) { } + public void Method0() { } + public void Method1(T t) { } + public void Method2(V v) { } + public void Method3(T t, U u, V v) { } + public void Method4(X x, U u) { } + public void Method5(List x, U u) { } + + internal class MoreInner + { + public void Method0(T t, V v, I i, U u) { } + } } } -} -internal class OuterPrime : Outer { } + internal class OuterPrime : Outer { } -internal class OuterPrime : Outer { } + internal class OuterPrime : Outer { } -internal class OuterString : Outer { } + internal class OuterString : Outer { } -internal interface IImplementation -{ - void ImplMethod0(); - void ImplMethod1(int i); -} + internal interface IImplementation + { + void ImplMethod0(); + void ImplMethod1(int i); + } -internal class Impl : IImplementation -{ - void IImplementation.ImplMethod0() { } - void IImplementation.ImplMethod1(int i) { } -} + internal class Impl : IImplementation + { + void IImplementation.ImplMethod0() { } + void IImplementation.ImplMethod1(int i) { } + } -internal interface IImplementation -{ - void ImplMethod0(); - void ImplMethod1(T t); - void ImplMethod2(T t, U u, string s); -} + internal interface IImplementation + { + void ImplMethod0(); + void ImplMethod1(T t); + void ImplMethod2(T t, U u, string s); + } -internal class Impl : IImplementation -{ - void IImplementation.ImplMethod0() { } - void IImplementation.ImplMethod1(T t) { } - void IImplementation.ImplMethod2(T t, U u, string s) { } -} + internal class Impl : IImplementation + { + void IImplementation.ImplMethod0() { } + void IImplementation.ImplMethod1(T t) { } + void IImplementation.ImplMethod2(T t, U u, string s) { } + } -internal class Overloads -{ - public void Overload0() { } - public void Overload0(int i) { } - public void Overload0(int i, Overloads c) { } - public unsafe void Overload0(int* p) { } - public void Overload0(dynamic d) { } - public void Overload0(U u) { } - public void Overload0() { } - public void Overload0() { } - public void Overload0(U[] u) { } - public void Overload0(U[][] u) { } - public void Overload0(U[,] u) { } - public void Overload0(U[,,] u) { } - public void Overload0(List l) { } - public void Overload0(List l) { } - public void Overload0(Tuple t0, Tuple t1) { } - public void Overload0(Tuple> t0) { } - public void Overload0(Tuple, Tuple> t) { } - public void Overload0(Tuple.Inner>> t) { } + internal class Overloads + { + public void Overload0() { } + public void Overload0(int i) { } + public void Overload0(int i, Overloads c) { } + public unsafe void Overload0(int* p) { } + public void Overload0(dynamic d) { } + public void Overload0(U u) { } + public void Overload0() { } + public void Overload0() { } + public void Overload0(U[] u) { } + public void Overload0(U[][] u) { } + public void Overload0(U[,] u) { } + public void Overload0(U[,,] u) { } + public void Overload0(List l) { } + public void Overload0(List l) { } + public void Overload0(Tuple t0, Tuple t1) { } + public void Overload0(Tuple> t0) { } + public void Overload0(Tuple, Tuple> t) { } + public void Overload0(Tuple.Inner>> t) { } + } } + +#pragma warning restore IDE0161 // Convert to file-scoped namespace #pragma warning restore IDE0060 // Remove unused parameter #pragma warning restore CA1822 // Mark members as static diff --git a/test/TestAssets/CILProject/CILProject.proj b/test/TestAssets/CILProject/CILProject.proj index 9d57990289..ec8caa12cf 100644 --- a/test/TestAssets/CILProject/CILProject.proj +++ b/test/TestAssets/CILProject/CILProject.proj @@ -10,7 +10,7 @@ - net462 + $(NetFrameworkMinimum) bin\$(Configuration) true false