2010-12-22 04:09:27 +01:00
|
|
|
|
/*
|
|
|
|
|
|
TimeValue(Test) - working with time values and time intervals in C++...
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2010, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
2017-02-22 01:54:20 +01:00
|
|
|
|
/** @file time-value-test.cpp
|
2017-02-22 03:17:18 +01:00
|
|
|
|
** unit test \ref TimeValue_test
|
2016-11-03 18:20:10 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
2011-01-09 05:51:26 +01:00
|
|
|
|
#include "lib/test/test-helper.hpp"
|
2010-12-22 04:09:27 +01:00
|
|
|
|
#include "lib/time/timevalue.hpp"
|
2016-01-07 03:58:29 +01:00
|
|
|
|
#include "lib/format-cout.hpp"
|
2010-12-22 04:09:27 +01:00
|
|
|
|
#include "lib/util.hpp"
|
|
|
|
|
|
|
2010-12-26 23:00:34 +01:00
|
|
|
|
#include <boost/lexical_cast.hpp>
|
2010-12-28 07:24:54 +01:00
|
|
|
|
#include <string>
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
2010-12-26 23:00:34 +01:00
|
|
|
|
using boost::lexical_cast;
|
|
|
|
|
|
using util::isnil;
|
2010-12-28 07:24:54 +01:00
|
|
|
|
using std::string;
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
2024-03-16 02:04:47 +01:00
|
|
|
|
using LERR_(BOTTOM_VALUE);
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace time{
|
|
|
|
|
|
namespace test{
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-24 23:06:36 +02:00
|
|
|
|
/****************************************************//**
|
2010-12-22 04:09:27 +01:00
|
|
|
|
* @test verify handling of time values, time intervals.
|
|
|
|
|
|
* - creating times and time intervals
|
|
|
|
|
|
* - comparisons
|
|
|
|
|
|
* - time arithmetics
|
|
|
|
|
|
*/
|
|
|
|
|
|
class TimeValue_test : public Test
|
|
|
|
|
|
{
|
2025-05-17 23:12:47 +02:00
|
|
|
|
raw_time_64
|
2010-12-27 05:55:15 +01:00
|
|
|
|
random_or_get (Arg arg)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isnil(arg))
|
2024-11-13 02:23:23 +01:00
|
|
|
|
{// use random time value for all tests
|
|
|
|
|
|
seedRand();
|
|
|
|
|
|
return 1 + rani(10000);
|
|
|
|
|
|
}
|
2010-12-27 05:55:15 +01:00
|
|
|
|
else
|
2025-05-17 23:12:47 +02:00
|
|
|
|
return lexical_cast<raw_time_64> (arg[1]);
|
2010-12-27 05:55:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-22 04:09:27 +01:00
|
|
|
|
virtual void
|
2025-06-07 23:59:57 +02:00
|
|
|
|
run (Arg arg)
|
2010-12-22 04:09:27 +01:00
|
|
|
|
{
|
2010-12-27 05:55:15 +01:00
|
|
|
|
TimeValue ref (random_or_get(arg));
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
2010-12-27 05:55:15 +01:00
|
|
|
|
checkBasicTimeValues (ref);
|
2010-12-27 06:58:27 +01:00
|
|
|
|
checkMutableTime (ref);
|
2023-04-30 22:33:42 +02:00
|
|
|
|
checkTimeHash (ref);
|
2011-04-02 04:01:06 +02:00
|
|
|
|
checkTimeConvenience (ref);
|
2011-01-09 05:51:26 +01:00
|
|
|
|
verify_invalidFramerateProtection();
|
2010-12-28 07:24:54 +01:00
|
|
|
|
createOffsets (ref);
|
|
|
|
|
|
buildDuration (ref);
|
|
|
|
|
|
buildTimeSpan (ref);
|
2011-05-20 20:31:16 +02:00
|
|
|
|
compareTimeSpan (Time(ref));
|
2011-05-16 04:02:26 +02:00
|
|
|
|
relateTimeIntervals (ref);
|
2022-12-05 03:34:04 +01:00
|
|
|
|
verify_extremeValues();
|
2012-03-17 00:49:58 +01:00
|
|
|
|
verify_fractionalOffset();
|
2025-06-07 23:59:57 +02:00
|
|
|
|
}
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
2010-12-27 05:55:15 +01:00
|
|
|
|
/** @test creating some time values and performing trivial comparisons.
|
|
|
|
|
|
* @note you can't do much beyond that, because TimeValues as such
|
|
|
|
|
|
* are a "dead end": they are opaque and can't be altered.
|
|
|
|
|
|
*/
|
2010-12-22 04:09:27 +01:00
|
|
|
|
void
|
2010-12-27 05:55:15 +01:00
|
|
|
|
checkBasicTimeValues (TimeValue org)
|
2010-12-22 04:09:27 +01:00
|
|
|
|
{
|
Library: rectify clipping of time::Duration (see #1263)
This is a deep refactoring to allow to represent the distance
between all valid time points as a time::Offset or time::Duration.
By design this is possible, since Time::MAX was defined as 1/30 of
the maximum value technically representable as int64_t. However,
introducing a different limiter for offsets and durations turns
out difficult, due to the inconsistencies in the exiting hierarchy
of temporal entities. Which in turn seems to stem from the unfortunate
decision to make time entities immutable, see #1261
Since the limiter is hard wired into the `time::TimeValue` constructor,
we are forced to create a "backdoor" of sorts, to pass up values
with different limiting from child classes. This would not be so
much of a problem if calculations weren't forced to go through `TimeVar`,
which does not distinguish between time points and time durations.
This solution rearranges all checks to be performed now by time::Offset,
while time::Duration will only take the absolute value at construction,
based on the fact that there is no valid construction path to yield
a duration which does not go through an offset first.
Later, when we're ready to sort out the implementation base of time values
(see #1258), this design issue should be revisited
- either we'll allow derived classes explicitly to invoke the limiter functions
- or we may be able to have an automatic conversion path from clearly
marked base implementation types, in which case we wouldn't use the
buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
|
|
|
|
TimeValue zero(0);
|
2010-12-27 05:55:15 +01:00
|
|
|
|
TimeValue one (1);
|
|
|
|
|
|
TimeValue max (Time::MAX);
|
|
|
|
|
|
TimeValue min (Time::MIN);
|
|
|
|
|
|
|
|
|
|
|
|
TimeValue val (org);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (zero == zero);
|
|
|
|
|
|
CHECK (zero <= zero);
|
|
|
|
|
|
CHECK (zero >= zero);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (zero < one);
|
|
|
|
|
|
CHECK (min < max);
|
|
|
|
|
|
CHECK (min < val);
|
|
|
|
|
|
CHECK (val < max);
|
|
|
|
|
|
|
|
|
|
|
|
// mixed comparisons with raw numeric time
|
2025-05-17 23:12:47 +02:00
|
|
|
|
raw_time_64 g2 (-2);
|
2010-12-27 05:55:15 +01:00
|
|
|
|
CHECK (zero > g2);
|
|
|
|
|
|
CHECK (one > g2);
|
|
|
|
|
|
CHECK (one >= g2);
|
|
|
|
|
|
CHECK (g2 < max);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (!(g2 > max));
|
|
|
|
|
|
CHECK (!(g2 < min));
|
2010-12-22 04:09:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-28 07:24:54 +01:00
|
|
|
|
/** @test time variables can be used for the typical calculations,
|
|
|
|
|
|
* like summing and subtracting values, as well as multiplication
|
|
|
|
|
|
* with a scale factor. Additionally, the raw time value is
|
|
|
|
|
|
* accessible by conversion.
|
|
|
|
|
|
*/
|
2010-12-27 06:58:27 +01:00
|
|
|
|
void
|
|
|
|
|
|
checkMutableTime (TimeValue org)
|
|
|
|
|
|
{
|
|
|
|
|
|
TimeVar zero;
|
|
|
|
|
|
TimeVar one = TimeValue(1);
|
|
|
|
|
|
TimeVar two = TimeValue(2);
|
|
|
|
|
|
|
|
|
|
|
|
TimeVar var (org);
|
|
|
|
|
|
|
|
|
|
|
|
var += two;
|
|
|
|
|
|
var *= 2;
|
|
|
|
|
|
CHECK (zero == (var - 2*(org + two)) );
|
|
|
|
|
|
|
|
|
|
|
|
// the transient vars caused no side-effects
|
|
|
|
|
|
CHECK (var == 2*two + org + org);
|
|
|
|
|
|
CHECK (two == TimeValue(2));
|
|
|
|
|
|
|
|
|
|
|
|
var = org; // assign new value
|
|
|
|
|
|
CHECK (zero == (var - org));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (zero < one);
|
|
|
|
|
|
CHECK (one < two);
|
|
|
|
|
|
CHECK (var < Time::MAX);
|
|
|
|
|
|
CHECK (var > Time::MIN);
|
|
|
|
|
|
|
2025-06-18 16:50:19 +02:00
|
|
|
|
raw_time_64 raw = _raw(var);
|
2010-12-27 06:58:27 +01:00
|
|
|
|
CHECK (raw == org);
|
|
|
|
|
|
CHECK (raw > org - two);
|
2011-01-08 19:11:42 +01:00
|
|
|
|
|
|
|
|
|
|
// unary minus will flip around origin
|
|
|
|
|
|
CHECK (zero == -var + var);
|
|
|
|
|
|
CHECK (zero != -var);
|
|
|
|
|
|
CHECK (var == org); // unaltered
|
2010-12-27 06:58:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-07 16:28:13 +01:00
|
|
|
|
/** @test additional convenience shortcuts supported
|
|
|
|
|
|
* especially by the canonical Time values.
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
2011-04-02 04:01:06 +02:00
|
|
|
|
checkTimeConvenience (TimeValue org)
|
2011-01-07 16:28:13 +01:00
|
|
|
|
{
|
|
|
|
|
|
Time o1(org);
|
|
|
|
|
|
TimeVar v(org);
|
|
|
|
|
|
Time o2(v);
|
|
|
|
|
|
CHECK (o1 == o2);
|
|
|
|
|
|
CHECK (o1 == org);
|
|
|
|
|
|
|
2011-03-31 18:43:50 +02:00
|
|
|
|
// time in seconds
|
|
|
|
|
|
Time t1(FSecs(1));
|
2018-11-17 18:00:39 +01:00
|
|
|
|
CHECK (t1 == TimeValue(TimeValue::SCALE));
|
2011-01-07 16:28:13 +01:00
|
|
|
|
|
|
|
|
|
|
// create from fractional seconds
|
|
|
|
|
|
FSecs halve(1,2);
|
|
|
|
|
|
CHECK (0.5 == boost::rational_cast<double> (halve));
|
|
|
|
|
|
Time th(halve);
|
2018-11-17 18:00:39 +01:00
|
|
|
|
CHECK (th == TimeValue(TimeValue::SCALE/2));
|
2011-01-07 16:28:13 +01:00
|
|
|
|
|
|
|
|
|
|
Time tx1(500,0);
|
|
|
|
|
|
CHECK (tx1 == th);
|
|
|
|
|
|
Time tx2(1,2);
|
2018-11-17 18:00:39 +01:00
|
|
|
|
CHECK (tx2 == TimeValue(2.001*TimeValue::SCALE));
|
2011-01-07 16:28:13 +01:00
|
|
|
|
Time tx3(1,1,1,1);
|
2018-11-17 18:00:39 +01:00
|
|
|
|
CHECK (tx3 == TimeValue(TimeValue::SCALE*(0.001 + 1 + 60 + 60*60)));
|
2011-01-07 16:28:13 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK ("1:01:01.001" == string(tx3));
|
|
|
|
|
|
|
|
|
|
|
|
// create time variable on the fly....
|
|
|
|
|
|
CHECK (th+th == t1);
|
|
|
|
|
|
CHECK (t1-th == th);
|
|
|
|
|
|
CHECK (((t1-th)*=2) == t1);
|
2011-03-31 18:43:50 +02:00
|
|
|
|
CHECK (th-th == TimeValue(0));
|
2011-01-07 16:28:13 +01:00
|
|
|
|
|
|
|
|
|
|
// that was indeed a temporary and didn't affect the originals
|
2018-11-17 18:00:39 +01:00
|
|
|
|
CHECK (t1 == TimeValue(TimeValue::SCALE));
|
|
|
|
|
|
CHECK (th == TimeValue(TimeValue::SCALE/2));
|
2011-01-07 16:28:13 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-04-30 22:33:42 +02:00
|
|
|
|
/** @test calculate a generic hash value from a time spec*/
|
|
|
|
|
|
void
|
|
|
|
|
|
checkTimeHash (TimeValue org)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::hash<TimeValue> hashFunc;
|
|
|
|
|
|
CHECK (0 == hashFunc (Time::ZERO));
|
|
|
|
|
|
size_t hh = sizeof(size_t)*CHAR_BIT/2;
|
|
|
|
|
|
CHECK (size_t(1)<<hh == hashFunc (TimeValue{1}));
|
|
|
|
|
|
CHECK (size_t(1) == hashFunc (TimeValue(size_t(1)<<hh)));
|
|
|
|
|
|
|
|
|
|
|
|
size_t h1 = hashFunc (org);
|
|
|
|
|
|
size_t h2 = hashFunc (Time{org} + TimeValue{1});
|
|
|
|
|
|
size_t h3 = hashFunc (TimeValue(h1));
|
2025-06-07 23:59:57 +02:00
|
|
|
|
CHECK (h1 > 0 or org == Time::ZERO);
|
2023-04-30 22:33:42 +02:00
|
|
|
|
CHECK (h2 - h1 == size_t(1)<<hh);
|
|
|
|
|
|
CHECK (h3 == size_t(_raw(org)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-09 05:51:26 +01:00
|
|
|
|
void
|
|
|
|
|
|
verify_invalidFramerateProtection()
|
|
|
|
|
|
{
|
|
|
|
|
|
VERIFY_ERROR (BOTTOM_VALUE, FrameRate infinite(0) );
|
|
|
|
|
|
VERIFY_ERROR (BOTTOM_VALUE, FrameRate infinite(0,123) );
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (isnil (Duration (0, FrameRate::PAL)));
|
|
|
|
|
|
CHECK (isnil (Duration (0, FrameRate(123))));
|
2023-11-10 03:34:29 +01:00
|
|
|
|
|
2023-11-10 19:35:38 +01:00
|
|
|
|
CHECK (FrameRate::approx(2000) == "2000FPS"_expect);
|
|
|
|
|
|
CHECK (FrameRate::approx(1e05) == "100000FPS"_expect);
|
|
|
|
|
|
CHECK (FrameRate::approx(1e06) == "1000000FPS"_expect); // exact
|
|
|
|
|
|
CHECK (FrameRate::approx(1e12) == "4194303FPS"_expect); // limited (≈4.2e+6)
|
|
|
|
|
|
CHECK (FrameRate::approx(1e14) == "4194303FPS"_expect); // limited + numeric overflow prevented
|
|
|
|
|
|
CHECK (FrameRate::approx(1e-5) == "14/1398101FPS"_expect); // quantised ≈ 1.00135827e-5
|
|
|
|
|
|
CHECK (FrameRate::approx(1e-6) == "4/4194303FPS"_expect); // quantised ≈ 0.95367454e-6
|
|
|
|
|
|
CHECK (FrameRate::approx(1e-7) == "1/4194303FPS"_expect); // limited ≈ 2.38418636e-7
|
|
|
|
|
|
CHECK (FrameRate::approx(1e-9) == "1/4194303FPS"_expect); // limited ≈ 2.38418636e-7
|
2023-11-10 03:34:29 +01:00
|
|
|
|
|
2023-11-10 19:35:38 +01:00
|
|
|
|
CHECK (FrameRate( 20'000, Duration{Time{0,10}}) == "2000FPS"_expect); // exact
|
|
|
|
|
|
CHECK (FrameRate( 20'000, Duration{Time::MAX }) == "1/4194303FPS"_expect); // limited
|
2023-11-10 03:34:29 +01:00
|
|
|
|
|
2023-11-10 19:35:38 +01:00
|
|
|
|
CHECK (FrameRate(size_t(2e10), Duration{Time::MAX }) == "272848/4194303FPS"_expect); // quantised ≈ 6.5052048e-2
|
|
|
|
|
|
CHECK (FrameRate(size_t(2e14), Duration{Time::MAX }) == "3552496/5461FPS"_expect); // quantised ≈ 650.52115 exact:650.521
|
|
|
|
|
|
CHECK (FrameRate(size_t(2e15), Duration{Time::MAX }) == "3324163/511FPS"_expect); // quantised ≈ 6505.2114 exact:6505.21
|
|
|
|
|
|
CHECK (FrameRate(size_t(2e16), Duration{Time::MAX }) == "4098284/63FPS"_expect); // quantised ≈ 65052,127 exact:65052.1
|
|
|
|
|
|
CHECK (FrameRate(size_t(2e17), Duration{Time::MAX }) == "650521FPS"_expect); // exact:650521
|
|
|
|
|
|
CHECK (FrameRate(size_t(2e18), Duration{Time::MAX }) == "4194303FPS"_expect); // limited (≈4.2e+6) exact:6.50521e+06
|
|
|
|
|
|
CHECK (FrameRate(size_t(2e20), Duration{Time::MAX }) == "4194303FPS"_expect); // limited exact:6.50521e+08
|
2011-01-09 05:51:26 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-22 04:09:27 +01:00
|
|
|
|
void
|
2010-12-28 07:24:54 +01:00
|
|
|
|
createOffsets (TimeValue org)
|
2010-12-22 04:09:27 +01:00
|
|
|
|
{
|
2010-12-28 07:24:54 +01:00
|
|
|
|
TimeValue four(4);
|
|
|
|
|
|
TimeValue five(5);
|
|
|
|
|
|
|
|
|
|
|
|
Offset off5 (five);
|
|
|
|
|
|
CHECK (0 < off5);
|
|
|
|
|
|
|
|
|
|
|
|
TimeVar point(org);
|
|
|
|
|
|
point += off5;
|
|
|
|
|
|
CHECK (org < point);
|
|
|
|
|
|
|
|
|
|
|
|
Offset reverse(point,org);
|
|
|
|
|
|
CHECK (reverse < off5);
|
|
|
|
|
|
CHECK (reverse.abs() == off5);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (0 == off5 + reverse);
|
|
|
|
|
|
|
|
|
|
|
|
// chaining and copy construction
|
|
|
|
|
|
Offset off9 (off5 + Offset(four));
|
|
|
|
|
|
CHECK (9 == off9);
|
2011-01-15 01:45:55 +01:00
|
|
|
|
// simple linear combinations
|
|
|
|
|
|
CHECK (7 == -2*off9 + off5*5);
|
2011-06-11 20:20:20 +02:00
|
|
|
|
|
|
|
|
|
|
// build offset by number of frames
|
|
|
|
|
|
Offset byFrames(-125, FrameRate::PAL);
|
|
|
|
|
|
CHECK (Time(FSecs(-5)) == byFrames);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (Offset(-5, FrameRate(5,4)) == -Offset(5, FrameRate(5,4)));
|
|
|
|
|
|
CHECK (Offset(3, FrameRate(3)) == Offset(12345, FrameRate(24690,2)));
|
|
|
|
|
|
} // precise rational number calculations
|
2010-12-22 04:09:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2010-12-28 07:24:54 +01:00
|
|
|
|
buildDuration (TimeValue org)
|
2010-12-22 04:09:27 +01:00
|
|
|
|
{
|
Library: rectify clipping of time::Duration (see #1263)
This is a deep refactoring to allow to represent the distance
between all valid time points as a time::Offset or time::Duration.
By design this is possible, since Time::MAX was defined as 1/30 of
the maximum value technically representable as int64_t. However,
introducing a different limiter for offsets and durations turns
out difficult, due to the inconsistencies in the exiting hierarchy
of temporal entities. Which in turn seems to stem from the unfortunate
decision to make time entities immutable, see #1261
Since the limiter is hard wired into the `time::TimeValue` constructor,
we are forced to create a "backdoor" of sorts, to pass up values
with different limiting from child classes. This would not be so
much of a problem if calculations weren't forced to go through `TimeVar`,
which does not distinguish between time points and time durations.
This solution rearranges all checks to be performed now by time::Offset,
while time::Duration will only take the absolute value at construction,
based on the fact that there is no valid construction path to yield
a duration which does not go through an offset first.
Later, when we're ready to sort out the implementation base of time values
(see #1258), this design issue should be revisited
- either we'll allow derived classes explicitly to invoke the limiter functions
- or we may be able to have an automatic conversion path from clearly
marked base implementation types, in which case we wouldn't use the
buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
|
|
|
|
TimeValue zero(0);
|
2010-12-28 07:24:54 +01:00
|
|
|
|
TimeVar point(org);
|
|
|
|
|
|
point += TimeValue(5);
|
|
|
|
|
|
CHECK (org < point);
|
|
|
|
|
|
|
|
|
|
|
|
Offset backwards(point,org);
|
|
|
|
|
|
CHECK (backwards < zero);
|
|
|
|
|
|
|
|
|
|
|
|
Duration distance(backwards);
|
|
|
|
|
|
CHECK (distance > zero);
|
|
|
|
|
|
CHECK (distance == backwards.abs());
|
|
|
|
|
|
|
2011-01-09 09:49:48 +01:00
|
|
|
|
Duration len1(Time(23,4,5,6));
|
2011-03-31 18:43:50 +02:00
|
|
|
|
CHECK (len1 == Time(FSecs(23,1000)) + Time(0, 4 + 5*60 + 6*3600));
|
2011-01-09 09:49:48 +01:00
|
|
|
|
|
2011-03-31 18:43:50 +02:00
|
|
|
|
Duration len2(Time(FSecs(-10))); // negative specs...
|
|
|
|
|
|
CHECK (len2 == Time(FSecs(10)));//
|
|
|
|
|
|
CHECK (len2 > zero); // will be taken absolute
|
2011-01-09 09:49:48 +01:00
|
|
|
|
|
2011-01-09 05:51:26 +01:00
|
|
|
|
Duration unit(50, FrameRate::PAL);
|
2011-03-31 18:43:50 +02:00
|
|
|
|
CHECK (Time(0,2,0,0) == unit); // duration of 50 frames at 25fps is... (guess what)
|
2011-01-09 05:51:26 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK (FrameRate::PAL.duration() == Time(FSecs(1,25)));
|
2011-01-15 03:49:35 +01:00
|
|
|
|
CHECK (FrameRate::NTSC.duration() == Time(FSecs(1001,30000)));
|
2011-01-09 05:51:26 +01:00
|
|
|
|
cout << "NTSC-Framerate = " << FrameRate::NTSC.asDouble() << endl;
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (zero == Duration::NIL);
|
|
|
|
|
|
CHECK (isnil (Duration::NIL));
|
|
|
|
|
|
|
2011-01-01 03:56:31 +01:00
|
|
|
|
// assign to variable for calculations
|
2010-12-28 07:24:54 +01:00
|
|
|
|
point = backwards;
|
|
|
|
|
|
point *= 2;
|
|
|
|
|
|
CHECK (point < zero);
|
|
|
|
|
|
CHECK (point < backwards);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (distance + point < zero); // using the duration as offset
|
|
|
|
|
|
CHECK (distance == backwards.abs()); // while this didn't alter the duration as such
|
2010-12-22 04:09:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-28 07:24:54 +01:00
|
|
|
|
|
2012-03-17 00:49:58 +01:00
|
|
|
|
void
|
2022-12-05 03:34:04 +01:00
|
|
|
|
verify_extremeValues()
|
|
|
|
|
|
{
|
|
|
|
|
|
CHECK (Time::MIN < Time::MAX);
|
|
|
|
|
|
CHECK (_raw(Time::MAX) < std::numeric_limits<int64_t>::max());
|
|
|
|
|
|
CHECK (_raw(Time::MIN) > std::numeric_limits<int64_t>::min());
|
|
|
|
|
|
|
|
|
|
|
|
// Values are limited at construction, but not in calculations
|
|
|
|
|
|
CHECK (Time::MAX - Time(0,1) < Time::MAX);
|
|
|
|
|
|
CHECK (Time::MAX - Time(0,1) + Time(0,3) > Time::MAX);
|
|
|
|
|
|
CHECK (TimeValue{_raw(Time::MAX-Time(0,1)+Time(0,3))} == Time::MAX); // clipped at max
|
|
|
|
|
|
CHECK (TimeValue{_raw(Time::MIN+Time(0,5)-Time(0,9))} == Time::MIN); // clipped at min
|
|
|
|
|
|
|
2022-12-17 01:15:34 +01:00
|
|
|
|
TimeValue outlier{Time::MIN - TimeValue(1)};
|
|
|
|
|
|
CHECK (outlier < Time::MIN);
|
|
|
|
|
|
|
2022-12-05 03:34:04 +01:00
|
|
|
|
CHECK (Duration::MAX > Time::MAX);
|
|
|
|
|
|
CHECK (_raw(Duration::MAX) < std::numeric_limits<int64_t>::max());
|
|
|
|
|
|
CHECK (Duration::MAX == Time::MAX - Time::MIN);
|
|
|
|
|
|
CHECK (-Duration::MAX == Offset{Time::MIN - Time::MAX});
|
2022-12-17 01:15:34 +01:00
|
|
|
|
CHECK (Duration{3*Offset{Time::MAX}} == Duration::MAX);
|
2022-12-05 03:34:04 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK ( Time::MAX + Duration::MAX > Duration::MAX);
|
|
|
|
|
|
CHECK ( Time::MIN - Duration::MAX < -Duration::MAX);
|
|
|
|
|
|
CHECK ( Offset{Time::MAX + Duration::MAX} == Duration::MAX); // clipped at max
|
|
|
|
|
|
CHECK ( Offset{Time::MIN - Duration::MAX} == -Duration::MAX); // clipped at min
|
|
|
|
|
|
CHECK (Duration{Offset{Time::MIN - Duration::MAX}} == Duration::MAX); // duration is absolute
|
2022-12-14 03:00:07 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK (TimeSpan(Time::MIN, Time::MAX) == TimeSpan(Time::MAX, Time::MIN));
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, Duration::MAX).start() == Time::MAX);
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, Duration::MAX).end() == Time::MAX + Duration::MAX); // note: end() can yield value beyond [Time::MIN...Time::MAX]
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, Duration::MAX).duration() == Duration::MAX);
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, Duration::MAX).conform() == TimeSpan(Time::MIN,Duration::MAX));
|
2022-12-17 01:15:34 +01:00
|
|
|
|
CHECK (TimeSpan(outlier, Duration::MAX).conform() == TimeSpan(Time::MIN,Duration::MAX));
|
2022-12-14 03:00:07 +01:00
|
|
|
|
CHECK (TimeSpan(Time::MAX, Offset(FSecs(-1))) == TimeSpan(Time::MAX-Offset(FSecs(1)), FSecs(1)));
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, FSecs(5)).start() == Time::MAX);
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, FSecs(5)).duration() == Duration(FSecs(5)));
|
|
|
|
|
|
CHECK (TimeSpan(Time::MAX, FSecs(5)).conform() == TimeSpan(Time::MAX-Offset(FSecs(5)), FSecs(5)));
|
2022-12-05 03:34:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_fractionalOffset()
|
2012-03-17 00:49:58 +01:00
|
|
|
|
{
|
2013-11-18 00:01:43 +01:00
|
|
|
|
typedef boost::rational<FrameCnt> Frac;
|
2012-03-17 00:49:58 +01:00
|
|
|
|
|
|
|
|
|
|
Duration three (TimeValue(3)); // three micro seconds
|
|
|
|
|
|
|
|
|
|
|
|
Offset o1 = Frac(1,2) * three;
|
|
|
|
|
|
CHECK (o1 > Time::ZERO);
|
|
|
|
|
|
CHECK (o1 == TimeValue(1)); // bias towards the next lower micro grid position
|
|
|
|
|
|
|
|
|
|
|
|
Offset o2 = -Frac(1,2) * three;
|
|
|
|
|
|
CHECK (o2 < Time::ZERO);
|
|
|
|
|
|
CHECK (o2 == TimeValue(-2));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (three * Frac(1,2) * 2 != three);
|
|
|
|
|
|
CHECK (three *(Frac(1,2) * 2) == three);
|
|
|
|
|
|
// integral arithmetics is precise,
|
|
|
|
|
|
// but not necessarily exact!
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-28 07:24:54 +01:00
|
|
|
|
void
|
|
|
|
|
|
buildTimeSpan (TimeValue org)
|
|
|
|
|
|
{
|
|
|
|
|
|
TimeValue five(5);
|
|
|
|
|
|
|
2011-01-09 09:49:48 +01:00
|
|
|
|
TimeSpan interval (Time(org), Duration(Offset (org,five)));
|
2010-12-28 07:24:54 +01:00
|
|
|
|
|
|
|
|
|
|
// the time span behaves like a time
|
|
|
|
|
|
CHECK (org == interval);
|
|
|
|
|
|
|
|
|
|
|
|
// can get the length by direct conversion
|
|
|
|
|
|
Duration theLength(interval);
|
|
|
|
|
|
CHECK (theLength == Offset(org,five).abs());
|
|
|
|
|
|
|
2011-01-09 09:49:48 +01:00
|
|
|
|
Time endpoint = interval.end();
|
2011-04-02 04:01:06 +02:00
|
|
|
|
TimeSpan successor (endpoint, FSecs(2));
|
2011-01-09 09:49:48 +01:00
|
|
|
|
|
2010-12-28 07:24:54 +01:00
|
|
|
|
CHECK (Offset(interval,endpoint) == Offset(org,five).abs());
|
2011-01-09 09:49:48 +01:00
|
|
|
|
CHECK (Offset(endpoint,successor.end()) == Duration(successor));
|
2010-12-28 07:24:54 +01:00
|
|
|
|
|
2011-04-02 04:01:06 +02:00
|
|
|
|
cout << "Interval-1: " << interval
|
|
|
|
|
|
<< " Interval-2: " << successor
|
2011-01-09 09:49:48 +01:00
|
|
|
|
<< " End point: " << successor.end()
|
2011-04-02 04:01:06 +02:00
|
|
|
|
<< endl;
|
2025-06-07 23:59:57 +02:00
|
|
|
|
}
|
2011-04-02 04:01:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2011-05-20 20:31:16 +02:00
|
|
|
|
compareTimeSpan (Time const& org)
|
2011-04-02 04:01:06 +02:00
|
|
|
|
{
|
2011-05-20 20:31:16 +02:00
|
|
|
|
TimeSpan span1 (org, org+org); // using the distance between start and end point
|
2022-12-14 03:00:07 +01:00
|
|
|
|
TimeSpan span2 (org+org, org); // note: TimeSpan is oriented automatically
|
2011-05-20 20:31:16 +02:00
|
|
|
|
TimeSpan span3 (org, FSecs(5,2)); // Duration given explicitly, in seconds
|
2022-12-14 03:00:07 +01:00
|
|
|
|
TimeSpan span4 (org, FSecs(5,-2)); // note: fractional seconds taken absolute, as Duration
|
2011-04-02 04:01:06 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (span1 == span2);
|
|
|
|
|
|
CHECK (span2 == span1);
|
|
|
|
|
|
CHECK (span3 == span4);
|
|
|
|
|
|
CHECK (span4 == span3);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (span1 != span3);
|
|
|
|
|
|
CHECK (span3 != span1);
|
|
|
|
|
|
CHECK (span1 != span4);
|
|
|
|
|
|
CHECK (span4 != span1);
|
|
|
|
|
|
CHECK (span2 != span3);
|
|
|
|
|
|
CHECK (span3 != span2);
|
|
|
|
|
|
CHECK (span2 != span4);
|
|
|
|
|
|
CHECK (span4 != span2);
|
|
|
|
|
|
|
2022-12-14 03:00:07 +01:00
|
|
|
|
// note that TimeSpan is oriented at creation
|
2011-04-02 04:01:06 +02:00
|
|
|
|
CHECK (span1.end() == span2.end());
|
|
|
|
|
|
CHECK (span3.end() == span4.end());
|
|
|
|
|
|
|
|
|
|
|
|
// Verify the extended order on time intervals
|
2011-05-20 20:31:16 +02:00
|
|
|
|
TimeSpan span1x (org+org, Duration(org)); // starting later than span1
|
|
|
|
|
|
TimeSpan span3y (org, FSecs(2)); // shorter than span3
|
|
|
|
|
|
TimeSpan span3z (org+org, FSecs(2)); // starting later and shorter than span3
|
2011-04-02 04:01:06 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (span1 != span1x);
|
|
|
|
|
|
CHECK (span3 != span3y);
|
|
|
|
|
|
CHECK (span3 != span3z);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ( span1 < span1x);
|
|
|
|
|
|
CHECK ( span1 <= span1x);
|
|
|
|
|
|
CHECK (!(span1 > span1x));
|
|
|
|
|
|
CHECK (!(span1 >= span1x));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ( span3 > span3y);
|
|
|
|
|
|
CHECK ( span3 >= span3y);
|
|
|
|
|
|
CHECK (!(span3 < span3y));
|
|
|
|
|
|
CHECK (!(span3 <= span3y));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ( span3 < span3z); // Note: the start point takes precedence on comparison
|
|
|
|
|
|
CHECK ( span3y < span3z);
|
|
|
|
|
|
|
|
|
|
|
|
// Verify this order is really different
|
|
|
|
|
|
// than the basic ordering of time values
|
|
|
|
|
|
CHECK (span1 < span1x);
|
|
|
|
|
|
CHECK (span1.duration() == span1x.duration());
|
|
|
|
|
|
CHECK (span1.start() < span1x.start());
|
|
|
|
|
|
CHECK (span1.end() < span1x.end());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (span3 > span3y);
|
|
|
|
|
|
CHECK (span3.duration() > span3y.duration());
|
|
|
|
|
|
CHECK (span3.start() == span3y.start());
|
2011-04-04 00:41:38 +02:00
|
|
|
|
CHECK (span3.end() > span3y.end());
|
2011-04-02 04:01:06 +02:00
|
|
|
|
CHECK (Time(span3) == Time(span3y));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (span3 < span3z);
|
|
|
|
|
|
CHECK (span3.duration() > span3z.duration());
|
|
|
|
|
|
CHECK (span3.start() < span3z.start());
|
2011-04-04 00:41:38 +02:00
|
|
|
|
CHECK (span3.end() != span3z.end()); // it's shorter, and org can be random, so that's all we know
|
2011-04-02 04:01:06 +02:00
|
|
|
|
CHECK (Time(span3) < Time(span3z));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (span3y < span3z);
|
|
|
|
|
|
CHECK (span3y.duration() == span3z.duration());
|
|
|
|
|
|
CHECK (span3y.start() < span3z.start());
|
2011-04-04 00:41:38 +02:00
|
|
|
|
CHECK (span3y.end() < span3z.end());
|
2011-04-02 04:01:06 +02:00
|
|
|
|
CHECK (Time(span3) < Time(span3z));
|
2010-12-28 07:24:54 +01:00
|
|
|
|
}
|
2011-05-16 04:02:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
relateTimeIntervals (TimeValue org)
|
|
|
|
|
|
{
|
2011-05-16 08:38:27 +02:00
|
|
|
|
Time oneSec(FSecs(1));
|
|
|
|
|
|
|
|
|
|
|
|
TimeSpan span1 (org, FSecs(2));
|
|
|
|
|
|
TimeSpan span2 (oneSec + org, FSecs(2));
|
2011-05-16 04:02:26 +02:00
|
|
|
|
|
|
|
|
|
|
TimeVar probe(org);
|
|
|
|
|
|
CHECK ( span1.contains(probe));
|
|
|
|
|
|
CHECK (!span2.contains(probe));
|
|
|
|
|
|
|
|
|
|
|
|
probe = span2;
|
|
|
|
|
|
CHECK ( span1.contains(probe));
|
|
|
|
|
|
CHECK ( span2.contains(probe));
|
|
|
|
|
|
|
|
|
|
|
|
probe = span1.end();
|
|
|
|
|
|
CHECK (!span1.contains(probe)); // Note: end is always exclusive
|
|
|
|
|
|
CHECK ( span2.contains(probe));
|
|
|
|
|
|
|
|
|
|
|
|
probe = span2.end();
|
|
|
|
|
|
CHECK (!span1.contains(probe));
|
|
|
|
|
|
CHECK (!span2.contains(probe));
|
|
|
|
|
|
}
|
2010-12-22 04:09:27 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
|
|
|
|
LAUNCHER (TimeValue_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}} // namespace lib::time::test
|