Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed bugs in ManagedMethod parsing, and updated hierarchies. #3704

Merged
merged 5 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TestPlatformRoot Condition="$(TestPlatformRoot) == ''">..\..\</TestPlatformRoot>
</PropertyGroup>
<Import Project="$(TestPlatformRoot)scripts/build/TestPlatform.Settings.targets" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.TestPlatform.AdapterUtilities\Microsoft.TestPlatform.AdapterUtilities.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="$(MicrosoftCodeAnalysisVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisVersion)" />
</ItemGroup>

<ItemGroup>
<Compile Update="TestClasses.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
</ItemGroup>

<Import Project="$(TestPlatformRoot)scripts\build\TestPlatform.targets" />
</Project>
101 changes: 101 additions & 0 deletions playground/AdapterUtilitiesPlayground/FindMethodExtensions.cs
Original file line number Diff line number Diff line change
@@ -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<IMethodSymbol, bool> 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<IMethodSymbol> GetCandidateMethods(INamedTypeSymbol type, string methodName)
{
var candidates = type.GetMembers(methodName).OfType<IMethodSymbol>();

if (type.BaseType != null && type.BaseType.SpecialType != SpecialType.System_Object)
{
candidates = candidates.Union(GetCandidateMethods(type.BaseType, methodName));
}

return candidates;
}
}
55 changes: 55 additions & 0 deletions playground/AdapterUtilitiesPlayground/Program.cs
Original file line number Diff line number Diff line change
@@ -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<string>).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);
}
}
}
117 changes: 117 additions & 0 deletions playground/AdapterUtilitiesPlayground/TestClasses.cs
Original file line number Diff line number Diff line change
@@ -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<string> ls) { }
public void Method3(string p, int l) { }
internal class Inner
{
public void Method0() { }
public void Method1(int i) { }
public void Method2<U>(int i) { }
public void Method3<U, T>(int i) { }
}
}

internal class OuterPrime : Outer { }

internal class Outer<T>
{
public void Method0() { }
public void Method1(T t) { }
public void Method2<U>(U[] u) { }
public void Method3<U>(T t, U u) { }

internal class Inner<V>
{
public void Method0() { }
public void Method1(T t) { }
public void Method2(V v) { }
public void Method3<U>(T t, U u, V v) { }
public void Method4<U, X>(X x, U u) { }
public void Method5<U, X>(List<X> x, U u) { }

internal class MoreInner<I>
{
public void Method0<U>(T t, V v, I i, U u) { }
}
}
}

internal class OuterPrime<Z> : Outer<Z> { }

internal class OuterPrime<Y, Z> : Outer<Z> { }

internal class OuterString : Outer<string> { }

internal interface IImplementation
{
void ImplMethod0();
void ImplMethod1(int i);
}

internal class Impl : IImplementation
{
void IImplementation.ImplMethod0() { }
void IImplementation.ImplMethod1(int i) { }
}

internal interface IImplementation<T>
{
void ImplMethod0();
void ImplMethod1(T t);
void ImplMethod2<U>(T t, U u, string s);
}

internal class Impl<T> : IImplementation<T>
{
void IImplementation<T>.ImplMethod0() { }
void IImplementation<T>.ImplMethod1(T t) { }
void IImplementation<T>.ImplMethod2<U>(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 u) { }
public void Overload0<U>() { }
public void Overload0<U, T>() { }
public void Overload0<U>(U[] u) { }
public void Overload0<U>(U[][] u) { }
public void Overload0<U>(U[,] u) { }
public void Overload0<U>(U[,,] u) { }
public void Overload0<U>(List<int> l) { }
public void Overload0<U>(List<U> l) { }
public void Overload0<U, V>(Tuple<U, V> t0, Tuple<V, U> t1) { }
public void Overload0(Tuple<Tuple<string[,], int>> t0) { }
public void Overload0(Tuple<Tuple<string>, Tuple<int>> t) { }
public void Overload0<U>(Tuple<Tuple<Outer<U>.Inner<U>>> t) { }
}
#pragma warning restore IDE0060 // Remove unused parameter
#pragma warning restore CA1822 // Mark members as static
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.TestPlatform.AdapterUtilities/Friends.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@

using System.Runtime.CompilerServices;

[assembly: System.Resources.NeutralResourcesLanguage("en-US")]
[assembly: InternalsVisibleTo("Microsoft.TestPlatform.AdapterUtilities.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,42 @@ public static class Levels
/// <summary>
/// Total length of Hierarchy array.
/// </summary>
public const int TotalLevelCount = 2;
/// <remarks>
/// Currently the order for this is usually:
/// <c>Assembly Display Name</c>, <c>Namespace</c>, <c>ClassName</c>, <c>Managed Method Name</c>.
/// </remarks>
public const int TotalLevelCount = 4;
Haplois marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Index of the test container element of the array.
/// </summary>
/// <remarks>
/// This is usually test asssembly display name.
/// </remarks>
public const int ContainerIndex = 0;

/// <summary>
/// Index of the namespace element of the array.
/// </summary>
public const int NamespaceIndex = 0;
/// <remarks>
/// This is usually test namespace without class name.
/// </remarks>
public const int NamespaceIndex = 1;

/// <summary>
/// Index of the class element of the array.
/// </summary>
public const int ClassIndex = 1;
/// <remarks>
/// This is usually test class name without namespace.
/// </remarks>
public const int ClassIndex = 2;

/// <summary>
/// Index of the test group element of the array.
/// </summary>
/// <remarks>
/// This is usually test method name.
/// </remarks>
public const int TestGroupIndex = 3;
}
}
Loading