unit TestDataRecord; interface uses DunitX.TestFramework, System.SysUtils, System.DateUtils, System.Rtti, Myc.Data.Records; type // A record that contains all supported data types for testing TMyTestRecord = record MyInteger: Integer; MyInt64: Int64; MySingle: Single; MyDouble: Double; MyString: string; MyTimestamp: TDateTime; MyRecord: TDataRecord; end; // A simple record to be nested inside another TNestedTestRecord = record NestedValue: Integer; NestedString: string; end; // A record that contains another record field of type TDataRecord TOuterTestRecord = record OuterValue: Integer; MyNestedRecord: TDataRecord; end; [TestFixture] TTestDataRecord = class(TObject) public [Test] [IgnoreMemoryLeaks] procedure TestLayoutCreation; [Test] [IgnoreMemoryLeaks] procedure TestCreateFromInstanceAndGetValue; [Test] [IgnoreMemoryLeaks] procedure TestSetValueAndGetValue; [Test] // [IgnoreMemoryLeaks] procedure TestNestedDataRecord; [Test] // [IgnoreMemoryLeaks] procedure TestInternalRecordAssignment; [Test] // [IgnoreMemoryLeaks] procedure TestNestedSetValueAndGetValue; end; implementation { TTestDataRecord } procedure TTestDataRecord.TestLayoutCreation; var layout: TDataRecord.TLayout; idx: Integer; begin // Test: Create layout from a record type layout := TDataRecord.TLayout.FromRecord; // Assert: Check field count Assert.IsTrue(Length(layout.Fields) = 7, 'Layout should have 7 fields'); // Assert: Check specific fields for correct type mapping idx := layout.IndexOf('MyTimestamp'); Assert.IsTrue(idx > -1, 'Field MyTimestamp not found'); Assert.AreEqual(TDataRecord.TFieldType.dfTimestamp, layout.Fields[idx].FieldType, 'MyTimestamp should be mapped to dfTimestamp'); idx := layout.IndexOf('MyInteger'); Assert.IsTrue(idx > -1, 'Field MyInteger not found'); Assert.AreEqual(TDataRecord.TFieldType.dfInteger, layout.Fields[idx].FieldType, 'MyInteger should be mapped to dfInteger'); idx := layout.IndexOf('MyString'); Assert.IsTrue(idx > -1, 'Field MyString not found'); Assert.AreEqual(TDataRecord.TFieldType.dfString, layout.Fields[idx].FieldType, 'MyString should be mapped to dfString'); idx := layout.IndexOf('MySingle'); Assert.IsTrue(idx > -1, 'Field MySingle not found'); Assert.AreEqual(TDataRecord.TFieldType.dfFloat, layout.Fields[idx].FieldType, 'MySingle should be mapped to dfFloat'); idx := layout.IndexOf('MyRecord'); Assert.IsTrue(idx > -1, 'Field MyRecord not found'); Assert.AreEqual(TDataRecord.TFieldType.dfRecord, layout.Fields[idx].FieldType, 'MyRecord should be mapped to dfRecord'); end; procedure TTestDataRecord.TestCreateFromInstanceAndGetValue; var srcRecord: TMyTestRecord; dataRecord: TDataRecord; begin // Setup: Create and populate a source record srcRecord.MyInteger := 12345; srcRecord.MyInt64 := 9876543210; srcRecord.MySingle := 123.456; srcRecord.MyDouble := 789.012345; srcRecord.MyString := 'Hello Delphi!'; srcRecord.MyTimestamp := EncodeDateTime(2024, 7, 19, 10, 30, 0, 0); srcRecord.MyRecord := default(TDataRecord); // Not testing nested here // Test: Create a TDataRecord from the source record instance dataRecord := TDataRecord.FromRecord(srcRecord); // Assert: Retrieve each value and check for correctness Assert.AreEqual(srcRecord.MyInt64, dataRecord.GetValue('MyInt64'), 'Int64 value mismatch'); Assert.AreEqual(srcRecord.MyInteger, dataRecord.GetValue('MyInteger'), 'Integer value mismatch'); Assert.AreEqual(srcRecord.MyString, dataRecord.GetValue('MyString'), 'String value mismatch'); Assert.AreEqual(srcRecord.MyTimestamp, dataRecord.GetValue('MyTimestamp'), 'TDateTime value mismatch'); // Assert: Check float values with a tolerance for precision differences Assert.AreEqual(srcRecord.MySingle, dataRecord.GetValue('MySingle'), 1e-6, 'Single value mismatch'); Assert.AreEqual(srcRecord.MyDouble, dataRecord.GetValue('MyDouble'), 1e-9, 'Double value mismatch'); // Assert: Test type promotion (reading an Integer as Int64) Assert.IsTrue(srcRecord.MyInteger = dataRecord.GetValue('MyInteger'), 'Integer to Int64 promotion failed'); end; procedure TTestDataRecord.TestSetValueAndGetValue; var dataRecord: TDataRecord; testInt: Integer; testInt64: Int64; testSingle: Single; testDouble: Double; testString: string; testTimestamp: TDateTime; begin // Setup: Create an empty TDataRecord with the layout of TMyTestRecord dataRecord := TDataRecord.FromRecord; // Setup: Define test values testInt := 54321; testInt64 := 12345678901; testSingle := 987.654; testDouble := 543.210987; testString := 'Testing SetValue'; testTimestamp := Now; // Test: Set values for each field by name dataRecord.SetValue('MyInteger', testInt); dataRecord.SetValue('MyInt64', testInt64); dataRecord.SetValue('MySingle', testSingle); dataRecord.SetValue('MyDouble', testDouble); dataRecord.SetValue('MyString', testString); dataRecord.SetValue('MyTimestamp', testTimestamp); // Assert: Retrieve the values and check if they match what was set Assert.AreEqual(testInt, dataRecord.GetValue('MyInteger'), 'SetValue failed for Integer'); Assert.AreEqual(testInt64, dataRecord.GetValue('MyInt64'), 'SetValue failed for Int64'); Assert.AreEqual(testString, dataRecord.GetValue('MyString'), 'SetValue failed for String'); Assert.AreEqual(testTimestamp, dataRecord.GetValue('MyTimestamp'), 'SetValue failed for TDateTime'); Assert.AreEqual(testSingle, dataRecord.GetValue('MySingle'), 1e-6, 'SetValue failed for Single'); Assert.AreEqual(testDouble, dataRecord.GetValue('MyDouble'), 1e-9, 'SetValue failed for Double'); end; procedure TTestDataRecord.TestNestedDataRecord; var nestedSrc: TNestedTestRecord; nestedDataRecord: TDataRecord; outerSrc: TOuterTestRecord; mainDataRecord: TDataRecord; retrievedNestedRecord: TDataRecord; begin // Setup: Create the nested record and convert it to a TDataRecord nestedSrc.NestedValue := 999; const str = 'Nested Record Test'; nestedSrc.NestedString := str; nestedDataRecord := TDataRecord.FromRecord(nestedSrc); // Setup: Create the outer record containing the nested TDataRecord outerSrc.OuterValue := 111; outerSrc.MyNestedRecord := nestedDataRecord; // Test: Create the main TDataRecord from the outer record instance mainDataRecord := TDataRecord.FromRecord(outerSrc); // Assert: Retrieve the nested record and check its contents retrievedNestedRecord := mainDataRecord.GetValue('MyNestedRecord'); Assert.IsNotNull(retrievedNestedRecord.Layout.Fields, 'Nested record layout should not be nil'); Assert.IsTrue(Length(retrievedNestedRecord.Layout.Fields) = 2, 'Nested record should have 2 fields'); // Assert values within the nested record Assert.AreEqual(999, retrievedNestedRecord.GetValue('NestedValue'), 'Nested integer value mismatch'); Assert.AreEqual(str, retrievedNestedRecord.GetValue('NestedString'), 'Nested string value mismatch'); // Assert value in the outer record Assert.AreEqual(111, mainDataRecord.GetValue('OuterValue'), 'Outer integer value mismatch'); end; procedure TTestDataRecord.TestInternalRecordAssignment; var nestedSrc: TNestedTestRecord; nestedDataRecord: TDataRecord; outerSrc: TOuterTestRecord; mainDataRecord: TDataRecord; retrievedNestedRecord: TDataRecord; begin // Setup: Create the nested record and convert it to a TDataRecord const str = 'Nested Record Test'; nestedSrc.NestedString := str; nestedDataRecord := TDataRecord.FromRecord(nestedSrc); // Setup: Create the outer record containing the nested TDataRecord outerSrc.MyNestedRecord := nestedDataRecord; // Place a record on the heap. FAILS: This erases the nested record data!! -- Fixed 25.07.2025! TDataRecord.FromRecord(outerSrc); // Test: Create the main TDataRecord from the outer record instance mainDataRecord := TDataRecord.FromRecord(outerSrc); // Assert: Retrieve the nested record and check its contents retrievedNestedRecord := mainDataRecord.GetValue('MyNestedRecord'); // Assert values within the nested record Assert.AreEqual(str, retrievedNestedRecord.GetValue('NestedString'), 'Nested string value mismatch'); end; procedure TTestDataRecord.TestNestedSetValueAndGetValue; var outerRecord: TDataRecord; nestedRecord: TDataRecord; retrievedNestedRecord: TDataRecord; begin // Setup: Create an empty TDataRecord for the outer structure outerRecord := TDataRecord.FromRecord; // Setup: Create a TDataRecord for the nested structure and populate it nestedRecord := TDataRecord.FromRecord; nestedRecord.SetValue('NestedValue', 888); nestedRecord.SetValue('NestedString', 'Manual Nested Set'); // Test: Set the nested record and another value in the outer record outerRecord.SetValue('OuterValue', 222); outerRecord.SetValue('MyNestedRecord', nestedRecord); // Assert: Retrieve the nested record and check its contents retrievedNestedRecord := outerRecord.GetValue('MyNestedRecord'); Assert.IsNotNull(retrievedNestedRecord.Layout.Fields, 'Retrieved nested record layout should not be nil'); Assert.IsTrue(Length(retrievedNestedRecord.Layout.Fields) = 2, 'Retrieved nested record should have 2 fields'); Assert.AreEqual(888, retrievedNestedRecord.GetValue('NestedValue'), 'Nested integer check failed after SetValue'); Assert .AreEqual('Manual Nested Set', retrievedNestedRecord.GetValue('NestedString'), 'Nested string check failed after SetValue'); // Assert: Check the outer value as well Assert.AreEqual(222, outerRecord.GetValue('OuterValue'), 'Outer value check failed after SetValue'); end; initialization TDUnitX.RegisterTestFixture(TTestDataRecord); end.