1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#ifdef QT_GUI_LIB
31# include <QtGui/QGuiApplication>
32#else
33# include <QtCore/QCoreApplication>
34#endif
35
36#include <QtCore/private/qglobal_p.h>
37#include <QtTest/QtTest>
38
39#include <qtimer.h>
40#include <qthread.h>
41#include <qelapsedtimer.h>
42
43#if defined Q_OS_UNIX
44#include <unistd.h>
45#endif
46
47class tst_QTimer : public QObject
48{
49 Q_OBJECT
50public:
51 static void initMain();
52
53private slots:
54 void cleanupTestCase();
55 void zeroTimer();
56 void singleShotTimeout();
57 void timeout();
58 void remainingTime();
59 void remainingTimeInitial_data();
60 void remainingTimeInitial();
61 void remainingTimeDuringActivation_data();
62 void remainingTimeDuringActivation();
63 void basic_chrono();
64 void livelock_data();
65 void livelock();
66 void timerInfiniteRecursion_data();
67 void timerInfiniteRecursion();
68 void recurringTimer_data();
69 void recurringTimer();
70 void deleteLaterOnQTimer(); // long name, don't want to shadow QObject::deleteLater()
71 void moveToThread();
72 void restartedTimerFiresTooSoon();
73 void timerFiresOnlyOncePerProcessEvents_data();
74 void timerFiresOnlyOncePerProcessEvents();
75 void timerIdPersistsAfterThreadExit();
76 void cancelLongTimer();
77 void singleShotStaticFunctionZeroTimeout();
78 void recurseOnTimeoutAndStopTimer();
79 void singleShotToFunctors();
80 void singleShot_chrono();
81 void singleShot_static();
82 void crossThreadSingleShotToFunctor();
83 void timerOrder();
84 void timerOrder_data();
85 void timerOrderBackgroundThread();
86 void timerOrderBackgroundThread_data() { timerOrder_data(); }
87
88 void dontBlockEvents();
89 void postedEventsShouldNotStarveTimers();
90 void callOnTimeout();
91};
92
93void tst_QTimer::zeroTimer()
94{
95 QTimer timer;
96 timer.setInterval(0);
97
98 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
99 timer.start();
100
101 QCoreApplication::processEvents();
102
103 QCOMPARE(timeoutSpy.count(), 1);
104}
105
106void tst_QTimer::singleShotTimeout()
107{
108 QTimer timer;
109 timer.setSingleShot(true);
110
111 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
112 timer.start(msec: 100);
113
114 QVERIFY(timeoutSpy.wait(500));
115 QCOMPARE(timeoutSpy.count(), 1);
116 QTest::qWait(ms: 500);
117 QCOMPARE(timeoutSpy.count(), 1);
118}
119
120#define TIMEOUT_TIMEOUT 200
121
122void tst_QTimer::timeout()
123{
124 QTimer timer;
125 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
126 timer.start(msec: 100);
127
128 QCOMPARE(timeoutSpy.count(), 0);
129
130 QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.count() > 0, TIMEOUT_TIMEOUT);
131 int oldCount = timeoutSpy.count();
132
133 QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.count() > oldCount, TIMEOUT_TIMEOUT);
134}
135
136void tst_QTimer::remainingTime()
137{
138 QTimer tested;
139 tested.setTimerType(Qt::PreciseTimer);
140
141 QTimer tester;
142 tester.setTimerType(Qt::PreciseTimer);
143 tester.setSingleShot(true);
144
145 const int testedInterval = 200;
146 const int testerInterval = 50;
147 const int expectedRemainingTime = testedInterval - testerInterval;
148
149 int testIteration = 0;
150 const int desiredTestCount = 2;
151
152 auto connection = QObject::connect(sender: &tested, signal: &QTimer::timeout, slot: [&tester]() {
153 // We let tested (which isn't a single-shot) run repeatedly, to verify
154 // it *does* repeat, and check that the single-shot tester, starting
155 // at the same time, does finish first each time, by about the right duration.
156 tester.start(); // Start tester again.
157 });
158
159 QObject::connect(sender: &tester, signal: &QTimer::timeout, slot: [&]() {
160 const int remainingTime = tested.remainingTime();
161 // We expect that remainingTime is at most 150 and not overdue.
162 const bool remainingTimeInRange = remainingTime > 0
163 && remainingTime <= expectedRemainingTime;
164 if (remainingTimeInRange)
165 ++testIteration;
166 else
167 testIteration = desiredTestCount; // We are going to fail on QVERIFY2()
168 // below, so we don't want to iterate
169 // anymore and quickly exit the QTRY_...()
170 // with this failure.
171 if (testIteration == desiredTestCount)
172 QObject::disconnect(connection); // Last iteration, don't start tester again.
173 QVERIFY2(remainingTimeInRange, qPrintable("Remaining time "
174 + QByteArray::number(remainingTime) + "ms outside expected range (0ms, "
175 + QByteArray::number(expectedRemainingTime) + "ms]"));
176 });
177
178 tested.start(msec: testedInterval);
179 tester.start(msec: testerInterval); // Start tester for the 1st time.
180
181 // Test it desiredTestCount times, give it reasonable amount of time
182 // (twice as much as needed).
183 QTRY_COMPARE_WITH_TIMEOUT(testIteration, desiredTestCount,
184 testedInterval * desiredTestCount * 2);
185}
186
187void tst_QTimer::remainingTimeInitial_data()
188{
189 QTest::addColumn<int>(name: "startTimeMs");
190 QTest::addColumn<Qt::TimerType>(name: "timerType");
191
192 QTest::addRow(format: "precise time 0ms") << 0 << Qt::PreciseTimer;
193 QTest::addRow(format: "precise time 1ms") << 1 << Qt::PreciseTimer;
194 QTest::addRow(format: "precise time 10ms") << 10 << Qt::PreciseTimer;
195
196 QTest::addRow(format: "coarse time 0ms") << 0 << Qt::CoarseTimer;
197 QTest::addRow(format: "coarse time 1ms") << 1 << Qt::CoarseTimer;
198 QTest::addRow(format: "coarse time 10ms") << 10 << Qt::CoarseTimer;
199}
200
201void tst_QTimer::remainingTimeInitial()
202{
203 QFETCH(int, startTimeMs);
204 QFETCH(Qt::TimerType, timerType);
205
206 QTimer timer;
207 timer.setTimerType(timerType);
208 timer.start(msec: startTimeMs);
209
210 const int rt = timer.remainingTime();
211 QVERIFY2(rt >= 0 && rt <= startTimeMs, qPrintable(QString::number(rt)));
212}
213
214void tst_QTimer::remainingTimeDuringActivation_data()
215{
216 QTest::addColumn<bool>(name: "singleShot");
217 QTest::newRow(dataTag: "repeating") << false;
218 QTest::newRow(dataTag: "single-shot") << true;
219}
220
221void tst_QTimer::remainingTimeDuringActivation()
222{
223 QFETCH(bool, singleShot);
224
225 QTimer timer;
226 timer.setSingleShot(singleShot);
227
228 int remainingTime = 0; // not the expected value in either case
229 connect(sender: &timer, signal: &QTimer::timeout,
230 slot: [&]() {
231 remainingTime = timer.remainingTime();
232 });
233 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
234 const int timeout = 20; // 20 ms is short enough and should not round down to 0 in any timer mode
235 timer.start(msec: timeout);
236
237 QVERIFY(timeoutSpy.wait());
238 if (singleShot)
239 QCOMPARE(remainingTime, -1); // timer not running
240 else
241 QVERIFY2(remainingTime <= timeout && remainingTime > 0,
242 qPrintable(QString::number(remainingTime)));
243
244 if (!singleShot) {
245 // do it again - see QTBUG-46940
246 remainingTime = -1;
247 QVERIFY(timeoutSpy.wait());
248 QVERIFY2(remainingTime <= timeout && remainingTime > 0,
249 qPrintable(QString::number(remainingTime)));
250 }
251}
252
253namespace {
254
255#if __has_include(<chrono>)
256 template <typename T>
257 std::chrono::milliseconds to_ms(T t)
258 { return std::chrono::duration_cast<std::chrono::milliseconds>(t); }
259#endif
260
261} // unnamed namespace
262
263void tst_QTimer::basic_chrono()
264{
265#if !__has_include(<chrono>)
266 QSKIP("This test requires C++11 <chrono> support");
267#else
268 // duplicates zeroTimer, singleShotTimeout, interval and remainingTime
269 using namespace std::chrono;
270 QTimer timer;
271 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
272 timer.setInterval(to_ms(t: nanoseconds(0)));
273 timer.start();
274 QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(0));
275 QCOMPARE(timer.remainingTimeAsDuration().count(), milliseconds::rep(0));
276
277 QCoreApplication::processEvents();
278
279 QCOMPARE(timeoutSpy.count(), 1);
280
281 timeoutSpy.clear();
282 timer.start(value: milliseconds(100));
283 QCOMPARE(timeoutSpy.count(), 0);
284
285 QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
286 QVERIFY(timeoutSpy.count() > 0);
287 int oldCount = timeoutSpy.count();
288
289 QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
290 QVERIFY(timeoutSpy.count() > oldCount);
291
292 timeoutSpy.clear();
293 timer.start(value: to_ms(t: microseconds(200000)));
294 QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(200));
295 QTest::qWait(ms: 50);
296 QCOMPARE(timeoutSpy.count(), 0);
297
298 milliseconds rt = timer.remainingTimeAsDuration();
299 QVERIFY2(rt.count() >= 50 && rt.count() <= 200, qPrintable(QString::number(rt.count())));
300
301 timeoutSpy.clear();
302 timer.setSingleShot(true);
303 timer.start(value: milliseconds(100));
304 QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
305 QCOMPARE(timeoutSpy.count(), 1);
306 QTest::qWait(ms: 500);
307 QCOMPARE(timeoutSpy.count(), 1);
308#endif
309}
310
311void tst_QTimer::livelock_data()
312{
313 QTest::addColumn<int>(name: "interval");
314 QTest::newRow(dataTag: "zero timer") << 0;
315 QTest::newRow(dataTag: "non-zero timer") << 1;
316 QTest::newRow(dataTag: "longer than sleep") << 20;
317}
318
319/*!
320 *
321 * DO NOT "FIX" THIS TEST! it is written like this for a reason, do
322 * not *change it without first dicussing it with its maintainers.
323 *
324*/
325class LiveLockTester : public QObject
326{
327public:
328 LiveLockTester(int i)
329 : interval(i),
330 timeoutsForFirst(0), timeoutsForExtra(0), timeoutsForSecond(0),
331 postEventAtRightTime(false)
332 {
333 firstTimerId = startTimer(interval);
334 extraTimerId = startTimer(interval: interval + 80);
335 secondTimerId = -1; // started later
336 }
337
338 bool event(QEvent *e) {
339 if (e->type() == 4002) {
340 // got the posted event
341 if (timeoutsForFirst == 1 && timeoutsForSecond == 0)
342 postEventAtRightTime = true;
343 return true;
344 }
345 return QObject::event(event: e);
346 }
347
348 void timerEvent(QTimerEvent *te) {
349 if (te->timerId() == firstTimerId) {
350 if (++timeoutsForFirst == 1) {
351 killTimer(id: extraTimerId);
352 extraTimerId = -1;
353 QCoreApplication::postEvent(receiver: this, event: new QEvent(static_cast<QEvent::Type>(4002)));
354 secondTimerId = startTimer(interval);
355 }
356 } else if (te->timerId() == secondTimerId) {
357 ++timeoutsForSecond;
358 } else if (te->timerId() == extraTimerId) {
359 ++timeoutsForExtra;
360 }
361
362 // sleep for 2ms
363 QTest::qSleep(ms: 2);
364 killTimer(id: te->timerId());
365 }
366
367 const int interval;
368 int firstTimerId;
369 int secondTimerId;
370 int extraTimerId;
371 int timeoutsForFirst;
372 int timeoutsForExtra;
373 int timeoutsForSecond;
374 bool postEventAtRightTime;
375};
376
377void tst_QTimer::livelock()
378{
379 /*
380 New timers created in timer event handlers should not be sent
381 until the next iteration of the eventloop. Note: this test
382 depends on the fact that we send posted events before timer
383 events (since new posted events are not sent until the next
384 iteration of the eventloop either).
385 */
386 QFETCH(int, interval);
387 LiveLockTester tester(interval);
388 QTest::qWait(ms: 180); // we have to use wait here, since we're testing timers with a non-zero timeout
389 QTRY_COMPARE(tester.timeoutsForFirst, 1);
390 QCOMPARE(tester.timeoutsForExtra, 0);
391 QTRY_COMPARE(tester.timeoutsForSecond, 1);
392 QVERIFY(tester.postEventAtRightTime);
393}
394
395class TimerInfiniteRecursionObject : public QObject
396{
397public:
398 bool inTimerEvent;
399 bool timerEventRecursed;
400 int interval;
401
402 TimerInfiniteRecursionObject(int interval)
403 : inTimerEvent(false), timerEventRecursed(false), interval(interval)
404 { }
405
406 void timerEvent(QTimerEvent *timerEvent)
407 {
408 timerEventRecursed = inTimerEvent;
409 if (timerEventRecursed) {
410 // bug detected!
411 return;
412 }
413
414 inTimerEvent = true;
415
416 QEventLoop eventLoop;
417 QTimer::singleShot(msec: qMax(a: 100, b: interval * 2), receiver: &eventLoop, SLOT(quit()));
418 eventLoop.exec();
419
420 inTimerEvent = false;
421
422 killTimer(id: timerEvent->timerId());
423 }
424};
425
426void tst_QTimer::timerInfiniteRecursion_data()
427{
428 QTest::addColumn<int>(name: "interval");
429 QTest::newRow(dataTag: "zero timer") << 0;
430 QTest::newRow(dataTag: "non-zero timer") << 1;
431 QTest::newRow(dataTag: "10ms timer") << 10;
432 QTest::newRow(dataTag: "11ms timer") << 11;
433 QTest::newRow(dataTag: "100ms timer") << 100;
434 QTest::newRow(dataTag: "1s timer") << 1000;
435}
436
437
438void tst_QTimer::timerInfiniteRecursion()
439{
440 QFETCH(int, interval);
441 TimerInfiniteRecursionObject object(interval);
442 (void) object.startTimer(interval);
443
444 QEventLoop eventLoop;
445 QTimer::singleShot(msec: qMax(a: 100, b: interval * 2), receiver: &eventLoop, SLOT(quit()));
446 eventLoop.exec();
447
448 QVERIFY(!object.timerEventRecursed);
449}
450
451class RecurringTimerObject : public QObject
452{
453Q_OBJECT
454public:
455 int times;
456 int target;
457 bool recurse;
458
459 RecurringTimerObject(int target)
460 : times(0), target(target), recurse(false)
461 { }
462
463 void timerEvent(QTimerEvent *timerEvent)
464 {
465 if (++times == target) {
466 killTimer(id: timerEvent->timerId());
467 emit done();
468 } if (recurse) {
469 QEventLoop eventLoop;
470 QTimer::singleShot(msec: 100, receiver: &eventLoop, SLOT(quit()));
471 eventLoop.exec();
472 }
473 }
474
475signals:
476 void done();
477};
478
479void tst_QTimer::recurringTimer_data()
480{
481 QTest::addColumn<int>(name: "interval");
482 QTest::addColumn<bool>(name: "recurse");
483 // make sure that eventloop recursion doesn't affect timer recurrence
484 QTest::newRow(dataTag: "zero timer, don't recurse") << 0 << false;
485 QTest::newRow(dataTag: "zero timer, recurse") << 0 << true;
486 QTest::newRow(dataTag: "non-zero timer, don't recurse") << 1 << false;
487 QTest::newRow(dataTag: "non-zero timer, recurse") << 1 << true;
488}
489
490void tst_QTimer::recurringTimer()
491{
492 const int target = 5;
493 QFETCH(int, interval);
494 QFETCH(bool, recurse);
495
496 RecurringTimerObject object(target);
497 object.recurse = recurse;
498 QSignalSpy doneSpy(&object, &RecurringTimerObject::done);
499
500 (void) object.startTimer(interval);
501 QVERIFY(doneSpy.wait());
502
503 QCOMPARE(object.times, target);
504}
505
506void tst_QTimer::deleteLaterOnQTimer()
507{
508 QTimer *timer = new QTimer;
509 connect(sender: timer, SIGNAL(timeout()), receiver: timer, SLOT(deleteLater()));
510 QSignalSpy destroyedSpy(timer, &QObject::destroyed);
511 timer->setInterval(1);
512 timer->setSingleShot(true);
513 timer->start();
514 QPointer<QTimer> pointer = timer;
515 QVERIFY(destroyedSpy.wait());
516 QVERIFY(pointer.isNull());
517}
518
519#define MOVETOTHREAD_TIMEOUT 200
520#define MOVETOTHREAD_WAIT 300
521
522void tst_QTimer::moveToThread()
523{
524#if defined(Q_OS_WIN32)
525 QSKIP("Does not work reliably on Windows :(");
526#elif defined(Q_OS_MACOS)
527 QSKIP("Does not work reliably on macOS 10.12+ (QTBUG-59679)");
528#endif
529 QTimer ti1;
530 QTimer ti2;
531 ti1.start(MOVETOTHREAD_TIMEOUT);
532 ti2.start(MOVETOTHREAD_TIMEOUT);
533 QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
534 QThread tr;
535 ti1.moveToThread(thread: &tr);
536 connect(sender: &ti1,SIGNAL(timeout()), receiver: &tr, SLOT(quit()));
537 tr.start();
538 QTimer ti3;
539 ti3.start(MOVETOTHREAD_TIMEOUT);
540 QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
541 QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
542 QTest::qWait(MOVETOTHREAD_WAIT);
543 QVERIFY(tr.wait());
544 ti2.stop();
545 QTimer ti4;
546 ti4.start(MOVETOTHREAD_TIMEOUT);
547 ti3.stop();
548 ti2.start(MOVETOTHREAD_TIMEOUT);
549 ti3.start(MOVETOTHREAD_TIMEOUT);
550 QVERIFY((ti4.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
551 QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
552 QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
553}
554
555class RestartedTimerFiresTooSoonObject : public QObject
556{
557 Q_OBJECT
558
559public:
560 QBasicTimer m_timer;
561
562 int m_interval;
563 QElapsedTimer m_elapsedTimer;
564 QEventLoop eventLoop;
565
566 inline RestartedTimerFiresTooSoonObject()
567 : QObject(), m_interval(0)
568 { }
569
570 void timerFired()
571 {
572 static int interval = 1000;
573
574 m_interval = interval;
575 m_elapsedTimer.start();
576 m_timer.start(msec: interval, obj: this);
577
578 // alternate between single-shot and 1 sec
579 interval = interval ? 0 : 1000;
580 }
581
582 void timerEvent(QTimerEvent* ev)
583 {
584 if (ev->timerId() != m_timer.timerId())
585 return;
586
587 m_timer.stop();
588
589 int elapsed = m_elapsedTimer.elapsed();
590
591 if (elapsed < m_interval / 2) {
592 // severely too early!
593 m_timer.stop();
594 eventLoop.exit(returnCode: -1);
595 return;
596 }
597
598 timerFired();
599
600 // don't do this forever
601 static int count = 0;
602 if (count++ > 20) {
603 m_timer.stop();
604 eventLoop.quit();
605 return;
606 }
607 }
608};
609
610void tst_QTimer::restartedTimerFiresTooSoon()
611{
612 RestartedTimerFiresTooSoonObject object;
613 object.timerFired();
614 QCOMPARE(object.eventLoop.exec(), 0);
615}
616
617class LongLastingSlotClass : public QObject
618{
619 Q_OBJECT
620
621public:
622 LongLastingSlotClass(QTimer *timer) : count(0), timer(timer) {}
623
624public slots:
625 void longLastingSlot()
626 {
627 // Don't use QTimer for this, because we are testing it.
628 QElapsedTimer control;
629 control.start();
630 while (control.elapsed() < 200) {
631 for (int c = 0; c < 100000; c++) {} // Mindless looping.
632 }
633 if (++count >= 2) {
634 timer->stop();
635 }
636 }
637
638public:
639 int count;
640 QTimer *timer;
641};
642
643void tst_QTimer::timerFiresOnlyOncePerProcessEvents_data()
644{
645 QTest::addColumn<int>(name: "interval");
646 QTest::newRow(dataTag: "zero timer") << 0;
647 QTest::newRow(dataTag: "non-zero timer") << 10;
648}
649
650void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
651{
652 QFETCH(int, interval);
653
654 QTimer t;
655 LongLastingSlotClass longSlot(&t);
656 t.start(msec: interval);
657 connect(sender: &t, SIGNAL(timeout()), receiver: &longSlot, SLOT(longLastingSlot()));
658 // Loop because there may be other events pending.
659 while (longSlot.count == 0) {
660 QCoreApplication::processEvents(flags: QEventLoop::WaitForMoreEvents);
661 }
662
663 QCOMPARE(longSlot.count, 1);
664}
665
666class TimerIdPersistsAfterThreadExitThread : public QThread
667{
668public:
669 QTimer *timer;
670 int timerId, returnValue;
671
672 TimerIdPersistsAfterThreadExitThread()
673 : QThread(), timer(0), timerId(-1), returnValue(-1)
674 { }
675 ~TimerIdPersistsAfterThreadExitThread()
676 {
677 delete timer;
678 }
679
680 void run()
681 {
682 QEventLoop eventLoop;
683 timer = new QTimer;
684 connect(sender: timer, SIGNAL(timeout()), receiver: &eventLoop, SLOT(quit()));
685 timer->start(msec: 100);
686 timerId = timer->timerId();
687 returnValue = eventLoop.exec();
688 }
689};
690
691void tst_QTimer::timerIdPersistsAfterThreadExit()
692{
693 TimerIdPersistsAfterThreadExitThread thread;
694 thread.start();
695 QVERIFY(thread.wait(30000));
696 QCOMPARE(thread.returnValue, 0);
697
698 // even though the thread has exited, and the event dispatcher destroyed, the timer is still
699 // "active", meaning the timer id should NOT be reused (i.e. the event dispatcher should not
700 // have unregistered it)
701 int timerId = thread.startTimer(interval: 100);
702 QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff));
703}
704
705void tst_QTimer::cancelLongTimer()
706{
707 QTimer timer;
708 timer.setSingleShot(true);
709 timer.start(msec: 1000 * 60 * 60); //set timer for 1 hour
710 QCoreApplication::processEvents();
711 QVERIFY(timer.isActive()); //if the timer completes immediately with an error, then this will fail
712 timer.stop();
713 QVERIFY(!timer.isActive());
714}
715
716class TimeoutCounter : public QObject
717{
718 Q_OBJECT
719public slots:
720 void timeout() { ++count; };
721public:
722 int count = 0;
723};
724
725void tst_QTimer::singleShotStaticFunctionZeroTimeout()
726{
727 {
728 TimeoutCounter counter;
729
730 QTimer::singleShot(msec: 0, receiver: &counter, SLOT(timeout()));
731 QTRY_COMPARE(counter.count, 1);
732 QTest::qWait(ms: 500);
733 QCOMPARE(counter.count, 1);
734 }
735
736 {
737 TimeoutCounter counter;
738
739 QTimer::singleShot(interval: 0, receiver: &counter, slot: &TimeoutCounter::timeout);
740 QTRY_COMPARE(counter.count, 1);
741 QTest::qWait(ms: 500);
742 QCOMPARE(counter.count, 1);
743 }
744}
745
746class RecursOnTimeoutAndStopTimerTimer : public QObject
747{
748 Q_OBJECT
749
750public:
751 QTimer *one;
752 QTimer *two;
753
754public slots:
755 void onetrigger()
756 {
757 QCoreApplication::processEvents();
758 }
759
760 void twotrigger()
761 {
762 one->stop();
763 }
764};
765
766void tst_QTimer::recurseOnTimeoutAndStopTimer()
767{
768 QEventLoop eventLoop;
769 QTimer::singleShot(msec: 1000, receiver: &eventLoop, SLOT(quit()));
770
771 RecursOnTimeoutAndStopTimerTimer t;
772 t.one = new QTimer(&t);
773 t.two = new QTimer(&t);
774
775 QObject::connect(sender: t.one, SIGNAL(timeout()), receiver: &t, SLOT(onetrigger()));
776 QObject::connect(sender: t.two, SIGNAL(timeout()), receiver: &t, SLOT(twotrigger()));
777
778 t.two->setSingleShot(true);
779
780 t.one->start();
781 t.two->start();
782
783 (void) eventLoop.exec();
784
785 QVERIFY(!t.one->isActive());
786 QVERIFY(!t.two->isActive());
787}
788
789struct CountedStruct
790{
791 CountedStruct(int *count, QThread *t = nullptr) : count(count), thread(t) { }
792 ~CountedStruct() { }
793 void operator()() const { ++(*count); if (thread) QCOMPARE(QThread::currentThread(), thread); }
794
795 int *count;
796 QThread *thread;
797};
798
799static QScopedPointer<QEventLoop> _e;
800static QThread *_t = nullptr;
801
802class StaticEventLoop
803{
804public:
805 static void quitEventLoop()
806 {
807 quitEventLoop_noexcept();
808 }
809
810 static void quitEventLoop_noexcept() noexcept
811 {
812 QVERIFY(!_e.isNull());
813 _e->quit();
814 if (_t)
815 QCOMPARE(QThread::currentThread(), _t);
816 }
817};
818
819void tst_QTimer::singleShotToFunctors()
820{
821 int count = 0;
822 _e.reset(other: new QEventLoop);
823 QEventLoop e;
824
825 QTimer::singleShot(interval: 0, slot: CountedStruct(&count));
826 QCoreApplication::processEvents();
827 QCOMPARE(count, 1);
828
829 QTimer::singleShot(interval: 0, slot: &StaticEventLoop::quitEventLoop);
830 QCOMPARE(_e->exec(), 0);
831
832 QTimer::singleShot(interval: 0, slot: &StaticEventLoop::quitEventLoop_noexcept);
833 QCOMPARE(_e->exec(), 0);
834
835 QThread t1;
836 QObject c1;
837 c1.moveToThread(thread: &t1);
838
839 QObject::connect(sender: &t1, SIGNAL(started()), receiver: &e, SLOT(quit()));
840 t1.start();
841 QCOMPARE(e.exec(), 0);
842
843 QTimer::singleShot(interval: 0, context: &c1, slot: CountedStruct(&count, &t1));
844 QTRY_COMPARE(count, 2);
845
846 t1.quit();
847 t1.wait();
848
849 _t = new QThread;
850 QObject c2;
851 c2.moveToThread(thread: _t);
852
853 QObject::connect(sender: _t, SIGNAL(started()), receiver: &e, SLOT(quit()));
854 _t->start();
855 QCOMPARE(e.exec(), 0);
856
857 QTimer::singleShot(interval: 0, context: &c2, slot: &StaticEventLoop::quitEventLoop);
858 QCOMPARE(_e->exec(), 0);
859
860 _t->quit();
861 _t->wait();
862 _t->deleteLater();
863 _t = nullptr;
864
865 {
866 QObject c3;
867 QTimer::singleShot(interval: 500, context: &c3, slot: CountedStruct(&count));
868 }
869 QTest::qWait(ms: 800); // Wait until the singleshot timer would have timed out
870 QCOMPARE(count, 2);
871
872 QTimer::singleShot(interval: 0, slot: [&count] { ++count; });
873 QTRY_COMPARE(count, 3);
874
875 QObject context;
876 QThread thread;
877
878 context.moveToThread(thread: &thread);
879 QObject::connect(sender: &thread, SIGNAL(started()), receiver: &e, SLOT(quit()));
880 thread.start();
881 QCOMPARE(e.exec(), 0);
882
883 QTimer::singleShot(interval: 0, context: &context, slot: [&count, &thread] { ++count; QCOMPARE(QThread::currentThread(), &thread); });
884 QTRY_COMPARE(count, 4);
885
886 thread.quit();
887 thread.wait();
888
889 struct MoveOnly : CountedStruct {
890 Q_DISABLE_COPY(MoveOnly);
891 MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {};
892 MoveOnly(int *c) : CountedStruct(c) {}
893 };
894 QTimer::singleShot(interval: 0, slot: MoveOnly(&count));
895 QTRY_COMPARE(count, 5);
896
897 _e.reset();
898 _t = nullptr;
899}
900
901void tst_QTimer::singleShot_chrono()
902{
903#if !__has_include(<chrono>)
904 QSKIP("This test requires C++11 <chrono> support");
905#else
906 // duplicates singleShotStaticFunctionZeroTimeout and singleShotToFunctors
907 using namespace std::chrono;
908 {
909 TimeoutCounter counter;
910
911 QTimer::singleShot(value: hours(0), receiver: &counter, SLOT(timeout()));
912 QTRY_COMPARE(counter.count, 1);
913 QTest::qWait(ms: 500);
914 QCOMPARE(counter.count, 1);
915 }
916
917 {
918 TimeoutCounter counter;
919
920 QTimer::singleShot(interval: hours(0), receiver: &counter, slot: &TimeoutCounter::timeout);
921 QTRY_COMPARE(counter.count, 1);
922 QTest::qWait(ms: 500);
923 QCOMPARE(counter.count, 1);
924 }
925
926 int count = 0;
927 QTimer::singleShot(interval: to_ms(t: microseconds(0)), slot: CountedStruct(&count));
928 QTRY_COMPARE(count, 1);
929
930 _e.reset(other: new QEventLoop);
931 QTimer::singleShot(interval: 0, slot: &StaticEventLoop::quitEventLoop);
932 QCOMPARE(_e->exec(), 0);
933
934 QObject c3;
935 QTimer::singleShot(interval: milliseconds(500), context: &c3, slot: CountedStruct(&count));
936 QTRY_COMPARE(count, 2);
937
938 QTimer::singleShot(interval: 0, slot: [&count] { ++count; });
939 QTRY_COMPARE(count, 3);
940
941 _e.reset();
942#endif
943}
944
945class DontBlockEvents : public QObject
946{
947 Q_OBJECT
948public:
949 DontBlockEvents();
950 void timerEvent(QTimerEvent*);
951
952 int count;
953 int total;
954 QBasicTimer m_timer;
955
956public slots:
957 void paintEvent();
958
959};
960
961DontBlockEvents::DontBlockEvents()
962{
963 count = 0;
964 total = 0;
965
966 // need a few unrelated timers running to reproduce the bug.
967 (new QTimer(this))->start(msec: 2000);
968 (new QTimer(this))->start(msec: 2500);
969 (new QTimer(this))->start(msec: 3000);
970 (new QTimer(this))->start(msec: 5000);
971 (new QTimer(this))->start(msec: 1000);
972 (new QTimer(this))->start(msec: 2000);
973
974 m_timer.start(msec: 1, obj: this);
975}
976
977void DontBlockEvents::timerEvent(QTimerEvent* event)
978{
979 if (event->timerId() == m_timer.timerId()) {
980 QMetaObject::invokeMethod(obj: this, member: "paintEvent", type: Qt::QueuedConnection);
981 m_timer.start(msec: 0, obj: this);
982 count++;
983 QCOMPARE(count, 1);
984 total++;
985 }
986}
987
988void DontBlockEvents::paintEvent()
989{
990 count--;
991 QCOMPARE(count, 0);
992}
993
994// This is a regression test for QTBUG-13633, where a timer with a zero
995// timeout that was restarted by the event handler could starve other timers.
996void tst_QTimer::dontBlockEvents()
997{
998 DontBlockEvents t;
999 QTest::qWait(ms: 60);
1000 QTRY_VERIFY(t.total > 2);
1001}
1002
1003class SlotRepeater : public QObject {
1004 Q_OBJECT
1005public:
1006 SlotRepeater() {}
1007
1008public slots:
1009 void repeatThisSlot()
1010 {
1011 QMetaObject::invokeMethod(obj: this, member: "repeatThisSlot", type: Qt::QueuedConnection);
1012 }
1013};
1014
1015void tst_QTimer::postedEventsShouldNotStarveTimers()
1016{
1017 QTimer timer;
1018 timer.setInterval(0);
1019 timer.setSingleShot(false);
1020 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
1021 timer.start();
1022 SlotRepeater slotRepeater;
1023 slotRepeater.repeatThisSlot();
1024 QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.count() > 5, 100);
1025}
1026
1027struct DummyFunctor {
1028 void operator()() {}
1029};
1030
1031void tst_QTimer::crossThreadSingleShotToFunctor()
1032{
1033 // We're testing for crashes here, so the test simply running to
1034 // completion is considered a success
1035 QThread t;
1036 t.start();
1037
1038 QObject* o = new QObject();
1039 o->moveToThread(thread: &t);
1040
1041 for (int i = 0; i < 10000; i++) {
1042 QTimer::singleShot(interval: 0, context: o, slot: DummyFunctor());
1043 }
1044
1045 t.quit();
1046 t.wait();
1047 delete o;
1048}
1049
1050void tst_QTimer::callOnTimeout()
1051{
1052 QTimer timer;
1053 QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
1054 timer.setInterval(0);
1055 timer.start();
1056
1057 auto context = new QObject();
1058
1059 int count = 0;
1060 timer.callOnTimeout(args: [&count] { count++; });
1061 QMetaObject::Connection connection = timer.callOnTimeout(args&: context, args: [&count] { count++; });
1062 timer.callOnTimeout(args: &timer, args: &QTimer::stop);
1063
1064
1065 QTest::qWait(ms: 100);
1066 QCOMPARE(count, 2);
1067 QCOMPARE(timeoutSpy.count(), 1);
1068
1069 // Test that connection is bound to context lifetime
1070 QVERIFY(connection);
1071 delete context;
1072 QVERIFY(!connection);
1073}
1074
1075class OrderHelper : public QObject
1076{
1077 Q_OBJECT
1078public:
1079 enum CallType
1080 {
1081 String,
1082 PMF,
1083 Functor,
1084 FunctorNoCtx
1085 };
1086 Q_ENUM(CallType)
1087 QVector<CallType> calls;
1088
1089 void triggerCall(CallType callType)
1090 {
1091 switch (callType)
1092 {
1093 case String:
1094 QTimer::singleShot(msec: 0, receiver: this, SLOT(stringSlot()));
1095 break;
1096 case PMF:
1097 QTimer::singleShot(interval: 0, receiver: this, slot: &OrderHelper::pmfSlot);
1098 break;
1099 case Functor:
1100 QTimer::singleShot(interval: 0, context: this, slot: [this]() { functorSlot(); });
1101 break;
1102 case FunctorNoCtx:
1103 QTimer::singleShot(interval: 0, slot: [this]() { functorNoCtxSlot(); });
1104 break;
1105 }
1106 }
1107
1108public slots:
1109 void stringSlot() { calls << String; }
1110 void pmfSlot() { calls << PMF; }
1111 void functorSlot() { calls << Functor; }
1112 void functorNoCtxSlot() { calls << FunctorNoCtx; }
1113};
1114
1115Q_DECLARE_METATYPE(OrderHelper::CallType)
1116
1117void tst_QTimer::timerOrder()
1118{
1119 QFETCH(QVector<OrderHelper::CallType>, calls);
1120
1121 OrderHelper helper;
1122
1123 for (const auto call : calls)
1124 helper.triggerCall(callType: call);
1125
1126 QTRY_COMPARE(helper.calls, calls);
1127}
1128
1129void tst_QTimer::timerOrder_data()
1130{
1131 QTest::addColumn<QVector<OrderHelper::CallType>>(name: "calls");
1132
1133 QVector<OrderHelper::CallType> calls = {
1134 OrderHelper::String, OrderHelper::PMF,
1135 OrderHelper::Functor, OrderHelper::FunctorNoCtx
1136 };
1137 std::sort(first: calls.begin(), last: calls.end());
1138
1139 int permutation = 0;
1140 do {
1141 QTest::addRow(format: "permutation=%d", permutation) << calls;
1142 ++permutation;
1143 } while (std::next_permutation(first: calls.begin(), last: calls.end()));
1144}
1145
1146void tst_QTimer::timerOrderBackgroundThread()
1147{
1148#if !QT_CONFIG(cxx11_future)
1149 QSKIP("This test requires QThread::create");
1150#else
1151 auto *thread = QThread::create(f: [this]() { timerOrder(); });
1152 thread->start();
1153 QVERIFY(thread->wait());
1154 delete thread;
1155#endif
1156}
1157
1158struct StaticSingleShotUser
1159{
1160 StaticSingleShotUser()
1161 {
1162 for (auto call : calls())
1163 helper.triggerCall(callType: call);
1164 }
1165 OrderHelper helper;
1166
1167 static QVector<OrderHelper::CallType> calls()
1168 {
1169 return {OrderHelper::String, OrderHelper::PMF,
1170 OrderHelper::Functor, OrderHelper::FunctorNoCtx};
1171 }
1172};
1173
1174// NOTE: to prevent any static initialization order fiasco, we implement
1175// initMain() to instantiate staticSingleShotUser before qApp
1176
1177static StaticSingleShotUser *s_staticSingleShotUser = nullptr;
1178
1179void tst_QTimer::initMain()
1180{
1181 s_staticSingleShotUser = new StaticSingleShotUser;
1182}
1183
1184void tst_QTimer::cleanupTestCase()
1185{
1186 delete s_staticSingleShotUser;
1187}
1188
1189void tst_QTimer::singleShot_static()
1190{
1191 QCoreApplication::processEvents();
1192 QCOMPARE(s_staticSingleShotUser->helper.calls, s_staticSingleShotUser->calls());
1193}
1194
1195QTEST_MAIN(tst_QTimer)
1196
1197#include "tst_qtimer.moc"
1198

source code of qtbase/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp