tests/auto/qtimer/tst_qtimer.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the test suite of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 
       
    43 #include <QtTest/QtTest>
       
    44 
       
    45 
       
    46 #include <qtimer.h>
       
    47 #include <qthread.h>
       
    48 
       
    49 #if defined Q_OS_UNIX
       
    50 #include <unistd.h>
       
    51 #endif
       
    52 
       
    53 
       
    54 //TESTED_CLASS=
       
    55 //TESTED_FILES=
       
    56 
       
    57 class tst_QTimer : public QObject
       
    58 {
       
    59     Q_OBJECT
       
    60 
       
    61 public:
       
    62     tst_QTimer();
       
    63     virtual ~tst_QTimer();
       
    64 
       
    65 
       
    66 public slots:
       
    67     void initTestCase();
       
    68     void cleanupTestCase();
       
    69     void init();
       
    70     void cleanup();
       
    71 private slots:
       
    72     void zeroTimer();
       
    73     void singleShotTimeout();
       
    74     void timeout();
       
    75     void livelock_data();
       
    76     void livelock();
       
    77     void timerInfiniteRecursion_data();
       
    78     void timerInfiniteRecursion();
       
    79     void recurringTimer_data();
       
    80     void recurringTimer();
       
    81     void deleteLaterOnQTimer(); // long name, don't want to shadow QObject::deleteLater()
       
    82     void moveToThread();
       
    83     void restartedTimerFiresTooSoon();
       
    84     void timerFiresOnlyOncePerProcessEvents_data();
       
    85     void timerFiresOnlyOncePerProcessEvents();
       
    86     void timerIdPersistsAfterThreadExit();
       
    87     void cancelLongTimer();
       
    88 };
       
    89 
       
    90 class TimerHelper : public QObject
       
    91 {
       
    92     Q_OBJECT
       
    93 public:
       
    94     TimerHelper() : QObject(), count(0)
       
    95     {
       
    96     }
       
    97 
       
    98     int count;
       
    99 
       
   100 public slots:
       
   101     void timeout();
       
   102 };
       
   103 
       
   104 void TimerHelper::timeout()
       
   105 {
       
   106     ++count;
       
   107 }
       
   108 
       
   109 tst_QTimer::tst_QTimer()
       
   110 {
       
   111 }
       
   112 
       
   113 tst_QTimer::~tst_QTimer()
       
   114 {
       
   115 }
       
   116 
       
   117 void tst_QTimer::initTestCase()
       
   118 {
       
   119 }
       
   120 
       
   121 void tst_QTimer::cleanupTestCase()
       
   122 {
       
   123 }
       
   124 
       
   125 void tst_QTimer::init()
       
   126 {
       
   127 }
       
   128 
       
   129 void tst_QTimer::cleanup()
       
   130 {
       
   131 }
       
   132 
       
   133 void tst_QTimer::zeroTimer()
       
   134 {
       
   135     TimerHelper helper;
       
   136     QTimer timer;
       
   137     timer.setInterval(0);
       
   138     timer.start();
       
   139 
       
   140     connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
       
   141 
       
   142     QCoreApplication::processEvents();
       
   143 
       
   144     QCOMPARE(helper.count, 1);
       
   145 }
       
   146 
       
   147 void tst_QTimer::singleShotTimeout()
       
   148 {
       
   149     TimerHelper helper;
       
   150     QTimer timer;
       
   151     timer.setSingleShot(true);
       
   152 
       
   153     connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
       
   154     timer.start(100);
       
   155 
       
   156     QTest::qWait(500);
       
   157     QCOMPARE(helper.count, 1);
       
   158     QTest::qWait(500);
       
   159     QCOMPARE(helper.count, 1);
       
   160 }
       
   161 
       
   162 #if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
       
   163 // Increase wait as emulator startup can cause unexpected delays
       
   164 #define TIMEOUT_TIMEOUT 2000
       
   165 #else
       
   166 #define TIMEOUT_TIMEOUT 200
       
   167 #endif
       
   168 
       
   169 void tst_QTimer::timeout()
       
   170 {
       
   171     TimerHelper helper;
       
   172     QTimer timer;
       
   173 
       
   174     connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
       
   175     timer.start(100);
       
   176 
       
   177     QCOMPARE(helper.count, 0);
       
   178 
       
   179     QTest::qWait(TIMEOUT_TIMEOUT);
       
   180     QVERIFY(helper.count > 0);
       
   181     int oldCount = helper.count;
       
   182 
       
   183     QTest::qWait(TIMEOUT_TIMEOUT);
       
   184     QVERIFY(helper.count > oldCount);
       
   185 }
       
   186 
       
   187 
       
   188 void tst_QTimer::livelock_data()
       
   189 {
       
   190     QTest::addColumn<int>("interval");
       
   191     QTest::newRow("zero timer") << 0;
       
   192     QTest::newRow("non-zero timer") << 1;
       
   193     QTest::newRow("longer than sleep") << 20;
       
   194 }
       
   195 
       
   196 /*!
       
   197  *
       
   198  * DO NOT "FIX" THIS TEST!  it is written like this for a reason, do
       
   199  * not *change it without first dicussing it with its maintainers.
       
   200  *
       
   201 */
       
   202 class LiveLockTester : public QObject
       
   203 {
       
   204 public:
       
   205     LiveLockTester(int i)
       
   206         : interval(i),
       
   207           timeoutsForFirst(0), timeoutsForExtra(0), timeoutsForSecond(0),
       
   208           postEventAtRightTime(false)
       
   209     {
       
   210         firstTimerId = startTimer(interval);
       
   211         extraTimerId = startTimer(interval + 80);
       
   212         secondTimerId = -1; // started later
       
   213     }
       
   214 
       
   215     bool event(QEvent *e) {
       
   216         if (e->type() == 4002) {
       
   217             // got the posted event
       
   218             if (timeoutsForFirst == 1 && timeoutsForSecond == 0)
       
   219                 postEventAtRightTime = true;
       
   220             return true;
       
   221         }
       
   222         return QObject::event(e);
       
   223     }
       
   224 
       
   225     void timerEvent(QTimerEvent *te) {
       
   226         if (te->timerId() == firstTimerId) {
       
   227             if (++timeoutsForFirst == 1) {
       
   228                 killTimer(extraTimerId);
       
   229                 extraTimerId = -1;
       
   230 		QCoreApplication::postEvent(this, new QEvent(static_cast<QEvent::Type>(4002)));
       
   231                 secondTimerId = startTimer(interval);
       
   232             }
       
   233         } else if (te->timerId() == secondTimerId) {
       
   234             ++timeoutsForSecond;
       
   235         } else if (te->timerId() == extraTimerId) {
       
   236             ++timeoutsForExtra;
       
   237         }
       
   238 
       
   239         // sleep for 2ms
       
   240         QTest::qSleep(2);
       
   241         killTimer(te->timerId());
       
   242     }
       
   243 
       
   244     const int interval;
       
   245     int firstTimerId;
       
   246     int secondTimerId;
       
   247     int extraTimerId;
       
   248     int timeoutsForFirst;
       
   249     int timeoutsForExtra;
       
   250     int timeoutsForSecond;
       
   251     bool postEventAtRightTime;
       
   252 };
       
   253 
       
   254 void tst_QTimer::livelock()
       
   255 {
       
   256     /*
       
   257       New timers created in timer event handlers should not be sent
       
   258       until the next iteration of the eventloop.  Note: this test
       
   259       depends on the fact that we send posted events before timer
       
   260       events (since new posted events are not sent until the next
       
   261       iteration of the eventloop either).
       
   262     */
       
   263     QFETCH(int, interval);
       
   264     LiveLockTester tester(interval);
       
   265     QTest::qWait(180); // we have to use wait here, since we're testing timers with a non-zero timeout
       
   266     QCOMPARE(tester.timeoutsForFirst, 1);
       
   267     QCOMPARE(tester.timeoutsForExtra, 0);
       
   268     QCOMPARE(tester.timeoutsForSecond, 1);
       
   269 #if defined(Q_OS_MAC)
       
   270     QEXPECT_FAIL("zero timer", "Posted events source are handled AFTER timers", Continue);
       
   271     QEXPECT_FAIL("non-zero timer", "Posted events source are handled AFTER timers", Continue);
       
   272 #elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
       
   273     QEXPECT_FAIL("zero timer", "", Continue);
       
   274     QEXPECT_FAIL("non-zero timer", "", Continue);
       
   275 #elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
       
   276 	if (QSysInfo::WindowsVersion < QSysInfo::WV_XP)
       
   277 		QEXPECT_FAIL("non-zero timer", "Multimedia timers are not available on Windows 2000", Continue);
       
   278 #elif defined(Q_OS_WINCE)
       
   279 	QEXPECT_FAIL("non-zero timer", "Windows CE devices often too slow", Continue);
       
   280 #endif
       
   281     QVERIFY(tester.postEventAtRightTime);
       
   282 }
       
   283 
       
   284 class TimerInfiniteRecursionObject : public QObject
       
   285 {
       
   286 public:
       
   287     bool inTimerEvent;
       
   288     bool timerEventRecursed;
       
   289     int interval;
       
   290 
       
   291     TimerInfiniteRecursionObject(int interval)
       
   292         : inTimerEvent(false), timerEventRecursed(false), interval(interval)
       
   293     { }
       
   294 
       
   295     void timerEvent(QTimerEvent *timerEvent)
       
   296     {
       
   297         timerEventRecursed = inTimerEvent;
       
   298         if (timerEventRecursed) {
       
   299             // bug detected!
       
   300             return;
       
   301         }
       
   302 
       
   303         inTimerEvent = true;
       
   304 
       
   305         QEventLoop eventLoop;
       
   306         QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
       
   307         eventLoop.exec();
       
   308 
       
   309         inTimerEvent = false;
       
   310 
       
   311         killTimer(timerEvent->timerId());
       
   312     }
       
   313 };
       
   314 
       
   315 void tst_QTimer::timerInfiniteRecursion_data()
       
   316 {
       
   317     QTest::addColumn<int>("interval");
       
   318     QTest::newRow("zero timer") << 0;
       
   319     QTest::newRow("non-zero timer") << 1;
       
   320     QTest::newRow("10ms timer") << 10;
       
   321     QTest::newRow("11ms timer") << 11;
       
   322     QTest::newRow("100ms timer") << 100;
       
   323     QTest::newRow("1s timer") << 1000;
       
   324 }
       
   325 
       
   326 
       
   327 void tst_QTimer::timerInfiniteRecursion()
       
   328 {
       
   329     QFETCH(int, interval);
       
   330     TimerInfiniteRecursionObject object(interval);
       
   331     (void) object.startTimer(interval);
       
   332 
       
   333     QEventLoop eventLoop;
       
   334     QTimer::singleShot(qMax(100, interval * 2), &eventLoop, SLOT(quit()));
       
   335     eventLoop.exec();
       
   336 
       
   337     QVERIFY(!object.timerEventRecursed);
       
   338 }
       
   339 
       
   340 class RecurringTimerObject : public QObject
       
   341 {
       
   342 Q_OBJECT
       
   343 public:
       
   344     int times;
       
   345     int target;
       
   346     bool recurse;
       
   347 
       
   348     RecurringTimerObject(int target)
       
   349         : times(0), target(target), recurse(false)
       
   350     { }
       
   351 
       
   352     void timerEvent(QTimerEvent *timerEvent)
       
   353     {
       
   354         if (++times == target) {
       
   355             killTimer(timerEvent->timerId());
       
   356             emit done();
       
   357         } if (recurse) {
       
   358             QEventLoop eventLoop;
       
   359             QTimer::singleShot(100, &eventLoop, SLOT(quit()));
       
   360             eventLoop.exec();
       
   361         }
       
   362     }
       
   363 
       
   364 signals:
       
   365     void done();
       
   366 };
       
   367 
       
   368 void tst_QTimer::recurringTimer_data()
       
   369 {
       
   370     QTest::addColumn<int>("interval");
       
   371     QTest::newRow("zero timer") << 0;
       
   372     QTest::newRow("non-zero timer") << 1;
       
   373 }
       
   374 
       
   375 void tst_QTimer::recurringTimer()
       
   376 {
       
   377     const int target = 5;
       
   378     QFETCH(int, interval);
       
   379 
       
   380     {
       
   381         RecurringTimerObject object(target);
       
   382         QObject::connect(&object, SIGNAL(done()), &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   383         (void) object.startTimer(interval);
       
   384         QTestEventLoop::instance().enterLoop(5);
       
   385 
       
   386         QCOMPARE(object.times, target);
       
   387     }
       
   388 
       
   389     {
       
   390         // make sure that eventloop recursion doesn't effect timer recurrance
       
   391         RecurringTimerObject object(target);
       
   392         object.recurse = true;
       
   393 
       
   394         QObject::connect(&object, SIGNAL(done()), &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   395         (void) object.startTimer(interval);
       
   396         QTestEventLoop::instance().enterLoop(5);
       
   397 
       
   398         QCOMPARE(object.times, target);
       
   399     }
       
   400 }
       
   401 
       
   402 void tst_QTimer::deleteLaterOnQTimer()
       
   403 {
       
   404     QTimer *timer = new QTimer;
       
   405     connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
       
   406     connect(timer, SIGNAL(destroyed()), &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   407     timer->setInterval(1);
       
   408     timer->setSingleShot(true);
       
   409     timer->start();
       
   410     QPointer<QTimer> pointer = timer;
       
   411     QTestEventLoop::instance().enterLoop(5);
       
   412     QVERIFY(pointer.isNull());
       
   413 }
       
   414 
       
   415 #if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
       
   416 // Increase wait as emulator startup can cause unexpected delays
       
   417 #define MOVETOTHREAD_TIMEOUT 200
       
   418 #define MOVETOTHREAD_WAIT 5000
       
   419 #else
       
   420 #define MOVETOTHREAD_TIMEOUT 200
       
   421 #define MOVETOTHREAD_WAIT 300
       
   422 #endif
       
   423 
       
   424 void tst_QTimer::moveToThread()
       
   425 {
       
   426     QTimer ti1;
       
   427     QTimer ti2;
       
   428     ti1.start(MOVETOTHREAD_TIMEOUT);
       
   429     ti2.start(MOVETOTHREAD_TIMEOUT);
       
   430     QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
       
   431     QThread tr;
       
   432     ti1.moveToThread(&tr);
       
   433     connect(&ti1,SIGNAL(timeout()), &tr, SLOT(quit()));
       
   434     tr.start();
       
   435     QTimer ti3;
       
   436     ti3.start(MOVETOTHREAD_TIMEOUT);
       
   437     QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
       
   438     QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
       
   439     QTest::qWait(MOVETOTHREAD_WAIT);
       
   440     QVERIFY(tr.wait());
       
   441     ti2.stop();
       
   442     QTimer ti4;
       
   443     ti4.start(MOVETOTHREAD_TIMEOUT);
       
   444     ti3.stop();
       
   445     ti2.start(MOVETOTHREAD_TIMEOUT);
       
   446     ti3.start(MOVETOTHREAD_TIMEOUT);
       
   447     QVERIFY((ti4.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
       
   448     QVERIFY((ti3.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
       
   449     QVERIFY((ti3.timerId() & 0xffffff) != (ti1.timerId() & 0xffffff));
       
   450 }
       
   451 
       
   452 class RestartedTimerFiresTooSoonObject : public QObject
       
   453 {
       
   454     Q_OBJECT
       
   455 
       
   456 public:
       
   457     QBasicTimer m_timer;
       
   458 
       
   459     int m_interval;
       
   460     QTime m_startedTime;
       
   461     QEventLoop eventLoop;
       
   462 
       
   463     inline RestartedTimerFiresTooSoonObject()
       
   464         : QObject(), m_interval(0)
       
   465     { }
       
   466 
       
   467     void timerFired()
       
   468     {
       
   469         static int interval = 1000;
       
   470 
       
   471         m_interval = interval;
       
   472         m_startedTime.start();
       
   473         m_timer.start(interval, this);
       
   474 
       
   475         // alternate between single-shot and 1 sec
       
   476         interval = interval ? 0 : 1000;
       
   477     }
       
   478 
       
   479     void timerEvent(QTimerEvent* ev)
       
   480     {
       
   481         if (ev->timerId() != m_timer.timerId())
       
   482             return;
       
   483 
       
   484         m_timer.stop();
       
   485 
       
   486         QTime now = QTime::currentTime();
       
   487         int elapsed = m_startedTime.elapsed();
       
   488 
       
   489         if (elapsed < m_interval / 2) {
       
   490             // severely too early!
       
   491             m_timer.stop();
       
   492             eventLoop.exit(-1);
       
   493             return;
       
   494         }
       
   495 
       
   496         timerFired();
       
   497 
       
   498         // don't do this forever
       
   499         static int count = 0;
       
   500         if (count++ > 20) {
       
   501             m_timer.stop();
       
   502             eventLoop.quit();
       
   503             return;
       
   504         }
       
   505     }
       
   506 };
       
   507 
       
   508 void tst_QTimer::restartedTimerFiresTooSoon()
       
   509 {
       
   510     RestartedTimerFiresTooSoonObject object;
       
   511     object.timerFired();
       
   512     QVERIFY(object.eventLoop.exec() == 0);
       
   513 }
       
   514 
       
   515 class LongLastingSlotClass : public QObject
       
   516 {
       
   517     Q_OBJECT
       
   518 
       
   519 public:
       
   520     LongLastingSlotClass(QTimer *timer) : count(0), timer(timer) {}
       
   521 
       
   522 public slots:
       
   523     void longLastingSlot()
       
   524     {
       
   525         // Don't use timers for this, because we are testing them.
       
   526         QTime time;
       
   527         time.start();
       
   528         while (time.elapsed() < 200) {
       
   529             for (int c = 0; c < 100000; c++) {} // Mindless looping.
       
   530         }
       
   531         if (++count >= 2) {
       
   532             timer->stop();
       
   533         }
       
   534     }
       
   535 
       
   536 public:
       
   537     int count;
       
   538     QTimer *timer;
       
   539 };
       
   540 
       
   541 void tst_QTimer::timerFiresOnlyOncePerProcessEvents_data()
       
   542 {
       
   543     QTest::addColumn<int>("interval");
       
   544     QTest::newRow("zero timer") << 0;
       
   545     QTest::newRow("non-zero timer") << 10;
       
   546 }
       
   547 
       
   548 void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
       
   549 {
       
   550     QFETCH(int, interval);
       
   551 
       
   552     QTimer t;
       
   553     LongLastingSlotClass longSlot(&t);
       
   554     t.start(interval);
       
   555     connect(&t, SIGNAL(timeout()), &longSlot, SLOT(longLastingSlot()));
       
   556     // Loop because there may be other events pending.
       
   557     while (longSlot.count == 0) {
       
   558         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
       
   559     }
       
   560 
       
   561     QCOMPARE(longSlot.count, 1);
       
   562 }
       
   563 
       
   564 class TimerIdPersistsAfterThreadExitThread : public QThread
       
   565 {
       
   566 public:
       
   567     QTimer *timer;
       
   568     int timerId, returnValue;
       
   569 
       
   570     TimerIdPersistsAfterThreadExitThread()
       
   571         : QThread(), timer(0), timerId(-1), returnValue(-1)
       
   572     { }
       
   573     ~TimerIdPersistsAfterThreadExitThread()
       
   574     {
       
   575         delete timer;
       
   576     }
       
   577 
       
   578     void run()
       
   579     {
       
   580         QEventLoop eventLoop;
       
   581         timer = new QTimer;
       
   582         connect(timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
       
   583         timer->start(100);
       
   584         timerId = timer->timerId();
       
   585         returnValue = eventLoop.exec();
       
   586     }
       
   587 };
       
   588 
       
   589 void tst_QTimer::timerIdPersistsAfterThreadExit()
       
   590 {
       
   591     TimerIdPersistsAfterThreadExitThread thread;
       
   592     thread.start();
       
   593     QVERIFY(thread.wait(30000));
       
   594     QCOMPARE(thread.returnValue, 0);
       
   595 
       
   596     // even though the thread has exited, and the event dispatcher destroyed, the timer is still
       
   597     // "active", meaning the timer id should NOT be reused (i.e. the event dispatcher should not
       
   598     // have unregistered it)
       
   599     int timerId = thread.startTimer(100);
       
   600     QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff));
       
   601 }
       
   602 
       
   603 void tst_QTimer::cancelLongTimer()
       
   604 {
       
   605     QTimer timer;
       
   606     timer.setSingleShot(true);
       
   607     timer.start(1000 * 60 * 60); //set timer for 1 hour (which would overflow Symbian RTimer)
       
   608     QCoreApplication::processEvents();
       
   609     QVERIFY(timer.isActive()); //if the timer completes immediately with an error, then this will fail
       
   610     timer.stop();
       
   611     QVERIFY(!timer.isActive());
       
   612 }
       
   613 
       
   614 QTEST_MAIN(tst_QTimer)
       
   615 #include "tst_qtimer.moc"