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 | |
51 | class tst_qquickanimations : public QQmlDataTest |
52 | { |
53 | Q_OBJECT |
54 | public: |
55 | tst_qquickanimations() {} |
56 | |
57 | private 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 | |
131 | void 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 | |
163 | void 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 | |
195 | void 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 | |
235 | void 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 | |
268 | void 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 | |
345 | void 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 | |
420 | void 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 | |
468 | void 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 | |
501 | void 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 | |
565 | void 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 | |
600 | void 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 | |
634 | void 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 | |
654 | void 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 | |
713 | void 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 | |
732 | void 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 | |
781 | void 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 |
808 | void 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 | |
846 | void 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 | |
917 | void 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 | |
1025 | void 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 | |
1046 | void 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 | |
1077 | void 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 | |
1090 | void 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 | |
1102 | void 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 | |
1148 | void 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 | |
1183 | void 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 | |
1255 | void 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 | |
1286 | void 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 | |
1331 | void 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 | |
1341 | void 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 | |
1381 | void 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 |
1397 | void 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 |
1414 | void 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 |
1443 | void 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 | |
1454 | void 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 |
1469 | void 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 |
1496 | void 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 |
1509 | void 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 |
1522 | void 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 |
1536 | void 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 |
1556 | void 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 |
1569 | void 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 |
1581 | void 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 |
1601 | void 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 |
1615 | void 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 | |
1625 | Q_DECLARE_METATYPE(QList<QQmlError>) |
1626 | |
1627 | // QTBUG-22141 |
1628 | void 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 |
1646 | void 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 |
1668 | void 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 | |
1689 | void 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 | |
1705 | void 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 | |
1779 | void 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 | |
1814 | void 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 | |
1835 | void 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 | |
1864 | void 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 | |
1900 | void 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 | |
2002 | void 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 | |
2015 | QTEST_MAIN(tst_qquickanimations) |
2016 | |
2017 | #include "tst_qquickanimations.moc" |
2018 | |