184 lines
5.3 KiB
C#
184 lines
5.3 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using WhiteMagic;
|
|
using WhiteMagic.Native;
|
|
|
|
namespace WhiteMagicTest;
|
|
|
|
/// <summary>
|
|
/// Tests for <see cref="MemoryBase.ReadString"/> and <see cref="MemoryBase.WriteString"/>
|
|
/// with encoding, null-terminator stop, and max-length behavior.
|
|
/// </summary>
|
|
public class StringReadWriteTests
|
|
{
|
|
private static ExternalReader OpenSelf()
|
|
{
|
|
return new ExternalReader(
|
|
Process.GetCurrentProcess(),
|
|
ProcessAccess.VmRead | ProcessAccess.VmWrite | ProcessAccess.VmOperation | ProcessAccess.QueryInformation);
|
|
}
|
|
|
|
[Fact]
|
|
public void WriteString_ascii_then_ReadString_round_trips()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = new byte[64];
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
Assert.True(reader.WriteString(addr, "hello", Encoding.ASCII));
|
|
string result = reader.ReadString(addr, Encoding.ASCII);
|
|
Assert.Equal("hello", result);
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void WriteString_utf8_then_ReadString_round_trips()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = new byte[64];
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
Assert.True(reader.WriteString(addr, "héllo wörld", Encoding.UTF8));
|
|
string result = reader.ReadString(addr, Encoding.UTF8);
|
|
Assert.Equal("héllo wörld", result);
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void WriteString_unicode_then_ReadString_round_trips()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = new byte[128];
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
Assert.True(reader.WriteString(addr, "Hello\u00A9\u00AE\u20AC", Encoding.Unicode));
|
|
string result = reader.ReadString(addr, Encoding.Unicode);
|
|
Assert.Equal("Hello\u00A9\u00AE\u20AC", result);
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadString_stops_at_null_terminator()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = Encoding.ASCII.GetBytes("hello\0world");
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
string result = reader.ReadString(addr, Encoding.ASCII, maxLength: 64);
|
|
Assert.Equal("hello", result);
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadString_respects_max_length()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = Encoding.ASCII.GetBytes("hello world this is a test");
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
string result = reader.ReadString(addr, Encoding.ASCII, maxLength: 5);
|
|
Assert.Equal("hello", result);
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void WriteString_appends_null_terminator_automatically()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = new byte[32];
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
|
|
// Write without terminator
|
|
Assert.True(reader.WriteString(addr, "test", Encoding.ASCII));
|
|
|
|
// The written bytes should end with \0
|
|
byte[] read = reader.ReadBytes(addr, 8);
|
|
Assert.Equal((byte)'t', read[0]);
|
|
Assert.Equal((byte)'e', read[1]);
|
|
Assert.Equal((byte)'s', read[2]);
|
|
Assert.Equal((byte)'t', read[3]);
|
|
Assert.Equal(0, read[4]); // null terminator
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadString_empty_buffer_returns_empty_string()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = new byte[1] { 0 };
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
string result = reader.ReadString(addr, Encoding.ASCII, maxLength: 1);
|
|
Assert.Equal("", result);
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void WriteString_empty_string_writes_only_null()
|
|
{
|
|
using var reader = OpenSelf();
|
|
byte[] slot = new byte[8];
|
|
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
IntPtr addr = pin.AddrOfPinnedObject();
|
|
|
|
// Write a marker first
|
|
reader.WriteBytes(addr, [0xAB, 0xCD, 0xEF, 0x00]);
|
|
// Now overwrite with empty string
|
|
Assert.True(reader.WriteString(addr, "", Encoding.ASCII));
|
|
|
|
byte[] read = reader.ReadBytes(addr, 4);
|
|
Assert.Equal(0, read[0]); // null
|
|
}
|
|
finally
|
|
{
|
|
pin.Free();
|
|
}
|
|
}
|
|
}
|