{ ***************************************************************************** * * * See the file COPYING.modifiedLGPL.txt, included in this distribution, * * for details about the copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** Authors: Alexander Klenin } unit SourcesTest; {$mode objfpc}{$H+}{$R+} interface uses Classes, SysUtils, FPCUnit, TestRegistry, TAChartUtils, TACustomSource, TAIntervalSources, TASources; type { TListSourceTest } TListSourceTest = class(TTestCase) private FSource: TListChartSource; procedure AssertItemEquals( const AItem: TChartDataItem; AX, AY: Double; AText: String = ''; AColor: TChartColor = clTAColor); protected procedure SetUp; override; procedure TearDown; override; published procedure Basic; procedure Bounds; procedure Cache; procedure DataPoint; procedure DataPointSeparator; procedure Enum; procedure Extent; procedure Multi; end; { TRandomSourceTest } TRandomSourceTest = class(TTestCase) published procedure Extent; end; { TCalculatedSourceTest } TCalculatedSourceTest = class(TTestCase) private FOrigin: TListChartSource; FSource: TCalculatedChartSource; protected procedure SetUp; override; procedure TearDown; override; published procedure Accumulate; procedure Derivative; procedure Percentage; procedure Reorder; end; TIntervalSourceTest = class(TTestCase) private procedure AssertValueEquals( const AExpected: array of Double; const AActual: TChartValueTextArray); published procedure IntervalSource; procedure ListSource; end; implementation uses Math, TAMath, AssertHelpers; type TDummyTransform = object public function IdentityDouble(AX: Double): Double; function IdentityInteger(AX: Integer): Integer; function PrepareValuesInRangeParams: TValuesInRangeParams; function Round(AX: Double): Integer; end; var VDummyTransform: TDummyTransform; { TDummyTransform } function TDummyTransform.IdentityDouble(AX: Double): Double; begin Result := AX; end; function TDummyTransform.IdentityInteger(AX: Integer): Integer; begin Result := AX; end; function TDummyTransform.PrepareValuesInRangeParams: TValuesInRangeParams; begin with Result do begin FAxisToGraph := @IdentityDouble; FGraphToAxis := @IdentityDouble; FFormat := ''; FGraphToImage := @Round; FMin := 30; FMax := 69; FMinStep := 0; FScale := @IdentityInteger; FUseY := false; FIntervals := TChartAxisIntervalParams.Create(nil); end; end; function TDummyTransform.Round(AX: Double): Integer; begin Result := System.Round(AX); end; { TCalculatedSourceTest } procedure TCalculatedSourceTest.Accumulate; var i, j: Integer; rng: TMWCRandomGenerator; begin FSource.AccumulationMethod := camSum; FSource.AccumulationRange := 2; AssertEquals(3, FSource.YCount); AssertEquals(1, FSource[0]^.X); AssertEquals(102, FSource[0]^.Y); AssertEquals(2, FSource[1]^.X); AssertEquals(102 + 202, FSource[1]^.Y); AssertEquals(202 + 302, FSource[2]^.Y); FSource.AccumulationDirection := cadForward; AssertEquals(202 + 302, FSource[1]^.Y); AssertEquals(302 + 402, FSource[2]^.Y); FSource.AccumulationDirection := cadBackward; FSource.AccumulationMethod := camAverage; AssertEquals((2002 + 2102) / 2, FSource[20]^.Y); AssertEquals(1, FSource[0]^.X); AssertEquals(102, FSource[0]^.Y); AssertEquals((102 + 202) / 2, FSource[1]^.Y); AssertEquals(102, FSource[0]^.Y); FSource.AccumulationDirection := cadCenter; AssertEquals((1102 + 1202 + 1302) / 3, FSource[11]^.Y); FSource.AccumulationDirection := cadBackward; FSource.AccumulationRange := 5; rng := TMWCRandomGenerator.Create; try rng.Seed := 89237634; for i := 1 to 100 do begin j := rng.GetInRange(5, FSource.Count - 1); AssertEquals(IntToStr(j), (j - 1) * 100 + 2, FSource[j]^.Y); end; FSource.AccumulationRange := 0; FSource.AccumulationMethod := camSum; rng.Seed := 23784538; for i := 1 to 20 do begin j := rng.GetInRange(0, FSource.Count - 1); AssertEquals( IntToStr(j), (j + 1) * (j + 2) * 50 + (j + 1) * 3, FSource[j]^.YList[0]); end; finally rng.Free; end; end; procedure TCalculatedSourceTest.Derivative; begin FSource.AccumulationMethod := camDerivative; FSource.AccumulationRange := 2; FOrigin.SetYValue(1, 202); AssertTrue(IsNan(FSource[0]^.Y)); AssertEquals(100, FSource[1]^.Y); FSource.AccumulationDirection := cadCenter; AssertEquals(100, FSource[0]^.Y); end; procedure TCalculatedSourceTest.Percentage; begin FSource.Percentage := true; AssertEquals(3, FSource.YCount); AssertEquals(102 / (102 + 103 + 104) * 100, FSource[0]^.Y); AssertEquals(103 / (102 + 103 + 104) * 100, FSource[0]^.YList[0]); end; procedure TCalculatedSourceTest.Reorder; var i: Integer; begin AssertEquals(3, FSource.YCount); FSource.ReorderYList := '2'; AssertEquals(1, FSource.YCount); AssertEquals(104, FSource[0]^.Y); AssertEquals(204, FSource[1]^.Y); FSource.ReorderYList := '0,2'; AssertEquals(2, FSource.YCount); AssertEquals(102, FSource[0]^.Y); AssertEquals(104, FSource[0]^.YList[0]); AssertEquals(202, FSource[1]^.Y); AssertEquals(204, FSource[1]^.YList[0]); FSource.ReorderYList := '1,1,1'; AssertEquals(3, FSource.YCount); AssertEquals(103, FSource[0]^.Y); AssertEquals([103, 103], FSource[0]^.YList); FSource.ReorderYList := ''; for i := 0 to FSource.Count - 1 do begin AssertEquals(FOrigin[i]^.Y, FSource[i]^.Y); AssertEquals(FOrigin[i]^.YList, FSource[i]^.YList); end; end; procedure TCalculatedSourceTest.SetUp; var i: Integer; begin inherited SetUp; FOrigin := TListChartSource.Create(nil); FSource := TCalculatedChartSource.Create(nil); FSource.Origin := FOrigin; FOrigin.YCount := 3; for i := 1 to 100 do FOrigin.SetYList(FOrigin.Add(i, i * 100 + 2), [i * 100 + 3, i * 100 + 4]); end; procedure TCalculatedSourceTest.TearDown; begin FreeAndNil(FSource); FreeAndNil(FOrigin); inherited TearDown; end; { TListSourceTest } procedure TListSourceTest.AssertItemEquals( const AItem: TChartDataItem; AX, AY: Double; AText: String; AColor: TChartColor); begin AssertEquals('X', AX, AItem.X); AssertEquals('Y', AY, AItem.Y); AssertEquals('Text', AText, AItem.Text); AssertEquals('Color', AColor, AItem.Color); end; procedure TListSourceTest.Basic; var i: Integer; srcDest: TListChartSource; begin FSource.Clear; AssertEquals(0, FSource.Count); AssertEquals(0, FSource.Add(1, 2, 'text', $FFFFFF)); AssertEquals(1, FSource.Count); FSource.Delete(0); AssertEquals(0, FSource.Count); for i := 1 to 10 do FSource.Add(i, i * 2, IntToStr(i)); srcDest := TListChartSource.Create(nil); try srcDest.CopyFrom(FSource); AssertEquals(FSource.Count, srcDest.Count); for i := 0 to FSource.Count - 1 do with FSource[i]^ do AssertItemEquals(srcDest[i]^, X, Y, Text, Color); finally srcDest.Free; end; end; procedure TListSourceTest.Bounds; procedure Check2(AExpectedLB, AExpectedUB: Integer; AXMin, AXMax: Double); var lb, ub: Integer; begin FSource.FindBounds(AXMin, AXMax, lb, ub); AssertEquals(AExpectedLB, lb); AssertEquals(AExpectedUB, ub); end; procedure Check(AExpectedLB, AExpectedUB: Integer; AValue: Double); begin Check2(AExpectedLB, AExpectedUB, AValue, AValue) end; procedure CheckAll; begin Check(1, 1, 2); Check(1, 0, 1.9); Check(0, -1, 0.9); Check(5, 4, 5.1); Check(4, 3, 4.9); Check2(2, 4, 3, 1e100); Check2(0, 1, -1e100, 2); end; begin FSource.Clear; FSource.Add(1, 2); FSource.Add(2, 3); FSource.Add(3, 4); FSource.Add(4, 5); FSource.Add(5, 6); FSource.Sorted := true; CheckAll; FSource.Sorted := false; CheckAll; FSource.SetXValue(1, SafeNan); Check(2, 0, 2); FSource.SetXValue(0, SafeNan); Check2(2, 2, -1e100, 3); Check2(2, 2, NegInfinity, 3); end; procedure TListSourceTest.Cache; begin FSource.Clear; FSource.Add(5, 6); FSource.Add(7, 8); AssertEquals(14, FSource.ValuesTotal); FSource.Add(8, SafeNan); AssertEquals(14, FSource.ValuesTotal); FSource.Delete(2); AssertEquals(14, FSource.ValuesTotal); FSource.Delete(1); AssertEquals(6, FSource.ValuesTotal); FSource.SetYValue(0, SafeNan); AssertEquals(0, FSource.ValuesTotal); FSource.SetYValue(0, 5); AssertEquals(5, FSource.ValuesTotal); FSource.Clear; AssertEquals(0, FSource.ValuesTotal); FSource.Add(NaN, NaN); FSource.BeginUpdate; FSource.EndUpdate; AssertEquals(0, FSource.ValuesTotal); end; procedure TListSourceTest.DataPoint; begin FSource.Clear; FSource.DataPoints.Add('3|4|?|text1'); FSource.DataPoints.Add('5|6|$FF0000|'); AssertEquals(2, FSource.Count); AssertItemEquals(FSource[0]^, 3, 4, 'text1'); AssertItemEquals(FSource[1]^, 5, 6, '', $FF0000); FSource[0]^.Color := 0; AssertEquals('3|4|$000000|text1', FSource.DataPoints[0]); FSource.DataPoints.Add('7|8|0|two words'); AssertEquals('two words', FSource[2]^.Text); end; procedure TListSourceTest.DataPointSeparator; var oldSeparator: Char; begin FSource.Clear; oldSeparator := DefaultFormatSettings.DecimalSeparator; try DefaultFormatSettings.DecimalSeparator := ':'; FSource.DataPoints.Add('3:5'); AssertEquals(3.5, FSource[0]^.X); FSource.DataPoints[0] := '4.5'; AssertEquals(4.5, FSource[0]^.X); finally DefaultFormatSettings.DecimalSeparator := oldSeparator; end; end; procedure TListSourceTest.Enum; var it: PChartDataItem; s: Double = 0; begin FSource.Clear; for it in FSource do s += 1; AssertEquals(0, s); FSource.Add(10, 1); FSource.Add(20, 7); for it in FSource do s += it^.X + it^.Y; AssertEquals(38, s); end; procedure TListSourceTest.Extent; procedure AssertExtent(AX1, AY1, AX2, AY2: Double); begin with FSource.Extent do begin AssertEquals('X1', AX1, a.X); AssertEquals('Y1', AY1, a.Y); AssertEquals('X2', AX2, b.X); AssertEquals('Y2', AY2, b.Y); end; end; begin FSource.Clear; Assert(IsInfinite(FSource.Extent.a.X) and IsInfinite(FSource.Extent.a.Y)); Assert(IsInfinite(FSource.Extent.b.X) and IsInfinite(FSource.Extent.b.Y)); FSource.Add(1, 2); AssertExtent(1, 2, 1, 2); FSource.Add(3, 4); AssertExtent(1, 2, 3, 4); FSource.SetXValue(0, -1); AssertExtent(-1, 2, 3, 4); FSource.SetXValue(1, -2); AssertExtent(-2, 2, -1, 4); FSource.SetXValue(1, SafeNaN); AssertExtent(-1, 2, -1, 4); FSource.SetXValue(1, -2); FSource.SetYValue(0, 5); AssertExtent(-2, 4, -1, 5); FSource.SetYValue(0, 4.5); AssertExtent(-2, 4, -1, 4.5); FSource.SetYValue(1, SafeNaN); AssertExtent(-2, 4.5, -1, 4.5); FSource.Delete(1); AssertExtent(-1, 4.5, -1, 4.5); FSource.Clear; FSource.Add(1, 1); FSource.Add(2, 2); FSource.Add(3, 3); FSource.Add(4, 4); FSource.Delete(0); FSource.Delete(1); AssertExtent(2, 2, 4, 4); end; procedure TListSourceTest.Multi; begin FSource.Clear; AssertEquals(1, FSource.YCount); FSource.Add(1, 2); FSource.YCount := 2; AssertEquals([0], FSource[0]^.YList); FSource.SetYList(0, [3, 4]); AssertEquals('Extra items are chopped', [3], FSource[0]^.YList); FSource.DataPoints.Add('1|2|3|4|?|t'); AssertEquals(3, FSource.YCount); AssertEquals(2, FSource[1]^.Y); AssertEquals([3, 4], FSource[1]^.YList); FSource.AddXYList(2, [7, 8, 9]); AssertEquals(3, FSource.YCount); AssertEquals(7, FSource[2]^.Y); AssertEquals([8, 9], FSource[2]^.YList); FSource.AddXYList(3, [10]); AssertEquals(4, FSource.Count); AssertEquals(3, FSource.YCount); AssertEquals(10, FSource[3]^.Y); AssertEquals([0, 0], FSource[3]^.YList); try FSource.AddXYList(4, []); Fail('Empty YList'); except on E: Exception do AssertTrue('Empty YList', E is TListChartSource.EYListEmptyError); end; end; procedure TListSourceTest.SetUp; begin inherited SetUp; FSource := TListChartSource.Create(nil); end; procedure TListSourceTest.TearDown; begin FreeAndNil(FSource); inherited TearDown; end; { TRandomSourceTest } procedure TRandomSourceTest.Extent; var s: TRandomChartSource; ext: TDoubleRect; begin s := TRandomChartSource.Create(nil); try s.XMin := 10; s.XMax := 20; s.YMin := 5; s.YMax := 6; s.PointsNumber := 1000; ext := s.Extent; AssertEquals(10, ext.a.X); AssertEquals(20, ext.b.X); Assert(ext.a.Y > 5); Assert(ext.b.Y < 6); Assert(ext.a.Y < ext.b.Y); finally s.Free; end; end; { TIntervalSourceTest } procedure TIntervalSourceTest.AssertValueEquals( const AExpected: array of Double; const AActual: TChartValueTextArray); var i: Integer; a: array of Double; begin SetLength(a, Length(AActual)); for i := 0 to High(AActual) do a[i] := AActual[i].FValue; AssertEquals(AExpected, a, 1e-6); end; procedure TIntervalSourceTest.IntervalSource; var p: TValuesInRangeParams; src: TIntervalChartSource; r: TChartValueTextArray = nil; begin p := VDummyTransform.PrepareValuesInRangeParams; src := TIntervalChartSource.Create(nil); try src.Params.MaxLength := 15; src.ValuesInRange(p, r); AssertValueEquals([20, 30, 40, 50, 60, 70], r); src.Params.Options := [aipUseCount]; src.Params.Count := 7; src.Params.Tolerance := 1; src.ValuesInRange(p, r); AssertValueEquals([24, 30, 36, 41, 47, 52, 58, 63, 69, 75], r); finally p.FIntervals.Free; src.Free; end; end; procedure TIntervalSourceTest.ListSource; var i: Integer; p: TValuesInRangeParams; r: TChartValueTextArray = nil; src: TListChartSource; procedure Check(const AExpected: array of Double); begin r := nil; src.ValuesInRange(p, r); AssertValueEquals(AExpected, r); end; begin p := VDummyTransform.PrepareValuesInRangeParams; p.FFormat := '%4:g'; src := TListChartSource.Create(nil); for i := 1 to 10 do src.Add(10 * i, i); try Check([20, 30, 40, 50, 60, 70]); p.FIntervals.MinLength := 20; Check([20, 30, 50, 70]); p.FMin := 81; p.FMax := 82; Check([80, 90]); p.FMin := 9; p.FMax := 11; Check([10, 20]); src.Add(8, 11); Check([8, 10, 20]); p.FMin := 1; p.FMax := 20; p.FIntervals.Options := p.FIntervals.Options - [aipUseMinLength]; Check([8, 10, 20, 30]); p.FIntervals.Options := p.FIntervals.Options + [aipUseMinLength]; p.FMax := 50; Check([8, 30, 50, 60]); AssertEquals('Lower bound not first in-range value', '8', r[0].FText); src.Sort; Check([8, 30, 50, 60]); p.FIntervals.Tolerance := 3; Check([10, 30, 50, 60]); finally p.FIntervals.Free; src.Free; end; end; initialization RegisterTests([ TListSourceTest, TRandomSourceTest, TCalculatedSourceTest, TIntervalSourceTest]); end.