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:
parent
b07e5fcd1b
commit
d292f0143f
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user