Details
-
Bug
-
Resolution: Fixed
-
P1: Critical
-
6.4.2, 6.5.1
-
None
-
198a90f56 (dev), b156a1f1f (6.7), 11dcf2d22 (6.6), 5e4b4b02f (tqtc/lts-6.5), 0c8694d8b (tqtc/lts-6.2)
Description
[This is a /possible/ regression from Qt 6.2.3, although I'm not sure if this has ever been intentionally reliable / documented / described behavior.]
Consider the following qml testcase. A 7000ms timer is started, we wait 6900ms, check that the timer is still running, then wait another 200ms and check that the timer has stopped. Running this test using qmltestrunner, default platform, is (as) stable (as a walltime based test can be).
import QtQuick 2.0 import QtTest 1.2 TestCase { Timer { id: timer interval: 7000 } function test_wait() { timer.start(); wait(6900); verify(timer.running) wait(200); verify(!timer.running) } }
Now run the same testcase, but add '-platform offscreen'. The test now becomes flaky and unreliable. TL;DR scroll to end.
The reason seems to be that wait() (qtbase/src/corelib/kernel/qtestsupport_core.cpp:72 QTest::qWait()) makes use of a QDeadLineTimer with Qt::PreciseTimer precision, which, for the sake of the wait function itself, is IMO a good thing. In contrast, QML Timer (src/qml/types/qqmltimer.cpp), which is driven by a QPauseAnimationJob (src/qml/animations/qpauseanimationjob.cpp) whose timer is a thread-local QQmlAnimationTimer which uses QUnifiedTimer which ends up using Qt::CoarseTimer because the pause is so long.
Qt::CoarseTimer (https://doc.qt.io/qt-6/qt.html#TimerType-enum) is documented to have a slack of 5% on the scheduled expiry. So when starting a coarse 7000ms timer, the timer could trigger between 6650ms and 7350ms, the exact delay depending on what time it is right now. (OT, it looks like the unix timerinfo implementation never snaps to more than 1 second.)
TL;DR: Making this test reliable is quite hard. But it would be nice if QML's own test framework's wait function(s) could be used to reliably test QML's own timers and/or animations. For test cases, and in particular the offscreen case, what are the benefits of using Qt::CoarseTimer? And also, shouldn't the user be allowed to specify whether it's OK if a QML Timer of 7000ms misses its deadline by between -350ms and +350ms?
Attachments
For Gerrit Dashboard: QTBUG-120105 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
534251,7 | Add a precision argument to Timer | dev | qt/qtdeclarative | Status: DEFERRED | +1 | 0 |
537992,3 | QmlTest: Document timer drift between TestCase and Timer | dev | qt/qtdeclarative | Status: MERGED | +2 | 0 |
540649,2 | QmlTest: Document timer drift between TestCase and Timer | 6.7 | qt/qtdeclarative | Status: MERGED | +2 | 0 |
540730,2 | QmlTest: Document timer drift between TestCase and Timer | 6.6 | qt/qtdeclarative | Status: MERGED | +2 | 0 |
540770,2 | QmlTest: Document timer drift between TestCase and Timer | tqtc/lts-6.5 | qt/tqtc-qtdeclarative | Status: MERGED | +2 | 0 |
540920,2 | QmlTest: Document timer drift between TestCase and Timer | tqtc/lts-6.2 | qt/tqtc-qtdeclarative | Status: MERGED | +2 | 0 |