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/QSignalSpy>
29#include <qtest.h>
30#include <QtQml/qqmlengine.h>
31#include <QtQml/qqmlcomponent.h>
32#include <QtQml/private/qqmltimer_p.h>
33#include <QtQuick/qquickitem.h>
34#include <QDebug>
35#include <QtCore/QPauseAnimation>
36#include <private/qabstractanimation_p.h>
37
38void consistentWait(int ms)
39{
40 //Use animations for timing, because we enabled consistentTiming
41 //This function will qWait for >= ms worth of consistent timing to elapse
42 QPauseAnimation waitTimer(ms);
43 waitTimer.start();
44 while (waitTimer.state() == QAbstractAnimation::Running)
45 QTest::qWait(ms: 20);
46}
47
48void eventLoopWait(int ms)
49{
50 // QTest::qWait() always calls sendPostedEvents before exiting, so we can't use it to stop
51 // between an event is posted and it is received; But we can use an event loop instead
52
53 QPauseAnimation waitTimer(ms);
54 waitTimer.start();
55 while (waitTimer.state() == QAbstractAnimation::Running)
56 {
57 QTimer timer;
58 QEventLoop eventLoop;
59 timer.start(msec: 0);
60 timer.connect(sender: &timer, signal: &QTimer::timeout, receiver: &eventLoop, slot: &QEventLoop::quit);
61 eventLoop.exec();
62 }
63}
64
65class tst_qqmltimer : public QObject
66{
67 Q_OBJECT
68public:
69 tst_qqmltimer();
70
71private slots:
72 void initTestCase();
73 void notRepeating();
74 void notRepeatingStart();
75 void repeat();
76 void noTriggerIfNotRunning();
77 void triggeredOnStart();
78 void triggeredOnStartRepeat();
79 void changeDuration();
80 void restart();
81 void restartFromTriggered();
82 void runningFromTriggered();
83 void parentProperty();
84 void stopWhenEventPosted();
85 void restartWhenEventPosted();
86};
87
88class TimerHelper : public QObject
89{
90 Q_OBJECT
91public:
92 TimerHelper() { }
93
94 int count = 0;
95
96public slots:
97 void timeout() {
98 ++count;
99 }
100};
101
102tst_qqmltimer::tst_qqmltimer() { }
103
104void tst_qqmltimer::initTestCase()
105{
106 QUnifiedTimer::instance()->setConsistentTiming(true);
107}
108
109void tst_qqmltimer::notRepeating()
110{
111 QQmlEngine engine;
112 QQmlComponent component(&engine);
113 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
114 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
115 QVERIFY(timer != nullptr);
116 QVERIFY(timer->isRunning());
117 QVERIFY(!timer->isRepeating());
118 QCOMPARE(timer->interval(), 100);
119
120 TimerHelper helper;
121 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
122
123
124 consistentWait(ms: 200);
125 QCOMPARE(helper.count, 1);
126 consistentWait(ms: 200);
127 QCOMPARE(helper.count, 1);
128 QVERIFY(!timer->isRunning());
129}
130
131void tst_qqmltimer::notRepeatingStart()
132{
133 QQmlEngine engine;
134 QQmlComponent component(&engine);
135 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100 }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
136 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
137 QVERIFY(timer != nullptr);
138 QVERIFY(!timer->isRunning());
139
140 TimerHelper helper;
141 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
142
143 consistentWait(ms: 200);
144 QCOMPARE(helper.count, 0);
145
146 timer->start();
147 consistentWait(ms: 200);
148 QCOMPARE(helper.count, 1);
149 consistentWait(ms: 200);
150 QCOMPARE(helper.count, 1);
151 QVERIFY(!timer->isRunning());
152
153 delete timer;
154}
155
156void tst_qqmltimer::repeat()
157{
158 QQmlEngine engine;
159 QQmlComponent component(&engine);
160 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; repeat: true; running: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
161 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
162 QVERIFY(timer != nullptr);
163
164 TimerHelper helper;
165 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
166 QCOMPARE(helper.count, 0);
167
168 consistentWait(ms: 200);
169 QVERIFY(helper.count > 0);
170 int oldCount = helper.count;
171
172 consistentWait(ms: 200);
173 QVERIFY(helper.count > oldCount);
174 QVERIFY(timer->isRunning());
175
176 oldCount = helper.count;
177 timer->stop();
178
179 consistentWait(ms: 200);
180 QCOMPARE(helper.count, oldCount);
181 QVERIFY(!timer->isRunning());
182
183 QSignalSpy spy(timer, SIGNAL(repeatChanged()));
184
185 timer->setRepeating(false);
186 QVERIFY(!timer->isRepeating());
187 QCOMPARE(spy.count(),1);
188
189 timer->setRepeating(false);
190 QCOMPARE(spy.count(),1);
191
192 timer->setRepeating(true);
193 QCOMPARE(spy.count(),2);
194
195 delete timer;
196}
197
198void tst_qqmltimer::triggeredOnStart()
199{
200 QQmlEngine engine;
201 QQmlComponent component(&engine);
202 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
203 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
204 QVERIFY(timer != nullptr);
205 QVERIFY(timer->triggeredOnStart());
206
207 TimerHelper helper;
208 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
209 consistentWait(ms: 1);
210 QCOMPARE(helper.count, 1);
211 consistentWait(ms: 200);
212 QCOMPARE(helper.count, 2);
213 consistentWait(ms: 200);
214 QCOMPARE(helper.count, 2);
215 QVERIFY(!timer->isRunning());
216
217 QSignalSpy spy(timer, SIGNAL(triggeredOnStartChanged()));
218
219 timer->setTriggeredOnStart(false);
220 QVERIFY(!timer->triggeredOnStart());
221 QCOMPARE(spy.count(),1);
222
223 timer->setTriggeredOnStart(false);
224 QCOMPARE(spy.count(),1);
225
226 timer->setTriggeredOnStart(true);
227 QCOMPARE(spy.count(),2);
228
229 delete timer;
230}
231
232void tst_qqmltimer::triggeredOnStartRepeat()
233{
234 QQmlEngine engine;
235 QQmlComponent component(&engine);
236 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true; repeat: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
237 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
238 QVERIFY(timer != nullptr);
239
240 TimerHelper helper;
241 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
242 consistentWait(ms: 1);
243 QCOMPARE(helper.count, 1);
244
245 consistentWait(ms: 200);
246 QVERIFY(helper.count > 1);
247 int oldCount = helper.count;
248 consistentWait(ms: 200);
249 QVERIFY(helper.count > oldCount);
250 QVERIFY(timer->isRunning());
251
252 delete timer;
253}
254
255void tst_qqmltimer::noTriggerIfNotRunning()
256{
257 QQmlEngine engine;
258 QQmlComponent component(&engine);
259 component.setData(QByteArray(
260 "import QtQml 2.0\n"
261 "QtObject { property bool ok: true\n"
262 "property Timer timer1: Timer { id: t1; interval: 100; repeat: true; running: true; onTriggered: if (!running) ok=false }"
263 "property Timer timer2: Timer { interval: 10; running: true; onTriggered: t1.running=false }"
264 "}"
265 ), baseUrl: QUrl::fromLocalFile(localfile: ""));
266 QObject *item = component.create();
267 QVERIFY(item != nullptr);
268 consistentWait(ms: 200);
269 QCOMPARE(item->property("ok").toBool(), true);
270
271 delete item;
272}
273
274void tst_qqmltimer::changeDuration()
275{
276 QQmlEngine engine;
277 QQmlComponent component(&engine);
278 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; repeat: true; running: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
279 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
280 QVERIFY(timer != nullptr);
281
282 TimerHelper helper;
283 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
284 QCOMPARE(helper.count, 0);
285
286 consistentWait(ms: 500);
287 QCOMPARE(helper.count, 2);
288
289 timer->setInterval(500);
290
291 consistentWait(ms: 600);
292 QCOMPARE(helper.count, 3);
293 QVERIFY(timer->isRunning());
294
295 QSignalSpy spy(timer, SIGNAL(intervalChanged()));
296
297 timer->setInterval(200);
298 QCOMPARE(timer->interval(), 200);
299 QCOMPARE(spy.count(),1);
300
301 timer->setInterval(200);
302 QCOMPARE(spy.count(),1);
303
304 timer->setInterval(300);
305 QCOMPARE(spy.count(),2);
306
307 delete timer;
308}
309
310void tst_qqmltimer::restart()
311{
312 QQmlEngine engine;
313 QQmlComponent component(&engine);
314 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 500; repeat: true; running: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
315 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
316 QVERIFY(timer != nullptr);
317
318 TimerHelper helper;
319 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
320 QCOMPARE(helper.count, 0);
321
322 consistentWait(ms: 600);
323 QCOMPARE(helper.count, 1);
324
325 consistentWait(ms: 300);
326
327 timer->restart();
328
329 consistentWait(ms: 700);
330
331 QCOMPARE(helper.count, 2);
332 QVERIFY(timer->isRunning());
333
334 delete timer;
335}
336
337void tst_qqmltimer::restartFromTriggered()
338{
339 QQmlEngine engine;
340 QQmlComponent component(&engine);
341 component.setData(QByteArray("import QtQml 2.0\nTimer { "
342 "interval: 500; "
343 "repeat: false; "
344 "running: true; "
345 "onTriggered: restart()"
346 " }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
347 QScopedPointer<QObject> object(component.create());
348 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: object.data());
349 QVERIFY(timer != nullptr);
350
351 TimerHelper helper;
352 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
353 QCOMPARE(helper.count, 0);
354
355 consistentWait(ms: 600);
356 QCOMPARE(helper.count, 1);
357 QVERIFY(timer->isRunning());
358
359 consistentWait(ms: 600);
360 QCOMPARE(helper.count, 2);
361 QVERIFY(timer->isRunning());
362}
363
364void tst_qqmltimer::runningFromTriggered()
365{
366 QQmlEngine engine;
367 QQmlComponent component(&engine);
368 component.setData(QByteArray("import QtQml 2.0\nTimer { "
369 "property bool ok: false; "
370 "interval: 500; "
371 "repeat: false; "
372 "running: true; "
373 "onTriggered: { ok = !running; running = true }"
374 " }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
375 QScopedPointer<QObject> object(component.create());
376 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: object.data());
377 QVERIFY(timer != nullptr);
378
379 TimerHelper helper;
380 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
381 QCOMPARE(helper.count, 0);
382
383 consistentWait(ms: 600);
384 QCOMPARE(helper.count, 1);
385 QVERIFY(timer->property("ok").toBool());
386 QVERIFY(timer->isRunning());
387
388 consistentWait(ms: 600);
389 QCOMPARE(helper.count, 2);
390 QVERIFY(timer->property("ok").toBool());
391 QVERIFY(timer->isRunning());
392}
393
394void tst_qqmltimer::parentProperty()
395{
396 QQmlEngine engine;
397 QQmlComponent component(&engine);
398 component.setData(QByteArray("import QtQuick 2.0\nItem { Timer { objectName: \"timer\"; running: parent.visible } }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
399 QQuickItem *item = qobject_cast<QQuickItem*>(object: component.create());
400 QVERIFY(item != nullptr);
401 QQmlTimer *timer = item->findChild<QQmlTimer*>(aName: "timer");
402 QVERIFY(timer != nullptr);
403
404 QVERIFY(timer->isRunning());
405
406 delete timer;
407}
408
409void tst_qqmltimer::stopWhenEventPosted()
410{
411 QQmlEngine engine;
412 QQmlComponent component(&engine);
413 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
414 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
415
416 TimerHelper helper;
417 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
418 QCOMPARE(helper.count, 0);
419
420 eventLoopWait(ms: 200);
421 QCOMPARE(helper.count, 0);
422 QVERIFY(timer->isRunning());
423 timer->stop();
424 QVERIFY(!timer->isRunning());
425
426 consistentWait(ms: 300);
427 QCOMPARE(helper.count, 0);
428}
429
430
431void tst_qqmltimer::restartWhenEventPosted()
432{
433 QQmlEngine engine;
434 QQmlComponent component(&engine);
435 component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), baseUrl: QUrl::fromLocalFile(localfile: ""));
436 QQmlTimer *timer = qobject_cast<QQmlTimer*>(object: component.create());
437
438 TimerHelper helper;
439 connect(sender: timer, SIGNAL(triggered()), receiver: &helper, SLOT(timeout()));
440 QCOMPARE(helper.count, 0);
441
442 eventLoopWait(ms: 200);
443 QCOMPARE(helper.count, 0);
444 timer->restart();
445
446 consistentWait(ms: 100);
447 QCOMPARE(helper.count, 0);
448 QVERIFY(timer->isRunning());
449
450 consistentWait(ms: 200);
451 QCOMPARE(helper.count, 1);
452}
453
454QTEST_MAIN(tst_qqmltimer)
455
456#include "tst_qqmltimer.moc"
457

source code of qtdeclarative/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp