tests/auto/qeventloop/tst_qeventloop.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 <qabstracteventdispatcher.h>
       
    47 #include <qcoreapplication.h>
       
    48 #include <qcoreevent.h>
       
    49 #include <qeventloop.h>
       
    50 #include <qmutex.h>
       
    51 #include <qthread.h>
       
    52 #include <qtimer.h>
       
    53 #include <qwaitcondition.h>
       
    54 #include <QTcpServer>
       
    55 #include <QTcpSocket>
       
    56 
       
    57 #ifdef Q_OS_SYMBIAN
       
    58 #include <e32base.h>
       
    59 #include <unistd.h>
       
    60 #endif
       
    61 
       
    62 //TESTED_CLASS=
       
    63 //TESTED_FILES=
       
    64 
       
    65 class EventLoopExiter : public QObject
       
    66 {
       
    67     Q_OBJECT
       
    68     QEventLoop *eventLoop;
       
    69 public:
       
    70     inline EventLoopExiter(QEventLoop *el)
       
    71         : eventLoop(el)
       
    72     { }
       
    73 public slots:
       
    74     void exit();
       
    75     void exit1();
       
    76     void exit2();
       
    77 };
       
    78 
       
    79 void EventLoopExiter::exit()
       
    80 { eventLoop->exit(); }
       
    81 
       
    82 void EventLoopExiter::exit1()
       
    83 { eventLoop->exit(1); }
       
    84 
       
    85 void EventLoopExiter::exit2()
       
    86 { eventLoop->exit(2); }
       
    87 
       
    88 class EventLoopThread : public QThread
       
    89 {
       
    90     Q_OBJECT
       
    91 signals:
       
    92     void checkPoint();
       
    93 public:
       
    94     QEventLoop *eventLoop;
       
    95     void run();
       
    96 };
       
    97 
       
    98 void EventLoopThread::run()
       
    99 {
       
   100     eventLoop = new QEventLoop;
       
   101     emit checkPoint();
       
   102     (void) eventLoop->exec();
       
   103     delete eventLoop;
       
   104     eventLoop = 0;
       
   105 }
       
   106 
       
   107 class MultipleExecThread : public QThread
       
   108 {
       
   109     Q_OBJECT
       
   110 signals:
       
   111     void checkPoint();
       
   112 public:
       
   113     QMutex mutex;
       
   114     QWaitCondition cond;
       
   115     void run()
       
   116     {
       
   117         QMutexLocker locker(&mutex);
       
   118         // this exec should work
       
   119 
       
   120         cond.wakeOne();
       
   121         cond.wait(&mutex);
       
   122 
       
   123         QTimer timer;
       
   124         connect(&timer, SIGNAL(timeout()), SLOT(quit()), Qt::DirectConnection);
       
   125         timer.setInterval(1000);
       
   126         timer.start();
       
   127         (void) exec();
       
   128 
       
   129         // this should return immediately, since exit() has been called
       
   130         cond.wakeOne();
       
   131         cond.wait(&mutex);
       
   132         QEventLoop eventLoop;
       
   133         (void) eventLoop.exec();
       
   134     }
       
   135 };
       
   136 
       
   137 class StartStopEvent: public QEvent
       
   138 {
       
   139 public:
       
   140     StartStopEvent(int type, QEventLoop *loop = 0)
       
   141         : QEvent(Type(type)), el(loop)
       
   142     { }
       
   143 
       
   144     QEventLoop *el;
       
   145 };
       
   146 
       
   147 class EventLoopExecutor : public QObject
       
   148 {
       
   149     Q_OBJECT
       
   150     QEventLoop *eventLoop;
       
   151 public:
       
   152     int returnCode;
       
   153     EventLoopExecutor(QEventLoop *eventLoop)
       
   154         : QObject(), eventLoop(eventLoop), returnCode(-42)
       
   155     {
       
   156     }
       
   157 public slots:
       
   158     void exec()
       
   159     {
       
   160         QTimer::singleShot(100, eventLoop, SLOT(quit()));
       
   161         // this should return immediately, and the timer event should be delivered to
       
   162         // tst_QEventLoop::exec() test, letting the test complete
       
   163         returnCode = eventLoop->exec();
       
   164     }
       
   165 };
       
   166 
       
   167 #ifndef QT_NO_EXCEPTIONS
       
   168 class QEventLoopTestException { };
       
   169 
       
   170 class ExceptionThrower : public QObject
       
   171 {
       
   172     Q_OBJECT
       
   173 public:
       
   174     ExceptionThrower() : QObject() { }
       
   175 public slots:
       
   176     void throwException()
       
   177     {
       
   178         QEventLoopTestException e;
       
   179         throw e;
       
   180     }
       
   181 };
       
   182 #endif
       
   183 
       
   184 class tst_QEventLoop : public QObject
       
   185 {
       
   186     Q_OBJECT
       
   187 public:
       
   188     tst_QEventLoop();
       
   189     ~tst_QEventLoop();
       
   190 public slots:
       
   191     void init();
       
   192     void cleanup();
       
   193 private slots:
       
   194     // This test *must* run first. See the definition for why.
       
   195     void onlySymbianActiveScheduler();
       
   196     void symbianNestedActiveSchedulerLoop_data();
       
   197     void symbianNestedActiveSchedulerLoop();
       
   198     void processEvents();
       
   199     void exec();
       
   200     void exit();
       
   201     void wakeUp();
       
   202     void quit();
       
   203     void processEventsExcludeSocket();
       
   204     void processEventsExcludeTimers();
       
   205 
       
   206     // keep this test last:
       
   207     void nestedLoops();
       
   208 
       
   209 protected:
       
   210     void customEvent(QEvent *e);
       
   211 };
       
   212 
       
   213 tst_QEventLoop::tst_QEventLoop()
       
   214 { }
       
   215 
       
   216 tst_QEventLoop::~tst_QEventLoop()
       
   217 { }
       
   218 
       
   219 void tst_QEventLoop::init()
       
   220 { }
       
   221 
       
   222 void tst_QEventLoop::cleanup()
       
   223 { }
       
   224 
       
   225 #ifdef Q_OS_SYMBIAN
       
   226 class OnlySymbianActiveScheduler_helper : public QObject
       
   227 {
       
   228     Q_OBJECT
       
   229 
       
   230 public:
       
   231     OnlySymbianActiveScheduler_helper(int fd, QTimer *zeroTimer)
       
   232         : fd(fd),
       
   233           timerCount(0),
       
   234           zeroTimer(zeroTimer),
       
   235           zeroTimerCount(0),
       
   236           notifierCount(0)
       
   237     {
       
   238     }
       
   239     ~OnlySymbianActiveScheduler_helper() {}
       
   240 
       
   241 public slots:
       
   242     void timerSlot()
       
   243     {
       
   244         // Let all the events occur twice so we know they reactivated after
       
   245         // each occurrence.
       
   246         if (++timerCount >= 2) {
       
   247             // This will hopefully run last, so stop the active scheduler.
       
   248             CActiveScheduler::Stop();
       
   249         }
       
   250     }
       
   251     void zeroTimerSlot()
       
   252     {
       
   253         if (++zeroTimerCount >= 2) {
       
   254             zeroTimer->stop();
       
   255         }
       
   256     }
       
   257     void notifierSlot()
       
   258     {
       
   259         if (++notifierCount >= 2) {
       
   260             char dummy;
       
   261             ::read(fd, &dummy, 1);
       
   262         }
       
   263     }
       
   264 
       
   265 private:
       
   266     int fd;
       
   267     int timerCount;
       
   268     QTimer *zeroTimer;
       
   269     int zeroTimerCount;
       
   270     int notifierCount;
       
   271 };
       
   272 #endif
       
   273 
       
   274 void tst_QEventLoop::onlySymbianActiveScheduler() {
       
   275 #ifndef Q_OS_SYMBIAN
       
   276     QSKIP("This is a Symbian-only test.", SkipAll);
       
   277 #else
       
   278     // In here we try to use timers and sockets exclusively using the Symbian
       
   279     // active scheduler and no processEvents().
       
   280     // This test should therefore be run first, so that we can verify that
       
   281     // the first occurrence of processEvents does not do any initalization that
       
   282     // we depend on.
       
   283 
       
   284     // Open up a pipe so we can test socket notifiers.
       
   285     int pipeEnds[2];
       
   286     if (::pipe(pipeEnds) != 0) {
       
   287         QFAIL("Could not open pipe");
       
   288     }
       
   289     QSocketNotifier notifier(pipeEnds[0], QSocketNotifier::Read);
       
   290     QSignalSpy notifierSpy(&notifier, SIGNAL(activated(int)));
       
   291     char dummy = 1;
       
   292     ::write(pipeEnds[1], &dummy, 1);
       
   293 
       
   294     QTimer zeroTimer;
       
   295     QSignalSpy zeroTimerSpy(&zeroTimer, SIGNAL(timeout()));
       
   296     zeroTimer.setInterval(0);
       
   297     zeroTimer.start();
       
   298 
       
   299     QTimer timer;
       
   300     QSignalSpy timerSpy(&timer, SIGNAL(timeout()));
       
   301     timer.setInterval(2000); // Generous timeout or this test will fail if there is high load
       
   302     timer.start();
       
   303 
       
   304     OnlySymbianActiveScheduler_helper helper(pipeEnds[0], &zeroTimer);
       
   305     connect(&notifier, SIGNAL(activated(int)), &helper, SLOT(notifierSlot()));
       
   306     connect(&zeroTimer, SIGNAL(timeout()), &helper, SLOT(zeroTimerSlot()));
       
   307     connect(&timer, SIGNAL(timeout()), &helper, SLOT(timerSlot()));
       
   308 
       
   309     CActiveScheduler::Start();
       
   310 
       
   311     ::close(pipeEnds[1]);
       
   312     ::close(pipeEnds[0]);
       
   313 
       
   314     QCOMPARE(notifierSpy.count(), 2);
       
   315     QCOMPARE(zeroTimerSpy.count(), 2);
       
   316     QCOMPARE(timerSpy.count(), 2);
       
   317 #endif
       
   318 }
       
   319 
       
   320 void tst_QEventLoop::processEvents()
       
   321 {
       
   322     QSignalSpy spy1(QAbstractEventDispatcher::instance(), SIGNAL(aboutToBlock()));
       
   323     QSignalSpy spy2(QAbstractEventDispatcher::instance(), SIGNAL(awake()));
       
   324 
       
   325     QEventLoop eventLoop;
       
   326 
       
   327     QCoreApplication::postEvent(&eventLoop, new QEvent(QEvent::User));
       
   328 
       
   329     // process posted events, QEventLoop::processEvents() should return
       
   330     // true
       
   331     QVERIFY(eventLoop.processEvents());
       
   332     QCOMPARE(spy1.count(), 0);
       
   333     QCOMPARE(spy2.count(), 1);
       
   334 
       
   335     // allow any session manager to complete its handshake, so that
       
   336     // there are no pending events left.
       
   337     while (eventLoop.processEvents())
       
   338         ;
       
   339 
       
   340     // On mac we get application started events at this point,
       
   341     // so process events one more time just to be sure.
       
   342     eventLoop.processEvents();
       
   343 
       
   344     // no events to process, QEventLoop::processEvents() should return
       
   345     // false
       
   346     spy1.clear();
       
   347     spy2.clear();
       
   348     QVERIFY(!eventLoop.processEvents());
       
   349     QCOMPARE(spy1.count(), 0);
       
   350     QCOMPARE(spy2.count(), 1);
       
   351 
       
   352     // make sure the test doesn't block forever
       
   353     int timerId = startTimer(100);
       
   354 
       
   355     // wait for more events to process, QEventLoop::processEvents()
       
   356     // should return true
       
   357     spy1.clear();
       
   358     spy2.clear();
       
   359     QVERIFY(eventLoop.processEvents(QEventLoop::WaitForMoreEvents));
       
   360 
       
   361     // Verify that the eventloop has blocked and woken up. Some eventloops
       
   362     // may block and wake up multiple times.
       
   363     QVERIFY(spy1.count() > 0);
       
   364     QVERIFY(spy2.count() > 0);
       
   365     // We should get one awake for each aboutToBlock, plus one awake when
       
   366     // processEvents is entered.
       
   367     QVERIFY(spy2.count() >= spy1.count());
       
   368 
       
   369     killTimer(timerId);
       
   370 }
       
   371 
       
   372 #if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
       
   373 // Symbian needs bit longer timeout for emulator, as emulator startup causes additional delay
       
   374 #  define EXEC_TIMEOUT 1000
       
   375 #else
       
   376 #  define EXEC_TIMEOUT 100
       
   377 #endif
       
   378 
       
   379 
       
   380 void tst_QEventLoop::exec()
       
   381 {
       
   382     {
       
   383         QEventLoop eventLoop;
       
   384         EventLoopExiter exiter(&eventLoop);
       
   385         int returnCode;
       
   386 
       
   387         QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit()));
       
   388         returnCode = eventLoop.exec();
       
   389         QCOMPARE(returnCode, 0);
       
   390 
       
   391         QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit1()));
       
   392         returnCode = eventLoop.exec();
       
   393         QCOMPARE(returnCode, 1);
       
   394 
       
   395         QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit2()));
       
   396         returnCode = eventLoop.exec();
       
   397         QCOMPARE(returnCode, 2);
       
   398     }
       
   399 
       
   400     {
       
   401         // calling exec() after exit()/quit() should return immediately
       
   402         MultipleExecThread thread;
       
   403 
       
   404         // start thread and wait for checkpoint
       
   405         thread.mutex.lock();
       
   406         thread.start();
       
   407         thread.cond.wait(&thread.mutex);
       
   408 
       
   409         // make sure the eventloop runs
       
   410         QSignalSpy spy(QAbstractEventDispatcher::instance(&thread), SIGNAL(awake()));
       
   411         thread.cond.wakeOne();
       
   412         thread.cond.wait(&thread.mutex);
       
   413         QVERIFY(spy.count() > 0);
       
   414 
       
   415         // exec should return immediately
       
   416         spy.clear();
       
   417         thread.cond.wakeOne();
       
   418         thread.mutex.unlock();
       
   419         thread.wait();
       
   420         QCOMPARE(spy.count(), 0);
       
   421     }
       
   422 
       
   423     {
       
   424         // a single instance of QEventLoop should not be allowed to recurse into exec()
       
   425         QEventLoop eventLoop;
       
   426         EventLoopExecutor executor(&eventLoop);
       
   427 
       
   428         QTimer::singleShot(EXEC_TIMEOUT, &executor, SLOT(exec()));
       
   429         int returnCode = eventLoop.exec();
       
   430         QCOMPARE(returnCode, 0);
       
   431         QCOMPARE(executor.returnCode, -1);
       
   432     }
       
   433 
       
   434 #if !defined(QT_NO_EXCEPTIONS) && !defined(Q_OS_WINCE_WM) && !defined(Q_OS_SYMBIAN)
       
   435     // Windows Mobile cannot handle cross library exceptions
       
   436     // qobject.cpp will try to rethrow the exception after handling
       
   437     // which causes gwes.exe to crash
       
   438 
       
   439     // Symbian doesn't propagate exceptions from eventloop, but converts them to
       
   440     // CActiveScheduler errors instead -> this test will hang.
       
   441     {
       
   442         // QEventLoop::exec() is exception safe
       
   443         QEventLoop eventLoop;
       
   444         int caughtExceptions = 0;
       
   445 
       
   446         try {
       
   447             ExceptionThrower exceptionThrower;
       
   448             QTimer::singleShot(EXEC_TIMEOUT, &exceptionThrower, SLOT(throwException()));
       
   449             (void) eventLoop.exec();
       
   450         } catch (...) {
       
   451             ++caughtExceptions;
       
   452         }
       
   453         try {
       
   454             ExceptionThrower exceptionThrower;
       
   455             QTimer::singleShot(EXEC_TIMEOUT, &exceptionThrower, SLOT(throwException()));
       
   456             (void) eventLoop.exec();
       
   457         } catch (...) {
       
   458             ++caughtExceptions;
       
   459         }
       
   460         QCOMPARE(caughtExceptions, 2);
       
   461     }
       
   462 #endif
       
   463 }
       
   464 
       
   465 void tst_QEventLoop::exit()
       
   466 { DEPENDS_ON(exec()); }
       
   467 
       
   468 void tst_QEventLoop::wakeUp()
       
   469 {
       
   470     EventLoopThread thread;
       
   471     QEventLoop eventLoop;
       
   472     connect(&thread, SIGNAL(checkPoint()), &eventLoop, SLOT(quit()));
       
   473     connect(&thread, SIGNAL(finished()), &eventLoop, SLOT(quit()));
       
   474 
       
   475     thread.start();
       
   476     (void) eventLoop.exec();
       
   477 
       
   478     QSignalSpy spy(QAbstractEventDispatcher::instance(&thread), SIGNAL(awake()));
       
   479     thread.eventLoop->wakeUp();
       
   480 
       
   481     // give the thread time to wake up
       
   482     QTimer::singleShot(1000, &eventLoop, SLOT(quit()));
       
   483     (void) eventLoop.exec();
       
   484 
       
   485     QVERIFY(spy.count() > 0);
       
   486 
       
   487     thread.quit();
       
   488     (void) eventLoop.exec();
       
   489 }
       
   490 
       
   491 void tst_QEventLoop::quit()
       
   492 {
       
   493     QEventLoop eventLoop;
       
   494     int returnCode;
       
   495 
       
   496     QTimer::singleShot(100, &eventLoop, SLOT(quit()));
       
   497     returnCode = eventLoop.exec();
       
   498     QCOMPARE(returnCode, 0);
       
   499 }
       
   500 
       
   501 
       
   502 void tst_QEventLoop::nestedLoops()
       
   503 {
       
   504     QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
       
   505     QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
       
   506     QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
       
   507 
       
   508     // without the fix, this will *wedge* and never return
       
   509     QTest::qWait(1000);
       
   510 }
       
   511 
       
   512 void tst_QEventLoop::customEvent(QEvent *e)
       
   513 {
       
   514     if (e->type() == QEvent::User) {
       
   515         QEventLoop loop;
       
   516         QCoreApplication::postEvent(this, new StartStopEvent(int(QEvent::User) + 1, &loop));
       
   517         loop.exec();
       
   518     } else {
       
   519         static_cast<StartStopEvent *>(e)->el->exit();
       
   520     }
       
   521 }
       
   522 
       
   523 class SocketEventsTester: public QObject
       
   524 {
       
   525     Q_OBJECT
       
   526 public:
       
   527     SocketEventsTester()
       
   528     {
       
   529         socket = 0;
       
   530         server = 0;
       
   531         dataArrived = false;
       
   532         testResult = false;
       
   533     }
       
   534     ~SocketEventsTester()
       
   535     {
       
   536         delete socket;
       
   537         delete server;
       
   538     }
       
   539     bool init()
       
   540     {
       
   541         bool ret = false;
       
   542         server = new QTcpServer();
       
   543         socket = new QTcpSocket();
       
   544         connect(server, SIGNAL(newConnection()), this, SLOT(sendHello()));
       
   545         connect(socket, SIGNAL(readyRead()), this, SLOT(sendAck()), Qt::DirectConnection);
       
   546         if((ret = server->listen(QHostAddress::LocalHost, 0))) {
       
   547             socket->connectToHost(server->serverAddress(), server->serverPort());
       
   548             socket->waitForConnected();
       
   549         }
       
   550         return ret;
       
   551     }
       
   552 
       
   553     QTcpSocket *socket;
       
   554     QTcpServer *server;
       
   555     bool dataArrived;
       
   556     bool testResult;
       
   557 public slots:
       
   558     void sendAck()
       
   559     {
       
   560         dataArrived = true;
       
   561     }
       
   562     void sendHello()
       
   563     {
       
   564         char data[10] ="HELLO";
       
   565         qint64 size = sizeof(data);
       
   566 
       
   567         QTcpSocket *serverSocket = server->nextPendingConnection();
       
   568         serverSocket->write(data, size);
       
   569         serverSocket->flush();
       
   570         QCoreApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
       
   571         testResult = dataArrived;
       
   572         serverSocket->close();
       
   573         QThread::currentThread()->exit(0);
       
   574     }
       
   575 };
       
   576 
       
   577 class SocketTestThread : public QThread
       
   578 {
       
   579     Q_OBJECT
       
   580 public:
       
   581     SocketTestThread():QThread(0),testResult(false){};
       
   582     void run()
       
   583     {
       
   584         SocketEventsTester *tester = new SocketEventsTester();
       
   585         if (tester->init())
       
   586             exec();
       
   587         testResult = tester->testResult;
       
   588         delete tester;
       
   589     }
       
   590      bool testResult;
       
   591 };
       
   592 
       
   593 void tst_QEventLoop::processEventsExcludeSocket()
       
   594 {
       
   595     SocketTestThread thread;
       
   596     thread.start();
       
   597     QVERIFY(thread.wait());
       
   598     QVERIFY(!thread.testResult);
       
   599 }
       
   600 
       
   601 class TimerReceiver : public QObject
       
   602 {
       
   603 public:
       
   604     int gotTimerEvent;
       
   605 
       
   606     TimerReceiver()
       
   607         : QObject(), gotTimerEvent(-1)
       
   608     { }
       
   609 
       
   610     void timerEvent(QTimerEvent *event)
       
   611     {
       
   612         gotTimerEvent = event->timerId();
       
   613     }
       
   614 };
       
   615 
       
   616 void tst_QEventLoop::processEventsExcludeTimers()
       
   617 {
       
   618     TimerReceiver timerReceiver;
       
   619     int timerId = timerReceiver.startTimer(0);
       
   620 
       
   621     QEventLoop eventLoop;
       
   622 
       
   623     // normal process events will send timers
       
   624     eventLoop.processEvents();
       
   625     QCOMPARE(timerReceiver.gotTimerEvent, timerId);
       
   626     timerReceiver.gotTimerEvent = -1;
       
   627 
       
   628     // normal process events will send timers
       
   629     eventLoop.processEvents(QEventLoop::X11ExcludeTimers);
       
   630 #if !defined(Q_OS_UNIX) || defined(Q_OS_SYMBIAN)
       
   631     QEXPECT_FAIL("", "X11ExcludeTimers only works on UN*X", Continue);
       
   632 #endif
       
   633     QCOMPARE(timerReceiver.gotTimerEvent, -1);
       
   634     timerReceiver.gotTimerEvent = -1;
       
   635 
       
   636     // resume timer processing
       
   637     eventLoop.processEvents();
       
   638     QCOMPARE(timerReceiver.gotTimerEvent, timerId);
       
   639     timerReceiver.gotTimerEvent = -1;
       
   640 }
       
   641 
       
   642 #ifdef Q_OS_SYMBIAN
       
   643 class DummyActiveObject : public CActive
       
   644 {
       
   645 public:
       
   646     DummyActiveObject(int levels);
       
   647     ~DummyActiveObject();
       
   648 
       
   649     void Start();
       
   650 
       
   651 protected:
       
   652     void DoCancel();
       
   653     void RunL();
       
   654 
       
   655 public:
       
   656     bool succeeded;
       
   657 
       
   658 private:
       
   659     RTimer m_rTimer;
       
   660     int remainingLevels;
       
   661 };
       
   662 
       
   663 class ActiveSchedulerLoop : public QObject
       
   664 {
       
   665 public:
       
   666     ActiveSchedulerLoop(int levels) : succeeded(false), timerId(-1), remainingLevels(levels) {}
       
   667     ~ActiveSchedulerLoop() {}
       
   668 
       
   669     void timerEvent(QTimerEvent *e);
       
   670 
       
   671 public:
       
   672     bool succeeded;
       
   673     int timerId;
       
   674     int remainingLevels;
       
   675 };
       
   676 
       
   677 DummyActiveObject::DummyActiveObject(int levels)
       
   678     : CActive(CActive::EPriorityStandard),
       
   679       succeeded(false),
       
   680       remainingLevels(levels)
       
   681 {
       
   682     m_rTimer.CreateLocal();
       
   683 }
       
   684 
       
   685 DummyActiveObject::~DummyActiveObject()
       
   686 {
       
   687     Cancel();
       
   688     m_rTimer.Close();
       
   689 }
       
   690 
       
   691 void DummyActiveObject::DoCancel()
       
   692 {
       
   693     m_rTimer.Cancel();
       
   694 }
       
   695 
       
   696 void DummyActiveObject::RunL()
       
   697 {
       
   698     if (remainingLevels - 1 <= 0) {
       
   699         ActiveSchedulerLoop loop(remainingLevels - 1);
       
   700         loop.timerId = loop.startTimer(0);
       
   701         QCoreApplication::processEvents();
       
   702 
       
   703         succeeded = loop.succeeded;
       
   704     } else {
       
   705         succeeded = true;
       
   706     }
       
   707     CActiveScheduler::Stop();
       
   708 }
       
   709 
       
   710 void DummyActiveObject::Start()
       
   711 {
       
   712     m_rTimer.After(iStatus, 100000); // 100 ms
       
   713     SetActive();
       
   714 }
       
   715 
       
   716 void ActiveSchedulerLoop::timerEvent(QTimerEvent *e)
       
   717 {
       
   718     Q_UNUSED(e);
       
   719     DummyActiveObject *dummy = new(ELeave) DummyActiveObject(remainingLevels);
       
   720     CActiveScheduler::Add(dummy);
       
   721 
       
   722     dummy->Start();
       
   723 
       
   724     CActiveScheduler::Start();
       
   725 
       
   726     succeeded = dummy->succeeded;
       
   727 
       
   728     delete dummy;
       
   729 
       
   730     killTimer(timerId);
       
   731 }
       
   732 
       
   733 // We cannot trap panics when the test case fails, so run it in a different thread instead.
       
   734 class ActiveSchedulerThread : public QThread
       
   735 {
       
   736 public:
       
   737     ActiveSchedulerThread(QEventLoop::ProcessEventsFlag flags);
       
   738     ~ActiveSchedulerThread();
       
   739 
       
   740 protected:
       
   741     void run();
       
   742 
       
   743 public:
       
   744     volatile bool succeeded;
       
   745 
       
   746 private:
       
   747     QEventLoop::ProcessEventsFlag m_flags;
       
   748 };
       
   749 
       
   750 ActiveSchedulerThread::ActiveSchedulerThread(QEventLoop::ProcessEventsFlag flags)
       
   751     : succeeded(false),
       
   752       m_flags(flags)
       
   753 {
       
   754 }
       
   755 
       
   756 ActiveSchedulerThread::~ActiveSchedulerThread()
       
   757 {
       
   758 }
       
   759 
       
   760 void ActiveSchedulerThread::run()
       
   761 {
       
   762     ActiveSchedulerLoop loop(2);
       
   763     loop.timerId = loop.startTimer(0);
       
   764     // It may panic in here if the active scheduler and the Qt loop don't go together.
       
   765     QCoreApplication::processEvents(m_flags);
       
   766 
       
   767     succeeded = loop.succeeded;
       
   768 }
       
   769 #endif // ifdef Q_OS_SYMBIAN
       
   770 
       
   771 void tst_QEventLoop::symbianNestedActiveSchedulerLoop_data()
       
   772 {
       
   773     QTest::addColumn<int>("processEventFlags");
       
   774 
       
   775     QTest::newRow("AllEvents") << (int)QEventLoop::AllEvents;
       
   776     QTest::newRow("WaitForMoreEvents") << (int)QEventLoop::WaitForMoreEvents;
       
   777 }
       
   778 
       
   779 /*
       
   780   Before you start fiddling with this test, you should have a good understanding of how
       
   781   Symbian active objects work. What the test does is to try to screw up the semaphore count
       
   782   in the active scheduler to cause stray signals, by running the Qt event loop and the
       
   783   active scheduler inside each other. Naturally, its attempts to do this should be futile!
       
   784 */
       
   785 void tst_QEventLoop::symbianNestedActiveSchedulerLoop()
       
   786 {
       
   787 #ifndef Q_OS_SYMBIAN
       
   788     QSKIP("This is a Symbian only test.", SkipAll);
       
   789 #else
       
   790     QFETCH(int, processEventFlags);
       
   791 
       
   792     ActiveSchedulerThread thread((QEventLoop::ProcessEventsFlag)processEventFlags);
       
   793     thread.start();
       
   794     thread.wait(2000);
       
   795 
       
   796     QVERIFY(thread.succeeded);
       
   797 #endif
       
   798 }
       
   799 
       
   800 QTEST_MAIN(tst_QEventLoop)
       
   801 #include "tst_qeventloop.moc"