diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp | 514 |
1 files changed, 435 insertions, 79 deletions
diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index 1db210e0bd..40190ca465 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -1,49 +1,46 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** 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) 2020 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +/* WARNING: this source-code is reused by another test. + + As Qt built with GUI support may use a different backend for its event loops + and other timer-related matters, it is important to test it in that form, as + well as in its GUI-less form. So this source file is reused by a build config + in the GUI module. Similarly, testing with and without glib is supported, + where relevant (see DISABLE_GLIB below). +*/ #ifdef QT_GUI_LIB +// When compiled as tests/auto/gui/kernel/qguitimer/'s source-code: # include <QtGui/QGuiApplication> #else +// When compiled as tests/auto/corelib/kernel/qtimer/'s source-code: # include <QtCore/QCoreApplication> #endif #include <QtCore/private/qglobal_p.h> -#include <QtTest/QtTest> +#include <QTest> +#include <QSignalSpy> +#include <QtTest/private/qpropertytesthelper_p.h> #include <qtimer.h> #include <qthread.h> #include <qelapsedtimer.h> +#include <qproperty.h> #if defined Q_OS_UNIX #include <unistd.h> #endif +#ifdef DISABLE_GLIB +static bool glibDisabled = []() { + qputenv("QT_NO_GLIB", "1"); + return true; +}(); +#endif + +using namespace std::chrono_literals; + class tst_QTimer : public QObject { Q_OBJECT @@ -55,6 +52,12 @@ private slots: void zeroTimer(); void singleShotTimeout(); void timeout(); + void singleShotNormalizes_data(); + void singleShotNormalizes(); + void sequentialTimers_data(); + void sequentialTimers(); + void singleShotSequentialTimers_data(); + void singleShotSequentialTimers(); void remainingTime(); void remainingTimeInitial_data(); void remainingTimeInitial(); @@ -79,6 +82,7 @@ private slots: void singleShotToFunctors(); void singleShot_chrono(); void singleShot_static(); + void crossThreadSingleShotToFunctor_data(); void crossThreadSingleShotToFunctor(); void timerOrder(); void timerOrder_data(); @@ -88,33 +92,46 @@ private slots: void dontBlockEvents(); void postedEventsShouldNotStarveTimers(); void callOnTimeout(); + + void bindToTimer(); + void bindTimer(); + void automatedBindingTests(); + + void negativeInterval(); + void testTimerId(); }; void tst_QTimer::zeroTimer() { QTimer timer; + QVERIFY(!timer.isSingleShot()); timer.setInterval(0); + timer.setSingleShot(true); + QVERIFY(timer.isSingleShot()); QSignalSpy timeoutSpy(&timer, &QTimer::timeout); timer.start(); - QCoreApplication::processEvents(); + // Pass timeout to work round glib issue, see QTBUG-84291. + QCoreApplication::processEvents(QEventLoop::AllEvents, INT_MAX); - QCOMPARE(timeoutSpy.count(), 1); + QCOMPARE(timeoutSpy.size(), 1); } void tst_QTimer::singleShotTimeout() { QTimer timer; + QVERIFY(!timer.isSingleShot()); timer.setSingleShot(true); + QVERIFY(timer.isSingleShot()); QSignalSpy timeoutSpy(&timer, &QTimer::timeout); timer.start(100); QVERIFY(timeoutSpy.wait(500)); - QCOMPARE(timeoutSpy.count(), 1); + QCOMPARE(timeoutSpy.size(), 1); QTest::qWait(500); - QCOMPARE(timeoutSpy.count(), 1); + QCOMPARE(timeoutSpy.size(), 1); } #define TIMEOUT_TIMEOUT 200 @@ -125,12 +142,147 @@ void tst_QTimer::timeout() QSignalSpy timeoutSpy(&timer, &QTimer::timeout); timer.start(100); - QCOMPARE(timeoutSpy.count(), 0); + QCOMPARE(timeoutSpy.size(), 0); + + QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 0, TIMEOUT_TIMEOUT); + int oldCount = timeoutSpy.size(); + + QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, TIMEOUT_TIMEOUT); +} + +void tst_QTimer::singleShotNormalizes_data() +{ + QTest::addColumn<QByteArray>("slotName"); + + QTest::newRow("normalized") << QByteArray(SLOT(exitLoop())); + + QTest::newRow("space-before") << QByteArray(SLOT( exitLoop())); + QTest::newRow("space-after") << QByteArray(SLOT(exitLoop ())); + QTest::newRow("space-around") << QByteArray(SLOT( exitLoop ())); + QTest::newRow("spaces-before") << QByteArray(SLOT( exitLoop())); + QTest::newRow("spaces-after") << QByteArray(SLOT(exitLoop ())); + QTest::newRow("spaces-around") << QByteArray(SLOT( exitLoop ())); + + QTest::newRow("space-in-parens") << QByteArray(SLOT(exitLoop( ))); + QTest::newRow("spaces-in-parens") << QByteArray(SLOT(exitLoop( ))); + QTest::newRow("space-after-parens") << QByteArray(SLOT(exitLoop() )); + QTest::newRow("spaces-after-parens") << QByteArray(SLOT(exitLoop() )); +} + +void tst_QTimer::singleShotNormalizes() +{ + static constexpr auto TestTimeout = 250ms; + QFETCH(QByteArray, slotName); + QEventLoop loop; + + // control test: regular connection + { + QTimer timer; + QVERIFY(QObject::connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), slotName)); + timer.setSingleShot(true); + timer.start(1); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + + // non-zero time + QTimer::singleShot(1, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QTimer::singleShot(1ms, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // zero time + QTimer::singleShot(0, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QTimer::singleShot(0ms, &QTestEventLoop::instance(), slotName); + QTestEventLoop::instance().enterLoop(TestTimeout); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_QTimer::sequentialTimers_data() +{ +#ifdef Q_OS_WIN + QSKIP("The API used by QEventDispatcherWin32 doesn't respect the order"); +#endif + QTest::addColumn<QList<int>>("timeouts"); + auto addRow = [](const QList<int> &l) { + QByteArray name; + int last = -1; + for (int i = 0; i < l.size(); ++i) { + Q_ASSERT_X(l[i] >= last, "tst_QTimer", "input list must be sorted"); + name += QByteArray::number(l[i]) + ','; + } + name.chop(1); + QTest::addRow("%s", name.constData()) << l; + }; + // PreciseTimers + addRow({0, 0, 0, 0, 0, 0}); + addRow({0, 1, 2}); + addRow({1, 1, 1, 2, 2, 2, 2}); + addRow({1, 2, 3}); + addRow({19, 19, 19}); + // CoarseTimer for setInterval + addRow({20, 20, 20, 20, 20}); + addRow({25, 25, 25, 25, 25, 25, 50}); +} + +void tst_QTimer::sequentialTimers() +{ + QFETCH(const QList<int>, timeouts); + QByteArray result, expected; + std::vector<std::unique_ptr<QTimer>> timers; + expected.resize(timeouts.size()); + result.reserve(timeouts.size()); + timers.reserve(timeouts.size()); + for (int i = 0; i < timeouts.size(); ++i) { + auto timer = std::make_unique<QTimer>(); + timer->setSingleShot(true); + timer->setInterval(timeouts[i]); + + char c = 'A' + i; + expected[i] = c; + QObject::connect(timer.get(), &QTimer::timeout, this, [&result, c = c]() { + result.append(c); + }); + timers.push_back(std::move(timer)); + } + + // start the timers + for (auto &timer : timers) + timer->start(); + + QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10); + + QCOMPARE(result, expected); +} + +void tst_QTimer::singleShotSequentialTimers_data() +{ + sequentialTimers_data(); +} - QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.count() > 0, TIMEOUT_TIMEOUT); - int oldCount = timeoutSpy.count(); +void tst_QTimer::singleShotSequentialTimers() +{ + QFETCH(const QList<int>, timeouts); + QByteArray result, expected; + expected.resize(timeouts.size()); + result.reserve(timeouts.size()); + for (int i = 0; i < timeouts.size(); ++i) { + char c = 'A' + i; + expected[i] = c; + QTimer::singleShot(timeouts[i], this, [&result, c = c]() { + result.append(c); + }); + } + + QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10); - QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.count() > oldCount, TIMEOUT_TIMEOUT); + QCOMPARE(result, expected); } void tst_QTimer::remainingTime() @@ -204,7 +356,9 @@ void tst_QTimer::remainingTimeInitial() QFETCH(Qt::TimerType, timerType); QTimer timer; + QCOMPARE(timer.timerType(), Qt::CoarseTimer); timer.setTimerType(timerType); + QCOMPARE(timer.timerType(), timerType); timer.start(startTimeMs); const int rt = timer.remainingTime(); @@ -252,19 +406,14 @@ void tst_QTimer::remainingTimeDuringActivation() namespace { -#if __has_include(<chrono>) template <typename T> std::chrono::milliseconds to_ms(T t) { return std::chrono::duration_cast<std::chrono::milliseconds>(t); } -#endif } // unnamed namespace void tst_QTimer::basic_chrono() { -#if !__has_include(<chrono>) - QSKIP("This test requires C++11 <chrono> support"); -#else // duplicates zeroTimer, singleShotTimeout, interval and remainingTime using namespace std::chrono; QTimer timer; @@ -276,24 +425,24 @@ void tst_QTimer::basic_chrono() QCoreApplication::processEvents(); - QCOMPARE(timeoutSpy.count(), 1); + QCOMPARE(timeoutSpy.size(), 1); timeoutSpy.clear(); timer.start(milliseconds(100)); - QCOMPARE(timeoutSpy.count(), 0); + QCOMPARE(timeoutSpy.size(), 0); QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT)); - QVERIFY(timeoutSpy.count() > 0); - int oldCount = timeoutSpy.count(); + QVERIFY(timeoutSpy.size() > 0); + int oldCount = timeoutSpy.size(); QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT)); - QVERIFY(timeoutSpy.count() > oldCount); + QVERIFY(timeoutSpy.size() > oldCount); timeoutSpy.clear(); timer.start(to_ms(microseconds(200000))); QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(200)); QTest::qWait(50); - QCOMPARE(timeoutSpy.count(), 0); + QCOMPARE(timeoutSpy.size(), 0); milliseconds rt = timer.remainingTimeAsDuration(); QVERIFY2(rt.count() >= 50 && rt.count() <= 200, qPrintable(QString::number(rt.count()))); @@ -302,10 +451,9 @@ void tst_QTimer::basic_chrono() timer.setSingleShot(true); timer.start(milliseconds(100)); QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT)); - QCOMPARE(timeoutSpy.count(), 1); + QCOMPARE(timeoutSpy.size(), 1); QTest::qWait(500); - QCOMPARE(timeoutSpy.count(), 1); -#endif + QCOMPARE(timeoutSpy.size(), 1); } void tst_QTimer::livelock_data() @@ -530,6 +678,7 @@ void tst_QTimer::moveToThread() #endif QTimer ti1; QTimer ti2; + ti1.setSingleShot(true); ti1.start(MOVETOTHREAD_TIMEOUT); ti2.start(MOVETOTHREAD_TIMEOUT); QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff)); @@ -668,12 +817,10 @@ void tst_QTimer::timerFiresOnlyOncePerProcessEvents() class TimerIdPersistsAfterThreadExitThread : public QThread { public: - QTimer *timer; - int timerId, returnValue; + QTimer *timer = nullptr; + Qt::TimerId timerId = Qt::TimerId::Invalid; + int returnValue = -1; - TimerIdPersistsAfterThreadExitThread() - : QThread(), timer(0), timerId(-1), returnValue(-1) - { } ~TimerIdPersistsAfterThreadExitThread() { delete timer; @@ -685,11 +832,15 @@ public: timer = new QTimer; connect(timer, SIGNAL(timeout()), &eventLoop, SLOT(quit())); timer->start(100); - timerId = timer->timerId(); + timerId = timer->id(); returnValue = eventLoop.exec(); } }; +namespace { +int operator&(Qt::TimerId id, int i) { return qToUnderlying(id) & i; } +} + void tst_QTimer::timerIdPersistsAfterThreadExit() { TimerIdPersistsAfterThreadExitThread thread; @@ -715,6 +866,19 @@ void tst_QTimer::cancelLongTimer() QVERIFY(!timer.isActive()); } +void tst_QTimer::testTimerId() +{ + QTimer timer; + timer.start(100ms); + QVERIFY(timer.isActive()); + QCOMPARE_GT(timer.timerId(), 0); + QCOMPARE_GT(timer.id(), Qt::TimerId::Invalid); + timer.stop(); + QVERIFY(!timer.isActive()); + QCOMPARE(timer.timerId(), -1); + QCOMPARE(timer.id(), Qt::TimerId::Invalid); +} + class TimeoutCounter : public QObject { Q_OBJECT @@ -889,7 +1053,7 @@ void tst_QTimer::singleShotToFunctors() thread.wait(); struct MoveOnly : CountedStruct { - Q_DISABLE_COPY(MoveOnly); + Q_DISABLE_COPY(MoveOnly) MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {}; MoveOnly(int *c) : CountedStruct(c) {} }; @@ -902,9 +1066,6 @@ void tst_QTimer::singleShotToFunctors() void tst_QTimer::singleShot_chrono() { -#if !__has_include(<chrono>) - QSKIP("This test requires C++11 <chrono> support"); -#else // duplicates singleShotStaticFunctionZeroTimeout and singleShotToFunctors using namespace std::chrono; { @@ -941,7 +1102,6 @@ void tst_QTimer::singleShot_chrono() QTRY_COMPARE(count, 3); _e.reset(); -#endif } class DontBlockEvents : public QObject @@ -1023,30 +1183,57 @@ void tst_QTimer::postedEventsShouldNotStarveTimers() timer.start(); SlotRepeater slotRepeater; slotRepeater.repeatThisSlot(); - QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.count() > 5, 100); + QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 5, 100); } struct DummyFunctor { - void operator()() {} + static QThread *callThread; + void operator()() { + callThread = QThread::currentThread(); + callThread->quit(); + } }; +QThread *DummyFunctor::callThread = nullptr; + +void tst_QTimer::crossThreadSingleShotToFunctor_data() +{ + QTest::addColumn<int>("timeout"); + + QTest::addRow("zero-timer") << 0; + QTest::addRow("1ms") << 1; +} void tst_QTimer::crossThreadSingleShotToFunctor() { - // We're testing for crashes here, so the test simply running to - // completion is considered a success - QThread t; - t.start(); + QFETCH(int, timeout); + // We're also testing for crashes here, so the test simply running to + // completion is part of the success + DummyFunctor::callThread = nullptr; - QObject* o = new QObject(); + QThread t; + std::unique_ptr<QObject> o(new QObject()); o->moveToThread(&t); - for (int i = 0; i < 10000; i++) { - QTimer::singleShot(0, o, DummyFunctor()); - } + QTimer::singleShot(timeout, o.get(), DummyFunctor()); - t.quit(); + // wait enough time for the timer to have timed out before the timer + // could be start in the receiver's thread. + QTest::qWait(10 + timeout * 10); + t.start(); t.wait(); - delete o; + QCOMPARE(DummyFunctor::callThread, &t); + + // continue with a stress test - the calling thread is busy, the + // timer should still fire and no crashes. + DummyFunctor::callThread = nullptr; + t.start(); + for (int i = 0; i < 10000; i++) + QTimer::singleShot(timeout, o.get(), DummyFunctor()); + + t.wait(); + o.reset(); + + QCOMPARE(DummyFunctor::callThread, &t); } void tst_QTimer::callOnTimeout() @@ -1066,7 +1253,7 @@ void tst_QTimer::callOnTimeout() QTest::qWait(100); QCOMPARE(count, 2); - QCOMPARE(timeoutSpy.count(), 1); + QCOMPARE(timeoutSpy.size(), 1); // Test that connection is bound to context lifetime QVERIFY(connection); @@ -1074,6 +1261,179 @@ void tst_QTimer::callOnTimeout() QVERIFY(!connection); } +void tst_QTimer::bindToTimer() +{ + QTimer timer; + + // singleShot property + QProperty<bool> singleShot; + singleShot.setBinding(timer.bindableSingleShot().makeBinding()); + QCOMPARE(timer.isSingleShot(), singleShot); + + timer.setSingleShot(true); + QVERIFY(singleShot); + timer.setSingleShot(false); + QVERIFY(!singleShot); + + // interval property + QProperty<int> interval; + interval.setBinding([&](){ return timer.interval(); }); + QCOMPARE(timer.interval(), interval); + + timer.setInterval(10); + QCOMPARE(interval, 10); + timer.setInterval(100); + QCOMPARE(interval, 100); + + // timerType property + QProperty<Qt::TimerType> timerType; + timerType.setBinding(timer.bindableTimerType().makeBinding()); + QCOMPARE(timer.timerType(), timerType); + + timer.setTimerType(Qt::PreciseTimer); + QCOMPARE(timerType, Qt::PreciseTimer); + + timer.setTimerType(Qt::VeryCoarseTimer); + QCOMPARE(timerType, Qt::VeryCoarseTimer); + + // active property + QProperty<bool> active; + active.setBinding([&](){ return timer.isActive(); }); + QCOMPARE(active, timer.isActive()); + + timer.start(1000); + QVERIFY(active); + + timer.stop(); + QVERIFY(!active); + + auto ignoreMsg = [] { + QTest::ignoreMessage(QtWarningMsg, + "QObject::startTimer: Timers cannot have negative intervals"); + }; + + // also test that using negative interval updates the binding correctly + timer.start(100); + QVERIFY(active); + ignoreMsg(); + timer.setInterval(-100); + QVERIFY(!active); + timer.start(100); + QVERIFY(active); + ignoreMsg(); + timer.start(-100); + QVERIFY(!active); +} + +void tst_QTimer::bindTimer() +{ + QTimer timer; + + // singleShot property + QVERIFY(!timer.isSingleShot()); + + QProperty<bool> singleShot; + timer.bindableSingleShot().setBinding(Qt::makePropertyBinding(singleShot)); + + singleShot = true; + QVERIFY(timer.isSingleShot()); + singleShot = false; + QVERIFY(!timer.isSingleShot()); + + // interval property + QCOMPARE(timer.interval(), 0); + + QProperty<int> interval; + timer.bindableInterval().setBinding(Qt::makePropertyBinding(interval)); + + interval = 10; + QCOMPARE(timer.interval(), 10); + interval = 100; + QCOMPARE(timer.interval(), 100); + timer.setInterval(50); + QCOMPARE(timer.interval(), 50); + interval = 30; + QCOMPARE(timer.interval(), 50); + + // timerType property + QCOMPARE(timer.timerType(), Qt::CoarseTimer); + + QProperty<Qt::TimerType> timerType; + timer.bindableTimerType().setBinding(Qt::makePropertyBinding(timerType)); + + timerType = Qt::PreciseTimer; + QCOMPARE(timer.timerType(), Qt::PreciseTimer); + timerType = Qt::VeryCoarseTimer; + QCOMPARE(timer.timerType(), Qt::VeryCoarseTimer); +} + +void tst_QTimer::automatedBindingTests() +{ + QTimer timer; + + QVERIFY(!timer.isSingleShot()); + QTestPrivate::testReadWritePropertyBasics(timer, true, false, "singleShot"); + if (QTest::currentTestFailed()) { + qDebug("Failed property test for QTimer::singleShot"); + return; + } + + QCOMPARE_NE(timer.interval(), 10); + QTestPrivate::testReadWritePropertyBasics(timer, 10, 20, "interval"); + if (QTest::currentTestFailed()) { + qDebug("Failed property test for QTimer::interval"); + return; + } + + QCOMPARE_NE(timer.timerType(), Qt::PreciseTimer); + QTestPrivate::testReadWritePropertyBasics(timer, Qt::PreciseTimer, Qt::CoarseTimer, + "timerType"); + if (QTest::currentTestFailed()) { + qDebug("Failed property test for QTimer::timerType"); + return; + } + + timer.start(1000); + QVERIFY(timer.isActive()); + QTestPrivate::testReadOnlyPropertyBasics(timer, true, false, "active", + [&timer]() { timer.stop(); }); + if (QTest::currentTestFailed()) { + qDebug("Failed property test for QTimer::active"); + return; + } +} + +void tst_QTimer::negativeInterval() +{ + auto ignoreMsg = [] { + QTest::ignoreMessage(QtWarningMsg, + "QObject::startTimer: Timers cannot have negative intervals"); + }; + + QTimer timer; + + // Starting with a negative interval does not change active state. + ignoreMsg(); + timer.start(-100ms); + QVERIFY(!timer.isActive()); + + // Updating the interval to a negative value stops the timer and changes + // the active state. + timer.start(100ms); + QVERIFY(timer.isActive()); + ignoreMsg(); + timer.setInterval(-100); + QVERIFY(!timer.isActive()); + + // Starting with a negative interval when already started leads to stop + // and inactive state. + timer.start(100); + QVERIFY(timer.isActive()); + ignoreMsg(); + timer.start(-100ms); + QVERIFY(!timer.isActive()); +} + class OrderHelper : public QObject { Q_OBJECT @@ -1147,14 +1507,10 @@ void tst_QTimer::timerOrder_data() void tst_QTimer::timerOrderBackgroundThread() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else auto *thread = QThread::create([this]() { timerOrder(); }); thread->start(); QVERIFY(thread->wait()); delete thread; -#endif } struct StaticSingleShotUser |