Skip to content
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
2 changes: 1 addition & 1 deletion src/Common/tests/TestUtilities/AppContextSwitchScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ public static bool GetDefaultValueForSwitchInAssembly(string switchName, string
Type type = Type.GetType($"{typeName}, {assemblyName}")
?? throw new InvalidOperationException($"Could not find {typeName} type in {assemblyName} assembly.");

return type.TestAccessor().Dynamic.GetSwitchDefaultValue(switchName);
return type.TestAccessor.Dynamic.GetSwitchDefaultValue(switchName);
}
}
4 changes: 2 additions & 2 deletions src/Common/tests/TestUtilities/ITestAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ public interface ITestAccessor
///
/// public int InternalGetDirectoryNameOffset(ReadOnlySpan<char> path)
/// {
/// var accessor = typeof(System.IO.Path).TestAccessor();
/// var accessor = typeof(System.IO.Path).TestAccessor;
/// return accessor.CreateDelegate<GetDirectoryNameOffset>()(@"C:\Foo");
/// }
///
/// // Without ref structs you can just use Func/Action
/// var accessor = typeof(Color).TestAccessor();
/// var accessor = typeof(Color).TestAccessor;
/// bool result = accessor.CreateDelegate<Func<KnownColor, bool>>("IsKnownColorSystem")(KnownColor.Window);
/// ]]>
/// </example>
Expand Down
87 changes: 52 additions & 35 deletions src/Common/tests/TestUtilities/TestAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Dynamic;
using System.Reflection;
using System.Runtime.ExceptionServices;

namespace System;

Expand Down Expand Up @@ -63,7 +64,7 @@ public TDelegate CreateDelegate<TDelegate>(string? methodName = null)
{
Type type = typeof(TDelegate);
MethodInfo? invokeMethodInfo = type.GetMethod("Invoke");
Type[] types = invokeMethodInfo is null ? [] : invokeMethodInfo.GetParameters().Select(pi => pi.ParameterType).ToArray();
Type[] types = invokeMethodInfo is null ? [] : [.. invokeMethodInfo.GetParameters().Select(pi => pi.ParameterType)];

// To make it easier to write a class wrapper with a number of delegates,
// we'll take the name from the delegate itself when unspecified.
Expand All @@ -86,15 +87,15 @@ private sealed class DynamicWrapper : DynamicObject
{
private readonly object? _instance;

public DynamicWrapper(object? instance)
=> _instance = instance;
public DynamicWrapper(object? instance) => _instance = instance;

public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args, out object? result)
{
result = null;
ArgumentNullException.ThrowIfNull(args);
ArgumentNullException.ThrowIfNull(binder);

result = null;

MethodInfo? methodInfo = null;
Type? type = s_type;

Expand All @@ -115,7 +116,7 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args,
binder.Name,
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
binder: null,
args.Select(a => a!.GetType()).ToArray(),
[.. args.Select(a => a!.GetType())],
modifiers: null);
}

Expand All @@ -131,7 +132,9 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args,
while (true);

if (methodInfo is null)
{
return false;
}

try
{
Expand All @@ -140,31 +143,68 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args,
catch (TargetInvocationException ex) when (ex.InnerException is not null)
{
// Unwrap the inner exception to make it easier for callers to handle.
throw ex.InnerException;
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

return true;
}

public override bool TrySetMember(SetMemberBinder binder, object? value)
{
MemberInfo? info = TestAccessor<T>.DynamicWrapper.GetFieldOrPropertyInfo(binder.Name);
if (info is null)
MemberInfo? memberInfo = TestAccessor<T>.DynamicWrapper.GetFieldOrPropertyInfo(binder.Name);
if (memberInfo is null)
{
return false;
}

try
{
switch (memberInfo)
{
case FieldInfo fieldInfo:
fieldInfo.SetValue(_instance, value);
break;
case PropertyInfo propertyInfo:
propertyInfo.SetValue(_instance, value);
break;
default:
throw new InvalidOperationException();
}
}
catch (TargetInvocationException ex) when (ex.InnerException is not null)
{
// Unwrap the inner exception to make it easier for callers to handle.
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

SetValue(info, value);
return true;
}

public override bool TryGetMember(GetMemberBinder binder, out object? result)
{
result = null;

MemberInfo? info = TestAccessor<T>.DynamicWrapper.GetFieldOrPropertyInfo(binder.Name);
if (info is null)
MemberInfo? memberInfo = TestAccessor<T>.DynamicWrapper.GetFieldOrPropertyInfo(binder.Name);
if (memberInfo is null)
{
return false;
}

try
{
result = memberInfo switch
{
FieldInfo fieldInfo => fieldInfo.GetValue(_instance),
PropertyInfo propertyInfo => propertyInfo.GetValue(_instance),
_ => throw new InvalidOperationException()
};
}
catch (TargetInvocationException ex) when (ex.InnerException is not null)
{
// Unwrap the inner exception to make it easier for callers to handle.
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

result = GetValue(info);
return true;
}

Expand Down Expand Up @@ -195,28 +235,5 @@ public override bool TryGetMember(GetMemberBinder binder, out object? result)

return info;
}

private object? GetValue(MemberInfo memberInfo)
=> memberInfo switch
{
FieldInfo fieldInfo => fieldInfo.GetValue(_instance),
PropertyInfo propertyInfo => propertyInfo.GetValue(_instance),
_ => throw new InvalidOperationException()
};

private void SetValue(MemberInfo memberInfo, object? value)
{
switch (memberInfo)
{
case FieldInfo fieldInfo:
fieldInfo.SetValue(_instance, value);
break;
case PropertyInfo propertyInfo:
propertyInfo.SetValue(_instance, value);
break;
default:
throw new InvalidOperationException();
}
}
}
}
86 changes: 43 additions & 43 deletions src/Common/tests/TestUtilities/TestAccessors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,53 @@ public static partial class TestAccessors
// the array here.
private static readonly object?[] s_nullObjectParam = [null];

