using System.ComponentModel; using WhiteMagic; using WhiteMagic.Memory; using WhiteMagic.Native; namespace WhiteMagicTest.Memory; /// /// Tests for . /// public class AllocatedMemoryTests { private static InProcessReader CreateReader() { return new InProcessReader(); } [Fact] public void Constructor_allocates_memory_with_execute_read_write_protection() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); Assert.NotEqual(IntPtr.Zero, allocated.BaseAddress); Assert.Equal(4096, allocated.Size); } [Fact] public void Constructor_with_custom_protection() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096, MemoryProtectionType.ReadOnly); Assert.NotEqual(IntPtr.Zero, allocated.BaseAddress); } [Fact] public void Constructor_throws_on_negative_size() { using var reader = CreateReader(); var ex = Assert.Throws(() => new AllocatedMemory(reader, -1)); Assert.Equal("size", ex.ParamName); } [Fact] public void Constructor_throws_on_zero_size() { using var reader = CreateReader(); var ex = Assert.Throws(() => new AllocatedMemory(reader, 0)); Assert.Equal("size", ex.ParamName); } [Fact] public void AddRegion_adds_named_region() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("test", 100); Assert.Equal(100, allocated.AddressOf("test") - allocated.BaseAddress); } [Fact] public void AddRegion_throws_on_duplicate_name() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("test", 100); var ex = Assert.Throws(() => allocated.AddRegion("test", 200)); Assert.Contains("already exists", ex.Message); } [Fact] public void AddRegion_throws_on_negative_offset() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); var ex = Assert.Throws(() => allocated.AddRegion("test", -1)); Assert.Equal("offset", ex.ParamName); } [Fact] public void AddRegion_throws_on_offset_exceeding_size() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); var ex = Assert.Throws(() => allocated.AddRegion("test", 4096)); Assert.Equal("offset", ex.ParamName); } [Fact] public void AddressOf_returns_correct_address() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("region1", 0); allocated.AddRegion("region2", 100); allocated.AddRegion("region3", 200); Assert.Equal(allocated.BaseAddress, allocated.AddressOf("region1")); Assert.Equal(allocated.BaseAddress + 100, allocated.AddressOf("region2")); Assert.Equal(allocated.BaseAddress + 200, allocated.AddressOf("region3")); } [Fact] public void AddressOf_throws_on_unknown_region() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); var ex = Assert.Throws(() => allocated.AddressOf("unknown")); Assert.Contains("does not exist", ex.Message); } [Fact] public void Write_and_Read_int_roundtrip() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("value", 0); int original = unchecked((int)0xDEADBEEF); Assert.True(allocated.Write("value", original)); int read = allocated.Read("value"); Assert.Equal(original, read); } [Fact] public void Write_and_Read_long_roundtrip() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("value", 8); long original = 0x123456789ABCDEF0; Assert.True(allocated.Write("value", original)); long read = allocated.Read("value"); Assert.Equal(original, read); } [Fact] public void WriteBytes_and_ReadBytes_roundtrip() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("buffer", 0); byte[] original = { 0x01, 0x02, 0x03, 0x04, 0x05 }; int written = allocated.WriteBytes("buffer", original); Assert.Equal(original.Length, written); byte[] read = allocated.ReadBytes("buffer", original.Length); Assert.Equal(original, read); } [Fact] public void Dispose_frees_memory() { using var reader = CreateReader(); var allocated = new AllocatedMemory(reader, 4096); IntPtr baseAddr = allocated.BaseAddress; Assert.NotEqual(IntPtr.Zero, baseAddr); allocated.Dispose(); // After dispose, accessing properties should throw ObjectDisposedException Assert.Throws(() => allocated.AddressOf("any")); } [Fact] public void Multiple_regions_independent_access() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); allocated.AddRegion("a", 0); allocated.AddRegion("b", 4); allocated.AddRegion("c", 8); Assert.True(allocated.Write("a", 0x11111111)); Assert.True(allocated.Write("b", 0x22222222)); Assert.True(allocated.Write("c", 0x33333333)); Assert.Equal(0x11111111, allocated.Read("a")); Assert.Equal(0x22222222, allocated.Read("b")); Assert.Equal(0x33333333, allocated.Read("c")); } [Fact] public void Write_to_unknown_region_throws() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); var ex = Assert.Throws(() => allocated.Write("unknown", 42)); Assert.Contains("does not exist", ex.Message); } [Fact] public void Read_from_unknown_region_throws() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 4096); var ex = Assert.Throws(() => allocated.Read("unknown")); Assert.Contains("does not exist", ex.Message); } [Fact] public void Read_T_throws_when_value_exceeds_allocation() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 100); allocated.AddRegion("boundary", 99); var ex = Assert.Throws(() => allocated.Read("boundary")); Assert.Equal("name", ex.ParamName); } [Fact] public void Write_T_throws_when_value_exceeds_allocation() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 100); allocated.AddRegion("boundary", 99); var ex = Assert.Throws(() => allocated.Write("boundary", 42)); Assert.Equal("name", ex.ParamName); } [Fact] public void ReadBytes_throws_when_count_exceeds_allocation() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 100); allocated.AddRegion("boundary", 99); var ex = Assert.Throws(() => allocated.ReadBytes("boundary", 2)); Assert.Equal("count", ex.ParamName); } [Fact] public void WriteBytes_throws_when_span_exceeds_allocation() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 100); allocated.AddRegion("boundary", 99); var ex = Assert.Throws(() => allocated.WriteBytes("boundary", new byte[2])); Assert.Equal("bytes", ex.ParamName); } [Fact] public void Read_and_Write_at_exact_allocation_boundary_succeed() { using var reader = CreateReader(); using var allocated = new AllocatedMemory(reader, 100); allocated.AddRegion("boundary", 96); const int expected = unchecked((int)0xDEADBEEF); Assert.True(allocated.Write("boundary", expected)); Assert.Equal(expected, allocated.Read("boundary")); } }