From 08dfb9a66c7a2ca3c43d6a935e3c202cc01c7ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 3 Apr 2026 14:20:14 +0900 Subject: [PATCH 1/4] Revert "Rewrite UTF8 string marshalling test suite with Theory methods and bool-returning native functions (#126366)" This reverts commit 33384068b0585e6474b8930571a6a0f9856acced. --- .../StringMarshalling/UTF8/UTF8Test.cs | 400 ++++++++++-------- .../StringMarshalling/UTF8/UTF8TestNative.cpp | 29 +- 2 files changed, 243 insertions(+), 186 deletions(-) diff --git a/src/tests/Interop/StringMarshalling/UTF8/UTF8Test.cs b/src/tests/Interop/StringMarshalling/UTF8/UTF8Test.cs index 3dfcd9efa2de86..44decce077a698 100644 --- a/src/tests/Interop/StringMarshalling/UTF8/UTF8Test.cs +++ b/src/tests/Interop/StringMarshalling/UTF8/UTF8Test.cs @@ -7,217 +7,281 @@ using System.Collections.Generic; using Xunit; -namespace StringMarshaling +// UTF8 +class UTF8StringTests { - [OuterLoop] - [ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] - public class UTF8Tests + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static extern string StringParameterInOut([In, Out][MarshalAs(UnmanagedType.LPUTF8Str)]string s, int index); + public static void TestInOutStringParameter(string orgString, int index) { - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.LPUTF8Str)] - static extern string StringParameterInOut([In, Out][MarshalAs(UnmanagedType.LPUTF8Str)] string s, int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.LPUTF8Str)] - static extern string StringParameterOut([Out][MarshalAs(UnmanagedType.LPUTF8Str)] string s, int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - static extern void StringParameterRefOut([MarshalAs(UnmanagedType.LPUTF8Str)] out string s, int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool StringParameterRef([MarshalAs(UnmanagedType.LPUTF8Str)] ref string s, int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool StringBuilderParameterInOut([In, Out][MarshalAs(UnmanagedType.LPUTF8Str)] StringBuilder s, int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - static extern void StringBuilderParameterOut([Out][MarshalAs(UnmanagedType.LPUTF8Str)] StringBuilder s, int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.LPUTF8Str, SizeConst = 512)] - static extern StringBuilder StringBuilderParameterReturn(int index); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool TestStructWithUtf8Field(Utf8Struct utfStruct); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - static extern void SetStringInStruct(ref Utf8Struct utfStruct, [MarshalAs(UnmanagedType.LPUTF8Str)] string str); - - [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] - static extern void Utf8DelegateAsParameter(DelegateUTF8Parameter param); + string passedString = orgString; + string expectedNativeString = passedString; - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void DelegateUTF8Parameter([MarshalAs(UnmanagedType.LPUTF8Str)] string utf8String, int index); - - public struct Utf8Struct + string nativeString = StringParameterInOut(passedString, index); + if (!(nativeString == expectedNativeString)) { - [MarshalAs(UnmanagedType.LPUTF8Str)] - public string FirstName; - public int index; + throw new Exception("StringParameterInOut: nativeString != expectedNativeString "); } + } - unsafe struct UnmanagedStruct + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static extern string StringParameterOut([Out][MarshalAs(UnmanagedType.LPUTF8Str)]string s, int index); + public static void TestOutStringParameter(string orgString, int index) + { + string passedString = orgString; + string expectedNativeString = passedString; + string nativeString = StringParameterInOut(passedString, index); + if (!(nativeString == expectedNativeString)) { - public fixed byte psz[8]; + throw new Exception("StringParameterInOut: nativeString != expectedNativeString "); } + } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - struct ManagedStruct + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void StringParameterRefOut([MarshalAs(UnmanagedType.LPUTF8Str)]out string s, int index); + public static void TestStringPassByOut(string orgString, int index) + { + // out string + string expectedNative = string.Empty; + StringParameterRefOut(out expectedNative, index); + if (orgString != expectedNative) { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] - public string str; + throw new Exception("TestStringPassByOut : expectedNative != outString"); } + } - public static readonly string[] Utf8Strings = - { - "Managed", - "S\u00EEne kl\u00E2wen durh die wolken sint geslagen", - "\u0915\u093E\u091A\u0902 \u0936\u0915\u094D\u0928\u094B\u092E\u094D\u092F\u0924\u094D\u0924\u0941\u092E\u094D \u0964 \u0928\u094B\u092A\u0939\u093F\u0928\u0938\u094D\u0924\u093F \u092E\u093E\u092E\u094D", - "\u6211\u80FD\u541E\u4E0B\u73BB\u7483\u800C\u4E0D\u4F24\u8EAB\u4F53", - "\u10E6\u10DB\u10D4\u10E0\u10D7\u10E1\u10D8 \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4, \u10DC\u10E3\u10D7\u10E3 \u10D9\u10D5\u10DA\u10D0 \u10D3\u10D0\u10DB\u10EE\u10E1\u10DC\u10D0\u10E1 \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E1\u10DD\u10E4\u10DA\u10D8\u10E1\u10D0 \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4, \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10E0\u10DD\u10DB\u10D0\u10E1\u10D0, \u10EA\u10D4\u10EA\u10EE\u10DA\u10E1, \u10EC\u10E7\u10D0\u10DA\u10E1\u10D0 \u10D3\u10D0 \u10DB\u10D8\u10EC\u10D0\u10E1\u10D0, \u10F0\u10D0\u10D4\u10E0\u10D7\u10D0 \u10D7\u10D0\u10DC\u10D0 \u10DB\u10E0\u10DD\u10DB\u10D0\u10E1\u10D0; \u10DB\u10DD\u10DB\u10EA\u10DC\u10D4\u10E1 \u10E4\u10E0\u10D7\u10D4\u10DC\u10D8 \u10D3\u10D0 \u10D0\u10E6\u10D5\u10E4\u10E0\u10D8\u10DC\u10D3\u10D4, \u10DB\u10D8\u10D5\u10F0\u10EE\u10D5\u10D3\u10D4 \u10DB\u10D0\u10E1 \u10E9\u10D4\u10DB\u10E1\u10D0 \u10DC\u10D3\u10DD\u10DB\u10D0\u10E1\u10D0, \u10D3\u10E6\u10D8\u10E1\u10D8\u10D7 \u10D3\u10D0 \u10E6\u10D0\u10DB\u10D8\u10D7 \u10D5\u10F0\u10EE\u10D4\u10D3\u10D5\u10D8\u10D3\u10D4 \u10DB\u10D6\u10D8\u10E1\u10D0 \u10D4\u10DA\u10D5\u10D0\u10D7\u10D0 \u10D9\u10E0\u10D7\u10DD\u10DB\u10D0\u10D0\u10E1\u10D0\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,", - "\u03A4\u03B7 \u03B3\u03BB\u03CE\u03C3\u03C3\u03B1 \u03BC\u03BF\u03C5 \u03AD\u03B4\u03C9\u03C3\u03B1\u03BD \u03B5\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AE", - null, - }; - - public static IEnumerable Utf8StringsWithIndex() + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void StringParameterRef([MarshalAs(UnmanagedType.LPUTF8Str)]ref string s, int index); + public static void TestStringPassByRef(string orgString, int index) + { + string orgCopy = new string(orgString.ToCharArray()); + StringParameterRef(ref orgString, index); + if (orgString != orgCopy) { - for (int i = 0; i < Utf8Strings.Length; i++) - yield return [Utf8Strings[i], i]; + throw new Exception("TestStringPassByOut : string mismatch"); } + } - public static IEnumerable NonNullUtf8StringsWithIndex() - { - for (int i = 0; i < Utf8Strings.Length - 1; i++) - yield return [Utf8Strings[i], i]; - } + public static void EmptyStringTest() + { + StringParameterInOut(string.Empty, 0); + StringParameterOut(string.Empty, 0); + } +} - [Theory] - [MemberData(nameof(Utf8StringsWithIndex))] - public static void TestInOutStringParameter(string orgString, int index) - { - string nativeString = StringParameterInOut(orgString, index); - Assert.Equal(orgString, nativeString); - } +// UTF8 stringbuilder +class UTF8StringBuilderTests +{ + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void StringBuilderParameterInOut([In, Out][MarshalAs(UnmanagedType.LPUTF8Str)]StringBuilder s, int index); + public static void TestInOutStringBuilderParameter(string expectedString, int index) + { + StringBuilder nativeStrBuilder = new StringBuilder(expectedString); + StringBuilderParameterInOut(nativeStrBuilder, index); - [Theory] - [MemberData(nameof(Utf8StringsWithIndex))] - public static void TestOutStringParameter(string orgString, int index) + if (!nativeStrBuilder.ToString().Equals(expectedString)) { - string nativeString = StringParameterOut(orgString, index); - Assert.Equal(orgString, nativeString); + throw new Exception("TestInOutStringBuilderParameter: nativeString != expectedNativeString "); } + } - [Theory] - [MemberData(nameof(NonNullUtf8StringsWithIndex))] - public static void TestStringPassByOut(string orgString, int index) - { - StringParameterRefOut(out string result, index); - Assert.Equal(orgString, result); - } + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void StringBuilderParameterOut([Out][MarshalAs(UnmanagedType.LPUTF8Str)]StringBuilder s, int index); + public static void TestOutStringBuilderParameter(string expectedString, int index) + { + // string builder capacity + StringBuilder nativeStringBuilder = new StringBuilder(expectedString.Length); + StringBuilderParameterOut(nativeStringBuilder, index); - [Theory] - [MemberData(nameof(NonNullUtf8StringsWithIndex))] - public static void TestStringPassByRef(string orgString, int index) + if (!nativeStringBuilder.ToString().Equals(expectedString)) { - string copy = new string(orgString.ToCharArray()); - Assert.True(StringParameterRef(ref orgString, index)); - Assert.Equal(copy, orgString); + throw new Exception("TestOutStringBuilderParameter: string != expectedString "); } + } - [Theory] - [MemberData(nameof(NonNullUtf8StringsWithIndex))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/123529", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] - public static void TestInOutStringBuilderParameter(string expectedString, int index) - { - var builder = new StringBuilder(expectedString); - Assert.True(StringBuilderParameterInOut(builder, index)); - Assert.Equal(expectedString, builder.ToString()); - } - [Theory] - [MemberData(nameof(NonNullUtf8StringsWithIndex))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/123529", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] - public static void TestOutStringBuilderParameter(string expectedString, int index) + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.LPUTF8Str,SizeConst = 512)] + public static extern StringBuilder StringBuilderParameterReturn(int index); + public static void TestReturnStringBuilder(string expectedReturn, int index) + { + StringBuilder nativeString = StringBuilderParameterReturn(index); + if (!expectedReturn.Equals(nativeString.ToString())) { - var builder = new StringBuilder(expectedString.Length); - StringBuilderParameterOut(builder, index); - Assert.Equal(expectedString, builder.ToString()); + throw new Exception(string.Format( "TestReturnStringBuilder: nativeString {0} != expectedNativeString {1}",nativeString.ToString(),expectedReturn) ); } + } +} - [Theory] - [MemberData(nameof(NonNullUtf8StringsWithIndex))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/123529", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] - public static void TestReturnStringBuilder(string expectedReturn, int index) - { - StringBuilder nativeString = StringBuilderParameterReturn(index); - Assert.Equal(expectedReturn, nativeString.ToString()); - } +// UTF8 string as struct field +class UTF8StructMarshalling +{ + public struct Utf8Struct + { + [MarshalAs(UnmanagedType.LPUTF8Str)] + public string FirstName; + public int index; + } - [Theory] - [MemberData(nameof(Utf8StringsWithIndex))] - public static void TestStructWithUtf8FieldParameter(string str, int index) - { - var utf8Struct = new Utf8Struct { FirstName = str, index = index }; - Assert.True(TestStructWithUtf8Field(utf8Struct)); - } + unsafe struct UnmanagedStruct + { + public fixed byte psz[8]; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + struct ManagedStruct + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string str; + } + + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void TestStructWithUtf8Field(Utf8Struct utfStruct); - [Fact] - public static void TestSetStringInStruct() + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetStringInStruct(ref Utf8Struct utfStruct, [MarshalAs(UnmanagedType.LPUTF8Str)] string str); + + public static void TestUTF8StructMarshalling(string[] utf8Strings) + { + Utf8Struct utf8Struct = new Utf8Struct(); + for (int i = 0; i < utf8Strings.Length; i++) { - var utf8Struct = new Utf8Struct(); - string testString = "StructTestString\uD83D\uDE00"; - SetStringInStruct(ref utf8Struct, testString); - Assert.Equal(testString, utf8Struct.FirstName); + utf8Struct.FirstName = utf8Strings[i]; + utf8Struct.index = i; + TestStructWithUtf8Field(utf8Struct); } + if (!OperatingSystem.IsWindows()) + CompareWithUTF8Encoding(); + + string testString = "StructTestString\uD83D\uDE00"; - [Fact] - public static void TestUTF8DelegateMarshalling() + SetStringInStruct(ref utf8Struct, testString); + + if (utf8Struct.FirstName != testString) { - Utf8DelegateAsParameter(new DelegateUTF8Parameter(Utf8StringCallback)); + throw new Exception("Incorrect UTF8 string marshalled back from native to managed."); + } + } + + unsafe static void CompareWithUTF8Encoding() + { + // Compare results with UTF8Encoding + UnmanagedStruct ums; + ums.psz[0] = 0xFF; + ums.psz[1] = (byte)'a'; + ums.psz[2] = (byte)'b'; + ums.psz[3] = (byte)'c'; + ums.psz[4] = (byte)'d'; + ums.psz[5] = 0; + + IntPtr ptr = (IntPtr)(&ums); + ManagedStruct ms = Marshal.PtrToStructure(ptr); + string actual = ms.str; + + UTF8Encoding uTF8Encoding = new UTF8Encoding(); + byte [] b = new byte[5]; + b[0] = 0xFF; + b[1] = (byte)'a'; + b[2] = (byte)'b'; + b[3] = (byte)'c'; + b[4] = (byte)'d'; + string expected = uTF8Encoding.GetString(b); + if (actual != expected) + { + Console.WriteLine("Actual:" + actual + " Length:" + actual.Length); + Console.WriteLine("Expected:" + expected + " Length:" + expected.Length); + throw new Exception("UTF8Encoding.GetString doesn't match with Utf8 String Marshaller result"); } + } +} + +// UTF8 string as delegate parameter +class UTF8DelegateMarshalling +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void DelegateUTF8Parameter([MarshalAs(UnmanagedType.LPUTF8Str)]string utf8String, int index); + + + [DllImport("UTF8TestNative", CallingConvention = CallingConvention.Cdecl)] + public static extern void Utf8DelegateAsParameter(DelegateUTF8Parameter param); - [Fact] - public static void TestEmptyString() + + public static void TestUTF8DelegateMarshalling() + { + Utf8DelegateAsParameter(new DelegateUTF8Parameter(Utf8StringCallback)); + } + + public static void Utf8StringCallback(string nativeString, int index) + { + if (string.CompareOrdinal(nativeString, Test.utf8Strings[index]) != 0) { - Assert.Null(StringParameterInOut(string.Empty, 0)); - Assert.Null(StringParameterOut(string.Empty, 0)); + throw new Exception("Utf8StringCallback string do not match"); } + } +} + + +public class Test +{ + //test strings + public static string[] utf8Strings = { + "Managed", + "S\u00EEne kl\u00E2wen durh die wolken sint geslagen" , + "\u0915\u093E\u091A\u0902 \u0936\u0915\u094D\u0928\u094B\u092E\u094D\u092F\u0924\u094D\u0924\u0941\u092E\u094D \u0964 \u0928\u094B\u092A\u0939\u093F\u0928\u0938\u094D\u0924\u093F \u092E\u093E\u092E\u094D", + "\u6211\u80FD\u541E\u4E0B\u73BB\u7483\u800C\u4E0D\u4F24\u8EAB\u4F53", + "\u10E6\u10DB\u10D4\u10E0\u10D7\u10E1\u10D8 \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4, \u10DC\u10E3\u10D7\u10E3 \u10D9\u10D5\u10DA\u10D0 \u10D3\u10D0\u10DB\u10EE\u10E1\u10DC\u10D0\u10E1 \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E1\u10DD\u10E4\u10DA\u10D8\u10E1\u10D0 \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4, \u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10E0\u10DD\u10DB\u10D0\u10E1\u10D0, \u10EA\u10D4\u10EA\u10EE\u10DA\u10E1, \u10EC\u10E7\u10D0\u10DA\u10E1\u10D0 \u10D3\u10D0 \u10DB\u10D8\u10EC\u10D0\u10E1\u10D0, \u10F0\u10D0\u10D4\u10E0\u10D7\u10D0 \u10D7\u10D0\u10DC\u10D0 \u10DB\u10E0\u10DD\u10DB\u10D0\u10E1\u10D0; \u10DB\u10DD\u10DB\u10EA\u10DC\u10D4\u10E1 \u10E4\u10E0\u10D7\u10D4\u10DC\u10D8 \u10D3\u10D0 \u10D0\u10E6\u10D5\u10E4\u10E0\u10D8\u10DC\u10D3\u10D4, \u10DB\u10D8\u10D5\u10F0\u10EE\u10D5\u10D3\u10D4 \u10DB\u10D0\u10E1 \u10E9\u10D4\u10DB\u10E1\u10D0 \u10DC\u10D3\u10DD\u10DB\u10D0\u10E1\u10D0, \u10D3\u10E6\u10D8\u10E1\u10D8\u10D7 \u10D3\u10D0 \u10E6\u10D0\u10DB\u10D8\u10D7 \u10D5\u10F0\u10EE\u10D4\u10D3\u10D5\u10D8\u10D3\u10D4 \u10DB\u10D6\u10D8\u10E1\u10D0 \u10D4\u10DA\u10D5\u10D0\u10D7\u10D0 \u10D9\u10E0\u10D7\u10DD\u10DB\u10D0\u10D0\u10E1\u10D0\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,\u10E8\u10D4\u10DB\u10D5\u10D4\u10D3\u10E0\u10D4,", + "\u03A4\u03B7 \u03B3\u03BB\u03CE\u03C3\u03C3\u03B1 \u03BC\u03BF\u03C5 \u03AD\u03B4\u03C9\u03C3\u03B1\u03BD \u03B5\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AE", + null, + }; + + [Fact] + [OuterLoop] + [ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] + public static void TestEntryPoint() + { + // Test string as [In,Out] parameter + for (int i = 0; i < utf8Strings.Length; i++) + UTF8StringTests.TestInOutStringParameter(utf8Strings[i], i); + + // Test string as [Out] parameter + for (int i = 0; i < utf8Strings.Length; i++) + UTF8StringTests.TestOutStringParameter(utf8Strings[i], i); + + for (int i = 0; i < utf8Strings.Length - 1; i++) + UTF8StringTests.TestStringPassByOut(utf8Strings[i], i); - [Fact] - public static unsafe void CompareWithUTF8Encoding() + for (int i = 0; i < utf8Strings.Length - 1; i++) + UTF8StringTests.TestStringPassByRef(utf8Strings[i], i); + + // https://github.com/dotnet/runtime/issues/123529 + if (!TestLibrary.Utilities.IsNativeAot) { - if (OperatingSystem.IsWindows()) - return; - - UnmanagedStruct ums; - ums.psz[0] = 0xFF; - ums.psz[1] = (byte)'a'; - ums.psz[2] = (byte)'b'; - ums.psz[3] = (byte)'c'; - ums.psz[4] = (byte)'d'; - ums.psz[5] = 0; - - IntPtr ptr = (IntPtr)(&ums); - ManagedStruct ms = Marshal.PtrToStructure(ptr); - string actual = ms.str; - - UTF8Encoding uTF8Encoding = new UTF8Encoding(); - byte[] b = new byte[5]; - b[0] = 0xFF; - b[1] = (byte)'a'; - b[2] = (byte)'b'; - b[3] = (byte)'c'; - b[4] = (byte)'d'; - string expected = uTF8Encoding.GetString(b); - Assert.Equal(expected, actual); + // Test StringBuilder as [In,Out] parameter + for (int i = 0; i < utf8Strings.Length - 1; i++) + UTF8StringBuilderTests.TestInOutStringBuilderParameter(utf8Strings[i], i); + + // Test StringBuilder as [Out] parameter + for (int i = 0; i < utf8Strings.Length - 1; i++) + UTF8StringBuilderTests.TestOutStringBuilderParameter(utf8Strings[i], i); } - static void Utf8StringCallback(string nativeString, int index) + // utf8 string as struct fields + UTF8StructMarshalling.TestUTF8StructMarshalling(utf8Strings); + + // delegate + UTF8DelegateMarshalling.TestUTF8DelegateMarshalling(); + + // https://github.com/dotnet/runtime/issues/123529 + if (!TestLibrary.Utilities.IsNativeAot) { - Assert.Equal(0, string.CompareOrdinal(nativeString, Utf8Strings[index])); + // Test StringBuilder as [Out] parameter + for (int i = 0; i < utf8Strings.Length - 1; i++) + UTF8StringBuilderTests.TestReturnStringBuilder(utf8Strings[i], i); } + + // String.Empty tests + UTF8StringTests.EmptyStringTest(); } } diff --git a/src/tests/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp b/src/tests/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp index 0a535eba7138b4..11eb92075b7c24 100644 --- a/src/tests/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp +++ b/src/tests/Interop/StringMarshalling/UTF8/UTF8TestNative.cpp @@ -43,8 +43,7 @@ char* utf16_to_utf8(const wchar_t *srcstring) if (!nc) { - CoreClrFree(pszUTF8); - return nullptr; + throw; } pszUTF8[nc] = '\0'; @@ -82,9 +81,9 @@ wchar_t* utf8_to_utf16(const char *utf8) if (!nc) { - CoreClrFree(wszTextUTF16); - return nullptr; + throw; } + //MultiByteToWideChar do not null terminate the string when cbMultiByte is not -1 wszTextUTF16[nc] = '\0'; return wszTextUTF16; } @@ -136,11 +135,11 @@ LPSTR build_return_string(const char* pReturn) } // Modify the string builder in place, managed side validates. -extern "C" DLL_EXPORT BOOL __cdecl StringBuilderParameterInOut(/*[In,Out] StringBuilder*/ char *s, int index) +extern "C" DLL_EXPORT void __cdecl StringBuilderParameterInOut(/*[In,Out] StringBuilder*/ char *s, int index) { // if string.empty if (s == 0 || *s == 0) - return TRUE; + return; char *pszTextutf8 = get_utf8_string(index); @@ -151,8 +150,7 @@ extern "C" DLL_EXPORT BOOL __cdecl StringBuilderParameterInOut(/*[In,Out] String if (s[i] != pszTextutf8[i]) { printf("[in] managed string do not match native string\n"); - free_utf8_string(pszTextutf8); - return FALSE; + throw; } } @@ -163,7 +161,6 @@ extern "C" DLL_EXPORT BOOL __cdecl StringBuilderParameterInOut(/*[In,Out] String } s[outLen] = '\0'; free_utf8_string(pszTextutf8); - return TRUE; } //out string builder @@ -212,7 +209,7 @@ typedef struct FieldWithUtf8 }FieldWithUtf8; //utf8 struct field -extern "C" DLL_EXPORT BOOL __cdecl TestStructWithUtf8Field(struct FieldWithUtf8 fieldStruct) +extern "C" DLL_EXPORT void __cdecl TestStructWithUtf8Field(struct FieldWithUtf8 fieldStruct) { char *pszManagedutf8 = fieldStruct.pFirst; int stringIndex = fieldStruct.index; @@ -220,7 +217,7 @@ extern "C" DLL_EXPORT BOOL __cdecl TestStructWithUtf8Field(struct FieldWithUtf8 size_t outLen = 0; if (pszManagedutf8 == 0 || *pszManagedutf8 == 0) - return TRUE; + return; pszNative = get_utf8_string(stringIndex); outLen = strlen(pszNative); @@ -230,12 +227,10 @@ extern "C" DLL_EXPORT BOOL __cdecl TestStructWithUtf8Field(struct FieldWithUtf8 if (pszNative[i] != pszManagedutf8[i]) { printf("Native and managed string do not match.\n"); - free_utf8_string(pszNative); - return FALSE; + throw; } } free_utf8_string(pszNative); - return TRUE; } extern "C" DLL_EXPORT void __cdecl SetStringInStruct(FieldWithUtf8* fieldStruct, char* str) @@ -260,7 +255,7 @@ extern "C" DLL_EXPORT void __cdecl StringParameterRefOut(/*out*/ char **s, int i } //c# ref -extern "C" DLL_EXPORT BOOL __cdecl StringParameterRef(/*ref*/ char **s, int index) +extern "C" DLL_EXPORT void __cdecl StringParameterRef(/*ref*/ char **s, int index) { char *pszTextutf8 = get_utf8_string(index); size_t strLength = strlen(pszTextutf8); @@ -271,8 +266,7 @@ extern "C" DLL_EXPORT BOOL __cdecl StringParameterRef(/*ref*/ char **s, int inde if ((*s)[i] != pszTextutf8[i]) { printf("[in] managed string do not match native string\n"); - free_utf8_string(pszTextutf8); - return FALSE; + throw; } } @@ -285,7 +279,6 @@ extern "C" DLL_EXPORT BOOL __cdecl StringParameterRef(/*ref*/ char **s, int inde memcpy(*s, pszTextutf8, strLength); (*s)[strLength] = '\0'; free_utf8_string(pszTextutf8); - return TRUE; } // delegate test From 6d250d34fef96978bf76d9bdc1a5507b3063405a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 3 Apr 2026 18:13:30 +0900 Subject: [PATCH 2/4] Update SuppressGCTransitionTest.cs --- .../Interop/SuppressGCTransition/SuppressGCTransitionTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs b/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs index 07742123850194..0a041334394962 100644 --- a/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs +++ b/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs @@ -290,11 +290,12 @@ private static int ILStubCache_NoGCTransition_GCTransition(int expected) [ActiveIssue("https://github.com/dotnet/runtime/issues/64127", typeof(PlatformDetection), nameof(PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] [ActiveIssue("https://github.com/dotnet/runtime/issues/70490", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoFULLAOT))] [ActiveIssue("https://github.com/dotnet/runtime/issues/82859", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoMiniJIT), nameof(PlatformDetection.IsArm64Process))] - [ActiveIssue("https://github.com/dotnet/runtimelab/issues/165", typeof(Utilities), nameof(Utilities.IsNativeAot))] [Xunit.SkipOnCoreClrAttribute("Depends on marshalled pinvoke calli", RuntimeTestModes.InterpreterActive)] [Fact] public static void TestEntryPoint() { + throw new Exception(); +#if 0 CheckGCMode.Initialize(&SuppressGCTransitionNative.SetIsInCooperativeModeFunction); int n = 1; @@ -310,5 +311,6 @@ public static void TestEntryPoint() n = NoInline_GCTransition_FunctionPointer(n); n = CallAsFunctionPointer(n); n = ILStubCache_NoGCTransition_GCTransition(n); +#endif } } From e0c9bf1c12471257ddc6fb55289cc7f0b583e24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 3 Apr 2026 18:16:59 +0900 Subject: [PATCH 3/4] Update SuppressGCTransitionTest.cs --- .../SuppressGCTransitionTest.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs b/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs index 0a041334394962..718deaac53ab08 100644 --- a/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs +++ b/src/tests/Interop/SuppressGCTransition/SuppressGCTransitionTest.cs @@ -295,22 +295,5 @@ private static int ILStubCache_NoGCTransition_GCTransition(int expected) public static void TestEntryPoint() { throw new Exception(); -#if 0 - CheckGCMode.Initialize(&SuppressGCTransitionNative.SetIsInCooperativeModeFunction); - - int n = 1; - n = Inline_NoGCTransition(n); - n = Inline_GCTransition(n); - n = NoInline_NoGCTransition(n); - n = NoInline_GCTransition(n); - n = Mixed(n); - n = Mixed_TightLoop(n); - n = Inline_NoGCTransition_FunctionPointer(n); - n = Inline_GCTransition_FunctionPointer(n); - n = NoInline_NoGCTransition_FunctionPointer(n); - n = NoInline_GCTransition_FunctionPointer(n); - n = CallAsFunctionPointer(n); - n = ILStubCache_NoGCTransition_GCTransition(n); -#endif } } From c1f595b771c68bbb247195d919a09f05fa87cca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 3 Apr 2026 21:31:03 +0900 Subject: [PATCH 4/4] Fix SkipOnCoreClr generating unreachable test code in standalone runner SkipOnCoreClrAttribute handling in XUnitWrapperGenerator initialized skippedTestPlatforms, skippedConfigurations, and skippedTestModes to their respective .Any values. When the attribute only specified one dimension (e.g., RuntimeTestModes.InterpreterActive), the others remained at Any. This caused DecorateWithSkipOnCoreClrConfiguration to compute targetPlatform & ~Any = 0, which produced a literal (false) platform condition, making the test body unreachable dead code. The standalone runner would return 100 (pass) without ever executing the test. Fix by initializing the defaults to 0 (meaning 'not specified'), and updating the 'no args given' check accordingly. Now unspecified dimensions correctly mean 'don't skip'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../XUnitWrapperGenerator/XUnitWrapperGenerator.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs index 42f35185e43a28..6ff36425de25b7 100644 --- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs +++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs @@ -843,9 +843,9 @@ private static IEnumerable GetTestMethodInfosForMethod(IMethodSymbol continue; } - Xunit.TestPlatforms skippedTestPlatforms = Xunit.TestPlatforms.Any; - Xunit.RuntimeConfiguration skippedConfigurations = Xunit.RuntimeConfiguration.Any; - Xunit.RuntimeTestModes skippedTestModes = Xunit.RuntimeTestModes.Any; + Xunit.TestPlatforms skippedTestPlatforms = 0; + Xunit.RuntimeConfiguration skippedConfigurations = 0; + Xunit.RuntimeTestModes skippedTestModes = 0; for (int i = 1; i < filterAttribute.ConstructorArguments.Length; i++) { @@ -871,9 +871,9 @@ void ReadSkippedInformationFromSkipOnCoreClrAttributeArgument(TypedConstant argu } } - if (skippedTestModes == Xunit.RuntimeTestModes.Any - && skippedConfigurations == Xunit.RuntimeConfiguration.Any - && skippedTestPlatforms == Xunit.TestPlatforms.Any) + if (skippedTestModes == 0 + && skippedConfigurations == 0 + && skippedTestPlatforms == 0) { testInfos = FilterForSkippedRuntime(testInfos, (int)Xunit.TestRuntimes.CoreCLR, options); }