1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qcoreapplication.h>
42 #include <qdebug.h>
43 
44 #include <QtTest/QtTest>
45 #include <QtDBus/QtDBus>
46 
47 class BaseObject: public QObject
48 {
49     Q_OBJECT
50     Q_CLASSINFO("D-Bus Interface", "local.BaseObject")
51 public:
BaseObject(QObject * parent=0)52     BaseObject(QObject *parent = 0) : QObject(parent) { }
53 public slots:
anotherMethod()54     void anotherMethod() { }
55 };
56 
57 class MyObject: public BaseObject
58 {
59     Q_OBJECT
60 public slots:
61     void method(const QDBusMessage &msg);
62 
63 public:
64     static QString path;
65     int callCount;
MyObject(QObject * parent=0)66     MyObject(QObject *parent = 0) : BaseObject(parent), callCount(0) {}
67 };
68 
method(const QDBusMessage & msg)69 void MyObject::method(const QDBusMessage &msg)
70 {
71     path = msg.path();
72     ++callCount;
73     //qDebug() << msg;
74 }
75 
76 class tst_QDBusConnection: public QObject
77 {
78     Q_OBJECT
79 
80     int signalsReceived;
81 public slots:
oneSlot()82     void oneSlot() { ++signalsReceived; }
exitLoop()83     void exitLoop() { ++signalsReceived; QTestEventLoop::instance().exitLoop(); }
84     void secondCallWithCallback();
85 
86 private slots:
87     void noConnection();
88     void connectToBus();
89     void connectToPeer();
90     void connect();
91     void send();
92     void sendWithGui();
93     void sendAsync();
94     void sendSignal();
95 
96     void registerObject_data();
97     void registerObject();
98     void registerObjectPeer_data();
99     void registerObjectPeer();
100     void registerObject2();
101     void registerObjectPeer2();
102 
103     void registerQObjectChildren();
104     void registerQObjectChildrenPeer();
105 
106     void callSelf();
107     void callSelfByAnotherName_data();
108     void callSelfByAnotherName();
109     void multipleInterfacesInQObject();
110 
111     void slotsWithLessParameters();
112     void nestedCallWithCallback();
113 
114     void serviceRegistrationRaceCondition();
115 
116     void registerVirtualObject();
117     void callVirtualObject();
118     void callVirtualObjectLocal();
119 
120 public:
serviceName() const121     QString serviceName() const { return "com.trolltech.Qt.Autotests.QDBusConnection"; }
122     bool callMethod(const QDBusConnection &conn, const QString &path);
123     bool callMethodPeer(const QDBusConnection &conn, const QString &path);
124 };
125 
126 class QDBusSpy: public QObject
127 {
128     Q_OBJECT
129 public slots:
handlePing(const QString & str)130     void handlePing(const QString &str) { args.clear(); args << str; }
asyncReply(const QDBusMessage & msg)131     void asyncReply(const QDBusMessage &msg) { args = msg.arguments(); }
132 
133 public:
134     QList<QVariant> args;
135 };
136 
noConnection()137 void tst_QDBusConnection::noConnection()
138 {
139     QDBusConnection con = QDBusConnection::connectToBus("unix:path=/dev/null", "testconnection");
140     QVERIFY(!con.isConnected());
141 
142     // try sending a message. This should fail
143     QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.selftest", "/org/kde/selftest",
144                                                       "org.kde.selftest", "Ping");
145     msg << QLatin1String("ping");
146 
147     QVERIFY(!con.send(msg));
148 
149     QDBusSpy spy;
150     QVERIFY(con.callWithCallback(msg, &spy, SLOT(asyncReply)) == 0);
151 
152     QDBusMessage reply = con.call(msg);
153     QVERIFY(reply.type() == QDBusMessage::ErrorMessage);
154 
155     QDBusReply<void> voidreply(reply);
156     QVERIFY(!voidreply.isValid());
157 
158     QDBusConnection::disconnectFromBus("testconnection");
159 }
160 
sendSignal()161 void tst_QDBusConnection::sendSignal()
162 {
163     QDBusConnection con = QDBusConnection::sessionBus();
164 
165     QVERIFY(con.isConnected());
166 
167     QDBusMessage msg = QDBusMessage::createSignal("/org/kde/selftest", "org.kde.selftest",
168                                                   "Ping");
169     msg << QLatin1String("ping");
170 
171     QVERIFY(con.send(msg));
172 
173     QTest::qWait(1000);
174 }
175 
send()176 void tst_QDBusConnection::send()
177 {
178     QDBusConnection con = QDBusConnection::sessionBus();
179 
180     QVERIFY(con.isConnected());
181 
182     QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus",
183         "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
184 
185     QDBusMessage reply = con.call(msg);
186 
187     QCOMPARE(reply.arguments().count(), 1);
188     QCOMPARE(reply.arguments().at(0).typeName(), "QStringList");
189     QVERIFY(reply.arguments().at(0).toStringList().contains(con.baseService()));
190 }
191 
sendWithGui()192 void tst_QDBusConnection::sendWithGui()
193 {
194     QDBusConnection con = QDBusConnection::sessionBus();
195 
196     QVERIFY(con.isConnected());
197 
198     QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus",
199         "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
200 
201     QDBusMessage reply = con.call(msg, QDBus::BlockWithGui);
202 
203     QCOMPARE(reply.arguments().count(), 1);
204     QCOMPARE(reply.arguments().at(0).typeName(), "QStringList");
205     QVERIFY(reply.arguments().at(0).toStringList().contains(con.baseService()));
206 }
207 
sendAsync()208 void tst_QDBusConnection::sendAsync()
209 {
210     QDBusConnection con = QDBusConnection::sessionBus();
211     QVERIFY(con.isConnected());
212 
213     QDBusSpy spy;
214 
215     QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus",
216             "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
217     QVERIFY(con.callWithCallback(msg, &spy, SLOT(asyncReply(QDBusMessage))));
218 
219     QTest::qWait(1000);
220 
221     QCOMPARE(spy.args.value(0).typeName(), "QStringList");
222     QVERIFY(spy.args.at(0).toStringList().contains(con.baseService()));
223 }
224 
connect()225 void tst_QDBusConnection::connect()
226 {
227     QDBusSpy spy;
228 
229     QDBusConnection con = QDBusConnection::sessionBus();
230 
231     con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy,
232                  SLOT(handlePing(QString)));
233 
234     QDBusMessage msg = QDBusMessage::createSignal("/org/kde/selftest", "org.kde.selftest",
235                                                   "ping");
236     msg << QLatin1String("ping");
237 
238     QVERIFY(con.send(msg));
239 
240     QTest::qWait(1000);
241 
242     QCOMPARE(spy.args.count(), 1);
243     QCOMPARE(spy.args.at(0).toString(), QString("ping"));
244 }
245 
connectToBus()246 void tst_QDBusConnection::connectToBus()
247 {
248     {
249         QDBusConnection con = QDBusConnection::connectToBus(
250                 QDBusConnection::SessionBus, "bubu");
251 
252         QVERIFY(con.isConnected());
253         QVERIFY(!con.lastError().isValid());
254 
255         QDBusConnection con2("foo");
256         QVERIFY(!con2.isConnected());
257         QVERIFY(!con2.lastError().isValid());
258 
259         con2 = con;
260         QVERIFY(con.isConnected());
261         QVERIFY(con2.isConnected());
262         QVERIFY(!con.lastError().isValid());
263         QVERIFY(!con2.lastError().isValid());
264     }
265 
266     {
267         QDBusConnection con("bubu");
268         QVERIFY(con.isConnected());
269         QVERIFY(!con.lastError().isValid());
270     }
271 
272     QDBusConnection::disconnectFromPeer("bubu");
273 
274     {
275         QDBusConnection con("bubu");
276         QVERIFY(con.isConnected());
277         QVERIFY(!con.lastError().isValid());
278     }
279 
280     QDBusConnection::disconnectFromBus("bubu");
281 
282     {
283         QDBusConnection con("bubu");
284         QVERIFY(!con.isConnected());
285         QVERIFY(!con.lastError().isValid());
286     }
287 
288     QByteArray address = qgetenv("DBUS_SESSION_BUS_ADDRESS");
289     if (!address.isEmpty()) {
290         QDBusConnection con = QDBusConnection::connectToBus(address, "newconn");
291         QVERIFY(con.isConnected());
292         QVERIFY(!con.lastError().isValid());
293 
294         QDBusConnection::disconnectFromBus("newconn");
295     }
296 }
297 
connectToPeer()298 void tst_QDBusConnection::connectToPeer()
299 {
300     {
301         QDBusConnection con = QDBusConnection::connectToPeer(
302                 "", "newconn");
303         QVERIFY(!con.isConnected());
304         QVERIFY(con.lastError().isValid());
305     }
306 
307     QDBusServer server("unix:tmpdir=/tmp", 0);
308 
309     {
310         QDBusConnection con = QDBusConnection::connectToPeer(
311                 "unix:abstract=/tmp/dbus-XXXXXXXXXX,guid=00000000000000000000000000000000", "newconn2");
312         QVERIFY(!con.isConnected());
313         QVERIFY(con.lastError().isValid());
314     }
315 
316     {
317         QDBusConnection con = QDBusConnection::connectToPeer(
318                 server.address(), "bubu");
319 
320         QVERIFY(con.isConnected());
321         QVERIFY(!con.lastError().isValid());
322 
323         QDBusConnection con2("foo");
324         QVERIFY(!con2.isConnected());
325         QVERIFY(!con2.lastError().isValid());
326 
327         con2 = con;
328         QVERIFY(con.isConnected());
329         QVERIFY(con2.isConnected());
330         QVERIFY(!con.lastError().isValid());
331         QVERIFY(!con2.lastError().isValid());
332     }
333 
334     {
335         QDBusConnection con("bubu");
336         QVERIFY(con.isConnected());
337         QVERIFY(!con.lastError().isValid());
338     }
339 
340     QDBusConnection::disconnectFromBus("bubu");
341 
342     {
343         QDBusConnection con("bubu");
344         QVERIFY(con.isConnected());
345         QVERIFY(!con.lastError().isValid());
346     }
347 
348     QDBusConnection::disconnectFromPeer("bubu");
349 
350     {
351         QDBusConnection con("bubu");
352         QVERIFY(!con.isConnected());
353         QVERIFY(!con.lastError().isValid());
354     }
355 }
356 
registerObject_data()357 void tst_QDBusConnection::registerObject_data()
358 {
359     QTest::addColumn<QString>("path");
360 
361     QTest::newRow("/") << "/";
362     QTest::newRow("/p1") << "/p1";
363     QTest::newRow("/p2") << "/p2";
364     QTest::newRow("/p1/q") << "/p1/q";
365     QTest::newRow("/p1/q/r") << "/p1/q/r";
366 }
367 
registerObject()368 void tst_QDBusConnection::registerObject()
369 {
370     QFETCH(QString, path);
371 
372     QDBusConnection con = QDBusConnection::sessionBus();
373     QVERIFY(con.isConnected());
374 
375     //QVERIFY(!callMethod(con, path));
376     {
377         // register one object at root:
378         MyObject obj;
379         QVERIFY(con.registerObject(path, &obj, QDBusConnection::ExportAllSlots));
380         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(&obj));
381         QVERIFY(callMethod(con, path));
382         QCOMPARE(obj.path, path);
383     }
384     // make sure it's gone
385     QVERIFY(!callMethod(con, path));
386 }
387 
388 class MyServer : public QDBusServer
389 {
390     Q_OBJECT
391 public:
MyServer(QString path,QString addr,QObject * parent)392     MyServer(QString path, QString addr, QObject* parent) : QDBusServer(addr, parent),
393                                                             m_path(path),
394                                                             m_connections()
395     {
396         connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&)));
397     }
398 
registerObject(const QDBusConnection & c)399     bool registerObject(const QDBusConnection& c)
400     {
401         QDBusConnection conn(c);
402         if (!conn.registerObject(m_path, &m_obj, QDBusConnection::ExportAllSlots))
403             return false;
404         if (!(conn.objectRegisteredAt(m_path) == &m_obj))
405             return false;
406         return true;
407     }
408 
registerObject()409     bool registerObject()
410     {
411         Q_FOREACH (const QString &name, m_connections) {
412             if (!registerObject(QDBusConnection(name)))
413                 return false;
414         }
415         return true;
416     }
417 
unregisterObject()418     void unregisterObject()
419     {
420         Q_FOREACH (const QString &name, m_connections) {
421             QDBusConnection c(name);
422             c.unregisterObject(m_path);
423         }
424     }
425 
426 public slots:
handleConnection(const QDBusConnection & c)427     void handleConnection(const QDBusConnection& c)
428     {
429         m_connections << c.name();
430         QVERIFY(isConnected());
431         QVERIFY(c.isConnected());
432         QVERIFY(registerObject(c));
433     }
434 
435 private:
436     MyObject m_obj;
437     QString m_path;
438     QStringList m_connections;
439 };
440 
441 
registerObjectPeer_data()442 void tst_QDBusConnection::registerObjectPeer_data()
443 {
444     QTest::addColumn<QString>("path");
445 
446     QTest::newRow("/") << "/";
447     QTest::newRow("/p1") << "/p1";
448     QTest::newRow("/p2") << "/p2";
449     QTest::newRow("/p1/q") << "/p1/q";
450     QTest::newRow("/p1/q/r") << "/p1/q/r";
451 }
452 
registerObjectPeer()453 void tst_QDBusConnection::registerObjectPeer()
454 {
455     QFETCH(QString, path);
456 
457     MyServer server(path, "unix:tmpdir=/tmp", 0);
458 
459     QDBusConnection::connectToPeer(server.address(), "beforeFoo");
460 
461     {
462         QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo");
463 
464         QCoreApplication::processEvents();
465         QVERIFY(con.isConnected());
466 
467         MyObject obj;
468         QVERIFY(callMethodPeer(con, path));
469         QCOMPARE(obj.path, path);
470     }
471 
472     QDBusConnection::connectToPeer(server.address(), "afterFoo");
473 
474     {
475         QDBusConnection con("foo");
476         QVERIFY(con.isConnected());
477         QVERIFY(callMethodPeer(con, path));
478     }
479 
480     server.unregisterObject();
481 
482     {
483         QDBusConnection con("foo");
484         QVERIFY(con.isConnected());
485         QVERIFY(!callMethodPeer(con, path));
486     }
487 
488     server.registerObject();
489 
490     {
491         QDBusConnection con("foo");
492         QVERIFY(con.isConnected());
493         QVERIFY(callMethodPeer(con, path));
494     }
495 
496     QDBusConnection::disconnectFromPeer("foo");
497 
498     {
499         QDBusConnection con("foo");
500         QVERIFY(!con.isConnected());
501         QVERIFY(!callMethodPeer(con, path));
502     }
503 
504     QDBusConnection::disconnectFromPeer("beforeFoo");
505     QDBusConnection::disconnectFromPeer("afterFoo");
506 }
507 
registerObject2()508 void tst_QDBusConnection::registerObject2()
509 {
510     QDBusConnection con = QDBusConnection::sessionBus();
511     QVERIFY(con.isConnected());
512 
513     // make sure nothing is using our paths:
514      QVERIFY(!callMethod(con, "/"));
515      QVERIFY(!callMethod(con, "/p1"));
516      QVERIFY(!callMethod(con, "/p2"));
517      QVERIFY(!callMethod(con, "/p1/q"));
518      QVERIFY(!callMethod(con, "/p1/q/r"));
519 
520     {
521         // register one object at root:
522         MyObject obj;
523         QVERIFY(con.registerObject("/", &obj, QDBusConnection::ExportAllSlots));
524         QVERIFY(callMethod(con, "/"));
525         qDebug() << obj.path;
526         QCOMPARE(obj.path, QString("/"));
527     }
528     // make sure it's gone
529     QVERIFY(!callMethod(con, "/"));
530 
531     {
532         // register one at an element:
533         MyObject obj;
534         QVERIFY(con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots));
535         QVERIFY(!callMethod(con, "/"));
536         QVERIFY(callMethod(con, "/p1"));
537         qDebug() << obj.path;
538         QCOMPARE(obj.path, QString("/p1"));
539 
540         // re-register it somewhere else
541         QVERIFY(con.registerObject("/p2", &obj, QDBusConnection::ExportAllSlots));
542         QVERIFY(callMethod(con, "/p1"));
543         QCOMPARE(obj.path, QString("/p1"));
544         QVERIFY(callMethod(con, "/p2"));
545         QCOMPARE(obj.path, QString("/p2"));
546     }
547     // make sure it's gone
548     QVERIFY(!callMethod(con, "/p1"));
549     QVERIFY(!callMethod(con, "/p2"));
550 
551     {
552         // register at a deep path
553         MyObject obj;
554         QVERIFY(con.registerObject("/p1/q/r", &obj, QDBusConnection::ExportAllSlots));
555         QVERIFY(!callMethod(con, "/"));
556         QVERIFY(!callMethod(con, "/p1"));
557         QVERIFY(!callMethod(con, "/p1/q"));
558         QVERIFY(callMethod(con, "/p1/q/r"));
559         QCOMPARE(obj.path, QString("/p1/q/r"));
560     }
561     // make sure it's gone
562     QVERIFY(!callMethod(con, "/p1/q/r"));
563 
564     {
565         MyObject obj;
566         QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots));
567         QVERIFY(callMethod(con, "/p1/q2"));
568         QCOMPARE(obj.path, QString("/p1/q2"));
569 
570         // try unregistering
571         con.unregisterObject("/p1/q2");
572         QVERIFY(!callMethod(con, "/p1/q2"));
573 
574         // register it again
575         QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots));
576         QVERIFY(callMethod(con, "/p1/q2"));
577         QCOMPARE(obj.path, QString("/p1/q2"));
578 
579         // now try removing things around it:
580         con.unregisterObject("/p2");
581         QVERIFY(callMethod(con, "/p1/q2")); // unrelated object shouldn't affect
582 
583         con.unregisterObject("/p1");
584         QVERIFY(callMethod(con, "/p1/q2")); // unregistering just the parent shouldn't affect it
585 
586         con.unregisterObject("/p1/q2/r");
587         QVERIFY(callMethod(con, "/p1/q2")); // unregistering non-existing child shouldn't affect it either
588 
589         con.unregisterObject("/p1/q");
590         QVERIFY(callMethod(con, "/p1/q2")); // unregistering sibling (before) shouldn't affect
591 
592         con.unregisterObject("/p1/r");
593         QVERIFY(callMethod(con, "/p1/q2")); // unregistering sibling (after) shouldn't affect
594 
595         // now remove it:
596         con.unregisterObject("/p1", QDBusConnection::UnregisterTree);
597         QVERIFY(!callMethod(con, "/p1/q2")); // we removed the full tree
598     }
599 }
600 
601 class MyServer2 : public QDBusServer
602 {
603     Q_OBJECT
604 public:
MyServer2(QString addr,QObject * parent)605     MyServer2(QString addr, QObject* parent) : QDBusServer(addr, parent),
606                                                m_conn("none")
607     {
608         connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&)));
609     }
610 
connection()611     QDBusConnection connection()
612     {
613         return m_conn;
614     }
615 
616 public slots:
handleConnection(const QDBusConnection & c)617     void handleConnection(const QDBusConnection& c)
618     {
619         m_conn = c;
620         QVERIFY(isConnected());
621         QVERIFY(m_conn.isConnected());
622     }
623 
624 private:
625     MyObject m_obj;
626     QDBusConnection m_conn;
627 };
628 
registerObjectPeer2()629 void tst_QDBusConnection::registerObjectPeer2()
630 {
631     MyServer2 server("unix:tmpdir=/tmp", 0);
632     QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo");
633     QCoreApplication::processEvents();
634     QVERIFY(con.isConnected());
635 
636     QDBusConnection srv_con = server.connection();
637 
638     // make sure nothing is using our paths:
639     QVERIFY(!callMethodPeer(srv_con, "/"));
640     QVERIFY(!callMethodPeer(srv_con, "/p1"));
641     QVERIFY(!callMethodPeer(srv_con, "/p2"));
642     QVERIFY(!callMethodPeer(srv_con, "/p1/q"));
643     QVERIFY(!callMethodPeer(srv_con, "/p1/q/r"));
644 
645     {
646         // register one object at root:
647         MyObject obj;
648         QVERIFY(con.registerObject("/", &obj, QDBusConnection::ExportAllSlots));
649         QVERIFY(callMethodPeer(srv_con, "/"));
650         qDebug() << obj.path;
651         QCOMPARE(obj.path, QString("/"));
652     }
653     // make sure it's gone
654     QVERIFY(!callMethodPeer(srv_con, "/"));
655 
656     {
657         // register one at an element:
658         MyObject obj;
659         QVERIFY(con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots));
660         QVERIFY(!callMethodPeer(srv_con, "/"));
661         QVERIFY(callMethodPeer(srv_con, "/p1"));
662         qDebug() << obj.path;
663         QCOMPARE(obj.path, QString("/p1"));
664 
665         // re-register it somewhere else
666         QVERIFY(con.registerObject("/p2", &obj, QDBusConnection::ExportAllSlots));
667         QVERIFY(callMethodPeer(srv_con, "/p1"));
668         QCOMPARE(obj.path, QString("/p1"));
669         QVERIFY(callMethodPeer(srv_con, "/p2"));
670         QCOMPARE(obj.path, QString("/p2"));
671     }
672     // make sure it's gone
673     QVERIFY(!callMethodPeer(srv_con, "/p1"));
674     QVERIFY(!callMethodPeer(srv_con, "/p2"));
675 
676     {
677         // register at a deep path
678         MyObject obj;
679         QVERIFY(con.registerObject("/p1/q/r", &obj, QDBusConnection::ExportAllSlots));
680         QVERIFY(!callMethodPeer(srv_con, "/"));
681         QVERIFY(!callMethodPeer(srv_con, "/p1"));
682         QVERIFY(!callMethodPeer(srv_con, "/p1/q"));
683         QVERIFY(callMethodPeer(srv_con, "/p1/q/r"));
684         QCOMPARE(obj.path, QString("/p1/q/r"));
685     }
686     // make sure it's gone
687     QVERIFY(!callMethodPeer(srv_con, "/p1/q/r"));
688 
689     {
690         MyObject obj;
691         QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots));
692         QVERIFY(callMethodPeer(srv_con, "/p1/q2"));
693         QCOMPARE(obj.path, QString("/p1/q2"));
694 
695         // try unregistering
696         con.unregisterObject("/p1/q2");
697         QVERIFY(!callMethodPeer(srv_con, "/p1/q2"));
698 
699         // register it again
700         QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots));
701         QVERIFY(callMethodPeer(srv_con, "/p1/q2"));
702         QCOMPARE(obj.path, QString("/p1/q2"));
703 
704         // now try removing things around it:
705         con.unregisterObject("/p2");
706         QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unrelated object shouldn't affect
707 
708         con.unregisterObject("/p1");
709         QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering just the parent shouldn't affect it
710 
711         con.unregisterObject("/p1/q2/r");
712         QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering non-existing child shouldn't affect it either
713 
714         con.unregisterObject("/p1/q");
715         QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering sibling (before) shouldn't affect
716 
717         con.unregisterObject("/p1/r");
718         QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering sibling (after) shouldn't affect
719 
720         // now remove it:
721         con.unregisterObject("/p1", QDBusConnection::UnregisterTree);
722         QVERIFY(!callMethodPeer(srv_con, "/p1/q2")); // we removed the full tree
723     }
724 
725     QDBusConnection::disconnectFromPeer("foo");
726 }
727 
728 
registerQObjectChildren()729 void tst_QDBusConnection::registerQObjectChildren()
730 {
731     // make sure no one is there
732     QDBusConnection con = QDBusConnection::sessionBus();
733     QVERIFY(!callMethod(con, "/p1"));
734 
735     {
736         MyObject obj, *a, *b, *c, *cc;
737 
738         a = new MyObject(&obj);
739         a->setObjectName("a");
740 
741         b = new MyObject(&obj);
742         b->setObjectName("b");
743 
744         c = new MyObject(&obj);
745         c->setObjectName("c");
746 
747         cc = new MyObject(c);
748         cc->setObjectName("cc");
749 
750         con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots |
751                            QDBusConnection::ExportChildObjects);
752 
753         // make calls
754         QVERIFY(callMethod(con, "/p1"));
755         QCOMPARE(obj.callCount, 1);
756         QVERIFY(callMethod(con, "/p1/a"));
757         QCOMPARE(a->callCount, 1);
758         QVERIFY(callMethod(con, "/p1/b"));
759         QCOMPARE(b->callCount, 1);
760         QVERIFY(callMethod(con, "/p1/c"));
761         QCOMPARE(c->callCount, 1);
762         QVERIFY(callMethod(con, "/p1/c/cc"));
763         QCOMPARE(cc->callCount, 1);
764 
765         QVERIFY(!callMethod(con, "/p1/d"));
766         QVERIFY(!callMethod(con, "/p1/c/abc"));
767 
768         // pull an object, see if it goes away:
769         delete b;
770         QVERIFY(!callMethod(con, "/p1/b"));
771 
772         delete c;
773         QVERIFY(!callMethod(con, "/p1/c"));
774         QVERIFY(!callMethod(con, "/p1/c/cc"));
775     }
776 
777     QVERIFY(!callMethod(con, "/p1"));
778     QVERIFY(!callMethod(con, "/p1/a"));
779     QVERIFY(!callMethod(con, "/p1/b"));
780     QVERIFY(!callMethod(con, "/p1/c"));
781     QVERIFY(!callMethod(con, "/p1/c/cc"));
782 }
783 
registerQObjectChildrenPeer()784 void tst_QDBusConnection::registerQObjectChildrenPeer()
785 {
786     MyServer2 server("unix:tmpdir=/tmp", 0);
787     QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo");
788     QCoreApplication::processEvents();
789     QVERIFY(con.isConnected());
790 
791     QDBusConnection srv_con = server.connection();
792 
793     QVERIFY(!callMethodPeer(srv_con, "/p1"));
794 
795     {
796         MyObject obj, *a, *b, *c, *cc;
797 
798         a = new MyObject(&obj);
799         a->setObjectName("a");
800 
801         b = new MyObject(&obj);
802         b->setObjectName("b");
803 
804         c = new MyObject(&obj);
805         c->setObjectName("c");
806 
807         cc = new MyObject(c);
808         cc->setObjectName("cc");
809 
810         con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots |
811                            QDBusConnection::ExportChildObjects);
812 
813         // make calls
814         QVERIFY(callMethodPeer(srv_con, "/p1"));
815         QCOMPARE(obj.callCount, 1);
816         QVERIFY(callMethodPeer(srv_con, "/p1/a"));
817         QCOMPARE(a->callCount, 1);
818         QVERIFY(callMethodPeer(srv_con, "/p1/b"));
819         QCOMPARE(b->callCount, 1);
820         QVERIFY(callMethodPeer(srv_con, "/p1/c"));
821         QCOMPARE(c->callCount, 1);
822         QVERIFY(callMethodPeer(srv_con, "/p1/c/cc"));
823         QCOMPARE(cc->callCount, 1);
824 
825         QVERIFY(!callMethodPeer(srv_con, "/p1/d"));
826         QVERIFY(!callMethodPeer(srv_con, "/p1/c/abc"));
827 
828         // pull an object, see if it goes away:
829         delete b;
830         QVERIFY(!callMethodPeer(srv_con, "/p1/b"));
831 
832         delete c;
833         QVERIFY(!callMethodPeer(srv_con, "/p1/c"));
834         QVERIFY(!callMethodPeer(srv_con, "/p1/c/cc"));
835     }
836 
837     QVERIFY(!callMethodPeer(srv_con, "/p1"));
838     QVERIFY(!callMethodPeer(srv_con, "/p1/a"));
839     QVERIFY(!callMethodPeer(srv_con, "/p1/b"));
840     QVERIFY(!callMethodPeer(srv_con, "/p1/c"));
841     QVERIFY(!callMethodPeer(srv_con, "/p1/c/cc"));
842 
843     QDBusConnection::disconnectFromPeer("foo");
844 }
845 
callMethod(const QDBusConnection & conn,const QString & path)846 bool tst_QDBusConnection::callMethod(const QDBusConnection &conn, const QString &path)
847 {
848     QDBusMessage msg = QDBusMessage::createMethodCall(conn.baseService(), path, "", "method");
849     QDBusMessage reply = conn.call(msg, QDBus::Block/*WithGui*/);
850     if (reply.type() != QDBusMessage::ReplyMessage)
851         return false;
852     if (MyObject::path == path) {
853         QTest::compare_helper(true, "COMPARE()", __FILE__, __LINE__);
854     } else {
855         QTest::compare_helper(false, "Compared values are not the same",
856                               QTest::toString(MyObject::path), QTest::toString(path),
857                               "MyObject::path", "path", __FILE__, __LINE__);
858         return false;
859     }
860 
861     return true;
862 }
863 
callMethodPeer(const QDBusConnection & conn,const QString & path)864 bool tst_QDBusConnection::callMethodPeer(const QDBusConnection &conn, const QString &path)
865 {
866     QDBusMessage msg = QDBusMessage::createMethodCall("", path, "", "method");
867     QDBusMessage reply = conn.call(msg, QDBus::BlockWithGui);
868 
869     if (reply.type() != QDBusMessage::ReplyMessage)
870         return false;
871     if (MyObject::path == path) {
872         QTest::compare_helper(true, "COMPARE()", __FILE__, __LINE__);
873     } else {
874         QTest::compare_helper(false, "Compared values are not the same",
875                               QTest::toString(MyObject::path), QTest::toString(path),
876                               "MyObject::path", "path", __FILE__, __LINE__);
877         return false;
878     }
879 
880     return true;
881 }
882 
883 class TestObject : public QObject
884 {
885 Q_OBJECT
886 public:
TestObject(QObject * parent=0)887     TestObject(QObject *parent = 0) : QObject(parent) {}
~TestObject()888     ~TestObject() {}
889 
890     QString func;
891 
892 public slots:
test0()893     void test0() { func = "test0"; }
test1(int i)894     void test1(int i) { func = "test1 " + QString::number(i); }
test2()895     int test2() { func = "test2"; return 43; }
test3(int i)896     int test3(int i) { func = "test2"; return i + 1; }
897 };
898 
callSelf()899 void tst_QDBusConnection::callSelf()
900 {
901     TestObject testObject;
902     QDBusConnection connection = QDBusConnection::sessionBus();
903     QVERIFY(connection.registerObject("/test", &testObject,
904             QDBusConnection::ExportAllContents));
905     QCOMPARE(connection.objectRegisteredAt("/test"), static_cast<QObject *>(&testObject));
906     QVERIFY(connection.registerService(serviceName()));
907     QDBusInterface interface(serviceName(), "/test");
908     QVERIFY(interface.isValid());
909 
910     interface.call(QDBus::Block, "test0");
911     QCOMPARE(testObject.func, QString("test0"));
912     interface.call(QDBus::Block, "test1", 42);
913     QCOMPARE(testObject.func, QString("test1 42"));
914     QDBusMessage reply = interface.call(QDBus::Block, "test2");
915     QCOMPARE(testObject.func, QString("test2"));
916     QCOMPARE(reply.arguments().value(0).toInt(), 43);
917 
918     QDBusMessage msg = QDBusMessage::createMethodCall(serviceName(), "/test",
919                                                       QString(), "test3");
920     msg << 44;
921     reply = connection.call(msg);
922     QCOMPARE(reply.arguments().value(0).toInt(), 45);
923 }
924 
callSelfByAnotherName_data()925 void tst_QDBusConnection::callSelfByAnotherName_data()
926 {
927     QTest::addColumn<int>("registerMethod");
928     QTest::newRow("connection") << 0;
929     QTest::newRow("connection-interface") << 1;
930     QTest::newRow("direct") << 2;
931 }
932 
callSelfByAnotherName()933 void tst_QDBusConnection::callSelfByAnotherName()
934 {
935     static int counter = 0;
936     QString sname = serviceName() + QString::number(counter++);
937 
938     QDBusConnection con = QDBusConnection::sessionBus();
939     QVERIFY(con.isConnected());
940 
941     TestObject testObject;
942     QVERIFY(con.registerObject("/test", &testObject,
943             QDBusConnection::ExportAllContents));
944     con.connect("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged",
945                 QStringList() << sname << "",
946                 QString(), &QTestEventLoop::instance(), SLOT(exitLoop()));
947 
948     // register the name
949     QFETCH(int, registerMethod);
950     switch (registerMethod) {
951     case 0:
952         QVERIFY(con.registerService(sname));
953         break;
954 
955     case 1:
956         QVERIFY(con.interface()->registerService(sname).value() == QDBusConnectionInterface::ServiceRegistered);
957         break;
958 
959     case 2: {
960             // flag is DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04
961             // reply is DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1
962             QDBusReply<uint> reply = con.interface()->call("RequestName", sname, 4u);
963             QVERIFY(reply.value() == 1);
964         }
965     }
966 
967     struct Deregisterer {
968         QDBusConnection con;
969         QString sname;
970         Deregisterer(const QDBusConnection &con, const QString &sname) : con(con), sname(sname) {}
971         ~Deregisterer() { con.interface()->unregisterService(sname); }
972     } deregisterer(con, sname);
973 
974     // give the connection a chance to find out that we're good to go
975     QTestEventLoop::instance().enterLoop(2);
976     con.disconnect("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged",
977                  QStringList() << sname << "",
978                  QString(), &QTestEventLoop::instance(), SLOT(exitLoop()));
979     QVERIFY(!QTestEventLoop::instance().timeout());
980 
981     // make the call
982     QDBusMessage msg = QDBusMessage::createMethodCall(sname, "/test",
983                                                       QString(), "test0");
984     QDBusMessage reply = con.call(msg, QDBus::Block, 1000);
985 
986     QVERIFY(reply.type() == QDBusMessage::ReplyMessage);
987 }
988 
multipleInterfacesInQObject()989 void tst_QDBusConnection::multipleInterfacesInQObject()
990 {
991     QDBusConnection con = QDBusConnection::sessionBus();
992     QVERIFY(!callMethod(con, "/p1"));
993 
994     MyObject obj;
995     con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots);
996 
997     // check if we can call the BaseObject's interface
998     QDBusMessage msg = QDBusMessage::createMethodCall(con.baseService(), "/p1",
999                                                       "local.BaseObject", "anotherMethod");
1000     QDBusMessage reply = con.call(msg, QDBus::Block);
1001     QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
1002     QVERIFY(reply.arguments().count() == 0);
1003 }
1004 
slotsWithLessParameters()1005 void tst_QDBusConnection::slotsWithLessParameters()
1006 {
1007     QDBusConnection con = QDBusConnection::sessionBus();
1008 
1009     QDBusMessage signal = QDBusMessage::createSignal("/", "com.trolltech.TestCase",
1010                                                      "oneSignal");
1011     signal << "one parameter";
1012 
1013     signalsReceived = 0;
1014     QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(),
1015                         signal.member(), this, SLOT(oneSlot())));
1016     QVERIFY(con.send(signal));
1017     QTest::qWait(100);
1018     QCOMPARE(signalsReceived, 1);
1019 
1020     // disconnect and try with a signature
1021     signalsReceived = 0;
1022     QVERIFY(con.disconnect(con.baseService(), signal.path(), signal.interface(),
1023                            signal.member(), this, SLOT(oneSlot())));
1024     QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(),
1025                         signal.member(), "s", this, SLOT(oneSlot())));
1026     QVERIFY(con.send(signal));
1027     QTest::qWait(100);
1028     QCOMPARE(signalsReceived, 1);
1029 }
1030 
secondCallWithCallback()1031 void tst_QDBusConnection::secondCallWithCallback()
1032 {
1033     qDebug("Hello");
1034     QDBusConnection con = QDBusConnection::sessionBus();
1035     QDBusMessage msg = QDBusMessage::createMethodCall(con.baseService(), "/test", QString(),
1036                                                       "test0");
1037     con.callWithCallback(msg, this, SLOT(exitLoop()), SLOT(secondCallWithCallback()));
1038 }
1039 
nestedCallWithCallback()1040 void tst_QDBusConnection::nestedCallWithCallback()
1041 {
1042     TestObject testObject;
1043     QDBusConnection connection = QDBusConnection::sessionBus();
1044     QVERIFY(connection.registerObject("/test", &testObject,
1045             QDBusConnection::ExportAllContents));
1046 
1047     QDBusMessage msg = QDBusMessage::createMethodCall(connection.baseService(), "/test", QString(),
1048                                                       "ThisFunctionDoesntExist");
1049     signalsReceived = 0;
1050 
1051     connection.callWithCallback(msg, this, SLOT(exitLoop()), SLOT(secondCallWithCallback()), 10);
1052     QTestEventLoop::instance().enterLoop(15);
1053     QVERIFY(!QTestEventLoop::instance().timeout());
1054     QCOMPARE(signalsReceived, 1);
1055 }
1056 
1057 class RaceConditionSignalWaiter : public QObject
1058 {
1059     Q_OBJECT
1060 public:
1061     int count;
RaceConditionSignalWaiter()1062     RaceConditionSignalWaiter() : count (0) {}
~RaceConditionSignalWaiter()1063     virtual ~RaceConditionSignalWaiter() {}
1064 
1065 public slots:
countUp()1066     void countUp() { ++count; emit done(); }
1067 signals:
1068     void done();
1069 };
1070 
serviceRegistrationRaceCondition()1071 void tst_QDBusConnection::serviceRegistrationRaceCondition()
1072 {
1073     // There was a race condition in the updating of list of name owners in
1074     // QtDBus. When the user connects to a signal coming from a given
1075     // service, we must listen for NameOwnerChanged signals relevant to that
1076     // name and update when the owner changes. However, it's possible that we
1077     // receive in one chunk from the server both the NameOwnerChanged signal
1078     // about the service and the signal we're interested in. Since QtDBus
1079     // posts events in order to handle the incoming signals, the update
1080     // happens too late.
1081 
1082     const QString connectionName = "testConnectionName";
1083     const QString serviceName = "org.example.SecondaryName";
1084 
1085     QDBusConnection session = QDBusConnection::sessionBus();
1086     QVERIFY(!session.interface()->isServiceRegistered(serviceName));
1087 
1088     // connect to the signal:
1089     RaceConditionSignalWaiter recv;
1090     session.connect(serviceName, "/", "com.trolltech.TestCase", "oneSignal", &recv, SLOT(countUp()));
1091 
1092     // create a secondary connection and register a name
1093     QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, connectionName);
1094     QDBusConnection::disconnectFromBus(connectionName); // disconnection happens when "connection" goes out of scope
1095     QVERIFY(connection.isConnected());
1096     QVERIFY(connection.registerService(serviceName));
1097 
1098     // send a signal
1099     QDBusMessage msg = QDBusMessage::createSignal("/", "com.trolltech.TestCase", "oneSignal");
1100     connection.send(msg);
1101 
1102     // make a blocking call just to be sure that the buffer was flushed
1103     msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
1104                                          "NameHasOwner");
1105     msg << connectionName;
1106     connection.call(msg); // ignore result
1107 
1108     // Now here's the race condition (more info on task QTBUG-15651):
1109     // the bus has most likely queued three signals for us to work on:
1110     // 1) NameOwnerChanged for the connection we created above
1111     // 2) NameOwnerChanged for the service we registered above
1112     // 3) The "oneSignal" signal we sent
1113     //
1114     // We'll most likely receive all three in one go from the server. We must
1115     // update the owner of serviceName before we start processing the
1116     // "oneSignal" signal.
1117 
1118     QTestEventLoop::instance().connect(&recv, SIGNAL(done()), SLOT(exitLoop()));
1119     QTestEventLoop::instance().enterLoop(1);
1120     QVERIFY(!QTestEventLoop::instance().timeout());
1121     QCOMPARE(recv.count, 1);
1122 }
1123 
1124 class VirtualObject: public QDBusVirtualObject
1125 {
1126     Q_OBJECT
1127 public:
VirtualObject()1128     VirtualObject() :success(true) {}
1129 
introspect(const QString & path) const1130     QString introspect(const QString &path) const
1131     {
1132         return QString();
1133     }
1134 
handleMessage(const QDBusMessage & message,const QDBusConnection & connection)1135     bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) {
1136         ++callCount;
1137         lastMessage = message;
1138 
1139         if (success) {
1140             QDBusMessage reply = message.createReply(replyArguments);
1141             connection.send(reply);
1142         }
1143         emit messageReceived(message);
1144         return success;
1145     }
1146 signals:
1147     void messageReceived(const QDBusMessage &message) const;
1148 
1149 public:
1150     mutable QDBusMessage lastMessage;
1151     QVariantList replyArguments;
1152     mutable int callCount;
1153     bool success;
1154 };
1155 
1156 
registerVirtualObject()1157 void tst_QDBusConnection::registerVirtualObject()
1158 {
1159     QDBusConnection con = QDBusConnection::sessionBus();
1160     QVERIFY(con.isConnected());
1161 
1162     QString path = "/tree/node";
1163     QString childPath = "/tree/node/child";
1164     QString childChildPath = "/tree/node/child/another";
1165 
1166     {
1167         // Register VirtualObject that handles child paths. Unregister by going out of scope.
1168         VirtualObject obj;
1169         QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
1170         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(&obj));
1171         QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&obj));
1172         QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(&obj));
1173     }
1174     QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
1175     QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0));
1176 
1177     {
1178         // Register VirtualObject that handles child paths. Unregister by calling unregister.
1179         VirtualObject obj;
1180         QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
1181         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(&obj));
1182         QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&obj));
1183         QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(&obj));
1184         con.unregisterObject(path);
1185         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
1186         QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0));
1187     }
1188 
1189     {
1190         // Single node has no sub path handling.
1191         VirtualObject obj;
1192         QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SingleNode));
1193         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(&obj));
1194         QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0));
1195     }
1196 
1197     {
1198         // Register VirtualObject that handles child paths. Try to register an object on a child path of that.
1199         VirtualObject obj;
1200         QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
1201         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(&obj));
1202 
1203         QObject objectAtSubPath;
1204         QVERIFY(!con.registerObject(path, &objectAtSubPath));
1205         QVERIFY(!con.registerObject(childPath, &objectAtSubPath));
1206         QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&obj));
1207     }
1208 
1209     {
1210         // Register object, make sure no SubPath handling object can be registered on a parent path.
1211         QObject objectAtSubPath;
1212         QVERIFY(con.registerObject(childPath, &objectAtSubPath));
1213         QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&objectAtSubPath));
1214 
1215         VirtualObject obj;
1216         QVERIFY(!con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
1217         QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
1218     }
1219     QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0));
1220     QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0));
1221     QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(0));
1222 }
1223 
callVirtualObject()1224 void tst_QDBusConnection::callVirtualObject()
1225 {
1226     QDBusConnection con = QDBusConnection::sessionBus();
1227     QVERIFY(con.isConnected());
1228 
1229     QDBusConnection con2 = QDBusConnection::connectToBus(QDBusConnection::SessionBus, "con2");
1230 
1231     QString path = "/tree/node";
1232     QString childPath = "/tree/node/child";
1233 
1234     // register one object at root:
1235     VirtualObject obj;
1236     QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
1237     obj.callCount = 0;
1238     obj.replyArguments << 42 << 47u;
1239 
1240     QObject::connect(&obj, SIGNAL(messageReceived(QDBusMessage)), &QTestEventLoop::instance(), SLOT(exitLoop()));
1241 
1242     QDBusMessage message = QDBusMessage::createMethodCall(con.baseService(), path, QString(), "hello");
1243     QDBusPendingCall reply = con2.asyncCall(message);
1244 
1245     QTestEventLoop::instance().enterLoop(5);
1246     QVERIFY(!QTestEventLoop::instance().timeout());
1247 
1248     QCOMPARE(obj.callCount, 1);
1249     QCOMPARE(obj.lastMessage.service(), con2.baseService());
1250     QCOMPARE(obj.lastMessage.interface(), QString());
1251     QCOMPARE(obj.lastMessage.path(), path);
1252     reply.waitForFinished();
1253     QVERIFY(reply.isValid());
1254     QCOMPARE(reply.reply().arguments(), obj.replyArguments);
1255 
1256     // call sub path
1257     QDBusMessage childMessage = QDBusMessage::createMethodCall(con.baseService(), childPath, QString(), "helloChild");
1258     obj.replyArguments.clear();
1259     obj.replyArguments << 99;
1260     QDBusPendingCall childReply = con2.asyncCall(childMessage);
1261 
1262     QTestEventLoop::instance().enterLoop(5);
1263     QVERIFY(!QTestEventLoop::instance().timeout());
1264 
1265     QCOMPARE(obj.callCount, 2);
1266     QCOMPARE(obj.lastMessage.service(), con2.baseService());
1267     QCOMPARE(obj.lastMessage.interface(), QString());
1268     QCOMPARE(obj.lastMessage.path(), childPath);
1269 
1270     childReply.waitForFinished();
1271     QVERIFY(childReply.isValid());
1272     QCOMPARE(childReply.reply().arguments(), obj.replyArguments);
1273 
1274     // let the call fail by having the virtual object return false
1275     obj.success = false;
1276     QDBusMessage errorMessage = QDBusMessage::createMethodCall(con.baseService(), childPath, QString(), "someFunc");
1277     QDBusPendingCall errorReply = con2.asyncCall(errorMessage);
1278 
1279     QTestEventLoop::instance().enterLoop(5);
1280     QVERIFY(!QTestEventLoop::instance().timeout());
1281     QTest::qWait(100);
1282     QVERIFY(errorReply.isError());
1283     qDebug() << errorReply.reply().arguments();
1284     QCOMPARE(errorReply.reply().errorName(), QString("org.freedesktop.DBus.Error.UnknownObject"));
1285 
1286     QDBusConnection::disconnectFromBus("con2");
1287 }
1288 
callVirtualObjectLocal()1289 void tst_QDBusConnection::callVirtualObjectLocal()
1290 {
1291     QDBusConnection con = QDBusConnection::sessionBus();
1292     QVERIFY(con.isConnected());
1293 
1294     QString path = "/tree/node";
1295     QString childPath = "/tree/node/child";
1296 
1297     // register one object at root:
1298     VirtualObject obj;
1299     QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath));
1300     obj.callCount = 0;
1301     obj.replyArguments << 42 << 47u;
1302 
1303     QDBusMessage message = QDBusMessage::createMethodCall(con.baseService(), path, QString(), "hello");
1304     QDBusMessage reply = con.call(message, QDBus::Block, 5000);
1305     QCOMPARE(obj.callCount, 1);
1306     QCOMPARE(obj.lastMessage.service(), con.baseService());
1307     QCOMPARE(obj.lastMessage.interface(), QString());
1308     QCOMPARE(obj.lastMessage.path(), path);
1309     QCOMPARE(obj.replyArguments, reply.arguments());
1310 
1311     obj.replyArguments << QString("alien abduction");
1312     QDBusMessage subPathMessage = QDBusMessage::createMethodCall(con.baseService(), childPath, QString(), "hello");
1313     QDBusMessage subPathReply = con.call(subPathMessage , QDBus::Block, 5000);
1314     QCOMPARE(obj.callCount, 2);
1315     QCOMPARE(obj.lastMessage.service(), con.baseService());
1316     QCOMPARE(obj.lastMessage.interface(), QString());
1317     QCOMPARE(obj.lastMessage.path(), childPath);
1318     QCOMPARE(obj.replyArguments, subPathReply.arguments());
1319 }
1320 
1321 QString MyObject::path;
1322 QTEST_MAIN(tst_QDBusConnection)
1323 
1324 #include "tst_qdbusconnection.moc"
1325 
1326