1/*
2 * Copyright (c) 2013, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "core/animation/TimedItem.h"
33
34#include <gtest/gtest.h>
35
36using namespace WebCore;
37
38namespace {
39
40class TestTimedItemEventDelegate : public TimedItemEventDelegate {
41public:
42    void onEventCondition(bool wasInPlay, bool isInPlay, double previousIteration, double currentIteration) OVERRIDE
43    {
44        m_eventTriggered = true;
45        m_playStateChanged = wasInPlay != isInPlay;
46        m_iterationChanged = isInPlay && previousIteration != currentIteration;
47        if (isInPlay)
48            ASSERT(!isNull(currentIteration));
49
50    }
51    void reset()
52    {
53        m_eventTriggered = false;
54        m_playStateChanged = false;
55        m_iterationChanged = false;
56    }
57    bool eventTriggered() { return m_eventTriggered; }
58    bool playStateChanged() { return m_playStateChanged; }
59    bool iterationChanged() { return m_iterationChanged; }
60
61private:
62    bool m_eventTriggered;
63    bool m_playStateChanged;
64    bool m_iterationChanged;
65};
66
67class TestTimedItem : public TimedItem {
68public:
69    static PassRefPtr<TestTimedItem> create(const Timing& specified)
70    {
71        return adoptRef(new TestTimedItem(specified, new TestTimedItemEventDelegate()));
72    }
73
74    void updateInheritedTime(double time)
75    {
76        m_eventDelegate->reset();
77        TimedItem::updateInheritedTime(time);
78    }
79
80    void updateChildrenAndEffects(bool wasActiveOrInEffect) const FINAL OVERRIDE {
81    }
82
83    void willDetach() { }
84
85    TestTimedItemEventDelegate* eventDelegate() { return m_eventDelegate; }
86
87private:
88    TestTimedItem(const Timing& specified, TestTimedItemEventDelegate* eventDelegate)
89        : TimedItem(specified, adoptPtr(eventDelegate))
90        , m_eventDelegate(eventDelegate)
91    {
92    }
93
94    TestTimedItemEventDelegate* m_eventDelegate;
95};
96
97TEST(TimedItem, Sanity)
98{
99    Timing timing;
100    timing.hasIterationDuration = true;
101    timing.iterationDuration = 2;
102    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
103
104    ASSERT_FALSE(timedItem->isCurrent());
105    ASSERT_FALSE(timedItem->isInEffect());
106    ASSERT_FALSE(timedItem->isInPlay());
107    ASSERT_TRUE(isNull(timedItem->currentIteration()));
108    ASSERT_EQ(0, timedItem->startTime());
109    ASSERT_TRUE(isNull(timedItem->activeDuration()));
110    ASSERT_TRUE(isNull(timedItem->timeFraction()));
111
112    timedItem->updateInheritedTime(0);
113
114    ASSERT_TRUE(timedItem->isInPlay());
115    ASSERT_TRUE(timedItem->isCurrent());
116    ASSERT_TRUE(timedItem->isInEffect());
117    ASSERT_EQ(0, timedItem->currentIteration());
118    ASSERT_EQ(0, timedItem->startTime());
119    ASSERT_EQ(2, timedItem->activeDuration());
120    ASSERT_EQ(0, timedItem->timeFraction());
121
122    timedItem->updateInheritedTime(1);
123
124    ASSERT_TRUE(timedItem->isInPlay());
125    ASSERT_TRUE(timedItem->isCurrent());
126    ASSERT_TRUE(timedItem->isInEffect());
127    ASSERT_EQ(0, timedItem->currentIteration());
128    ASSERT_EQ(0, timedItem->startTime());
129    ASSERT_EQ(2, timedItem->activeDuration());
130    ASSERT_EQ(0.5, timedItem->timeFraction());
131
132    timedItem->updateInheritedTime(2);
133
134    ASSERT_FALSE(timedItem->isInPlay());
135    ASSERT_FALSE(timedItem->isCurrent());
136    ASSERT_TRUE(timedItem->isInEffect());
137    ASSERT_EQ(0, timedItem->currentIteration());
138    ASSERT_EQ(0, timedItem->startTime());
139    ASSERT_EQ(2, timedItem->activeDuration());
140    ASSERT_EQ(1, timedItem->timeFraction());
141
142    timedItem->updateInheritedTime(3);
143
144    ASSERT_FALSE(timedItem->isInPlay());
145    ASSERT_FALSE(timedItem->isCurrent());
146    ASSERT_TRUE(timedItem->isInEffect());
147    ASSERT_EQ(0, timedItem->currentIteration());
148    ASSERT_EQ(0, timedItem->startTime());
149    ASSERT_EQ(2, timedItem->activeDuration());
150    ASSERT_EQ(1, timedItem->timeFraction());
151}
152
153TEST(TimedItem, FillForwards)
154{
155    Timing timing;
156    timing.hasIterationDuration = true;
157    timing.iterationDuration = 1;
158    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
159
160    timedItem->updateInheritedTime(-1);
161    ASSERT_TRUE(isNull(timedItem->timeFraction()));
162
163    timedItem->updateInheritedTime(2);
164    ASSERT_EQ(1, timedItem->timeFraction());
165}
166
167TEST(TimedItem, FillBackwards)
168{
169    Timing timing;
170    timing.hasIterationDuration = true;
171    timing.iterationDuration = 1;
172    timing.fillMode = Timing::FillModeBackwards;
173    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
174
175    timedItem->updateInheritedTime(-1);
176    ASSERT_EQ(0, timedItem->timeFraction());
177
178    timedItem->updateInheritedTime(2);
179    ASSERT_TRUE(isNull(timedItem->timeFraction()));
180}
181
182TEST(TimedItem, FillBoth)
183{
184    Timing timing;
185    timing.hasIterationDuration = true;
186    timing.iterationDuration = 1;
187    timing.fillMode = Timing::FillModeBoth;
188    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
189
190    timedItem->updateInheritedTime(-1);
191    ASSERT_EQ(0, timedItem->timeFraction());
192
193    timedItem->updateInheritedTime(2);
194    ASSERT_EQ(1, timedItem->timeFraction());
195}
196
197TEST(TimedItem, StartDelay)
198{
199    Timing timing;
200    timing.hasIterationDuration = true;
201    timing.iterationDuration = 1;
202    timing.startDelay = 0.5;
203    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
204
205    timedItem->updateInheritedTime(0);
206    ASSERT_TRUE(isNull(timedItem->timeFraction()));
207
208    timedItem->updateInheritedTime(0.5);
209    ASSERT_EQ(0, timedItem->timeFraction());
210
211    timedItem->updateInheritedTime(1.5);
212    ASSERT_EQ(1, timedItem->timeFraction());
213}
214
215TEST(TimedItem, InfiniteIteration)
216{
217    Timing timing;
218    timing.hasIterationDuration = true;
219    timing.iterationDuration = 1;
220    timing.iterationCount = std::numeric_limits<double>::infinity();
221    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
222
223    timedItem->updateInheritedTime(-1);
224    ASSERT_TRUE(isNull(timedItem->currentIteration()));
225    ASSERT_TRUE(isNull(timedItem->timeFraction()));
226
227    ASSERT_EQ(std::numeric_limits<double>::infinity(), timedItem->activeDuration());
228
229    timedItem->updateInheritedTime(0);
230    ASSERT_EQ(0, timedItem->currentIteration());
231    ASSERT_EQ(0, timedItem->timeFraction());
232}
233
234TEST(TimedItem, Iteration)
235{
236    Timing timing;
237    timing.iterationCount = 2;
238    timing.hasIterationDuration = true;
239    timing.iterationDuration = 2;
240    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
241
242    timedItem->updateInheritedTime(0);
243    ASSERT_EQ(0, timedItem->currentIteration());
244    ASSERT_EQ(0, timedItem->timeFraction());
245
246    timedItem->updateInheritedTime(1);
247    ASSERT_EQ(0, timedItem->currentIteration());
248    ASSERT_EQ(0.5, timedItem->timeFraction());
249
250    timedItem->updateInheritedTime(2);
251    ASSERT_EQ(1, timedItem->currentIteration());
252    ASSERT_EQ(0, timedItem->timeFraction());
253
254    timedItem->updateInheritedTime(2);
255    ASSERT_EQ(1, timedItem->currentIteration());
256    ASSERT_EQ(0, timedItem->timeFraction());
257
258    timedItem->updateInheritedTime(5);
259    ASSERT_EQ(1, timedItem->currentIteration());
260    ASSERT_EQ(1, timedItem->timeFraction());
261}
262
263TEST(TimedItem, IterationStart)
264{
265    Timing timing;
266    timing.iterationStart = 1.2;
267    timing.iterationCount = 2.2;
268    timing.hasIterationDuration = true;
269    timing.iterationDuration = 1;
270    timing.fillMode = Timing::FillModeBoth;
271    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
272
273    timedItem->updateInheritedTime(-1);
274    ASSERT_EQ(1, timedItem->currentIteration());
275    ASSERT_NEAR(0.2, timedItem->timeFraction(), 0.000000000000001);
276
277    timedItem->updateInheritedTime(0);
278    ASSERT_EQ(1, timedItem->currentIteration());
279    ASSERT_NEAR(0.2, timedItem->timeFraction(), 0.000000000000001);
280
281    timedItem->updateInheritedTime(10);
282    ASSERT_EQ(3, timedItem->currentIteration());
283    ASSERT_NEAR(0.4, timedItem->timeFraction(), 0.000000000000001);
284}
285
286TEST(TimedItem, IterationAlternate)
287{
288    Timing timing;
289    timing.iterationCount = 10;
290    timing.hasIterationDuration = true;
291    timing.iterationDuration = 1;
292    timing.direction = Timing::PlaybackDirectionAlternate;
293    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
294
295    timedItem->updateInheritedTime(0.75);
296    ASSERT_EQ(0, timedItem->currentIteration());
297    ASSERT_EQ(0.75, timedItem->timeFraction());
298
299    timedItem->updateInheritedTime(1.75);
300    ASSERT_EQ(1, timedItem->currentIteration());
301    ASSERT_EQ(0.25, timedItem->timeFraction());
302
303    timedItem->updateInheritedTime(2.75);
304    ASSERT_EQ(2, timedItem->currentIteration());
305    ASSERT_EQ(0.75, timedItem->timeFraction());
306}
307
308TEST(TimedItem, IterationAlternateReverse)
309{
310    Timing timing;
311    timing.iterationCount = 10;
312    timing.hasIterationDuration = true;
313    timing.iterationDuration = 1;
314    timing.direction = Timing::PlaybackDirectionAlternateReverse;
315    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
316
317    timedItem->updateInheritedTime(0.75);
318    ASSERT_EQ(0, timedItem->currentIteration());
319    ASSERT_EQ(0.25, timedItem->timeFraction());
320
321    timedItem->updateInheritedTime(1.75);
322    ASSERT_EQ(1, timedItem->currentIteration());
323    ASSERT_EQ(0.75, timedItem->timeFraction());
324
325    timedItem->updateInheritedTime(2.75);
326    ASSERT_EQ(2, timedItem->currentIteration());
327    ASSERT_EQ(0.25, timedItem->timeFraction());
328}
329
330TEST(TimedItem, ZeroDurationSanity)
331{
332    Timing timing;
333    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
334
335    ASSERT_FALSE(timedItem->isInPlay());
336    ASSERT_FALSE(timedItem->isCurrent());
337    ASSERT_FALSE(timedItem->isInEffect());
338    ASSERT_TRUE(isNull(timedItem->currentIteration()));
339    ASSERT_EQ(0, timedItem->startTime());
340    ASSERT_TRUE(isNull(timedItem->activeDuration()));
341    ASSERT_TRUE(isNull(timedItem->timeFraction()));
342
343    timedItem->updateInheritedTime(0);
344
345    ASSERT_FALSE(timedItem->isInPlay());
346    ASSERT_FALSE(timedItem->isCurrent());
347    ASSERT_TRUE(timedItem->isInEffect());
348    ASSERT_EQ(0, timedItem->currentIteration());
349    ASSERT_EQ(0, timedItem->startTime());
350    ASSERT_EQ(0, timedItem->activeDuration());
351    ASSERT_EQ(1, timedItem->timeFraction());
352
353    timedItem->updateInheritedTime(1);
354
355    ASSERT_FALSE(timedItem->isInPlay());
356    ASSERT_FALSE(timedItem->isCurrent());
357    ASSERT_TRUE(timedItem->isInEffect());
358    ASSERT_EQ(0, timedItem->currentIteration());
359    ASSERT_EQ(0, timedItem->startTime());
360    ASSERT_EQ(0, timedItem->activeDuration());
361    ASSERT_EQ(1, timedItem->timeFraction());
362}
363
364TEST(TimedItem, ZeroDurationFillForwards)
365{
366    Timing timing;
367    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
368
369    timedItem->updateInheritedTime(-1);
370    ASSERT_TRUE(isNull(timedItem->timeFraction()));
371
372    timedItem->updateInheritedTime(0);
373    ASSERT_EQ(1, timedItem->timeFraction());
374
375    timedItem->updateInheritedTime(1);
376    ASSERT_EQ(1, timedItem->timeFraction());
377}
378
379TEST(TimedItem, ZeroDurationFillBackwards)
380{
381    Timing timing;
382    timing.fillMode = Timing::FillModeBackwards;
383    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
384
385    timedItem->updateInheritedTime(-1);
386    ASSERT_EQ(0, timedItem->timeFraction());
387
388    timedItem->updateInheritedTime(0);
389    ASSERT_TRUE(isNull(timedItem->timeFraction()));
390
391    timedItem->updateInheritedTime(1);
392    ASSERT_TRUE(isNull(timedItem->timeFraction()));
393}
394
395TEST(TimedItem, ZeroDurationFillBoth)
396{
397    Timing timing;
398    timing.fillMode = Timing::FillModeBoth;
399    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
400
401    timedItem->updateInheritedTime(-1);
402    ASSERT_EQ(0, timedItem->timeFraction());
403
404    timedItem->updateInheritedTime(0);
405    ASSERT_EQ(1, timedItem->timeFraction());
406
407    timedItem->updateInheritedTime(1);
408    ASSERT_EQ(1, timedItem->timeFraction());
409}
410
411TEST(TimedItem, ZeroDurationStartDelay)
412{
413    Timing timing;
414    timing.startDelay = 0.5;
415    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
416
417    timedItem->updateInheritedTime(0);
418    ASSERT_TRUE(isNull(timedItem->timeFraction()));
419
420    timedItem->updateInheritedTime(0.5);
421    ASSERT_EQ(1, timedItem->timeFraction());
422
423    timedItem->updateInheritedTime(1.5);
424    ASSERT_EQ(1, timedItem->timeFraction());
425}
426
427TEST(TimedItem, ZeroDurationIterationStartAndCount)
428{
429    Timing timing;
430    timing.iterationStart = 0.1;
431    timing.iterationCount = 0.2;
432    timing.fillMode = Timing::FillModeBoth;
433    timing.startDelay = 0.3;
434    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
435
436    timedItem->updateInheritedTime(0);
437    ASSERT_EQ(0.1, timedItem->timeFraction());
438
439    timedItem->updateInheritedTime(0.3);
440    ASSERT_DOUBLE_EQ(0.3, timedItem->timeFraction());
441
442    timedItem->updateInheritedTime(1);
443    ASSERT_DOUBLE_EQ(0.3, timedItem->timeFraction());
444}
445
446// FIXME: Needs specification work -- ASSERTION FAILED: activeDuration >= 0
447TEST(TimedItem, DISABLED_ZeroDurationInfiniteIteration)
448{
449    Timing timing;
450    timing.iterationCount = std::numeric_limits<double>::infinity();
451    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
452
453    timedItem->updateInheritedTime(-1);
454    ASSERT_TRUE(isNull(timedItem->currentIteration()));
455    ASSERT_TRUE(isNull(timedItem->timeFraction()));
456    ASSERT_TRUE(isNull(timedItem->activeDuration()));
457
458    timedItem->updateInheritedTime(0);
459    ASSERT_EQ(std::numeric_limits<double>::infinity(), timedItem->currentIteration());
460    ASSERT_EQ(1, timedItem->timeFraction());
461}
462
463TEST(TimedItem, ZeroDurationIteration)
464{
465    Timing timing;
466    timing.iterationCount = 2;
467    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
468
469    timedItem->updateInheritedTime(-1);
470    ASSERT_TRUE(isNull(timedItem->currentIteration()));
471    ASSERT_TRUE(isNull(timedItem->timeFraction()));
472
473    timedItem->updateInheritedTime(0);
474    ASSERT_EQ(1, timedItem->currentIteration());
475    ASSERT_EQ(1, timedItem->timeFraction());
476
477    timedItem->updateInheritedTime(1);
478    ASSERT_EQ(1, timedItem->currentIteration());
479    ASSERT_EQ(1, timedItem->timeFraction());
480}
481
482TEST(TimedItem, ZeroDurationIterationStart)
483{
484    Timing timing;
485    timing.iterationStart = 1.2;
486    timing.iterationCount = 2.2;
487    timing.fillMode = Timing::FillModeBoth;
488    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
489
490    timedItem->updateInheritedTime(-1);
491    ASSERT_EQ(1, timedItem->currentIteration());
492    ASSERT_NEAR(0.2, timedItem->timeFraction(), 0.000000000000001);
493
494    timedItem->updateInheritedTime(0);
495    ASSERT_EQ(3, timedItem->currentIteration());
496    ASSERT_NEAR(0.4, timedItem->timeFraction(), 0.000000000000001);
497
498    timedItem->updateInheritedTime(10);
499    ASSERT_EQ(3, timedItem->currentIteration());
500    ASSERT_NEAR(0.4, timedItem->timeFraction(), 0.000000000000001);
501}
502
503TEST(TimedItem, ZeroDurationIterationAlternate)
504{
505    Timing timing;
506    timing.iterationCount = 2;
507    timing.direction = Timing::PlaybackDirectionAlternate;
508    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
509
510    timedItem->updateInheritedTime(-1);
511    ASSERT_TRUE(isNull(timedItem->currentIteration()));
512    ASSERT_TRUE(isNull(timedItem->timeFraction()));
513
514    timedItem->updateInheritedTime(0);
515    ASSERT_EQ(1, timedItem->currentIteration());
516    ASSERT_EQ(0, timedItem->timeFraction());
517
518    timedItem->updateInheritedTime(1);
519    ASSERT_EQ(1, timedItem->currentIteration());
520    ASSERT_EQ(0, timedItem->timeFraction());
521}
522
523TEST(TimedItem, ZeroDurationIterationAlternateReverse)
524{
525    Timing timing;
526    timing.iterationCount = 2;
527    timing.direction = Timing::PlaybackDirectionAlternateReverse;
528    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
529
530    timedItem->updateInheritedTime(-1);
531    ASSERT_TRUE(isNull(timedItem->currentIteration()));
532    ASSERT_TRUE(isNull(timedItem->timeFraction()));
533
534    timedItem->updateInheritedTime(0);
535    ASSERT_EQ(1, timedItem->currentIteration());
536    ASSERT_EQ(1, timedItem->timeFraction());
537
538    timedItem->updateInheritedTime(1);
539    ASSERT_EQ(1, timedItem->currentIteration());
540    ASSERT_EQ(1, timedItem->timeFraction());
541}
542
543TEST(TimedItem, Events)
544{
545    Timing timing;
546    timing.hasIterationDuration = true;
547    timing.iterationDuration = 1;
548    timing.iterationCount = 2;
549    RefPtr<TestTimedItem> timedItem = TestTimedItem::create(timing);
550
551    timedItem->updateInheritedTime(0.3);
552    ASSERT_TRUE(timedItem->eventDelegate()->eventTriggered());
553    EXPECT_TRUE(timedItem->eventDelegate()->playStateChanged());
554    EXPECT_TRUE(timedItem->eventDelegate()->iterationChanged());
555
556    timedItem->updateInheritedTime(0.6);
557    ASSERT_FALSE(timedItem->eventDelegate()->eventTriggered());
558
559    timedItem->updateInheritedTime(1.5);
560    ASSERT_TRUE(timedItem->eventDelegate()->eventTriggered());
561    EXPECT_TRUE(timedItem->eventDelegate()->iterationChanged());
562    EXPECT_FALSE(timedItem->eventDelegate()->playStateChanged());
563
564    timedItem->updateInheritedTime(2.5);
565    ASSERT_TRUE(timedItem->eventDelegate()->eventTriggered());
566    EXPECT_FALSE(timedItem->eventDelegate()->iterationChanged());
567    EXPECT_TRUE(timedItem->eventDelegate()->playStateChanged());
568
569    timedItem->updateInheritedTime(3);
570    ASSERT_FALSE(timedItem->eventDelegate()->eventTriggered());
571
572    timedItem->updateInheritedTime(1.5);
573    ASSERT_TRUE(timedItem->eventDelegate()->eventTriggered());
574    EXPECT_FALSE(timedItem->eventDelegate()->iterationChanged());
575    EXPECT_TRUE(timedItem->eventDelegate()->playStateChanged());
576}
577}
578