From 52b9353a05890be9302111a8a0eec1cddc719945 Mon Sep 17 00:00:00 2001 From: Hugh Bellamy Date: Sun, 18 Nov 2018 18:31:28 +0000 Subject: [PATCH] Add ICustomMarshaler tests (#19195) * Add ICustomMarshaler tests --- .../Interop/ICustomMarshaler/ICustomMarshaler.cs | 597 +++++++++++++++++++++ .../ICustomMarshaler/ICustomMarshaler.csproj | 34 ++ tests/src/Interop/common/XunitBase.cs | 77 +++ 3 files changed, 708 insertions(+) create mode 100644 tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs create mode 100644 tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj create mode 100644 tests/src/Interop/common/XunitBase.cs diff --git a/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs b/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs new file mode 100644 index 0000000..b07b810 --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs @@ -0,0 +1,597 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.Runtime.InteropServices.Tests +{ + public class ICustomMarshalerTests : XunitBase + { + // To avoid having to create a native test library to reference in tests that + // interact with native libraries, we can use a simple method from the C standard + // library. Unfortunately, the C standard library has different names on Windows + // vs Unix. +#if Windows + public const string LibcLibrary = "msvcrt.dll"; +#else + public const string LibcLibrary = "libc"; +#endif + + [Fact] + public void CustomMarshaler_StringType_Success() + { + int val = 64001; + Assert.Equal(val, MarshalerOnStringTypeMethod(val.ToString())); + } + + public class StringForwardingCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new StringForwardingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi")] + public static extern int MarshalerOnStringTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_ArrayType_Success() + { + int val = 64001; + Assert.Equal(val, MarshalerOnArrayTypeMethod(new string[] { val.ToString() })); + } + + public class ArrayForwardingCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi(((string[])ManagedObj)[0]); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new ArrayForwardingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi")] + public static extern int MarshalerOnArrayTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.Tests.ICustomMarshalerTests+ArrayForwardingCustomMarshaler")] string[] str); + + [Fact] + public void CustomMarshaler_BoxedValueType_Success() + { + int val = 64001; + Assert.Equal(val * 2, MarshalerOnBoxedValueTypeMethod(val)); + } + + public class BoxedValueTypeCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + int unboxedValueType = (int)ManagedObj * 2; + return Marshal.StringToCoTaskMemAnsi(unboxedValueType.ToString()); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new BoxedValueTypeCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi")] + public static extern int MarshalerOnBoxedValueTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] object i); + + [Fact] + public void Parameter_CustomMarshalerProvidedOnClassType_ForwardsCorrectly() + { + int val = 64001; + Assert.Equal((val * 2).ToString(), MarshalerOnClassTypeMethod(new StringContainer { Value = val.ToString() }).Value); + } + + public class StringContainer + { + public string Value { get; set; } + } + + public class ClassForwardingCustomMarshaler : ICustomMarshaler + { + private bool CleanedString { get; set; } + + public void CleanUpManagedData(object ManagedObj) {} + + public void CleanUpNativeData(IntPtr pNativeData) + { + if (CleanedString) + { + return; + } + + Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); + CleanedString = true; + } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + return Marshal.StringToCoTaskMemAnsi(((StringContainer)ManagedObj).Value); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + int doubleValue = pNativeData.ToInt32() * 2; + return new StringContainer { Value = doubleValue.ToString() }; + } + + public static ICustomMarshaler GetInstance(string cookie) => new ClassForwardingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ClassForwardingCustomMarshaler))] + public static extern StringContainer MarshalerOnClassTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ClassForwardingCustomMarshaler))] StringContainer str); + + [Fact] + public void Parameter_CustomMarshalerProvided_CallsMethodsInCorrectOrdering() + { + Assert.Empty(OrderTrackingCustomMarshaler.Events); + + string val1 = "64001"; + Assert.Equal(val1, OrderTrackingMethod(val1)); + + string[] expectedOrderingFirstCall = new string[] + { + "Called GetInstance", + "Called MarshalManagedToNative", + "Called MarshalNativeToManaged", + "Called CleanUpNativeData" + }; + Assert.Equal(expectedOrderingFirstCall, OrderTrackingCustomMarshaler.Events); + + // GetInstance is only called once. + string val2 = "234"; + Assert.Equal(val2, OrderTrackingMethod(val2)); + IEnumerable expectedOrderingSecondCall = expectedOrderingFirstCall.Concat(new string[] + { + "Called MarshalManagedToNative", + "Called MarshalNativeToManaged", + "Called CleanUpNativeData" + }); + Assert.Equal(expectedOrderingSecondCall, OrderTrackingCustomMarshaler.Events); + } + + // This should only be used *once*, as it uses static state. + public class OrderTrackingCustomMarshaler : ICustomMarshaler + { + public static List Events { get; } = new List(); + public static IntPtr MarshaledNativeData { get; set; } + + public void CleanUpManagedData(object ManagedObj) + { + Events.Add("Called CleanUpManagedData"); + } + + public void CleanUpNativeData(IntPtr pNativeData) + { + Assert.Equal(MarshaledNativeData, pNativeData); + Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); + + Events.Add("Called CleanUpNativeData"); + } + + public int GetNativeDataSize() + { + Events.Add("Called GetNativeDataSize"); + return 0; + } + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + Events.Add("Called MarshalManagedToNative"); + MarshaledNativeData = Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + return MarshaledNativeData; + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + Events.Add("Called MarshalNativeToManaged"); + return pNativeData.ToInt32().ToString(); + } + + public static ICustomMarshaler GetInstance(string cookie) + { + Assert.Empty(cookie); + Events.Add("Called GetInstance"); + return new OrderTrackingCustomMarshaler(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] + public static extern string OrderTrackingMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_BothMarshalTypeRefAndMarshalTypeProvided_PicksMarshalType() + { + Assert.Equal(2, BothTypeRefAndTypeMethod("64001")); + } + + public class OverridingCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("2"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new OverridingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int BothTypeRefAndTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.Tests.ICustomMarshalerTests+OverridingCustomMarshaler", MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] string str); + + [Fact] + public void Parameter_CookieProvided_PassesCookieToGetInstance() + { + int val = 64001; + Assert.Equal(val, CustomCookieMethod(val.ToString())); + Assert.Equal("Cookie", CookieTrackingCustomMarshaler.Cookie); + } + + public class CookieTrackingCustomMarshaler : ICustomMarshaler + { + public static string Cookie { get; set; } + + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) + { + Cookie = cookie; + return new CookieTrackingCustomMarshaler(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int CustomCookieMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CookieTrackingCustomMarshaler), MarshalCookie = "Cookie")] string str); + + [Fact] + public void Parameter_NotCustomMarshalerType_UsesSpecifiedMarshaler() + { + int val = 64001; + Assert.Equal(val, NonCustomMarshalerTypeMethod(val.ToString())); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NonCustomMarshalerTypeMethod([MarshalAs(UnmanagedType.LPStr, MarshalTypeRef = typeof(OverridingCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_Generic_Success() + { + Assert.Equal(234, GenericGetInstanceCustomMarshalerMethod("64001")); + } + + public class GenericCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) + { + return new GenericCustomMarshaler(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int GenericGetInstanceCustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GenericCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_ValueTypeWithStringType_Success() + { + Assert.Equal(234, ValueTypeMarshalerOnStringTypeMethod("64001")); + } + + public struct CustomMarshalerValueType : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) + { + return new CustomMarshalerValueType(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ValueTypeMarshalerOnStringTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CustomMarshalerValueType))] string str); + + [Fact] + public void Parameter_MarshalerOnValueType_ThrowsMarshalDirectiveException() + { + Assert.Throws(() => MarshalerOnValueTypeMethod(0)); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int MarshalerOnValueTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] int str); + + [Fact] + public unsafe void Parameter_MarshalerOnPointer_ThrowsMarshalDirectiveException() + { + Assert.Throws(() => MarshalerOnPointerMethod(null)); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static unsafe extern int MarshalerOnPointerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] int* str); + + [Fact] + public void Parameter_NullICustomMarshaler_ThrowsTypeLoadException() + { + Assert.Throws(() => NullCustomMarshalerMethod("")); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NullCustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = null)] string str); + + [Fact] + public void Parameter_NotICustomMarshaler_ThrowsApplicationException() + { + Assert.Throws(() => NonICustomMarshalerMethod("")); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NonICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(string))] string str); + + [Fact] + public void Parameter_OpenGenericICustomMarshaler_ThrowsTypeLoadException() + { + Assert.Throws(() => OpenGenericICustomMarshalerMethod("")); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int OpenGenericICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GenericCustomMarshaler<>))] string str); + + [Fact] + public void Parameter_GetInstanceMethodDoesntExist_ThrowsApplicationException() + { + Assert.Throws(() => NoGetInstanceMethod("")); + } + + public class NoGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NoGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NoGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodInstanceMethod_ThrowsApplicationException() + { + Assert.Throws(() => InstanceGetInstanceMethod("")); + } + + public class InstanceGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + public ICustomMarshaler GetInstance(string cookie) => new InstanceGetInstanceCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int InstanceGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(InstanceGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodNoParameters_ThrowsApplicationException() + { + Assert.Throws(() => NoParametersGetInstanceMethod("")); + } + + public class NoParameterGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance() => new NoParameterGetInstanceCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NoParametersGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NoParameterGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodNonStringParameter_ThrowsApplicationException() + { + Assert.Throws(() => NonStringGetInstanceMethod("")); + } + + public class NonStringGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(int x) => new NonStringGetInstanceCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NonStringGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NonStringGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodReturnsVoid_ThrowsApplicationException() + { + Assert.Throws(() => VoidGetInstanceMethod("")); + } + + public class VoidGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static void GetInstance(string cookie) { } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int VoidGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(VoidGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodReturnsNull_ThrowsApplicationException() + { + Assert.Throws(() => NullGetInstanceMethod("")); + } + + public class NullGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => null; + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NullGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodThrows_ThrowsActualException() + { + Assert.Throws(() => ThrowingGetInstanceMethod("")); + } + + public class ThrowingGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => throw new NotImplementedException(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ThrowingGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_MarshalManagedToNativeThrows_ThrowsActualException() + { + Assert.Throws(() => ThrowingMarshalManagedToNativeMethod("")); + } + + public class ThrowingMarshalManagedToNativeCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => throw new NotImplementedException(); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new ThrowingMarshalManagedToNativeCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ThrowingMarshalManagedToNativeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingMarshalManagedToNativeCustomMarshaler))] string str); + + [Fact] + public void Parameter_CleanUpNativeDataMethodThrows_ThrowsActualException() + { + Assert.Throws(() => ThrowingCleanUpNativeDataMethod("")); + } + + public class ThrowingCleanUpNativeDataCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException(); + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new ThrowingMarshalManagedToNativeCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ThrowingCleanUpNativeDataMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingCleanUpNativeDataCustomMarshaler))] string str); + + [Fact] + public static void Field_ParentIsStruct_ThrowsTypeLoadException() + { + Assert.Throws(() => StructWithCustomMarshalerFieldMethod(new StructWithCustomMarshalerField())); + } + + public struct StructWithCustomMarshalerField + { + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] + public string Field; + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int StructWithCustomMarshalerFieldMethod(StructWithCustomMarshalerField c); + + public static int Main(String[] args) + { + return new ICustomMarshalerTests().RunTests(); + } + } +} diff --git a/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj b/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj new file mode 100644 index 0000000..ae3ef0e --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj @@ -0,0 +1,34 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + $(DefineConstants);Windows + + + + + + + False + + + + + + + + + + + + + + diff --git a/tests/src/Interop/common/XunitBase.cs b/tests/src/Interop/common/XunitBase.cs new file mode 100644 index 0000000..06341e7 --- /dev/null +++ b/tests/src/Interop/common/XunitBase.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using Xunit.Runners; + +namespace Xunit +{ + public abstract class XunitBase + { + private static object consoleLock = new object(); + + private static ManualResetEvent finished = new ManualResetEvent(false); + + private static int result = 100; + + public int RunTests() + { + var runner = AssemblyRunner.WithoutAppDomain(GetType().Assembly.Location); + runner.OnDiscoveryComplete = OnDiscoveryComplete; + runner.OnExecutionComplete = OnExecutionComplete; + runner.OnTestFailed = OnTestFailed; + runner.OnTestSkipped = OnTestSkipped; + + Console.WriteLine("Discovering..."); + + runner.Start(); + + finished.WaitOne(); + finished.Dispose(); + + return result; + } + + private static void OnDiscoveryComplete(DiscoveryCompleteInfo info) + { + lock (consoleLock) + Console.WriteLine($"Running {info.TestCasesToRun} of {info.TestCasesDiscovered} tests..."); + } + + private static void OnExecutionComplete(ExecutionCompleteInfo info) + { + lock (consoleLock) + Console.WriteLine($"Finished: {info.TotalTests} tests in {Math.Round(info.ExecutionTime, 3)}s ({info.TestsFailed} failed, {info.TestsSkipped} skipped)"); + + finished.Set(); + } + + private static void OnTestFailed(TestFailedInfo info) + { + lock (consoleLock) + { + Console.ForegroundColor = ConsoleColor.Red; + + Console.WriteLine("[FAIL] {0}: {1}", info.TestDisplayName, info.ExceptionMessage); + if (info.ExceptionStackTrace != null) + Console.WriteLine(info.ExceptionStackTrace); + + Console.ResetColor(); + } + + result = 101; + } + + private static void OnTestSkipped(TestSkippedInfo info) + { + lock (consoleLock) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("[SKIP] {0}: {1}", info.TestDisplayName, info.SkipReason); + Console.ResetColor(); + } + } + } +} -- 2.7.4