unit TestDataRecord.RawAccess;

interface

uses
    System.SysuTils,
    DUnitX.TestFramework,
    Myc.DataRecord;

type
    [TestFixture]
    TTestDataRecordRawAccess = class
    public
        [Test]
        procedure TestRawAccess_Integer;
        [Test]
        procedure TestRawAccess_String;
        [Test]
        procedure TestRawAccess_Interface;
    end;

implementation

uses
    System.Classes;

type
    // A simple interface for testing managed type behavior.
    IMyTestInterface = interface
        ['{1D23E456-AB78-4CD9-89EF-0123456789AB}'] // GUID for good practice, not strictly needed here
        function GetValue: integer;
    end;

    // Implementation of the test interface.
    TMyTestInterfaceImpl = class(TInterfacedObject, IMyTestInterface)
    private
        FValue: Integer;
    public
        constructor Create(const AValue: Integer);
        function GetValue: integer;
    end;

{ TMyTestInterfaceImpl }

constructor TMyTestInterfaceImpl.Create(const AValue: Integer);
begin
    inherited Create;
    FValue := AValue;
end;

function TMyTestInterfaceImpl.GetValue: integer;
begin
    Result := FValue;
end;

procedure TTestDataRecordRawAccess.TestRawAccess_Integer;
var
    rec: TDataRecord;
    buffer: TBytes;
    field: TDataRecord.IField;
    initialValue, readValue, newValue: Integer;
begin
    // Arrange: Create a record layout and its buffer.
    var builder := TDataRecord.CreateBuilder;
    builder.AddField<Integer>('IntField');
    rec := builder.CreateRec;
    field := rec.GetField('IntField');
    Assert.IsNotNull(field, 'Field should exist');

    SetLength(buffer, rec.GetBufferSize);
    rec.Initialize(buffer);
    try
        // Set initial value using raw access
        initialValue := 123;
        field.SetRaw(buffer, @initialValue);

        // Act & Assert (GetRaw): Read the initial value via raw pointer.
        readValue := 0;
        field.GetRaw(buffer, @readValue);
        Assert.AreEqual(123, readValue, 'GetRaw should retrieve the correct integer value');

        // Act (SetRaw): Set a new value via raw pointer.
        newValue := 456;
        field.SetRaw(buffer, @newValue);

        // Assert: Verify the new value using the typed accessor.
        var typedField := rec.GetField<Integer>('IntField');
        Assert.AreEqual(456, typedField.Value[buffer], 'SetRaw should update the integer value correctly');
    finally
        rec.Finalize(buffer);
    end;
end;

procedure TTestDataRecordRawAccess.TestRawAccess_Interface;
var
    rec: TDataRecord;
    buffer: TBytes;
    field: TDataRecord.IField;
    initialObj, readObj, newObj: IMyTestInterface;
begin
    // Arrange: Create a record layout and its buffer.
    var builder := TDataRecord.CreateBuilder;
    builder.AddField<IMyTestInterface>('IField');
    rec := builder.CreateRec;
    field := rec.GetField('IField');
    Assert.IsNotNull(field, 'Field should exist');

    SetLength(buffer, rec.GetBufferSize);
    rec.Initialize(buffer);
    try
        // Set initial value
        initialObj := TMyTestInterfaceImpl.Create(10);
        field.SetRaw(buffer, @initialObj);

        // Act & Assert (GetRaw): Read the initial interface via raw pointer.
        readObj := nil;
        field.GetRaw(buffer, @readObj);
        Assert.IsNotNull(readObj, 'GetRaw should retrieve a non-nil interface');
        Assert.AreSame(initialObj, readObj, 'GetRaw should retrieve the same interface instance');
        Assert.AreEqual(10, readObj.GetValue, 'Value of retrieved interface should be correct');

        // Act (SetRaw): Set a new interface via raw pointer.
        newObj := TMyTestInterfaceImpl.Create(20);
        field.SetRaw(buffer, @newObj);

        // Assert: Verify the new interface is set correctly.
        var typedField := rec.GetField<IMyTestInterface>('IField');
        Assert.AreSame(newObj, typedField.Value[buffer], 'SetRaw should update to the new interface instance');
        Assert.AreEqual(20, typedField.Value[buffer].GetValue, 'Value of new interface should be correct');
    finally
        rec.Finalize(buffer);
    end;
end;

procedure TTestDataRecordRawAccess.TestRawAccess_String;
var
    rec: TDataRecord;
    buffer: TBytes;
    field: TDataRecord.IField;
    initialString, readString, newString: string;
begin
    // Arrange: Create a record layout and its buffer.
    var builder := TDataRecord.CreateBuilder;
    builder.AddField<string>('StrField');
    rec := builder.CreateRec;
    field := rec.GetField('StrField');
    Assert.IsNotNull(field, 'Field should exist');

    SetLength(buffer, rec.GetBufferSize);
    rec.Initialize(buffer);
    try
        // Set initial value
        initialString := 'Hello';
        field.SetRaw(buffer, @initialString);

        // Act & Assert (GetRaw): Read the initial string via raw pointer.
        readString := '';
        field.GetRaw(buffer, @readString);
        Assert.AreEqual('Hello', readString, 'GetRaw should retrieve the correct string value');

        // Act (SetRaw): Set a new string via raw pointer.
        newString := 'World';
        field.SetRaw(buffer, @newString);

        // Assert: Verify the new string using the typed accessor.
        var typedField := rec.GetField<string>('StrField');
        Assert.AreEqual('World', typedField.Value[buffer], 'SetRaw should update the string value correctly');
    finally
        rec.Finalize(buffer);
    end;
end;

initialization
    TDUnitX.RegisterTestFixture(TTestDataRecordRawAccess);

end.
