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

source code of qtbase/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp