| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the test suite of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
| 21 | ** included in the packaging of this file. Please review the following | 
| 22 | ** information to ensure the GNU General Public License requirements will | 
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 24 | ** | 
| 25 | ** $QT_END_LICENSE$ | 
| 26 | ** | 
| 27 | ****************************************************************************/ | 
| 28 |  | 
| 29 | #include <QtTest/QtTest> | 
| 30 |  | 
| 31 | #include <qabstracteventdispatcher.h> | 
| 32 | #include <qcoreapplication.h> | 
| 33 | #include <qcoreevent.h> | 
| 34 | #include <qeventloop.h> | 
| 35 | #include <private/qeventloop_p.h> | 
| 36 | #if defined(Q_OS_UNIX) | 
| 37 |   #include <private/qeventdispatcher_unix_p.h> | 
| 38 |   #include <QtCore/private/qcore_unix_p.h> | 
| 39 |   #if defined(HAVE_GLIB) | 
| 40 |     #include <private/qeventdispatcher_glib_p.h> | 
| 41 |   #endif | 
| 42 | #endif | 
| 43 | #include <qmutex.h> | 
| 44 | #include <qthread.h> | 
| 45 | #include <qtimer.h> | 
| 46 | #include <qwaitcondition.h> | 
| 47 | #include <QTcpServer> | 
| 48 | #include <QTcpSocket> | 
| 49 |  | 
| 50 | class EventLoopExiter : public QObject | 
| 51 | { | 
| 52 |     Q_OBJECT | 
| 53 |     QEventLoop *eventLoop; | 
| 54 | public: | 
| 55 |     inline EventLoopExiter(QEventLoop *el) | 
| 56 |         : eventLoop(el) | 
| 57 |     { } | 
| 58 | public slots: | 
| 59 |     void exit(); | 
| 60 |     void exit1(); | 
| 61 |     void exit2(); | 
| 62 | }; | 
| 63 |  | 
| 64 | void EventLoopExiter::exit() | 
| 65 | { eventLoop->exit(); } | 
| 66 |  | 
| 67 | void EventLoopExiter::exit1() | 
| 68 | { eventLoop->exit(returnCode: 1); } | 
| 69 |  | 
| 70 | void EventLoopExiter::exit2() | 
| 71 | { eventLoop->exit(returnCode: 2); } | 
| 72 |  | 
| 73 | class EventLoopThread : public QThread | 
| 74 | { | 
| 75 |     Q_OBJECT | 
| 76 | signals: | 
| 77 |     void checkPoint(); | 
| 78 | public: | 
| 79 |     QEventLoop *eventLoop; | 
| 80 |     void run(); | 
| 81 | }; | 
| 82 |  | 
| 83 | void EventLoopThread::run() | 
| 84 | { | 
| 85 |     eventLoop = new QEventLoop; | 
| 86 |     emit checkPoint(); | 
| 87 |     (void) eventLoop->exec(); | 
| 88 |     delete eventLoop; | 
| 89 |     eventLoop = 0; | 
| 90 | } | 
| 91 |  | 
| 92 | class MultipleExecThread : public QThread | 
| 93 | { | 
| 94 |     Q_OBJECT | 
| 95 | signals: | 
| 96 |     void checkPoint(); | 
| 97 | public: | 
| 98 |     QMutex mutex; | 
| 99 |     QWaitCondition cond; | 
| 100 |     volatile int result1; | 
| 101 |     volatile int result2; | 
| 102 |     MultipleExecThread() : result1(0xdead), result2(0xbeef) {} | 
| 103 |  | 
| 104 |     void run() | 
| 105 |     { | 
| 106 |         QMutexLocker locker(&mutex); | 
| 107 |         // this exec should work | 
| 108 |  | 
| 109 |         cond.wakeOne(); | 
| 110 |         cond.wait(lockedMutex: &mutex); | 
| 111 |  | 
| 112 |         QTimer timer; | 
| 113 |         connect(asender: &timer, SIGNAL(timeout()), SLOT(quit()), atype: Qt::DirectConnection); | 
| 114 |         timer.setInterval(1000); | 
| 115 |         timer.start(); | 
| 116 |         result1 = exec(); | 
| 117 |  | 
| 118 |         // this should return immediately, since exit() has been called | 
| 119 |         cond.wakeOne(); | 
| 120 |         cond.wait(lockedMutex: &mutex); | 
| 121 |         QEventLoop eventLoop; | 
| 122 |         result2 = eventLoop.exec(); | 
| 123 |     } | 
| 124 | }; | 
| 125 |  | 
| 126 | class StartStopEvent: public QEvent | 
| 127 | { | 
| 128 | public: | 
| 129 |     explicit StartStopEvent(int type, QEventLoop *loop = 0) | 
| 130 |         : QEvent(Type(type)), el(loop) | 
| 131 |     { } | 
| 132 |  | 
| 133 |     QEventLoop *el; | 
| 134 | }; | 
| 135 |  | 
| 136 | class EventLoopExecutor : public QObject | 
| 137 | { | 
| 138 |     Q_OBJECT | 
| 139 |     QEventLoop *eventLoop; | 
| 140 | public: | 
| 141 |     int returnCode; | 
| 142 |     EventLoopExecutor(QEventLoop *eventLoop) | 
| 143 |         : QObject(), eventLoop(eventLoop), returnCode(-42) | 
| 144 |     { | 
| 145 |     } | 
| 146 | public slots: | 
| 147 |     void exec() | 
| 148 |     { | 
| 149 |         QTimer::singleShot(msec: 100, receiver: eventLoop, SLOT(quit())); | 
| 150 |         // this should return immediately, and the timer event should be delivered to | 
| 151 |         // tst_QEventLoop::exec() test, letting the test complete | 
| 152 |         returnCode = eventLoop->exec(); | 
| 153 |     } | 
| 154 | }; | 
| 155 |  | 
| 156 | #ifdef QT_GUI_LIB | 
| 157 |   #define tst_QEventLoop tst_QGuiEventLoop | 
| 158 | #endif | 
| 159 |  | 
| 160 | class tst_QEventLoop : public QObject | 
| 161 | { | 
| 162 |     Q_OBJECT | 
| 163 | private slots: | 
| 164 |     // This test *must* run first. See the definition for why. | 
| 165 |     void processEvents(); | 
| 166 |     void exec(); | 
| 167 |     void reexec(); | 
| 168 |     void execAfterExit(); | 
| 169 |     void wakeUp(); | 
| 170 |     void quit(); | 
| 171 | #if defined(Q_OS_UNIX) | 
| 172 |     void processEventsExcludeSocket(); | 
| 173 | #endif | 
| 174 |     void processEventsExcludeTimers(); | 
| 175 |     void deliverInDefinedOrder(); | 
| 176 |  | 
| 177 |     // keep this test last: | 
| 178 |     void nestedLoops(); | 
| 179 |  | 
| 180 |     void testQuitLock(); | 
| 181 |  | 
| 182 | protected: | 
| 183 |     void customEvent(QEvent *e); | 
| 184 | }; | 
| 185 |  | 
| 186 | void tst_QEventLoop::processEvents() | 
| 187 | { | 
| 188 |     QSignalSpy aboutToBlockSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock); | 
| 189 |     QSignalSpy awakeSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::awake); | 
| 190 |  | 
| 191 |     QVERIFY(aboutToBlockSpy.isValid()); | 
| 192 |     QVERIFY(awakeSpy.isValid()); | 
| 193 |  | 
| 194 |     QEventLoop eventLoop; | 
| 195 |  | 
| 196 |     QCoreApplication::postEvent(receiver: &eventLoop, event: new QEvent(QEvent::User)); | 
| 197 |  | 
| 198 |     // process posted events, QEventLoop::processEvents() should return | 
| 199 |     // true | 
| 200 |     QVERIFY(eventLoop.processEvents()); | 
| 201 |     QCOMPARE(aboutToBlockSpy.count(), 0); | 
| 202 |     QCOMPARE(awakeSpy.count(), 1); | 
| 203 |  | 
| 204 |     // allow any session manager to complete its handshake, so that | 
| 205 |     // there are no pending events left. This tests that we are able | 
| 206 |     // to process all events from the queue, otherwise it will hang. | 
| 207 |     while (eventLoop.processEvents()) | 
| 208 |         ; | 
| 209 |  | 
| 210 |     // make sure the test doesn't block forever | 
| 211 |     int timerId = startTimer(interval: 100); | 
| 212 |  | 
| 213 |     // wait for more events to process, QEventLoop::processEvents() | 
| 214 |     // should return true | 
| 215 |     aboutToBlockSpy.clear(); | 
| 216 |     awakeSpy.clear(); | 
| 217 |     QVERIFY(eventLoop.processEvents(QEventLoop::WaitForMoreEvents)); | 
| 218 |  | 
| 219 |     // We should get one awake for each aboutToBlock, plus one awake when | 
| 220 |     // processEvents is entered. There is no guarantee that that the | 
| 221 |     // processEvents call actually blocked, since the OS may introduce | 
| 222 |     // native events at any time. | 
| 223 |     QVERIFY(awakeSpy.count() > 0); | 
| 224 |     QVERIFY(awakeSpy.count() >= aboutToBlockSpy.count()); | 
| 225 |  | 
| 226 |     killTimer(id: timerId); | 
| 227 | } | 
| 228 |  | 
| 229 | #define EXEC_TIMEOUT 100 | 
| 230 |  | 
| 231 | void tst_QEventLoop::exec() | 
| 232 | { | 
| 233 |     { | 
| 234 |         QEventLoop eventLoop; | 
| 235 |         EventLoopExiter exiter(&eventLoop); | 
| 236 |         int returnCode; | 
| 237 |  | 
| 238 |         QTimer::singleShot(EXEC_TIMEOUT, receiver: &exiter, SLOT(exit())); | 
| 239 |         returnCode = eventLoop.exec(); | 
| 240 |         QCOMPARE(returnCode, 0); | 
| 241 |  | 
| 242 |         QTimer::singleShot(EXEC_TIMEOUT, receiver: &exiter, SLOT(exit1())); | 
| 243 |         returnCode = eventLoop.exec(); | 
| 244 |         QCOMPARE(returnCode, 1); | 
| 245 |  | 
| 246 |         QTimer::singleShot(EXEC_TIMEOUT, receiver: &exiter, SLOT(exit2())); | 
| 247 |         returnCode = eventLoop.exec(); | 
| 248 |         QCOMPARE(returnCode, 2); | 
| 249 |     } | 
| 250 |  | 
| 251 |     { | 
| 252 |         // calling QEventLoop::exec() after a thread loop has exit()ed should return immediately | 
| 253 |         // Note: this behaviour differs from QCoreApplication and QEventLoop | 
| 254 |         // see tst_QCoreApplication::eventLoopExecAfterExit, tst_QEventLoop::reexec | 
| 255 |         MultipleExecThread thread; | 
| 256 |  | 
| 257 |         // start thread and wait for checkpoint | 
| 258 |         thread.mutex.lock(); | 
| 259 |         thread.start(); | 
| 260 |         thread.cond.wait(lockedMutex: &thread.mutex); | 
| 261 |  | 
| 262 |         // make sure the eventloop runs | 
| 263 |         QSignalSpy spy(QAbstractEventDispatcher::instance(thread: &thread), &QAbstractEventDispatcher::awake); | 
| 264 |         QVERIFY(spy.isValid()); | 
| 265 |         thread.cond.wakeOne(); | 
| 266 |         thread.cond.wait(lockedMutex: &thread.mutex); | 
| 267 |         QVERIFY(spy.count() > 0); | 
| 268 |         int v = thread.result1; | 
| 269 |         QCOMPARE(v, 0); | 
| 270 |  | 
| 271 |         // exec should return immediately | 
| 272 |         spy.clear(); | 
| 273 |         thread.cond.wakeOne(); | 
| 274 |         thread.mutex.unlock(); | 
| 275 |         thread.wait(); | 
| 276 |         QCOMPARE(spy.count(), 0); | 
| 277 |         v = thread.result2; | 
| 278 |         QCOMPARE(v, -1); | 
| 279 |     } | 
| 280 |  | 
| 281 |     { | 
| 282 |         // a single instance of QEventLoop should not be allowed to recurse into exec() | 
| 283 |         QEventLoop eventLoop; | 
| 284 |         EventLoopExecutor executor(&eventLoop); | 
| 285 |  | 
| 286 |         QTimer::singleShot(EXEC_TIMEOUT, receiver: &executor, SLOT(exec())); | 
| 287 |         int returnCode = eventLoop.exec(); | 
| 288 |         QCOMPARE(returnCode, 0); | 
| 289 |         QCOMPARE(executor.returnCode, -1); | 
| 290 |     } | 
| 291 | } | 
| 292 |  | 
| 293 | void tst_QEventLoop::reexec() | 
| 294 | { | 
| 295 |     QEventLoop loop; | 
| 296 |  | 
| 297 |     // exec once | 
| 298 |     QMetaObject::invokeMethod(obj: &loop, member: "quit" , type: Qt::QueuedConnection); | 
| 299 |     QCOMPARE(loop.exec(), 0); | 
| 300 |  | 
| 301 |     // and again | 
| 302 |     QMetaObject::invokeMethod(obj: &loop, member: "quit" , type: Qt::QueuedConnection); | 
| 303 |     QCOMPARE(loop.exec(), 0); | 
| 304 | } | 
| 305 |  | 
| 306 | void tst_QEventLoop::execAfterExit() | 
| 307 | { | 
| 308 |     QEventLoop loop; | 
| 309 |     EventLoopExiter obj(&loop); | 
| 310 |  | 
| 311 |     QMetaObject::invokeMethod(obj: &obj, member: "exit" , type: Qt::QueuedConnection); | 
| 312 |     loop.exit(returnCode: 1); | 
| 313 |     QCOMPARE(loop.exec(), 0); | 
| 314 | } | 
| 315 |  | 
| 316 | void tst_QEventLoop::wakeUp() | 
| 317 | { | 
| 318 |     EventLoopThread thread; | 
| 319 |     QEventLoop eventLoop; | 
| 320 |     connect(sender: &thread, SIGNAL(checkPoint()), receiver: &eventLoop, SLOT(quit())); | 
| 321 |     connect(sender: &thread, SIGNAL(finished()), receiver: &eventLoop, SLOT(quit())); | 
| 322 |  | 
| 323 |     thread.start(); | 
| 324 |     (void) eventLoop.exec(); | 
| 325 |  | 
| 326 |     QSignalSpy spy(QAbstractEventDispatcher::instance(thread: &thread), &QAbstractEventDispatcher::awake); | 
| 327 |     QVERIFY(spy.isValid()); | 
| 328 |     thread.eventLoop->wakeUp(); | 
| 329 |  | 
| 330 |     // give the thread time to wake up | 
| 331 |     QTimer::singleShot(msec: 1000, receiver: &eventLoop, SLOT(quit())); | 
| 332 |     (void) eventLoop.exec(); | 
| 333 |  | 
| 334 |     QVERIFY(spy.count() > 0); | 
| 335 |  | 
| 336 |     thread.quit(); | 
| 337 |     (void) eventLoop.exec(); | 
| 338 | } | 
| 339 |  | 
| 340 | void tst_QEventLoop::quit() | 
| 341 | { | 
| 342 |     QEventLoop eventLoop; | 
| 343 |     int returnCode; | 
| 344 |  | 
| 345 |     QTimer::singleShot(msec: 100, receiver: &eventLoop, SLOT(quit())); | 
| 346 |     returnCode = eventLoop.exec(); | 
| 347 |     QCOMPARE(returnCode, 0); | 
| 348 | } | 
| 349 |  | 
| 350 |  | 
| 351 | void tst_QEventLoop::nestedLoops() | 
| 352 | { | 
| 353 |     QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(QEvent::User)); | 
| 354 |     QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(QEvent::User)); | 
| 355 |     QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(QEvent::User)); | 
| 356 |  | 
| 357 |     // without the fix, this will *wedge* and never return | 
| 358 |     QTest::qWait(ms: 1000); | 
| 359 | } | 
| 360 |  | 
| 361 | void tst_QEventLoop::customEvent(QEvent *e) | 
| 362 | { | 
| 363 |     if (e->type() == QEvent::User) { | 
| 364 |         QEventLoop loop; | 
| 365 |         QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(int(QEvent::User) + 1, &loop)); | 
| 366 |         loop.exec(); | 
| 367 |     } else { | 
| 368 |         static_cast<StartStopEvent *>(e)->el->exit(); | 
| 369 |     } | 
| 370 | } | 
| 371 |  | 
| 372 | #if defined(Q_OS_UNIX) | 
| 373 | class SocketEventsTester: public QObject | 
| 374 | { | 
| 375 |     Q_OBJECT | 
| 376 | public: | 
| 377 |     SocketEventsTester() | 
| 378 |     { | 
| 379 |         socket = 0; | 
| 380 |         server = 0; | 
| 381 |         dataSent = false; | 
| 382 |         dataReadable = false; | 
| 383 |         testResult = false; | 
| 384 |         dataArrived = false; | 
| 385 |     } | 
| 386 |     ~SocketEventsTester() | 
| 387 |     { | 
| 388 |         delete socket; | 
| 389 |         delete server; | 
| 390 |     } | 
| 391 |     bool init() | 
| 392 |     { | 
| 393 |         bool ret = false; | 
| 394 |         server = new QTcpServer(); | 
| 395 |         socket = new QTcpSocket(); | 
| 396 |         connect(sender: server, SIGNAL(newConnection()), receiver: this, SLOT(sendHello())); | 
| 397 |         connect(sender: socket, SIGNAL(readyRead()), receiver: this, SLOT(sendAck()), Qt::DirectConnection); | 
| 398 |         if((ret = server->listen(address: QHostAddress::LocalHost, port: 0))) { | 
| 399 |             socket->connectToHost(address: server->serverAddress(), port: server->serverPort()); | 
| 400 |             socket->waitForConnected(); | 
| 401 |         } | 
| 402 |         return ret; | 
| 403 |     } | 
| 404 |  | 
| 405 |     QTcpSocket *socket; | 
| 406 |     QTcpServer *server; | 
| 407 |     bool dataSent; | 
| 408 |     bool dataReadable; | 
| 409 |     bool testResult; | 
| 410 |     bool dataArrived; | 
| 411 | public slots: | 
| 412 |     void sendAck() | 
| 413 |     { | 
| 414 |         dataArrived = true; | 
| 415 |     } | 
| 416 |     void sendHello() | 
| 417 |     { | 
| 418 |         char data[10] ="HELLO" ; | 
| 419 |         qint64 size = sizeof(data); | 
| 420 |  | 
| 421 |         QTcpSocket *serverSocket = server->nextPendingConnection(); | 
| 422 |         QCoreApplication::processEvents(); | 
| 423 |         serverSocket->write(data, len: size); | 
| 424 |         dataSent = serverSocket->waitForBytesWritten(msecs: -1); | 
| 425 |  | 
| 426 |         if (dataSent) { | 
| 427 |             pollfd pfd = qt_make_pollfd(fd: socket->socketDescriptor(), POLLIN); | 
| 428 |             dataReadable = (1 == qt_safe_poll(fds: &pfd, nfds: 1, timeout_ts: nullptr)); | 
| 429 |         } | 
| 430 |  | 
| 431 |         if (!dataReadable) { | 
| 432 |             testResult = dataArrived; | 
| 433 |         } else { | 
| 434 |             QCoreApplication::processEvents(flags: QEventLoop::ExcludeSocketNotifiers); | 
| 435 |             testResult = dataArrived; | 
| 436 |             // to check if the deferred event is processed | 
| 437 |             QCoreApplication::processEvents(); | 
| 438 |         } | 
| 439 |         serverSocket->close(); | 
| 440 |         QThread::currentThread()->exit(retcode: 0); | 
| 441 |     } | 
| 442 | }; | 
| 443 |  | 
| 444 | class SocketTestThread : public QThread | 
| 445 | { | 
| 446 |     Q_OBJECT | 
| 447 | public: | 
| 448 |     SocketTestThread():QThread(0),testResult(false){}; | 
| 449 |     void run() | 
| 450 |     { | 
| 451 |         SocketEventsTester *tester = new SocketEventsTester(); | 
| 452 |         if (tester->init()) | 
| 453 |             exec(); | 
| 454 |         dataSent = tester->dataSent; | 
| 455 |         dataReadable = tester->dataReadable; | 
| 456 |         testResult = tester->testResult; | 
| 457 |         dataArrived = tester->dataArrived; | 
| 458 |         delete tester; | 
| 459 |     } | 
| 460 |     bool dataSent; | 
| 461 |     bool dataReadable; | 
| 462 |     bool testResult; | 
| 463 |     bool dataArrived; | 
| 464 | }; | 
| 465 |  | 
| 466 | void tst_QEventLoop::processEventsExcludeSocket() | 
| 467 | { | 
| 468 |     SocketTestThread thread; | 
| 469 |     thread.start(); | 
| 470 |     QVERIFY(thread.wait()); | 
| 471 |     QVERIFY(thread.dataSent); | 
| 472 |     QVERIFY(thread.dataReadable); | 
| 473 |   #if defined(HAVE_GLIB) | 
| 474 |     QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); | 
| 475 |     if (qobject_cast<QEventDispatcherGlib *>(object: eventDispatcher)) | 
| 476 |         QEXPECT_FAIL("" , "ExcludeSocketNotifiers is currently broken in the Glib dispatchers" , Continue); | 
| 477 |   #endif | 
| 478 |     QVERIFY(!thread.testResult); | 
| 479 |     QVERIFY(thread.dataArrived); | 
| 480 | } | 
| 481 | #endif | 
| 482 |  | 
| 483 | class TimerReceiver : public QObject | 
| 484 | { | 
| 485 | public: | 
| 486 |     int gotTimerEvent; | 
| 487 |  | 
| 488 |     TimerReceiver() | 
| 489 |         : QObject(), gotTimerEvent(-1) | 
| 490 |     { } | 
| 491 |  | 
| 492 |     void timerEvent(QTimerEvent *event) | 
| 493 |     { | 
| 494 |         gotTimerEvent = event->timerId(); | 
| 495 |     } | 
| 496 | }; | 
| 497 |  | 
| 498 | void tst_QEventLoop::processEventsExcludeTimers() | 
| 499 | { | 
| 500 |     TimerReceiver timerReceiver; | 
| 501 |     int timerId = timerReceiver.startTimer(interval: 0); | 
| 502 |  | 
| 503 |     QEventLoop eventLoop; | 
| 504 |  | 
| 505 |     // normal process events will send timers | 
| 506 |     eventLoop.processEvents(); | 
| 507 |     QCOMPARE(timerReceiver.gotTimerEvent, timerId); | 
| 508 |     timerReceiver.gotTimerEvent = -1; | 
| 509 |  | 
| 510 |     // but not if we exclude timers | 
| 511 |     eventLoop.processEvents(flags: QEventLoop::X11ExcludeTimers); | 
| 512 |  | 
| 513 | #if defined(Q_OS_UNIX) | 
| 514 |     QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); | 
| 515 |     if (!qobject_cast<QEventDispatcherUNIX *>(object: eventDispatcher) | 
| 516 |   #if defined(HAVE_GLIB) | 
| 517 |         && !qobject_cast<QEventDispatcherGlib *>(object: eventDispatcher) | 
| 518 |   #endif | 
| 519 |         ) | 
| 520 | #endif | 
| 521 |         QEXPECT_FAIL("" , "X11ExcludeTimers only supported in the UNIX/Glib dispatchers" , Continue); | 
| 522 |  | 
| 523 |     QCOMPARE(timerReceiver.gotTimerEvent, -1); | 
| 524 |     timerReceiver.gotTimerEvent = -1; | 
| 525 |  | 
| 526 |     // resume timer processing | 
| 527 |     eventLoop.processEvents(); | 
| 528 |     QCOMPARE(timerReceiver.gotTimerEvent, timerId); | 
| 529 |     timerReceiver.gotTimerEvent = -1; | 
| 530 | } | 
| 531 |  | 
| 532 | namespace DeliverInDefinedOrder { | 
| 533 |     enum { NbThread = 3,  NbObject = 500, NbEventQueue = 5, NbEvent = 50 }; | 
| 534 |  | 
| 535 |     struct CustomEvent : public QEvent { | 
| 536 |         CustomEvent(int q, int v) : QEvent(Type(User + q)), value(v) {} | 
| 537 |         int value; | 
| 538 |     }; | 
| 539 |  | 
| 540 |     struct Object : public QObject { | 
| 541 |         Q_OBJECT | 
| 542 |     public: | 
| 543 |         Object() : count(0) { | 
| 544 |             for (int i = 0; i < NbEventQueue;  i++) | 
| 545 |                 lastReceived[i] = -1; | 
| 546 |         } | 
| 547 |         int lastReceived[NbEventQueue]; | 
| 548 |         int count; | 
| 549 |         virtual void customEvent(QEvent* e) { | 
| 550 |             QVERIFY(e->type() >= QEvent::User); | 
| 551 |             QVERIFY(e->type() < QEvent::User + 5); | 
| 552 |             uint idx = e->type() - QEvent::User; | 
| 553 |             int value = static_cast<CustomEvent *>(e)->value; | 
| 554 |             QVERIFY(lastReceived[idx] < value); | 
| 555 |             lastReceived[idx] = value; | 
| 556 |             count++; | 
| 557 |         } | 
| 558 |  | 
| 559 |     public slots: | 
| 560 |         void moveToThread(QThread *t) { | 
| 561 |             QObject::moveToThread(thread: t); | 
| 562 |         } | 
| 563 |     }; | 
| 564 |  | 
| 565 | } | 
| 566 |  | 
| 567 | void tst_QEventLoop::deliverInDefinedOrder() | 
| 568 | { | 
| 569 |     using namespace DeliverInDefinedOrder; | 
| 570 |     qMetaTypeId<QThread*>(); | 
| 571 |     QThread threads[NbThread]; | 
| 572 |     Object objects[NbObject]; | 
| 573 |     for (int t = 0; t < NbThread; t++) { | 
| 574 |         threads[t].start(); | 
| 575 |     } | 
| 576 |  | 
| 577 |     int event = 0; | 
| 578 |  | 
| 579 |     for (int o = 0; o < NbObject; o++) { | 
| 580 |         objects[o].moveToThread(t: &threads[o % NbThread]); | 
| 581 |         for (int e = 0; e < NbEvent; e++) { | 
| 582 |             int q = e % NbEventQueue; | 
| 583 |             QCoreApplication::postEvent(receiver: &objects[o], event: new CustomEvent(q, ++event) , priority: q); | 
| 584 |             if (e % 7) | 
| 585 |                 QMetaObject::invokeMethod(obj: &objects[o], member: "moveToThread" , type: Qt::QueuedConnection, Q_ARG(QThread*, &threads[(e+o)%NbThread])); | 
| 586 |         } | 
| 587 |     } | 
| 588 |  | 
| 589 |     for (int o = 0; o < NbObject; o++) { | 
| 590 |         QTRY_COMPARE(objects[o].count, int(NbEvent)); | 
| 591 |     } | 
| 592 |  | 
| 593 |     for (int t = 0; t < NbThread; t++) { | 
| 594 |         threads[t].quit(); | 
| 595 |         threads[t].wait(); | 
| 596 |     } | 
| 597 |  | 
| 598 | } | 
| 599 |  | 
| 600 | class JobObject : public QObject | 
| 601 | { | 
| 602 |     Q_OBJECT | 
| 603 | public: | 
| 604 |  | 
| 605 |     explicit JobObject(QEventLoop *loop, QObject *parent = 0) | 
| 606 |         : QObject(parent), locker(loop) | 
| 607 |     { | 
| 608 |     } | 
| 609 |  | 
| 610 |     explicit JobObject(QObject *parent = 0) | 
| 611 |         : QObject(parent) | 
| 612 |     { | 
| 613 |     } | 
| 614 |  | 
| 615 | public slots: | 
| 616 |     void start(int timeout = 200) | 
| 617 |     { | 
| 618 |         QTimer::singleShot(msec: timeout, receiver: this, SLOT(timeout())); | 
| 619 |     } | 
| 620 |  | 
| 621 | private slots: | 
| 622 |     void timeout() | 
| 623 |     { | 
| 624 |         emit done(); | 
| 625 |         deleteLater(); | 
| 626 |     } | 
| 627 |  | 
| 628 | signals: | 
| 629 |     void done(); | 
| 630 |  | 
| 631 | private: | 
| 632 |     QEventLoopLocker locker; | 
| 633 | }; | 
| 634 |  | 
| 635 | void tst_QEventLoop::testQuitLock() | 
| 636 | { | 
| 637 |     QEventLoop eventLoop; | 
| 638 |  | 
| 639 |     QEventLoopPrivate* privateClass = static_cast<QEventLoopPrivate*>(QObjectPrivate::get(o: &eventLoop)); | 
| 640 |  | 
| 641 |     QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0); | 
| 642 |  | 
| 643 |     JobObject *job1 = new JobObject(&eventLoop, this); | 
| 644 |     job1->start(timeout: 500); | 
| 645 |  | 
| 646 |     QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 1); | 
| 647 |  | 
| 648 |     eventLoop.exec(); | 
| 649 |  | 
| 650 |     QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0); | 
| 651 |  | 
| 652 |  | 
| 653 |     job1 = new JobObject(&eventLoop, this); | 
| 654 |     job1->start(timeout: 200); | 
| 655 |  | 
| 656 |     JobObject *previousJob = job1; | 
| 657 |     for (int i = 0; i < 9; ++i) { | 
| 658 |         JobObject *subJob = new JobObject(&eventLoop, this); | 
| 659 |         connect(sender: previousJob, SIGNAL(done()), receiver: subJob, SLOT(start())); | 
| 660 |         previousJob = subJob; | 
| 661 |     } | 
| 662 |  | 
| 663 |     eventLoop.exec(); | 
| 664 | } | 
| 665 |  | 
| 666 | QTEST_MAIN(tst_QEventLoop) | 
| 667 | #include "tst_qeventloop.moc" | 
| 668 |  |