diff --git a/CODEOWNERS b/CODEOWNERS
index 191fcd19f3..2ff501c43e 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -7,3 +7,8 @@ PublicAPI.Unshipped.txt @nohwnd @MarcoRossignoli
# something that should be treated with care and we should be transparent about it. It also
# requires making sure all telemetry collection systems in place will continue to function well.
TelemetryDataConstants.cs @cvpoienaru @nohwnd
+
+# Changes here might break our contracts with other adapters, and possibly
+# Visual Studio.
+/src/Microsoft.TestPlatform.AdapterUtilities/ @haplois @Evangelink
+/test/Microsoft.TestPlatform.AdapterUtilities.UnitTests/ @haplois @Evangelink
\ No newline at end of file
diff --git a/playground/AdapterUtilitiesPlayground/AdapterUtilitiesPlayground.csproj b/playground/AdapterUtilitiesPlayground/AdapterUtilitiesPlayground.csproj
new file mode 100644
index 0000000000..83a8efa2f2
--- /dev/null
+++ b/playground/AdapterUtilitiesPlayground/AdapterUtilitiesPlayground.csproj
@@ -0,0 +1,32 @@
+
+
+ ..\..\
+
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
diff --git a/playground/AdapterUtilitiesPlayground/FindMethodExtensions.cs b/playground/AdapterUtilitiesPlayground/FindMethodExtensions.cs
new file mode 100644
index 0000000000..ca61906545
--- /dev/null
+++ b/playground/AdapterUtilitiesPlayground/FindMethodExtensions.cs
@@ -0,0 +1,101 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.UnitTests;
+
+[DebuggerStepThrough]
+internal static class FindMethodExtensions
+{
+ private const BindingFlags PrivateBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+
+ internal static MethodInfo? FindMethod(this Type type, string signature)
+ => type.FindMembers(MemberTypes.Method, PrivateBindingFlags,
+ (mbr, sig) => mbr.ToString() == (string?)sig, signature).FirstOrDefault() as MethodInfo;
+
+ internal static IMethodSymbol FindMethod(
+ this INamedTypeSymbol type,
+ string methodName,
+ int methodGenericArity = -1,
+ params ITypeSymbol[] methodParameterTypes)
+ {
+ var candidates = GetCandidateMethods(type, methodName);
+ if (candidates.Any() && !candidates.Skip(1).Any())
+ {
+ return candidates.Single();
+ }
+
+ if (methodGenericArity != -1)
+ {
+ candidates = candidates.Where(m => m.Arity == methodGenericArity);
+ if (candidates.Any() && !candidates.Skip(1).Any())
+ {
+ return candidates.Single();
+ }
+ }
+
+ if (methodParameterTypes != null && methodParameterTypes.Length >= 0)
+ {
+ candidates = candidates.Where(m => m.Parameters.Length == methodParameterTypes.Length);
+ if (candidates.Any() && !candidates.Skip(1).Any())
+ {
+ return candidates.Single();
+ }
+
+ candidates = candidates.Where(m => m.Parameters.Select(p => p.Type).SequenceEqual(methodParameterTypes));
+ }
+
+ Debug.Assert(candidates.Any() && !candidates.Skip(1).Any());
+ return candidates.Single();
+ }
+
+ internal static IMethodSymbol FindMethod(
+ this INamedTypeSymbol type,
+ string methodName,
+ int methodGenericArity,
+ int methodParameterCount,
+ Func selector)
+ {
+ var candidates = GetCandidateMethods(type, methodName);
+ if (candidates.Any() && !candidates.Skip(1).Any())
+ {
+ return candidates.Single();
+ }
+
+ candidates = candidates.Where(m => m.Arity == methodGenericArity);
+ if (candidates.Any() && !candidates.Skip(1).Any())
+ {
+ return candidates.Single();
+ }
+
+ candidates = candidates.Where(m => m.Parameters.Length == methodParameterCount);
+ if (candidates.Any() && !candidates.Skip(1).Any())
+ {
+ return candidates.Single();
+ }
+
+ candidates = candidates.Where(selector);
+
+ Debug.Assert(candidates.Any() && !candidates.Skip(1).Any());
+ return candidates.Single();
+ }
+
+ private static IEnumerable GetCandidateMethods(INamedTypeSymbol type, string methodName)
+ {
+ var candidates = type.GetMembers(methodName).OfType();
+
+ if (type.BaseType != null && type.BaseType.SpecialType != SpecialType.System_Object)
+ {
+ candidates = candidates.Union(GetCandidateMethods(type.BaseType, methodName));
+ }
+
+ return candidates;
+ }
+}
diff --git a/playground/AdapterUtilitiesPlayground/Program.cs b/playground/AdapterUtilitiesPlayground/Program.cs
new file mode 100644
index 0000000000..325e62a4d0
--- /dev/null
+++ b/playground/AdapterUtilitiesPlayground/Program.cs
@@ -0,0 +1,55 @@
+// 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.Reflection;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
+using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.UnitTests;
+
+namespace AdapterUtilitiesPlayground;
+
+internal class Program
+{
+ private const BindingFlags PrivateBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+ private static readonly Compilation _compilation = CSharpCompilation.Create(
+ "Test.dll",
+ new[] { CSharpSyntaxTree.ParseText(File.ReadAllText("TestClasses.cs")) },
+ new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
+
+ static void Main(string[] args)
+ {
+ var derivedClass = typeof(TestClasses.DerivedClass);
+ var baseClass = typeof(TestClasses.BaseClass);
+
+ var derivedMethods = derivedClass.GetMethods(PrivateBindingFlags).ToArray();
+ var baseMethods = baseClass.GetMethods(PrivateBindingFlags).ToArray();
+ var derivedMethod0 = derivedMethods.Single(i => i.Name == "Method0" && i.DeclaringType == derivedClass);
+ var derivedbaseMethod0 = derivedMethods.Single(i => i.Name == "Method0" && i.DeclaringType == baseClass);
+ var baseMethod0 = baseMethods.Single(i => i.Name == "Method0" && i.DeclaringType == baseClass);
+
+ // {
+ // ManagedNameHelper.GetManagedName(derivedMethod0, out var managedType, out var managedMethod, out var hierarchies);
+ // var methodBase = ManagedNameHelper.GetMethod(derivedClass.Assembly, managedType, managedMethod);
+ // }
+ //
+ // {
+ // ManagedNameHelper.GetManagedName(derivedbaseMethod0, out var managedType, out var managedMethod, out var hierarchies);
+ // var methodBase = ManagedNameHelper.GetMethod(derivedClass.Assembly, managedType, managedMethod);
+ // }
+
+ //{
+ // ManagedNameHelper.GetManagedName(baseMethod0, out var managedType, out var managedMethod, out var hierarchies);
+ // var methodBase = ManagedNameHelper.GetMethod(derivedClass.Assembly, managedType, managedMethod);
+ //}
+
+ {
+ var method = typeof(TestClasses.IImplementation).GetMethods(PrivateBindingFlags).SingleOrDefault(i => i.Name == "ImplMethod2")!;
+ method = method.MakeGenericMethod(typeof(int));
+
+ ManagedNameHelper.GetManagedName(method, out var managedType, out var managedMethod, out var hierarchies);
+ var methodBase = ManagedNameHelper.GetMethod(derivedClass.Assembly, managedType, managedMethod);
+ }
+ }
+}
diff --git a/playground/AdapterUtilitiesPlayground/TestClasses.cs b/playground/AdapterUtilitiesPlayground/TestClasses.cs
new file mode 100644
index 0000000000..0e320efe49
--- /dev/null
+++ b/playground/AdapterUtilitiesPlayground/TestClasses.cs
@@ -0,0 +1,117 @@
+// 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;
+using System.Collections.Generic;
+
+namespace TestClasses;
+
+#pragma warning disable IDE0060 // Remove unused parameter
+#pragma warning disable CA1822 // Mark members as static
+
+internal class DerivedClass : BaseClass
+{
+ public new void Method0(int i) { }
+}
+
+internal class BaseClass
+{
+ public void Method0(int i) { }
+ public void Method1(int i) { }
+}
+
+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 Outer
+{
+ public void Method0() { }
+ public void Method1(T t) { }
+ public void Method2(U[] u) { }
+ public void Method3(T t, U u) { }
+
+ internal class Inner
+ {
+ 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 OuterString : Outer { }
+
+internal interface IImplementation
+{
+ void ImplMethod0();
+ void 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 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) { }
+}
+#pragma warning restore IDE0060 // Remove unused parameter
+#pragma warning restore CA1822 // Mark members as static
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Properties/AssemblyInfo.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Properties/AssemblyInfo.cs
index 74ecc9e079..207eba39c9 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Properties/AssemblyInfo.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/Properties/AssemblyInfo.cs
@@ -13,7 +13,7 @@
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft Corporation")]
-[assembly: AssemblyCopyright(" Microsoft Corporation. All rights reserved.")]
+[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyProduct("Microsoft.TestPlatform.Extensions.EventLogCollector")]
[assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en-US")]
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Friends.cs b/src/Microsoft.TestPlatform.AdapterUtilities/Friends.cs
index 8511196751..ba7b78b310 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Friends.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Friends.cs
@@ -3,4 +3,5 @@
using System.Runtime.CompilerServices;
+[assembly: System.Resources.NeutralResourcesLanguage("en-US")]
[assembly: InternalsVisibleTo("Microsoft.TestPlatform.AdapterUtilities.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs b/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs
index c34b772b86..970ceb06c6 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs
@@ -26,16 +26,42 @@ public static class Levels
///
/// Total length of Hierarchy array.
///
- public const int TotalLevelCount = 2;
+ ///
+ /// Currently the order for this is usually:
+ /// Assembly Display Name, Namespace, ClassName, Managed Method Name.
+ ///
+ public const int TotalLevelCount = 4;
+
+ ///
+ /// Index of the test container element of the array.
+ ///
+ ///
+ /// This is usually test asssembly display name.
+ ///
+ public const int ContainerIndex = 0;
///
/// Index of the namespace element of the array.
///
- public const int NamespaceIndex = 0;
+ ///
+ /// This is usually test namespace without class name.
+ ///
+ public const int NamespaceIndex = 1;
///
/// Index of the class element of the array.
///
- public const int ClassIndex = 1;
+ ///
+ /// This is usually test class name without namespace.
+ ///
+ public const int ClassIndex = 2;
+
+ ///
+ /// Index of the test group element of the array.
+ ///
+ ///
+ /// This is usually test method name.
+ ///
+ public const int TestGroupIndex = 3;
}
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs
index ab47587eb3..078aea9b6a 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs
@@ -43,7 +43,7 @@ public static partial class ManagedNameHelper
/// the RFC.
///
public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName)
- => GetManagedName(method, out managedTypeName, out managedMethodName, out _);
+ => GetManagedNameAndHierarchy(method, false, out managedTypeName, out managedMethodName, out _);
///
/// Gets fully qualified managed type and method name from given instance.
@@ -79,20 +79,50 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
/// the RFC.
///
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);
+ }
+
+ ///
+ /// Gets default hierarchy values for a given .
+ ///
+ ///
+ /// A instance to get default hierarchy values.
+ ///
+ ///
+ /// is null.
+ ///
+ ///
+ /// must describe a method.
+ ///
+ ///
+ /// Required functionality on is missing on the current platform.
+ ///
+ ///
+ /// The hierarchy values.
+ ///
+ 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)
{
_ = method ?? throw new ArgumentNullException(nameof(method));
if (!ReflectionHelpers.IsMethod(method))
{
- // TODO: @Haplois, exception expects a message and not a param name.
- throw new NotSupportedException(nameof(method));
+ throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.Resources.ErrorMethodExpectedAsAnArgument, nameof(method)));
}
var semanticType = ReflectionHelpers.GetReflectedType(method)
// TODO: @Haplois, exception expects a message and not a param name.
?? throw new NotSupportedException(nameof(method));
- if (ReflectionHelpers.IsGenericType(semanticType))
+ if (ReflectionHelpers.IsGenericType(semanticType) && !useClosedTypes)
{
// The type might have some of its generic parameters specified, so make
// sure we are working with the open form of the generic type.
@@ -106,7 +136,7 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
method = MethodBase.GetMethodFromHandle(methodHandle, semanticType.TypeHandle)!;
}
- if (method.IsGenericMethod)
+ if (method.IsGenericMethod && !useClosedTypes)
{
// If this method is generic, then convert to the generic method definition
// so that we get the open generic type definitions for parameters.
@@ -117,11 +147,11 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
var methodBuilder = new StringBuilder();
// Namespace and Type Name (with arity designation)
- var hierarchyPos = AppendTypeString(typeBuilder, semanticType, closedType: false);
- if (hierarchyPos is null || hierarchyPos.Length != HierarchyConstants.Levels.TotalLevelCount)
+ // hierarchyPos contains [startIndexOfNamespace, endIndexOfNameSpace, endIndexOfTypeName]
+ var hierarchyPos = AppendTypeString(typeBuilder, semanticType, closedType: useClosedTypes);
+ if (hierarchyPos is null || hierarchyPos.Length != 3)
{
- // TODO: @Haplois, exception expects a message and not a param name.
- throw new NotSupportedException(nameof(method));
+ throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.Resources.ErrorMethodExpectedAsAnArgument, nameof(method)));
}
// Method Name with method arity
@@ -129,8 +159,15 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
AppendMethodString(methodBuilder, method.Name, arity);
if (arity > 0)
{
- methodBuilder.Append('`');
- methodBuilder.Append(arity);
+ if (useClosedTypes)
+ {
+ AppendGenericMethodParameters(methodBuilder, method);
+ }
+ else
+ {
+ methodBuilder.Append('`');
+ methodBuilder.Append(arity);
+ }
}
// Type Parameters
@@ -140,20 +177,23 @@ public static void GetManagedName(MethodBase method, out string managedTypeName,
methodBuilder.Append('(');
foreach (var p in paramList)
{
+ // closedType is always true here by RFC
AppendTypeString(methodBuilder, p.ParameterType, closedType: true);
methodBuilder.Append(',');
}
// Replace the last ',' with ')'
methodBuilder[methodBuilder.Length - 1] = ')';
}
+ var methodNameEndIndex = methodBuilder.Length;
managedTypeName = typeBuilder.ToString();
managedMethodName = methodBuilder.ToString();
- hierarchyValues = new[]
- {
- managedTypeName.Substring(hierarchyPos[0], hierarchyPos[1] - hierarchyPos[0]),
- managedTypeName.Substring(hierarchyPos[1] + 1, hierarchyPos[2] - hierarchyPos[1] - 1),
- };
+
+ 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]);
+ hierarchyValues[HierarchyConstants.Levels.ContainerIndex] = method.DeclaringType.GetTypeInfo().Assembly.GetName().Name;
}
///
@@ -245,7 +285,8 @@ bool Filter(MemberInfo mbr, object? param)
for (int i = 0; i < paramList.Length; i++)
{
- if (GetTypeString(paramList[i].ParameterType, closedType: true) != parameterTypes[i])
+ var parameterType = GetTypeString(paramList[i].ParameterType, closedType: true);
+ if (parameterType != parameterTypes[i])
{
return false;
}
@@ -263,7 +304,12 @@ bool Filter(MemberInfo mbr, object? param)
methods = type.GetRuntimeMethods().Where(m => Filter(m, null)).ToArray();
#endif
- return (MethodInfo?)methods.SingleOrDefault();
+ return (MethodInfo?)(methods.Length switch
+ {
+ 1 => methods[0],
+ > 1 => methods.SingleOrDefault(i => i.DeclaringType == type),
+ _ => null
+ });
}
private static int[]? AppendTypeString(StringBuilder b, Type? type, bool closedType)
@@ -303,11 +349,12 @@ bool Filter(MemberInfo mbr, object? param)
b.Append('.');
- AppendNestedTypeName(b, type);
+ AppendNestedTypeName(b, type, closedType);
if (closedType)
{
AppendGenericTypeParameters(b, type);
}
+
hierarchies[2] = b.Length;
}
@@ -410,7 +457,7 @@ private static void NormalizeAndAppendString(StringBuilder b, string name)
b.Append('\'');
}
- private static int AppendNestedTypeName(StringBuilder b, Type? type)
+ private static int AppendNestedTypeName(StringBuilder b, Type? type, bool closedType)
{
if (type is null)
{
@@ -420,7 +467,7 @@ private static int AppendNestedTypeName(StringBuilder b, Type? type)
var outerArity = 0;
if (type.IsNested)
{
- outerArity = AppendNestedTypeName(b, type.DeclaringType);
+ outerArity = AppendNestedTypeName(b, type.DeclaringType, closedType);
b.Append('+');
}
@@ -451,20 +498,34 @@ private static int AppendNestedTypeName(StringBuilder b, Type? type)
return arity;
}
+ private static void AppendGenericMethodParameters(StringBuilder methodBuilder, MethodBase method)
+ {
+ Type[] genericArguments;
+
+ genericArguments = method.GetGenericArguments();
+
+ AppendGenericArguments(methodBuilder, genericArguments);
+ }
+
private static void AppendGenericTypeParameters(StringBuilder b, Type type)
{
- Type[] genargs;
+ Type[] genericArguments;
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- genargs = type.GetGenericArguments();
+ genericArguments = type.GetGenericArguments();
#else
- genargs = type.GetTypeInfo().GenericTypeArguments;
+ genericArguments = type.GetTypeInfo().GenericTypeArguments;
#endif
- if (genargs.Length != 0)
+ AppendGenericArguments(b, genericArguments);
+ }
+
+ private static void AppendGenericArguments(StringBuilder b, Type[] genericArguments)
+ {
+ if (genericArguments.Length != 0)
{
b.Append('<');
- foreach (var argType in genargs)
+ foreach (var argType in genericArguments)
{
AppendTypeString(b, argType, closedType: true);
b.Append(',');
@@ -476,15 +537,33 @@ private static void AppendGenericTypeParameters(StringBuilder b, Type type)
private static bool IsNormalized(string s)
{
+ var brackets = 0;
+
for (int i = 0; i < s.Length; i++)
{
- if (NeedsEscaping(s[i], i) && s[i] != '.')
+ var c = s[i];
+ if (NeedsEscaping(c, i) && c != '.')
{
+ if (i != 0)
+ {
+ if (c == '<')
+ {
+ brackets++;
+ continue;
+ }
+
+ if (c == '>' && s[i - 1] != '<' && brackets > 0)
+ {
+ brackets--;
+ continue;
+ }
+ }
+
return false;
}
}
- return true;
+ return brackets == 0;
}
private static bool NeedsEscaping(char c, int pos)
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Microsoft.TestPlatform.AdapterUtilities.csproj b/src/Microsoft.TestPlatform.AdapterUtilities/Microsoft.TestPlatform.AdapterUtilities.csproj
index 6c665c18e2..3d4467f0de 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Microsoft.TestPlatform.AdapterUtilities.csproj
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Microsoft.TestPlatform.AdapterUtilities.csproj
@@ -54,7 +54,7 @@
-
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt
index 10e594feff..f8e730f6cf 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/PublicAPI/PublicAPI.Shipped.txt
@@ -1,9 +1,11 @@
#nullable enable
const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.HierarchyLabel = "Hierarchy" -> string!
const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.HierarchyPropertyId = "TestCase.Hierarchy" -> string!
-const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.ClassIndex = 1 -> int
-const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.NamespaceIndex = 0 -> int
-const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.TotalLevelCount = 2 -> int
+const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.ContainerIndex = 0 -> int
+const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.NamespaceIndex = 1 -> int
+const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.ClassIndex = 2 -> int
+const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.TestGroupIndex = 3 -> int
+const Microsoft.TestPlatform.AdapterUtilities.HierarchyConstants.Levels.TotalLevelCount = 4 -> int
const Microsoft.TestPlatform.AdapterUtilities.ManagedNameConstants.ManagedMethodLabel = "ManagedMethod" -> string!
const Microsoft.TestPlatform.AdapterUtilities.ManagedNameConstants.ManagedMethodPropertyId = "TestCase.ManagedMethod" -> string!
const Microsoft.TestPlatform.AdapterUtilities.ManagedNameConstants.ManagedTypeLabel = "ManagedType" -> string!
@@ -18,11 +20,13 @@ Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameParser
Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities.ManagedNameParser.ManagedNameParser() -> void
Microsoft.TestPlatform.AdapterUtilities.TestIdProvider
Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.AppendString(string! str) -> void
+Microsoft.TestPlatform.AdapterUtilities.TestIdProvider.AppendBytes(byte[]! bytes) -> void
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.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/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.Designer.cs
index 0239a66dd2..02775b05ab 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.Designer.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.Designer.cs
@@ -1,157 +1,163 @@
-namespace Microsoft.TestPlatform.AdapterUtilities.Resources
-{
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.TestPlatform.AdapterUtilities.Resources {
+ using System;
using System.Reflection;
-
-
+
+
///
/// A strongly-typed resource class, for looking up localized strings, etc.
///
- internal class Resources
- {
-
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
private static global::System.Resources.ResourceManager resourceMan;
-
+
private static global::System.Globalization.CultureInfo resourceCulture;
-
- internal Resources()
- {
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
}
-
+
///
/// Returns the cached ResourceManager instance used by this class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager
- {
- get
- {
- if (object.ReferenceEquals(resourceMan, null))
- {
-#if NET20 || NET35 || NET40
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.TestPlatform.AdapterUtilities.Resources.Resources", typeof(Resources).Assembly);
-#else
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.TestPlatform.AdapterUtilities.Resources.Resources", typeof(Resources).GetTypeInfo().Assembly);
-#endif
resourceMan = temp;
}
return resourceMan;
}
}
-
+
///
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture
- {
- get
- {
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
return resourceCulture;
}
- set
- {
+ set {
resourceCulture = value;
}
}
-
+
+ ///
+ /// Looks up a localized string similar to Cannot append to a TestIdProvider, after GetId or GetHash method is called..
+ ///
+ internal static string ErrorCannotAppendAfterHashCalculation {
+ get {
+ return ResourceManager.GetString("ErrorCannotAppendAfterHashCalculation", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to ManagedName is incomplete.
///
- internal static string ErrorIncompleteManagedName
- {
- get
- {
+ internal static string ErrorIncompleteManagedName {
+ get {
return ResourceManager.GetString("ErrorIncompleteManagedName", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to Invalid escape sequence! (segment: {0}, pos: {1}).
+ ///
+ internal static string ErrorInvalidSequenceAt {
+ get {
+ return ResourceManager.GetString("ErrorInvalidSequenceAt", resourceCulture);
+ }
+ }
+
///
- /// Looks up a localized string similar to Method arity must be numeric.
+ /// Looks up a localized string similar to Method arity must be numeric..
///
- internal static string ErrorMethodArityMustBeNumeric
- {
- get
- {
+ internal static string ErrorMethodArityMustBeNumeric {
+ get {
return ResourceManager.GetString("ErrorMethodArityMustBeNumeric", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to Argument must be a method. (Argument name: {0}).
+ ///
+ internal static string ErrorMethodExpectedAsAnArgument {
+ get {
+ return ResourceManager.GetString("ErrorMethodExpectedAsAnArgument", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Method '{0}' not found on type '{1}'.
///
- internal static string ErrorMethodNotFound
- {
- get
- {
+ internal static string ErrorMethodNotFound {
+ get {
return ResourceManager.GetString("ErrorMethodNotFound", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to A closing single quote was expected at the end of the segment! (segment: {0}).
+ ///
+ internal static string ErrorNoClosingQuote {
+ get {
+ return ResourceManager.GetString("ErrorNoClosingQuote", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Type '{0}' not found.
///
- internal static string ErrorTypeNotFound
- {
- get
- {
+ internal static string ErrorTypeNotFound {
+ get {
return ResourceManager.GetString("ErrorTypeNotFound", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Unexpected characters after the end of the ManagedName (pos: {0}).
///
- internal static string ErrorUnexpectedCharactersAtEnd
- {
- get
- {
+ internal static string ErrorUnexpectedCharactersAtEnd {
+ get {
return ResourceManager.GetString("ErrorUnexpectedCharactersAtEnd", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Whitespace is not valid in a ManagedName (pos: {0}).
///
- internal static string ErrorWhitespaceNotValid
- {
- get
- {
+ internal static string ErrorWhitespaceNotValid {
+ get {
return ResourceManager.GetString("ErrorWhitespaceNotValid", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to '{0}.{1}' is not implemented on this platform!.
///
- internal static string MethodNotImplementedOnPlatform
- {
- get
- {
+ internal static string MethodNotImplementedOnPlatform {
+ get {
return ResourceManager.GetString("MethodNotImplementedOnPlatform", resourceCulture);
}
}
-
- ///
- /// Looks up a localized string similar to A closing single quote was expected at the end of the segment! (segment: {0}).
- ///
- internal static string ErrorNoClosingQuote
- {
- get
- {
- return ResourceManager.GetString("ErrorNoClosingQuote", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Invalid escape sequence! (segment: {0}, pos: {1})
- ///
- internal static string ErrorInvalidSequenceAt
- {
- get
- {
- return ResourceManager.GetString("ErrorInvalidSequenceAt", resourceCulture);
- }
- }
}
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.resx b/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.resx
index b7a711c5af..04375544a3 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.resx
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Resources/Resources.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Cannot append to a TestIdProvider, after GetId or GetHash method is called.
+
ManagedName is incomplete
@@ -127,6 +130,9 @@
Method arity must be numeric.
+
+ Argument must be a method. (Argument name: {0})
+
Method '{0}' not found on type '{1}'
{0} is the method name, {1} is the full type name.
@@ -153,4 +159,4 @@
Example: 'System.Reflection.MethodBase' is not implemented on this platform!
-
+
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Resources/xlf/Resources.cs.xlf b/src/Microsoft.TestPlatform.AdapterUtilities/Resources/xlf/Resources.cs.xlf
index c08e3a861b..67f26ed9d4 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Resources/xlf/Resources.cs.xlf
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Resources/xlf/Resources.cs.xlf
@@ -49,6 +49,16 @@ Example: 'System.Reflection.MethodBase' is not implemented on this platform!Na konci segmentu se očekávala ukončovací jednoduchá uvozovka. (Segment: {0})
An error thrown when the type name ended but opened single quote was not matched.
+
+
+ Argument must be a method.
+
+
+
+
+ Cannot append to a TestIdProvider, after GetId or GetHash method is called.
+
+