1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include <QtCore/qparallelanimationgroup.h>
32
33Q_DECLARE_METATYPE(QAbstractAnimation::State)
34
35class tst_QParallelAnimationGroup : public QObject
36{
37 Q_OBJECT
38public Q_SLOTS:
39 void initTestCase();
40
41private slots:
42 void construction();
43 void setCurrentTime();
44 void stateChanged();
45 void clearGroup();
46 void propagateGroupUpdateToChildren();
47 void updateChildrenWithRunningGroup();
48 void deleteChildrenWithRunningGroup();
49 void startChildrenWithStoppedGroup();
50 void stopGroupWithRunningChild();
51 void startGroupWithRunningChild();
52 void zeroDurationAnimation();
53 void stopUncontrolledAnimations();
54 void loopCount_data();
55 void loopCount();
56 void autoAdd();
57 void pauseResume();
58
59 void crashWhenRemovingUncontrolledAnimation();
60};
61
62void tst_QParallelAnimationGroup::initTestCase()
63{
64 qRegisterMetaType<QAbstractAnimation::State>(typeName: "QAbstractAnimation::State");
65#if defined(Q_OS_DARWIN)
66 // give the Darwin app start event queue time to clear
67 QTest::qWait(1000);
68#endif
69}
70
71void tst_QParallelAnimationGroup::construction()
72{
73 QParallelAnimationGroup animationgroup;
74}
75
76class AnimationObject : public QObject
77{
78 Q_OBJECT
79 Q_PROPERTY(int value READ value WRITE setValue)
80public:
81 AnimationObject(int startValue = 0)
82 : v(startValue)
83 { }
84
85 int value() const { return v; }
86 void setValue(int value) { v = value; }
87
88 int v;
89};
90
91class TestAnimation : public QVariantAnimation
92{
93 Q_OBJECT
94public:
95 virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)};
96 virtual void updateState(QAbstractAnimation::State newState,
97 QAbstractAnimation::State oldState)
98 {
99 Q_UNUSED(oldState)
100 Q_UNUSED(newState)
101 };
102};
103
104class TestAnimation2 : public QVariantAnimation
105{
106 Q_OBJECT
107public:
108 TestAnimation2(QAbstractAnimation *animation) : QVariantAnimation(animation) {}
109 TestAnimation2(int duration, QAbstractAnimation *animation) : QVariantAnimation(animation), m_duration(duration) {}
110
111 virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)};
112 virtual void updateState(QAbstractAnimation::State newState,
113 QAbstractAnimation::State oldState)
114 {
115 Q_UNUSED(oldState)
116 Q_UNUSED(newState)
117 };
118
119 virtual int duration() const {
120 return m_duration;
121 }
122private:
123 int m_duration;
124};
125
126class UncontrolledAnimation : public QPropertyAnimation
127{
128 Q_OBJECT
129public:
130 UncontrolledAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0)
131 : QPropertyAnimation(target, propertyName, parent), id(0)
132 {
133 setDuration(250);
134 setEndValue(0);
135 }
136
137 int duration() const { return -1; /* not time driven */ }
138
139protected:
140 void timerEvent(QTimerEvent *event)
141 {
142 if (event->timerId() == id)
143 stop();
144 }
145
146 void updateRunning(bool running)
147 {
148 if (running) {
149 id = startTimer(interval: 500);
150 } else {
151 killTimer(id);
152 id = 0;
153 }
154 }
155
156private:
157 int id;
158};
159
160void tst_QParallelAnimationGroup::setCurrentTime()
161{
162 AnimationObject p_o1;
163 AnimationObject p_o2;
164 AnimationObject p_o3;
165 AnimationObject t_o1;
166 AnimationObject t_o2;
167
168 // parallel operating on different object/properties
169 QAnimationGroup *parallel = new QParallelAnimationGroup();
170 QVariantAnimation *a1_p_o1 = new QPropertyAnimation(&p_o1, "value");
171 QVariantAnimation *a1_p_o2 = new QPropertyAnimation(&p_o2, "value");
172 QVariantAnimation *a1_p_o3 = new QPropertyAnimation(&p_o3, "value");
173 a1_p_o2->setLoopCount(3);
174 parallel->addAnimation(animation: a1_p_o1);
175 parallel->addAnimation(animation: a1_p_o2);
176 parallel->addAnimation(animation: a1_p_o3);
177
178 UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation(&t_o1, "value");
179 QCOMPARE(notTimeDriven->totalDuration(), -1);
180
181 QVariantAnimation *loopsForever = new QPropertyAnimation(&t_o2, "value");
182 loopsForever->setLoopCount(-1);
183 QCOMPARE(loopsForever->totalDuration(), -1);
184
185 QParallelAnimationGroup group;
186 group.addAnimation(animation: parallel);
187 group.addAnimation(animation: notTimeDriven);
188 group.addAnimation(animation: loopsForever);
189
190 // Current time = 1
191 group.setCurrentTime(1);
192 QCOMPARE(group.state(), QAnimationGroup::Stopped);
193 QCOMPARE(parallel->state(), QAnimationGroup::Stopped);
194 QCOMPARE(a1_p_o1->state(), QAnimationGroup::Stopped);
195 QCOMPARE(a1_p_o2->state(), QAnimationGroup::Stopped);
196 QCOMPARE(a1_p_o3->state(), QAnimationGroup::Stopped);
197 QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped);
198 QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped);
199
200 QCOMPARE(group.currentLoopTime(), 1);
201 QCOMPARE(a1_p_o1->currentLoopTime(), 1);
202 QCOMPARE(a1_p_o2->currentLoopTime(), 1);
203 QCOMPARE(a1_p_o3->currentLoopTime(), 1);
204 QCOMPARE(notTimeDriven->currentLoopTime(), 1);
205 QCOMPARE(loopsForever->currentLoopTime(), 1);
206
207 // Current time = 250
208 group.setCurrentTime(250);
209 QCOMPARE(group.currentLoopTime(), 250);
210 QCOMPARE(a1_p_o1->currentLoopTime(), 250);
211 QCOMPARE(a1_p_o2->currentLoopTime(), 0);
212 QCOMPARE(a1_p_o2->currentLoop(), 1);
213 QCOMPARE(a1_p_o3->currentLoopTime(), 250);
214 QCOMPARE(notTimeDriven->currentLoopTime(), 250);
215 QCOMPARE(loopsForever->currentLoopTime(), 0);
216 QCOMPARE(loopsForever->currentLoop(), 1);
217
218 // Current time = 251
219 group.setCurrentTime(251);
220 QCOMPARE(group.currentLoopTime(), 251);
221 QCOMPARE(a1_p_o1->currentLoopTime(), 250);
222 QCOMPARE(a1_p_o2->currentLoopTime(), 1);
223 QCOMPARE(a1_p_o2->currentLoop(), 1);
224 QCOMPARE(a1_p_o3->currentLoopTime(), 250);
225 QCOMPARE(notTimeDriven->currentLoopTime(), 251);
226 QCOMPARE(loopsForever->currentLoopTime(), 1);
227}
228
229void tst_QParallelAnimationGroup::stateChanged()
230{
231 //this ensures that the correct animations are started when starting the group
232 TestAnimation *anim1 = new TestAnimation;
233 TestAnimation *anim2 = new TestAnimation;
234 TestAnimation *anim3 = new TestAnimation;
235 TestAnimation *anim4 = new TestAnimation;
236 anim1->setDuration(1000);
237 anim2->setDuration(2000);
238 anim3->setDuration(3000);
239 anim4->setDuration(3000);
240 QParallelAnimationGroup group;
241 group.addAnimation(animation: anim1);
242 group.addAnimation(animation: anim2);
243 group.addAnimation(animation: anim3);
244 group.addAnimation(animation: anim4);
245
246 QSignalSpy spy1(anim1, &TestAnimation::stateChanged);
247 QSignalSpy spy2(anim2, &TestAnimation::stateChanged);
248 QSignalSpy spy3(anim3, &TestAnimation::stateChanged);
249 QSignalSpy spy4(anim4, &TestAnimation::stateChanged);
250
251 QVERIFY(spy1.isValid());
252 QVERIFY(spy2.isValid());
253 QVERIFY(spy3.isValid());
254 QVERIFY(spy4.isValid());
255
256 //first; let's start forward
257 group.start();
258 //all the animations should be started
259 QCOMPARE(spy1.count(), 1);
260 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy1.last().first()), TestAnimation::Running);
261 QCOMPARE(spy2.count(), 1);
262 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy2.last().first()), TestAnimation::Running);
263 QCOMPARE(spy3.count(), 1);
264 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy3.last().first()), TestAnimation::Running);
265 QCOMPARE(spy4.count(), 1);
266 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy4.last().first()), TestAnimation::Running);
267
268 group.setCurrentTime(1500); //anim1 should be finished
269 QCOMPARE(group.state(), QAnimationGroup::Running);
270 QCOMPARE(spy1.count(), 2);
271 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy1.last().first()), TestAnimation::Stopped);
272 QCOMPARE(spy2.count(), 1); //no change
273 QCOMPARE(spy3.count(), 1); //no change
274 QCOMPARE(spy4.count(), 1); //no change
275
276 group.setCurrentTime(2500); //anim2 should be finished
277 QCOMPARE(group.state(), QAnimationGroup::Running);
278 QCOMPARE(spy1.count(), 2); //no change
279 QCOMPARE(spy2.count(), 2);
280 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy2.last().first()), TestAnimation::Stopped);
281 QCOMPARE(spy3.count(), 1); //no change
282 QCOMPARE(spy4.count(), 1); //no change
283
284 group.setCurrentTime(3500); //everything should be finished
285 QCOMPARE(group.state(), QAnimationGroup::Stopped);
286 QCOMPARE(spy1.count(), 2); //no change
287 QCOMPARE(spy2.count(), 2); //no change
288 QCOMPARE(spy3.count(), 2);
289 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy3.last().first()), TestAnimation::Stopped);
290 QCOMPARE(spy4.count(), 2);
291 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy4.last().first()), TestAnimation::Stopped);
292
293 //cleanup
294 spy1.clear();
295 spy2.clear();
296 spy3.clear();
297 spy4.clear();
298
299 //now let's try to reverse that
300 group.setDirection(QAbstractAnimation::Backward);
301 group.start();
302
303 //only anim3 and anim4 should be started
304 QCOMPARE(group.state(), QAnimationGroup::Running);
305 QCOMPARE(spy1.count(), 0);
306 QCOMPARE(spy2.count(), 0);
307 QCOMPARE(spy3.count(), 1);
308 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy3.last().first()), TestAnimation::Running);
309 QCOMPARE(spy4.count(), 1);
310 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy4.last().first()), TestAnimation::Running);
311
312 group.setCurrentTime(1500); //anim2 should be started
313 QCOMPARE(group.state(), QAnimationGroup::Running);
314 QCOMPARE(spy1.count(), 0); //no change
315 QCOMPARE(spy2.count(), 1);
316 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy2.last().first()), TestAnimation::Running);
317 QCOMPARE(spy3.count(), 1); //no change
318 QCOMPARE(spy4.count(), 1); //no change
319
320 group.setCurrentTime(500); //anim1 is finally also started
321 QCOMPARE(group.state(), QAnimationGroup::Running);
322 QCOMPARE(spy1.count(), 1);
323 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy1.last().first()), TestAnimation::Running);
324 QCOMPARE(spy2.count(), 1); //no change
325 QCOMPARE(spy3.count(), 1); //no change
326 QCOMPARE(spy4.count(), 1); //no change
327
328 group.setCurrentTime(0); //everything should be stopped
329 QCOMPARE(group.state(), QAnimationGroup::Stopped);
330 QCOMPARE(spy1.count(), 2);
331 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy1.last().first()), TestAnimation::Stopped);
332 QCOMPARE(spy2.count(), 2);
333 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy2.last().first()), TestAnimation::Stopped);
334 QCOMPARE(spy3.count(), 2);
335 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy3.last().first()), TestAnimation::Stopped);
336 QCOMPARE(spy4.count(), 2);
337 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy4.last().first()), TestAnimation::Stopped);
338}
339
340void tst_QParallelAnimationGroup::clearGroup()
341{
342 QParallelAnimationGroup group;
343 static const int animationCount = 10;
344
345 for (int i = 0; i < animationCount; ++i) {
346 new QParallelAnimationGroup(&group);
347 }
348
349 QCOMPARE(group.animationCount(), animationCount);
350
351 QPointer<QAbstractAnimation> children[animationCount];
352 for (int i = 0; i < animationCount; ++i) {
353 QVERIFY(group.animationAt(i) != 0);
354 children[i] = group.animationAt(index: i);
355 }
356
357 group.clear();
358 QCOMPARE(group.animationCount(), 0);
359 QCOMPARE(group.currentLoopTime(), 0);
360 for (int i = 0; i < animationCount; ++i)
361 QVERIFY(children[i].isNull());
362}
363
364void tst_QParallelAnimationGroup::propagateGroupUpdateToChildren()
365{
366 // this test verifies if group state changes are updating its children correctly
367 QParallelAnimationGroup group;
368
369 QObject o;
370 o.setProperty(name: "ole", value: 42);
371 QCOMPARE(o.property("ole").toInt(), 42);
372
373 QPropertyAnimation anim1(&o, "ole");
374 anim1.setEndValue(43);
375 anim1.setDuration(100);
376 QVERIFY(!anim1.currentValue().isValid());
377 QCOMPARE(anim1.currentValue().toInt(), 0);
378 QCOMPARE(o.property("ole").toInt(), 42);
379
380 TestAnimation anim2;
381 anim2.setStartValue(0);
382 anim2.setEndValue(100);
383 anim2.setDuration(200);
384
385 QVERIFY(anim2.currentValue().isValid());
386 QCOMPARE(anim2.currentValue().toInt(), 0);
387
388 QCOMPARE(group.state(), QAnimationGroup::Stopped);
389 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
390 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
391
392 group.addAnimation(animation: &anim1);
393 group.addAnimation(animation: &anim2);
394
395 group.start();
396
397 QCOMPARE(group.state(), QAnimationGroup::Running);
398 QCOMPARE(anim1.state(), QAnimationGroup::Running);
399 QCOMPARE(anim2.state(), QAnimationGroup::Running);
400
401 group.pause();
402
403 QCOMPARE(group.state(), QAnimationGroup::Paused);
404 QCOMPARE(anim1.state(), QAnimationGroup::Paused);
405 QCOMPARE(anim2.state(), QAnimationGroup::Paused);
406
407 group.stop();
408
409 QCOMPARE(group.state(), QAnimationGroup::Stopped);
410 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
411 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
412}
413
414void tst_QParallelAnimationGroup::updateChildrenWithRunningGroup()
415{
416 // assert that its possible to modify a child's state directly while their group is running
417 QParallelAnimationGroup group;
418
419 TestAnimation anim;
420 anim.setStartValue(0);
421 anim.setEndValue(100);
422 anim.setDuration(200);
423
424 QSignalSpy groupStateChangedSpy(&group, &QParallelAnimationGroup::stateChanged);
425 QSignalSpy childStateChangedSpy(&anim, &TestAnimation::stateChanged);
426
427 QVERIFY(groupStateChangedSpy.isValid());
428 QVERIFY(childStateChangedSpy.isValid());
429
430 QCOMPARE(groupStateChangedSpy.count(), 0);
431 QCOMPARE(childStateChangedSpy.count(), 0);
432 QCOMPARE(group.state(), QAnimationGroup::Stopped);
433 QCOMPARE(anim.state(), QAnimationGroup::Stopped);
434
435 group.addAnimation(animation: &anim);
436
437 group.start();
438
439 QCOMPARE(group.state(), QAnimationGroup::Running);
440 QCOMPARE(anim.state(), QAnimationGroup::Running);
441
442 QCOMPARE(groupStateChangedSpy.count(), 1);
443 QCOMPARE(childStateChangedSpy.count(), 1);
444
445 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(0).first()),
446 QAnimationGroup::Running);
447 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(childStateChangedSpy.at(0).first()),
448 QAnimationGroup::Running);
449
450 // starting directly a running child will not have any effect
451 anim.start();
452
453 QCOMPARE(groupStateChangedSpy.count(), 1);
454 QCOMPARE(childStateChangedSpy.count(), 1);
455
456 anim.pause();
457
458 QCOMPARE(group.state(), QAnimationGroup::Running);
459 QCOMPARE(anim.state(), QAnimationGroup::Paused);
460
461 // in the animation stops directly, the group will still be running
462 anim.stop();
463
464 QCOMPARE(group.state(), QAnimationGroup::Running);
465 QCOMPARE(anim.state(), QAnimationGroup::Stopped);
466}
467
468void tst_QParallelAnimationGroup::deleteChildrenWithRunningGroup()
469{
470 // test if children can be activated when their group is stopped
471 QParallelAnimationGroup group;
472
473 QVariantAnimation *anim1 = new TestAnimation;
474 anim1->setStartValue(0);
475 anim1->setEndValue(100);
476 anim1->setDuration(200);
477 group.addAnimation(animation: anim1);
478
479 QCOMPARE(group.duration(), anim1->duration());
480
481 group.start();
482 QCOMPARE(group.state(), QAnimationGroup::Running);
483 QCOMPARE(anim1->state(), QAnimationGroup::Running);
484
485 QTest::qWait(ms: 80);
486 QVERIFY(group.currentLoopTime() > 0);
487
488 delete anim1;
489 QCOMPARE(group.animationCount(), 0);
490 QCOMPARE(group.duration(), 0);
491 QCOMPARE(group.state(), QAnimationGroup::Stopped);
492 QCOMPARE(group.currentLoopTime(), 0); //that's the invariant
493}
494
495void tst_QParallelAnimationGroup::startChildrenWithStoppedGroup()
496{
497 // test if children can be activated when their group is stopped
498 QParallelAnimationGroup group;
499
500 TestAnimation anim1;
501 anim1.setStartValue(0);
502 anim1.setEndValue(100);
503 anim1.setDuration(200);
504
505 TestAnimation anim2;
506 anim2.setStartValue(0);
507 anim2.setEndValue(100);
508 anim2.setDuration(200);
509
510 QCOMPARE(group.state(), QAnimationGroup::Stopped);
511 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
512 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
513
514 group.addAnimation(animation: &anim1);
515 group.addAnimation(animation: &anim2);
516
517 group.stop();
518
519 QCOMPARE(group.state(), QAnimationGroup::Stopped);
520 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
521 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
522
523 anim1.start();
524 anim2.start();
525 anim2.pause();
526
527 QCOMPARE(group.state(), QAnimationGroup::Stopped);
528 QCOMPARE(anim1.state(), QAnimationGroup::Running);
529 QCOMPARE(anim2.state(), QAnimationGroup::Paused);
530}
531
532void tst_QParallelAnimationGroup::stopGroupWithRunningChild()
533{
534 // children that started independently will not be affected by a group stop
535 QParallelAnimationGroup group;
536
537 TestAnimation anim1;
538 anim1.setStartValue(0);
539 anim1.setEndValue(100);
540 anim1.setDuration(200);
541
542 TestAnimation anim2;
543 anim2.setStartValue(0);
544 anim2.setEndValue(100);
545 anim2.setDuration(200);
546
547 QCOMPARE(group.state(), QAnimationGroup::Stopped);
548 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
549 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
550
551 group.addAnimation(animation: &anim1);
552 group.addAnimation(animation: &anim2);
553
554 anim1.start();
555 anim2.start();
556 anim2.pause();
557
558 QCOMPARE(group.state(), QAnimationGroup::Stopped);
559 QCOMPARE(anim1.state(), QAnimationGroup::Running);
560 QCOMPARE(anim2.state(), QAnimationGroup::Paused);
561
562 group.stop();
563
564 QCOMPARE(group.state(), QAnimationGroup::Stopped);
565 QCOMPARE(anim1.state(), QAnimationGroup::Running);
566 QCOMPARE(anim2.state(), QAnimationGroup::Paused);
567
568 anim1.stop();
569 anim2.stop();
570
571 QCOMPARE(group.state(), QAnimationGroup::Stopped);
572 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
573 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
574}
575
576void tst_QParallelAnimationGroup::startGroupWithRunningChild()
577{
578 // as the group has precedence over its children, starting a group will restart all the children
579 QParallelAnimationGroup group;
580
581 TestAnimation anim1;
582 anim1.setStartValue(0);
583 anim1.setEndValue(100);
584 anim1.setDuration(200);
585
586 TestAnimation anim2;
587 anim2.setStartValue(0);
588 anim2.setEndValue(100);
589 anim2.setDuration(200);
590
591 QSignalSpy stateChangedSpy1(&anim1, &TestAnimation::stateChanged);
592 QSignalSpy stateChangedSpy2(&anim2, &TestAnimation::stateChanged);
593
594 QVERIFY(stateChangedSpy1.isValid());
595 QVERIFY(stateChangedSpy2.isValid());
596
597 QCOMPARE(stateChangedSpy1.count(), 0);
598 QCOMPARE(stateChangedSpy2.count(), 0);
599 QCOMPARE(group.state(), QAnimationGroup::Stopped);
600 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
601 QCOMPARE(anim2.state(), QAnimationGroup::Stopped);
602
603 group.addAnimation(animation: &anim1);
604 group.addAnimation(animation: &anim2);
605
606 anim1.start();
607 anim2.start();
608 anim2.pause();
609
610 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy1.at(0).first()),
611 QAnimationGroup::Running);
612 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy2.at(0).first()),
613 QAnimationGroup::Running);
614 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy2.at(1).first()),
615 QAnimationGroup::Paused);
616
617 QCOMPARE(group.state(), QAnimationGroup::Stopped);
618 QCOMPARE(anim1.state(), QAnimationGroup::Running);
619 QCOMPARE(anim2.state(), QAnimationGroup::Paused);
620
621 group.start();
622
623 QCOMPARE(stateChangedSpy1.count(), 3);
624 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy1.at(1).first()),
625 QAnimationGroup::Stopped);
626 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy1.at(2).first()),
627 QAnimationGroup::Running);
628
629 QCOMPARE(stateChangedSpy2.count(), 4);
630 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy2.at(2).first()),
631 QAnimationGroup::Stopped);
632 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy2.at(3).first()),
633 QAnimationGroup::Running);
634
635 QCOMPARE(group.state(), QAnimationGroup::Running);
636 QCOMPARE(anim1.state(), QAnimationGroup::Running);
637 QCOMPARE(anim2.state(), QAnimationGroup::Running);
638}
639
640void tst_QParallelAnimationGroup::zeroDurationAnimation()
641{
642 QParallelAnimationGroup group;
643
644 TestAnimation anim1;
645 anim1.setStartValue(0);
646 anim1.setEndValue(100);
647 anim1.setDuration(0);
648
649 TestAnimation anim2;
650 anim2.setStartValue(0);
651 anim2.setEndValue(100);
652 anim2.setDuration(100);
653
654 TestAnimation anim3;
655 anim3.setStartValue(0);
656 anim3.setEndValue(100);
657 anim3.setDuration(10);
658
659 QSignalSpy stateChangedSpy1(&anim1, &TestAnimation::stateChanged);
660 QSignalSpy finishedSpy1(&anim1, &TestAnimation::finished);
661
662 QVERIFY(stateChangedSpy1.isValid());
663 QVERIFY(finishedSpy1.isValid());
664
665 QSignalSpy stateChangedSpy2(&anim2, &TestAnimation::stateChanged);
666 QSignalSpy finishedSpy2(&anim2, &TestAnimation::finished);
667
668 QVERIFY(stateChangedSpy2.isValid());
669 QVERIFY(finishedSpy2.isValid());
670
671 QSignalSpy stateChangedSpy3(&anim3, &TestAnimation::stateChanged);
672 QSignalSpy finishedSpy3(&anim3, &TestAnimation::finished);
673
674 QVERIFY(stateChangedSpy3.isValid());
675 QVERIFY(finishedSpy3.isValid());
676
677 group.addAnimation(animation: &anim1);
678 group.addAnimation(animation: &anim2);
679 group.addAnimation(animation: &anim3);
680 QCOMPARE(stateChangedSpy1.count(), 0);
681 group.start();
682 QCOMPARE(stateChangedSpy1.count(), 2);
683 QCOMPARE(finishedSpy1.count(), 1);
684 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy1.at(0).first()),
685 QAnimationGroup::Running);
686 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy1.at(1).first()),
687 QAnimationGroup::Stopped);
688
689 QCOMPARE(stateChangedSpy2.count(), 1);
690 QCOMPARE(finishedSpy2.count(), 0);
691 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy1.at(0).first()),
692 QAnimationGroup::Running);
693
694 QCOMPARE(stateChangedSpy3.count(), 1);
695 QCOMPARE(finishedSpy3.count(), 0);
696 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy3.at(0).first()),
697 QAnimationGroup::Running);
698
699
700 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
701 QCOMPARE(anim2.state(), QAnimationGroup::Running);
702 QCOMPARE(anim3.state(), QAnimationGroup::Running);
703 QCOMPARE(group.state(), QAnimationGroup::Running);
704
705
706 group.stop();
707 group.setLoopCount(4);
708 stateChangedSpy1.clear();
709 stateChangedSpy2.clear();
710 stateChangedSpy3.clear();
711
712 group.start();
713 QCOMPARE(stateChangedSpy1.count(), 2);
714 QCOMPARE(stateChangedSpy2.count(), 1);
715 QCOMPARE(stateChangedSpy3.count(), 1);
716 group.setCurrentTime(50);
717 QCOMPARE(stateChangedSpy1.count(), 2);
718 QCOMPARE(stateChangedSpy2.count(), 1);
719 QCOMPARE(stateChangedSpy3.count(), 2);
720 group.setCurrentTime(150);
721 QCOMPARE(stateChangedSpy1.count(), 4);
722 QCOMPARE(stateChangedSpy2.count(), 3);
723 QCOMPARE(stateChangedSpy3.count(), 4);
724 group.setCurrentTime(50);
725 QCOMPARE(stateChangedSpy1.count(), 6);
726 QCOMPARE(stateChangedSpy2.count(), 5);
727 QCOMPARE(stateChangedSpy3.count(), 6);
728
729}
730
731void tst_QParallelAnimationGroup::stopUncontrolledAnimations()
732{
733 QParallelAnimationGroup group;
734
735 TestAnimation anim1;
736 anim1.setStartValue(0);
737 anim1.setEndValue(100);
738 anim1.setDuration(0);
739
740 AnimationObject o1;
741 UncontrolledAnimation notTimeDriven(&o1, "value");
742 QCOMPARE(notTimeDriven.totalDuration(), -1);
743
744 TestAnimation loopsForever;
745 loopsForever.setStartValue(0);
746 loopsForever.setEndValue(100);
747 loopsForever.setDuration(100);
748 loopsForever.setLoopCount(-1);
749
750 QSignalSpy stateChangedSpy(&anim1, &TestAnimation::stateChanged);
751 QVERIFY(stateChangedSpy.isValid());
752
753 group.addAnimation(animation: &anim1);
754 group.addAnimation(animation: &notTimeDriven);
755 group.addAnimation(animation: &loopsForever);
756
757 group.start();
758
759 QCOMPARE(stateChangedSpy.count(), 2);
760 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy.at(0).first()),
761 QAnimationGroup::Running);
762 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(stateChangedSpy.at(1).first()),
763 QAnimationGroup::Stopped);
764
765 QCOMPARE(group.state(), QAnimationGroup::Running);
766 QCOMPARE(notTimeDriven.state(), QAnimationGroup::Running);
767 QCOMPARE(loopsForever.state(), QAnimationGroup::Running);
768 QCOMPARE(anim1.state(), QAnimationGroup::Stopped);
769
770 notTimeDriven.stop();
771
772 QCOMPARE(group.state(), QAnimationGroup::Running);
773 QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped);
774 QCOMPARE(loopsForever.state(), QAnimationGroup::Running);
775
776 loopsForever.stop();
777
778 QCOMPARE(group.state(), QAnimationGroup::Stopped);
779 QCOMPARE(notTimeDriven.state(), QAnimationGroup::Stopped);
780 QCOMPARE(loopsForever.state(), QAnimationGroup::Stopped);
781}
782
783struct AnimState {
784 AnimState(int time = -1) : time(time), state(-1) {}
785 AnimState(int time, int state) : time(time), state(state) {}
786 int time;
787 int state;
788};
789QT_BEGIN_NAMESPACE
790Q_DECLARE_TYPEINFO(AnimState, Q_MOVABLE_TYPE);
791QT_END_NAMESPACE
792
793#define Running QAbstractAnimation::Running
794#define Stopped QAbstractAnimation::Stopped
795
796Q_DECLARE_METATYPE(AnimState)
797void tst_QParallelAnimationGroup::loopCount_data()
798{
799 QTest::addColumn<bool>(name: "directionBackward");
800 QTest::addColumn<int>(name: "setLoopCount");
801 QTest::addColumn<int>(name: "initialGroupTime");
802 QTest::addColumn<int>(name: "currentGroupTime");
803 QTest::addColumn<AnimState>(name: "expected1");
804 QTest::addColumn<AnimState>(name: "expected2");
805 QTest::addColumn<AnimState>(name: "expected3");
806
807 // D U R A T I O N
808 // 100 60*2 0
809 // direction = Forward
810 QTest::newRow(dataTag: "50") << false << 3 << 0 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
811 QTest::newRow(dataTag: "100") << false << 3 << 0 << 100 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped);
812 QTest::newRow(dataTag: "110") << false << 3 << 0 << 110 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
813 QTest::newRow(dataTag: "120") << false << 3 << 0 << 120 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
814
815 QTest::newRow(dataTag: "170") << false << 3 << 0 << 170 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
816 QTest::newRow(dataTag: "220") << false << 3 << 0 << 220 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped);
817 QTest::newRow(dataTag: "230") << false << 3 << 0 << 230 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
818 QTest::newRow(dataTag: "240") << false << 3 << 0 << 240 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
819
820 QTest::newRow(dataTag: "290") << false << 3 << 0 << 290 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
821 QTest::newRow(dataTag: "340") << false << 3 << 0 << 340 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped);
822 QTest::newRow(dataTag: "350") << false << 3 << 0 << 350 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
823 QTest::newRow(dataTag: "360") << false << 3 << 0 << 360 << AnimState(100, Stopped) << AnimState( 60 ) << AnimState( 0, Stopped);
824
825 QTest::newRow(dataTag: "410") << false << 3 << 0 << 410 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
826 QTest::newRow(dataTag: "460") << false << 3 << 0 << 460 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
827 QTest::newRow(dataTag: "470") << false << 3 << 0 << 470 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
828 QTest::newRow(dataTag: "480") << false << 3 << 0 << 480 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
829
830 // direction = Forward, rewind
831 QTest::newRow(dataTag: "120-110") << false << 3 << 120 << 110 << AnimState( 0, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
832 QTest::newRow(dataTag: "120-50") << false << 3 << 120 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
833 QTest::newRow(dataTag: "120-0") << false << 3 << 120 << 0 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
834 QTest::newRow(dataTag: "300-110") << false << 3 << 300 << 110 << AnimState( 0, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
835 QTest::newRow(dataTag: "300-50") << false << 3 << 300 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
836 QTest::newRow(dataTag: "300-0") << false << 3 << 300 << 0 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
837 QTest::newRow(dataTag: "115-105") << false << 3 << 115 << 105 << AnimState( 42, Stopped) << AnimState( 45, Running) << AnimState( 0, Stopped);
838
839 // direction = Backward
840 QTest::newRow(dataTag: "b120-120") << true << 3 << 120 << 120 << AnimState( 42, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
841 QTest::newRow(dataTag: "b120-110") << true << 3 << 120 << 110 << AnimState( 42, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
842 QTest::newRow(dataTag: "b120-100") << true << 3 << 120 << 100 << AnimState(100, Running) << AnimState( 40, Running) << AnimState( 0, Stopped);
843 QTest::newRow(dataTag: "b120-50") << true << 3 << 120 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
844 QTest::newRow(dataTag: "b120-0") << true << 3 << 120 << 0 << AnimState( 0, Stopped) << AnimState( 0, Stopped) << AnimState( 0, Stopped);
845 QTest::newRow(dataTag: "b360-170") << true << 3 << 360 << 170 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
846 QTest::newRow(dataTag: "b360-220") << true << 3 << 360 << 220 << AnimState(100, Running) << AnimState( 40, Running) << AnimState( 0, Stopped);
847 QTest::newRow(dataTag: "b360-210") << true << 3 << 360 << 210 << AnimState( 90, Running) << AnimState( 30, Running) << AnimState( 0, Stopped);
848 QTest::newRow(dataTag: "b360-120") << true << 3 << 360 << 120 << AnimState( 0, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
849
850 // rewind, direction = Backward
851 QTest::newRow(dataTag: "b50-110") << true << 3 << 50 << 110 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
852 QTest::newRow(dataTag: "b50-120") << true << 3 << 50 << 120 << AnimState(100, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
853 QTest::newRow(dataTag: "b50-140") << true << 3 << 50 << 140 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
854 QTest::newRow(dataTag: "b50-240") << true << 3 << 50 << 240 << AnimState(100, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
855 QTest::newRow(dataTag: "b50-260") << true << 3 << 50 << 260 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
856 QTest::newRow(dataTag: "b50-350") << true << 3 << 50 << 350 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
857
858 // infinite looping
859 QTest::newRow(dataTag: "inf1220") << false << -1 << 0 << 1220 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
860 QTest::newRow(dataTag: "inf1310") << false << -1 << 0 << 1310 << AnimState( 100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
861 // infinite looping, direction = Backward (will only loop once)
862 QTest::newRow(dataTag: "b.inf120-120") << true << -1 << 120 << 120 << AnimState( 42, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
863 QTest::newRow(dataTag: "b.inf120-20") << true << -1 << 120 << 20 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
864 QTest::newRow(dataTag: "b.inf120-110") << true << -1 << 120 << 110 << AnimState( 42, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
865
866
867}
868
869void tst_QParallelAnimationGroup::loopCount()
870{
871 QFETCH(bool, directionBackward);
872 QFETCH(int, setLoopCount);
873 QFETCH(int, initialGroupTime);
874 QFETCH(int, currentGroupTime);
875 QFETCH(AnimState, expected1);
876 QFETCH(AnimState, expected2);
877 QFETCH(AnimState, expected3);
878
879 QParallelAnimationGroup group;
880
881 TestAnimation anim1;
882 anim1.setStartValue(0);
883 anim1.setEndValue(100);
884 anim1.setDuration(100);
885
886 TestAnimation anim2;
887 anim2.setStartValue(0);
888 anim2.setEndValue(100);
889 anim2.setDuration(60); //total 120
890 anim2.setLoopCount(2);
891
892 TestAnimation anim3;
893 anim3.setStartValue(0);
894 anim3.setEndValue(100);
895 anim3.setDuration(0);
896
897 group.addAnimation(animation: &anim1);
898 group.addAnimation(animation: &anim2);
899 group.addAnimation(animation: &anim3);
900
901 group.setLoopCount(setLoopCount);
902 if (initialGroupTime >= 0)
903 group.setCurrentTime(initialGroupTime);
904 if (directionBackward)
905 group.setDirection(QAbstractAnimation::Backward);
906
907 group.start();
908 if (initialGroupTime >= 0)
909 group.setCurrentTime(initialGroupTime);
910
911 anim1.setCurrentTime(42); // 42 is "untouched"
912 anim2.setCurrentTime(42);
913
914 group.setCurrentTime(currentGroupTime);
915
916 QCOMPARE(anim1.currentLoopTime(), expected1.time);
917 QCOMPARE(anim2.currentLoopTime(), expected2.time);
918 QCOMPARE(anim3.currentLoopTime(), expected3.time);
919
920 if (expected1.state >=0)
921 QCOMPARE(int(anim1.state()), expected1.state);
922 if (expected2.state >=0)
923 QCOMPARE(int(anim2.state()), expected2.state);
924 if (expected3.state >=0)
925 QCOMPARE(int(anim3.state()), expected3.state);
926
927}
928
929void tst_QParallelAnimationGroup::autoAdd()
930{
931 QParallelAnimationGroup group;
932 QCOMPARE(group.duration(), 0);
933 TestAnimation2 *test = new TestAnimation2(250, &group); // 0, duration = 250;
934 QCOMPARE(test->group(), static_cast<QAnimationGroup*>(&group));
935 QCOMPARE(test->duration(), 250);
936 QCOMPARE(group.duration(), 250);
937
938 test = new TestAnimation2(750, &group); // 1
939 QCOMPARE(test->group(), static_cast<QAnimationGroup*>(&group));
940 QCOMPARE(group.duration(), 750);
941 test = new TestAnimation2(500, &group); // 2
942 QCOMPARE(test->group(), static_cast<QAnimationGroup*>(&group));
943 QCOMPARE(group.duration(), 750);
944
945 delete group.animationAt(index: 1); // remove the one with duration = 750
946 QCOMPARE(group.duration(), 500);
947
948 delete group.animationAt(index: 1); // remove the one with duration = 500
949 QCOMPARE(group.duration(), 250);
950
951 test = static_cast<TestAnimation2*>(group.animationAt(index: 0));
952 test->setParent(0); // remove the last one (with duration = 250)
953 QCOMPARE(test->group(), static_cast<QAnimationGroup*>(0));
954 QCOMPARE(group.duration(), 0);
955}
956
957void tst_QParallelAnimationGroup::pauseResume()
958{
959 QParallelAnimationGroup group;
960 TestAnimation2 *anim = new TestAnimation2(250, &group); // 0, duration = 250;
961 QSignalSpy spy(anim, &TestAnimation::stateChanged);
962 QVERIFY(spy.isValid());
963 QCOMPARE(group.duration(), 250);
964 group.start();
965 QTest::qWait(ms: 100);
966 QCOMPARE(group.state(), QAnimationGroup::Running);
967 QCOMPARE(anim->state(), QAnimationGroup::Running);
968 QCOMPARE(spy.count(), 1);
969 spy.clear();
970 const int currentTime = group.currentLoopTime();
971 QCOMPARE(anim->currentLoopTime(), currentTime);
972
973 group.pause();
974 QCOMPARE(group.state(), QAnimationGroup::Paused);
975 QCOMPARE(group.currentLoopTime(), currentTime);
976 QCOMPARE(anim->state(), QAnimationGroup::Paused);
977 QCOMPARE(anim->currentLoopTime(), currentTime);
978 QCOMPARE(spy.count(), 1);
979 spy.clear();
980
981 group.resume();
982 QCOMPARE(group.state(), QAnimationGroup::Running);
983 QCOMPARE(group.currentLoopTime(), currentTime);
984 QCOMPARE(anim->state(), QAnimationGroup::Running);
985 QCOMPARE(anim->currentLoopTime(), currentTime);
986 QCOMPARE(spy.count(), 1);
987
988 group.stop();
989 spy.clear();
990 new TestAnimation2(500, &group);
991 group.start();
992 QCOMPARE(spy.count(), 1); //the animation should have been started
993 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy.last().first()), TestAnimation::Running);
994 group.setCurrentTime(250); //end of first animation
995 QCOMPARE(spy.count(), 2); //the animation should have been stopped
996 QCOMPARE(qvariant_cast<QAbstractAnimation::State>(spy.last().first()), TestAnimation::Stopped);
997 group.pause();
998 QCOMPARE(spy.count(), 2); //this shouldn't have changed
999 group.resume();
1000 QCOMPARE(spy.count(), 2); //this shouldn't have changed
1001}
1002
1003// This is a regression test for QTBUG-8910, where a crash occurred when the
1004// last animation was removed from a group.
1005void tst_QParallelAnimationGroup::crashWhenRemovingUncontrolledAnimation()
1006{
1007 QParallelAnimationGroup group;
1008 TestAnimation *anim = new TestAnimation;
1009 anim->setLoopCount(-1);
1010 TestAnimation *anim2 = new TestAnimation;
1011 anim2->setLoopCount(-1);
1012 group.addAnimation(animation: anim);
1013 group.addAnimation(animation: anim2);
1014 group.start();
1015 delete anim;
1016 // it would crash here because the internals of the group would still have a reference to anim
1017 delete anim2;
1018}
1019
1020
1021QTEST_MAIN(tst_QParallelAnimationGroup)
1022#include "tst_qparallelanimationgroup.moc"
1023

source code of qtbase/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp