unit Myc.Test.Trade.DataPoint; interface uses System.SysUtils, System.Classes, DUnitX.TestFramework, Myc.Trade.DataPoint; type [TestFixture] TTestTDataSeries = class(TObject) private FSeries: TDataSeries; procedure SetupSeriesWithData; public [Setup] procedure Setup; [Teardown] procedure Teardown; [Test] procedure TestSetupSeriesWithDataVerification; [Test] procedure TestAddAndCount; [Test] [IgnoreMemoryLeaks] procedure TestAddOrderAssertion; [Test] procedure TestGetItemsIndexing; [Test] procedure TestIndexOfExistingTimeStamp; [Test] procedure TestIndexOfNonExistingBetween; [Test] procedure TestIndexOfBeforeFirstItem; [Test] procedure TestIndexOfAfterLastItem; [Test] procedure TestIndexOfExactOldestItem; [Test] procedure TestIndexOfExactNewestItem; [Test] procedure TestIndexOfEmptySeries; [Test] procedure TestIndexOfSingleItemSeries; end; implementation { TTestTDataSeries } procedure TTestTDataSeries.Setup; begin // Managed record is implicitly initialized. end; procedure TTestTDataSeries.Teardown; begin // Ensure managed record is finalized to prevent false leak reports. FSeries := Default(TDataSeries); end; procedure TTestTDataSeries.SetupSeriesWithData; var i: Integer; DataPoint: TDataPoint; baseTime: TDateTime; begin FSeries := Default(TDataSeries); baseTime := EncodeDate(2020, 7, 7); // Add 10 data points with increasing timestamps. for i := 0 to 9 do begin DataPoint := TDataPoint.Create(baseTime + i, TAskBidItem.Create(i * 1.0, i * 1.0 + 0.1)); FSeries.Add(DataPoint); end; // Resulting state: // Newest item: Time = baseTime + 9, Logical Index = 0 // Oldest item: Time = baseTime + 0, Logical Index = 9 end; // Verifies that the test data setup helper works as expected. procedure TTestTDataSeries.TestSetupSeriesWithDataVerification; var i: Integer; baseTime, expectedTime: TDateTime; expectedAsk: Single; begin SetupSeriesWithData; baseTime := EncodeDate(2020, 7, 7); Assert.AreEqual(Int64(10), FSeries.Count, 'Setup should create exactly 10 items'); // Check all items to ensure correct reverse chronological order. for i := 0 to FSeries.Count - 1 do begin expectedTime := baseTime + (9 - i); expectedAsk := Single(9 - i); Assert.AreEqual(expectedTime, FSeries[i].Time, 'Item at logical index should have correct reversed timestamp'); Assert.AreEqual(expectedAsk, FSeries[i].Data.Ask, 'Item at logical index should have correct reversed data'); end; end; // Tests adding new items and the resulting count. procedure TTestTDataSeries.TestAddAndCount; var DataPoint: TDataPoint; newTime: TDateTime; begin SetupSeriesWithData; newTime := EncodeDate(2020, 7, 17); Assert.AreEqual(Int64(10), FSeries.Count, 'Initial count should be 10'); DataPoint := TDataPoint.Create(newTime, TAskBidItem.Create(100.0, 100.1)); FSeries.Add(DataPoint); Assert.AreEqual(Int64(11), FSeries.Count, 'Count should be 11 after adding one more'); Assert.AreEqual(newTime, FSeries.Items[0].Time, 'Newest item should be at index 0'); end; // Ensures that adding an item with an older timestamp raises an assertion. procedure TTestTDataSeries.TestAddOrderAssertion; var olderTime: TDateTime; begin SetupSeriesWithData; // Newest item is at 2020-07-16 olderTime := EncodeDate(2020, 7, 15); Assert.WillRaise( procedure begin var DataPoint := TDataPoint.Create(olderTime, TAskBidItem.Create(0.0, 0.0)); FSeries.Add(DataPoint); end, EAssertionFailed, 'Adding item with older timestamp should raise an assert error' ); end; // Verifies that the logical-to-physical index mapping is correct. procedure TTestTDataSeries.TestGetItemsIndexing; var i: Integer; expectedTime: TDateTime; baseTime: TDateTime; begin SetupSeriesWithData; baseTime := EncodeDate(2020, 7, 7); for i := 0 to 9 do begin expectedTime := baseTime + (9 - i); Assert.AreEqual(expectedTime, FSeries.Items[i].Time, 'Item at logical index should have reversed chronological time'); Assert.AreEqual(Single(9 - i), FSeries.Items[i].Data.Ask, 'Item data at logical index should match reversed insertion order'); end; end; // Tests finding an existing item (in this case, the oldest). procedure TTestTDataSeries.TestIndexOfExistingTimeStamp; var ATimeStamp: TDateTime; expectedIndex: Int64; begin SetupSeriesWithData; ATimeStamp := EncodeDate(2020, 7, 7); // Oldest item expectedIndex := 9; Assert.AreEqual( expectedIndex, FSeries.IndexOf(ATimeStamp), 'IndexOf for the oldest existing item timestamp should return its correct index' ); end; // Tests finding the index for a timestamp that falls between two existing items. procedure TTestTDataSeries.TestIndexOfNonExistingBetween; var ATimeStamp: TDateTime; expectedIndex: Int64; begin SetupSeriesWithData; ATimeStamp := EncodeDate(2020, 7, 7) + 0.5; // 12:00 on the day of the oldest item expectedIndex := Int64(9); // Should find the item from 00:00 Assert.AreEqual( expectedIndex, FSeries.IndexOf(ATimeStamp), 'IndexOf for a non-existing timestamp should return the index of the immediately preceding item' ); end; // Tests finding a timestamp that is older than any item in the series. procedure TTestTDataSeries.TestIndexOfBeforeFirstItem; var ATimeStamp: TDateTime; baseTime: TDateTime; begin SetupSeriesWithData; baseTime := EncodeDate(2020, 7, 7); ATimeStamp := baseTime - 1; // A day before the oldest item Assert.AreEqual(Int64(-1), FSeries.IndexOf(ATimeStamp), 'IndexOf for a timestamp before the oldest item should return -1'); end; // Tests finding a timestamp that is newer than any item in the series. procedure TTestTDataSeries.TestIndexOfAfterLastItem; var ATimeStamp: TDateTime; baseTime: TDateTime; begin SetupSeriesWithData; baseTime := EncodeDate(2020, 7, 7); ATimeStamp := baseTime + 10; // A day after the newest item Assert.AreEqual( Int64(0), FSeries.IndexOf(ATimeStamp), 'IndexOf for a timestamp after the newest item should return the index of the newest item' ); end; // Specifically tests finding the exact oldest item. procedure TTestTDataSeries.TestIndexOfExactOldestItem; var ATimeStamp: TDateTime; begin SetupSeriesWithData; ATimeStamp := EncodeDate(2020, 7, 7); Assert.AreEqual(Int64(9), FSeries.IndexOf(ATimeStamp), 'IndexOf for the exact oldest timestamp should return the last index'); end; // Specifically tests finding the exact newest item. procedure TTestTDataSeries.TestIndexOfExactNewestItem; var ATimeStamp: TDateTime; begin SetupSeriesWithData; ATimeStamp := EncodeDate(2020, 7, 16); Assert.AreEqual(Int64(0), FSeries.IndexOf(ATimeStamp), 'IndexOf for the exact newest timestamp should return index 0'); end; // Tests IndexOf on an empty series. procedure TTestTDataSeries.TestIndexOfEmptySeries; begin FSeries := Default(TDataSeries); Assert.AreEqual(Int64(0), FSeries.Count); Assert.AreEqual(Int64(-1), FSeries.IndexOf(Now), 'IndexOf on empty series should return -1'); end; // Tests IndexOf on a series with only one item. procedure TTestTDataSeries.TestIndexOfSingleItemSeries; var DataPoint: TDataPoint; testTime: TDateTime; begin FSeries := Default(TDataSeries); testTime := EncodeDate(2025, 1, 1); DataPoint := TDataPoint.Create(testTime, TAskBidItem.Create(1.0, 2.0)); FSeries.Add(DataPoint); Assert.AreEqual(Int64(1), FSeries.Count); Assert.AreEqual(Int64(0), FSeries.IndexOf(testTime), 'IndexOf for exact single item should be 0'); Assert.AreEqual(Int64(0), FSeries.IndexOf(testTime + 0.5), 'IndexOf for time after single item should be 0'); Assert.AreEqual(Int64(-1), FSeries.IndexOf(testTime - 1), 'IndexOf for time before single item should be -1'); end; initialization TDUnitX.RegisterTestFixture(TTestTDataSeries); end.