1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | |
31 | #include <QtCore/qanimationgroup.h> |
32 | #include <QtCore/qsequentialanimationgroup.h> |
33 | #include <QtCore/qparallelanimationgroup.h> |
34 | |
35 | Q_DECLARE_METATYPE(QAbstractAnimation::State) |
36 | |
37 | class tst_QAnimationGroup : public QObject |
38 | { |
39 | Q_OBJECT |
40 | public Q_SLOTS: |
41 | void initTestCase(); |
42 | |
43 | private slots: |
44 | void construction(); |
45 | void emptyGroup(); |
46 | void setCurrentTime(); |
47 | void setParentAutoAdd(); |
48 | void beginNestedGroup(); |
49 | void addChildTwice(); |
50 | void loopWithoutStartValue(); |
51 | }; |
52 | |
53 | void tst_QAnimationGroup::initTestCase() |
54 | { |
55 | qRegisterMetaType<QAbstractAnimation::State>(typeName: "QAbstractAnimation::State" ); |
56 | } |
57 | |
58 | void tst_QAnimationGroup::construction() |
59 | { |
60 | QSequentialAnimationGroup animationgroup; |
61 | } |
62 | |
63 | class AnimationObject : public QObject |
64 | { |
65 | Q_OBJECT |
66 | Q_PROPERTY(int value READ value WRITE setValue) |
67 | public: |
68 | AnimationObject(int startValue = 0) |
69 | : v(startValue) |
70 | { } |
71 | |
72 | int value() const { return v; } |
73 | void setValue(int value) { v = value; } |
74 | |
75 | int v; |
76 | }; |
77 | |
78 | class TestAnimation : public QVariantAnimation |
79 | { |
80 | Q_OBJECT |
81 | public: |
82 | virtual void updateCurrentValue(const QVariant &value) { Q_UNUSED(value)}; |
83 | virtual void updateState(QAbstractAnimation::State oldState, |
84 | QAbstractAnimation::State newState) |
85 | { |
86 | Q_UNUSED(oldState) |
87 | Q_UNUSED(newState) |
88 | }; |
89 | }; |
90 | |
91 | class UncontrolledAnimation : public QPropertyAnimation |
92 | { |
93 | Q_OBJECT |
94 | public: |
95 | UncontrolledAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0) |
96 | : QPropertyAnimation(target, propertyName, parent), id(0) |
97 | { |
98 | setDuration(250); |
99 | } |
100 | |
101 | int duration() const { return -1; /* not time driven */ } |
102 | |
103 | protected: |
104 | void timerEvent(QTimerEvent *event) |
105 | { |
106 | if (event->timerId() == id) |
107 | stop(); |
108 | } |
109 | |
110 | void updateRunning(bool running) |
111 | { |
112 | if (running) { |
113 | id = startTimer(interval: 500); |
114 | } else { |
115 | killTimer(id); |
116 | id = 0; |
117 | } |
118 | } |
119 | |
120 | private: |
121 | int id; |
122 | }; |
123 | |
124 | void tst_QAnimationGroup::emptyGroup() |
125 | { |
126 | QSequentialAnimationGroup group; |
127 | QSignalSpy groupStateChangedSpy(&group, &QSequentialAnimationGroup::stateChanged); |
128 | QVERIFY(groupStateChangedSpy.isValid()); |
129 | |
130 | QCOMPARE(group.state(), QAnimationGroup::Stopped); |
131 | group.start(); |
132 | |
133 | QCOMPARE(groupStateChangedSpy.count(), 2); |
134 | |
135 | QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(0).first()), |
136 | QAnimationGroup::Running); |
137 | QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(1).first()), |
138 | QAnimationGroup::Stopped); |
139 | |
140 | QCOMPARE(group.state(), QAnimationGroup::Stopped); |
141 | |
142 | QTest::ignoreMessage(type: QtWarningMsg, message: "QAbstractAnimation::pause: Cannot pause a stopped animation" ); |
143 | group.pause(); |
144 | |
145 | QCOMPARE(groupStateChangedSpy.count(), 2); |
146 | QCOMPARE(group.state(), QAnimationGroup::Stopped); |
147 | |
148 | group.start(); |
149 | |
150 | QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(2).first()), |
151 | QAnimationGroup::Running); |
152 | QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(3).first()), |
153 | QAnimationGroup::Stopped); |
154 | |
155 | QCOMPARE(group.state(), QAnimationGroup::Stopped); |
156 | |
157 | group.stop(); |
158 | |
159 | QCOMPARE(groupStateChangedSpy.count(), 4); |
160 | QCOMPARE(group.state(), QAnimationGroup::Stopped); |
161 | } |
162 | |
163 | void tst_QAnimationGroup::setCurrentTime() |
164 | { |
165 | AnimationObject s_o1; |
166 | AnimationObject s_o2; |
167 | AnimationObject s_o3; |
168 | AnimationObject p_o1; |
169 | AnimationObject p_o2; |
170 | AnimationObject p_o3; |
171 | AnimationObject t_o1; |
172 | AnimationObject t_o2; |
173 | |
174 | // sequence operating on same object/property |
175 | QSequentialAnimationGroup *sequence = new QSequentialAnimationGroup(); |
176 | QAbstractAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value" ); |
177 | QAbstractAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value" ); |
178 | QAbstractAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value" ); |
179 | a2_s_o1->setLoopCount(3); |
180 | sequence->addAnimation(animation: a1_s_o1); |
181 | sequence->addAnimation(animation: a2_s_o1); |
182 | sequence->addAnimation(animation: a3_s_o1); |
183 | |
184 | // sequence operating on different object/properties |
185 | QAnimationGroup *sequence2 = new QSequentialAnimationGroup(); |
186 | QAbstractAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value" ); |
187 | QAbstractAnimation *a1_s_o3 = new QPropertyAnimation(&s_o3, "value" ); |
188 | sequence2->addAnimation(animation: a1_s_o2); |
189 | sequence2->addAnimation(animation: a1_s_o3); |
190 | |
191 | // parallel operating on different object/properties |
192 | QAnimationGroup *parallel = new QParallelAnimationGroup(); |
193 | QAbstractAnimation *a1_p_o1 = new QPropertyAnimation(&p_o1, "value" ); |
194 | QAbstractAnimation *a1_p_o2 = new QPropertyAnimation(&p_o2, "value" ); |
195 | QAbstractAnimation *a1_p_o3 = new QPropertyAnimation(&p_o3, "value" ); |
196 | a1_p_o2->setLoopCount(3); |
197 | parallel->addAnimation(animation: a1_p_o1); |
198 | parallel->addAnimation(animation: a1_p_o2); |
199 | parallel->addAnimation(animation: a1_p_o3); |
200 | |
201 | QAbstractAnimation *notTimeDriven = new UncontrolledAnimation(&t_o1, "value" ); |
202 | QCOMPARE(notTimeDriven->totalDuration(), -1); |
203 | |
204 | QAbstractAnimation *loopsForever = new QPropertyAnimation(&t_o2, "value" ); |
205 | loopsForever->setLoopCount(-1); |
206 | QCOMPARE(loopsForever->totalDuration(), -1); |
207 | |
208 | QParallelAnimationGroup group; |
209 | group.addAnimation(animation: sequence); |
210 | group.addAnimation(animation: sequence2); |
211 | group.addAnimation(animation: parallel); |
212 | group.addAnimation(animation: notTimeDriven); |
213 | group.addAnimation(animation: loopsForever); |
214 | |
215 | // Current time = 1 |
216 | group.setCurrentTime(1); |
217 | QCOMPARE(group.state(), QAnimationGroup::Stopped); |
218 | QCOMPARE(sequence->state(), QAnimationGroup::Stopped); |
219 | QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped); |
220 | QCOMPARE(sequence2->state(), QAnimationGroup::Stopped); |
221 | QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped); |
222 | QCOMPARE(parallel->state(), QAnimationGroup::Stopped); |
223 | QCOMPARE(a1_p_o1->state(), QAnimationGroup::Stopped); |
224 | QCOMPARE(a1_p_o2->state(), QAnimationGroup::Stopped); |
225 | QCOMPARE(a1_p_o3->state(), QAnimationGroup::Stopped); |
226 | QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped); |
227 | QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped); |
228 | |
229 | QCOMPARE(group.currentLoopTime(), 1); |
230 | QCOMPARE(sequence->currentLoopTime(), 1); |
231 | QCOMPARE(a1_s_o1->currentLoopTime(), 1); |
232 | QCOMPARE(a2_s_o1->currentLoopTime(), 0); |
233 | QCOMPARE(a3_s_o1->currentLoopTime(), 0); |
234 | QCOMPARE(a1_s_o2->currentLoopTime(), 1); |
235 | QCOMPARE(a1_s_o3->currentLoopTime(), 0); |
236 | QCOMPARE(a1_p_o1->currentLoopTime(), 1); |
237 | QCOMPARE(a1_p_o2->currentLoopTime(), 1); |
238 | QCOMPARE(a1_p_o3->currentLoopTime(), 1); |
239 | QCOMPARE(notTimeDriven->currentLoopTime(), 1); |
240 | QCOMPARE(loopsForever->currentLoopTime(), 1); |
241 | |
242 | // Current time = 250 |
243 | group.setCurrentTime(250); |
244 | QCOMPARE(group.currentLoopTime(), 250); |
245 | QCOMPARE(sequence->currentLoopTime(), 250); |
246 | QCOMPARE(a1_s_o1->currentLoopTime(), 250); |
247 | QCOMPARE(a2_s_o1->currentLoopTime(), 0); |
248 | QCOMPARE(a3_s_o1->currentLoopTime(), 0); |
249 | QCOMPARE(a1_s_o2->currentLoopTime(), 250); |
250 | QCOMPARE(a1_s_o3->currentLoopTime(), 0); |
251 | QCOMPARE(a1_p_o1->currentLoopTime(), 250); |
252 | QCOMPARE(a1_p_o2->currentLoopTime(), 0); |
253 | QCOMPARE(a1_p_o2->currentLoop(), 1); |
254 | QCOMPARE(a1_p_o3->currentLoopTime(), 250); |
255 | QCOMPARE(notTimeDriven->currentLoopTime(), 250); |
256 | QCOMPARE(loopsForever->currentLoopTime(), 0); |
257 | QCOMPARE(loopsForever->currentLoop(), 1); |
258 | QCOMPARE(sequence->currentAnimation(), a2_s_o1); |
259 | |
260 | // Current time = 251 |
261 | group.setCurrentTime(251); |
262 | QCOMPARE(group.currentLoopTime(), 251); |
263 | QCOMPARE(sequence->currentLoopTime(), 251); |
264 | QCOMPARE(a1_s_o1->currentLoopTime(), 250); |
265 | QCOMPARE(a2_s_o1->currentLoopTime(), 1); |
266 | QCOMPARE(a2_s_o1->currentLoop(), 0); |
267 | QCOMPARE(a3_s_o1->currentLoopTime(), 0); |
268 | QCOMPARE(sequence2->currentLoopTime(), 251); |
269 | QCOMPARE(a1_s_o2->currentLoopTime(), 250); |
270 | QCOMPARE(a1_s_o3->currentLoopTime(), 1); |
271 | QCOMPARE(a1_p_o1->currentLoopTime(), 250); |
272 | QCOMPARE(a1_p_o2->currentLoopTime(), 1); |
273 | QCOMPARE(a1_p_o2->currentLoop(), 1); |
274 | QCOMPARE(a1_p_o3->currentLoopTime(), 250); |
275 | QCOMPARE(notTimeDriven->currentLoopTime(), 251); |
276 | QCOMPARE(loopsForever->currentLoopTime(), 1); |
277 | QCOMPARE(sequence->currentAnimation(), a2_s_o1); |
278 | } |
279 | |
280 | void tst_QAnimationGroup::setParentAutoAdd() |
281 | { |
282 | QParallelAnimationGroup group; |
283 | QVariantAnimation *animation = new QPropertyAnimation(&group); |
284 | QCOMPARE(animation->group(), static_cast<QAnimationGroup*>(&group)); |
285 | } |
286 | |
287 | void tst_QAnimationGroup::beginNestedGroup() |
288 | { |
289 | QParallelAnimationGroup group; |
290 | QAnimationGroup *parent = &group; |
291 | |
292 | for (int i = 0; i < 10; ++i) { |
293 | if (i & 1) { |
294 | new QParallelAnimationGroup(parent); |
295 | } else { |
296 | new QSequentialAnimationGroup(parent); |
297 | } |
298 | |
299 | QCOMPARE(parent->animationCount(), 1); |
300 | QAnimationGroup *child = static_cast<QAnimationGroup *>(parent->animationAt(index: 0)); |
301 | |
302 | QCOMPARE(child->parent(), static_cast<QObject *>(parent)); |
303 | if (i & 1) |
304 | QVERIFY(qobject_cast<QParallelAnimationGroup *> (child)); |
305 | else |
306 | QVERIFY(qobject_cast<QSequentialAnimationGroup *> (child)); |
307 | |
308 | parent = child; |
309 | } |
310 | } |
311 | |
312 | void tst_QAnimationGroup::addChildTwice() |
313 | { |
314 | QAbstractAnimation *subGroup; |
315 | QAbstractAnimation *subGroup2; |
316 | QAnimationGroup *parent = new QSequentialAnimationGroup(); |
317 | |
318 | subGroup = new QPropertyAnimation(); |
319 | subGroup->setParent(parent); |
320 | parent->addAnimation(animation: subGroup); |
321 | QCOMPARE(parent->animationCount(), 1); |
322 | |
323 | parent->clear(); |
324 | |
325 | QCOMPARE(parent->animationCount(), 0); |
326 | |
327 | // adding the same item twice to a group will remove the item from its current position |
328 | // and append it to the end |
329 | subGroup = new QPropertyAnimation(parent); |
330 | subGroup2 = new QPropertyAnimation(parent); |
331 | |
332 | QCOMPARE(parent->animationCount(), 2); |
333 | QCOMPARE(parent->animationAt(0), subGroup); |
334 | QCOMPARE(parent->animationAt(1), subGroup2); |
335 | |
336 | parent->addAnimation(animation: subGroup); |
337 | |
338 | QCOMPARE(parent->animationCount(), 2); |
339 | QCOMPARE(parent->animationAt(0), subGroup2); |
340 | QCOMPARE(parent->animationAt(1), subGroup); |
341 | |
342 | delete parent; |
343 | } |
344 | |
345 | void tst_QAnimationGroup::loopWithoutStartValue() |
346 | { |
347 | QSequentialAnimationGroup group; |
348 | QAnimationGroup *parent = &group; |
349 | QObject o; |
350 | o.setProperty(name: "ole" , value: 0); |
351 | QCOMPARE(o.property("ole" ).toInt(), 0); |
352 | |
353 | QPropertyAnimation anim1(&o, "ole" ); |
354 | anim1.setEndValue(-50); |
355 | anim1.setDuration(100); |
356 | |
357 | QPropertyAnimation anim2(&o, "ole" ); |
358 | anim2.setEndValue(50); |
359 | anim2.setDuration(100); |
360 | |
361 | parent->addAnimation(animation: &anim1); |
362 | parent->addAnimation(animation: &anim2); |
363 | |
364 | parent->setLoopCount(-1); |
365 | parent->start(); |
366 | |
367 | QVERIFY(anim1.startValue().isNull()); |
368 | QCOMPARE(anim1.currentValue().toInt(), 0); |
369 | QCOMPARE(parent->currentLoop(), 0); |
370 | |
371 | parent->setCurrentTime(200); |
372 | QCOMPARE(parent->currentLoop(), 1); |
373 | QCOMPARE(anim1.currentValue().toInt(), 50); |
374 | parent->stop(); |
375 | } |
376 | |
377 | QTEST_MAIN(tst_QAnimationGroup) |
378 | #include "tst_qanimationgroup.moc" |
379 | |