1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Sonic Visualiser
5     An audio file viewer and annotation editor.
6     Centre for Digital Music, Queen Mary, University of London.
7 
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.  See the file
12     COPYING included with this distribution for more information.
13 */
14 
15 #ifndef TEST_SCALE_TICK_INTERVALS_H
16 #define TEST_SCALE_TICK_INTERVALS_H
17 
18 #include "../ScaleTickIntervals.h"
19 
20 #include <QObject>
21 #include <QtTest>
22 #include <QDir>
23 
24 #include <iostream>
25 
26 using namespace std;
27 
28 class TestScaleTickIntervals : public QObject
29 {
30     Q_OBJECT
31 
printDiff(vector<ScaleTickIntervals::Tick> ticks,vector<ScaleTickIntervals::Tick> expected)32     void printDiff(vector<ScaleTickIntervals::Tick> ticks,
33                    vector<ScaleTickIntervals::Tick> expected) {
34 
35         SVCERR << "Have " << ticks.size() << " ticks, expected "
36                << expected.size() << endl;
37         for (int i = 0; i < int(ticks.size()); ++i) {
38             SVCERR << i << ": have " << ticks[i].value << " \""
39                    << ticks[i].label << "\", expected ";
40             if (i < int(expected.size())) {
41                 SVCERR << expected[i].value << " \"" << expected[i].label
42                        << "\"" << endl;
43             } else {
44                 SVCERR << "(n/a)" << endl;
45             }
46         }
47     }
48 
49     void compareTicks(ScaleTickIntervals::Ticks ticks,
50                       ScaleTickIntervals::Ticks expected,
51                       bool fuzzier = false)
52     {
53         for (int i = 0; i < int(expected.size()); ++i) {
54             if (i < int(ticks.size())) {
55                 bool pass = true;
56                 if (ticks[i].label != expected[i].label) {
57                     pass = false;
58                 } else {
59                     double eps = fuzzier ? 1e-5 : 1e-10;
60                     double diff = fabs(ticks[i].value - expected[i].value);
61                     double limit = max(eps, fabs(ticks[i].value) * eps);
62                     if (diff > limit) {
63                         pass = false;
64                     }
65                 }
66                 if (!pass) {
67                     printDiff(ticks, expected);
68                 }
69                 QCOMPARE(ticks[i].label, expected[i].label);
70                 QCOMPARE(ticks[i].value, expected[i].value);
71             }
72         }
73         if (ticks.size() != expected.size()) {
74             printDiff(ticks, expected);
75         }
76         QCOMPARE(ticks.size(), expected.size());
77     }
78 
79 private slots:
linear_0_1_10()80     void linear_0_1_10()
81     {
82         auto ticks = ScaleTickIntervals::linear({ 0, 1, 10 });
83         ScaleTickIntervals::Ticks expected {
84             { 0.0, "0.0" },
85             { 0.1, "0.1" },
86             { 0.2, "0.2" },
87             { 0.3, "0.3" },
88             { 0.4, "0.4" },
89             { 0.5, "0.5" },
90             { 0.6, "0.6" },
91             { 0.7, "0.7" },
92             { 0.8, "0.8" },
93             { 0.9, "0.9" },
94             { 1.0, "1.0" }
95         };
96         compareTicks(ticks, expected);
97     }
98 
linear_0_5_5()99     void linear_0_5_5()
100     {
101         auto ticks = ScaleTickIntervals::linear({ 0, 5, 5 });
102         ScaleTickIntervals::Ticks expected {
103             { 0, "0" },
104             { 1, "1" },
105             { 2, "2" },
106             { 3, "3" },
107             { 4, "4" },
108             { 5, "5" },
109         };
110         compareTicks(ticks, expected);
111     }
112 
linear_0_10_5()113     void linear_0_10_5()
114     {
115         auto ticks = ScaleTickIntervals::linear({ 0, 10, 5 });
116         ScaleTickIntervals::Ticks expected {
117             { 0, "0" },
118             { 2, "2" },
119             { 4, "4" },
120             { 6, "6" },
121             { 8, "8" },
122             { 10, "10" }
123         };
124         compareTicks(ticks, expected);
125     }
126 
linear_10_0_5()127     void linear_10_0_5()
128     {
129         auto ticks = ScaleTickIntervals::linear({ 10, 0, 5 });
130         ScaleTickIntervals::Ticks expected {
131             { 0, "0" },
132             { 2, "2" },
133             { 4, "4" },
134             { 6, "6" },
135             { 8, "8" },
136             { 10, "10" }
137         };
138         compareTicks(ticks, expected);
139     }
140 
linear_m10_0_5()141     void linear_m10_0_5()
142     {
143         auto ticks = ScaleTickIntervals::linear({ -10, 0, 5 });
144         ScaleTickIntervals::Ticks expected {
145             { -10, "-10" },
146             { -8, "-8" },
147             { -6, "-6" },
148             { -4, "-4" },
149             { -2, "-2" },
150             { 0, "0" }
151         };
152         compareTicks(ticks, expected);
153     }
154 
linear_0_m10_5()155     void linear_0_m10_5()
156     {
157         auto ticks = ScaleTickIntervals::linear({ 0, -10, 5 });
158         ScaleTickIntervals::Ticks expected {
159             { -10, "-10" },
160             { -8, "-8" },
161             { -6, "-6" },
162             { -4, "-4" },
163             { -2, "-2" },
164             { 0, "0" }
165         };
166         compareTicks(ticks, expected);
167     }
168 
linear_0_0p1_5()169     void linear_0_0p1_5()
170     {
171         auto ticks = ScaleTickIntervals::linear({ 0, 0.1, 5 });
172         ScaleTickIntervals::Ticks expected {
173             { 0.00, "0.00" },
174             { 0.02, "0.02" },
175             { 0.04, "0.04" },
176             { 0.06, "0.06" },
177             { 0.08, "0.08" },
178             { 0.10, "0.10" }
179         };
180         compareTicks(ticks, expected);
181     }
182 
linear_0_0p01_5()183     void linear_0_0p01_5()
184     {
185         auto ticks = ScaleTickIntervals::linear({ 0, 0.01, 5 });
186         ScaleTickIntervals::Ticks expected {
187             { 0.000, "0.000" },
188             { 0.002, "0.002" },
189             { 0.004, "0.004" },
190             { 0.006, "0.006" },
191             { 0.008, "0.008" },
192             { 0.010, "0.010" }
193         };
194         compareTicks(ticks, expected);
195     }
196 
linear_0_0p005_5()197     void linear_0_0p005_5()
198     {
199         auto ticks = ScaleTickIntervals::linear({ 0, 0.005, 5 });
200         ScaleTickIntervals::Ticks expected {
201             { 0.000, "0.000" },
202             { 0.001, "0.001" },
203             { 0.002, "0.002" },
204             { 0.003, "0.003" },
205             { 0.004, "0.004" },
206             { 0.005, "0.005" }
207         };
208         compareTicks(ticks, expected);
209     }
210 
linear_0_0p001_5()211     void linear_0_0p001_5()
212     {
213         auto ticks = ScaleTickIntervals::linear({ 0, 0.001, 5 });
214         ScaleTickIntervals::Ticks expected {
215             { 0.0000, "0.0e+00" },
216             { 0.0002, "2.0e-04" },
217             { 0.0004, "4.0e-04" },
218             { 0.0006, "6.0e-04" },
219             { 0.0008, "8.0e-04" },
220             { 0.0010, "1.0e-03" }
221         };
222         compareTicks(ticks, expected);
223     }
224 
linear_1_1p001_5()225     void linear_1_1p001_5()
226     {
227         auto ticks = ScaleTickIntervals::linear({ 1, 1.001, 5 });
228         ScaleTickIntervals::Ticks expected {
229             { 1.0000, "1.0000" },
230             { 1.0002, "1.0002" },
231             { 1.0004, "1.0004" },
232             { 1.0006, "1.0006" },
233             { 1.0008, "1.0008" },
234             { 1.0010, "1.0010" }
235         };
236         compareTicks(ticks, expected);
237     }
238 
linear_0p001_1_5()239     void linear_0p001_1_5()
240     {
241         auto ticks = ScaleTickIntervals::linear({ 0.001, 1, 5 });
242         ScaleTickIntervals::Ticks expected {
243             { 0.1, "0.1" },
244             { 0.3, "0.3" },
245             { 0.5, "0.5" },
246             { 0.7, "0.7" },
247             { 0.9, "0.9" },
248         };
249         compareTicks(ticks, expected);
250     }
251 
linear_10000_10010_5()252     void linear_10000_10010_5()
253     {
254         auto ticks = ScaleTickIntervals::linear({ 10000, 10010, 5 });
255         ScaleTickIntervals::Ticks expected {
256             { 10000, "10000" },
257             { 10002, "10002" },
258             { 10004, "10004" },
259             { 10006, "10006" },
260             { 10008, "10008" },
261             { 10010, "10010" },
262         };
263         compareTicks(ticks, expected);
264     }
265 
linear_10000_20000_5()266     void linear_10000_20000_5()
267     {
268         auto ticks = ScaleTickIntervals::linear({ 10000, 20000, 5 });
269         ScaleTickIntervals::Ticks expected {
270             { 10000, "10000" },
271             { 12000, "12000" },
272             { 14000, "14000" },
273             { 16000, "16000" },
274             { 18000, "18000" },
275             { 20000, "20000" },
276         };
277         compareTicks(ticks, expected);
278     }
279 
linear_m1_1_10()280     void linear_m1_1_10()
281     {
282         auto ticks = ScaleTickIntervals::linear({ -1, 1, 10 });
283         ScaleTickIntervals::Ticks expected {
284             { -1.0, "-1.0" },
285             { -0.8, "-0.8" },
286             { -0.6, "-0.6" },
287             { -0.4, "-0.4" },
288             { -0.2, "-0.2" },
289             { 0.0, "0.0" },
290             { 0.2, "0.2" },
291             { 0.4, "0.4" },
292             { 0.6, "0.6" },
293             { 0.8, "0.8" },
294             { 1.0, "1.0" }
295         };
296         compareTicks(ticks, expected);
297     }
298 
linear_221p23_623p7_57p4()299     void linear_221p23_623p7_57p4()
300     {
301         auto ticks = ScaleTickIntervals::linear({ 221.23, 623.7, 4 });
302         // only 4 ticks, not 5, because none of the rounded tick
303         // values lies on an end value
304         ScaleTickIntervals::Ticks expected {
305             { 300, "300" },
306             { 400, "400" },
307             { 500, "500" },
308             { 600, "600" },
309         };
310         compareTicks(ticks, expected);
311     }
312 
linear_sqrt2_pi_7()313     void linear_sqrt2_pi_7()
314     {
315         auto ticks = ScaleTickIntervals::linear({ sqrt(2.0), M_PI, 7 });
316         // This would be better in steps of 0.25, but we only round to
317         // integral powers of ten
318         ScaleTickIntervals::Ticks expected {
319             { 1.5, "1.5" },
320             { 1.7, "1.7" },
321             { 1.9, "1.9" },
322             { 2.1, "2.1" },
323             { 2.3, "2.3" },
324             { 2.5, "2.5" },
325             { 2.7, "2.7" },
326             { 2.9, "2.9" },
327             { 3.1, "3.1" },
328         };
329         compareTicks(ticks, expected);
330     }
331 
linear_pi_avogadro_7()332     void linear_pi_avogadro_7()
333     {
334         auto ticks = ScaleTickIntervals::linear({ M_PI, 6.022140857e23, 7 });
335         ScaleTickIntervals::Ticks expected {
336             // not perfect, but ok-ish
337             { 0, "0.0e+00" },
338             { 9e+22, "9.0e+22" },
339             { 1.8e+23, "1.8e+23" },
340             { 2.7e+23, "2.7e+23" },
341             { 3.6e+23, "3.6e+23" },
342             { 4.5e+23, "4.5e+23" },
343             { 5.4e+23, "5.4e+23" },
344         };
345         compareTicks(ticks, expected);
346     }
347 
linear_2_3_1()348     void linear_2_3_1()
349     {
350         auto ticks = ScaleTickIntervals::linear({ 2, 3, 1 });
351         ScaleTickIntervals::Ticks expected {
352             { 2.0, "2" },
353             { 3.0, "3" }
354         };
355         compareTicks(ticks, expected);
356     }
357 
linear_2_3_2()358     void linear_2_3_2()
359     {
360         auto ticks = ScaleTickIntervals::linear({ 2, 3, 2 });
361         ScaleTickIntervals::Ticks expected {
362             { 2.0, "2.0" },
363             { 2.5, "2.5" },
364             { 3.0, "3.0" }
365         };
366         compareTicks(ticks, expected);
367     }
368 
linear_2_3_3()369     void linear_2_3_3()
370     {
371         auto ticks = ScaleTickIntervals::linear({ 2, 3, 3 });
372         ScaleTickIntervals::Ticks expected {
373             { 2.0, "2.0" },
374             { 2.3, "2.3" },
375             { 2.6, "2.6" },
376             { 2.9, "2.9" }
377         };
378         compareTicks(ticks, expected);
379     }
380 
linear_2_3_4()381     void linear_2_3_4()
382     {
383         auto ticks = ScaleTickIntervals::linear({ 2, 3, 4 });
384         // This would be better in steps of 0.25, but we only round to
385         // integral powers of ten
386         ScaleTickIntervals::Ticks expected {
387             { 2.0, "2.0" },
388             { 2.3, "2.3" },
389             { 2.6, "2.6" },
390             { 2.9, "2.9" }
391         };
392         compareTicks(ticks, expected);
393     }
394 
linear_2_3_5()395     void linear_2_3_5()
396     {
397         auto ticks = ScaleTickIntervals::linear({ 2, 3, 5 });
398         ScaleTickIntervals::Ticks expected {
399             { 2.0, "2.0" },
400             { 2.2, "2.2" },
401             { 2.4, "2.4" },
402             { 2.6, "2.6" },
403             { 2.8, "2.8" },
404             { 3.0, "3.0" }
405         };
406         compareTicks(ticks, expected);
407     }
408 
linear_2_3_6()409     void linear_2_3_6()
410     {
411         auto ticks = ScaleTickIntervals::linear({ 2, 3, 6 });
412         ScaleTickIntervals::Ticks expected {
413             { 2.0, "2.0" },
414             { 2.2, "2.2" },
415             { 2.4, "2.4" },
416             { 2.6, "2.6" },
417             { 2.8, "2.8" },
418             { 3.0, "3.0" }
419         };
420         compareTicks(ticks, expected);
421     }
422 
linear_1_1_10()423     void linear_1_1_10()
424     {
425         // pathological range
426         auto ticks = ScaleTickIntervals::linear({ 1, 1, 10 });
427         ScaleTickIntervals::Ticks expected {
428             { 1.0, "1" }
429         };
430         compareTicks(ticks, expected);
431     }
432 
linear_0_0_10()433     void linear_0_0_10()
434     {
435         // pathological range
436         auto ticks = ScaleTickIntervals::linear({ 0, 0, 10 });
437         ScaleTickIntervals::Ticks expected {
438             { 0.0, "0.0" }
439         };
440         compareTicks(ticks, expected);
441     }
442 
linear_0_1_1()443     void linear_0_1_1()
444     {
445         auto ticks = ScaleTickIntervals::linear({ 0, 1, 1 });
446         ScaleTickIntervals::Ticks expected {
447             { 0.0, "0" },
448             { 1.0, "1" }
449         };
450         compareTicks(ticks, expected);
451     }
452 
linear_0_1_0()453     void linear_0_1_0()
454     {
455         // senseless input
456         auto ticks = ScaleTickIntervals::linear({ 0, 1, 0 });
457         ScaleTickIntervals::Ticks expected {
458             { 0.0, "0.0" },
459         };
460         compareTicks(ticks, expected);
461     }
462 
linear_0_1_m1()463     void linear_0_1_m1()
464     {
465         // senseless input
466         auto ticks = ScaleTickIntervals::linear({ 0, 1, -1 });
467         ScaleTickIntervals::Ticks expected {
468             { 0.0, "0.0" },
469         };
470         compareTicks(ticks, expected);
471     }
472 
linear_0p465_778_10()473     void linear_0p465_778_10()
474     {
475         // a case that gave unsatisfactory results in real life
476         // (initially it had the first tick at 1)
477         auto ticks = ScaleTickIntervals::linear({ 0.465, 778.08, 10 });
478         ScaleTickIntervals::Ticks expected {
479             { 10, "10" },
480             { 90, "90" },
481             { 170, "170" },
482             { 250, "250" },
483             { 330, "330" },
484             { 410, "410" },
485             { 490, "490" },
486             { 570, "570" },
487             { 650, "650" },
488             { 730, "730" },
489         };
490         compareTicks(ticks, expected);
491     }
492 
log_1_10_2()493     void log_1_10_2()
494     {
495         auto ticks = ScaleTickIntervals::logarithmic({ 1, 10, 2 });
496         ScaleTickIntervals::Ticks expected {
497             { 1.0, "1.0" },
498             { 3.2, "3.2" },
499             { 10.0, "10" },
500         };
501         compareTicks(ticks, expected);
502     }
503 
log_0_10_2()504     void log_0_10_2()
505     {
506         auto ticks = ScaleTickIntervals::logarithmic({ 0, 10, 2 });
507         ScaleTickIntervals::Ticks expected {
508             { 1e-6, "1e-06" },
509             { 1, "1" },
510         };
511         compareTicks(ticks, expected);
512     }
513 
log_pi_avogadro_7()514     void log_pi_avogadro_7()
515     {
516         auto ticks = ScaleTickIntervals::logarithmic({ M_PI, 6.022140857e23, 7 });
517         ScaleTickIntervals::Ticks expected {
518             { 1000, "1000" },
519             { 1e+06, "1e+06" },
520             { 1e+09, "1e+09" },
521             { 1e+12, "1e+12" },
522             { 1e+15, "1e+15" },
523             { 1e+18, "1e+18" },
524             { 1e+21, "1e+21" },
525         };
526         compareTicks(ticks, expected, true);
527     }
528 
log_0p465_778_10()529     void log_0p465_778_10()
530     {
531         auto ticks = ScaleTickIntervals::logarithmic({ 0.465, 778.08, 10 });
532         ScaleTickIntervals::Ticks expected {
533             { 0.5, "0.50" },
534             { 1, "1.0" },
535             { 2, "2.0" },
536             { 4, "4.0" },
537             { 8, "8.0" },
538             { 16, "16" },
539             { 32, "32" },
540             { 64, "64" },
541             { 130, "130" },
542             { 260, "260" },
543             { 510, "510" },
544         };
545         compareTicks(ticks, expected);
546     }
547 
log_1_10k_10()548     void log_1_10k_10()
549     {
550         auto ticks = ScaleTickIntervals::logarithmic({ 1.0, 10000.0, 10 });
551         ScaleTickIntervals::Ticks expected {
552             { 1.0, "1.0" },
553             { 2.5, "2.5" },
554             { 6.3, "6.3" },
555             { 16.0, "16" },
556             { 40.0, "40" },
557             { 100.0, "100" },
558             { 250.0, "250" },
559             { 630.0, "630" },
560             { 1600.0, "1600" },
561             { 4000.0, "4000" },
562             { 10000.0, "1e+04" },
563         };
564         compareTicks(ticks, expected, true);
565     }
566 
log_80_10k_6()567     void log_80_10k_6()
568     {
569         auto ticks = ScaleTickIntervals::logarithmic({ 80.0, 10000.0, 6 });
570         ScaleTickIntervals::Ticks expected {
571             { 130, "130" },
572             { 260, "260" },
573             { 510, "510" },
574             { 1000, "1000" },
575             { 2000, "2000" },
576             { 4100, "4100" },
577             { 8200, "8200" }
578         };
579         compareTicks(ticks, expected, true);
580     }
581 
log_80_800k_10()582     void log_80_800k_10()
583     {
584         auto ticks = ScaleTickIntervals::logarithmic({ 80.0, 800000.0, 10 });
585         ScaleTickIntervals::Ticks expected {
586             { 100, "100" },
587             { 250, "250" },
588             { 630, "630" },
589             { 1600, "1600" },
590             { 4000, "4000" },
591             { 10000, "1e+04" },
592             { 25000, "2.5e+04" },
593             { 63000, "6.3e+04" },
594             { 160000, "1.6e+05" },
595             { 400000, "4e+05" },
596         };
597         compareTicks(ticks, expected, true);
598     }
599 
log_0_1_0()600     void log_0_1_0()
601     {
602         // senseless input
603         auto ticks = ScaleTickIntervals::logarithmic({ 0, 1, 0 });
604         ScaleTickIntervals::Ticks expected {
605         };
606         compareTicks(ticks, expected);
607     }
608 
log_0_1_m1()609     void log_0_1_m1()
610     {
611         // senseless input
612         auto ticks = ScaleTickIntervals::logarithmic({ 0, 1, -1 });
613         ScaleTickIntervals::Ticks expected {
614         };
615         compareTicks(ticks, expected);
616     }
617 
618 };
619 
620 #endif
621 
622 
623