1 {
2  *****************************************************************************
3   See the file COPYING.modifiedLGPL.txt, included in this distribution,
4   for details about the license.
5  *****************************************************************************
6 
7   Authors: Alexander Klenin
8 
9 }
10 
11 unit SourcesTest;
12 
13 {$mode objfpc}{$H+}{$R+}
14 
15 interface
16 
17 uses
18   Classes, SysUtils, FPCUnit, TestRegistry,
19   TAChartUtils, TACustomSource, TAIntervalSources, TASources;
20 
21 type
22 
23   { TListSourceTest }
24 
25   TListSourceTest = class(TTestCase)
26   private
27     FSource: TListChartSource;
28 
29     procedure AssertItemEquals(
30       const AItem: TChartDataItem; AX, AY: Double; AText: String = '';
31       AColor: TChartColor = clTAColor);
Comparenull32     function Compare(AItem1, AItem2: Pointer): Integer;
33   protected
34     procedure SetUp; override;
35     procedure TearDown; override;
36   published
37     procedure Basic;
38     procedure Bounds;
39     procedure Cache;
40     procedure DataPoint;
41     procedure DataPointSeparator;
42     procedure Enum;
43     procedure Extent;
44     procedure Multi;
45     procedure Sort;
46   end;
47 
48   { TRandomSourceTest }
49 
50   TRandomSourceTest = class(TTestCase)
51   published
52     procedure Extent;
53   end;
54 
55   { TCalculatedSourceTest }
56 
57   TCalculatedSourceTest = class(TTestCase)
58   private
59     FOrigin: TListChartSource;
60     FSource: TCalculatedChartSource;
61   protected
62     procedure SetUp; override;
63     procedure TearDown; override;
64   published
65     procedure Accumulate;
66     procedure Derivative;
67     procedure Percentage;
68     procedure Reorder;
69   end;
70 
71   TIntervalSourceTest = class(TTestCase)
72   private
73     procedure AssertValueEquals(
74       const AExpected: array of Double; const AActual: TChartValueTextArray);
75   published
76     procedure IntervalSource;
77     procedure ListSource;
78   end;
79 
80 implementation
81 
82 uses
83   Math, TAMath, AssertHelpers;
84 
85 type
86   TDummyTransform = object
87   public
IdentityDoublenull88     function IdentityDouble(AX: Double): Double;
IdentityIntegernull89     function IdentityInteger(AX: Integer): Integer;
PrepareValuesInRangeParamsnull90     function PrepareValuesInRangeParams: TValuesInRangeParams;
Roundnull91     function Round(AX: Double): Integer;
92   end;
93 
94 var
95   VDummyTransform: TDummyTransform;
96 
97 { TDummyTransform }
98 
TDummyTransform.IdentityDoublenull99 function TDummyTransform.IdentityDouble(AX: Double): Double;
100 begin
101   Result := AX;
102 end;
103 
IdentityIntegernull104 function TDummyTransform.IdentityInteger(AX: Integer): Integer;
105 begin
106   Result := AX;
107 end;
108 
PrepareValuesInRangeParamsnull109 function TDummyTransform.PrepareValuesInRangeParams: TValuesInRangeParams;
110 begin
111   with Result do begin
112     FAxisToGraph := @IdentityDouble;
113     FGraphToAxis := @IdentityDouble;
114     FFormat := '';
115     FGraphToImage := @Round;
116     FMin := 30;
117     FMax := 69;
118     FMinStep := 0;
119     FScale := @IdentityInteger;
120     FUseY := false;
121     FIntervals := TChartAxisIntervalParams.Create(nil);
122   end;
123 end;
124 
TDummyTransform.Roundnull125 function TDummyTransform.Round(AX: Double): Integer;
126 begin
127   Result := System.Round(AX);
128 end;
129 
130 { TCalculatedSourceTest }
131 
132 procedure TCalculatedSourceTest.Accumulate;
133 var
134   i, j: Integer;
135   rng: TMWCRandomGenerator;
136 begin
137   FSource.AccumulationMethod := camSum;
138   FSource.AccumulationRange := 2;
139   AssertEquals(3, FSource.YCount);
140   AssertEquals(1, FSource[0]^.X);
141   AssertEquals(102, FSource[0]^.Y);
142   AssertEquals(2, FSource[1]^.X);
143   AssertEquals(102 + 202, FSource[1]^.Y);
144   AssertEquals(202 + 302, FSource[2]^.Y);
145   FSource.AccumulationDirection := cadForward;
146   AssertEquals(202 + 302, FSource[1]^.Y);
147   AssertEquals(302 + 402, FSource[2]^.Y);
148   FSource.AccumulationDirection := cadBackward;
149   FSource.AccumulationMethod := camAverage;
150   AssertEquals((2002 + 2102) / 2, FSource[20]^.Y);
151   AssertEquals(1, FSource[0]^.X);
152   AssertEquals(102, FSource[0]^.Y);
153   AssertEquals((102 + 202) / 2, FSource[1]^.Y);
154   AssertEquals(102, FSource[0]^.Y);
155   FSource.AccumulationDirection := cadCenter;
156   AssertEquals((1102 + 1202 + 1302) / 3, FSource[11]^.Y);
157   FSource.AccumulationDirection := cadBackward;
158 
159   FSource.AccumulationRange := 5;
160   rng := TMWCRandomGenerator.Create;
161   try
162     rng.Seed := 89237634;
163     for i := 1 to 100 do begin
164       j := rng.GetInRange(5, FSource.Count - 1);
165       AssertEquals(IntToStr(j), (j - 1) * 100 + 2, FSource[j]^.Y);
166     end;
167     FSource.AccumulationRange := 0;
168     FSource.AccumulationMethod := camSum;
169     rng.Seed := 23784538;
170     for i := 1 to 20 do begin
171       j := rng.GetInRange(0, FSource.Count - 1);
172       AssertEquals(
173         IntToStr(j), (j + 1) * (j + 2) * 50 + (j + 1) * 3, FSource[j]^.YList[0]);
174     end;
175   finally
176     rng.Free;
177   end;
178 end;
179 
180 procedure TCalculatedSourceTest.Derivative;
181 begin
182   FSource.AccumulationMethod := camDerivative;
183   FSource.AccumulationRange := 2;
184   FOrigin.SetYValue(1, 202);
185   AssertTrue(IsNan(FSource[0]^.Y));
186   AssertEquals(100, FSource[1]^.Y);
187   FSource.AccumulationDirection := cadCenter;
188   AssertEquals(100, FSource[0]^.Y);
189 end;
190 
191 procedure TCalculatedSourceTest.Percentage;
192 begin
193   FSource.Percentage := true;
194   AssertEquals(3, FSource.YCount);
195   AssertEquals(102 / (102 + 103 + 104) * 100, FSource[0]^.Y);
196   AssertEquals(103 / (102 + 103 + 104) * 100, FSource[0]^.YList[0]);
197 end;
198 
199 procedure TCalculatedSourceTest.Reorder;
200 var
201   i: Integer;
202 begin
203   AssertEquals(3, FSource.YCount);
204   FSource.ReorderYList := '2';
205   AssertEquals(1, FSource.YCount);
206   AssertEquals(104, FSource[0]^.Y);
207   AssertEquals(204, FSource[1]^.Y);
208   FSource.ReorderYList := '0,2';
209   AssertEquals(2, FSource.YCount);
210   AssertEquals(102, FSource[0]^.Y);
211   AssertEquals(104, FSource[0]^.YList[0]);
212   AssertEquals(202, FSource[1]^.Y);
213   AssertEquals(204, FSource[1]^.YList[0]);
214   FSource.ReorderYList := '1,1,1';
215   AssertEquals(3, FSource.YCount);
216   AssertEquals(103, FSource[0]^.Y);
217   AssertEquals([103, 103], FSource[0]^.YList);
218   FSource.ReorderYList := '';
219   for i := 0 to FSource.Count - 1 do begin
220     AssertEquals(FOrigin[i]^.Y, FSource[i]^.Y);
221     AssertEquals(FOrigin[i]^.YList, FSource[i]^.YList);
222   end;
223 end;
224 
225 procedure TCalculatedSourceTest.SetUp;
226 var
227   i: Integer;
228 begin
229   inherited SetUp;
230   FOrigin := TListChartSource.Create(nil);
231   FSource := TCalculatedChartSource.Create(nil);
232   FSource.Origin := FOrigin;
233   FOrigin.YCount := 3;
234   for i := 1 to 100 do
235     FOrigin.SetYList(FOrigin.Add(i, i * 100 + 2), [i * 100 + 3, i * 100 + 4]);
236 end;
237 
238 procedure TCalculatedSourceTest.TearDown;
239 begin
240   FreeAndNil(FSource);
241   FreeAndNil(FOrigin);
242   inherited TearDown;
243 end;
244 
245 { TListSourceTest }
246 
247 procedure TListSourceTest.AssertItemEquals(
248   const AItem: TChartDataItem; AX, AY: Double; AText: String;
249   AColor: TChartColor);
250 begin
251   AssertEquals('X', AX, AItem.X);
252   AssertEquals('Y', AY, AItem.Y);
253   AssertEquals('Text', AText, AItem.Text);
254   AssertEquals('Color', AColor, AItem.Color);
255 end;
256 
257 procedure TListSourceTest.Basic;
258 var
259   i: Integer;
260   srcDest: TListChartSource;
261 begin
262   FSource.Clear;
263   AssertEquals(0, FSource.Count);
264   AssertEquals(0, FSource.Add(1, 2, 'text', $FFFFFF));
265   AssertEquals(1, FSource.Count);
266   FSource.Delete(0);
267   AssertEquals(0, FSource.Count);
268   for i := 1 to 10 do
269     FSource.Add(i, i * 2, IntToStr(i));
270   srcDest := TListChartSource.Create(nil);
271   try
272     srcDest.CopyFrom(FSource);
273     AssertEquals(FSource.Count, srcDest.Count);
274     for i := 0 to FSource.Count - 1 do
275       with FSource[i]^ do
276         AssertItemEquals(srcDest[i]^, X, Y, Text, Color);
277   finally
278     srcDest.Free;
279   end;
280 end;
281 
282 procedure TListSourceTest.Bounds;
283 
284   procedure Check2(AExpectedLB, AExpectedUB: Integer; AXMin, AXMax: Double);
285   var
286     lb, ub: Integer;
287   begin
288     FSource.FindBounds(AXMin, AXMax, lb, ub);
289     AssertEquals(AExpectedLB, lb);
290     AssertEquals(AExpectedUB, ub);
291   end;
292 
293   procedure Check(AExpectedLB, AExpectedUB: Integer; AValue: Double);
294   begin
295     Check2(AExpectedLB, AExpectedUB, AValue, AValue)
296   end;
297 
298   procedure CheckAll;
299   begin
300     Check2(1, 2, 2, 3);
301     Check2(1, 2, 1.9, 3.1);
302     Check2(2, 1, 2.1, 2.9);
303     Check(1, 1, 2);
304     Check(1, 0, 1.9);
305     Check(0, -1, 0.9);          // below left-most point
306     Check(5, 4, 5.1);           // above right-most point
307     Check(4, 3, 4.9);           // just below right-most point
308     Check2(2, 4, 3, 1e100);
309     Check2(0, 1, -1e100, 2);
310   end;
311 
312   procedure CheckAll_XCount0;
313   begin
314     Check2(2, 3, 2, 3);
315     Check2(2, 3, 1.9, 3.1);
316     Check2(3, 2, 2.1, 2.9);
317     Check(2, 2, 2);
318     Check(2, 1, 1.9);
319     Check(0, -1, -0.1);   // below left-most point
320     Check(5, 4, 4.1);     // above right-most point
321     Check(4, 3,  3.9);    // just below right-most point
322     Check2(2, 4, 2, 1e100);
323     Check2(0, 1, -1e100, 1);
324   end;
325 
326 begin
327   FSource.Clear;
328   FSource.Add(1, 2);
329   FSource.Add(2, 3);
330   FSource.Add(3, 4);
331   FSource.Add(4, 5);
332   FSource.Add(5, 6);
333   FSource.Sorted := true;
334   CheckAll;
335   FSource.Sorted := false;
336   CheckAll;
337 
338   FSource.XCount := 0;
339   CheckAll_XCount0;
340 
341   FSource.XCount := 1;
342   FSource.SetXValue(1, SafeNan);
343   Check(2, 0, 2);
344   FSource.SetXValue(0, SafeNan);
345   Check2(2, 2, -1e100, 3);
346   Check2(2, 2, NegInfinity, 3);
347 end;
348 
349 procedure TListSourceTest.Cache;
350 begin
351   FSource.Clear;
352   FSource.Add(5, 6);
353   FSource.Add(7, 8);
354   AssertEquals(14, FSource.ValuesTotal);
355   FSource.Add(8, SafeNan);
356   AssertEquals(14, FSource.ValuesTotal);
357   FSource.Delete(2);
358   AssertEquals(14, FSource.ValuesTotal);
359   FSource.Delete(1);
360   AssertEquals(6, FSource.ValuesTotal);
361   FSource.SetYValue(0, SafeNan);
362   AssertEquals(0, FSource.ValuesTotal);
363   FSource.SetYValue(0, 5);
364   AssertEquals(5, FSource.ValuesTotal);
365 
366   FSource.Clear;
367   AssertEquals(0, FSource.ValuesTotal);
368   FSource.Add(NaN, NaN);
369   FSource.BeginUpdate;
370   FSource.EndUpdate;
371   AssertEquals(0, FSource.ValuesTotal);
372 end;
373 
374 procedure TListSourceTest.DataPoint;
375 begin
376   FSource.Clear;
377   FSource.DataPoints.Add('3|4|?|text1');
378   FSource.DataPoints.Add('5|6|$FF0000|');
379   AssertEquals(2, FSource.Count);
380   AssertItemEquals(FSource[0]^, 3, 4, 'text1');
381   AssertItemEquals(FSource[1]^, 5, 6, '', $FF0000);
382   FSource[0]^.Color := 0;
383   AssertEquals('3|4|$000000|text1', FSource.DataPoints[0]);
384   FSource.DataPoints.Add('7|8|0|two words');
385   AssertEquals('two words', FSource[2]^.Text);
386 end;
387 
388 procedure TListSourceTest.DataPointSeparator;
389 var
390   oldSeparator: Char;
391 begin
392   FSource.Clear;
393   oldSeparator := DefaultFormatSettings.DecimalSeparator;
394   try
395     DefaultFormatSettings.DecimalSeparator := ':';
396     FSource.DataPoints.Add('3:5|?|?|');
397     AssertEquals(3.5, FSource[0]^.X);
398     FSource.DataPoints[0] := '4.5|?|?|';
399     AssertEquals(4.5, FSource[0]^.X);
400   finally
401     DefaultFormatSettings.DecimalSeparator := oldSeparator;
402   end;
403 end;
404 
405 procedure TListSourceTest.Enum;
406 var
407   it: PChartDataItem;
408   s: Double = 0;
409 begin
410   FSource.Clear;
411   for it in FSource do
412     s += 1;
413   AssertEquals(0, s);
414   FSource.Add(10, 1);
415   FSource.Add(20, 7);
416   for it in FSource do
417     s += it^.X + it^.Y;
418   AssertEquals(38, s);
419 end;
420 
421 procedure TListSourceTest.Extent;
422 
423   procedure AssertExtent(AX1, AY1, AX2, AY2: Double);
424   begin
425     with FSource.Extent do begin
426       AssertEquals('X1', AX1, a.X);
427       AssertEquals('Y1', AY1, a.Y);
428       AssertEquals('X2', AX2, b.X);
429       AssertEquals('Y2', AY2, b.Y);
430     end;
431   end;
432 
433 begin
434   FSource.Clear;
435   Assert(IsInfinite(FSource.Extent.a.X) and IsInfinite(FSource.Extent.a.Y));
436   Assert(IsInfinite(FSource.Extent.b.X) and IsInfinite(FSource.Extent.b.Y));
437 
438   FSource.Add(1, 2);
439   AssertExtent(1, 2, 1, 2);
440 
441   FSource.Add(3, 4);
442   AssertExtent(1, 2, 3, 4);
443 
444   FSource.SetXValue(0, -1);
445   AssertExtent(-1, 2, 3, 4);
446 
447   FSource.SetXValue(1, -2);
448   AssertExtent(-2, 2, -1, 4);
449 
450   FSource.SetXValue(1, SafeNaN);
451   AssertExtent(-1, 2, -1, 4);
452   FSource.SetXValue(1, -2);
453 
454   FSource.SetYValue(0, 5);
455   AssertExtent(-2, 4, -1, 5);
456 
457   FSource.SetYValue(0, 4.5);
458   AssertExtent(-2, 4, -1, 4.5);
459 
460   FSource.SetYValue(1, SafeNaN);
461   AssertExtent(-2, 4.5, -1, 4.5);
462 
463   FSource.Delete(1);
464   AssertExtent(-1, 4.5, -1, 4.5);
465 
466   FSource.Clear;
467   FSource.Add(1, 1);
468   FSource.Add(2, 2);
469   FSource.Add(3, 3);
470   FSource.Add(4, 4);
471   FSource.Delete(0);
472   FSource.Delete(1);
473   AssertExtent(2, 2, 4, 4);
474 end;
475 
476 procedure TListSourceTest.Multi;
477 begin
478   FSource.Clear;
479   AssertEquals(1, FSource.YCount);
480   AssertEquals(1, FSource.YCount);
481 
482   FSource.Add(1, 2);
483   FSource.YCount := 2;
484   AssertEquals([0], FSource[0]^.YList);
485 
486   FSource.SetYList(0, [3]);
487   AssertEquals(3, FSource[0]^.YList[0]);
488 
489   FSource.DataPoints.Add('1|2|3|?|t');
490   AssertEquals(1, FSource.XCount);
491   AssertEquals(2, FSource.YCount);
492   AssertEquals(1, FSource[1]^.X);
493   AssertEquals(2, FSource[1]^.Y);
494   AssertEquals(3, FSource[1]^.YList[0]);
495 
496   // Check too many parts
497   try
498     FSource.DataPoints.Add('10|20|30|40|?|');
499   except
500     on E: Exception do
501       AssertTrue('Too many values', E is EListSourceStringError);
502   end;
503   AssertEquals(2, FSource.Count);
504 
505   // Check too few parts
506   try
507     FSource.DataPoints.Add('10|20|?|');
508   except
509     on E: Exception do
510       AssertTrue('Too few values', E is EListSourceStringError);
511   end;
512   AssertEquals(2, FSource.Count);
513 
514   // Check text part missing
515   try
516     FSource.DataPoints.Add('10|20|30|?');
517   except
518     on E: Exception do
519       AssertTrue('Text field missing', E is EListSourceStringError);
520   end;
521   AssertEquals(2, FSource.Count);
522 
523   // Check color part missing
524   try
525     FSource.DataPoints.Add('10|20|30|t');
526   except
527     on E: Exception do
528       AssertTrue('Color field missing', E is EListSourceStringError);
529   end;
530   AssertEquals(2, FSource.Count);
531 
532   // Check non-numeric parts
533   try
534     FSource.DataPoints.Add('abc|20|30|?|t');
535   except
536     on E: Exception do
537       AssertTrue('Non-numeric X', E is EListSourceStringError);
538   end;
539   try
540     FSource.DataPoints.Add('10|abc|30|?|t');
541   except
542     on E: Exception do
543       AssertTrue('Non-numeric Y', E is EListSourceStringError);
544   end;
545   try
546     FSource.DataPoints.Add('10|20|abc|?|t');
547   except
548     on E: Exception do
549       AssertTrue('Non-numeric YList', E is EListSourceStringError);
550   end;
551   try
552     FSource.DataPoints.Add('10|20|30|abc|t');
553   except
554     on E: Exception do
555       AssertTrue('Non-numeric Color', E is EListSourceStringError);
556   end;
557 
558   // check empty list
559   try
560     FSource.AddXYList(4, []);
561   except
562     on E: Exception do
563       AssertTrue('Empty YList', E is TListChartSource.EYListEmptyError);
564   end;
565   AssertEquals(2, FSource.Count);
566 
567   // Check decimal separators
568   FSource.DataPoints.Add('1.23|2.34|3|?|t');
569   AssertEquals(1.23, FSource[2]^.X);
570   AssertEquals(2.34, FSource[2]^.Y);
571 
572   FSource.DataPoints.Add('1,23|2,34|3|?|t');
573   AssertEquals(1.23, FSource[3]^.X);
574   AssertEquals(2.34, FSource[3]^.Y);
575 
576   // Check missing values
577   FSource.DataPoints.Add('|2|3|?|t');
578   AssertTrue('IsNaN', IsNaN(FSource[4]^.X));
579   AssertEquals(2, FSource[4]^.Y);
580   AssertEquals(3, FSource[4]^.YList[0]);
581 
582   FSource.DataPoints.Add('1||3|?|t');
583   AssertEquals(1, FSource[5]^.X);
584   AssertTrue('IsNaN', IsNaN(FSource[5]^.Y));
585   AssertEquals(3, FSource[5]^.YList[0]);
586 
587   FSource.DataPoints.Add('1|2|3||t');
588   AssertEquals(clTAColor, FSource[6]^.Color);
589 
590   // Check Text part containing '|' character(s)
591   FSource.DataPoints.Add('1|2|3|?|"a|b|c"');
592   AssertEquals('a|b|c', FSource[7]^.Text);
593 
594   // Check Text part containing line ending
595   FSource.DataPoints.Add('1|2|3|?|"a'+LineEnding+'b"');
596   AssertEquals('a'+LineEnding+'b', FSource[8]^.Text);
597 
598   // Check Text part containing quotes
599   FSource.DataPoints.Add('1|2|3|?|This is "quoted".');
600   AssertEquals('This is "quoted".', FSource[9]^.Text);
601 
602   FSource.DataPoints.Add('1|2|3|?|"This is ""quoted""."');
603   AssertEquals('This is "quoted".', FSource[10]^.Text);
604 
605   FSource.DataPoints.Add('1|2|3|?|"This is ""quoted"""');
606   AssertEquals('This is "quoted"', FSource[11]^.Text);
607 
608   FSource.DataPoints.Add('1|2|3|?|Single ".');
609   AssertEquals('Single ".', FSource[12]^.Text);
610 
611   FSource.DataPoints.Add('1|2|3|?|Two quotes "".');
612   AssertEquals('Two quotes "".', FSource[13]^.Text);
613 
614   // Check Text part containing separator and quotes
615   FSource.DataPoints.Add('1|2|3|?|"Number of ""|"" items"');
616   AssertEquals('Number of "|" items', FSource[14]^.Text);
617 
618   // Check multiple x and y values
619   FSource.Clear;
620   FSource.XCount := 2;
621   FSource.YCount := 3;
622   FSource.AddXListYList([1, 2], [3, 4, 5]);
623   AssertEquals(2, FSource.XCount);
624   AssertEquals(3, FSource.YCount);
625   AssertEquals(1, FSource[0]^.X);
626   AssertEquals(2, FSource[0]^.XList[0]);
627   AssertEquals(3, FSource[0]^.Y);
628   AssertEquals(4, FSource[0]^.YList[0]);
629   AssertEquals(5, FSource[0]^.YList[1]);
630 
631   FSource.DataPoints.Add('10|20|30|40|50|?|t');
632   AssertEquals(10, FSource[1]^.X);
633   AssertEquals(20, FSource[1]^.XList[0]);
634   AssertEquals(30, FSource[1]^.Y);
635   AssertEquals(40, FSource[1]^.YList[0]);
636   AssertEquals(50, FSource[1]^.YList[1]);
637 
638   // Add multiple strings in a single AddText command
639   FSource.Clear;
640   FSource.XCount := 2;
641   FSource.YCount := 3;
642   FSource.DataPoints.AddText('100|200|300|400|500|?|Data1' + LineEnding +
643                              '101|201|301|401|501|?|Data2');
644   AssertEquals(2, FSource.Count);
645   AssertEquals(2, FSource.XCount);
646   AssertEquals(3, FSource.YCount);
647   AssertEquals(100, FSource[0]^.X);
648   AssertEquals(200, FSource[0]^.XList[0]);
649   AssertEquals(300, FSource[0]^.Y);
650   AssertEquals(500, FSource[0]^.YList[1]);
651   AssertEquals('Data1', FSource[0]^.Text);
652   AssertEquals(101, FSource[1]^.X);
653   AssertEquals(501, FSource[1]^.YList[1]);
654   AssertEquals('Data2', FSource[1]^.Text);
655 
656   // Add multiple strings in a single AddStrings command
657   FSource.Datapoints.AddStrings(['110|210|310|410|510|?|ABC', '111|211|311|411|511|?|abc']);
658   AssertEquals(4, FSource.Count);
659   AssertEquals(2, FSource.XCount);
660   AssertEquals(3, FSource.YCount);
661   AssertEquals(110, FSource[2]^.X);
662   AssertEquals(210, FSource[2]^.XList[0]);
663   AssertEquals(310, FSource[2]^.Y);
664   AssertEquals(510, FSource[2]^.YList[1]);
665   AssertEquals('ABC', FSource[2]^.Text);
666   AssertEquals(111, FSource[3]^.X);
667   AssertEquals(511, FSource[3]^.YList[1]);
668   AssertEquals('abc', FSource[3]^.Text);
669 
670   (*
671   FSource.SetYList(0, [3, 4]);
672   AssertEquals('Extra items are chopped', [3], FSource[0]^.YList);
673   FSource.DataPoints.Add('1|2|3|4|?|t');
674   AssertEquals(3, FSource.YCount);
675   AssertEquals(2, FSource[1]^.Y);
676   AssertEquals([3, 4], FSource[1]^.YList);
677 
678   FSource.AddXYList(2, [7, 8, 9]);
679   AssertEquals(3, FSource.YCount);
680   AssertEquals(7, FSource[2]^.Y);
681   AssertEquals([8, 9], FSource[2]^.YList);
682   FSource.AddXYList(3, [10]);
683   AssertEquals(4, FSource.Count);
684   AssertEquals(3, FSource.YCount);
685   AssertEquals(10, FSource[3]^.Y);
686   AssertEquals([0, 0], FSource[3]^.YList);
687   try
688     FSource.AddXYList(4, []);
689     Fail('Empty YList');
690   except on E: Exception do
691     AssertTrue('Empty YList', E is TListChartSource.EYListEmptyError);
692   end;
693   *)
694 end;
695 
TListSourceTest.Comparenull696 function TListSourceTest.Compare(AItem1, AItem2: Pointer): Integer;
697 var
698   item1: PChartDataItem absolute AItem1;
699   item2: PChartDataItem absolute AItem2;
700 begin
701   Result := CompareValue(item1^.X + item1^.XList[0], item2^.X + item2^.XList[0]);
702 end;
703 
704 procedure TListSourceTest.Sort;
705 begin
706   FSource.Clear;
707   FSource.XCount := 2;
708   FSource.YCount := 2;
709   FSource.AddXListYList([1, -0.1], [10, 100], 'A');     // x1+x2 = 0.9
710   FSource.AddXListYList([9, 0.9], [90, -900], 'M');     // x1+x2 = 9.9
711   FSource.AddXListYList([5, -0.5], [50, 50], 'D');      // x1+x2 = 4.5
712 
713   FSource.SortBy := sbX;
714   FSource.SortIndex := 0;
715   FSource.SortDir := sdAscending;
716   FSource.Sorted := true;
717   AssertEquals(1, FSource[0]^.X);
718   AssertEquals(5, FSource[1]^.X);
719   AssertEquals(9, FSource[2]^.X);
720 
721   FSource.SortBy := sbX;
722   FSource.SortIndex := 1;
723   AssertEquals(-0.5, FSource[0]^.XList[0]);
724   AssertEquals(-0.1, FSource[1]^.XList[0]);
725   AssertEquals( 0.9, FSource[2]^.XList[0]);
726 
727   FSource.SortBy := sbY;
728   FSource.SortIndex := 0;
729   AssertEquals(10, FSource[0]^.Y);
730   AssertEquals(50, FSource[1]^.Y);
731   AssertEquals(90, FSource[2]^.Y);
732 
733   FSource.SortBy := sbY;
734   FSource.SortIndex := 1;
735   FSource.SortDir := sdDescending;
736   AssertEquals(100, FSource[0]^.YList[0]);
737   AssertEquals(50, FSource[1]^.YList[0]);
738   AssertEquals(-900, FSource[2]^.YList[0]);
739 
740   FSource.SortBy := sbText;
741   FSource.SortDir := sdDescending;
742   AssertEquals('M', FSource[0]^.Text);
743   AssertEquals('D', FSource[1]^.Text);
744   AssertEquals('A', FSource[2]^.Text);
745 
746   FSource.OnCompare := @Compare;
747   FSource.SortBy := sbCustom;
748   FSource.SortDir := sdAscending;
749   AssertEquals(1, FSource[0]^.X);
750   AssertEquals(5, FSource[1]^.X);
751   AssertEquals(9, FSource[2]^.X);
752 
753   FSource.OnCompare := nil;
754   FSource.Sorted := false;
755 end;
756 
757 procedure TListSourceTest.SetUp;
758 begin
759   inherited SetUp;
760   FSource := TListChartSource.Create(nil);
761 end;
762 
763 procedure TListSourceTest.TearDown;
764 begin
765   FreeAndNil(FSource);
766   inherited TearDown;
767 end;
768 
769 { TRandomSourceTest }
770 
771 procedure TRandomSourceTest.Extent;
772 var
773   s: TRandomChartSource;
774   ext: TDoubleRect;
775 begin
776   s := TRandomChartSource.Create(nil);
777   try
778     s.XMin := 10;
779     s.XMax := 20;
780     s.YMin := 5;
781     s.YMax := 6;
782     s.PointsNumber := 1000;
783     ext := s.Extent;
784     AssertEquals(10, ext.a.X);
785     AssertEquals(20, ext.b.X);
786     Assert(ext.a.Y > 5);
787     Assert(ext.b.Y < 6);
788     Assert(ext.a.Y < ext.b.Y);
789   finally
790     s.Free;
791   end;
792 end;
793 
794 { TIntervalSourceTest }
795 
796 procedure TIntervalSourceTest.AssertValueEquals(
797   const AExpected: array of Double; const AActual: TChartValueTextArray);
798 var
799   i: Integer;
800   a: array of Double;
801 begin
802   SetLength(a, Length(AActual));
803   for i := 0 to High(AActual) do
804     a[i] := AActual[i].FValue;
805   AssertEquals(AExpected, a, 1e-6);
806 end;
807 
808 procedure TIntervalSourceTest.IntervalSource;
809 var
810   p: TValuesInRangeParams;
811   src: TIntervalChartSource;
812   r: TChartValueTextArray = nil;
813 begin
814   p := VDummyTransform.PrepareValuesInRangeParams;
815   src := TIntervalChartSource.Create(nil);
816   try
817     src.Params.MaxLength := 15;
818     src.ValuesInRange(p, r);
819     AssertValueEquals([20, 30, 40, 50, 60, 70], r);
820     src.Params.Options := [aipUseCount];
821     src.Params.Count := 7;
822     src.Params.Tolerance := 1;
823     src.ValuesInRange(p, r);
824     AssertValueEquals([24, 30, 36, 41, 47, 52, 58, 63, 69, 75], r);
825   finally
826     p.FIntervals.Free;
827     src.Free;
828   end;
829 end;
830 
831 procedure TIntervalSourceTest.ListSource;
832 var
833   i: Integer;
834   p: TValuesInRangeParams;
835   r: TChartValueTextArray = nil;
836   src: TListChartSource;
837 
838   procedure Check(const AExpected: array of Double);
839   begin
840     r := nil;
841     src.ValuesInRange(p, r);
842     AssertValueEquals(AExpected, r);
843   end;
844 
845 begin
846   p := VDummyTransform.PrepareValuesInRangeParams;
847   p.FFormat := '%4:g';
848   src := TListChartSource.Create(nil);
849   for i := 1 to 10 do
850     src.Add(10 * i, i);
851   try
852     Check([20, 30, 40, 50, 60, 70]);
853     p.FIntervals.MinLength := 20;
854     Check([20, 30, 50, 70]);
855     p.FMin := 81;
856     p.FMax := 82;
857     Check([80, 90]);
858     p.FMin := 9;
859     p.FMax := 11;
860     Check([10, 20]);
861     src.Add(8, 11);
862     Check([8, 10, 20]);
863     p.FMin := 1;
864     p.FMax := 20;
865     p.FIntervals.Options := p.FIntervals.Options - [aipUseMinLength];
866     Check([8, 10, 20, 30]);
867     p.FIntervals.Options := p.FIntervals.Options + [aipUseMinLength];
868     p.FMax := 50;
869     Check([8, 30, 50, 60]);
870     AssertEquals('Lower bound not first in-range value', '8', r[0].FText);
871     src.Sort;
872     Check([8, 30, 50, 60]);
873     p.FIntervals.Tolerance := 3;
874     Check([10, 30, 50, 60]);
875   finally
876     p.FIntervals.Free;
877     src.Free;
878   end;
879 end;
880 
881 initialization
882 
883   RegisterTests([
884     TListSourceTest, TRandomSourceTest, TCalculatedSourceTest,
885     TIntervalSourceTest]);
886 
887 end.
888 
889