summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
diff options
context:
space:
mode:
authorAhmad Samir <a.samirh78@gmail.com>2023-07-11 16:32:39 +0300
committerAhmad Samir <a.samirh78@gmail.com>2024-03-03 19:56:55 +0200
commitbd764cc1ca578759f16fbe292fbe14a243b7d263 (patch)
tree9e270f40316c60a3b536e2019fc70d63cadc0224 /tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
parent4fa9034d0c592e5f07531d41463c8c462f5e8895 (diff)
Add QChronoTimer, a timer with nanoseconds precision
The interval in QTimer is a QProperty of type int, which means it's limited to the number of milliseconds that would fit in an int (~24 days), this could cause overflow if a user constructs a QTimer with an interval > INT_MAX milliseconds. And it can't be easily changed to use qint64/std::chrono::nanoseconds: - changing the getters to return qint64 means user code would have narrowing conversions - the bindable QProperty interval can't be changed to qint64 during Qt6's lifetime without the risk of breaking user code - adding a new bindable QProperty that is qint64/nanoseconds is an option, but it has the complication of what to do with the int interval; set it when setInterval(milliseconds) is used by using saturation arithmetic? and what about notifying observers of the changed interval? Thus the idea of creating a new stop-gap class, QChronoTimer, as a cleaner solution. Both classes use QTimerPrivate. During the lifetime of Qt6, QTimer's interval range is about 24 days, whereas QChronoTimer's interval range is about 292 years (duration_cast<years>nanoseconds::max()). Currently the plan is to fold QChronotTimer back into QTimer in Qt7. Mark all QPropertyS in the new class as FINAL since they aren't intended to be overridden; this offers a performance boost for QML[1]. [1] https://lists.qt-project.org/pipermail/development/2024-February/044977.html [ChangeLog][QtCore] Added QChronoTimer, which uses a std::chrono::nanoseconds intervals, as a replacement for QTimer. Fixes: QTBUG-113544 Change-Id: I71697f4a8b35452c6b5604b1322ee7f0b4453f04 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp')
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp1035
1 files changed, 379 insertions, 656 deletions
diff --git a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
index 467fc9abd7..2b137b06e3 100644
--- a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
+++ b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
@@ -1,20 +1,10 @@
// Copyright (C) 2020 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-/* 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
@@ -23,8 +13,10 @@
#include <QSignalSpy>
#include <QtTest/private/qpropertytesthelper_p.h>
-#include <qtimer.h>
+#include <qbasictimer.h>
+#include <qchronotimer.h>
#include <qthread.h>
+#include <qtimer.h>
#include <qelapsedtimer.h>
#include <qproperty.h>
@@ -32,6 +24,8 @@
#include <unistd.h>
#endif
+using namespace std::chrono_literals;
+
#ifdef DISABLE_GLIB
static bool glibDisabled = []() {
qputenv("QT_NO_GLIB", "1");
@@ -39,25 +33,16 @@ static bool glibDisabled = []() {
}();
#endif
-using namespace std::chrono_literals;
-
-class tst_QTimer : public QObject
+class tst_QChronoTimer : public QObject
{
Q_OBJECT
-public:
- static void initMain();
private slots:
- void cleanupTestCase();
void zeroTimer();
- void singleShotTimeout();
+ void singleShotTimeout(); // Non-static singleShot()
void timeout();
- void singleShotNormalizes_data();
- void singleShotNormalizes();
void sequentialTimers_data();
void sequentialTimers();
- void singleShotSequentialTimers_data();
- void singleShotSequentialTimers();
void remainingTime();
void remainingTimeInitial_data();
void remainingTimeInitial();
@@ -70,24 +55,19 @@ private slots:
void timerInfiniteRecursion();
void recurringTimer_data();
void recurringTimer();
- void deleteLaterOnQTimer(); // long name, don't want to shadow QObject::deleteLater()
+ void deleteLaterOnQChronoTimer(); // long name, don't want to shadow QObject::deleteLater()
void moveToThread();
void restartedTimerFiresTooSoon();
void timerFiresOnlyOncePerProcessEvents_data();
void timerFiresOnlyOncePerProcessEvents();
void timerIdPersistsAfterThreadExit();
void cancelLongTimer();
- void singleShotStaticFunctionZeroTimeout();
void recurseOnTimeoutAndStopTimer();
- void singleShotToFunctors();
- void singleShot_chrono();
- void singleShot_static();
- void crossThreadSingleShotToFunctor_data();
- void crossThreadSingleShotToFunctor();
void timerOrder();
void timerOrder_data();
void timerOrderBackgroundThread();
void timerOrderBackgroundThread_data() { timerOrder_data(); }
+ void timerPrecision();
void dontBlockEvents();
void postedEventsShouldNotStarveTimers();
@@ -100,15 +80,15 @@ private slots:
void negativeInterval();
};
-void tst_QTimer::zeroTimer()
+void tst_QChronoTimer::zeroTimer()
{
- QTimer timer;
+ QChronoTimer timer;
QVERIFY(!timer.isSingleShot());
- timer.setInterval(0);
+ timer.setInterval(0ns);
timer.setSingleShot(true);
QVERIFY(timer.isSingleShot());
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
// Pass timeout to work round glib issue, see QTBUG-84291.
@@ -117,135 +97,80 @@ void tst_QTimer::zeroTimer()
QCOMPARE(timeoutSpy.size(), 1);
}
-void tst_QTimer::singleShotTimeout()
+void tst_QChronoTimer::singleShotTimeout()
{
- QTimer timer;
+ QChronoTimer timer;
QVERIFY(!timer.isSingleShot());
timer.setSingleShot(true);
QVERIFY(timer.isSingleShot());
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.start(100);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.setInterval(100ms);
+ timer.start();
- QVERIFY(timeoutSpy.wait(500));
+ QVERIFY(timeoutSpy.wait(500ms));
QCOMPARE(timeoutSpy.size(), 1);
- QTest::qWait(500);
+ QTest::qWait(500ms);
QCOMPARE(timeoutSpy.size(), 1);
}
-#define TIMEOUT_TIMEOUT 200
+static constexpr auto Timeout_Interval = 200ms;
-void tst_QTimer::timeout()
+void tst_QChronoTimer::timeout()
{
- QTimer timer;
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.start(100);
+ QChronoTimer timer{100ms};
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
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");
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 0, Timeout_Interval);
+ const qsizetype oldCount = timeoutSpy.size();
- 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() ));
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, Timeout_Interval);
}
-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()
+void tst_QChronoTimer::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) {
+ QTest::addColumn<QList<std::chrono::milliseconds>>("timeouts");
+ auto addRow = [](const QList<std::chrono::milliseconds> &l) {
+ Q_ASSERT_X(std::is_sorted(l.begin(), l.end()),
+ "tst_QChronoTimer", "input list must be sorted");
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]) + ',';
- }
+ for (auto msec : l)
+ name += QByteArray::number(msec.count()) + ',';
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});
+ addRow({0ms, 0ms, 0ms, 0ms, 0ms, 0ms});
+ addRow({0ms, 1ms, 2ms});
+ addRow({1ms, 1ms, 1ms, 2ms, 2ms, 2ms, 2ms});
+ addRow({1ms, 2ms, 3ms});
+ addRow({19ms, 19ms, 19ms});
+ // CoarseTimer for setinterval
+ addRow({20ms, 20ms, 20ms, 20ms, 20ms});
+ addRow({25ms, 25ms, 25ms, 25ms, 25ms, 25ms, 50ms});
}
-void tst_QTimer::sequentialTimers()
+void tst_QChronoTimer::sequentialTimers()
{
- QFETCH(const QList<int>, timeouts);
+ QFETCH(const QList<std::chrono::milliseconds>, timeouts);
QByteArray result, expected;
- std::vector<std::unique_ptr<QTimer>> timers;
+ std::vector<std::unique_ptr<QChronoTimer>> 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>();
+ auto timer = std::make_unique<QChronoTimer>(timeouts[i]);
timer->setSingleShot(true);
- timer->setInterval(timeouts[i]);
char c = 'A' + i;
expected[i] = c;
- QObject::connect(timer.get(), &QTimer::timeout, this, [&result, c = c]() {
+ QObject::connect(timer.get(), &QChronoTimer::timeout, this, [&result, c = c]() {
result.append(c);
});
timers.push_back(std::move(timer));
@@ -255,62 +180,37 @@ void tst_QTimer::sequentialTimers()
for (auto &timer : timers)
timer->start();
- QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10);
-
- QCOMPARE(result, expected);
-}
-
-void tst_QTimer::singleShotSequentialTimers_data()
-{
- sequentialTimers_data();
-}
-
-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);
+ QTestEventLoop::instance().enterLoop(timeouts.last() * 2 + 10ms);
QCOMPARE(result, expected);
}
-void tst_QTimer::remainingTime()
+void tst_QChronoTimer::remainingTime()
{
- QTimer tested;
+ QChronoTimer tested;
tested.setTimerType(Qt::PreciseTimer);
- QTimer tester;
+ QChronoTimer tester;
tester.setTimerType(Qt::PreciseTimer);
tester.setSingleShot(true);
- const int testedInterval = 200;
- const int testerInterval = 50;
- const int expectedRemainingTime = testedInterval - testerInterval;
+ constexpr auto tested_interval = 200ms;
+ constexpr auto tester_interval = 50ms;
+ constexpr auto expectedRemainingTime = tested_interval - tester_interval;
int testIteration = 0;
const int desiredTestCount = 2;
- auto connection = QObject::connect(&tested, &QTimer::timeout, [&tester]() {
- // We let tested (which isn't a single-shot) run repeatedly, to verify
- // it *does* repeat, and check that the single-shot tester, starting
- // at the same time, does finish first each time, by about the right duration.
- tester.start(); // Start tester again.
- });
+ // We let tested (which isn't a single-shot) run repeatedly, to verify
+ // it *does* repeat, and check that the single-shot tester, starting
+ // at the same time, does finish first each time, by about the right duration.
+ auto connection = QObject::connect(&tested, &QChronoTimer::timeout,
+ &tester, &QChronoTimer::start);
- QObject::connect(&tester, &QTimer::timeout, [&]() {
- const int remainingTime = tested.remainingTime();
+ QObject::connect(&tester, &QChronoTimer::timeout, this, [&]() {
+ const std::chrono::nanoseconds remainingTime = tested.remainingTime();
// We expect that remainingTime is at most 150 and not overdue.
- const bool remainingTimeInRange = remainingTime > 0
+ const bool remainingTimeInRange = remainingTime > 0ns
&& remainingTime <= expectedRemainingTime;
if (remainingTimeInRange)
++testIteration;
@@ -322,145 +222,155 @@ void tst_QTimer::remainingTime()
if (testIteration == desiredTestCount)
QObject::disconnect(connection); // Last iteration, don't start tester again.
QVERIFY2(remainingTimeInRange, qPrintable("Remaining time "
- + QByteArray::number(remainingTime) + "ms outside expected range (0ms, "
- + QByteArray::number(expectedRemainingTime) + "ms]"));
+ + QByteArray::number(remainingTime.count()) + "ms outside expected range (0ns, "
+ + QByteArray::number(expectedRemainingTime.count()) + "ms]"));
});
- tested.start(testedInterval);
- tester.start(testerInterval); // Start tester for the 1st time.
+ tested.setInterval(tested_interval);
+ tested.start();
+ tester.setInterval(tester_interval);
+ tester.start(); // Start tester for the 1st time.
// Test it desiredTestCount times, give it reasonable amount of time
// (twice as much as needed).
- QTRY_COMPARE_WITH_TIMEOUT(testIteration, desiredTestCount,
- testedInterval * desiredTestCount * 2);
+ const auto tryTimeout = tested_interval * desiredTestCount * 2;
+ QTRY_COMPARE_WITH_TIMEOUT(testIteration, desiredTestCount, tryTimeout);
}
-void tst_QTimer::remainingTimeInitial_data()
+void tst_QChronoTimer::remainingTimeInitial_data()
{
- QTest::addColumn<int>("startTimeMs");
+ using namespace std::chrono;
+
+ QTest::addColumn<nanoseconds>("startTimeNs");
QTest::addColumn<Qt::TimerType>("timerType");
- QTest::addRow("precise time 0ms") << 0 << Qt::PreciseTimer;
- QTest::addRow("precise time 1ms") << 1 << Qt::PreciseTimer;
- QTest::addRow("precise time 10ms") << 10 << Qt::PreciseTimer;
+ QTest::addRow("precisetiemr-0ns") << 0ns << Qt::PreciseTimer;
+ QTest::addRow("precisetimer-1ms") << nanoseconds{1ms} << Qt::PreciseTimer;
+ QTest::addRow("precisetimer-10ms") <<nanoseconds{10ms} << Qt::PreciseTimer;
- QTest::addRow("coarse time 0ms") << 0 << Qt::CoarseTimer;
- QTest::addRow("coarse time 1ms") << 1 << Qt::CoarseTimer;
- QTest::addRow("coarse time 10ms") << 10 << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-0ns") << 0ns << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-1ms") << nanoseconds{1ms} << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-10ms") << nanoseconds{10ms} << Qt::CoarseTimer;
}
-void tst_QTimer::remainingTimeInitial()
+void tst_QChronoTimer::remainingTimeInitial()
{
- QFETCH(int, startTimeMs);
+ QFETCH(std::chrono::nanoseconds, startTimeNs);
QFETCH(Qt::TimerType, timerType);
- QTimer timer;
+ QChronoTimer timer;
QCOMPARE(timer.timerType(), Qt::CoarseTimer);
timer.setTimerType(timerType);
QCOMPARE(timer.timerType(), timerType);
- timer.start(startTimeMs);
+ timer.setInterval(startTimeNs);
+ timer.start();
- const int rt = timer.remainingTime();
- QVERIFY2(rt >= 0 && rt <= startTimeMs, qPrintable(QString::number(rt)));
+ const std::chrono::nanoseconds rt = timer.remainingTime();
+ QCOMPARE_GE(rt, 0ns);
+ QCOMPARE_LE(rt, startTimeNs);
}
-void tst_QTimer::remainingTimeDuringActivation_data()
+void tst_QChronoTimer::remainingTimeDuringActivation_data()
{
QTest::addColumn<bool>("singleShot");
QTest::newRow("repeating") << false;
QTest::newRow("single-shot") << true;
}
-void tst_QTimer::remainingTimeDuringActivation()
+void tst_QChronoTimer::remainingTimeDuringActivation()
{
QFETCH(bool, singleShot);
- QTimer timer;
+ QChronoTimer timer;
timer.setSingleShot(singleShot);
- int remainingTime = 0; // not the expected value in either case
- connect(&timer, &QTimer::timeout,
- [&]() {
- remainingTime = timer.remainingTime();
- });
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- const int timeout = 20; // 20 ms is short enough and should not round down to 0 in any timer mode
- timer.start(timeout);
+ auto remainingTime = 0ns; // not the expected value in either case
+ connect(&timer, &QChronoTimer::timeout, this, [&]() { remainingTime = timer.remainingTime(); });
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ // 20 ms is short enough and should not round down to 0 in any timer mode
+ constexpr auto timeout = 20ms;
+ timer.setInterval(timeout);
+ timer.start();
QVERIFY(timeoutSpy.wait());
if (singleShot)
- QCOMPARE(remainingTime, -1); // timer not running
- else
- QVERIFY2(remainingTime <= timeout && remainingTime > 0,
- qPrintable(QString::number(remainingTime)));
+ QCOMPARE_LT(remainingTime, 0ns); // timer not running
+ else {
+ QCOMPARE_LE(remainingTime, timeout);
+ QCOMPARE_GT(remainingTime, 0ns);
+ }
if (!singleShot) {
// do it again - see QTBUG-46940
- remainingTime = -1;
+ remainingTime = std::chrono::milliseconds::min();
QVERIFY(timeoutSpy.wait());
- QVERIFY2(remainingTime <= timeout && remainingTime > 0,
- qPrintable(QString::number(remainingTime)));
+ QCOMPARE_LE(remainingTime, timeout);
+ QCOMPARE_GT(remainingTime, 0ns);
}
}
namespace {
-
template <typename T>
- std::chrono::milliseconds to_ms(T t)
- { return std::chrono::duration_cast<std::chrono::milliseconds>(t); }
-
+ auto to_ms(T t)
+ {
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(t);
+ }
} // unnamed namespace
-void tst_QTimer::basic_chrono()
+void tst_QChronoTimer::basic_chrono()
{
// duplicates zeroTimer, singleShotTimeout, interval and remainingTime
using namespace std::chrono;
- QTimer timer;
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.setInterval(to_ms(nanoseconds(0)));
+ QChronoTimer timer;
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
- QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(0));
- QCOMPARE(timer.remainingTimeAsDuration().count(), milliseconds::rep(0));
+ QCOMPARE(timer.interval(), 0ns);
+ QCOMPARE(timer.remainingTime(), 0ns);
QCoreApplication::processEvents();
QCOMPARE(timeoutSpy.size(), 1);
timeoutSpy.clear();
- timer.start(milliseconds(100));
+ timer.setInterval(100ms);
+ timer.start();
QCOMPARE(timeoutSpy.size(), 0);
- QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
QVERIFY(timeoutSpy.size() > 0);
- int oldCount = timeoutSpy.size();
+ const qsizetype oldCount = timeoutSpy.size();
- QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
QVERIFY(timeoutSpy.size() > oldCount);
timeoutSpy.clear();
- timer.start(to_ms(microseconds(200000)));
- QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(200));
- QTest::qWait(50);
+ timer.setInterval(200ms);
+ timer.start();
+ QCOMPARE(timer.interval(), 200ms);
+ QTest::qWait(50ms);
QCOMPARE(timeoutSpy.size(), 0);
- milliseconds rt = timer.remainingTimeAsDuration();
- QVERIFY2(rt.count() >= 50 && rt.count() <= 200, qPrintable(QString::number(rt.count())));
+ nanoseconds rt = timer.remainingTime();
+ QCOMPARE_GE(rt, 50ms);
+ QCOMPARE_LE(rt, 200ms);
timeoutSpy.clear();
timer.setSingleShot(true);
- timer.start(milliseconds(100));
- QVERIFY(timeoutSpy.wait(TIMEOUT_TIMEOUT));
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
QCOMPARE(timeoutSpy.size(), 1);
- QTest::qWait(500);
+ QTest::qWait(500ms);
QCOMPARE(timeoutSpy.size(), 1);
}
-void tst_QTimer::livelock_data()
+void tst_QChronoTimer::livelock_data()
{
- QTest::addColumn<int>("interval");
- QTest::newRow("zero timer") << 0;
- QTest::newRow("non-zero timer") << 1;
- QTest::newRow("longer than sleep") << 20;
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero-timer") << 0ns;
+ QTest::newRow("non-zero-timer") << std::chrono::nanoseconds{1ms};
+ QTest::newRow("longer-than-sleep") << std::chrono::nanoseconds{20ms};
}
/*!
@@ -471,20 +381,19 @@ void tst_QTimer::livelock_data()
*/
class LiveLockTester : public QObject
{
+ static constexpr QEvent::Type PostEventType = static_cast<QEvent::Type>(4002);
public:
- LiveLockTester(int i)
- : interval(i),
- timeoutsForFirst(0), timeoutsForExtra(0), timeoutsForSecond(0),
- postEventAtRightTime(false)
+ LiveLockTester(std::chrono::nanoseconds i)
+ : interval(i)
{
firstTimerId = startTimer(interval);
- extraTimerId = startTimer(interval + 80);
+ extraTimerId = startTimer(interval + 80ms);
secondTimerId = -1; // started later
}
bool event(QEvent *e) override
{
- if (e->type() == 4002) {
+ if (e->type() == PostEventType) {
// got the posted event
if (timeoutsForFirst == 1 && timeoutsForSecond == 0)
postEventAtRightTime = true;
@@ -499,7 +408,7 @@ public:
if (++timeoutsForFirst == 1) {
killTimer(extraTimerId);
extraTimerId = -1;
- QCoreApplication::postEvent(this, new QEvent(static_cast<QEvent::Type>(4002)));
+ QCoreApplication::postEvent(this, new QEvent(PostEventType));
secondTimerId = startTimer(interval);
}
} else if (te->timerId() == secondTimerId) {
@@ -513,17 +422,17 @@ public:
killTimer(te->timerId());
}
- const int interval;
- int firstTimerId;
- int secondTimerId;
- int extraTimerId;
- int timeoutsForFirst;
- int timeoutsForExtra;
- int timeoutsForSecond;
- bool postEventAtRightTime;
+ const std::chrono::nanoseconds interval;
+ int firstTimerId = -1;
+ int secondTimerId = -1;
+ int extraTimerId = -1;
+ int timeoutsForFirst = 0;
+ int timeoutsForExtra = 0;
+ int timeoutsForSecond = 0;
+ bool postEventAtRightTime = false;
};
-void tst_QTimer::livelock()
+void tst_QChronoTimer::livelock()
{
/*
New timers created in timer event handlers should not be sent
@@ -532,9 +441,9 @@ void tst_QTimer::livelock()
events (since new posted events are not sent until the next
iteration of the eventloop either).
*/
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
LiveLockTester tester(interval);
- QTest::qWait(180); // we have to use wait here, since we're testing timers with a non-zero timeout
+ QTest::qWait(180ms); // we have to use wait here, since we're testing timers with a non-zero timeout
QTRY_COMPARE(tester.timeoutsForFirst, 1);
QCOMPARE(tester.timeoutsForExtra, 0);
QTRY_COMPARE(tester.timeoutsForSecond, 1);
@@ -544,12 +453,12 @@ void tst_QTimer::livelock()
class TimerInfiniteRecursionObject : public QObject
{
public:
- bool inTimerEvent;
- bool timerEventRecursed;
- int interval;
+ bool inTimerEvent = false;
+ bool timerEventRecursed = false;
+ std::chrono::nanoseconds interval;
- TimerInfiniteRecursionObject(int interval)
- : inTimerEvent(false), timerEventRecursed(false), interval(interval)
+ TimerInfiniteRecursionObject(std::chrono::nanoseconds interval)
+ : interval(interval)
{ }
void timerEvent(QTimerEvent *timerEvent) override
@@ -563,7 +472,8 @@ public:
inTimerEvent = true;
QEventLoop eventLoop;
- QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(std::max<std::chrono::nanoseconds>(100ms, interval * 2),
+ &eventLoop, &QEventLoop::quit);
eventLoop.exec();
inTimerEvent = false;
@@ -572,26 +482,27 @@ public:
}
};
-void tst_QTimer::timerInfiniteRecursion_data()
+void tst_QChronoTimer::timerInfiniteRecursion_data()
{
- QTest::addColumn<int>("interval");
- QTest::newRow("zero timer") << 0;
- QTest::newRow("non-zero timer") << 1;
- QTest::newRow("10ms timer") << 10;
- QTest::newRow("11ms timer") << 11;
- QTest::newRow("100ms timer") << 100;
- QTest::newRow("1s timer") << 1000;
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero timer") << 0ns;
+ QTest::newRow("non-zero timer") << std::chrono::nanoseconds{1ms};
+ QTest::newRow("10ms timer") << std::chrono::nanoseconds{10ms};
+ QTest::newRow("11ms timer") << std::chrono::nanoseconds{11ms};
+ QTest::newRow("100ms timer") << std::chrono::nanoseconds{100ms};
+ QTest::newRow("1s timer") << std::chrono::nanoseconds{1000ms};
}
-void tst_QTimer::timerInfiniteRecursion()
+void tst_QChronoTimer::timerInfiniteRecursion()
{
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
TimerInfiniteRecursionObject object(interval);
(void) object.startTimer(interval);
QEventLoop eventLoop;
- QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(std::max<std::chrono::nanoseconds>(100ms, interval * 2), &eventLoop,
+ &QEventLoop::quit);
eventLoop.exec();
QVERIFY(!object.timerEventRecursed);
@@ -613,10 +524,10 @@ public:
{
if (++times == target) {
killTimer(timerEvent->timerId());
- emit done();
+ Q_EMIT done();
} if (recurse) {
QEventLoop eventLoop;
- QTimer::singleShot(100, &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(100ms, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
}
}
@@ -625,21 +536,21 @@ signals:
void done();
};
-void tst_QTimer::recurringTimer_data()
+void tst_QChronoTimer::recurringTimer_data()
{
- QTest::addColumn<int>("interval");
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
QTest::addColumn<bool>("recurse");
// make sure that eventloop recursion doesn't affect timer recurrence
- QTest::newRow("zero timer, don't recurse") << 0 << false;
- QTest::newRow("zero timer, recurse") << 0 << true;
- QTest::newRow("non-zero timer, don't recurse") << 1 << false;
- QTest::newRow("non-zero timer, recurse") << 1 << true;
+ QTest::newRow("zero timer, don't recurse") << 0ns << false;
+ QTest::newRow("zero timer, recurse") << 0ns << true;
+ QTest::newRow("non-zero timer, don't recurse") << std::chrono::nanoseconds{1ms} << false;
+ QTest::newRow("non-zero timer, recurse") << std::chrono::nanoseconds{1ms} << true;
}
-void tst_QTimer::recurringTimer()
+void tst_QChronoTimer::recurringTimer()
{
const int target = 5;
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
QFETCH(bool, recurse);
RecurringTimerObject object(target);
@@ -652,54 +563,56 @@ void tst_QTimer::recurringTimer()
QCOMPARE(object.times, target);
}
-void tst_QTimer::deleteLaterOnQTimer()
+void tst_QChronoTimer::deleteLaterOnQChronoTimer()
{
- QTimer *timer = new QTimer;
- connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
+ QChronoTimer *timer = new QChronoTimer;
+ connect(timer, &QChronoTimer::timeout, timer, &QObject::deleteLater);
QSignalSpy destroyedSpy(timer, &QObject::destroyed);
- timer->setInterval(1);
+ timer->setInterval(1ms);
timer->setSingleShot(true);
timer->start();
- QPointer<QTimer> pointer = timer;
+ QPointer<QChronoTimer> pointer = timer;
QVERIFY(destroyedSpy.wait());
QVERIFY(pointer.isNull());
}
-#define MOVETOTHREAD_TIMEOUT 200
-#define MOVETOTHREAD_WAIT 300
+static constexpr auto MoveToThread_Timeout = 200ms;
+static constexpr auto MoveToThread_Wait = 300ms;
-void tst_QTimer::moveToThread()
+void tst_QChronoTimer::moveToThread()
{
#if defined(Q_OS_WIN32)
QSKIP("Does not work reliably on Windows :(");
#elif defined(Q_OS_MACOS)
QSKIP("Does not work reliably on macOS 10.12+ (QTBUG-59679)");
#endif
- QTimer ti1;
- QTimer ti2;
- ti1.setSingleShot(true);
- ti1.start(MOVETOTHREAD_TIMEOUT);
- ti2.start(MOVETOTHREAD_TIMEOUT);
- QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
+ QChronoTimer timer1{MoveToThread_Timeout};
+ QChronoTimer timer2{MoveToThread_Timeout};
+ timer1.setSingleShot(true);
+ timer1.start();
+ timer2.start();
+ QVERIFY((timer1.id() & 0xffffff) != (timer2.id() & 0xffffff));
QThread tr;
- ti1.moveToThread(&tr);
- connect(&ti1,SIGNAL(timeout()), &tr, SLOT(quit()));
+ timer1.moveToThread(&tr);
+ connect(&timer1, &QChronoTimer::timeout, &tr, &QThread::quit);
tr.start();
- QTimer ti3;
- ti3.start(MOVETOTHREAD_TIMEOUT);
- QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
- QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
- QTest::qWait(MOVETOTHREAD_WAIT);
+ QChronoTimer ti3{MoveToThread_Timeout};
+ ti3.start();
+ QVERIFY((ti3.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff));
+ QTest::qWait(MoveToThread_Wait);
QVERIFY(tr.wait());
- ti2.stop();
- QTimer ti4;
- ti4.start(MOVETOTHREAD_TIMEOUT);
+ timer2.stop();
+ QChronoTimer ti4{MoveToThread_Timeout};
+ ti4.start();
ti3.stop();
- ti2.start(MOVETOTHREAD_TIMEOUT);
- ti3.start(MOVETOTHREAD_TIMEOUT);
- QVERIFY((ti4.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
- QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
- QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
+ timer2.setInterval(MoveToThread_Timeout);
+ timer2.start();
+ ti3.setInterval(MoveToThread_Timeout);
+ ti3.start();
+ QVERIFY((ti4.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff));
}
class RestartedTimerFiresTooSoonObject : public QObject
@@ -709,24 +622,22 @@ class RestartedTimerFiresTooSoonObject : public QObject
public:
QBasicTimer m_timer;
- int m_interval;
+ std::chrono::milliseconds m_interval = 0ms;
QElapsedTimer m_elapsedTimer;
QEventLoop eventLoop;
- inline RestartedTimerFiresTooSoonObject()
- : QObject(), m_interval(0)
- { }
+ RestartedTimerFiresTooSoonObject() = default;
void timerFired()
{
- static int interval = 1000;
+ static std::chrono::milliseconds interval = 1s;
m_interval = interval;
m_elapsedTimer.start();
m_timer.start(interval, this);
// alternate between single-shot and 1 sec
- interval = interval ? 0 : 1000;
+ interval = interval > 0ms ? 0ms : 1s;
}
void timerEvent(QTimerEvent* ev) override
@@ -736,7 +647,7 @@ public:
m_timer.stop();
- int elapsed = m_elapsedTimer.elapsed();
+ std::chrono::nanoseconds elapsed = m_elapsedTimer.durationElapsed();
if (elapsed < m_interval / 2) {
// severely too early!
@@ -757,7 +668,7 @@ public:
}
};
-void tst_QTimer::restartedTimerFiresTooSoon()
+void tst_QChronoTimer::restartedTimerFiresTooSoon()
{
RestartedTimerFiresTooSoonObject object;
object.timerFired();
@@ -769,16 +680,16 @@ class LongLastingSlotClass : public QObject
Q_OBJECT
public:
- LongLastingSlotClass(QTimer *timer) : count(0), timer(timer) {}
+ LongLastingSlotClass(QChronoTimer *timer) : timer(timer) { }
public slots:
void longLastingSlot()
{
- // Don't use QTimer for this, because we are testing it.
+ // Don't use QChronoTimer for this, because we are testing it.
QElapsedTimer control;
control.start();
- while (control.elapsed() < 200) {
- for (int c = 0; c < 100000; c++) {} // Mindless looping.
+ while (control.durationElapsed() < 200ms) {
+ for (int c = 0; c < 100'000; c++) {} // Mindless looping.
}
if (++count >= 2) {
timer->stop();
@@ -786,29 +697,28 @@ public slots:
}
public:
- int count;
- QTimer *timer;
+ int count = 0;
+ QChronoTimer *timer;
};
-void tst_QTimer::timerFiresOnlyOncePerProcessEvents_data()
+void tst_QChronoTimer::timerFiresOnlyOncePerProcessEvents_data()
{
- QTest::addColumn<int>("interval");
- QTest::newRow("zero timer") << 0;
- QTest::newRow("non-zero timer") << 10;
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero-timer") << 0ns;
+ QTest::newRow("non-zero-timer") << std::chrono::nanoseconds{10ms};
}
-void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
+void tst_QChronoTimer::timerFiresOnlyOncePerProcessEvents()
{
- QFETCH(int, interval);
+ QFETCH(std::chrono::nanoseconds, interval);
- QTimer t;
+ QChronoTimer t{interval};
LongLastingSlotClass longSlot(&t);
- t.start(interval);
- connect(&t, SIGNAL(timeout()), &longSlot, SLOT(longLastingSlot()));
+ t.start();
+ connect(&t, &QChronoTimer::timeout, &longSlot, &LongLastingSlotClass::longLastingSlot);
// Loop because there may be other events pending.
- while (longSlot.count == 0) {
+ while (longSlot.count == 0)
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
- }
QCOMPARE(longSlot.count, 1);
}
@@ -816,49 +726,44 @@ void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
class TimerIdPersistsAfterThreadExitThread : public QThread
{
public:
- QTimer *timer;
- int timerId, returnValue;
-
- TimerIdPersistsAfterThreadExitThread()
- : QThread(), timer(0), timerId(-1), returnValue(-1)
- { }
- ~TimerIdPersistsAfterThreadExitThread()
- {
- delete timer;
- }
+ std::unique_ptr<QChronoTimer> timer;
+ int timerId = -1;
+ int returnValue = -1;
void run() override
{
QEventLoop eventLoop;
- timer = new QTimer;
- connect(timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
- timer->start(100);
- timerId = timer->timerId();
+ timer = std::make_unique<QChronoTimer>();
+ connect(timer.get(), &QChronoTimer::timeout, &eventLoop, &QEventLoop::quit);
+ timer->setInterval(100ms);
+ timer->start();
+ timerId = timer->id();
returnValue = eventLoop.exec();
}
};
-void tst_QTimer::timerIdPersistsAfterThreadExit()
+void tst_QChronoTimer::timerIdPersistsAfterThreadExit()
{
TimerIdPersistsAfterThreadExitThread thread;
thread.start();
- QVERIFY(thread.wait(30000));
+ QVERIFY(thread.wait(30s));
QCOMPARE(thread.returnValue, 0);
// even though the thread has exited, and the event dispatcher destroyed, the timer is still
// "active", meaning the timer id should NOT be reused (i.e. the event dispatcher should not
// have unregistered it)
- int timerId = thread.startTimer(100);
+ int timerId = thread.startTimer(100ms);
QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff));
}
-void tst_QTimer::cancelLongTimer()
+void tst_QChronoTimer::cancelLongTimer()
{
- QTimer timer;
+ QChronoTimer timer{1h};
timer.setSingleShot(true);
- timer.start(1000 * 60 * 60); //set timer for 1 hour
+ timer.start();
QCoreApplication::processEvents();
- QVERIFY(timer.isActive()); //if the timer completes immediately with an error, then this will fail
+ // If the timer completes immediately with an error, then this will fail
+ QVERIFY(timer.isActive());
timer.stop();
QVERIFY(!timer.isActive());
}
@@ -872,34 +777,13 @@ public:
int count = 0;
};
-void tst_QTimer::singleShotStaticFunctionZeroTimeout()
-{
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(0, &counter, SLOT(timeout()));
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(0, &counter, &TimeoutCounter::timeout);
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-}
-
class RecursOnTimeoutAndStopTimerTimer : public QObject
{
Q_OBJECT
public:
- QTimer *one;
- QTimer *two;
+ QChronoTimer *one;
+ QChronoTimer *two;
public slots:
void onetrigger()
@@ -913,14 +797,14 @@ public slots:
}
};
-void tst_QTimer::recurseOnTimeoutAndStopTimer()
+void tst_QChronoTimer::recurseOnTimeoutAndStopTimer()
{
QEventLoop eventLoop;
- QTimer::singleShot(1000, &eventLoop, SLOT(quit()));
+ QChronoTimer::singleShot(1s, &eventLoop, &QEventLoop::quit);
RecursOnTimeoutAndStopTimerTimer t;
- t.one = new QTimer(&t);
- t.two = new QTimer(&t);
+ t.one = new QChronoTimer(&t);
+ t.two = new QChronoTimer(&t);
QObject::connect(t.one, SIGNAL(timeout()), &t, SLOT(onetrigger()));
QObject::connect(t.two, SIGNAL(timeout()), &t, SLOT(twotrigger()));
@@ -966,128 +850,6 @@ public:
}
};
-void tst_QTimer::singleShotToFunctors()
-{
- int count = 0;
- _e.reset(new QEventLoop);
- QEventLoop e;
-
- QTimer::singleShot(0, CountedStruct(&count));
- QCoreApplication::processEvents();
- QCOMPARE(count, 1);
-
- QTimer::singleShot(0, &StaticEventLoop::quitEventLoop);
- QCOMPARE(_e->exec(), 0);
-
- QTimer::singleShot(0, &StaticEventLoop::quitEventLoop_noexcept);
- QCOMPARE(_e->exec(), 0);
-
- QThread t1;
- QObject c1;
- c1.moveToThread(&t1);
-
- QObject::connect(&t1, SIGNAL(started()), &e, SLOT(quit()));
- t1.start();
- QCOMPARE(e.exec(), 0);
-
- QTimer::singleShot(0, &c1, CountedStruct(&count, &t1));
- QTRY_COMPARE(count, 2);
-
- t1.quit();
- t1.wait();
-
- _t = new QThread;
- QObject c2;
- c2.moveToThread(_t);
-
- QObject::connect(_t, SIGNAL(started()), &e, SLOT(quit()));
- _t->start();
- QCOMPARE(e.exec(), 0);
-
- QTimer::singleShot(0, &c2, &StaticEventLoop::quitEventLoop);
- QCOMPARE(_e->exec(), 0);
-
- _t->quit();
- _t->wait();
- _t->deleteLater();
- _t = nullptr;
-
- {
- QObject c3;
- QTimer::singleShot(500, &c3, CountedStruct(&count));
- }
- QTest::qWait(800); // Wait until the singleshot timer would have timed out
- QCOMPARE(count, 2);
-
- QTimer::singleShot(0, [&count] { ++count; });
- QTRY_COMPARE(count, 3);
-
- QObject context;
- QThread thread;
-
- context.moveToThread(&thread);
- QObject::connect(&thread, SIGNAL(started()), &e, SLOT(quit()));
- thread.start();
- QCOMPARE(e.exec(), 0);
-
- QTimer::singleShot(0, &context, [&count, &thread] { ++count; QCOMPARE(QThread::currentThread(), &thread); });
- QTRY_COMPARE(count, 4);
-
- thread.quit();
- thread.wait();
-
- struct MoveOnly : CountedStruct {
- Q_DISABLE_COPY(MoveOnly)
- MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {};
- MoveOnly(int *c) : CountedStruct(c) {}
- };
- QTimer::singleShot(0, MoveOnly(&count));
- QTRY_COMPARE(count, 5);
-
- _e.reset();
- _t = nullptr;
-}
-
-void tst_QTimer::singleShot_chrono()
-{
- // duplicates singleShotStaticFunctionZeroTimeout and singleShotToFunctors
- using namespace std::chrono;
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(hours(0), &counter, SLOT(timeout()));
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-
- {
- TimeoutCounter counter;
-
- QTimer::singleShot(hours(0), &counter, &TimeoutCounter::timeout);
- QTRY_COMPARE(counter.count, 1);
- QTest::qWait(500);
- QCOMPARE(counter.count, 1);
- }
-
- int count = 0;
- QTimer::singleShot(to_ms(microseconds(0)), CountedStruct(&count));
- QTRY_COMPARE(count, 1);
-
- _e.reset(new QEventLoop);
- QTimer::singleShot(0, &StaticEventLoop::quitEventLoop);
- QCOMPARE(_e->exec(), 0);
-
- QObject c3;
- QTimer::singleShot(milliseconds(500), &c3, CountedStruct(&count));
- QTRY_COMPARE(count, 2);
-
- QTimer::singleShot(0, [&count] { ++count; });
- QTRY_COMPARE(count, 3);
-
- _e.reset();
-}
-
class DontBlockEvents : public QObject
{
Q_OBJECT
@@ -1109,22 +871,21 @@ DontBlockEvents::DontBlockEvents()
count = 0;
total = 0;
+ const std::chrono::milliseconds intervals[] = {2s, 2500ms, 3s, 5s, 1s, 2s};
// need a few unrelated timers running to reproduce the bug.
- (new QTimer(this))->start(2000);
- (new QTimer(this))->start(2500);
- (new QTimer(this))->start(3000);
- (new QTimer(this))->start(5000);
- (new QTimer(this))->start(1000);
- (new QTimer(this))->start(2000);
-
- m_timer.start(1, this);
+ for (auto dur : intervals) {
+ auto *t = new QChronoTimer(dur, this);
+ t->start();
+ }
+
+ m_timer.start(1ms, this);
}
void DontBlockEvents::timerEvent(QTimerEvent* event)
{
if (event->timerId() == m_timer.timerId()) {
- QMetaObject::invokeMethod(this, "paintEvent", Qt::QueuedConnection);
- m_timer.start(0, this);
+ QMetaObject::invokeMethod(this, &DontBlockEvents::paintEvent, Qt::QueuedConnection);
+ m_timer.start(0ms, this);
count++;
QCOMPARE(count, 1);
total++;
@@ -1139,10 +900,10 @@ void DontBlockEvents::paintEvent()
// This is a regression test for QTBUG-13633, where a timer with a zero
// timeout that was restarted by the event handler could starve other timers.
-void tst_QTimer::dontBlockEvents()
+void tst_QChronoTimer::dontBlockEvents()
{
DontBlockEvents t;
- QTest::qWait(60);
+ QTest::qWait(60ms);
QTRY_VERIFY(t.total > 2);
}
@@ -1154,16 +915,16 @@ public:
public slots:
void repeatThisSlot()
{
- QMetaObject::invokeMethod(this, "repeatThisSlot", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, &SlotRepeater::repeatThisSlot, Qt::QueuedConnection);
}
};
-void tst_QTimer::postedEventsShouldNotStarveTimers()
+void tst_QChronoTimer::postedEventsShouldNotStarveTimers()
{
- QTimer timer;
- timer.setInterval(0);
+ QChronoTimer timer;
+ timer.setInterval(0ns);
timer.setSingleShot(false);
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
SlotRepeater slotRepeater;
slotRepeater.repeatThisSlot();
@@ -1179,75 +940,33 @@ struct DummyFunctor {
};
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()
-{
- 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;
-
- QThread t;
- std::unique_ptr<QObject> o(new QObject());
- o->moveToThread(&t);
-
- QTimer::singleShot(timeout, o.get(), DummyFunctor());
-
- // 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();
- 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()
+void tst_QChronoTimer::callOnTimeout()
{
- QTimer timer;
- QSignalSpy timeoutSpy(&timer, &QTimer::timeout);
- timer.setInterval(0);
+ QChronoTimer timer;
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
timer.start();
- auto context = new QObject();
+ auto context = std::make_unique<QObject>();
int count = 0;
timer.callOnTimeout([&count] { count++; });
- QMetaObject::Connection connection = timer.callOnTimeout(context, [&count] { count++; });
- timer.callOnTimeout(&timer, &QTimer::stop);
+ QMetaObject::Connection connection = timer.callOnTimeout(context.get(), [&count] { count++; });
+ timer.callOnTimeout(&timer, &QChronoTimer::stop);
- QTest::qWait(100);
+ QTest::qWait(100ms);
QCOMPARE(count, 2);
QCOMPARE(timeoutSpy.size(), 1);
// Test that connection is bound to context lifetime
QVERIFY(connection);
- delete context;
+ context.reset();
QVERIFY(!connection);
}
-void tst_QTimer::bindToTimer()
+void tst_QChronoTimer::bindToTimer()
{
- QTimer timer;
+ QChronoTimer timer;
// singleShot property
QProperty<bool> singleShot;
@@ -1260,14 +979,14 @@ void tst_QTimer::bindToTimer()
QVERIFY(!singleShot);
// interval property
- QProperty<int> interval;
+ QProperty<std::chrono::nanoseconds> interval;
interval.setBinding([&](){ return timer.interval(); });
- QCOMPARE(timer.interval(), interval);
+ QCOMPARE(timer.interval(), interval.value());
- timer.setInterval(10);
- QCOMPARE(interval, 10);
- timer.setInterval(100);
- QCOMPARE(interval, 100);
+ timer.setInterval(10ms);
+ QCOMPARE(interval.value(), 10ms);
+ timer.setInterval(100ms);
+ QCOMPARE(interval.value(), 100ms);
// timerType property
QProperty<Qt::TimerType> timerType;
@@ -1285,33 +1004,43 @@ void tst_QTimer::bindToTimer()
active.setBinding([&](){ return timer.isActive(); });
QCOMPARE(active, timer.isActive());
- timer.start(1000);
+ timer.setInterval(1s);
+ timer.start();
QVERIFY(active);
timer.stop();
QVERIFY(!active);
+ // Also test that using negative interval updates the binding correctly
+ timer.setInterval(100ms);
+ timer.start();
+ 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);
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
QVERIFY(!active);
- timer.start(100);
+
+ timer.setInterval(100ms);
+ timer.start();
QVERIFY(active);
+
+ ignoreMsg();
+ timer.setInterval(-100ms);
ignoreMsg();
- timer.start(-100);
+ timer.start();
QVERIFY(!active);
}
-void tst_QTimer::bindTimer()
+void tst_QChronoTimer::bindTimer()
{
- QTimer timer;
+ QChronoTimer timer;
// singleShot property
QVERIFY(!timer.isSingleShot());
@@ -1325,19 +1054,19 @@ void tst_QTimer::bindTimer()
QVERIFY(!timer.isSingleShot());
// interval property
- QCOMPARE(timer.interval(), 0);
+ QCOMPARE(timer.interval(), 0ns);
- QProperty<int> interval;
+ QProperty<std::chrono::nanoseconds> 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);
+ interval = 10ms;
+ QCOMPARE(timer.interval(), 10ms);
+ interval = 100ms;
+ QCOMPARE(timer.interval(), 100ms);
+ timer.setInterval(50ms);
+ QCOMPARE(timer.interval(), 50ms);
+ interval = 30ms;
+ QCOMPARE(timer.interval(), 50ms);
// timerType property
QCOMPARE(timer.timerType(), Qt::CoarseTimer);
@@ -1351,21 +1080,22 @@ void tst_QTimer::bindTimer()
QCOMPARE(timer.timerType(), Qt::VeryCoarseTimer);
}
-void tst_QTimer::automatedBindingTests()
+void tst_QChronoTimer::automatedBindingTests()
{
- QTimer timer;
+ QChronoTimer timer;
QVERIFY(!timer.isSingleShot());
QTestPrivate::testReadWritePropertyBasics(timer, true, false, "singleShot");
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::singleShot");
+ qDebug("Failed property test for QChronoTimer::singleShot");
return;
}
- QCOMPARE_NE(timer.interval(), 10);
- QTestPrivate::testReadWritePropertyBasics(timer, 10, 20, "interval");
+ QCOMPARE_NE(timer.interval(), 10ms);
+ using NSec = std::chrono::nanoseconds;
+ QTestPrivate::testReadWritePropertyBasics(timer, NSec{10ms}, NSec{20ms}, "interval");
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::interval");
+ qDebug("Failed property test for QChronoTimer::interval");
return;
}
@@ -1373,48 +1103,50 @@ void tst_QTimer::automatedBindingTests()
QTestPrivate::testReadWritePropertyBasics(timer, Qt::PreciseTimer, Qt::CoarseTimer,
"timerType");
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::timerType");
+ qDebug("Failed property test for QChronoTimer::timerType");
return;
}
- timer.start(1000);
+ timer.setInterval(1s);
+ timer.start();
QVERIFY(timer.isActive());
QTestPrivate::testReadOnlyPropertyBasics(timer, true, false, "active",
[&timer]() { timer.stop(); });
if (QTest::currentTestFailed()) {
- qDebug("Failed property test for QTimer::active");
+ qDebug("Failed property test for QChronoTimer::active");
return;
}
}
-void tst_QTimer::negativeInterval()
+void tst_QChronoTimer::negativeInterval()
{
+ QChronoTimer timer;
+
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);
+ // Setting a negative interval does not change the active state.
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
QVERIFY(!timer.isActive());
- // Updating the interval to a negative value stops the timer and changes
- // the active state.
- timer.start(100ms);
+ // Starting a timer that has a positive interval, the active state is changed
+ timer.setInterval(100ms);
+ timer.start();
QVERIFY(timer.isActive());
+
ignoreMsg();
- timer.setInterval(-100);
+ // Setting a negative interval on an already running timer...
+ timer.setInterval(-100ms);
+ // ... the timer is stopped and the active state is changed
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);
+ // Calling start on a timer that has a negative interval, does not change the active state
+ timer.start();
QVERIFY(!timer.isActive());
}
@@ -1437,16 +1169,16 @@ public:
switch (callType)
{
case String:
- QTimer::singleShot(0, this, SLOT(stringSlot()));
+ QChronoTimer::singleShot(0ns, this, SLOT(stringSlot()));
break;
case PMF:
- QTimer::singleShot(0, this, &OrderHelper::pmfSlot);
+ QChronoTimer::singleShot(0ns, this, &OrderHelper::pmfSlot);
break;
case Functor:
- QTimer::singleShot(0, this, [this]() { functorSlot(); });
+ QChronoTimer::singleShot(0ns, this, [this]() { functorSlot(); });
break;
case FunctorNoCtx:
- QTimer::singleShot(0, [this]() { functorNoCtxSlot(); });
+ QChronoTimer::singleShot(0ns, [this]() { functorNoCtxSlot(); });
break;
}
}
@@ -1460,7 +1192,7 @@ public slots:
Q_DECLARE_METATYPE(OrderHelper::CallType)
-void tst_QTimer::timerOrder()
+void tst_QChronoTimer::timerOrder()
{
QFETCH(QList<OrderHelper::CallType>, calls);
@@ -1472,7 +1204,7 @@ void tst_QTimer::timerOrder()
QTRY_COMPARE(helper.calls, calls);
}
-void tst_QTimer::timerOrder_data()
+void tst_QChronoTimer::timerOrder_data()
{
QTest::addColumn<QList<OrderHelper::CallType>>("calls");
@@ -1489,7 +1221,7 @@ void tst_QTimer::timerOrder_data()
} while (std::next_permutation(calls.begin(), calls.end()));
}
-void tst_QTimer::timerOrderBackgroundThread()
+void tst_QChronoTimer::timerOrderBackgroundThread()
{
auto *thread = QThread::create([this]() { timerOrder(); });
thread->start();
@@ -1497,43 +1229,34 @@ void tst_QTimer::timerOrderBackgroundThread()
delete thread;
}
-struct StaticSingleShotUser
+void tst_QChronoTimer::timerPrecision()
{
- StaticSingleShotUser()
- {
- for (auto call : calls())
- helper.triggerCall(call);
- }
- OrderHelper helper;
-
- static QList<OrderHelper::CallType> calls()
- {
- return {OrderHelper::String, OrderHelper::PMF,
- OrderHelper::Functor, OrderHelper::FunctorNoCtx};
- }
-};
-
-// NOTE: to prevent any static initialization order fiasco, we implement
-// initMain() to instantiate staticSingleShotUser before qApp
-
-static StaticSingleShotUser *s_staticSingleShotUser = nullptr;
+ using namespace std::chrono;
+ steady_clock::time_point t1{};
+ steady_clock::time_point t2{};
-void tst_QTimer::initMain()
-{
- s_staticSingleShotUser = new StaticSingleShotUser;
-}
+ QEventLoop loop;
-void tst_QTimer::cleanupTestCase()
-{
- delete s_staticSingleShotUser;
-}
+ QChronoTimer zeroTimer{0ns};
+ zeroTimer.setTimerType(Qt::PreciseTimer);
+ zeroTimer.setSingleShot(true);
+ connect(&zeroTimer, &QChronoTimer::timeout, this, [&t1] { t1 = steady_clock::now(); });
+
+ QChronoTimer oneNSecTimer{1ns};
+ oneNSecTimer.setTimerType(Qt::PreciseTimer);
+ oneNSecTimer.setSingleShot(true);
+ connect(&oneNSecTimer, &QChronoTimer::timeout, this, [&t2, &loop] {
+ t2 = steady_clock::now();
+ loop.quit();
+ });
-void tst_QTimer::singleShot_static()
-{
- QCoreApplication::processEvents();
- QCOMPARE(s_staticSingleShotUser->helper.calls, s_staticSingleShotUser->calls());
+ zeroTimer.start();
+ oneNSecTimer.start();
+ loop.exec();
+ QCOMPARE_GT(t2, t1);
+ // qDebug() << "t2 - t1" << duration<double, std::chrono::milliseconds::period>{t2 - t1};
}
-QTEST_MAIN(tst_QTimer)
+QTEST_MAIN(tst_QChronoTimer)
-#include "tst_qtimer.moc"
+#include "tst_qchronotimer.moc"