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