using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WhiteMagic; namespace WhiteMagicTest; /// /// Tests for : blittable size, marshal-required flag, /// and computed-once behavior. /// public class MarshalCacheTests { [Fact] public void Size_for_int_is_4() { Assert.Equal(4, MarshalCache.Size); } [Fact] public void Size_for_byte_is_1() { Assert.Equal(1, MarshalCache.Size); } [Fact] public void Size_for_IntPtr_matches_native_pointer_size() { Assert.Equal(IntPtr.Size, MarshalCache.Size); } [Fact] public void Size_for_bool_is_1() { Assert.Equal(1, MarshalCache.Size); } [Fact] public void Size_for_enum_matches_underlying_type() { Assert.Equal(4, MarshalCache.Size); } [Fact] public void Size_for_blittable_struct_is_accurate() { Assert.Equal(8, MarshalCache.Size); } [Fact] public void Size_for_struct_with_bool_is_managed_layout_width() { // Regression for the Marshal.SizeOf / Unsafe.SizeOf disagreement on a struct // whose only field is `bool`: Marshal reports 4 bytes (Win32 BOOL default marshaling), // but the blittable path (MemoryMarshal.Read) actually lays out a bool as 1 byte. // MarshalCache.Size must match what the reader actually touches. Assert.Equal(Unsafe.SizeOf(), MarshalCache.Size); Assert.Equal(1, MarshalCache.Size); } [Fact] public void Size_for_struct_with_bools_in_sequence_matches_managed_layout() { // Sequential struct { bool, bool } — managed width is 2, Marshal width is 8 (two BOOLs). // The blittable path uses 1 byte per bool, so Size must equal the managed width. Assert.Equal(Unsafe.SizeOf(), MarshalCache.Size); Assert.Equal(2, MarshalCache.Size); } [Fact] public void TypeRequiresMarshal_is_false_for_blittable_types() { Assert.False(MarshalCache.TypeRequiresMarshal); Assert.False(MarshalCache.TypeRequiresMarshal); Assert.False(MarshalCache.TypeRequiresMarshal); Assert.False(MarshalCache.TypeRequiresMarshal); } [Fact] public void TypeRequiresMarshal_is_true_for_types_with_MarshalAs_field() { Assert.True(MarshalCache.TypeRequiresMarshal); } [Fact] public void TypeRequiresMarshal_is_true_for_reference_containing_types() { Assert.True(MarshalCache.TypeRequiresMarshal); Assert.True(MarshalCache.TypeRequiresMarshal); } [Fact] public void Properties_are_computed_once_and_cached() { int size1 = MarshalCache.Size; bool marshal1 = MarshalCache.TypeRequiresMarshal; int size2 = MarshalCache.Size; bool marshal2 = MarshalCache.TypeRequiresMarshal; Assert.Equal(size1, size2); Assert.Equal(marshal1, marshal2); } [StructLayout(LayoutKind.Sequential)] private struct BlittableStruct { public int X; public int Y; } [StructLayout(LayoutKind.Sequential)] private struct MarshalAsStruct { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] Data; } [StructLayout(LayoutKind.Sequential)] private struct SingleBoolStruct { public bool Flag; } [StructLayout(LayoutKind.Sequential)] private struct SequentialBoolStruct { public bool A; public bool B; } private class ClassWithInt { public int Value = 0; } }