Fix QML Timer running not being updated together with triggered signal
authorTroels Nilsson <nilsson.troels@gmail.com>
Wed, 21 Jan 2015 13:18:35 +0000 (14:18 +0100)
committerLars Knoll <lars.knoll@digia.com>
Wed, 11 Feb 2015 18:36:10 +0000 (18:36 +0000)
The running property of the QML Timer should be updated at the same
time as the triggered signal is emitted, otherwise code like e.g. the following:
    if (qmlTimer.running)
    {
        qmlTimer.stop()
    }
doesn't work as expected. In addition if the timer is stopped or restarted
between posting the QEvent_Triggered event and receiving it, the triggered
event should not be emitted. This avoids the issue of stopped timers still
emitting the triggered signal which can potentially cause problems in
existing code.

Task-number: QTBUG-44026
Change-Id: Ia14d80d152967d09adc1586467715b2e1c6662cc
Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
src/qml/types/qqmltimer.cpp
tests/auto/qml/qqmltimer/tst_qqmltimer.cpp

index ddadd6c..8cbcf7a 100644 (file)
@@ -329,7 +329,11 @@ bool QQmlTimer::event(QEvent *e)
         ticked();
         return true;
     } else if (e->type() == QEvent_Triggered) {
-        emit triggered();
+        if (d->running && d->pause.isStopped()) {
+            d->running = false;
+            emit triggered();
+            emit runningChanged();
+        }
         return true;
     }
     return QObject::event(e);
@@ -340,10 +344,8 @@ void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *)
     Q_Q(QQmlTimer);
     if (repeating || !running)
         return;
-    running = false;
     firstTick = false;
     QCoreApplication::postEvent(q, new QEvent(QEvent_Triggered));
-    emit q->runningChanged();
 }
 
 QT_END_NAMESPACE
index 3d24bcb..b1e4bca 100644 (file)
@@ -50,6 +50,23 @@ void consistentWait(int ms)
         QTest::qWait(20);
 }
 
+void eventLoopWait(int ms)
+{
+    // QTest::qWait() always calls sendPostedEvents before exiting, so we can't use it to stop
+    // between an event is posted and it is received; But we can use an event loop instead
+
+    QPauseAnimation waitTimer(ms);
+    waitTimer.start();
+    while (waitTimer.state() == QAbstractAnimation::Running)
+    {
+        QTimer timer;
+        QEventLoop eventLoop;
+        timer.start(0);
+        timer.connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
+        eventLoop.exec();
+    }
+}
+
 class tst_qqmltimer : public QObject
 {
     Q_OBJECT
@@ -69,6 +86,8 @@ private slots:
     void restartFromTriggered();
     void runningFromTriggered();
     void parentProperty();
+    void stopWhenEventPosted();
+    void restartWhenEventPosted();
 };
 
 class TimerHelper : public QObject
@@ -396,6 +415,51 @@ void tst_qqmltimer::parentProperty()
     delete timer;
 }
 
+void tst_qqmltimer::stopWhenEventPosted()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine);
+    component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), QUrl::fromLocalFile(""));
+    QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
+
+    TimerHelper helper;
+    connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+    QCOMPARE(helper.count, 0);
+
+    eventLoopWait(200);
+    QCOMPARE(helper.count, 0);
+    QVERIFY(timer->isRunning());
+    timer->stop();
+    QVERIFY(!timer->isRunning());
+
+    consistentWait(300);
+    QCOMPARE(helper.count, 0);
+}
+
+
+void tst_qqmltimer::restartWhenEventPosted()
+{
+    QQmlEngine engine;
+    QQmlComponent component(&engine);
+    component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; running: true }"), QUrl::fromLocalFile(""));
+    QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create());
+
+    TimerHelper helper;
+    connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout()));
+    QCOMPARE(helper.count, 0);
+
+    eventLoopWait(200);
+    QCOMPARE(helper.count, 0);
+    timer->restart();
+
+    consistentWait(100);
+    QCOMPARE(helper.count, 0);
+    QVERIFY(timer->isRunning());
+
+    consistentWait(200);
+    QCOMPARE(helper.count, 1);
+}
+
 QTEST_MAIN(tst_qqmltimer)
 
 #include "tst_qqmltimer.moc"