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
50class EventLoopExiter : public QObject
51{
52 Q_OBJECT
53 QEventLoop *eventLoop;
54public:
55 inline EventLoopExiter(QEventLoop *el)
56 : eventLoop(el)
57 { }
58public slots:
59 void exit();
60 void exit1();
61 void exit2();
62};
63
64void EventLoopExiter::exit()
65{ eventLoop->exit(); }
66
67void EventLoopExiter::exit1()
68{ eventLoop->exit(returnCode: 1); }
69
70void EventLoopExiter::exit2()
71{ eventLoop->exit(returnCode: 2); }
72
73class EventLoopThread : public QThread
74{
75 Q_OBJECT
76signals:
77 void checkPoint();
78public:
79 QEventLoop *eventLoop;
80 void run();
81};
82
83void EventLoopThread::run()
84{
85 eventLoop = new QEventLoop;
86 emit checkPoint();
87 (void) eventLoop->exec();
88 delete eventLoop;
89 eventLoop = 0;
90}
91
92class MultipleExecThread : public QThread
93{
94 Q_OBJECT
95signals:
96 void checkPoint();
97public:
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
126class StartStopEvent: public QEvent
127{
128public:
129 explicit StartStopEvent(int type, QEventLoop *loop = 0)
130 : QEvent(Type(type)), el(loop)
131 { }
132
133 QEventLoop *el;
134};
135
136class EventLoopExecutor : public QObject
137{
138 Q_OBJECT
139 QEventLoop *eventLoop;
140public:
141 int returnCode;
142 EventLoopExecutor(QEventLoop *eventLoop)
143 : QObject(), eventLoop(eventLoop), returnCode(-42)
144 {
145 }
146public 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
160class tst_QEventLoop : public QObject
161{
162 Q_OBJECT
163private 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
182protected:
183 void customEvent(QEvent *e);
184};
185
186void 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
231void 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
293void 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
306void 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
316void 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
340void 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
351void 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
361void 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)
373class SocketEventsTester: public QObject
374{
375 Q_OBJECT
376public:
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;
411public 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
444class SocketTestThread : public QThread
445{
446 Q_OBJECT
447public:
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
466void 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
483class TimerReceiver : public QObject
484{
485public:
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
498void 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
532namespace 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
567void 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
600class JobObject : public QObject
601{
602 Q_OBJECT
603public:
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
615public slots:
616 void start(int timeout = 200)
617 {
618 QTimer::singleShot(msec: timeout, receiver: this, SLOT(timeout()));
619 }
620
621private slots:
622 void timeout()
623 {
624 emit done();
625 deleteLater();
626 }
627
628signals:
629 void done();
630
631private:
632 QEventLoopLocker locker;
633};
634
635void 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
666QTEST_MAIN(tst_QEventLoop)
667#include "tst_qeventloop.moc"
668

source code of qtbase/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp