tests/auto/qdbusthreading/tst_qdbusthreading.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 #include <QtTest>
       
    42 #include <QtDBus>
       
    43 #include <QtCore/QVarLengthArray>
       
    44 #include <QtCore/QThread>
       
    45 #include <QtCore/QObject>
       
    46 #include <QtCore/QSemaphore>
       
    47 #include <QtCore/QMutex>
       
    48 #include <QtCore/QWaitCondition>
       
    49 #include <QtCore/QMap>
       
    50 
       
    51 class Thread : public QThread
       
    52 {
       
    53     Q_OBJECT
       
    54     static int counter;
       
    55 public:
       
    56     Thread(bool automatic = true);
       
    57     void run();
       
    58 
       
    59     using QThread::exec;
       
    60 };
       
    61 int Thread::counter;
       
    62 
       
    63 class tst_QDBusThreading : public QObject
       
    64 {
       
    65     Q_OBJECT
       
    66     static tst_QDBusThreading *_self;
       
    67     QAtomicInt threadJoinCount;
       
    68     QSemaphore threadJoin;
       
    69 public:
       
    70     QSemaphore sem1, sem2;
       
    71     volatile bool success;
       
    72     QEventLoop *loop;
       
    73     const char *functionSpy;
       
    74     QThread *threadSpy;
       
    75     int signalSpy;
       
    76 
       
    77     tst_QDBusThreading();
       
    78     static inline tst_QDBusThreading *self() { return _self; }
       
    79 
       
    80     void joinThreads();
       
    81     bool waitForSignal(QObject *obj, const char *signal, int delay = 1);
       
    82 
       
    83 public Q_SLOTS:
       
    84     void cleanup();
       
    85     void signalSpySlot() { ++signalSpy; }
       
    86     void threadStarted() { threadJoinCount.ref(); }
       
    87     void threadFinished() { threadJoin.release(); }
       
    88 
       
    89     void dyingThread_thread();
       
    90     void lastInstanceInOtherThread_thread();
       
    91     void concurrentCreation_thread();
       
    92     void disconnectAnothersConnection_thread();
       
    93     void accessMainsConnection_thread();
       
    94     void accessOthersConnection_thread();
       
    95     void registerObjectInOtherThread_thread();
       
    96     void registerAdaptorInOtherThread_thread();
       
    97     void callbackInMainThread_thread();
       
    98     void callbackInAuxThread_thread();
       
    99     void callbackInAnotherAuxThread_thread();
       
   100 
       
   101 private Q_SLOTS:
       
   102     void initTestCase();
       
   103     void dyingThread();
       
   104     void lastInstanceInOtherThread();
       
   105     void concurrentCreation();
       
   106     void disconnectAnothersConnection();
       
   107     void accessMainsConnection();
       
   108     void accessOthersConnection();
       
   109     void registerObjectInOtherThread();
       
   110     void registerAdaptorInOtherThread();
       
   111     void callbackInMainThread();
       
   112     void callbackInAuxThread();
       
   113     void callbackInAnotherAuxThread();
       
   114 };
       
   115 tst_QDBusThreading *tst_QDBusThreading::_self;
       
   116 
       
   117 class Adaptor : public QDBusAbstractAdaptor
       
   118 {
       
   119     Q_OBJECT
       
   120     Q_CLASSINFO("D-Bus Interface", "local.Adaptor")
       
   121 public:
       
   122     Adaptor(QObject *parent)
       
   123         : QDBusAbstractAdaptor(parent)
       
   124     {
       
   125     }
       
   126 
       
   127 public Q_SLOTS:
       
   128     void method()
       
   129     {
       
   130         tst_QDBusThreading::self()->functionSpy = Q_FUNC_INFO;
       
   131         tst_QDBusThreading::self()->threadSpy = QThread::currentThread();
       
   132         emit signal();
       
   133     }
       
   134 
       
   135 Q_SIGNALS:
       
   136     void signal();
       
   137 };
       
   138 
       
   139 class Object : public QObject
       
   140 {
       
   141     Q_OBJECT
       
   142     Q_CLASSINFO("D-Bus Interface", "local.Object")
       
   143 public:
       
   144     Object(bool useAdaptor)
       
   145     {
       
   146         if (useAdaptor)
       
   147             new Adaptor(this);
       
   148     }
       
   149 
       
   150     ~Object()
       
   151     {
       
   152         QMetaObject::invokeMethod(QThread::currentThread(), "quit", Qt::QueuedConnection);
       
   153     }
       
   154 
       
   155 public Q_SLOTS:
       
   156     void method()
       
   157     {
       
   158         tst_QDBusThreading::self()->functionSpy = Q_FUNC_INFO;
       
   159         tst_QDBusThreading::self()->threadSpy = QThread::currentThread();
       
   160         emit signal();
       
   161         deleteLater();
       
   162     }
       
   163 
       
   164 Q_SIGNALS:
       
   165     void signal();
       
   166 };
       
   167 
       
   168 #if 0
       
   169 typedef void (*qdbusThreadDebugFunc)(int, int, QDBusConnectionPrivate *);
       
   170 QDBUS_EXPORT void qdbusDefaultThreadDebug(int, int, QDBusConnectionPrivate *);
       
   171 extern QDBUS_EXPORT qdbusThreadDebugFunc qdbusThreadDebug;
       
   172 
       
   173 static void threadDebug(int action, int condition, QDBusConnectionPrivate *p)
       
   174 {
       
   175     qdbusDefaultThreadDebug(action, condition, p);
       
   176 }
       
   177 #endif
       
   178 
       
   179 Thread::Thread(bool automatic)
       
   180 {
       
   181     setObjectName(QString::fromLatin1("Aux thread %1").arg(++counter));
       
   182     connect(this, SIGNAL(started()), tst_QDBusThreading::self(), SLOT(threadStarted()));
       
   183     connect(this, SIGNAL(finished()), tst_QDBusThreading::self(), SLOT(threadFinished()),
       
   184             Qt::DirectConnection);
       
   185     connect(this, SIGNAL(finished()), this, SLOT(deleteLater()), Qt::DirectConnection);
       
   186     if (automatic)
       
   187         start();
       
   188 }
       
   189 
       
   190 void Thread::run()
       
   191 {
       
   192     QVarLengthArray<char, 56> name;
       
   193     name.append(QTest::currentTestFunction(), qstrlen(QTest::currentTestFunction()));
       
   194     name.append("_thread", sizeof "_thread");
       
   195     QMetaObject::invokeMethod(tst_QDBusThreading::self(), name.constData(), Qt::DirectConnection);
       
   196 }
       
   197 
       
   198 static const char myConnectionName[] = "connection";
       
   199 
       
   200 tst_QDBusThreading::tst_QDBusThreading()
       
   201     : loop(0), functionSpy(0), threadSpy(0)
       
   202 {
       
   203     _self = this;
       
   204     QCoreApplication::instance()->thread()->setObjectName("Main thread");
       
   205 }
       
   206 
       
   207 void tst_QDBusThreading::joinThreads()
       
   208 {
       
   209     threadJoin.acquire(threadJoinCount);
       
   210     threadJoinCount = 0;
       
   211 }
       
   212 
       
   213 bool tst_QDBusThreading::waitForSignal(QObject *obj, const char *signal, int delay)
       
   214 {
       
   215     QObject::connect(obj, signal, &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   216     QPointer<QObject> safe = obj;
       
   217 
       
   218     QTestEventLoop::instance().enterLoop(delay);
       
   219     if (!safe.isNull())
       
   220         QObject::disconnect(safe, signal, &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   221     return QTestEventLoop::instance().timeout();
       
   222 }
       
   223 
       
   224 void tst_QDBusThreading::cleanup()
       
   225 {
       
   226     joinThreads();
       
   227 
       
   228     if (sem1.available())
       
   229         sem1.acquire(sem1.available());
       
   230     if (sem2.available())
       
   231         sem2.acquire(sem2.available());
       
   232 
       
   233     if (QDBusConnection(myConnectionName).isConnected())
       
   234         QDBusConnection::disconnectFromBus(myConnectionName);
       
   235 
       
   236     delete loop;
       
   237     loop = 0;
       
   238 
       
   239     QTest::qWait(500);
       
   240 }
       
   241 
       
   242 void tst_QDBusThreading::initTestCase()
       
   243 {
       
   244 }
       
   245 
       
   246 void tst_QDBusThreading::dyingThread_thread()
       
   247 {
       
   248     QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName);
       
   249 }
       
   250 
       
   251 void tst_QDBusThreading::dyingThread()
       
   252 {
       
   253     Thread *th = new Thread(false);
       
   254     QTestEventLoop::instance().connect(th, SIGNAL(destroyed(QObject*)), SLOT(exitLoop()));
       
   255     th->start();
       
   256 
       
   257     QTestEventLoop::instance().enterLoop(10);
       
   258     QVERIFY(!QTestEventLoop::instance().timeout());
       
   259 
       
   260     QDBusConnection con(myConnectionName);
       
   261     QDBusConnection::disconnectFromBus(myConnectionName);
       
   262 
       
   263     QVERIFY(con.isConnected());
       
   264     QDBusReply<QStringList> reply = con.interface()->registeredServiceNames();
       
   265     QVERIFY(reply.isValid());
       
   266     QVERIFY(!reply.value().isEmpty());
       
   267     QVERIFY(reply.value().contains(con.baseService()));
       
   268 
       
   269     con.interface()->callWithCallback("ListNames", QVariantList(),
       
   270                                       &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   271 
       
   272     QTestEventLoop::instance().enterLoop(1);
       
   273     QVERIFY(!QTestEventLoop::instance().timeout());
       
   274 }
       
   275 
       
   276 void tst_QDBusThreading::lastInstanceInOtherThread_thread()
       
   277 {
       
   278     QDBusConnection con(myConnectionName);
       
   279     QVERIFY(con.isConnected());
       
   280 
       
   281     QDBusConnection::disconnectFromBus(myConnectionName);
       
   282 
       
   283     // con is being destroyed in the wrong thread
       
   284 }
       
   285 
       
   286 void tst_QDBusThreading::lastInstanceInOtherThread()
       
   287 {
       
   288     Thread *th = new Thread(false);
       
   289     // create the connection:
       
   290     QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName);
       
   291 
       
   292     th->start();
       
   293     th->wait();
       
   294 }
       
   295 
       
   296 void tst_QDBusThreading::concurrentCreation_thread()
       
   297 {
       
   298     sem1.acquire();
       
   299     QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus,
       
   300                                                         myConnectionName);
       
   301     sem2.release();
       
   302 }
       
   303 
       
   304 void tst_QDBusThreading::concurrentCreation()
       
   305 {
       
   306     Thread *th = new Thread;
       
   307 
       
   308     {
       
   309         sem1.release();
       
   310         QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus,
       
   311                                                             myConnectionName);
       
   312         QVERIFY(con.isConnected());
       
   313         sem2.acquire();
       
   314     }
       
   315     waitForSignal(th, SIGNAL(finished()));
       
   316     QDBusConnection::disconnectFromBus(myConnectionName);
       
   317 
       
   318     QVERIFY(!QDBusConnection(myConnectionName).isConnected());
       
   319 }
       
   320 
       
   321 void tst_QDBusThreading::disconnectAnothersConnection_thread()
       
   322 {
       
   323     QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus,
       
   324                                                         myConnectionName);
       
   325     sem2.release();
       
   326 }
       
   327 
       
   328 void tst_QDBusThreading::disconnectAnothersConnection()
       
   329 {
       
   330     new Thread;
       
   331     sem2.acquire();
       
   332 
       
   333     QVERIFY(QDBusConnection(myConnectionName).isConnected());
       
   334     QDBusConnection::disconnectFromBus(myConnectionName);
       
   335 }
       
   336 
       
   337 void tst_QDBusThreading::accessMainsConnection_thread()
       
   338 {
       
   339     sem1.acquire();
       
   340     QDBusConnection con = QDBusConnection::sessionBus();
       
   341     con.interface()->registeredServiceNames();
       
   342     sem2.release();
       
   343 }
       
   344 
       
   345 void tst_QDBusThreading::accessMainsConnection()
       
   346 {
       
   347     QVERIFY(QDBusConnection::sessionBus().isConnected());
       
   348 
       
   349     new Thread;
       
   350     sem1.release();
       
   351     sem2.acquire();
       
   352 };
       
   353 
       
   354 void tst_QDBusThreading::accessOthersConnection_thread()
       
   355 {
       
   356     QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName);
       
   357     sem2.release();
       
   358 
       
   359     // wait for main thread to be done
       
   360     sem1.acquire();
       
   361     QDBusConnection::disconnectFromBus(myConnectionName);
       
   362     sem2.release();
       
   363 }
       
   364 
       
   365 void tst_QDBusThreading::accessOthersConnection()
       
   366 {
       
   367     new Thread;
       
   368 
       
   369     // wait for the connection to be created
       
   370     sem2.acquire();
       
   371 
       
   372     {
       
   373         QDBusConnection con(myConnectionName);
       
   374         QVERIFY(con.isConnected());
       
   375         QVERIFY(con.baseService() != QDBusConnection::sessionBus().baseService());
       
   376 
       
   377         QDBusReply<QStringList> reply = con.interface()->registeredServiceNames();
       
   378         if (!reply.isValid())
       
   379             qDebug() << reply.error().name() << reply.error().message();
       
   380         QVERIFY(reply.isValid());
       
   381         QVERIFY(!reply.value().isEmpty());
       
   382         QVERIFY(reply.value().contains(con.baseService()));
       
   383         QVERIFY(reply.value().contains(QDBusConnection::sessionBus().baseService()));
       
   384     }
       
   385 
       
   386     // tell it to destroy:
       
   387     sem1.release();
       
   388     sem2.acquire();
       
   389 
       
   390     QDBusConnection con(myConnectionName);
       
   391     QVERIFY(!con.isConnected());
       
   392 }
       
   393 
       
   394 void tst_QDBusThreading::registerObjectInOtherThread_thread()
       
   395 {
       
   396     {
       
   397         Object *obj = new Object(false);
       
   398         QDBusConnection::sessionBus().registerObject("/", obj, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals);
       
   399 
       
   400         sem2.release();
       
   401         static_cast<Thread *>(QThread::currentThread())->exec();
       
   402     }
       
   403 
       
   404     sem2.release();
       
   405 }
       
   406 
       
   407 void tst_QDBusThreading::registerObjectInOtherThread()
       
   408 {
       
   409     QVERIFY(QDBusConnection::sessionBus().isConnected());
       
   410     QThread *th = new Thread;
       
   411     sem2.acquire();
       
   412 
       
   413     signalSpy = 0;
       
   414 
       
   415     QDBusInterface iface(QDBusConnection::sessionBus().baseService(), "/", "local.Object");
       
   416     QVERIFY(iface.isValid());
       
   417 
       
   418     connect(&iface, SIGNAL(signal()), SLOT(signalSpySlot()));
       
   419 
       
   420     QTest::qWait(100);
       
   421     QCOMPARE(signalSpy, 0);
       
   422 
       
   423     functionSpy = 0;
       
   424     threadSpy = 0;
       
   425     QDBusReply<void> reply = iface.call("method");
       
   426     QVERIFY(reply.isValid());
       
   427     QCOMPARE(functionSpy, "void Object::method()");
       
   428     QCOMPARE(threadSpy, th);
       
   429 
       
   430     QTest::qWait(100);
       
   431     QCOMPARE(signalSpy, 1);
       
   432 
       
   433     sem2.acquire();             // the object is gone
       
   434     functionSpy = 0;
       
   435     threadSpy = 0;
       
   436     reply = iface.call("method");
       
   437     QVERIFY(!reply.isValid());
       
   438     QCOMPARE(functionSpy, (const char*)0);
       
   439     QCOMPARE(threadSpy, (QThread*)0);
       
   440 }
       
   441 
       
   442 void tst_QDBusThreading::registerAdaptorInOtherThread_thread()
       
   443 {
       
   444     {
       
   445         Object *obj = new Object(true);
       
   446         QDBusConnection::sessionBus().registerObject("/", obj, QDBusConnection::ExportAdaptors |
       
   447                                                      QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals);
       
   448 
       
   449         sem2.release();
       
   450         static_cast<Thread *>(QThread::currentThread())->exec();
       
   451     }
       
   452 
       
   453     sem2.release();
       
   454 }
       
   455 
       
   456 void tst_QDBusThreading::registerAdaptorInOtherThread()
       
   457 {
       
   458     QVERIFY(QDBusConnection::sessionBus().isConnected());
       
   459     QThread *th = new Thread;
       
   460     sem2.acquire();
       
   461 
       
   462     QDBusInterface object(QDBusConnection::sessionBus().baseService(), "/", "local.Object");
       
   463     QDBusInterface adaptor(QDBusConnection::sessionBus().baseService(), "/", "local.Adaptor");
       
   464     QVERIFY(object.isValid());
       
   465     QVERIFY(adaptor.isValid());
       
   466 
       
   467     signalSpy = 0;
       
   468     connect(&adaptor, SIGNAL(signal()), SLOT(signalSpySlot()));
       
   469     QCOMPARE(signalSpy, 0);
       
   470 
       
   471     functionSpy = 0;
       
   472     threadSpy = 0;
       
   473     QDBusReply<void> reply = adaptor.call("method");
       
   474     QVERIFY(reply.isValid());
       
   475     QCOMPARE(functionSpy, "void Adaptor::method()");
       
   476     QCOMPARE(threadSpy, th);
       
   477 
       
   478     QTest::qWait(100);
       
   479     QCOMPARE(signalSpy, 1);
       
   480 
       
   481     functionSpy = 0;
       
   482     threadSpy = 0;
       
   483     reply = object.call("method");
       
   484     QVERIFY(reply.isValid());
       
   485     QCOMPARE(functionSpy, "void Object::method()");
       
   486     QCOMPARE(threadSpy, th);
       
   487 
       
   488     QTest::qWait(100);
       
   489     QCOMPARE(signalSpy, 1);
       
   490 
       
   491     sem2.acquire();             // the object is gone
       
   492     functionSpy = 0;
       
   493     threadSpy = 0;
       
   494     reply = adaptor.call("method");
       
   495     QVERIFY(!reply.isValid());
       
   496     QCOMPARE(functionSpy, (const char*)0);
       
   497     QCOMPARE(threadSpy, (QThread*)0);
       
   498     reply = object.call("method");
       
   499     QVERIFY(!reply.isValid());
       
   500     QCOMPARE(functionSpy, (const char*)0);
       
   501     QCOMPARE(threadSpy, (QThread*)0);
       
   502 }
       
   503 
       
   504 void tst_QDBusThreading::callbackInMainThread_thread()
       
   505 {
       
   506     QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName);
       
   507     sem2.release();
       
   508 
       
   509     static_cast<Thread *>(QThread::currentThread())->exec();
       
   510     QDBusConnection::disconnectFromBus(myConnectionName);
       
   511 }
       
   512 
       
   513 void tst_QDBusThreading::callbackInMainThread()
       
   514 {
       
   515     Thread *th = new Thread;
       
   516 
       
   517     // wait for it to be connected
       
   518     sem2.acquire();
       
   519 
       
   520     QDBusConnection con(myConnectionName);
       
   521     con.interface()->callWithCallback("ListNames", QVariantList(),
       
   522                                       &QTestEventLoop::instance(), SLOT(exitLoop()));
       
   523     QTestEventLoop::instance().enterLoop(10);
       
   524     QVERIFY(!QTestEventLoop::instance().timeout());
       
   525 
       
   526     QMetaObject::invokeMethod(th, "quit");
       
   527     waitForSignal(th, SIGNAL(finished()));
       
   528 }
       
   529 
       
   530 void tst_QDBusThreading::callbackInAuxThread_thread()
       
   531 {
       
   532     QDBusConnection con(QDBusConnection::sessionBus());
       
   533     QTestEventLoop ownLoop;
       
   534     con.interface()->callWithCallback("ListNames", QVariantList(),
       
   535                                       &ownLoop, SLOT(exitLoop()));
       
   536     ownLoop.enterLoop(10);
       
   537     loop->exit(ownLoop.timeout() ? 1 : 0);
       
   538 }
       
   539 
       
   540 void tst_QDBusThreading::callbackInAuxThread()
       
   541 {
       
   542     QVERIFY(QDBusConnection::sessionBus().isConnected());
       
   543 
       
   544     loop = new QEventLoop;
       
   545 
       
   546     new Thread;
       
   547     QCOMPARE(loop->exec(), 0);
       
   548 }
       
   549 
       
   550 void tst_QDBusThreading::callbackInAnotherAuxThread_thread()
       
   551 {
       
   552     sem1.acquire();
       
   553     if (!loop) {
       
   554         // first thread
       
   555         // create the connection and just wait
       
   556         QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName);
       
   557         loop = new QEventLoop;
       
   558 
       
   559         // tell the main thread we have created the loop and connection
       
   560         sem2.release();
       
   561 
       
   562         // wait for the main thread to connect its signal
       
   563         sem1.acquire();
       
   564         success = loop->exec() == 0;
       
   565         sem2.release();
       
   566 
       
   567         // clean up
       
   568         QDBusConnection::disconnectFromBus(myConnectionName);
       
   569     } else {
       
   570         // second thread
       
   571         // try waiting for a message
       
   572         QDBusConnection con(myConnectionName);
       
   573         QTestEventLoop ownLoop;
       
   574         con.interface()->callWithCallback("ListNames", QVariantList(),
       
   575                                           &ownLoop, SLOT(exitLoop()));
       
   576         ownLoop.enterLoop(1);
       
   577         loop->exit(ownLoop.timeout() ? 1 : 0);
       
   578     }
       
   579 }
       
   580 
       
   581 void tst_QDBusThreading::callbackInAnotherAuxThread()
       
   582 {
       
   583     // create first thread
       
   584     success = false;
       
   585     new Thread;
       
   586 
       
   587     // wait for the event loop
       
   588     sem1.release();
       
   589     sem2.acquire();
       
   590     Q_ASSERT(loop);
       
   591 
       
   592     // create the second thread
       
   593     new Thread;
       
   594     sem1.release(2);
       
   595 
       
   596     // wait for loop thread to finish executing:
       
   597     sem2.acquire();
       
   598 
       
   599     QVERIFY(success);
       
   600 }
       
   601 
       
   602 // Next tests:
       
   603 // - unexport an object at the moment the call is being delivered
       
   604 // - delete an object at the moment the call is being delivered
       
   605 // - keep a global-static QDBusConnection for a thread-created connection
       
   606 
       
   607 QTEST_MAIN(tst_QDBusThreading)
       
   608 #include "tst_qdbusthreading.moc"