Lumiera  0.pre.03
»edit your freedom«
time-mutation-test.cpp
Go to the documentation of this file.
1 /*
2  TimeMutation(Test) - explicitly changing time specifications
3 
4  Copyright (C) Lumiera.org
5  2011, 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/time/timequant.hpp"
32 #include "lib/time/mutation.hpp"
34 #include "lib/util.hpp"
35 
36 #include <boost/lexical_cast.hpp>
37 #include <string>
38 
39 using boost::lexical_cast;
40 using util::isnil;
41 using std::string;
42 
43 
44 namespace lib {
45 namespace time{
46 namespace test{
47 
49 
50  namespace {
51  inline string
52  pop (Arg arg)
53  {
54  if (isnil (arg)) return "";
55  string entry = arg[0];
56  arg.erase (arg.begin());
57  return entry;
58  }
59  }
60 
61 
62 
63 
64  /************************************************************/
71  class TimeMutation_test : public Test
72  {
73  gavl_time_t
74  random_or_get (string arg)
75  {
76  if (isnil(arg))
77  return gavl_time_t (1 + (rand() % 100000)) * TimeValue::SCALE;
78  else
79  return lexical_cast<gavl_time_t> (arg);
80  }
81 
82  struct TestValues
83  {
84  TimeVar var;
85  Duration dur;
86  TimeSpan span;
87  QuTime quant;
88 
90  : var(o)
91  , dur(o)
92  , span(o, Offset(o))
93  , quant(o, "test_grid")
94  { }
95  };
96 
97 
98  virtual void
99  run (Arg arg)
100  {
101  TimeValue o (random_or_get (pop(arg)));
102  TimeValue c (random_or_get (pop(arg)));
103  CHECK (o != c, "unsuitable testdata");
104 
105  // using a 25fps-grid, but with an time origin offset by 1/50sec
106  TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50)));
107 
108  QuTime qChange (c, "test_grid");
109  FrameNr count(qChange);
110 
111  mutate_by_Value (o, Time(c));
112  mutate_by_Offset (o, Offset(c));
113  mutate_quantised (o, qChange);
114  mutate_by_Increment(o, count);
115  }
116 
117 
118  void
119  mutate_by_Value(TimeValue original, Time newStart)
120  {
121  TestValues t(original);
122 
123  CHECK (t.span.start() == original);
124  t.span.accept (Mutation::changeTime (newStart));
125  CHECK (t.span.start() != original);
126  CHECK (t.span.start() == newStart);
127 
128  // instead of invoking directly, we can store and copy mutation messages
129  EncapsulatedMutation change_back(Mutation::changeTime (Time(original)));
130  t.span.accept (change_back);
131  CHECK (t.span.start() == original);
132 
133  CHECK (t.quant == original);
134  t.quant.accept (Mutation::changeTime (newStart));
135  CHECK (t.quant != original);
136  CHECK (t.quant == newStart);
137 
138  // Durations have no start time...
139  VERIFY_ERROR (INVALID_MUTATION, t.dur.accept(change_back));
140  VERIFY_ERROR (INVALID_MUTATION, t.span.duration().accept(change_back));
141 
142  CHECK (t.dur == original);
143  t.dur.accept (Mutation::changeDuration (Duration(2*t.var)));
144  CHECK (t.dur != original);
145  CHECK (t.dur == t.var*2);
146 
147  CHECK (t.span.start() == original);
148  CHECK (t.span.duration() == original);
149  t.span.accept (Mutation::changeDuration(Duration(3*t.var)));
150  CHECK (t.span.duration() != original);
151  CHECK (t.span.duration() == t.var*3); // affects the duration,
152  CHECK (t.span.start() == original); // while the start time remains unaltered
153 
154  // can't change the 'duration' of a quantised time point...
155  VERIFY_ERROR (INVALID_MUTATION, t.quant.accept(Mutation::changeDuration (Duration(t.var))));
156  }
157 
158 
159  void
160  mutate_by_Offset (TimeValue original, Offset change)
161  {
162  TestValues t(original);
163  TimeValue& should_be(t.var+=change); // use as ref for verification
164 
165  CHECK (t.span == original);
166  CHECK (t.span != should_be);
167  t.span.accept (Mutation::adjust (change));
168  CHECK (t.span == should_be);
169 
170  t.dur.accept (Mutation::adjust (change));
171  CHECK (t.dur == should_be);
172 
173  t.quant.accept (Mutation::adjust (change));
174  CHECK (t.quant == should_be);
175 
176  // adjustment is cumulative
177  EncapsulatedMutation back_off = Mutation::adjust (-change);
178  t.span.accept (back_off);
179  CHECK (t.span == original);
180  t.span.accept (back_off);
181  t.span.accept (back_off);
182  t.span.accept (back_off);
183  CHECK (t.span == Time(original) - 3*change);
184  }
185 
186 
187  void
188  mutate_quantised (TimeValue original, QuTime change)
189  {
190  TestValues t(original);
191  t.var = change;
192  CHECK (Time(change) == t.var); // the underlying raw time value
193 
194  CHECK (t.span == original);
195  t.span.accept (Mutation::materialise (change));
196  CHECK (t.span != original);
197  CHECK (t.span != t.var); // really materialised (grid-aligned)
198 
199  // simulate what happened by explicit operations...
200  Secs seconds = change.formatAs<format::Seconds>();
201  PQuant quantiser(change);
202  Time materialised (quantiser->materialise(change));
203  CHECK (t.span == materialised);
204 
205  CHECK (t.span.duration() == original); // not affected by mutation as usual
206  VERIFY_ERROR (INVALID_MUTATION, t.dur.accept (Mutation::materialise (change)));
207  // not surprising, a time point has no duration!!
208 
209  CHECK (t.quant == original);
210  t.quant.accept (Mutation::materialise (change));
211  CHECK (t.quant != original);
212  CHECK (t.quant == materialised);
213  // but note, here we checked the underlying raw value.
214  // because t.quant is itself quantised, this might
215  // result in a second, chained quantisation finally
216 
217  // Here accidentally both the change and t.quant use the same grid.
218  // For a more contrived example, we try to use a different grid...
219  TimeGrid::build("special_funny_grid", 1, Time(0,-10)); // (1 frame per second, zero point at -10s)
220  QuTime funny (original, "special_funny_grid");
221  funny.accept (Mutation::materialise (change));
222  CHECK (funny == t.quant); // leading to the same raw value this far
223 
224  Time doublyQuantised (PQuant(funny)->materialise(funny));
225  CHECK (doublyQuantised != materialised);
226  }
227 
228 
229  void
230  mutate_by_Increment (TimeValue original, int change)
231  {
232  TestValues t(original);
233 
234  // without any additional specification,
235  // the nudge-Mutation uses a 'natural grid'
236  t.span.accept (Mutation::nudge (change));
237  t.dur.accept (Mutation::nudge (change));
238 
239  t.var += Time(FSecs(change)); // natural grid is in seconds
240  CHECK (t.span.start() == t.var);
241  CHECK (t.dur == t.var);
242 
243  // any other grid can be specified explicitly
244  t.dur.accept (Mutation::nudge (change, "test_grid"));
245  CHECK (t.dur != t.var);
246  CHECK (t.dur == t.var + change * FrameRate::PAL.duration());
247  // ....this time the change was measured in grid units,
248  // taken relative to the origin of the specified grid
249  PQuant testGrid = Quantiser::retrieve("test_grid");
250  Offset distance (testGrid->timeOf(0), testGrid->timeOf(change));
251  CHECK (distance == change * FrameRate::PAL.duration());
252  CHECK (t.dur - t.var == distance);
253 
254 
255 
256  // To the contrary, *quantised* values behave quite differently...
257  long frameNr = t.quant.formatAs<format::Frames>();
258 
259  t.quant.accept (Mutation::nudge (change));
260  CHECK (t.quant != original);
261  long frameNr_after = t.quant.formatAs<format::Frames>();
262  CHECK (frameNr_after == frameNr + change);
263  //i.e. the quantised time's own grid is used
264  }
265  };
266 
267 
269  LAUNCHER (TimeMutation_test, "unit common");
270 
271 
272 
273 }}} // namespace lib::time::test
a mutable time value, behaving like a plain number, allowing copy and re-accessing ...
Definition: timevalue.hpp:241
Interface: a grid and scale definition for time quantisation.
Definition: time-grid.hpp:86
Frame count as timecode format.
Definition: formats.hpp:84
Modifying time and timecode values.
static PQuant retrieve(Symbol gridID)
Access an existing grid definition or quantiser, known by the given symbolic ID.
static EncapsulatedMutation changeTime(Time)
Convenience factory to yield a simple Mutation changing the absolute start time.
Definition: mutation.cpp:294
void accept(Mutation const &)
may change start / duration
Definition: mutation.hpp:132
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Definition: run.hpp:49
static const gavl_time_t SCALE
Number of micro ticks (µs) per second as basic time scale.
Definition: timevalue.hpp:176
static EncapsulatedMutation adjust(Offset)
Convenience factory: simple Mutation to adjust the duration or length of a timespan.
Definition: mutation.cpp:316
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
static PGrid build(FrameRate frames_per_second)
Definition: time-grid.cpp:167
void accept(Mutation const &)
receive change message, which might cause re-quantisation
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:308
format::Traits< FMT >::TimeCode formatAs() const
create new time code instance, then castInto
Definition: timequant.hpp:147
static EncapsulatedMutation materialise(QuTime const &)
Convenience factory: materialise the given quantised time into an explicit fixed internal time value...
Definition: mutation.cpp:328
Simple test class runner.
Template to build polymorphic value objects.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Simple timecode specification as fractional seconds.
Definition: formats.hpp:135
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.
Duration duration() const
duration of one frame
Definition: time.cpp:263
Support library to represent grid-aligned time specifications This is part of Lumiera&#39;s time and time...
Offset measures a distance in time.
Definition: timevalue.hpp:367
To establish a reference scale for quantised time values.
static EncapsulatedMutation nudge(int adjustment)
build a time mutation to nudge the target time value by an offset, defined as number of steps on an i...
Definition: mutation.cpp:345
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:477
static EncapsulatedMutation changeDuration(Duration)
Convenience factory: simple Mutation to adjust the duration or length of a timespan.
Definition: mutation.cpp:305
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:582
A frame counting timecode value.
Definition: timecode.hpp:105
a family of time value like entities and their relationships.
basic constant internal time value.
Definition: timevalue.hpp:142
static const FrameRate PAL
predefined constant for PAL framerate
Definition: timevalue.hpp:680
grid aligned time specification, referring to a specific scale.
Definition: timequant.hpp:99