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#include <QtTest/QtTest>
29#include <QtQml/qqmlengine.h>
30#include <QtQml/qqmlcomponent.h>
31#include <QtQuick/qquickview.h>
32#include <QtQml/private/qqmltimer_p.h>
33#include <QtQmlModels/private/qqmllistmodel_p.h>
34#include <QtQml/private/qanimationgroupjob_p.h>
35#include <QtQuick/private/qquickrectangle_p.h>
36#include <QtQuick/private/qquickitemanimation_p.h>
37#include <QtQuick/private/qquickitemanimation_p_p.h>
38#include <QtQuick/private/qquicktransition_p.h>
39#include <QtQuick/private/qquickanimation_p.h>
40#include <QtQuick/private/qquickanimatorjob_p.h>
41#include <QtQuick/private/qquickpathinterpolator_p.h>
42#include <QtQuick/private/qquickitem_p.h>
43#include <QtQuick/private/qquicklistview_p.h>
44#include <QEasingCurve>
45
46#include <limits.h>
47#include <math.h>
48
49#include "../../shared/util.h"
50
51class tst_qquickanimations : public QQmlDataTest
52{
53 Q_OBJECT
54public:
55 tst_qquickanimations() {}
56
57private slots:
58 void initTestCase()
59 {
60 QQmlEngine engine; // ensure types are registered
61 QQmlDataTest::initTestCase();
62 }
63
64 void simpleProperty();
65 void simpleNumber();
66 void simpleColor();
67 void simpleRotation();
68 void simplePath();
69 void simpleAnchor();
70 void reparent();
71 void pathInterpolator();
72 void pathInterpolatorBackwardJump();
73 void pathWithNoStart();
74 void alwaysRunToEnd();
75 void complete();
76 void resume();
77 void dotProperty();
78 void badTypes();
79 void badProperties();
80 void mixedTypes();
81 void properties();
82 void propertiesTransition();
83 void pathTransition();
84 void disabledTransition();
85 void invalidDuration();
86 void attached();
87 void propertyValueSourceDefaultStart();
88 void dontStart();
89 void easingProperties();
90 void rotation();
91 void startStopSignals();
92 void signalOrder_data();
93 void signalOrder();
94 void runningTrueBug();
95 void nonTransitionBug();
96 void registrationBug();
97 void doubleRegistrationBug();
98 void alwaysRunToEndRestartBug();
99 void transitionAssignmentBug();
100 void pauseBindingBug();
101 void pauseBug();
102 void loopingBug();
103 void anchorBug();
104 void pathAnimationInOutBackBug();
105 void scriptActionBug();
106 void groupAnimationNullChildBug();
107 void scriptActionCrash();
108 void animatorInvalidTargetCrash();
109 void defaultPropertyWarning();
110 void pathSvgAnimation();
111 void pathLineUnspecifiedXYBug();
112 void unsetAnimatorProxyJobWindow();
113 void finished();
114 void replacingTransitions();
115 void animationJobSelfDestruction();
116 void fastFlickingBug();
117 void opacityAnimationFromZero();
118 void alwaysRunToEndInSequentialAnimationBug();
119 void cleanupWhenRenderThreadStops();
120};
121
122#define QTIMED_COMPARE(lhs, rhs) do { \
123 for (int ii = 0; ii < 5; ++ii) { \
124 if (lhs == rhs) \
125 break; \
126 QTest::qWait(50); \
127 } \
128 QCOMPARE(lhs, rhs); \
129} while (false)
130
131void tst_qquickanimations::simpleProperty()
132{
133 QQuickRectangle rect;
134 QQuickPropertyAnimation animation;
135 QSignalSpy fromChangedSpy(&animation, &QQuickPropertyAnimation::fromChanged);
136 QSignalSpy toChangedSpy(&animation, &QQuickPropertyAnimation::toChanged);
137 animation.setTargetObject(&rect);
138 animation.setProperty("x");
139 animation.setTo(200);
140 QCOMPARE(animation.target(), &rect);
141 QCOMPARE(animation.property(), QLatin1String("x"));
142 QCOMPARE(animation.to().toReal(), 200.0);
143 QCOMPARE(fromChangedSpy.count(), 0);
144 QCOMPARE(toChangedSpy.count(), 1);
145 animation.start();
146 QVERIFY(animation.isRunning());
147 QTest::qWait(ms: animation.duration());
148 QTIMED_COMPARE(rect.x(), 200.0);
149
150 rect.setPosition(QPointF(0,0));
151 animation.start();
152 QVERIFY(animation.isRunning());
153 animation.pause();
154 QVERIFY(animation.isPaused());
155 animation.setCurrentTime(125);
156 QCOMPARE(animation.currentTime(), 125);
157 QCOMPARE(rect.x(),100.0);
158 animation.setFrom(100);
159 QCOMPARE(fromChangedSpy.count(), 1);
160 QCOMPARE(toChangedSpy.count(), 1);
161}
162
163void tst_qquickanimations::simpleNumber()
164{
165 QQuickRectangle rect;
166 QQuickNumberAnimation animation;
167 QSignalSpy fromChangedSpy(&animation, &QQuickNumberAnimation::fromChanged);
168 QSignalSpy toChangedSpy(&animation, &QQuickNumberAnimation::toChanged);
169 animation.setTargetObject(&rect);
170 animation.setProperty("x");
171 animation.setTo(200);
172 QCOMPARE(animation.target(), &rect);
173 QCOMPARE(animation.property(), QLatin1String("x"));
174 QCOMPARE(animation.to(), qreal(200));
175 QCOMPARE(fromChangedSpy.count(), 0);
176 QCOMPARE(toChangedSpy.count(), 1);
177 animation.start();
178 QVERIFY(animation.isRunning());
179 QTest::qWait(ms: animation.duration());
180 QTIMED_COMPARE(rect.x(), qreal(200));
181
182 rect.setX(0);
183 animation.start();
184 animation.pause();
185 QVERIFY(animation.isRunning());
186 QVERIFY(animation.isPaused());
187 animation.setCurrentTime(125);
188 QCOMPARE(animation.currentTime(), 125);
189 QCOMPARE(rect.x(), qreal(100));
190 animation.setFrom(100);
191 QCOMPARE(fromChangedSpy.count(), 1);
192 QCOMPARE(toChangedSpy.count(), 1);
193}
194
195void tst_qquickanimations::simpleColor()
196{
197 QQuickRectangle rect;
198 QQuickColorAnimation animation;
199 QSignalSpy fromChangedSpy(&animation, &QQuickColorAnimation::fromChanged);
200 QSignalSpy toChangedSpy(&animation, &QQuickColorAnimation::toChanged);
201 animation.setTargetObject(&rect);
202 animation.setProperty("color");
203 animation.setTo(QColor("red"));
204 QCOMPARE(animation.target(), &rect);
205 QCOMPARE(animation.property(), QLatin1String("color"));
206 QCOMPARE(animation.to(), QColor("red"));
207 QCOMPARE(fromChangedSpy.count(), 0);
208 QCOMPARE(toChangedSpy.count(), 1);
209 animation.start();
210 QVERIFY(animation.isRunning());
211 QTest::qWait(ms: animation.duration());
212 QTIMED_COMPARE(rect.color(), QColor("red"));
213
214 rect.setColor(QColor("blue"));
215 animation.start();
216 animation.pause();
217 QVERIFY(animation.isRunning());
218 QVERIFY(animation.isPaused());
219 animation.setCurrentTime(125);
220 QCOMPARE(animation.currentTime(), 125);
221 QCOMPARE(rect.color(), QColor::fromRgbF(0.498039, 0, 0.498039, 1));
222
223 rect.setColor(QColor("green"));
224 animation.setFrom(QColor("blue"));
225 QCOMPARE(animation.from(), QColor("blue"));
226 QCOMPARE(fromChangedSpy.count(), 1);
227 QCOMPARE(toChangedSpy.count(), 1);
228 animation.restart();
229 QCOMPARE(rect.color(), QColor("blue"));
230 QVERIFY(animation.isRunning());
231 animation.setCurrentTime(125);
232 QCOMPARE(rect.color(), QColor::fromRgbF(0.498039, 0, 0.498039, 1));
233}
234
235void tst_qquickanimations::simpleRotation()
236{
237 QQuickRectangle rect;
238 QQuickRotationAnimation animation;
239 QSignalSpy fromChangedSpy(&animation, &QQuickRotationAnimation::fromChanged);
240 QSignalSpy toChangedSpy(&animation, &QQuickRotationAnimation::toChanged);
241 animation.setTargetObject(&rect);
242 animation.setProperty("rotation");
243 animation.setTo(270);
244 QCOMPARE(animation.target(), &rect);
245 QCOMPARE(animation.property(), QLatin1String("rotation"));
246 QCOMPARE(animation.to(), qreal(270));
247 QCOMPARE(animation.direction(), QQuickRotationAnimation::Numerical);
248 QCOMPARE(fromChangedSpy.count(), 0);
249 QCOMPARE(toChangedSpy.count(), 1);
250 animation.start();
251 QVERIFY(animation.isRunning());
252 QTest::qWait(ms: animation.duration());
253 QTIMED_COMPARE(rect.rotation(), qreal(270));
254
255 rect.setRotation(0);
256 animation.start();
257 animation.pause();
258 QVERIFY(animation.isRunning());
259 QVERIFY(animation.isPaused());
260 animation.setCurrentTime(125);
261 QCOMPARE(animation.currentTime(), 125);
262 QCOMPARE(rect.rotation(), qreal(135));
263 animation.setFrom(90);
264 QCOMPARE(fromChangedSpy.count(), 1);
265 QCOMPARE(toChangedSpy.count(), 1);
266}
267
268void tst_qquickanimations::simplePath()
269{
270 {
271 QQmlEngine engine;
272 QQmlComponent c(&engine, testFileUrl(fileName: "pathAnimation.qml"));
273 QScopedPointer<QObject> obj(c.create());
274 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
275 QVERIFY(rect);
276
277 QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>();
278 QVERIFY(redRect);
279 QQuickPathAnimation *pathAnim = rect->findChild<QQuickPathAnimation*>();
280 QVERIFY(pathAnim);
281
282 QCOMPARE(pathAnim->duration(), 100);
283 QCOMPARE(pathAnim->target(), redRect);
284
285 pathAnim->start();
286 pathAnim->pause();
287
288 pathAnim->setCurrentTime(30);
289 QCOMPARE(redRect->x(), qreal(167));
290 QCOMPARE(redRect->y(), qreal(104));
291
292 pathAnim->setCurrentTime(100);
293 QCOMPARE(redRect->x(), qreal(300));
294 QCOMPARE(redRect->y(), qreal(300));
295
296 //verify animation runs to end
297 pathAnim->start();
298 QCOMPARE(redRect->x(), qreal(50));
299 QCOMPARE(redRect->y(), qreal(50));
300 QTRY_COMPARE(redRect->x(), qreal(300));
301 QCOMPARE(redRect->y(), qreal(300));
302
303 pathAnim->setOrientation(QQuickPathAnimation::RightFirst);
304 QCOMPARE(pathAnim->orientation(), QQuickPathAnimation::RightFirst);
305 pathAnim->start();
306 QTRY_VERIFY(redRect->rotation() != 0);
307 pathAnim->stop();
308 }
309
310 {
311 QQmlEngine engine;
312 QQmlComponent c(&engine, testFileUrl(fileName: "pathAnimation2.qml"));
313 QScopedPointer<QObject> obj(c.create());
314 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
315 QVERIFY(rect);
316
317 QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>();
318 QVERIFY(redRect);
319 QQuickPathAnimation *pathAnim = rect->findChild<QQuickPathAnimation*>();
320 QVERIFY(pathAnim);
321
322 QCOMPARE(pathAnim->orientation(), QQuickPathAnimation::RightFirst);
323 QCOMPARE(pathAnim->endRotation(), qreal(0));
324 QCOMPARE(pathAnim->orientationEntryDuration(), 10);
325 QCOMPARE(pathAnim->orientationExitDuration(), 10);
326
327 pathAnim->start();
328 pathAnim->pause();
329 QCOMPARE(redRect->x(), qreal(50));
330 QCOMPARE(redRect->y(), qreal(50));
331 QCOMPARE(redRect->rotation(), qreal(-360));
332
333 pathAnim->setCurrentTime(50);
334 QCOMPARE(redRect->x(), qreal(175));
335 QCOMPARE(redRect->y(), qreal(175));
336 QCOMPARE(redRect->rotation(), qreal(-315));
337
338 pathAnim->setCurrentTime(100);
339 QCOMPARE(redRect->x(), qreal(300));
340 QCOMPARE(redRect->y(), qreal(300));
341 QCOMPARE(redRect->rotation(), qreal(0));
342 }
343}
344
345void tst_qquickanimations::simpleAnchor()
346{
347 QQmlEngine engine;
348 QQmlComponent c(&engine, testFileUrl(fileName: "reanchor.qml"));
349 QScopedPointer<QObject> obj(c.create());
350 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
351 QVERIFY(rect);
352
353 QQuickRectangle *greenRect = rect->findChild<QQuickRectangle*>();
354 QVERIFY(greenRect);
355
356 QCOMPARE(rect->state(), QLatin1String("reanchored"));
357 QCOMPARE(greenRect->x(), qreal(10));
358 QCOMPARE(greenRect->y(), qreal(0));
359 QCOMPARE(greenRect->width(), qreal(190));
360 QCOMPARE(greenRect->height(), qreal(150));
361
362 rect->setState("");
363
364 //verify animation in progress
365 QTRY_VERIFY(greenRect->x() < 10 && greenRect->x() > 0);
366 QVERIFY(greenRect->y() > 0 && greenRect->y() < 10);
367 QVERIFY(greenRect->width() < 190 && greenRect->width() > 150);
368 QVERIFY(greenRect->height() > 150 && greenRect->height() < 190);
369
370 //verify end state ("")
371 QTRY_COMPARE(greenRect->x(), qreal(0));
372 QCOMPARE(greenRect->y(), qreal(10));
373 QCOMPARE(greenRect->width(), qreal(150));
374 QCOMPARE(greenRect->height(), qreal(190));
375
376 rect->setState("reanchored2");
377
378 //verify animation in progress
379 QTRY_VERIFY(greenRect->y() > 10 && greenRect->y() < 50);
380 QVERIFY(greenRect->height() > 125 && greenRect->height() < 190);
381 //NOTE: setting left/right anchors to undefined removes the anchors, but does not resize.
382 QCOMPARE(greenRect->x(), qreal(0));
383 QCOMPARE(greenRect->width(), qreal(150));
384
385 //verify end state ("reanchored2")
386 QTRY_COMPARE(greenRect->y(), qreal(50));
387 QCOMPARE(greenRect->height(), qreal(125));
388 QCOMPARE(greenRect->x(), qreal(0));
389 QCOMPARE(greenRect->width(), qreal(150));
390
391 rect->setState("reanchored");
392
393 //verify animation in progress
394 QTRY_VERIFY(greenRect->x() < 10 && greenRect->x() > 0);
395 QVERIFY(greenRect->y() > 0 && greenRect->y() < 50);
396 QVERIFY(greenRect->width() < 190 && greenRect->width() > 150);
397 QVERIFY(greenRect->height() > 125 && greenRect->height() < 150);
398
399 //verify end state ("reanchored")
400 QTRY_COMPARE(greenRect->x(), qreal(10));
401 QCOMPARE(greenRect->y(), qreal(0));
402 QCOMPARE(greenRect->width(), qreal(190));
403 QCOMPARE(greenRect->height(), qreal(150));
404
405 rect->setState("reanchored2");
406
407 //verify animation in progress
408 QTRY_VERIFY(greenRect->x() < 10 && greenRect->x() > 0);
409 QVERIFY(greenRect->y() > 0 && greenRect->y() < 50);
410 QVERIFY(greenRect->width() < 190 && greenRect->width() > 150);
411 QVERIFY(greenRect->height() > 125 && greenRect->height() < 150);
412
413 //verify end state ("reanchored2")
414 QTRY_COMPARE(greenRect->x(), qreal(0));
415 QCOMPARE(greenRect->y(), qreal(50));
416 QCOMPARE(greenRect->width(), qreal(150));
417 QCOMPARE(greenRect->height(), qreal(125));
418}
419
420void tst_qquickanimations::reparent()
421{
422 QQmlEngine engine;
423 QQmlComponent c(&engine, testFileUrl(fileName: "reparent.qml"));
424 QScopedPointer<QObject> obj(c.create());
425 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
426 QVERIFY(rect);
427
428 QQuickRectangle *target = rect->findChild<QQuickRectangle*>(aName: "target");
429 QVERIFY(target);
430
431 QCOMPARE(target->parentItem(), rect);
432 QCOMPARE(target->x(), qreal(0));
433 QCOMPARE(target->y(), qreal(0));
434 QCOMPARE(target->width(), qreal(50));
435 QCOMPARE(target->height(), qreal(50));
436 QCOMPARE(target->rotation(), qreal(0));
437 QCOMPARE(target->scale(), qreal(1));
438
439 rect->setState("state1");
440
441 QQuickRectangle *viaParent = rect->findChild<QQuickRectangle*>(aName: "viaParent");
442 QVERIFY(viaParent);
443
444 QQuickRectangle *newParent = rect->findChild<QQuickRectangle*>(aName: "newParent");
445 QVERIFY(newParent);
446
447 QTest::qWait(ms: 100);
448
449 //animation in progress
450 QTRY_COMPARE(target->parentItem(), viaParent);
451 QVERIFY(target->x() > -100 && target->x() < 50);
452 QVERIFY(target->y() > -100 && target->y() < 50);
453 QVERIFY(target->width() > 50 && target->width() < 100);
454 QCOMPARE(target->height(), qreal(50));
455 QCOMPARE(target->rotation(), qreal(-45));
456 QCOMPARE(target->scale(), qreal(.5));
457
458 //end state
459 QTRY_COMPARE(target->parentItem(), newParent);
460 QCOMPARE(target->x(), qreal(50));
461 QCOMPARE(target->y(), qreal(50));
462 QCOMPARE(target->width(), qreal(100));
463 QCOMPARE(target->height(), qreal(50));
464 QCOMPARE(target->rotation(), qreal(0));
465 QCOMPARE(target->scale(), qreal(1));
466}
467
468void tst_qquickanimations::pathInterpolator()
469{
470 QQmlEngine engine;
471 QQmlComponent c(&engine, testFileUrl(fileName: "pathInterpolator.qml"));
472 QScopedPointer<QObject> obj(c.create());
473 auto *interpolator = qobject_cast<QQuickPathInterpolator*>(object: obj.data());
474 QVERIFY(interpolator);
475
476 QCOMPARE(interpolator->progress(), qreal(0));
477 QCOMPARE(interpolator->x(), qreal(50));
478 QCOMPARE(interpolator->y(), qreal(50));
479 QCOMPARE(interpolator->angle(), qreal(0));
480
481 interpolator->setProgress(.5);
482 QCOMPARE(interpolator->progress(), qreal(.5));
483 QCOMPARE(interpolator->x(), qreal(175));
484 QCOMPARE(interpolator->y(), qreal(175));
485 QCOMPARE(interpolator->angle(), qreal(90));
486
487 interpolator->setProgress(1);
488 QCOMPARE(interpolator->progress(), qreal(1));
489 QCOMPARE(interpolator->x(), qreal(300));
490 QCOMPARE(interpolator->y(), qreal(300));
491 QCOMPARE(interpolator->angle(), qreal(0));
492
493 //for path interpulator the progress value must be [0,1] range.
494 interpolator->setProgress(1.1);
495 QCOMPARE(interpolator->progress(), qreal(1));
496
497 interpolator->setProgress(-0.000123);
498 QCOMPARE(interpolator->progress(), qreal(0));
499}
500
501void tst_qquickanimations::pathInterpolatorBackwardJump()
502{
503#ifdef Q_CC_MINGW
504 QSKIP("QTBUG-36290 - MinGW Animation tests are flaky.");
505#endif
506 {
507 QQmlEngine engine;
508 QQmlComponent c(&engine, testFileUrl(fileName: "pathInterpolatorBack.qml"));
509 QScopedPointer<QObject> obj(c.create());
510 auto *interpolator = qobject_cast<QQuickPathInterpolator*>(object: obj.data());
511 QVERIFY(interpolator);
512
513 QCOMPARE(interpolator->progress(), qreal(0));
514 QCOMPARE(interpolator->x(), qreal(50));
515 QCOMPARE(interpolator->y(), qreal(50));
516 QCOMPARE(interpolator->angle(), qreal(90));
517
518 interpolator->setProgress(.5);
519 QCOMPARE(interpolator->progress(), qreal(.5));
520 QCOMPARE(interpolator->x(), qreal(100));
521 QCOMPARE(interpolator->y(), qreal(75));
522 QCOMPARE(interpolator->angle(), qreal(270));
523
524 interpolator->setProgress(1);
525 QCOMPARE(interpolator->progress(), qreal(1));
526 QCOMPARE(interpolator->x(), qreal(200));
527 QCOMPARE(interpolator->y(), qreal(50));
528 QCOMPARE(interpolator->angle(), qreal(0));
529
530 //make sure we don't get caught in infinite loop here
531 interpolator->setProgress(0);
532 QCOMPARE(interpolator->progress(), qreal(0));
533 QCOMPARE(interpolator->x(), qreal(50));
534 QCOMPARE(interpolator->y(), qreal(50));
535 QCOMPARE(interpolator->angle(), qreal(90));
536 }
537
538 {
539 QQmlEngine engine;
540 QQmlComponent c(&engine, testFileUrl(fileName: "pathInterpolatorBack2.qml"));
541 QScopedPointer<QObject> obj(c.create());
542 auto *interpolator = qobject_cast<QQuickPathInterpolator*>(object: obj.data());
543 QVERIFY(interpolator);
544
545 QCOMPARE(interpolator->progress(), qreal(0));
546 QCOMPARE(interpolator->x(), qreal(200));
547 QCOMPARE(interpolator->y(), qreal(280));
548 QCOMPARE(interpolator->angle(), qreal(180));
549
550 interpolator->setProgress(1);
551 QCOMPARE(interpolator->progress(), qreal(1));
552 QCOMPARE(interpolator->x(), qreal(0));
553 QCOMPARE(interpolator->y(), qreal(80));
554 QCOMPARE(interpolator->angle(), qreal(180));
555
556 //make sure we don't get caught in infinite loop here
557 interpolator->setProgress(0);
558 QCOMPARE(interpolator->progress(), qreal(0));
559 QCOMPARE(interpolator->x(), qreal(200));
560 QCOMPARE(interpolator->y(), qreal(280));
561 QCOMPARE(interpolator->angle(), qreal(180));
562 }
563}
564
565void tst_qquickanimations::pathWithNoStart()
566{
567 QQmlEngine engine;
568 QQmlComponent c(&engine, testFileUrl(fileName: "pathAnimationNoStart.qml"));
569 QScopedPointer<QObject> obj(c.create());
570 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
571 QVERIFY(rect);
572
573 QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>();
574 QVERIFY(redRect);
575 QQuickPathAnimation *pathAnim = rect->findChild<QQuickPathAnimation*>();
576 QVERIFY(pathAnim);
577
578 pathAnim->start();
579 pathAnim->pause();
580 QCOMPARE(redRect->x(), qreal(50));
581 QCOMPARE(redRect->y(), qreal(50));
582
583 pathAnim->setCurrentTime(50);
584 QCOMPARE(redRect->x(), qreal(175));
585 QCOMPARE(redRect->y(), qreal(175));
586
587 pathAnim->setCurrentTime(100);
588 QCOMPARE(redRect->x(), qreal(300));
589 QCOMPARE(redRect->y(), qreal(300));
590
591 redRect->setX(100);
592 redRect->setY(100);
593 pathAnim->start();
594 QCOMPARE(redRect->x(), qreal(100));
595 QCOMPARE(redRect->y(), qreal(100));
596 QTRY_COMPARE(redRect->x(), qreal(300));
597 QCOMPARE(redRect->y(), qreal(300));
598}
599
600void tst_qquickanimations::alwaysRunToEnd()
601{
602 QQuickRectangle rect;
603 QQuickPropertyAnimation animation;
604 animation.setTargetObject(&rect);
605 animation.setProperty("x");
606 animation.setTo(200);
607 animation.setDuration(1000);
608 animation.setLoops(-1);
609 animation.setAlwaysRunToEnd(true);
610 QCOMPARE(animation.loops(), -1);
611 QVERIFY(animation.alwaysRunToEnd());
612
613 QElapsedTimer timer;
614 timer.start();
615 animation.start();
616
617 // Make sure the animation has started but is not finished, yet.
618 QTRY_VERIFY(rect.x() > qreal(0) && rect.x() != qreal(200));
619
620 animation.stop();
621
622 // Make sure it didn't just jump to the end and also didn't revert to the start.
623 QVERIFY(rect.x() > qreal(0) && rect.x() != qreal(200));
624
625 // Make sure it eventually reaches the end.
626 QTRY_COMPARE(rect.x(), qreal(200));
627
628 // This should have taken at least 1s but less than 2s
629 // (otherwise it has run the animation twice).
630 qint64 elapsed = timer.elapsed();
631 QVERIFY(elapsed >= 1000 && elapsed < 2000);
632}
633
634void tst_qquickanimations::complete()
635{
636 QQuickRectangle rect;
637 QQuickPropertyAnimation animation;
638 animation.setTargetObject(&rect);
639 animation.setProperty("x");
640 animation.setFrom(1);
641 animation.setTo(200);
642 animation.setDuration(500);
643 QCOMPARE(animation.from().toInt(), 1);
644 animation.start();
645 QTest::qWait(ms: 50);
646 animation.stop();
647 QVERIFY(rect.x() != qreal(200));
648 animation.start();
649 QTRY_VERIFY(animation.isRunning());
650 animation.complete();
651 QCOMPARE(rect.x(), qreal(200));
652}
653
654void tst_qquickanimations::resume()
655{
656 QQuickRectangle rect;
657 QQuickPropertyAnimation animation;
658 animation.setTargetObject(&rect);
659 animation.setProperty("x");
660 animation.setFrom(10);
661 animation.setTo(200);
662 animation.setDuration(1000);
663 QCOMPARE(animation.from().toInt(), 10);
664
665 animation.start();
666 QTest::qWait(ms: 400);
667 animation.pause();
668 qreal x = rect.x();
669 QVERIFY(x != qreal(200) && x != qreal(10));
670 QVERIFY(animation.isRunning());
671 QVERIFY(animation.isPaused());
672
673 animation.resume();
674 QVERIFY(animation.isRunning());
675 QVERIFY(!animation.isPaused());
676 QTest::qWait(ms: 400);
677 animation.stop();
678 QVERIFY(rect.x() > x);
679
680 animation.start();
681 QVERIFY(animation.isRunning());
682 animation.pause();
683 QVERIFY(animation.isPaused());
684 animation.resume();
685 QVERIFY(!animation.isPaused());
686
687 QSignalSpy spy(&animation, SIGNAL(pausedChanged(bool)));
688 animation.pause();
689 QCOMPARE(spy.count(), 1);
690 QVERIFY(animation.isPaused());
691 animation.stop();
692 QVERIFY(!animation.isPaused());
693 QCOMPARE(spy.count(), 2);
694
695 // Load QtQuick to ensure that QQuickPropertyAnimation is registered as PropertyAnimation
696 {
697 QQmlEngine engine;
698 QQmlComponent component(&engine);
699 component.setData("import QtQuick 2.0\nQtObject {}\n", baseUrl: QUrl());
700 }
701
702 QByteArray message = "<Unknown File>: QML PropertyAnimation: setPaused() cannot be used when animation isn't running.";
703 QTest::ignoreMessage(type: QtWarningMsg, message);
704 animation.pause();
705 QCOMPARE(spy.count(), 2);
706 QVERIFY(!animation.isPaused());
707 animation.resume();
708 QVERIFY(!animation.isPaused());
709 QVERIFY(!animation.isRunning());
710 QCOMPARE(spy.count(), 2);
711}
712
713void tst_qquickanimations::dotProperty()
714{
715 QQuickRectangle rect;
716 QQuickNumberAnimation animation;
717 animation.setTargetObject(&rect);
718 animation.setProperty("border.width");
719 animation.setTo(10);
720 animation.start();
721 QTest::qWait(ms: animation.duration()+50);
722 QTIMED_COMPARE(rect.border()->width(), 10.0);
723
724 rect.border()->setWidth(0);
725 animation.start();
726 animation.pause();
727 animation.setCurrentTime(125);
728 QCOMPARE(animation.currentTime(), 125);
729 QCOMPARE(rect.border()->width(), 5.0);
730}
731
732void tst_qquickanimations::badTypes()
733{
734 //don't crash
735 {
736 QScopedPointer<QQuickView> view(new QQuickView);
737 view->setSource(testFileUrl(fileName: "badtype1.qml"));
738 qApp->processEvents();
739 }
740
741 //make sure we get a compiler error
742 {
743 QQmlEngine engine;
744 QQmlComponent c(&engine, testFileUrl(fileName: "badtype2.qml"));
745 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
746 QScopedPointer<QObject> obj(c.create());
747 QVERIFY(obj.isNull());
748
749 QCOMPARE(c.errors().count(), 1);
750 QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: number expected"));
751 }
752
753 //make sure we get a compiler error
754 {
755 QQmlEngine engine;
756 QQmlComponent c(&engine, testFileUrl(fileName: "badtype3.qml"));
757 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
758 QScopedPointer<QObject> obj(c.create());
759 QVERIFY(obj.isNull());
760
761 QCOMPARE(c.errors().count(), 1);
762 QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: color expected"));
763 }
764
765 //don't crash
766 {
767 QQmlEngine engine;
768 QQmlComponent c(&engine, testFileUrl(fileName: "badtype4.qml"));
769 QScopedPointer<QObject> obj(c.create());
770 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
771 QVERIFY(rect);
772
773 QQuickItemPrivate::get(item: rect)->setState("state1");
774
775 QQuickRectangle *myRect = nullptr;
776 QTRY_VERIFY(myRect = rect->findChild<QQuickRectangle*>("MyRect"));
777 QTRY_COMPARE(myRect->x(),qreal(200));
778 }
779}
780
781void tst_qquickanimations::badProperties()
782{
783 //make sure we get a runtime error
784 {
785 QQmlEngine engine;
786
787 QQmlComponent c1(&engine, testFileUrl(fileName: "badproperty1.qml"));
788 QByteArray message = testFileUrl(fileName: "badproperty1.qml").toString().toUtf8() + ":18:9: QML ColorAnimation: Cannot animate non-existent property \"border.colr\"";
789 QTest::ignoreMessage(type: QtWarningMsg, message);
790 QScopedPointer<QObject> obj(c1.create());
791 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
792 QVERIFY(rect);
793
794 QQmlComponent c2(&engine, testFileUrl(fileName: "badproperty2.qml"));
795 message = testFileUrl(fileName: "badproperty2.qml").toString().toUtf8() + ":18:9: QML ColorAnimation: Cannot animate read-only property \"border\"";
796 QTest::ignoreMessage(type: QtWarningMsg, message);
797 QScopedPointer<QObject> obj2(c2.create());
798 rect = qobject_cast<QQuickRectangle*>(object: obj2.data());
799 QVERIFY(rect);
800
801 //### should we warn here are well?
802 //rect->setState("state1");
803 }
804}
805
806//test animating mixed types with property animation in a transition
807//for example, int + real; color + real; etc
808void tst_qquickanimations::mixedTypes()
809{
810 //assumes border.width stays a real -- not real robust
811 {
812 QQmlEngine engine;
813 QQmlComponent c(&engine, testFileUrl(fileName: "mixedtype1.qml"));
814 QScopedPointer<QObject> obj(c.create());
815 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
816 QVERIFY(rect);
817
818 QQuickItemPrivate::get(item: rect)->setState("state1");
819 QTest::qWait(ms: 500);
820 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "MyRect");
821 QVERIFY(myRect);
822
823 // We cannot get that more exact than that without dependable real-time behavior.
824 QVERIFY(myRect->x() > 100 && myRect->x() < 200);
825 QVERIFY(myRect->border()->width() > 1 && myRect->border()->width() < 10);
826 }
827
828 {
829 QQmlEngine engine;
830 QQmlComponent c(&engine, testFileUrl(fileName: "mixedtype2.qml"));
831 QScopedPointer<QObject> obj(c.create());
832 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
833 QVERIFY(rect);
834
835 QQuickItemPrivate::get(item: rect)->setState("state1");
836 QTest::qWait(ms: 500);
837 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "MyRect");
838 QVERIFY(myRect);
839
840 // We cannot get that more exact than that without dependable real-time behavior.
841 QVERIFY(myRect->x() > 100 && myRect->x() < 200);
842 QVERIFY(myRect->color() != QColor("red") && myRect->color() != QColor("blue"));
843 }
844}
845
846void tst_qquickanimations::properties()
847{
848 const int waitDuration = 300;
849 {
850 QQmlEngine engine;
851 QQmlComponent c(&engine, testFileUrl(fileName: "properties.qml"));
852 QScopedPointer<QObject> obj(c.create());
853 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
854 QVERIFY(rect);
855
856 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
857 QVERIFY(myRect);
858 QTest::qWait(ms: waitDuration);
859 QTIMED_COMPARE(myRect->x(),qreal(200));
860 }
861
862 {
863 QQmlEngine engine;
864 QQmlComponent c(&engine, testFileUrl(fileName: "properties2.qml"));
865 QScopedPointer<QObject> obj(c.create());
866 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
867 QVERIFY(rect);
868
869 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
870 QVERIFY(myRect);
871 QTest::qWait(ms: waitDuration);
872 QTIMED_COMPARE(myRect->x(),qreal(200));
873 }
874
875 {
876 QQmlEngine engine;
877 QQmlComponent c(&engine, testFileUrl(fileName: "properties3.qml"));
878 QScopedPointer<QObject> obj(c.create());
879 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
880 QVERIFY(rect);
881
882 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
883 QVERIFY(myRect);
884 QTest::qWait(ms: waitDuration);
885 QTIMED_COMPARE(myRect->x(),qreal(300));
886 }
887
888 {
889 QQmlEngine engine;
890 QQmlComponent c(&engine, testFileUrl(fileName: "properties4.qml"));
891 QScopedPointer<QObject> obj(c.create());
892 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
893 QVERIFY(rect);
894
895 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
896 QVERIFY(myRect);
897 QTest::qWait(ms: waitDuration);
898 QTIMED_COMPARE(myRect->y(),qreal(200));
899 QTIMED_COMPARE(myRect->x(),qreal(100));
900 }
901
902 {
903 QQmlEngine engine;
904 QQmlComponent c(&engine, testFileUrl(fileName: "properties5.qml"));
905 QScopedPointer<QObject> obj(c.create());
906 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
907 QVERIFY(rect);
908
909 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
910 QVERIFY(myRect);
911 QTest::qWait(ms: waitDuration);
912 QTIMED_COMPARE(myRect->x(),qreal(100));
913 QTIMED_COMPARE(myRect->y(),qreal(200));
914 }
915}
916
917void tst_qquickanimations::propertiesTransition()
918{
919 const int waitDuration = 300;
920 {
921 QQmlEngine engine;
922 QQmlComponent c(&engine, testFileUrl(fileName: "propertiesTransition.qml"));
923 QScopedPointer<QObject> obj(c.create());
924 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
925 QVERIFY(rect);
926
927 QQuickItemPrivate::get(item: rect)->setState("moved");
928 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
929 QVERIFY(myRect);
930 QTest::qWait(ms: waitDuration);
931 QTIMED_COMPARE(myRect->x(),qreal(200));
932 }
933
934 {
935 QQmlEngine engine;
936 QQmlComponent c(&engine, testFileUrl(fileName: "propertiesTransition2.qml"));
937 QScopedPointer<QObject> obj(c.create());
938 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
939 QVERIFY(rect);
940
941 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
942 QVERIFY(myRect);
943 QQuickItemPrivate::get(item: rect)->setState("moved");
944 QCOMPARE(myRect->x(),qreal(200));
945 QCOMPARE(myRect->y(),qreal(100));
946 QTest::qWait(ms: waitDuration);
947 QTIMED_COMPARE(myRect->y(),qreal(200));
948 }
949
950 {
951 QQmlEngine engine;
952 QQmlComponent c(&engine, testFileUrl(fileName: "propertiesTransition3.qml"));
953 QScopedPointer<QObject> obj(c.create());
954 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
955 QVERIFY(rect);
956
957 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
958 QVERIFY(myRect);
959 QQuickItemPrivate::get(item: rect)->setState("moved");
960 QCOMPARE(myRect->x(),qreal(200));
961 QCOMPARE(myRect->y(),qreal(100));
962 }
963
964 {
965 QQmlEngine engine;
966 QQmlComponent c(&engine, testFileUrl(fileName: "propertiesTransition4.qml"));
967 QScopedPointer<QObject> obj(c.create());
968 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
969 QVERIFY(rect);
970
971 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
972 QVERIFY(myRect);
973 QQuickItemPrivate::get(item: rect)->setState("moved");
974 QCOMPARE(myRect->x(),qreal(100));
975 QTest::qWait(ms: waitDuration);
976 QTIMED_COMPARE(myRect->x(),qreal(200));
977 }
978
979 {
980 QQmlEngine engine;
981 QQmlComponent c(&engine, testFileUrl(fileName: "propertiesTransition5.qml"));
982 QScopedPointer<QObject> obj(c.create());
983 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
984 QVERIFY(rect);
985
986 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
987 QVERIFY(myRect);
988 QQuickItemPrivate::get(item: rect)->setState("moved");
989 QCOMPARE(myRect->x(),qreal(100));
990 QTest::qWait(ms: waitDuration);
991 QTIMED_COMPARE(myRect->x(),qreal(200));
992 }
993
994 /*{
995 QQmlEngine engine;
996 QQmlComponent c(&engine, testFileUrl("propertiesTransition6.qml"));
997 QScopedPointer<QObject> obj(c.create());
998 auto *rect = qobject_cast<QQuickRectangle*>(obj.data());
999 QVERIFY(rect);
1000
1001 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>("TheRect");
1002 QVERIFY(myRect);
1003 QQuickItemPrivate::get(rect)->setState("moved");
1004 QCOMPARE(myRect->x(),qreal(100));
1005 QTest::qWait(waitDuration);
1006 QTIMED_COMPARE(myRect->x(),qreal(100));
1007 }*/
1008
1009 {
1010 QQmlEngine engine;
1011 QQmlComponent c(&engine, testFileUrl(fileName: "propertiesTransition7.qml"));
1012 QScopedPointer<QObject> obj(c.create());
1013 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1014 QVERIFY(rect);
1015
1016 QQuickItemPrivate::get(item: rect)->setState("moved");
1017 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
1018 QVERIFY(myRect);
1019 QTest::qWait(ms: waitDuration);
1020 QTIMED_COMPARE(myRect->x(),qreal(200));
1021 }
1022
1023}
1024
1025void tst_qquickanimations::pathTransition()
1026{
1027 QQmlEngine engine;
1028 QQmlComponent c(&engine, testFileUrl(fileName: "pathTransition.qml"));
1029 QScopedPointer<QObject> obj(c.create());
1030 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1031 QVERIFY(rect);
1032
1033 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "redRect");
1034 QVERIFY(myRect);
1035
1036 QQuickItemPrivate::get(item: rect)->setState("moved");
1037 QTRY_VERIFY(myRect->x() < 500 && myRect->x() > 100 && myRect->y() > 50 && myRect->y() < 700 ); //animation started
1038 QTRY_VERIFY(qFuzzyCompare(myRect->x(), qreal(100)) && qFuzzyCompare(myRect->y(), qreal(700)));
1039 QTest::qWait(ms: 100);
1040
1041 QQuickItemPrivate::get(item: rect)->setState("");
1042 QTRY_VERIFY(myRect->x() < 500 && myRect->x() > 100 && myRect->y() > 50 && myRect->y() < 700 ); //animation started
1043 QTRY_VERIFY(qFuzzyCompare(myRect->x(), qreal(500)) && qFuzzyCompare(myRect->y(), qreal(50)));
1044}
1045
1046void tst_qquickanimations::disabledTransition()
1047{
1048 QQmlEngine engine;
1049 QQmlComponent c(&engine, testFileUrl(fileName: "disabledTransition.qml"));
1050 QScopedPointer<QObject> obj(c.create());
1051 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1052 QVERIFY(rect);
1053
1054 QQuickRectangle *myRect = rect->findChild<QQuickRectangle*>(aName: "TheRect");
1055 QVERIFY(myRect);
1056
1057 QQuickTransition *trans = rect->findChild<QQuickTransition*>();
1058 QVERIFY(trans);
1059
1060 QCOMPARE(trans->enabled(), false);
1061
1062 QQuickItemPrivate::get(item: rect)->setState("moved");
1063 QCOMPARE(myRect->x(),qreal(200));
1064
1065 trans->setEnabled(true);
1066 QSignalSpy runningSpy(trans, SIGNAL(runningChanged()));
1067 QQuickItemPrivate::get(item: rect)->setState("");
1068 QCOMPARE(myRect->x(),qreal(200));
1069 QCOMPARE(runningSpy.count(), 1); //stopped -> running
1070 QVERIFY(trans->running());
1071 QTest::qWait(ms: 300);
1072 QTIMED_COMPARE(myRect->x(),qreal(100));
1073 QVERIFY(!trans->running());
1074 QCOMPARE(runningSpy.count(), 2); //running -> stopped
1075}
1076
1077void tst_qquickanimations::invalidDuration()
1078{
1079 QScopedPointer<QQuickPropertyAnimation> animation(new QQuickPropertyAnimation);
1080 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: QML PropertyAnimation: Cannot set a duration of < 0");
1081 animation->setDuration(-1);
1082 QCOMPARE(animation->duration(), 250);
1083
1084 QScopedPointer<QQuickPauseAnimation> pauseAnimation(new QQuickPauseAnimation);
1085 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: QML PauseAnimation: Cannot set a duration of < 0");
1086 pauseAnimation->setDuration(-1);
1087 QCOMPARE(pauseAnimation->duration(), 250);
1088}
1089
1090void tst_qquickanimations::attached()
1091{
1092 QQmlEngine engine;
1093
1094 QQmlComponent c(&engine, testFileUrl(fileName: "attached.qml"));
1095 QTest::ignoreMessage(type: QtDebugMsg, message: "off");
1096 QTest::ignoreMessage(type: QtDebugMsg, message: "on");
1097 QScopedPointer<QObject> obj(c.create());
1098 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1099 QVERIFY(rect);
1100}
1101
1102void tst_qquickanimations::propertyValueSourceDefaultStart()
1103{
1104 {
1105 QQmlEngine engine;
1106
1107 QQmlComponent c(&engine, testFileUrl(fileName: "valuesource.qml"));
1108
1109 QScopedPointer<QObject> obj(c.create());
1110 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1111 QVERIFY(rect);
1112
1113 QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>(aName: "MyAnim");
1114 QVERIFY(myAnim);
1115 QVERIFY(myAnim->isRunning());
1116 }
1117
1118 {
1119 QQmlEngine engine;
1120
1121 QQmlComponent c(&engine, testFileUrl(fileName: "valuesource2.qml"));
1122
1123 QScopedPointer<QObject> obj(c.create());
1124 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1125 QVERIFY(rect);
1126
1127 QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>(aName: "MyAnim");
1128 QVERIFY(myAnim);
1129 QVERIFY(!myAnim->isRunning());
1130 }
1131
1132 {
1133 QQmlEngine engine;
1134
1135 QQmlComponent c(&engine, testFileUrl(fileName: "dontAutoStart.qml"));
1136
1137 QScopedPointer<QObject> obj(c.create());
1138 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1139 QVERIFY(rect);
1140
1141 QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>(aName: "MyAnim");
1142 QVERIFY(myAnim && !myAnim->qtAnimation());
1143 //QCOMPARE(myAnim->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
1144 }
1145}
1146
1147
1148void tst_qquickanimations::dontStart()
1149{
1150 {
1151 QQmlEngine engine;
1152
1153 QQmlComponent c(&engine, testFileUrl(fileName: "dontStart.qml"));
1154
1155 QString warning = c.url().toString() + ":14:13: QML NumberAnimation: setRunning() cannot be used on non-root animation nodes.";
1156 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1157 QScopedPointer<QObject> obj(c.create());
1158 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1159 QVERIFY(rect);
1160
1161 QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>(aName: "MyAnim");
1162 QVERIFY(myAnim && !myAnim->qtAnimation());
1163 //QCOMPARE(myAnim->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
1164 }
1165
1166 {
1167 QQmlEngine engine;
1168
1169 QQmlComponent c(&engine, testFileUrl(fileName: "dontStart2.qml"));
1170
1171 QString warning = c.url().toString() + ":15:17: QML NumberAnimation: setRunning() cannot be used on non-root animation nodes.";
1172 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1173 QScopedPointer<QObject> obj(c.create());
1174 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1175 QVERIFY(rect);
1176
1177 QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>(aName: "MyAnim");
1178 QVERIFY(myAnim && !myAnim->qtAnimation());
1179 //QCOMPARE(myAnim->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
1180 }
1181}
1182
1183void tst_qquickanimations::easingProperties()
1184{
1185 {
1186 QQmlEngine engine;
1187 QString componentStr = "import QtQuick 2.0\nNumberAnimation { easing.type: \"InOutQuad\" }";
1188 QQmlComponent animationComponent(&engine);
1189 animationComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
1190 QScopedPointer<QObject> obj(animationComponent.create());
1191 auto *animObject = qobject_cast<QQuickPropertyAnimation *>(object: obj.data());
1192
1193 QVERIFY(animObject != nullptr);
1194 QCOMPARE(animObject->easing().type(), QEasingCurve::InOutQuad);
1195 }
1196
1197 {
1198 QQmlEngine engine;
1199 QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"OutBounce\"; easing.amplitude: 5.0 }";
1200 QQmlComponent animationComponent(&engine);
1201 animationComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
1202 QScopedPointer<QObject> obj(animationComponent.create());
1203 auto *animObject = qobject_cast<QQuickPropertyAnimation *>(object: obj.data());
1204
1205 QVERIFY(animObject != nullptr);
1206 QCOMPARE(animObject->easing().type(), QEasingCurve::OutBounce);
1207 QCOMPARE(animObject->easing().amplitude(), 5.0);
1208 }
1209
1210 {
1211 QQmlEngine engine;
1212 QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"OutElastic\"; easing.amplitude: 5.0; easing.period: 3.0}";
1213 QQmlComponent animationComponent(&engine);
1214 animationComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
1215 QScopedPointer<QObject> obj(animationComponent.create());
1216 auto *animObject = qobject_cast<QQuickPropertyAnimation *>(object: obj.data());
1217
1218 QVERIFY(animObject != nullptr);
1219 QCOMPARE(animObject->easing().type(), QEasingCurve::OutElastic);
1220 QCOMPARE(animObject->easing().amplitude(), 5.0);
1221 QCOMPARE(animObject->easing().period(), 3.0);
1222 }
1223
1224 {
1225 QQmlEngine engine;
1226 QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"InOutBack\"; easing.overshoot: 2 }";
1227 QQmlComponent animationComponent(&engine);
1228 animationComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
1229 QScopedPointer<QObject> obj(animationComponent.create());
1230 auto *animObject = qobject_cast<QQuickPropertyAnimation *>(object: obj.data());
1231
1232 QVERIFY(animObject != nullptr);
1233 QCOMPARE(animObject->easing().type(), QEasingCurve::InOutBack);
1234 QCOMPARE(animObject->easing().overshoot(), 2.0);
1235 }
1236
1237 {
1238 QQmlEngine engine;
1239 QString componentStr = "import QtQuick 2.0\nPropertyAnimation { easing.type: \"Bezier\"; easing.bezierCurve: [0.5, 0.2, 0.13, 0.65, 1.0, 1.0] }";
1240 QQmlComponent animationComponent(&engine);
1241 animationComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
1242 QScopedPointer<QObject> obj(animationComponent.create());
1243 auto *animObject = qobject_cast<QQuickPropertyAnimation *>(object: obj.data());
1244
1245 QVERIFY(animObject != nullptr);
1246 QCOMPARE(animObject->easing().type(), QEasingCurve::BezierSpline);
1247 QVector<QPointF> points = animObject->easing().toCubicSpline();
1248 QCOMPARE(points.count(), 3);
1249 QCOMPARE(points.at(0), QPointF(0.5, 0.2));
1250 QCOMPARE(points.at(1), QPointF(0.13, 0.65));
1251 QCOMPARE(points.at(2), QPointF(1.0, 1.0));
1252 }
1253}
1254
1255void tst_qquickanimations::rotation()
1256{
1257 QQmlEngine engine;
1258 QQmlComponent c(&engine, testFileUrl(fileName: "rotation.qml"));
1259 QScopedPointer<QObject> obj(c.create());
1260 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1261 QVERIFY(rect);
1262
1263 QQuickRectangle *rr = rect->findChild<QQuickRectangle*>(aName: "rr");
1264 QQuickRectangle *rr2 = rect->findChild<QQuickRectangle*>(aName: "rr2");
1265 QQuickRectangle *rr3 = rect->findChild<QQuickRectangle*>(aName: "rr3");
1266 QQuickRectangle *rr4 = rect->findChild<QQuickRectangle*>(aName: "rr4");
1267
1268 QQuickItemPrivate::get(item: rect)->setState("state1");
1269 QTest::qWait(ms: 800);
1270 qreal r1 = rr->rotation();
1271 qreal r2 = rr2->rotation();
1272 qreal r3 = rr3->rotation();
1273 qreal r4 = rr4->rotation();
1274
1275 QVERIFY(r1 > qreal(0) && r1 < qreal(370));
1276 QVERIFY(r2 > qreal(0) && r2 < qreal(370));
1277 QVERIFY(r3 < qreal(0) && r3 > qreal(-350));
1278 QVERIFY(r4 > qreal(0) && r4 < qreal(10));
1279 QCOMPARE(r1,r2);
1280 QVERIFY(r4 < r2);
1281
1282 QTest::qWait(ms: 800);
1283 QTIMED_COMPARE(rr->rotation() + rr2->rotation() + rr3->rotation() + rr4->rotation(), qreal(370*4));
1284}
1285
1286void tst_qquickanimations::startStopSignals()
1287{
1288 QQmlEngine engine;
1289 QQmlComponent c(&engine, testFileUrl(fileName: "signals.qml"));
1290 QScopedPointer<QObject> obj(c.create());
1291 auto *root = qobject_cast<QQuickItem *>(object: obj.data());
1292 QVERIFY(root);
1293
1294 QCOMPARE(root->property("startedCount").toInt(), 1); //autostart
1295 QCOMPARE(root->property("stoppedCount").toInt(), 0);
1296
1297 QMetaObject::invokeMethod(obj: root, member: "stop");
1298
1299 QCOMPARE(root->property("startedCount").toInt(), 1);
1300 QCOMPARE(root->property("stoppedCount").toInt(), 1);
1301
1302 QElapsedTimer timer;
1303 timer.start();
1304 QMetaObject::invokeMethod(obj: root, member: "start");
1305
1306 QCOMPARE(root->property("startedCount").toInt(), 2);
1307 QCOMPARE(root->property("stoppedCount").toInt(), 1);
1308
1309 QTRY_COMPARE(root->property("stoppedCount").toInt(), 2);
1310 QCOMPARE(root->property("startedCount").toInt(), 2);
1311 QVERIFY(timer.elapsed() >= 200);
1312
1313 root->setProperty(name: "alwaysRunToEnd", value: true);
1314
1315 timer.restart();
1316 QMetaObject::invokeMethod(obj: root, member: "start");
1317
1318 QCOMPARE(root->property("startedCount").toInt(), 3);
1319 QCOMPARE(root->property("stoppedCount").toInt(), 2);
1320
1321 QMetaObject::invokeMethod(obj: root, member: "stop");
1322
1323 QCOMPARE(root->property("startedCount").toInt(), 3);
1324 QCOMPARE(root->property("stoppedCount").toInt(), 2);
1325
1326 QTRY_COMPARE(root->property("stoppedCount").toInt(), 3);
1327 QCOMPARE(root->property("startedCount").toInt(), 3);
1328 QVERIFY(timer.elapsed() >= 200);
1329}
1330
1331void tst_qquickanimations::signalOrder_data()
1332{
1333 QTest::addColumn<QByteArray>(name: "animationType");
1334 QTest::addColumn<int>(name: "duration");
1335
1336 QTest::addRow(format: "ColorAnimation, duration = 10") << QByteArray("ColorAnimation") << 10;
1337 QTest::addRow(format: "ColorAnimation, duration = 0") << QByteArray("ColorAnimation") << 0;
1338 QTest::addRow(format: "ParallelAnimation, duration = 0") << QByteArray("ParallelAnimation") << 0;
1339}
1340
1341void tst_qquickanimations::signalOrder()
1342{
1343 QFETCH(QByteArray, animationType);
1344 QFETCH(int, duration);
1345
1346 QQmlEngine engine;
1347 QQmlComponent c(&engine, testFileUrl(fileName: "signalorder.qml"));
1348 QScopedPointer<QObject> obj(c.create());
1349 auto *root = qobject_cast<QQuickItem *>(object: obj.data());
1350 QVERIFY(root);
1351 QQuickAbstractAnimation *animation = root->findChild<QQuickAbstractAnimation*>(aName: animationType);
1352
1353 const QVector<void (QQuickAbstractAnimation::*)()> signalsToConnect = {
1354 &QQuickAbstractAnimation::started,
1355 &QQuickAbstractAnimation::stopped,
1356 &QQuickAbstractAnimation::finished
1357 };
1358 const QVector<const char*> expectedSignalOrder = {
1359 "started",
1360 "stopped",
1361 "finished"
1362 };
1363
1364 QVector<const char*> actualSignalOrder;
1365
1366 for (int i = 0; i < signalsToConnect.size(); ++i) {
1367 const char *str = expectedSignalOrder.at(i);
1368 connect(sender: animation, signal: signalsToConnect.at(i) , slot: [str, &actualSignalOrder] () {
1369 actualSignalOrder.append(t: str);
1370 });
1371 }
1372 QSignalSpy finishedSpy(animation, SIGNAL(finished()));
1373 if (QQuickColorAnimation *colorAnimation = qobject_cast<QQuickColorAnimation*>(object: animation))
1374 colorAnimation->setDuration(duration);
1375
1376 animation->start();
1377 QTRY_VERIFY(finishedSpy.count());
1378 QCOMPARE(actualSignalOrder, expectedSignalOrder);
1379}
1380
1381void tst_qquickanimations::runningTrueBug()
1382{
1383 //ensure we start correctly when "running: true" is explicitly set
1384 QQmlEngine engine;
1385 QQmlComponent c(&engine, testFileUrl(fileName: "runningTrueBug.qml"));
1386 QScopedPointer<QObject> obj(c.create());
1387 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1388 QVERIFY(rect);
1389
1390 QQuickRectangle *cloud = rect->findChild<QQuickRectangle*>(aName: "cloud");
1391 QVERIFY(cloud);
1392 QTest::qWait(ms: 1000);
1393 QVERIFY(cloud->x() > qreal(0));
1394}
1395
1396//QTBUG-24308
1397void tst_qquickanimations::pathAnimationInOutBackBug()
1398{
1399 //ensure we don't pass bad progress value (out of [0,1]) to QQuickPath::backwardsPointAt()
1400 QQmlEngine engine;
1401 QQmlComponent c(&engine, testFileUrl(fileName: "pathAnimationInOutBackCrash.qml"));
1402 QScopedPointer<QObject> obj(c.create());
1403 auto *item = qobject_cast<QQuickItem *>(object: obj.data());
1404 QVERIFY(item);
1405
1406 QQuickRectangle *rect = item->findChild<QQuickRectangle *>(aName: "rect");
1407 QVERIFY(rect);
1408 QTest::qWait(ms: 1000);
1409 QCOMPARE(rect->x(), qreal(0));
1410 QCOMPARE(rect->y(), qreal(0));
1411}
1412
1413//QTBUG-12805
1414void tst_qquickanimations::nonTransitionBug()
1415{
1416 //tests that the animation values from the previous transition are properly cleared
1417 //in the case where an animation in the transition doesn't match anything (but previously did)
1418 QQmlEngine engine;
1419
1420 QQmlComponent c(&engine, testFileUrl(fileName: "nonTransitionBug.qml"));
1421 QScopedPointer<QObject> obj(c.create());
1422 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1423 QVERIFY(rect != nullptr);
1424 QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(item: rect);
1425 QQuickRectangle *mover = rect->findChild<QQuickRectangle*>(aName: "mover");
1426
1427 mover->setX(100);
1428 QCOMPARE(mover->x(), qreal(100));
1429
1430 rectPrivate->setState("left");
1431 QTRY_COMPARE(mover->x(), qreal(0));
1432
1433 mover->setX(100);
1434 QCOMPARE(mover->x(), qreal(100));
1435
1436 //make sure we don't try to animate back to 0
1437 rectPrivate->setState("free");
1438 QTest::qWait(ms: 300);
1439 QCOMPARE(mover->x(), qreal(100));
1440}
1441
1442//QTBUG-14042
1443void tst_qquickanimations::registrationBug()
1444{
1445 QQmlEngine engine;
1446
1447 QQmlComponent c(&engine, testFileUrl(fileName: "registrationBug.qml"));
1448 QScopedPointer<QObject> obj(c.create());
1449 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1450 QVERIFY(rect != nullptr);
1451 QTRY_COMPARE(rect->property("value"), QVariant(int(100)));
1452}
1453
1454void tst_qquickanimations::doubleRegistrationBug()
1455{
1456 QQmlEngine engine;
1457
1458 QQmlComponent c(&engine, testFileUrl(fileName: "doubleRegistrationBug.qml"));
1459 QScopedPointer<QObject> obj(c.create());
1460 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1461 QVERIFY(rect != nullptr);
1462
1463 QQuickAbstractAnimation *anim = rect->findChild<QQuickAbstractAnimation*>(aName: "animation");
1464 QVERIFY(anim != nullptr);
1465 QTRY_COMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
1466}
1467
1468//QTBUG-16736
1469void tst_qquickanimations::alwaysRunToEndRestartBug()
1470{
1471 QQuickRectangle rect;
1472 QQuickPropertyAnimation animation;
1473 animation.setTargetObject(&rect);
1474 animation.setProperty("x");
1475 animation.setTo(200);
1476 animation.setDuration(1000);
1477 animation.setLoops(-1);
1478 animation.setAlwaysRunToEnd(true);
1479 QCOMPARE(animation.loops(), -1);
1480 QVERIFY(animation.alwaysRunToEnd());
1481 animation.start();
1482 animation.stop();
1483 animation.start();
1484 animation.stop();
1485
1486 // Waiting for a fixed time here would be dangerous as the starting and stopping itself takes
1487 // time and clocks are unreliable. The only thing we do know is that the animation should
1488 // eventually start and eventually stop. As its duration is 1000ms we can be pretty sure to hit
1489 // an in between state with the 50ms iterations QTRY_VERIFY does.
1490 QTRY_VERIFY(rect.x() != qreal(200));
1491 QTRY_COMPARE(rect.x(), qreal(200));
1492 QCOMPARE(static_cast<QQuickAbstractAnimation*>(&animation)->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
1493}
1494
1495//QTBUG-20227
1496void tst_qquickanimations::transitionAssignmentBug()
1497{
1498 QQmlEngine engine;
1499
1500 QQmlComponent c(&engine, testFileUrl(fileName: "transitionAssignmentBug.qml"));
1501 QScopedPointer<QObject> obj(c.create());
1502 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1503 QVERIFY(rect != nullptr);
1504
1505 QCOMPARE(rect->property("nullObject").toBool(), false);
1506}
1507
1508//QTBUG-19080
1509void tst_qquickanimations::pauseBindingBug()
1510{
1511 QQmlEngine engine;
1512
1513 QQmlComponent c(&engine, testFileUrl(fileName: "pauseBindingBug.qml"));
1514 QScopedPointer<QObject> obj(c.create());
1515 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1516 QVERIFY(rect != nullptr);
1517 QQuickAbstractAnimation *anim = rect->findChild<QQuickAbstractAnimation*>(aName: "animation");
1518 QCOMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Paused);
1519}
1520
1521//QTBUG-13598
1522void tst_qquickanimations::pauseBug()
1523{
1524 QQmlEngine engine;
1525
1526 QQmlComponent c(&engine, testFileUrl(fileName: "pauseBug.qml"));
1527 QScopedPointer<QObject> obj(c.create());
1528 auto *anim = qobject_cast<QQuickAbstractAnimation*>(object: obj.data());
1529 QVERIFY(anim != nullptr);
1530 QCOMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Paused);
1531 QCOMPARE(anim->isPaused(), true);
1532 QCOMPARE(anim->isRunning(), true);
1533}
1534
1535//QTBUG-23092
1536void tst_qquickanimations::loopingBug()
1537{
1538 QQmlEngine engine;
1539
1540 QQmlComponent c(&engine, testFileUrl(fileName: "looping.qml"));
1541 QScopedPointer<QObject> obj(c.create());
1542
1543 QQuickAbstractAnimation *anim = obj->findChild<QQuickAbstractAnimation*>();
1544 QVERIFY(anim != nullptr);
1545 QCOMPARE(anim->qtAnimation()->totalDuration(), 300);
1546 QCOMPARE(anim->isRunning(), true);
1547 QTRY_COMPARE(static_cast<QAnimationGroupJob*>(anim->qtAnimation())->firstChild()->currentLoop(), 2);
1548 QTRY_COMPARE(anim->isRunning(), false);
1549
1550 QQuickRectangle *rect = obj->findChild<QQuickRectangle*>();
1551 QVERIFY(rect != nullptr);
1552 QCOMPARE(rect->rotation(), qreal(90));
1553}
1554
1555//QTBUG-24532
1556void tst_qquickanimations::anchorBug()
1557{
1558 QQuickAnchorAnimation animation;
1559 animation.setDuration(5000);
1560 animation.setEasing(QEasingCurve(QEasingCurve::InOutBack));
1561 animation.start();
1562 animation.pause();
1563
1564 QCOMPARE(animation.qtAnimation()->duration(), 5000);
1565 QCOMPARE(static_cast<QQuickBulkValueAnimator*>(animation.qtAnimation())->easingCurve(), QEasingCurve(QEasingCurve::InOutBack));
1566}
1567
1568//ScriptAction should not match a StateChangeScript if no scriptName has been specified
1569void tst_qquickanimations::scriptActionBug()
1570{
1571 QQmlEngine engine;
1572 QQmlComponent c(&engine, testFileUrl(fileName: "scriptActionBug.qml"));
1573 QScopedPointer<QObject> obj(c.create());
1574
1575 //Both the ScriptAction and StateChangeScript should be triggered
1576 QCOMPARE(obj->property("actionTriggered").toBool(), true);
1577 QCOMPARE(obj->property("actionTriggered").toBool(), true);
1578}
1579
1580//QTBUG-34851
1581void tst_qquickanimations::groupAnimationNullChildBug()
1582{
1583 {
1584 QQmlEngine engine;
1585
1586 QQmlComponent c(&engine, testFileUrl(fileName: "sequentialAnimationNullChildBug.qml"));
1587 QScopedPointer<QObject> root(c.create());
1588 QVERIFY(root);
1589 }
1590
1591 {
1592 QQmlEngine engine;
1593
1594 QQmlComponent c(&engine, testFileUrl(fileName: "parallelAnimationNullChildBug.qml"));
1595 QScopedPointer<QObject> root(c.create());
1596 QVERIFY(root);
1597 }
1598}
1599
1600//ScriptAction should not crash if changing a state in a transition
1601void tst_qquickanimations::scriptActionCrash()
1602{
1603 QQmlEngine engine;
1604 QQmlComponent c(&engine, testFileUrl(fileName: "scriptActionCrash.qml"));
1605 QScopedPointer<QObject> obj(c.create());
1606
1607 //just testing that we don't crash
1608 QTest::qWait(ms: 1000); //5x transition duration
1609}
1610
1611// QTBUG-49364
1612// Test that we don't crash when the target of an Animator becomes
1613// invalid between the time the animator is started and the time the
1614// animator job is actually started
1615void tst_qquickanimations::animatorInvalidTargetCrash()
1616{
1617 QQmlEngine engine;
1618 QQmlComponent c(&engine, testFileUrl(fileName: "animatorInvalidTargetCrash.qml"));
1619 QScopedPointer<QObject> obj(c.create());
1620
1621 //just testing that we don't crash
1622 QTest::qWait(ms: 5000); //animator duration
1623}
1624
1625Q_DECLARE_METATYPE(QList<QQmlError>)
1626
1627// QTBUG-22141
1628void tst_qquickanimations::defaultPropertyWarning()
1629{
1630 QQmlEngine engine;
1631
1632 qRegisterMetaType<QList<QQmlError> >();
1633
1634 QSignalSpy warnings(&engine, SIGNAL(warnings(QList<QQmlError>)));
1635 QVERIFY(warnings.isValid());
1636
1637 QQmlComponent component(&engine, testFileUrl(fileName: "defaultRotationAnimation.qml"));
1638 QScopedPointer<QObject> obj(component.create());
1639 auto *root = qobject_cast<QQuickItem *>(object: obj.data());
1640 QVERIFY(root);
1641
1642 QVERIFY(warnings.isEmpty());
1643}
1644
1645// QTBUG-57666
1646void tst_qquickanimations::pathSvgAnimation()
1647{
1648 QQmlEngine engine;
1649 QQmlComponent component(&engine, testFileUrl(fileName: "pathSvgAnimation.qml"));
1650 QScopedPointer<QObject> obj(component.create());
1651 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1652 QVERIFY(rect);
1653
1654 QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>();
1655 QVERIFY(redRect);
1656 QQuickPathAnimation *pathAnim = rect->findChild<QQuickPathAnimation*>();
1657 QVERIFY(pathAnim);
1658
1659 QCOMPARE(redRect->x(), qreal(50));
1660 QCOMPARE(redRect->y(), qreal(50));
1661
1662 pathAnim->start();
1663 QTRY_COMPARE(redRect->x(), qreal(200));
1664 QCOMPARE(redRect->y(), qreal(200));
1665}
1666
1667// QTBUG-57666
1668void tst_qquickanimations::pathLineUnspecifiedXYBug()
1669{
1670 QQmlEngine engine;
1671 QQmlComponent component(&engine, testFileUrl(fileName: "pathLineUnspecifiedXYBug.qml"));
1672 QScopedPointer<QObject> obj(component.create());
1673 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1674 QVERIFY(rect);
1675
1676 QQuickRectangle *redRect = rect->findChild<QQuickRectangle*>();
1677 QVERIFY(redRect);
1678 QQuickPathAnimation *pathAnim = rect->findChild<QQuickPathAnimation*>();
1679 QVERIFY(pathAnim);
1680
1681 QCOMPARE(redRect->x(), qreal(50));
1682 QCOMPARE(redRect->y(), qreal(50));
1683
1684 pathAnim->start();
1685 QTRY_COMPARE(redRect->x(), qreal(0));
1686 QCOMPARE(redRect->y(), qreal(0));
1687}
1688
1689void tst_qquickanimations::unsetAnimatorProxyJobWindow()
1690{
1691 QQuickWindow window;
1692 QQuickItem item(window.contentItem());
1693 QQuickAbstractAnimation animation(&item);
1694 QAbstractAnimationJob *job = new QAbstractAnimationJob;
1695 QQuickAnimatorProxyJob proxy(job, &animation);
1696 QQuickItem dummy;
1697 item.setParentItem(&dummy);
1698 QSignalSpy spy(&window, SIGNAL(sceneGraphInitialized()));
1699 window.show();
1700 if (spy.count() < 1)
1701 spy.wait();
1702 QCOMPARE(proxy.job().data(), job);
1703}
1704
1705void tst_qquickanimations::finished()
1706{
1707 QQmlEngine engine;
1708 QQmlComponent component(&engine, testFileUrl(fileName: "finished.qml"));
1709 QScopedPointer<QObject> obj(component.create());
1710 auto *root = qobject_cast<QQuickItem *>(object: obj.data());
1711 QVERIFY(root);
1712
1713 // Test that finished() is emitted for a simple top-level animation.
1714 // (Each test is in its own block so that we can reuse the nice signal names :))
1715 {
1716 QQuickAbstractAnimation *simpleTopLevelAnimation
1717 = root->property(name: "simpleTopLevelAnimation").value<QQuickAbstractAnimation*>();
1718 QVERIFY(simpleTopLevelAnimation);
1719
1720 QSignalSpy stoppedSpy(simpleTopLevelAnimation, SIGNAL(stopped()));
1721 QVERIFY(stoppedSpy.isValid());
1722
1723 QSignalSpy finishedSpy(simpleTopLevelAnimation, SIGNAL(finished()));
1724 QVERIFY(finishedSpy.isValid());
1725
1726 QVERIFY(simpleTopLevelAnimation->setProperty("running", QVariant(true)));
1727 QTRY_COMPARE(stoppedSpy.count(), 1);
1728 QCOMPARE(finishedSpy.count(), 1);
1729
1730 // Test that the signal is properly revisioned and hence accessible from QML.
1731 QCOMPARE(root->property("finishedUsableInQml").toBool(), true);
1732 }
1733
1734 // Test that finished() is not emitted for animations within a Transition.
1735 {
1736 QObject *transition = root->property(name: "transition").value<QObject*>();
1737 QVERIFY(transition);
1738
1739 QSignalSpy runningChangedSpy(transition, SIGNAL(runningChanged()));
1740 QVERIFY(runningChangedSpy.isValid());
1741
1742 QQuickAbstractAnimation *animationWithinTransition
1743 = root->property(name: "animationWithinTransition").value<QQuickAbstractAnimation*>();
1744 QVERIFY(animationWithinTransition);
1745
1746 QSignalSpy stoppedSpy(animationWithinTransition, SIGNAL(stopped()));
1747 QVERIFY(stoppedSpy.isValid());
1748
1749 QSignalSpy finishedSpy(animationWithinTransition, SIGNAL(finished()));
1750 QVERIFY(finishedSpy.isValid());
1751
1752 QObject *transitionRect = root->property(name: "transitionRect").value<QObject*>();
1753 QVERIFY(transitionRect);
1754 QVERIFY(transitionRect->setProperty("state", QVariant(QLatin1String("go"))));
1755 QTRY_COMPARE(runningChangedSpy.count(), 1);
1756 QCOMPARE(stoppedSpy.count(), 0);
1757 QCOMPARE(finishedSpy.count(), 0);
1758 }
1759
1760 // Test that finished() is not emitted for animations within a Behavior.
1761 {
1762 QQuickAbstractAnimation *animationWithinBehavior
1763 = root->property(name: "animationWithinBehavior").value<QQuickAbstractAnimation*>();
1764 QVERIFY(animationWithinBehavior);
1765
1766 QSignalSpy stoppedSpy(animationWithinBehavior, SIGNAL(stopped()));
1767 QVERIFY(stoppedSpy.isValid());
1768
1769 QSignalSpy finishedSpy(animationWithinBehavior, SIGNAL(finished()));
1770 QVERIFY(finishedSpy.isValid());
1771
1772 QVERIFY(root->setProperty("bar", QVariant(1.0)));
1773 QTRY_COMPARE(root->property("bar").toReal(), 1.0);
1774 QCOMPARE(stoppedSpy.count(), 0);
1775 QCOMPARE(finishedSpy.count(), 0);
1776 }
1777}
1778
1779void tst_qquickanimations::replacingTransitions()
1780{
1781 QQmlEngine engine;
1782 QQmlComponent c(&engine, testFileUrl(fileName: "replacingTransitions.qml"));
1783 QScopedPointer<QObject> obj(c.create());
1784 auto *rect = qobject_cast<QQuickRectangle*>(object: obj.data());
1785 if (!c.errors().isEmpty())
1786 qDebug() << c.errorString();
1787 QVERIFY(rect);
1788
1789 QQmlTimer *addTimer = rect->property(name: "addTimer").value<QQmlTimer*>();
1790 QVERIFY(addTimer);
1791 QCOMPARE(addTimer->isRunning(), false);
1792
1793 QQuickTransition *addTrans = rect->property(name: "addTransition").value<QQuickTransition*>();
1794 QVERIFY(addTrans);
1795 QCOMPARE(addTrans->running(), false);
1796
1797 QQuickTransition *displaceTrans = rect->property(name: "displaceTransition").value<QQuickTransition*>();
1798 QVERIFY(displaceTrans);
1799 QCOMPARE(displaceTrans->running(), false);
1800
1801 QQmlListModel *model = rect->property(name: "model").value<QQmlListModel *>();
1802 QVERIFY(model);
1803 QCOMPARE(model->count(), 0);
1804
1805 addTimer->start();
1806 QTest::qWait(ms: 1000 + 1000 + 10000);
1807
1808 QTRY_COMPARE(addTimer->isRunning(), false);
1809 QTRY_COMPARE(addTrans->running(), false);
1810 QTRY_COMPARE(displaceTrans->running(), false);
1811 QCOMPARE(model->count(), 3);
1812}
1813
1814void tst_qquickanimations::animationJobSelfDestruction()
1815{
1816 // Don't crash
1817 QQmlEngine engine;
1818 engine.clearComponentCache();
1819 QQmlComponent c(&engine, testFileUrl(fileName: "animationJobSelfDestructionBug.qml"));
1820 QScopedPointer<QObject> obj(c.create());
1821 auto *win = qobject_cast<QQuickWindow *>(object: obj.data());
1822 if (!c.errors().isEmpty())
1823 qDebug() << c.errorString();
1824 QVERIFY(win);
1825 win->setTitle(QTest::currentTestFunction());
1826 win->show();
1827 QVERIFY(QTest::qWaitForWindowExposed(win));
1828 QQmlTimer *timer = win->property(name: "timer").value<QQmlTimer*>();
1829 QVERIFY(timer);
1830 QCOMPARE(timer->isRunning(), false);
1831 timer->start();
1832 QTest::qWait(ms: 1000);
1833}
1834
1835void tst_qquickanimations::fastFlickingBug()
1836{
1837 // Don't crash
1838 QQmlEngine engine;
1839 engine.clearComponentCache();
1840 QQmlComponent c(&engine, testFileUrl(fileName: "fastFlickingBug.qml"));
1841 QScopedPointer<QObject> obj(c.create());
1842 auto *win = qobject_cast<QQuickWindow *>(object: obj.data());
1843 if (!c.errors().isEmpty())
1844 qDebug() << c.errorString();
1845 QVERIFY(win);
1846 win->setTitle(QTest::currentTestFunction());
1847 win->show();
1848 QVERIFY(QTest::qWaitForWindowExposed(win));
1849 auto timer = win->property(name: "timer").value<QQmlTimer*>();
1850 QVERIFY(timer);
1851 QCOMPARE(timer->isRunning(), false);
1852 auto listView = win->property(name: "listView").value<QQuickFlickable*>();
1853 QVERIFY(listView);
1854 timer->start();
1855 // flick listView up and down quickly in the middle of a slow transition
1856 for (int sign = 1; timer->isRunning(); sign *= -1) {
1857 listView->flick(xVelocity: 0, yVelocity: sign * 4000);
1858 qApp->processEvents();
1859 QTest::qWait(ms: 53);
1860 qApp->processEvents();
1861 }
1862}
1863
1864void tst_qquickanimations::opacityAnimationFromZero()
1865{
1866 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
1867 || (QGuiApplication::platformName() == QLatin1String("minimal")))
1868 QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
1869
1870 // not easy to verify this in threaded render loop
1871 // since it's difficult to capture the first frame when scene graph
1872 // is renderred in another thread
1873 qputenv(varName: "QSG_RENDER_LOOP", value: "basic");
1874 auto cleanup = qScopeGuard(f: []() { qputenv(varName: "QSG_RENDER_LOOP", value: ""); });
1875
1876 QQmlEngine engine;
1877 QQmlComponent c(&engine, testFileUrl(fileName: "opacityAnimationFromZero.qml"));
1878 QScopedPointer<QQuickWindow> win(qobject_cast<QQuickWindow*>(object: c.create()));
1879 if (!c.errors().isEmpty())
1880 qDebug() << c.errorString();
1881 QVERIFY(win);
1882 win->setTitle(QTest::currentTestFunction());
1883 win->show();
1884 QVERIFY(QTest::qWaitForWindowExposed(win.data()));
1885
1886 QImage img;
1887 bool firstFrameSwapped = false;
1888 QObject::connect(sender: win.get(), signal: &QQuickWindow::frameSwapped, context: win.get(), slot: [&win, &img, &firstFrameSwapped]() {
1889 if (firstFrameSwapped)
1890 return;
1891 else
1892 firstFrameSwapped = true;
1893 img = win->grabWindow();
1894 if (img.width() < win->width())
1895 QSKIP("Skipping due to grabWindow not functional");
1896 });
1897 QTRY_VERIFY(!img.isNull() && img.pixel(100, 100) > qRgb(10, 10, 10));
1898}
1899
1900void tst_qquickanimations::alwaysRunToEndInSequentialAnimationBug()
1901{
1902 QQuickView view(QUrl::fromLocalFile(localfile: "data/alwaysRunToEndInSequentialAnimationBug.qml"));
1903 view.show();
1904
1905 QVERIFY(QTest::qWaitForWindowExposed(&view));
1906 QVERIFY(view.rootObject());
1907 QObject *root = view.rootObject();
1908 QQuickAbstractAnimation* seqAnim = root->property(name: "seqAnim").value<QQuickAbstractAnimation *>();
1909 QObject *whiteRect = root->findChild<QObject*>(aName: "whiteRect");
1910
1911 //
1912 // Tesing the SequentialAnimation with alwaysRunToEnd = true
1913 //
1914
1915 //
1916 // Stopping in the first loop
1917
1918 QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0);
1919 seqAnim->start();
1920 QTRY_VERIFY(seqAnim->isRunning() == true);
1921
1922 seqAnim->stop();
1923 QTRY_VERIFY(seqAnim->isRunning() == false);
1924 QTRY_VERIFY(!root->property("onStoppedCalled").value<bool>());
1925 QTRY_VERIFY(!root->property("onFinishedCalled").value<bool>());
1926
1927 //The animation should still be running
1928 QTest::qWait(ms: 500);
1929 QTRY_VERIFY(whiteRect->property("opacity").value<qreal>() != 1.0);
1930
1931 QTest::qWait(ms: 500);
1932
1933 //The animation should now be stopped - after reaching its cycle end
1934 QTRY_COMPARE(whiteRect->property("opacity").value<qreal>(), 1.0);
1935 QTRY_VERIFY(root->property("onStoppedCalled").value<bool>());
1936 QTRY_VERIFY(root->property("onFinishedCalled").value<bool>());
1937
1938 //
1939 // Stopping in the second loop
1940
1941 QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0);
1942 seqAnim->start();
1943 QTRY_VERIFY(seqAnim->isRunning() == true);
1944
1945 QTRY_COMPARE(root->property("loopsMade").value<int>(), 1); // Wait for cycle no 2 to start
1946
1947 seqAnim->stop();
1948 QTRY_VERIFY(seqAnim->isRunning() == false);
1949 QTRY_VERIFY(!root->property("onStoppedCalled").value<bool>());
1950 QTRY_VERIFY(!root->property("onFinishedCalled").value<bool>());
1951
1952 //The animation should still be running
1953 QTest::qWait(ms: 500);
1954 QTRY_VERIFY(whiteRect->property("opacity").value<qreal>() != 1.0);
1955
1956 QTest::qWait(ms: 500);
1957
1958 //The animation should now be stopped - after reaching its cycle end
1959 QTRY_COMPARE(whiteRect->property("opacity").value<qreal>(), 1.0);
1960 QTRY_VERIFY(root->property("onStoppedCalled").value<bool>());
1961 QTRY_VERIFY(root->property("onFinishedCalled").value<bool>());
1962
1963
1964 //
1965 // Tesing the SequentialAnimation with alwaysRunToEnd = false
1966 //
1967
1968 //
1969 // Stopping in the first loop
1970
1971 seqAnim->setProperty(name: "alwaysRunToEnd", value: false);
1972 QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0);
1973
1974 seqAnim->start();
1975 QTRY_VERIFY(seqAnim->isRunning() == true);
1976
1977 QTest::qWait(ms: 50);
1978 seqAnim->stop();
1979 //The animation should be stopped immediately
1980 QVERIFY(seqAnim->isRunning() == false);
1981 QVERIFY(root->property("onStoppedCalled").value<bool>());
1982 QVERIFY(root->property("onFinishedCalled").value<bool>());
1983 QCOMPARE(whiteRect->property("opacity").value<qreal>(), 1.0);
1984
1985 //
1986 // Stopping in the second loop
1987 QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0);
1988 seqAnim->start();
1989 QTRY_VERIFY(seqAnim->isRunning() == true);
1990
1991 QTRY_COMPARE(root->property("loopsMade").value<int>(), 1); // Wait for cycle no 2 to start
1992
1993 QTest::qWait(ms: 50);
1994 seqAnim->stop();
1995 //The animation should be stopped immediately
1996 QVERIFY(seqAnim->isRunning() == false);
1997 QVERIFY(root->property("onStoppedCalled").value<bool>());
1998 QVERIFY(root->property("onFinishedCalled").value<bool>());
1999 QCOMPARE(whiteRect->property("opacity").value<qreal>(),1.0);
2000}
2001
2002void tst_qquickanimations::cleanupWhenRenderThreadStops()
2003{
2004 QQuickView view(QUrl::fromLocalFile(localfile: "data/cleanupWhenRenderThreadStops.qml"));
2005 view.show();
2006 view.setPersistentOpenGLContext(false);
2007 view.setPersistentSceneGraph(false);
2008 QVERIFY(QTest::qWaitForWindowExposed(&view));
2009 QTest::qWait(ms: 50);
2010 view.hide();
2011 view.show();
2012 QVERIFY(QTest::qWaitForWindowExposed(&view));
2013}
2014
2015QTEST_MAIN(tst_qquickanimations)
2016
2017#include "tst_qquickanimations.moc"
2018

source code of qtdeclarative/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp