using System;
using System.Runtime.InteropServices;
using WhiteMagic;
using WhiteMagic.Execution;
using Xunit;
namespace WhiteMagicTest.Execution;
///
/// Tests for operating in-process.
///
public class InProcessInvokerTests
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int AddDelegate(int a, int b);
private static int NativeAdd(int a, int b) => a + b;
[Fact]
public void CreateFunction_calls_known_in_process_function()
{
using var reader = new InProcessReader();
var invoker = new InProcessInvoker(reader);
var native = new AddDelegate(NativeAdd);
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(native);
AddDelegate callable = invoker.CreateFunction(functionPointer);
int result = callable(5, 7);
Assert.Equal(12, result);
GC.KeepAlive(native);
}
[Fact]
public void CreateFunction_rejects_zero_address()
{
using var reader = new InProcessReader();
var invoker = new InProcessInvoker(reader);
ArgumentException ex = Assert.Throws(() => invoker.CreateFunction(IntPtr.Zero));
Assert.Equal("address", ex.ParamName);
}
[Fact]
public void Vtable_helper_reads_function_pointer_slot()
{
using var reader = new InProcessReader();
var invoker = new InProcessInvoker(reader);
// Build a tiny fake vtable in a pinned buffer: two slots holding known pointers.
IntPtr slot0 = Marshal.GetFunctionPointerForDelegate(new AddDelegate(NativeAdd));
IntPtr slot1 = new IntPtr(0x12345678);
IntPtr[] vtable;
if (reader.Is64Bit)
{
vtable = [slot0, slot1];
}
else
{
vtable = [slot0, slot1];
}
GCHandle pin = GCHandle.Alloc(vtable, GCHandleType.Pinned);
try
{
IntPtr vTableAddress = pin.AddrOfPinnedObject();
Assert.Equal(slot0, invoker.ReadVTableFunction(vTableAddress, 0));
Assert.Equal(slot1, invoker.ReadVTableFunction(vTableAddress, 1));
}
finally
{
pin.Free();
}
}
}