/// <summary>
/// Extension that creates a generic internals test accessor for a
/// given instance or Type class (if only accessing statics).
/// </summary>
/// <param name="instanceOrType">
/// Instance or Type class (if only accessing statics).
/// </param>
/// <remarks>
/// <para>
/// Use <see cref="ITestAccessor.CreateDelegate">CreateDelegate</see> to deal with methods that take spans or
/// other ref structs. For other members, use the dynamic accessor:
/// </para>
/// <code>
/// <![CDATA[
/// Version version = new Version(4, 1);
/// Assert.Equal(4, version.TestAccessor().Dynamic._Major));
///
/// // Or
///
/// dynamic accessor = version.TestAccessor().Dynamic;
/// Assert.Equal(4, accessor._Major));
///
/// // Or
///
/// Version version2 = new Version("4.1");
/// dynamic accessor = typeof(Version).TestAccessor().Dynamic;
/// Assert.Equal(version2, accessor.Parse("4.1")));
/// ]]>
/// </code>
/// <para>
/// When attempting to get nested private types that are generic (nested types in a generic type
/// are always generic, and inherit the type specifiers of the the parent type), use the extension
/// <see cref="ReflectionHelper.GetFullNestedType(Type, string, Span{Type})"/> to get a fully
/// instantiated type for the nested type, then pass that Type to this method.
/// </para>
/// </remarks>
public static ITestAccessor TestAccessor(this object instanceOrType)
extension(object instanceOrType)
{
ITestAccessor? testAccessor = instanceOrType is Type type
? (ITestAccessor?)Activator.CreateInstance(
typeof(TestAccessor<>).MakeGenericType(type),
s_nullObjectParam)
: (ITestAccessor?)Activator.CreateInstance(
typeof(TestAccessor<>).MakeGenericType(instanceOrType.GetType()),
instanceOrType);
/// <summary>
/// Extension that creates a generic internals test accessor for a
/// given instance or Type class (if only accessing statics).
/// </summary>
/// <remarks>
/// <para>
/// Use <see cref="ITestAccessor.CreateDelegate">CreateDelegate</see> to deal with methods that take spans or
/// other ref structs. For other members, use the dynamic accessor:
/// </para>
/// <code>
/// <![CDATA[
/// Version version = new Version(4, 1);
/// Assert.Equal(4, version.TestAccessor.Dynamic._Major));
///
/// // Or
///
/// dynamic accessor = version.TestAccessor.Dynamic;
/// Assert.Equal(4, accessor._Major));
///
/// // Or
///
/// Version version2 = new Version("4.1");
/// dynamic accessor = typeof(Version).TestAccessor.Dynamic;
/// Assert.Equal(version2, accessor.Parse("4.1")));
/// ]]>
/// </code>
/// </remarks>
public ITestAccessor TestAccessor
{
get
{
ITestAccessor? testAccessor = instanceOrType is Type type
? (ITestAccessor?)Activator.CreateInstance(
typeof(TestAccessor<>).MakeGenericType(type),
s_nullObjectParam)
: (ITestAccessor?)Activator.CreateInstance(
typeof(TestAccessor<>).MakeGenericType(instanceOrType.GetType()),
instanceOrType);

return testAccessor
?? throw new ArgumentException("Cannot create TestAccessor for Nullable<T> instances with no value.");
return testAccessor
?? throw new ArgumentException("Cannot create TestAccessor for Nullable<T> instances with no value.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ Namespace Microsoft.VisualBasic.Forms.Tests
listener.TraceOutputOptions = TraceOptions.Callstack
listener.TraceOutputOptions.Should.Be(TraceOptions.Callstack)

CStr(listener.TestAccessor().Dynamic.HostName).Should.NotBeEmpty()
CStr(TestAccessors.get_TestAccessor(listener).Dynamic.HostName).Should.NotBeEmpty()

Dim listenerStream As FileLogTraceListener.ReferencedStream = CType(listener.TestAccessor().Dynamic.ListenerStream, FileLogTraceListener.ReferencedStream)
Dim listenerStream As FileLogTraceListener.ReferencedStream = CType(TestAccessors.get_TestAccessor(listener).Dynamic.ListenerStream, FileLogTraceListener.ReferencedStream)
listenerStream.Should.NotBeNull()
listenerStream.IsInUse.Should.BeTrue()
listenerStream.FileSize.Should.Be(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ Namespace Microsoft.VisualBasic.Forms.Tests
Dim xPos As Integer = -1
Dim yPos As Integer = -1
Dim inputHandler As New InputBoxHandler(prompt, title, defaultResponse, xPos, yPos, ParentWindow:=Nothing)
CType(inputHandler.TestAccessor.Dynamic()._prompt, String).Should.Be(prompt)
CType(inputHandler.TestAccessor.Dynamic()._title, String).Should.Be(title)
CType(inputHandler.TestAccessor.Dynamic()._defaultResponse, String).Should.Be(defaultResponse)
CType(inputHandler.TestAccessor.Dynamic()._xPos, String).Should.Be(xPos)
CType(inputHandler.TestAccessor.Dynamic()._yPos, String).Should.Be(yPos)
CType(inputHandler.TestAccessor.Dynamic()._parentWindow, IWin32Window).Should.Be(Nothing)
CType(TestAccessors.get_TestAccessor(inputHandler).Dynamic()._prompt, String).Should.Be(prompt)
CType(TestAccessors.get_TestAccessor(inputHandler).Dynamic()._title, String).Should.Be(title)
CType(TestAccessors.get_TestAccessor(inputHandler).Dynamic()._defaultResponse, String).Should.Be(defaultResponse)
CType(TestAccessors.get_TestAccessor(inputHandler).Dynamic()._xPos, String).Should.Be(xPos)
CType(TestAccessors.get_TestAccessor(inputHandler).Dynamic()._yPos, String).Should.Be(yPos)
CType(TestAccessors.get_TestAccessor(inputHandler).Dynamic()._parentWindow, IWin32Window).Should.Be(Nothing)
inputHandler.Exception.Should.BeNull()
inputHandler.Result.Should.BeNull()
End Sub
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private static dynamic GetTestHelper()
{
var assembly = typeof(WindowsFormsApplicationBase).Assembly;
var type = assembly.GetType("Microsoft.VisualBasic.ApplicationServices.SingleInstanceHelpers");
return type.TestAccessor().Dynamic;
return type.TestAccessor.Dynamic;
}

private bool TryCreatePipeServer(string pipeName, out NamedPipeServerStream pipeServer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class WindowsFormsApplicationBaseTests
{
private static string GetAppID(Assembly assembly)
{
var testAccessor = typeof(WindowsFormsApplicationBase).TestAccessor();
var testAccessor = typeof(WindowsFormsApplicationBase).TestAccessor;
return testAccessor.Dynamic.GetApplicationInstanceID(assembly);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void ReadStringFromHGLOBAL_InvalidHGLOBAL_Throws(bool unicode)

Action action = () =>
{
string result = type.TestAccessor().Dynamic.ReadStringFromHGLOBAL(HGLOBAL.Null, unicode);
string result = type.TestAccessor.Dynamic.ReadStringFromHGLOBAL(HGLOBAL.Null, unicode);
};

action.Should().Throw<Win32Exception>().And.HResult.Should().Be((int)HRESULT.E_FAIL);
Expand All @@ -145,7 +145,7 @@ public void ReadStringFromHGLOBAL_NoTerminator_ReturnsEmptyString(bool unicode)
span.Fill(0x20);
}

string result = type.TestAccessor().Dynamic.ReadStringFromHGLOBAL(global, unicode);
string result = type.TestAccessor.Dynamic.ReadStringFromHGLOBAL(global, unicode);
result.Should().BeEmpty();
}
finally
Expand Down Expand Up @@ -173,7 +173,7 @@ public void ReadStringFromHGLOBAL_Terminator_ReturnsString(bool unicode)
span[..^2].Fill(0x20);
}

string result = type.TestAccessor().Dynamic.ReadStringFromHGLOBAL(global, unicode);
string result = type.TestAccessor.Dynamic.ReadStringFromHGLOBAL(global, unicode);
result.Should().NotBeEmpty();
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace System.Windows.Forms.Analyzers.Tests;
public class ProjectFileReaderTests
{
public static readonly char s_separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator[0];
private static readonly dynamic s_static = typeof(ProjectFileReader).TestAccessor().Dynamic;
private static readonly dynamic s_static = typeof(ProjectFileReader).TestAccessor.Dynamic;
private readonly ITestOutputHelper _output;

private static bool TryReadBool(AnalyzerConfigOptionsProvider configOptions, string propertyName, bool defaultValue, out bool value, out Diagnostic? diagnostic)
Expand Down
Loading