Add more tests for event dispatcher waking up

Add two tests for some problematic scenarios where the behavior is not
consistent across platforms and depending on which event dispatcher is
used:

1) reliably waking up the dispatcher when posting events from a worker
thread.

That test fails 100% of the time on Windows no matter what type of
application is created. It passes reliably on Linux and macOS for both
core and gui applications.

2) waking up the dispatcher when we post an event from within an
event handler.

That test fails 100% of the time on Windows, both with core
and GUI event dispatchers. On macOS, the test fails 100% of the time
with the core dispatcher, and passes 100% of the time with the GUI
dispatcher. On Linux, it passes only if a Glib based event dispatcher
is used; the default Unix event dispatcher (which is also the one
used on macOS for core applications) fails.

Task-number: QTBUG-99323
Pick-to: 6.2 6.3 5.15
Change-Id: I2489533b9f0032488707777be0512bb933669a7d
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Alex Trotsenko <alex1973tr@gmail.com>
This commit is contained in:
Volker Hilsheimer 2022-01-06 14:51:42 +01:00
parent b07e5fcd1b
commit d292f0143f

View File

@ -35,6 +35,7 @@
#include <QTest>
#include <QAbstractEventDispatcher>
#include <QTimer>
#include <QThreadPool>
enum {
PreciseTimerInterval = 10,
@ -58,20 +59,29 @@ protected:
public:
inline tst_QEventDispatcher()
: QObject(),
eventDispatcher(QAbstractEventDispatcher::instance(thread()))
eventDispatcher(QAbstractEventDispatcher::instance(thread())),
isGuiEventDispatcher(QCoreApplication::instance()->inherits("QGuiApplication"))
{ }
private slots:
void initTestCase();
void registerTimer();
/* void registerSocketNotifier(); */ // Not implemented here, see tst_QSocketNotifier instead
/* void registerEventNotifiier(); */ // Not implemented here, see tst_QWinEventNotifier instead
void sendPostedEvents_data();
void sendPostedEvents();
void processEventsOnlySendsQueuedEvents();
// these two tests need to run before postedEventsPingPong
void postEventFromThread();
void postEventFromEventHandler();
// these tests don't leave the event dispatcher in a reliable state
void postedEventsPingPong();
void eventLoopExit();
void interruptTrampling();
private:
const bool isGuiEventDispatcher;
};
bool tst_QEventDispatcher::event(QEvent *e)
@ -353,6 +363,106 @@ void tst_QEventDispatcher::processEventsOnlySendsQueuedEvents()
QCOMPARE(object.eventsReceived, 4);
}
void tst_QEventDispatcher::postEventFromThread()
{
QThreadPool *threadPool = QThreadPool::globalInstance();
QAtomicInt hadToQuit = false;
QAtomicInt done = false;
threadPool->start([&]{
int loop = 1000 / 10; // give it a second
while (!done && --loop)
QThread::msleep(10);
if (done)
return;
hadToQuit = true;
QCoreApplication::eventDispatcher()->wakeUp();
});
struct EventReceiver : public QObject {
bool event(QEvent* event) override {
if (event->type() == QEvent::User)
return true;
return QObject::event(event);
}
} receiver;
int count = 500;
while (!hadToQuit && --count) {
threadPool->start([&receiver]{
QCoreApplication::postEvent(&receiver, new QEvent(QEvent::User));
});
QAbstractEventDispatcher::instance()->processEvents(QEventLoop::WaitForMoreEvents);
}
done = true;
if (QAbstractEventDispatcher::instance()->inherits("QEventDispatcherWin32"))
QEXPECT_FAIL("", QAbstractEventDispatcher::instance()->metaObject()->className(), Continue);
QVERIFY(!hadToQuit);
QVERIFY(threadPool->waitForDone());
}
void tst_QEventDispatcher::postEventFromEventHandler()
{
QThreadPool *threadPool = QThreadPool::globalInstance();
QAtomicInt hadToQuit = false;
QAtomicInt done = false;
threadPool->start([&]{
int loop = 250 / 10; // give it 250ms
while (!done && --loop)
QThread::msleep(10);
if (done)
return;
hadToQuit = true;
QCoreApplication::eventDispatcher()->wakeUp();
});
struct EventReceiver : public QObject {
int i = 0;
bool event(QEvent* event) override
{
if (event->type() == QEvent::User) {
++i;
if (i < 2)
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
return true;
}
return QObject::event(event);
}
} receiver;
QCoreApplication::postEvent(&receiver, new QEvent(QEvent::User));
while (receiver.i < 2)
QAbstractEventDispatcher::instance()->processEvents(QEventLoop::WaitForMoreEvents);
done = true;
bool coreFails = false;
bool guiFails = false;
const QByteArrayView eventDispatcherName(QAbstractEventDispatcher::instance()->metaObject()->className());
#if defined(Q_OS_DARWIN)
coreFails = true;
#elif defined(Q_OS_WINDOWS)
coreFails = true;
guiFails = true;
#elif defined(Q_OS_LINUX)
// QXcbUnixEventDispatcher and QEventDispatcherUNIX do not do this correctly
// QXcbGlibEventDispatcher and QEventDispatcherGlib do
coreFails = !eventDispatcherName.contains("Glib");
guiFails = !eventDispatcherName.contains("Glib");
#endif
if (coreFails && !isGuiEventDispatcher)
QEXPECT_FAIL("", eventDispatcherName.constData(), Continue);
if (guiFails && isGuiEventDispatcher)
QEXPECT_FAIL("", eventDispatcherName.constData(), Continue);
QVERIFY(!hadToQuit);
QVERIFY(threadPool->waitForDone());
}
void tst_QEventDispatcher::postedEventsPingPong()
{
QEventLoop mainLoop;