DataRecord
This commit is contained in:
+108
-33
@@ -101,10 +101,19 @@ type
|
||||
procedure Test_tkVariant_Succeeds;
|
||||
|
||||
[Test]
|
||||
procedure TestFinalizeWithValue_True_Succeeds;
|
||||
procedure Test_Assign_CreatesIndependentCopy;
|
||||
|
||||
[Test]
|
||||
procedure TestFinalizeWithValue_False_Leaks;
|
||||
procedure Test_Reassign_ReleasesOldData;
|
||||
|
||||
// New tests for RTTI-based creation and assignment
|
||||
[Test]
|
||||
[IgnoreMemoryLeaks]
|
||||
procedure Test_CreateFrom_AssignTo_Succeeds;
|
||||
|
||||
[Test]
|
||||
[IgnoreMemoryLeaks]
|
||||
procedure Test_Assign_FromCreateFrom_IsIndependentCopy;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -329,47 +338,113 @@ begin
|
||||
Assert.AreEqual(testVar, field.Value, 'Variant value should match');
|
||||
end;
|
||||
|
||||
procedure TDataRecordTests.TestFinalizeWithValue_True_Succeeds;
|
||||
procedure TDataRecordTests.Test_Assign_CreatesIndependentCopy;
|
||||
var
|
||||
Buffer: TBytes;
|
||||
P: Pointer;
|
||||
s: string;
|
||||
rec1, rec2: TDataRecord;
|
||||
field1, field2: TDataRecord.IField<string>;
|
||||
begin
|
||||
// 1. Create a managed string and place it in a raw buffer
|
||||
s := 'This is a test string that should be finalized.';
|
||||
SetLength(Buffer, SizeOf(string));
|
||||
P := @Buffer[0];
|
||||
PString(P)^ := s;
|
||||
s := ''; // Clear the original variable, the buffer is now the owner
|
||||
// Create original record
|
||||
rec1 := CreateRecord<string>('Value', 'Original');
|
||||
|
||||
// 2. Try to finalize the string in the buffer using TValue
|
||||
var Val: TValue;
|
||||
TValue.MakeWithoutCopy(P, TypeInfo(string), Val, true);
|
||||
// Assign to a new record, invoking operator Assign. This covers 'CreateFromRec'.
|
||||
rec2 := rec1;
|
||||
|
||||
// If 'true' works as expected (takes ownership and finalizes), this test will pass without leaks.
|
||||
Assert.IsTrue(true);
|
||||
// Verify the copy
|
||||
Assert.IsTrue(Length(rec2.Layout) = 1, 'Copied record should have one field');
|
||||
Assert.AreEqual('Value', rec2.Layout[0].Name, 'Field name should be copied');
|
||||
field2 := rec2.GetField<string>('Value');
|
||||
Assert.IsNotNull(field2, 'Field should exist in copied record');
|
||||
Assert.AreEqual('Original', field2.Value, 'Field value should be copied');
|
||||
|
||||
// Modify the copy and check for independence (deep copy)
|
||||
field2.Value := 'Modified';
|
||||
|
||||
field1 := rec1.GetField<string>('Value');
|
||||
Assert.AreEqual('Original', field1.Value, 'Original record should not be modified');
|
||||
Assert.AreEqual('Modified', field2.Value, 'Copied record should reflect modification');
|
||||
end;
|
||||
|
||||
procedure TDataRecordTests.TestFinalizeWithValue_False_Leaks;
|
||||
procedure TDataRecordTests.Test_Reassign_ReleasesOldData;
|
||||
var
|
||||
Buffer: TBytes;
|
||||
P: Pointer;
|
||||
s: string;
|
||||
rec1, rec2: TDataRecord;
|
||||
field: TDataRecord.IField<string>;
|
||||
begin
|
||||
// 1. Create a managed string and place it in a raw buffer
|
||||
s := 'This string will be leaked.';
|
||||
SetLength(Buffer, SizeOf(string));
|
||||
P := @Buffer[0];
|
||||
PString(P)^ := s;
|
||||
s := ''; // Clear the original variable, the buffer is now the owner
|
||||
// Create two independent records with managed string fields.
|
||||
// DUnitX will report a leak if the memory for rec1 is not freed upon reassignment.
|
||||
rec1 := CreateRecord<string>('OldValue', 'This should be released');
|
||||
rec2 := CreateRecord<string>('NewValue', 'This should remain');
|
||||
|
||||
// 2. Try to "finalize" the string using 'false'
|
||||
var Val: TValue;
|
||||
TValue.MakeWithoutCopy(P, TypeInfo(string), Val, false);
|
||||
// Re-assign rec1. This should Finalize the old rec1 and Assign the new data.
|
||||
rec1 := rec2;
|
||||
|
||||
// If 'false' does NOT take ownership, this test will report a memory leak.
|
||||
// Log.Info('This test is expected to report a memory leak.');
|
||||
Assert.IsTrue(true);
|
||||
// Verify the state of the reassigned rec1
|
||||
Assert.AreEqual(-1, rec1.IndexOf('OldValue'), 'Old field should not exist anymore');
|
||||
Assert.AreNotEqual(-1, rec1.IndexOf('NewValue'), 'New field should exist');
|
||||
|
||||
field := rec1.GetField<string>('NewValue');
|
||||
Assert.AreEqual('This should remain', field.Value, 'Value should be from the new record');
|
||||
|
||||
// Also check that rec2 is unaffected by the assignment
|
||||
field := rec2.GetField<string>('NewValue');
|
||||
Assert.AreEqual('This should remain', field.Value, 'Source record for assignment should be unchanged');
|
||||
end;
|
||||
|
||||
procedure TDataRecordTests.Test_CreateFrom_AssignTo_Succeeds;
|
||||
var
|
||||
srcRec, dstRec: TTestRecord;
|
||||
dataRec: TDataRecord;
|
||||
fieldI: TDataRecord.IField<Integer>;
|
||||
fieldS: TDataRecord.IField<string>;
|
||||
begin
|
||||
// Setup a standard record with data
|
||||
srcRec.I := 123;
|
||||
srcRec.S := 'Hello from RTTI';
|
||||
|
||||
// Create a TDataRecord from the standard record via RTTI
|
||||
dataRec := TDataRecord.CreateFrom<TTestRecord>(srcRec);
|
||||
|
||||
// Verify that the data was correctly transferred into the TDataRecord
|
||||
Assert.IsTrue(Length(dataRec.Layout) = 2, 'Layout should have 2 fields');
|
||||
fieldI := dataRec.GetField<Integer>('I');
|
||||
Assert.IsNotNull(fieldI, 'Integer field should exist');
|
||||
Assert.AreEqual(srcRec.I, fieldI.Value, 'Integer value should match');
|
||||
|
||||
fieldS := dataRec.GetField<string>('S');
|
||||
Assert.IsNotNull(fieldS, 'String field should exist');
|
||||
Assert.AreEqual(srcRec.S, fieldS.Value, 'String value should match');
|
||||
|
||||
// Assign the data back from the TDataRecord to another standard record
|
||||
dataRec.AssignTo<TTestRecord>(dstRec);
|
||||
|
||||
// Verify that the data was correctly restored
|
||||
Assert.AreEqual(srcRec.I, dstRec.I, 'Assigned back record integer should match');
|
||||
Assert.AreEqual(srcRec.S, dstRec.S, 'Assigned back record string should match');
|
||||
end;
|
||||
|
||||
procedure TDataRecordTests.Test_Assign_FromCreateFrom_IsIndependentCopy;
|
||||
var
|
||||
srcRec: TTestRecord;
|
||||
rec1, rec2: TDataRecord;
|
||||
field1, field2: TDataRecord.IField<string>;
|
||||
begin
|
||||
// Setup a record and create a TDataRecord from it
|
||||
srcRec.I := 456;
|
||||
srcRec.S := 'Original RTTI value';
|
||||
rec1 := TDataRecord.CreateFrom<TTestRecord>(srcRec);
|
||||
|
||||
// Assign the TDataRecord to another, invoking operator Assign
|
||||
rec2 := rec1;
|
||||
|
||||
// Modify the string field in the copy
|
||||
field2 := rec2.GetField<string>('S');
|
||||
Assert.IsNotNull(field2, 'Field must exist in copy');
|
||||
field2.Value := 'Modified in copy';
|
||||
|
||||
// Verify that the original TDataRecord is unchanged
|
||||
field1 := rec1.GetField<string>('S');
|
||||
Assert.IsNotNull(field1, 'Field must exist in original');
|
||||
Assert.AreEqual('Original RTTI value', field1.Value, 'Original record should not be modified');
|
||||
Assert.AreEqual('Modified in copy', field2.Value, 'Copied record should reflect modification');
|
||||
end;
|
||||
|
||||
{ TTestClass }
|
||||
|
||||
Reference in New Issue
Block a user