| 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 |  | 
| 47 | class tst_QTimer : public QObject | 
| 48 | { | 
| 49 |     Q_OBJECT | 
| 50 | public: | 
| 51 |     static void initMain(); | 
| 52 |  | 
| 53 | private 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 |  | 
| 93 | void 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 |  | 
| 106 | void 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 |  | 
| 122 | void 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 |  | 
| 136 | void 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 |  | 
| 187 | void 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 |  | 
| 201 | void 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 |  | 
| 214 | void 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 |  | 
| 221 | void 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 |  | 
| 253 | namespace { | 
| 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 |  | 
| 263 | void 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 |  | 
| 311 | void 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 | */ | 
| 325 | class LiveLockTester : public QObject | 
| 326 | { | 
| 327 | public: | 
| 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 ; | 
| 371 |     int timeoutsForFirst; | 
| 372 |     int ; | 
| 373 |     int timeoutsForSecond; | 
| 374 |     bool postEventAtRightTime; | 
| 375 | }; | 
| 376 |  | 
| 377 | void 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 |  | 
| 395 | class TimerInfiniteRecursionObject : public QObject | 
| 396 | { | 
| 397 | public: | 
| 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 |  | 
| 426 | void 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 |  | 
| 438 | void 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 |  | 
| 451 | class RecurringTimerObject : public QObject | 
| 452 | { | 
| 453 | Q_OBJECT | 
| 454 | public: | 
| 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 |  | 
| 475 | signals: | 
| 476 |     void done(); | 
| 477 | }; | 
| 478 |  | 
| 479 | void 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 |  | 
| 490 | void 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 |  | 
| 506 | void 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 |  | 
| 522 | void 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 |  | 
| 555 | class RestartedTimerFiresTooSoonObject : public QObject | 
| 556 | { | 
| 557 |     Q_OBJECT | 
| 558 |  | 
| 559 | public: | 
| 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 |  | 
| 610 | void tst_QTimer::restartedTimerFiresTooSoon() | 
| 611 | { | 
| 612 |     RestartedTimerFiresTooSoonObject object; | 
| 613 |     object.timerFired(); | 
| 614 |     QCOMPARE(object.eventLoop.exec(), 0); | 
| 615 | } | 
| 616 |  | 
| 617 | class LongLastingSlotClass : public QObject | 
| 618 | { | 
| 619 |     Q_OBJECT | 
| 620 |  | 
| 621 | public: | 
| 622 |     LongLastingSlotClass(QTimer *timer) : count(0), timer(timer) {} | 
| 623 |  | 
| 624 | public 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 |  | 
| 638 | public: | 
| 639 |     int count; | 
| 640 |     QTimer *timer; | 
| 641 | }; | 
| 642 |  | 
| 643 | void 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 |  | 
| 650 | void 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 |  | 
| 666 | class TimerIdPersistsAfterThreadExitThread : public QThread | 
| 667 | { | 
| 668 | public: | 
| 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 |  | 
| 691 | void 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 |  | 
| 705 | void 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 |  | 
| 716 | class TimeoutCounter : public QObject | 
| 717 | { | 
| 718 |     Q_OBJECT | 
| 719 | public slots: | 
| 720 |     void timeout() { ++count; }; | 
| 721 | public: | 
| 722 |     int count = 0; | 
| 723 | }; | 
| 724 |  | 
| 725 | void 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 |  | 
| 746 | class RecursOnTimeoutAndStopTimerTimer : public QObject | 
| 747 | { | 
| 748 |     Q_OBJECT | 
| 749 |  | 
| 750 | public: | 
| 751 |     QTimer *one; | 
| 752 |     QTimer *two; | 
| 753 |  | 
| 754 | public slots: | 
| 755 |     void onetrigger() | 
| 756 |     { | 
| 757 |         QCoreApplication::processEvents(); | 
| 758 |     } | 
| 759 |  | 
| 760 |     void twotrigger() | 
| 761 |     { | 
| 762 |         one->stop(); | 
| 763 |     } | 
| 764 | }; | 
| 765 |  | 
| 766 | void 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 |  | 
| 789 | struct 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 |  | 
| 799 | static QScopedPointer<QEventLoop> _e; | 
| 800 | static QThread *_t = nullptr; | 
| 801 |  | 
| 802 | class StaticEventLoop | 
| 803 | { | 
| 804 | public: | 
| 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 |  | 
| 819 | void 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 |  | 
| 901 | void 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 |  | 
| 945 | class DontBlockEvents : public QObject | 
| 946 | { | 
| 947 |     Q_OBJECT | 
| 948 | public: | 
| 949 |     DontBlockEvents(); | 
| 950 |     void timerEvent(QTimerEvent*); | 
| 951 |  | 
| 952 |     int count; | 
| 953 |     int total; | 
| 954 |     QBasicTimer m_timer; | 
| 955 |  | 
| 956 | public slots: | 
| 957 |     void paintEvent(); | 
| 958 |  | 
| 959 | }; | 
| 960 |  | 
| 961 | DontBlockEvents::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 |  | 
| 977 | void 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 |  | 
| 988 | void 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. | 
| 996 | void tst_QTimer::dontBlockEvents() | 
| 997 | { | 
| 998 |     DontBlockEvents t; | 
| 999 |     QTest::qWait(ms: 60); | 
| 1000 |     QTRY_VERIFY(t.total > 2); | 
| 1001 | } | 
| 1002 |  | 
| 1003 | class SlotRepeater : public QObject { | 
| 1004 |     Q_OBJECT | 
| 1005 | public: | 
| 1006 |     SlotRepeater() {} | 
| 1007 |  | 
| 1008 | public slots: | 
| 1009 |     void repeatThisSlot() | 
| 1010 |     { | 
| 1011 |         QMetaObject::invokeMethod(obj: this, member: "repeatThisSlot" , type: Qt::QueuedConnection); | 
| 1012 |     } | 
| 1013 | }; | 
| 1014 |  | 
| 1015 | void 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 |  | 
| 1027 | struct DummyFunctor { | 
| 1028 |     void operator()() {} | 
| 1029 | }; | 
| 1030 |  | 
| 1031 | void 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 |  | 
| 1050 | void 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 |  | 
| 1075 | class OrderHelper : public QObject | 
| 1076 | { | 
| 1077 |     Q_OBJECT | 
| 1078 | public: | 
| 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 |  | 
| 1108 | public slots: | 
| 1109 |     void stringSlot() { calls << String; } | 
| 1110 |     void pmfSlot() { calls << PMF; } | 
| 1111 |     void functorSlot() { calls << Functor; } | 
| 1112 |     void functorNoCtxSlot() { calls << FunctorNoCtx; } | 
| 1113 | }; | 
| 1114 |  | 
| 1115 | Q_DECLARE_METATYPE(OrderHelper::CallType) | 
| 1116 |  | 
| 1117 | void 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 |  | 
| 1129 | void 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 |  | 
| 1146 | void 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 |  | 
| 1158 | struct 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 |  | 
| 1177 | static StaticSingleShotUser *s_staticSingleShotUser = nullptr; | 
| 1178 |  | 
| 1179 | void tst_QTimer::initMain() | 
| 1180 | { | 
| 1181 |     s_staticSingleShotUser = new StaticSingleShotUser; | 
| 1182 | } | 
| 1183 |  | 
| 1184 | void tst_QTimer::cleanupTestCase() | 
| 1185 | { | 
| 1186 |     delete s_staticSingleShotUser; | 
| 1187 | } | 
| 1188 |  | 
| 1189 | void tst_QTimer::singleShot_static() | 
| 1190 | { | 
| 1191 |     QCoreApplication::processEvents(); | 
| 1192 |     QCOMPARE(s_staticSingleShotUser->helper.calls, s_staticSingleShotUser->calls()); | 
| 1193 | } | 
| 1194 |  | 
| 1195 | QTEST_MAIN(tst_QTimer) | 
| 1196 |  | 
| 1197 | #include "tst_qtimer.moc" | 
| 1198 |  |