Files
whitemagic/WhiteMagicTest/InProcessReaderTests.cs
2026-07-21 22:30:10 +02:00

158 lines
4.1 KiB
C#

using System.Runtime.InteropServices;
using WhiteMagic;
namespace WhiteMagicTest;
/// <summary>
/// Tests for <see cref="InProcessReader"/>. The current implementation uses
/// <c>ReadProcessMemory</c>/<c>WriteProcessMemory</c> on a self-handle (per the D1
/// revision — unsafe direct-pointer dereference was rejected because .NET cannot
/// catch <see cref="AccessViolationException"/>). Verifies the shared
/// <see cref="MemoryBase"/> API works for both external and in-process readers.
/// </summary>
public class InProcessReaderTests
{
private static InProcessReader CreateReader()
{
return new InProcessReader();
}
[Fact]
public void ImageBase_is_nonzero()
{
using var reader = CreateReader();
Assert.NotEqual(IntPtr.Zero, reader.ImageBase);
}
[Fact]
public void Read_int_reads_known_value_from_own_memory()
{
using var reader = CreateReader();
int expected = 0x12345678;
GCHandle pin = GCHandle.Alloc(expected, GCHandleType.Pinned);
try
{
IntPtr addr = pin.AddrOfPinnedObject();
int result = reader.Read<int>(addr);
Assert.Equal(expected, result);
}
finally
{
pin.Free();
}
}
[Fact]
public void Write_int_writes_and_reads_back()
{
using var reader = CreateReader();
int slot = 0;
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
try
{
IntPtr addr = pin.AddrOfPinnedObject();
Assert.True(reader.Write(addr, unchecked((int)0xCAFEBABE)));
Assert.Equal(unchecked((int)0xCAFEBABE), reader.Read<int>(addr));
}
finally
{
pin.Free();
}
}
[Fact]
public void Read_bytes_reads_known_bytes()
{
using var reader = CreateReader();
byte[] expected = [0x0A, 0x0B, 0x0C, 0x0D];
GCHandle pin = GCHandle.Alloc(expected, GCHandleType.Pinned);
try
{
IntPtr addr = pin.AddrOfPinnedObject();
byte[] result = reader.ReadBytes(addr, 4);
Assert.Equal(expected, result);
}
finally
{
pin.Free();
}
}
[Fact]
public void Write_bytes_writes_and_reads_back()
{
using var reader = CreateReader();
byte[] slot = new byte[4];
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
try
{
IntPtr addr = pin.AddrOfPinnedObject();
byte[] expected = [0xDE, 0xAD, 0xBE, 0xEF];
int written = reader.WriteBytes(addr, expected);
Assert.Equal(4, written);
byte[] result = reader.ReadBytes(addr, 4);
Assert.Equal(expected, result);
}
finally
{
pin.Free();
}
}
[Fact]
public void Read_struct_via_InProcessReader()
{
using var reader = CreateReader();
var slot = new TestStruct { X = 10, Y = 20 };
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
try
{
IntPtr addr = pin.AddrOfPinnedObject();
var result = reader.Read<TestStruct>(addr);
Assert.Equal(10, result.X);
Assert.Equal(20, result.Y);
}
finally
{
pin.Free();
}
}
[Fact]
public void Write_struct_via_InProcessReader()
{
using var reader = CreateReader();
var slot = new TestStruct { X = 1, Y = 2 };
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
try
{
IntPtr addr = pin.AddrOfPinnedObject();
Assert.True(reader.Write(addr, new TestStruct { X = 99, Y = 88 }));
var result = reader.Read<TestStruct>(addr);
Assert.Equal(99, result.X);
Assert.Equal(88, result.Y);
}
finally
{
pin.Free();
}
}
[Fact]
public void Dispose_disposes_handle()
{
var reader = CreateReader();
Assert.False(reader.Handle.IsClosed);
reader.Dispose();
Assert.True(reader.Handle.IsClosed);
}
}