Lumiera  0.pre.03
»edit your freedom«
time-value-test.cpp
Go to the documentation of this file.
1 /*
2  TimeValue(Test) - working with time values and time intervals in C++...
3 
4  Copyright (C) Lumiera.org
5  2010, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
29 #include "lib/test/test-helper.hpp"
30 #include "lib/time/timevalue.hpp"
31 #include "lib/format-cout.hpp"
32 #include "lib/util.hpp"
33 
34 #include <boost/lexical_cast.hpp>
35 #include <string>
36 
37 using boost::lexical_cast;
38 using util::isnil;
39 using std::string;
40 
41 using LERR_(BOTTOM_VALUE);
42 
43 namespace lib {
44 namespace time{
45 namespace test{
46 
47 
48  /****************************************************/
54  class TimeValue_test : public Test
55  {
56  gavl_time_t
57  random_or_get (Arg arg)
58  {
59  if (isnil(arg))
60  return 1 + (rand() % 10000);
61  else
62  return lexical_cast<gavl_time_t> (arg[1]);
63  }
64 
65 
66  virtual void
67  run (Arg arg)
68  {
69  TimeValue ref (random_or_get(arg));
70 
72  checkMutableTime (ref);
73  checkTimeHash (ref);
75  verify_invalidFramerateProtection();
76  createOffsets (ref);
77  buildDuration (ref);
78  buildTimeSpan (ref);
79  compareTimeSpan (Time(ref));
80  relateTimeIntervals (ref);
81  verify_extremeValues();
82  verify_fractionalOffset();
83  }
84 
85 
90  void
92  {
93  TimeValue zero(0);
94  TimeValue one (1);
95  TimeValue max (Time::MAX);
96  TimeValue min (Time::MIN);
97 
98  TimeValue val (org);
99 
100  CHECK (zero == zero);
101  CHECK (zero <= zero);
102  CHECK (zero >= zero);
103 
104  CHECK (zero < one);
105  CHECK (min < max);
106  CHECK (min < val);
107  CHECK (val < max);
108 
109  // mixed comparisons with raw numeric time
110  gavl_time_t g2 (-2);
111  CHECK (zero > g2);
112  CHECK (one > g2);
113  CHECK (one >= g2);
114  CHECK (g2 < max);
115 
116  CHECK (!(g2 > max));
117  CHECK (!(g2 < min));
118  }
119 
120 
126  void
128  {
129  TimeVar zero;
130  TimeVar one = TimeValue(1);
131  TimeVar two = TimeValue(2);
132 
133  TimeVar var (org);
134 
135  var += two;
136  var *= 2;
137  CHECK (zero == (var - 2*(org + two)) );
138 
139  // the transient vars caused no side-effects
140  CHECK (var == 2*two + org + org);
141  CHECK (two == TimeValue(2));
142 
143  var = org; // assign new value
144  CHECK (zero == (var - org));
145 
146  CHECK (zero < one);
147  CHECK (one < two);
148  CHECK (var < Time::MAX);
149  CHECK (var > Time::MIN);
150 
151  gavl_time_t raw (var);
152  CHECK (raw == org);
153  CHECK (raw > org - two);
154 
155  // unary minus will flip around origin
156  CHECK (zero == -var + var);
157  CHECK (zero != -var);
158  CHECK (var == org); // unaltered
159  }
160 
161 
165  void
167  {
168  Time o1(org);
169  TimeVar v(org);
170  Time o2(v);
171  CHECK (o1 == o2);
172  CHECK (o1 == org);
173 
174  // time in seconds
175  Time t1(FSecs(1));
176  CHECK (t1 == TimeValue(TimeValue::SCALE));
177 
178  // create from fractional seconds
179  FSecs halve(1,2);
180  CHECK (0.5 == boost::rational_cast<double> (halve));
181  Time th(halve);
182  CHECK (th == TimeValue(TimeValue::SCALE/2));
183 
184  Time tx1(500,0);
185  CHECK (tx1 == th);
186  Time tx2(1,2);
187  CHECK (tx2 == TimeValue(2.001*TimeValue::SCALE));
188  Time tx3(1,1,1,1);
189  CHECK (tx3 == TimeValue(TimeValue::SCALE*(0.001 + 1 + 60 + 60*60)));
190 
191  CHECK ("1:01:01.001" == string(tx3));
192 
193  // create time variable on the fly....
194  CHECK (th+th == t1);
195  CHECK (t1-th == th);
196  CHECK (((t1-th)*=2) == t1);
197  CHECK (th-th == TimeValue(0));
198 
199  // that was indeed a temporary and didn't affect the originals
200  CHECK (t1 == TimeValue(TimeValue::SCALE));
201  CHECK (th == TimeValue(TimeValue::SCALE/2));
202  }
203 
204 
206  void
208  {
209  std::hash<TimeValue> hashFunc;
210  CHECK (0 == hashFunc (Time::ZERO));
211  size_t hh = sizeof(size_t)*CHAR_BIT/2;
212  CHECK (size_t(1)<<hh == hashFunc (TimeValue{1}));
213  CHECK (size_t(1) == hashFunc (TimeValue(size_t(1)<<hh)));
214 
215  size_t h1 = hashFunc (org);
216  size_t h2 = hashFunc (Time{org} + TimeValue{1});
217  size_t h3 = hashFunc (TimeValue(h1));
218  CHECK (h1 > 0 || org == Time::ZERO);
219  CHECK (h2 - h1 == size_t(1)<<hh);
220  CHECK (h3 == size_t(_raw(org)));
221  }
222 
223 
224  void
225  verify_invalidFramerateProtection()
226  {
227  VERIFY_ERROR (BOTTOM_VALUE, FrameRate infinite(0) );
228  VERIFY_ERROR (BOTTOM_VALUE, FrameRate infinite(0,123) );
229 
230  CHECK (isnil (Duration (0, FrameRate::PAL)));
231  CHECK (isnil (Duration (0, FrameRate(123))));
232 
233  CHECK (FrameRate::approx(2000) == "2000FPS"_expect);
234  CHECK (FrameRate::approx(1e05) == "100000FPS"_expect);
235  CHECK (FrameRate::approx(1e06) == "1000000FPS"_expect); // exact
236  CHECK (FrameRate::approx(1e12) == "4194303FPS"_expect); // limited (≈4.2e+6)
237  CHECK (FrameRate::approx(1e14) == "4194303FPS"_expect); // limited + numeric overflow prevented
238  CHECK (FrameRate::approx(1e-5) == "14/1398101FPS"_expect); // quantised ≈ 1.00135827e-5
239  CHECK (FrameRate::approx(1e-6) == "4/4194303FPS"_expect); // quantised ≈ 0.95367454e-6
240  CHECK (FrameRate::approx(1e-7) == "1/4194303FPS"_expect); // limited ≈ 2.38418636e-7
241  CHECK (FrameRate::approx(1e-9) == "1/4194303FPS"_expect); // limited ≈ 2.38418636e-7
242 
243  CHECK (FrameRate( 20'000, Duration{Time{0,10}}) == "2000FPS"_expect); // exact
244  CHECK (FrameRate( 20'000, Duration{Time::MAX }) == "1/4194303FPS"_expect); // limited
245 
246  CHECK (FrameRate(size_t(2e10), Duration{Time::MAX }) == "272848/4194303FPS"_expect); // quantised ≈ 6.5052048e-2
247  CHECK (FrameRate(size_t(2e14), Duration{Time::MAX }) == "3552496/5461FPS"_expect); // quantised ≈ 650.52115 exact:650.521
248  CHECK (FrameRate(size_t(2e15), Duration{Time::MAX }) == "3324163/511FPS"_expect); // quantised ≈ 6505.2114 exact:6505.21
249  CHECK (FrameRate(size_t(2e16), Duration{Time::MAX }) == "4098284/63FPS"_expect); // quantised ≈ 65052,127 exact:65052.1
250  CHECK (FrameRate(size_t(2e17), Duration{Time::MAX }) == "650521FPS"_expect); // exact:650521
251  CHECK (FrameRate(size_t(2e18), Duration{Time::MAX }) == "4194303FPS"_expect); // limited (≈4.2e+6) exact:6.50521e+06
252  CHECK (FrameRate(size_t(2e20), Duration{Time::MAX }) == "4194303FPS"_expect); // limited exact:6.50521e+08
253  }
254 
255 
256  void
257  createOffsets (TimeValue org)
258  {
259  TimeValue four(4);
260  TimeValue five(5);
261 
262  Offset off5 (five);
263  CHECK (0 < off5);
264 
265  TimeVar point(org);
266  point += off5;
267  CHECK (org < point);
268 
269  Offset reverse(point,org);
270  CHECK (reverse < off5);
271  CHECK (reverse.abs() == off5);
272 
273  CHECK (0 == off5 + reverse);
274 
275  // chaining and copy construction
276  Offset off9 (off5 + Offset(four));
277  CHECK (9 == off9);
278  // simple linear combinations
279  CHECK (7 == -2*off9 + off5*5);
280 
281  // build offset by number of frames
282  Offset byFrames(-125, FrameRate::PAL);
283  CHECK (Time(FSecs(-5)) == byFrames);
284 
285  CHECK (Offset(-5, FrameRate(5,4)) == -Offset(5, FrameRate(5,4)));
286  CHECK (Offset(3, FrameRate(3)) == Offset(12345, FrameRate(24690,2)));
287  } // precise rational number calculations
288 
289 
290  void
291  buildDuration (TimeValue org)
292  {
293  TimeValue zero(0);
294  TimeVar point(org);
295  point += TimeValue(5);
296  CHECK (org < point);
297 
298  Offset backwards(point,org);
299  CHECK (backwards < zero);
300 
301  Duration distance(backwards);
302  CHECK (distance > zero);
303  CHECK (distance == backwards.abs());
304 
305  Duration len1(Time(23,4,5,6));
306  CHECK (len1 == Time(FSecs(23,1000)) + Time(0, 4 + 5*60 + 6*3600));
307 
308  Duration len2(Time(FSecs(-10))); // negative specs...
309  CHECK (len2 == Time(FSecs(10)));//
310  CHECK (len2 > zero); // will be taken absolute
311 
312  Duration unit(50, FrameRate::PAL);
313  CHECK (Time(0,2,0,0) == unit); // duration of 50 frames at 25fps is... (guess what)
314 
315  CHECK (FrameRate::PAL.duration() == Time(FSecs(1,25)));
316  CHECK (FrameRate::NTSC.duration() == Time(FSecs(1001,30000)));
317  cout << "NTSC-Framerate = " << FrameRate::NTSC.asDouble() << endl;
318 
319  CHECK (zero == Duration::NIL);
320  CHECK (isnil (Duration::NIL));
321 
322  // assign to variable for calculations
323  point = backwards;
324  point *= 2;
325  CHECK (point < zero);
326  CHECK (point < backwards);
327 
328  CHECK (distance + point < zero); // using the duration as offset
329  CHECK (distance == backwards.abs()); // while this didn't alter the duration as such
330  }
331 
332 
333  void
334  verify_extremeValues()
335  {
336  CHECK (Time::MIN < Time::MAX);
337  CHECK (_raw(Time::MAX) < std::numeric_limits<int64_t>::max());
338  CHECK (_raw(Time::MIN) > std::numeric_limits<int64_t>::min());
339 
340  // Values are limited at construction, but not in calculations
341  CHECK (Time::MAX - Time(0,1) < Time::MAX);
342  CHECK (Time::MAX - Time(0,1) + Time(0,3) > Time::MAX);
343  CHECK (TimeValue{_raw(Time::MAX-Time(0,1)+Time(0,3))} == Time::MAX); // clipped at max
344  CHECK (TimeValue{_raw(Time::MIN+Time(0,5)-Time(0,9))} == Time::MIN); // clipped at min
345 
346  TimeValue outlier{Time::MIN - TimeValue(1)};
347  CHECK (outlier < Time::MIN);
348 
349  CHECK (Duration::MAX > Time::MAX);
350  CHECK (_raw(Duration::MAX) < std::numeric_limits<int64_t>::max());
351  CHECK (Duration::MAX == Time::MAX - Time::MIN);
352  CHECK (-Duration::MAX == Offset{Time::MIN - Time::MAX});
353  CHECK (Duration{3*Offset{Time::MAX}} == Duration::MAX);
354 
356  CHECK ( Time::MIN - Duration::MAX < -Duration::MAX);
357  CHECK ( Offset{Time::MAX + Duration::MAX} == Duration::MAX); // clipped at max
358  CHECK ( Offset{Time::MIN - Duration::MAX} == -Duration::MAX); // clipped at min
359  CHECK (Duration{Offset{Time::MIN - Duration::MAX}} == Duration::MAX); // duration is absolute
360 
361  CHECK (TimeSpan(Time::MIN, Time::MAX) == TimeSpan(Time::MAX, Time::MIN));
362  CHECK (TimeSpan(Time::MAX, Duration::MAX).start() == Time::MAX);
363  CHECK (TimeSpan(Time::MAX, Duration::MAX).end() == Time::MAX + Duration::MAX); // note: end() can yield value beyond [Time::MIN...Time::MAX]
364  CHECK (TimeSpan(Time::MAX, Duration::MAX).duration() == Duration::MAX);
365  CHECK (TimeSpan(Time::MAX, Duration::MAX).conform() == TimeSpan(Time::MIN,Duration::MAX));
366  CHECK (TimeSpan(outlier, Duration::MAX).conform() == TimeSpan(Time::MIN,Duration::MAX));
367  CHECK (TimeSpan(Time::MAX, Offset(FSecs(-1))) == TimeSpan(Time::MAX-Offset(FSecs(1)), FSecs(1)));
368  CHECK (TimeSpan(Time::MAX, FSecs(5)).start() == Time::MAX);
369  CHECK (TimeSpan(Time::MAX, FSecs(5)).duration() == Duration(FSecs(5)));
370  CHECK (TimeSpan(Time::MAX, FSecs(5)).conform() == TimeSpan(Time::MAX-Offset(FSecs(5)), FSecs(5)));
371  }
372 
373 
374  void
375  verify_fractionalOffset()
376  {
377  typedef boost::rational<FrameCnt> Frac;
378 
379  Duration three (TimeValue(3)); // three micro seconds
380 
381  Offset o1 = Frac(1,2) * three;
382  CHECK (o1 > Time::ZERO);
383  CHECK (o1 == TimeValue(1)); // bias towards the next lower micro grid position
384 
385  Offset o2 = -Frac(1,2) * three;
386  CHECK (o2 < Time::ZERO);
387  CHECK (o2 == TimeValue(-2));
388 
389  CHECK (three * Frac(1,2) * 2 != three);
390  CHECK (three *(Frac(1,2) * 2) == three);
391  // integral arithmetics is precise,
392  // but not necessarily exact!
393  }
394 
395 
396  void
397  buildTimeSpan (TimeValue org)
398  {
399  TimeValue five(5);
400 
401  TimeSpan interval (Time(org), Duration(Offset (org,five)));
402 
403  // the time span behaves like a time
404  CHECK (org == interval);
405 
406  // can get the length by direct conversion
407  Duration theLength(interval);
408  CHECK (theLength == Offset(org,five).abs());
409 
410  Time endpoint = interval.end();
411  TimeSpan successor (endpoint, FSecs(2));
412 
413  CHECK (Offset(interval,endpoint) == Offset(org,five).abs());
414  CHECK (Offset(endpoint,successor.end()) == Duration(successor));
415 
416  cout << "Interval-1: " << interval
417  << " Interval-2: " << successor
418  << " End point: " << successor.end()
419  << endl;
420  }
421 
422 
423  void
424  compareTimeSpan (Time const& org)
425  {
426  TimeSpan span1 (org, org+org); // using the distance between start and end point
427  TimeSpan span2 (org+org, org); // note: TimeSpan is oriented automatically
428  TimeSpan span3 (org, FSecs(5,2)); // Duration given explicitly, in seconds
429  TimeSpan span4 (org, FSecs(5,-2)); // note: fractional seconds taken absolute, as Duration
430 
431  CHECK (span1 == span2);
432  CHECK (span2 == span1);
433  CHECK (span3 == span4);
434  CHECK (span4 == span3);
435 
436  CHECK (span1 != span3);
437  CHECK (span3 != span1);
438  CHECK (span1 != span4);
439  CHECK (span4 != span1);
440  CHECK (span2 != span3);
441  CHECK (span3 != span2);
442  CHECK (span2 != span4);
443  CHECK (span4 != span2);
444 
445  // note that TimeSpan is oriented at creation
446  CHECK (span1.end() == span2.end());
447  CHECK (span3.end() == span4.end());
448 
449  // Verify the extended order on time intervals
450  TimeSpan span1x (org+org, Duration(org)); // starting later than span1
451  TimeSpan span3y (org, FSecs(2)); // shorter than span3
452  TimeSpan span3z (org+org, FSecs(2)); // starting later and shorter than span3
453 
454  CHECK (span1 != span1x);
455  CHECK (span3 != span3y);
456  CHECK (span3 != span3z);
457 
458  CHECK ( span1 < span1x);
459  CHECK ( span1 <= span1x);
460  CHECK (!(span1 > span1x));
461  CHECK (!(span1 >= span1x));
462 
463  CHECK ( span3 > span3y);
464  CHECK ( span3 >= span3y);
465  CHECK (!(span3 < span3y));
466  CHECK (!(span3 <= span3y));
467 
468  CHECK ( span3 < span3z); // Note: the start point takes precedence on comparison
469  CHECK ( span3y < span3z);
470 
471  // Verify this order is really different
472  // than the basic ordering of time values
473  CHECK (span1 < span1x);
474  CHECK (span1.duration() == span1x.duration());
475  CHECK (span1.start() < span1x.start());
476  CHECK (span1.end() < span1x.end());
477 
478  CHECK (span3 > span3y);
479  CHECK (span3.duration() > span3y.duration());
480  CHECK (span3.start() == span3y.start());
481  CHECK (span3.end() > span3y.end());
482  CHECK (Time(span3) == Time(span3y));
483 
484  CHECK (span3 < span3z);
485  CHECK (span3.duration() > span3z.duration());
486  CHECK (span3.start() < span3z.start());
487  CHECK (span3.end() != span3z.end()); // it's shorter, and org can be random, so that's all we know
488  CHECK (Time(span3) < Time(span3z));
489 
490  CHECK (span3y < span3z);
491  CHECK (span3y.duration() == span3z.duration());
492  CHECK (span3y.start() < span3z.start());
493  CHECK (span3y.end() < span3z.end());
494  CHECK (Time(span3) < Time(span3z));
495  }
496 
497 
498  void
499  relateTimeIntervals (TimeValue org)
500  {
501  Time oneSec(FSecs(1));
502 
503  TimeSpan span1 (org, FSecs(2));
504  TimeSpan span2 (oneSec + org, FSecs(2));
505 
506  TimeVar probe(org);
507  CHECK ( span1.contains(probe));
508  CHECK (!span2.contains(probe));
509 
510  probe = span2;
511  CHECK ( span1.contains(probe));
512  CHECK ( span2.contains(probe));
513 
514  probe = span1.end();
515  CHECK (!span1.contains(probe)); // Note: end is always exclusive
516  CHECK ( span2.contains(probe));
517 
518  probe = span2.end();
519  CHECK (!span1.contains(probe));
520  CHECK (!span2.contains(probe));
521  }
522  };
523 
524 
526  LAUNCHER (TimeValue_test, "unit common");
527 
528 
529 
530 }}} // namespace lib::time::test
a mutable time value, behaving like a plain number, allowing copy and re-accessing ...
Definition: timevalue.hpp:241
static const Duration MAX
maximum possible temporal extension
Definition: timevalue.hpp:516
Automatically use custom string conversion in C++ stream output.
Definition: run.hpp:49
Framerate specified as frames per second.
Definition: timevalue.hpp:664
static const gavl_time_t SCALE
Number of micro ticks (µs) per second as basic time scale.
Definition: timevalue.hpp:176
Primary class template for std::hash.
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
void checkTimeConvenience(TimeValue org)
static const Duration NIL
constant to indicate "no duration"
Definition: timevalue.hpp:515
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:308
Simple test class runner.
Duration abs() const
interpret the distance given by this offset as a time duration
Definition: timevalue.hpp:840
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
boost::rational< int64_t > FSecs
rational representation of fractional seconds
Definition: timevalue.hpp:229
A collection of frequently used helper functions to support unit testing.
void checkBasicTimeValues(TimeValue org)
ExampleStrategy::Qualifier two(string additionalArg)
definition of another qualifier two(arg), accepting an additional argument
Offset measures a distance in time.
Definition: timevalue.hpp:367
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:477
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:582
ExampleStrategy::Qualifier one()
definition of a qualifier one()
a family of time value like entities and their relationships.
basic constant internal time value.
Definition: timevalue.hpp:142
static const Time MAX
Definition: timevalue.hpp:318
void checkMutableTime(TimeValue org)
static const FrameRate PAL
predefined constant for PAL framerate
Definition: timevalue.hpp:680