diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp | 285 |
1 files changed, 200 insertions, 85 deletions
diff --git a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp index 21aee94e5d..285d080960 100644 --- a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp +++ b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp @@ -1,46 +1,49 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifdef QT_GUI_LIB # include <QtGui/QGuiApplication> -# define tst_QEventDispatcher tst_QGuiEventDispatcher #else # include <QtCore/QCoreApplication> #endif #include <QTest> #include <QAbstractEventDispatcher> #include <QTimer> +#include <QThreadPool> -enum { - PreciseTimerInterval = 10, - CoarseTimerInterval = 200, - VeryCoarseTimerInterval = 1000 -}; +#ifdef DISABLE_GLIB +static bool glibDisabled = []() { + qputenv("QT_NO_GLIB", "1"); + return true; +}(); +#endif + +#include <chrono> + +using namespace std::chrono_literals; + +static constexpr auto PreciseTimerInterval = 10ms; +static constexpr auto CoarseTimerInterval = 200ms; +static constexpr auto VeryCoarseTimerInterval = 1s; + +static constexpr +std::chrono::nanoseconds fudgeInterval(std::chrono::nanoseconds interval, Qt::TimerType timerType) +{ + // Make the intervals have have fractions of milliseconds so we can check + // that they have been rounded & stored properly (where applicable). + switch (timerType) { + case Qt::VeryCoarseTimer: + // rounds down (floor) to seconds + return interval + 1010us; + case Qt::CoarseTimer: + // rounds up (ceil) to milliseconds + return interval - 10us; + case Qt::PreciseTimer: + // not rounded using QAbstractEventDispatcherV2; rounded up (ceil) on V1 + return interval - 10us; + } + Q_UNREACHABLE_RETURN(std::chrono::nanoseconds::min()); +} class tst_QEventDispatcher : public QObject { @@ -58,20 +61,31 @@ protected: public: inline tst_QEventDispatcher() : QObject(), - eventDispatcher(QAbstractEventDispatcher::instance(thread())) + eventDispatcher(QAbstractEventDispatcher::instance(thread())), + isGuiEventDispatcher(QCoreApplication::instance()->inherits("QGuiApplication")) { } private slots: void initTestCase(); + void cleanup(); + 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) @@ -94,11 +108,15 @@ bool tst_QEventDispatcher::event(QEvent *e) // drain the system event queue after the test starts to avoid destabilizing the test functions void tst_QEventDispatcher::initTestCase() { - QElapsedTimer elapsedTimer; - elapsedTimer.start(); - while (!elapsedTimer.hasExpired(CoarseTimerInterval) && eventDispatcher->processEvents(QEventLoop::AllEvents)) { - ; - } + QDeadlineTimer deadline(CoarseTimerInterval); + while (!deadline.hasExpired() && eventDispatcher->processEvents(QEventLoop::AllEvents)) + ; +} + +// consume pending posted events to avoid impact on the next test function +void tst_QEventDispatcher::cleanup() +{ + eventDispatcher->processEvents(QEventLoop::AllEvents); } class TimerManager { @@ -119,35 +137,36 @@ public: TimerManager(TimerManager &&) = delete; TimerManager &operator=(TimerManager &&) = delete; - int preciseTimerId() const { return m_preciseTimerId; } - int coarseTimerId() const { return m_coarseTimerId; } - int veryCoarseTimerId() const { return m_veryCoarseTimerId; } + int preciseTimerId() const { return int(m_preciseTimerId); } + int coarseTimerId() const { return int(m_coarseTimerId); } + int veryCoarseTimerId() const { return int(m_veryCoarseTimerId); } - bool foundPrecise() const { return m_preciseTimerId > 0; } - bool foundCoarse() const { return m_coarseTimerId > 0; } - bool foundVeryCoarse() const { return m_veryCoarseTimerId > 0; } + bool foundPrecise() const { return preciseTimerId() > 0; } + bool foundCoarse() const { return coarseTimerId() > 0; } + bool foundVeryCoarse() const { return veryCoarseTimerId() > 0; } - QList<QAbstractEventDispatcher::TimerInfo> registeredTimers() const + QList<QAbstractEventDispatcher::TimerInfoV2> registeredTimers() const { - return m_eventDispatcher->registeredTimers(m_parent); + return m_eventDispatcher->timersForObject(m_parent); } void registerAll() { // start 3 timers, each with the different timer types and different intervals - m_preciseTimerId = m_eventDispatcher->registerTimer( - PreciseTimerInterval, Qt::PreciseTimer, m_parent); - m_coarseTimerId = m_eventDispatcher->registerTimer( - CoarseTimerInterval, Qt::CoarseTimer, m_parent); - m_veryCoarseTimerId = m_eventDispatcher->registerTimer( - VeryCoarseTimerInterval, Qt::VeryCoarseTimer, m_parent); - QVERIFY(m_preciseTimerId > 0); - QVERIFY(m_coarseTimerId > 0); - QVERIFY(m_veryCoarseTimerId > 0); + auto registerTimer = [&](std::chrono::milliseconds interval, Qt::TimerType timerType) { + return m_eventDispatcher->registerTimer(fudgeInterval(interval, timerType), timerType, + m_parent); + }; + m_preciseTimerId = registerTimer(PreciseTimerInterval, Qt::PreciseTimer); + m_coarseTimerId = registerTimer(CoarseTimerInterval, Qt::CoarseTimer); + m_veryCoarseTimerId = registerTimer(VeryCoarseTimerInterval, Qt::VeryCoarseTimer); + QVERIFY(foundPrecise()); + QVERIFY(foundCoarse()); + QVERIFY(foundVeryCoarse()); findTimers(); } - void unregister(int timerId) + void unregister(Qt::TimerId timerId) { m_eventDispatcher->unregisterTimer(timerId); findTimers(); @@ -165,36 +184,43 @@ private: bool foundPrecise = false; bool foundCoarse = false; bool foundVeryCoarse = false; - const QList<QAbstractEventDispatcher::TimerInfo> timers = registeredTimers(); - for (int i = 0; i < timers.count(); ++i) { - const QAbstractEventDispatcher::TimerInfo &timerInfo = timers.at(i); + const QList<QAbstractEventDispatcher::TimerInfoV2> timers = registeredTimers(); + for (const QAbstractEventDispatcher::TimerInfoV2 &timerInfo : timers) { if (timerInfo.timerId == m_preciseTimerId) { - QCOMPARE(timerInfo.interval, int(PreciseTimerInterval)); + // For precise timers, we expect the fudge factor to be present + QAbstractEventDispatcher::Duration interval = + fudgeInterval(PreciseTimerInterval, Qt::PreciseTimer); +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + if (!qobject_cast<QAbstractEventDispatcherV2 *>(m_eventDispatcher)) + interval = PreciseTimerInterval; +#endif + QCOMPARE(timerInfo.interval, interval); QCOMPARE(timerInfo.timerType, Qt::PreciseTimer); foundPrecise = true; } else if (timerInfo.timerId == m_coarseTimerId) { - QCOMPARE(timerInfo.interval, int(CoarseTimerInterval)); + // For other timers, the fudge factors ought to have been rounded away + QCOMPARE(timerInfo.interval, CoarseTimerInterval); QCOMPARE(timerInfo.timerType, Qt::CoarseTimer); foundCoarse = true; } else if (timerInfo.timerId == m_veryCoarseTimerId) { - QCOMPARE(timerInfo.interval, int(VeryCoarseTimerInterval)); + QCOMPARE(timerInfo.interval, VeryCoarseTimerInterval); QCOMPARE(timerInfo.timerType, Qt::VeryCoarseTimer); foundVeryCoarse = true; } } if (!foundPrecise) - m_preciseTimerId = -1; + m_preciseTimerId = Qt::TimerId::Invalid; if (!foundCoarse) - m_coarseTimerId = -1; + m_coarseTimerId = Qt::TimerId::Invalid; if (!foundVeryCoarse) - m_veryCoarseTimerId = -1; + m_veryCoarseTimerId = Qt::TimerId::Invalid; } QAbstractEventDispatcher *m_eventDispatcher = nullptr; - int m_preciseTimerId = -1; - int m_coarseTimerId = -1; - int m_veryCoarseTimerId = -1; + Qt::TimerId m_preciseTimerId = Qt::TimerId::Invalid; + Qt::TimerId m_coarseTimerId = Qt::TimerId::Invalid; + Qt::TimerId m_veryCoarseTimerId = Qt::TimerId::Invalid; QObject *m_parent = nullptr; }; @@ -208,7 +234,7 @@ void tst_QEventDispatcher::registerTimer() return; // check that all 3 are present in the eventDispatcher's registeredTimer() list - QCOMPARE(timers.registeredTimers().count(), 3); + QCOMPARE(timers.registeredTimers().size(), 3); QVERIFY(timers.foundPrecise()); QVERIFY(timers.foundCoarse()); QVERIFY(timers.foundVeryCoarse()); @@ -234,16 +260,16 @@ void tst_QEventDispatcher::registerTimer() if (doubleTimer) QSKIP("Double timer during a single timeout - aborting test as flaky on macOS"); if (timerIdFromEvent != timers.preciseTimerId() - && elapsedTimer.elapsed() > PreciseTimerInterval * 3) + && elapsedTimer.durationElapsed() > PreciseTimerInterval * 3) QSKIP("Ignore flaky test behavior due to VM scheduling on macOS"); #endif QCOMPARE(timerIdFromEvent, timers.preciseTimerId()); // now unregister it and make sure it's gone - timers.unregister(timers.preciseTimerId()); + timers.unregister(Qt::TimerId(timers.preciseTimerId())); if (QTest::currentTestFailed()) return; - QCOMPARE(timers.registeredTimers().count(), 2); + QCOMPARE(timers.registeredTimers().size(), 2); QVERIFY(!timers.foundPrecise()); QVERIFY(timers.foundCoarse()); QVERIFY(timers.foundVeryCoarse()); @@ -258,16 +284,16 @@ void tst_QEventDispatcher::registerTimer() if (doubleTimer) QSKIP("Double timer during a single timeout - aborting test as flaky on macOS"); if (timerIdFromEvent != timers.coarseTimerId() - && elapsedTimer.elapsed() > CoarseTimerInterval * 3) + && elapsedTimer.durationElapsed() > CoarseTimerInterval * 3) QSKIP("Ignore flaky test behavior due to VM scheduling on macOS"); #endif QCOMPARE(timerIdFromEvent, timers.coarseTimerId()); // now unregister it and make sure it's gone - timers.unregister(timers.coarseTimerId()); + timers.unregister(Qt::TimerId(timers.coarseTimerId())); if (QTest::currentTestFailed()) return; - QCOMPARE(timers.registeredTimers().count(), 1); + QCOMPARE(timers.registeredTimers().size(), 1); QVERIFY(!timers.foundPrecise()); QVERIFY(!timers.foundCoarse()); QVERIFY(timers.foundVeryCoarse()); @@ -293,9 +319,8 @@ void tst_QEventDispatcher::sendPostedEvents() QFETCH(int, processEventsFlagsInt); QEventLoop::ProcessEventsFlags processEventsFlags = QEventLoop::ProcessEventsFlags(processEventsFlagsInt); - QElapsedTimer elapsedTimer; - elapsedTimer.start(); - while (!elapsedTimer.hasExpired(200)) { + QDeadlineTimer deadline(200ms); + while (!deadline.hasExpired()) { receivedEventType = -1; QCoreApplication::postEvent(this, new QEvent(QEvent::User)); @@ -353,6 +378,96 @@ 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::sleep(std::chrono::milliseconds{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; + + 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::sleep(std::chrono::milliseconds{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; + + const QByteArrayView eventDispatcherName(QAbstractEventDispatcher::instance()->metaObject()->className()); + qDebug() << eventDispatcherName; + // QXcbUnixEventDispatcher and QEventDispatcherUNIX do not do this correctly on any platform; + // both Windows event dispatchers fail as well. + const bool knownToFail = eventDispatcherName.contains("UNIX") + || eventDispatcherName.contains("Unix") + || eventDispatcherName.contains("Win32") + || eventDispatcherName.contains("WindowsGui") + || eventDispatcherName.contains("Android"); + + if (knownToFail) + QEXPECT_FAIL("", eventDispatcherName.constData(), Continue); + + QVERIFY(!hadToQuit); + QVERIFY(threadPool->waitForDone()); +} + + void tst_QEventDispatcher::postedEventsPingPong() { QEventLoop mainLoop; @@ -371,7 +486,7 @@ void tst_QEventDispatcher::postedEventsPingPong() // We should use Qt::CoarseTimer on Windows, to prevent event // dispatcher from sending a posted event. - QTimer::singleShot(500, Qt::CoarseTimer, [&mainLoop]() { + QTimer::singleShot(500, Qt::CoarseTimer, &mainLoop, [&mainLoop]() { mainLoop.exit(1); }); @@ -388,12 +503,12 @@ void tst_QEventDispatcher::eventLoopExit() // Imitates QApplication::exec(): QEventLoop mainLoop; // The test itself is a lambda: - QTimer::singleShot(0, [&mainLoop]() { + QTimer::singleShot(0, &mainLoop, [&mainLoop]() { // Two more single shots, both will be posted as events // (zero timeout) and supposed to be processes by the // mainLoop: - QTimer::singleShot(0, [&mainLoop]() { + QTimer::singleShot(0, &mainLoop, [&mainLoop]() { // wakeUp triggers QCocoaEventDispatcher into incrementing // its 'serialNumber': mainLoop.wakeUp(); @@ -402,7 +517,7 @@ void tst_QEventDispatcher::eventLoopExit() QCoreApplication::processEvents(); }); - QTimer::singleShot(0, [&mainLoop]() { + QTimer::singleShot(0, &mainLoop, [&mainLoop]() { // With QCocoaEventDispatcher this is executed while in the // processEvents (see above) and would fail to actually // interrupt the loop. @@ -411,7 +526,7 @@ void tst_QEventDispatcher::eventLoopExit() }); bool timeoutObserved = false; - QTimer::singleShot(500, [&timeoutObserved, &mainLoop]() { + QTimer::singleShot(500, &mainLoop, [&timeoutObserved, &mainLoop]() { // In case the QEventLoop::exit above failed, we have to bail out // early, not wasting time: mainLoop.exit(); @@ -434,7 +549,7 @@ void tst_QEventDispatcher::interruptTrampling() auto dispatcher = eventDispatcher(); QVERIFY(dispatcher); dispatcher->processEvents(QEventLoop::AllEvents); - QTimer::singleShot(0, [dispatcher]() { + QTimer::singleShot(0, dispatcher, [dispatcher]() { dispatcher->wakeUp(); }); dispatcher->processEvents(QEventLoop::WaitForMoreEvents); |