/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "tst_qdbusconnection.h" #include #include #include #include #ifdef Q_OS_UNIX # include # include #endif void MyObject::method(const QDBusMessage &msg) { path = msg.path(); ++callCount; //qDebug() << msg; } void MyObjectWithoutInterface::method(const QDBusMessage &msg) { path = msg.path(); interface = msg.interface(); ++callCount; //qDebug() << msg; } int tst_QDBusConnection::hookCallCount; tst_QDBusConnection::tst_QDBusConnection() { #ifdef HAS_HOOKSETUPFUNCTION # define QCOMPARE_HOOKCOUNT(n) QCOMPARE(hookCallCount, n); hookCallCount = 0 # define QVERIFY_HOOKCALLED() QCOMPARE(hookCallCount, 1); hookCallCount = 0 hookSetupFunction(); #else # define QCOMPARE_HOOKCOUNT(n) qt_noop() # define QVERIFY_HOOKCALLED() qt_noop() #endif } // called before each testcase void tst_QDBusConnection::init() { hookCallCount = 0; } void tst_QDBusConnection::cleanup() { QVERIFY2(!hookCallCount, "Unchecked call"); } void tst_QDBusConnection::noConnection() { QDBusConnection con = QDBusConnection::connectToBus("unix:path=/dev/null", "testconnection"); QVERIFY(!con.isConnected()); // try sending a message. This should fail QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.selftest", "/org/kde/selftest", "org.kde.selftest", "Ping"); msg << QLatin1String("ping"); QVERIFY(!con.send(msg)); QDBusSpy spy; QVERIFY(con.callWithCallback(msg, &spy, SLOT(asyncReply)) == 0); QDBusMessage reply = con.call(msg); QCOMPARE(reply.type(), QDBusMessage::ErrorMessage); QDBusReply voidreply(reply); QVERIFY(!voidreply.isValid()); QDBusConnection::disconnectFromBus("testconnection"); } void tst_QDBusConnection::sendSignal() { QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QDBusMessage msg = QDBusMessage::createSignal("/org/kde/selftest", "org.kde.selftest", "Ping"); msg << QLatin1String("ping"); QVERIFY(con.send(msg)); } void tst_QDBusConnection::sendSignalToName() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); // because of the qWait() QDBusSpy spy; QDBusConnection con = QDBusConnection::sessionBus(); con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy, SLOT(handlePing(QString))); QDBusMessage msg = QDBusMessage::createTargetedSignal(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping"); msg << QLatin1String("ping"); QVERIFY(con.send(msg)); QTRY_COMPARE(spy.args.count(), 1); QCOMPARE(spy.args.at(0).toString(), QString("ping")); } void tst_QDBusConnection::sendSignalToOtherName() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); // because of the qWait() QDBusSpy spy; QDBusConnection con = QDBusConnection::sessionBus(); con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy, SLOT(handlePing(QString))); QDBusMessage msg = QDBusMessage::createTargetedSignal("some.other.service", "/org/kde/selftest", "org.kde.selftest", "ping"); msg << QLatin1String("ping"); QVERIFY(con.send(msg)); QTest::qWait(1000); QCOMPARE(spy.args.count(), 0); } void tst_QDBusConnection::send() { QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"); QDBusMessage reply = con.call(msg); QCOMPARE(reply.arguments().count(), 1); QCOMPARE(reply.arguments().at(0).typeName(), "QStringList"); QVERIFY(reply.arguments().at(0).toStringList().contains(con.baseService())); } void tst_QDBusConnection::sendWithGui() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"); QDBusMessage reply = con.call(msg, QDBus::BlockWithGui); QCOMPARE(reply.arguments().count(), 1); QCOMPARE(reply.arguments().at(0).typeName(), "QStringList"); QVERIFY(reply.arguments().at(0).toStringList().contains(con.baseService())); } void tst_QDBusConnection::sendAsync() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QDBusSpy spy; QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"); QVERIFY(con.callWithCallback(msg, &spy, SLOT(asyncReply(QDBusMessage)))); QTRY_COMPARE(spy.args.count(), 1); QCOMPARE(spy.args.value(0).typeName(), "QStringList"); QVERIFY(spy.args.at(0).toStringList().contains(con.baseService())); } void tst_QDBusConnection::connect() { QDBusSpy spy; QDBusConnection con = QDBusConnection::sessionBus(); if (!QCoreApplication::instance()) return; // cannot receive signals in this thread without QCoreApplication con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy, SLOT(handlePing(QString))); QDBusMessage msg = QDBusMessage::createSignal("/org/kde/selftest", "org.kde.selftest", "ping"); msg << QLatin1String("ping"); QVERIFY(con.send(msg)); QTRY_COMPARE(spy.args.count(), 1); QCOMPARE(spy.args.at(0).toString(), QString("ping")); } void tst_QDBusConnection::connectToBus() { { QDBusConnection con = QDBusConnection::connectToBus( QDBusConnection::SessionBus, "bubu"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); QDBusConnection con2("foo"); QVERIFY(!con2.isConnected()); QVERIFY(con2.lastError().isValid()); con2 = con; QVERIFY(con.isConnected()); QVERIFY(con2.isConnected()); QVERIFY(!con.lastError().isValid()); QVERIFY(!con2.lastError().isValid()); } { QDBusConnection con("bubu"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); } QDBusConnection::disconnectFromPeer("bubu"); { QDBusConnection con("bubu"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); } QDBusConnection::disconnectFromBus("bubu"); { QDBusConnection con("bubu"); QVERIFY(!con.isConnected()); QVERIFY(con.lastError().isValid()); } QByteArray address = qgetenv("DBUS_SESSION_BUS_ADDRESS"); if (!address.isEmpty()) { QDBusConnection con = QDBusConnection::connectToBus(address, "newconn"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); QDBusConnection::disconnectFromBus("newconn"); } } void tst_QDBusConnection::connectToPeer() { { QDBusConnection con = QDBusConnection::connectToPeer( "", "newconn"); QVERIFY(!con.isConnected()); QVERIFY(con.lastError().isValid()); QDBusConnection::disconnectFromPeer("newconn"); } QDBusServer server; { QDBusConnection con = QDBusConnection::connectToPeer( "unix:abstract=/tmp/dbus-XXXXXXXXXX,guid=00000000000000000000000000000000", "newconn2"); QVERIFY(!con.isConnected()); QVERIFY(con.lastError().isValid()); QDBusConnection::disconnectFromPeer("newconn2"); } { QDBusConnection con = QDBusConnection::connectToPeer( server.address(), "bubu"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); QDBusConnection con2("foo"); QVERIFY(!con2.isConnected()); QVERIFY(con2.lastError().isValid()); con2 = con; QVERIFY(con.isConnected()); QVERIFY(con2.isConnected()); QVERIFY(!con.lastError().isValid()); QVERIFY(!con2.lastError().isValid()); } { QDBusConnection con("bubu"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); } QDBusConnection::disconnectFromBus("bubu"); { QDBusConnection con("bubu"); QVERIFY(con.isConnected()); QVERIFY(!con.lastError().isValid()); } QDBusConnection::disconnectFromPeer("bubu"); { QDBusConnection con("bubu"); QVERIFY(!con.isConnected()); QVERIFY(con.lastError().isValid()); } } void tst_QDBusConnection::registerObject_data() { QTest::addColumn("path"); QTest::newRow("/") << "/"; QTest::newRow("/p1") << "/p1"; QTest::newRow("/p2") << "/p2"; QTest::newRow("/p1/q") << "/p1/q"; QTest::newRow("/p1/q/r") << "/p1/q/r"; } void tst_QDBusConnection::registerObject() { QFETCH(QString, path); QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); //QVERIFY(!callMethod(con, path)); { // register one object at root: MyObject obj; QVERIFY(con.registerObject(path, &obj, QDBusConnection::ExportAllSlots)); QCOMPARE(con.objectRegisteredAt(path), static_cast(&obj)); QVERIFY(callMethod(con, path)); QCOMPARE(obj.path, path); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethod(con, path)); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::registerObjectWithInterface_data() { QTest::addColumn("path"); QTest::addColumn("interface"); QTest::newRow("/") << "/" << "org.foo"; QTest::newRow("/p1") << "/p1" << "org.foo"; QTest::newRow("/p2") << "/p2" << "org.foo"; QTest::newRow("/p1/q") << "/p1/q" << "org.foo"; QTest::newRow("/p1/q/r") << "/p1/q/r" << "org.foo"; } void tst_QDBusConnection::registerObjectWithInterface() { QFETCH(QString, path); QFETCH(QString, interface); QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); { // register one object at root: MyObjectWithoutInterface obj; QVERIFY(con.registerObject(path, interface, &obj, QDBusConnection::ExportAllSlots)); QCOMPARE(con.objectRegisteredAt(path), static_cast(&obj)); QVERIFY(callMethod(con, path, interface)); QCOMPARE(obj.path, path); QCOMPARE(obj.interface, interface); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethod(con, path, interface)); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::registerObjectPeer_data() { QTest::addColumn("path"); QTest::newRow("/") << "/"; QTest::newRow("/p1") << "/p1"; QTest::newRow("/p2") << "/p2"; QTest::newRow("/p1/q") << "/p1/q"; QTest::newRow("/p1/q/r") << "/p1/q/r"; } void tst_QDBusConnection::registerObjectPeer() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QFETCH(QString, path); MyServer server(path); QDBusConnection::connectToPeer(server.address(), "beforeFoo"); QTestEventLoop::instance().enterLoop(2); QVERIFY(!QTestEventLoop::instance().timeout()); { QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo"); QTestEventLoop::instance().enterLoop(2); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(con.isConnected()); MyObject obj; QVERIFY(callMethodPeer(con, path)); QCOMPARE(obj.path, path); QVERIFY_HOOKCALLED(); } QDBusConnection::connectToPeer(server.address(), "afterFoo"); QTestEventLoop::instance().enterLoop(2); { QDBusConnection con("foo"); QVERIFY(con.isConnected()); QVERIFY(callMethodPeer(con, path)); QVERIFY_HOOKCALLED(); } server.unregisterObject(); { QDBusConnection con("foo"); QVERIFY(con.isConnected()); QVERIFY(!callMethodPeer(con, path)); QVERIFY_HOOKCALLED(); } server.registerObject(); { QDBusConnection con("foo"); QVERIFY(con.isConnected()); QVERIFY(callMethodPeer(con, path)); QVERIFY_HOOKCALLED(); } QDBusConnection::disconnectFromPeer("foo"); { QDBusConnection con("foo"); QVERIFY(!con.isConnected()); QVERIFY(!callMethodPeer(con, path)); } QDBusConnection::disconnectFromPeer("beforeFoo"); QDBusConnection::disconnectFromPeer("afterFoo"); } void tst_QDBusConnection::registerObject2() { QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); // make sure nothing is using our paths: QVERIFY(!callMethod(con, "/")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p2")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/q")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/q/r")); QVERIFY_HOOKCALLED(); { // register one object at root: MyObject obj; QVERIFY(con.registerObject("/", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethod(con, "/")); QCOMPARE(obj.path, QString("/")); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethod(con, "/")); QVERIFY_HOOKCALLED(); { // register one at an element: MyObject obj; QVERIFY(con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(!callMethod(con, "/")); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p1")); QCOMPARE(obj.path, QString("/p1")); QVERIFY_HOOKCALLED(); // re-register it somewhere else QVERIFY(con.registerObject("/p2", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethod(con, "/p1")); QCOMPARE(obj.path, QString("/p1")); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p2")); QCOMPARE(obj.path, QString("/p2")); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethod(con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p2")); QVERIFY_HOOKCALLED(); { // register at a deep path MyObject obj; QVERIFY(con.registerObject("/p1/q/r", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(!callMethod(con, "/")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/q")); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p1/q/r")); QCOMPARE(obj.path, QString("/p1/q/r")); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethod(con, "/p1/q/r")); QVERIFY_HOOKCALLED(); { MyObject obj; QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethod(con, "/p1/q2")); QCOMPARE(obj.path, QString("/p1/q2")); QVERIFY_HOOKCALLED(); // try unregistering con.unregisterObject("/p1/q2"); QVERIFY(!callMethod(con, "/p1/q2")); QVERIFY_HOOKCALLED(); // register it again QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethod(con, "/p1/q2")); QCOMPARE(obj.path, QString("/p1/q2")); QVERIFY_HOOKCALLED(); // now try removing things around it: con.unregisterObject("/p2"); QVERIFY(callMethod(con, "/p1/q2")); // unrelated object shouldn't affect QVERIFY_HOOKCALLED(); con.unregisterObject("/p1"); QVERIFY(callMethod(con, "/p1/q2")); // unregistering just the parent shouldn't affect it QVERIFY_HOOKCALLED(); con.unregisterObject("/p1/q2/r"); QVERIFY(callMethod(con, "/p1/q2")); // unregistering non-existing child shouldn't affect it either QVERIFY_HOOKCALLED(); con.unregisterObject("/p1/q"); QVERIFY(callMethod(con, "/p1/q2")); // unregistering sibling (before) shouldn't affect QVERIFY_HOOKCALLED(); con.unregisterObject("/p1/r"); QVERIFY(callMethod(con, "/p1/q2")); // unregistering sibling (after) shouldn't affect QVERIFY_HOOKCALLED(); // now remove it: con.unregisterObject("/p1", QDBusConnection::UnregisterTree); QVERIFY(!callMethod(con, "/p1/q2")); // we removed the full tree QVERIFY_HOOKCALLED(); } } void tst_QDBusConnection::registerObjectPeer2() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); MyServer2 server; QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo"); QTestEventLoop::instance().enterLoop(2); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(con.isConnected()); QDBusConnection srv_con = server.connection(); // make sure nothing is using our paths: QVERIFY(!callMethodPeer(srv_con, "/")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p2")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/q")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/q/r")); QVERIFY_HOOKCALLED(); { // register one object at root: MyObject obj; QVERIFY(con.registerObject("/", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethodPeer(srv_con, "/")); QCOMPARE(obj.path, QString("/")); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethodPeer(srv_con, "/")); QVERIFY_HOOKCALLED(); { // register one at an element: MyObject obj; QVERIFY(con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(!callMethodPeer(srv_con, "/")); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p1")); QCOMPARE(obj.path, QString("/p1")); QVERIFY_HOOKCALLED(); // re-register it somewhere else QVERIFY(con.registerObject("/p2", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethodPeer(srv_con, "/p1")); QCOMPARE(obj.path, QString("/p1")); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p2")); QCOMPARE(obj.path, QString("/p2")); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethodPeer(srv_con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p2")); QVERIFY_HOOKCALLED(); { // register at a deep path MyObject obj; QVERIFY(con.registerObject("/p1/q/r", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(!callMethodPeer(srv_con, "/")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/q")); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p1/q/r")); QCOMPARE(obj.path, QString("/p1/q/r")); QVERIFY_HOOKCALLED(); } // make sure it's gone QVERIFY(!callMethodPeer(srv_con, "/p1/q/r")); QVERIFY_HOOKCALLED(); { MyObject obj; QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); QCOMPARE(obj.path, QString("/p1/q2")); QVERIFY_HOOKCALLED(); // try unregistering con.unregisterObject("/p1/q2"); QVERIFY(!callMethodPeer(srv_con, "/p1/q2")); QVERIFY_HOOKCALLED(); // register it again QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); QCOMPARE(obj.path, QString("/p1/q2")); QVERIFY_HOOKCALLED(); // now try removing things around it: con.unregisterObject("/p2"); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unrelated object shouldn't affect QVERIFY_HOOKCALLED(); con.unregisterObject("/p1"); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering just the parent shouldn't affect it QVERIFY_HOOKCALLED(); con.unregisterObject("/p1/q2/r"); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering non-existing child shouldn't affect it either QVERIFY_HOOKCALLED(); con.unregisterObject("/p1/q"); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering sibling (before) shouldn't affect QVERIFY_HOOKCALLED(); con.unregisterObject("/p1/r"); QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering sibling (after) shouldn't affect QVERIFY_HOOKCALLED(); // now remove it: con.unregisterObject("/p1", QDBusConnection::UnregisterTree); QVERIFY(!callMethodPeer(srv_con, "/p1/q2")); // we removed the full tree QVERIFY_HOOKCALLED(); } QDBusConnection::disconnectFromPeer("foo"); } void tst_QDBusConnection::registerQObjectChildren() { // make sure no one is there QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(!callMethod(con, "/p1")); QVERIFY_HOOKCALLED(); { MyObject obj, *a, *b, *c, *cc; a = new MyObject(&obj); a->setObjectName("a"); b = new MyObject(&obj); b->setObjectName("b"); c = new MyObject(&obj); c->setObjectName("c"); cc = new MyObject(c); cc->setObjectName("cc"); con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots | QDBusConnection::ExportChildObjects); // make calls QVERIFY(callMethod(con, "/p1")); QCOMPARE(obj.callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p1/a")); QCOMPARE(a->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p1/b")); QCOMPARE(b->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p1/c")); QCOMPARE(c->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethod(con, "/p1/c/cc")); QCOMPARE(cc->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/d")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/c/abc")); QVERIFY_HOOKCALLED(); // pull an object, see if it goes away: delete b; QVERIFY(!callMethod(con, "/p1/b")); QVERIFY_HOOKCALLED(); delete c; QVERIFY(!callMethod(con, "/p1/c")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/c/cc")); QVERIFY_HOOKCALLED(); } QVERIFY(!callMethod(con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/a")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/b")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/c")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethod(con, "/p1/c/cc")); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::registerQObjectChildrenPeer() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); MyServer2 server; QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo"); QTestEventLoop::instance().enterLoop(2); QVERIFY(!QTestEventLoop::instance().timeout()); QCoreApplication::processEvents(); QVERIFY(con.isConnected()); QDBusConnection srv_con = server.connection(); QVERIFY(!callMethodPeer(srv_con, "/p1")); QVERIFY_HOOKCALLED(); { MyObject obj, *a, *b, *c, *cc; a = new MyObject(&obj); a->setObjectName("a"); b = new MyObject(&obj); b->setObjectName("b"); c = new MyObject(&obj); c->setObjectName("c"); cc = new MyObject(c); cc->setObjectName("cc"); con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots | QDBusConnection::ExportChildObjects); // make calls QVERIFY(callMethodPeer(srv_con, "/p1")); QCOMPARE(obj.callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p1/a")); QCOMPARE(a->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p1/b")); QCOMPARE(b->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p1/c")); QCOMPARE(c->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(callMethodPeer(srv_con, "/p1/c/cc")); QCOMPARE(cc->callCount, 1); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/d")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/c/abc")); QVERIFY_HOOKCALLED(); // pull an object, see if it goes away: delete b; QVERIFY(!callMethodPeer(srv_con, "/p1/b")); QVERIFY_HOOKCALLED(); delete c; QVERIFY(!callMethodPeer(srv_con, "/p1/c")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/c/cc")); QVERIFY_HOOKCALLED(); } QVERIFY(!callMethodPeer(srv_con, "/p1")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/a")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/b")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/c")); QVERIFY_HOOKCALLED(); QVERIFY(!callMethodPeer(srv_con, "/p1/c/cc")); QVERIFY_HOOKCALLED(); QDBusConnection::disconnectFromPeer("foo"); } bool tst_QDBusConnection::callMethod(const QDBusConnection &conn, const QString &path) { QDBusMessage msg = QDBusMessage::createMethodCall(conn.baseService(), path, "", "method"); QDBusMessage reply = conn.call(msg, QDBus::Block/*WithGui*/); if (reply.type() != QDBusMessage::ReplyMessage) return false; QTest::qCompare(MyObject::path, path, "MyObject::path", "path", __FILE__, __LINE__); return (MyObject::path == path); } bool tst_QDBusConnection::callMethod(const QDBusConnection &conn, const QString &path, const QString &interface) { QDBusMessage msg = QDBusMessage::createMethodCall(conn.baseService(), path, interface, "method"); QDBusMessage reply = conn.call(msg, QDBus::Block/*WithGui*/); if (reply.type() != QDBusMessage::ReplyMessage) return false; QTest::qCompare(MyObjectWithoutInterface::path, path, "MyObjectWithoutInterface::path", "path", __FILE__, __LINE__); return (MyObjectWithoutInterface::path == path) && MyObjectWithoutInterface::interface == interface; } bool tst_QDBusConnection::callMethodPeer(const QDBusConnection &conn, const QString &path) { QDBusMessage msg = QDBusMessage::createMethodCall("", path, "", "method"); QDBusMessage reply = conn.call(msg, QDBus::BlockWithGui); if (reply.type() != QDBusMessage::ReplyMessage) return false; QTest::qCompare(MyObject::path, path, "MyObject::path", "path", __FILE__, __LINE__); return (MyObject::path == path); } void tst_QDBusConnection::callSelf() { TestObject testObject; QDBusConnection connection = QDBusConnection::sessionBus(); QVERIFY(connection.registerObject("/test", &testObject, QDBusConnection::ExportAllContents)); QCOMPARE(connection.objectRegisteredAt("/test"), static_cast(&testObject)); QVERIFY(connection.registerService(serviceName())); QDBusInterface interface(serviceName(), "/test"); QVERIFY(interface.isValid()); QVERIFY_HOOKCALLED(); interface.call(QDBus::Block, "test0"); QCOMPARE(testObject.func, QString("test0")); QVERIFY_HOOKCALLED(); interface.call(QDBus::Block, "test1", 42); QCOMPARE(testObject.func, QString("test1 42")); QVERIFY_HOOKCALLED(); QDBusMessage reply = interface.call(QDBus::Block, "test2"); QCOMPARE(testObject.func, QString("test2")); QCOMPARE(reply.arguments().value(0).toInt(), 43); QVERIFY_HOOKCALLED(); QDBusMessage msg = QDBusMessage::createMethodCall(serviceName(), "/test", QString(), "test3"); msg << 44; reply = connection.call(msg); QCOMPARE(reply.arguments().value(0).toInt(), 45); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::callSelfByAnotherName_data() { QTest::addColumn("registerMethod"); QTest::newRow("connection") << 0; QTest::newRow("connection-interface") << 1; QTest::newRow("direct") << 2; } void tst_QDBusConnection::callSelfByAnotherName() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); static int counter = 0; QString sname = serviceName() + QString::number(counter++); QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); TestObject testObject; QVERIFY(con.registerObject("/test", &testObject, QDBusConnection::ExportAllContents)); con.connect("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", QStringList() << sname << "", QString(), &QTestEventLoop::instance(), SLOT(exitLoop())); // register the name QFETCH(int, registerMethod); switch (registerMethod) { case 0: QVERIFY(con.registerService(sname)); break; case 1: QCOMPARE(con.interface()->registerService(sname).value(), QDBusConnectionInterface::ServiceRegistered); break; case 2: { // flag is DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04 // reply is DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1 QDBusReply reply = con.interface()->call("RequestName", sname, 4u); QCOMPARE(reply.value(), uint(1)); } } struct Deregisterer { QDBusConnection con; QString sname; Deregisterer(const QDBusConnection &con, const QString &sname) : con(con), sname(sname) {} ~Deregisterer() { con.interface()->unregisterService(sname); } } deregisterer(con, sname); // give the connection a chance to find out that we're good to go QTestEventLoop::instance().enterLoop(2); con.disconnect("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", QStringList() << sname << "", QString(), &QTestEventLoop::instance(), SLOT(exitLoop())); QVERIFY(!QTestEventLoop::instance().timeout()); // make the call QDBusMessage msg = QDBusMessage::createMethodCall(sname, "/test", QString(), "test0"); QDBusMessage reply = con.call(msg, QDBus::Block, 1000); QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::multipleInterfacesInQObject() { QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(!callMethod(con, "/p1")); QVERIFY_HOOKCALLED(); MyObject obj; con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots); // check if we can call the BaseObject's interface QDBusMessage msg = QDBusMessage::createMethodCall(con.baseService(), "/p1", "local.BaseObject", "anotherMethod"); QDBusMessage reply = con.call(msg, QDBus::Block); QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); QCOMPARE(reply.arguments().count(), 0); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::connectSignal() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QDBusConnection con = QDBusConnection::sessionBus(); QDBusMessage signal = QDBusMessage::createSignal("/", "org.qtproject.TestCase", "oneSignal"); signal << "one parameter"; SignalReceiver recv; QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(), signal.member(), &recv, SLOT(oneSlot(QString)))); QVERIFY(con.send(signal)); QTRY_COMPARE(recv.signalsReceived, 1); QCOMPARE(recv.argumentReceived, signal.arguments().at(0).toString()); // disconnect and try with a signature recv.argumentReceived.clear(); recv.signalsReceived = 0; QVERIFY(con.disconnect(con.baseService(), signal.path(), signal.interface(), signal.member(), &recv, SLOT(oneSlot(QString)))); QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(), signal.member(), "s", &recv, SLOT(oneSlot(QString)))); QVERIFY(con.send(signal)); QTRY_COMPARE(recv.signalsReceived, 1); QCOMPARE(recv.argumentReceived, signal.arguments().at(0).toString()); // confirm that we are, indeed, a unique connection recv.argumentReceived.clear(); recv.signalsReceived = 0; QVERIFY(!con.connect(con.baseService(), signal.path(), signal.interface(), signal.member(), "s", &recv, SLOT(oneSlot(QString)))); QVERIFY(con.send(signal)); QTRY_COMPARE(recv.signalsReceived, 1); QCOMPARE(recv.argumentReceived, signal.arguments().at(0).toString()); } void tst_QDBusConnection::slotsWithLessParameters() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QDBusConnection con = QDBusConnection::sessionBus(); QDBusMessage signal = QDBusMessage::createSignal("/", "org.qtproject.TestCase", "oneSignal"); signal << "one parameter"; SignalReceiver recv; QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(), signal.member(), &recv, SLOT(oneSlot()))); QVERIFY(con.send(signal)); QTRY_COMPARE(recv.signalsReceived, 1); QCOMPARE(recv.argumentReceived, QString()); // disconnect and try with a signature recv.signalsReceived = 0; QVERIFY(con.disconnect(con.baseService(), signal.path(), signal.interface(), signal.member(), &recv, SLOT(oneSlot()))); QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(), signal.member(), "s", &recv, SLOT(oneSlot()))); QVERIFY(con.send(signal)); QTRY_COMPARE(recv.signalsReceived, 1); QCOMPARE(recv.argumentReceived, QString()); // confirm that we are, indeed, a unique connection recv.signalsReceived = 0; QVERIFY(!con.connect(con.baseService(), signal.path(), signal.interface(), signal.member(), "s", &recv, SLOT(oneSlot()))); QVERIFY(con.send(signal)); QTRY_COMPARE(recv.signalsReceived, 1); QCOMPARE(recv.argumentReceived, QString()); } void SignalReceiver::secondCallWithCallback() { QDBusConnection con = QDBusConnection::sessionBus(); QDBusMessage msg = QDBusMessage::createMethodCall(con.baseService(), "/test", QString(), "test0"); con.callWithCallback(msg, this, SLOT(exitLoop()), SLOT(secondCallWithCallback())); } void tst_QDBusConnection::nestedCallWithCallback() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); TestObject testObject; QDBusConnection connection = QDBusConnection::sessionBus(); QVERIFY(connection.registerObject("/test", &testObject, QDBusConnection::ExportAllContents)); QDBusMessage msg = QDBusMessage::createMethodCall(connection.baseService(), "/test", QString(), "ThisFunctionDoesntExist"); SignalReceiver recv; connection.callWithCallback(msg, &recv, SLOT(exitLoop()), SLOT(secondCallWithCallback()), 10); QTestEventLoop::instance().enterLoop(15); QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(recv.signalsReceived, 1); QCOMPARE_HOOKCOUNT(2); } void tst_QDBusConnection::serviceRegistrationRaceCondition() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); // There was a race condition in the updating of list of name owners in // Qt D-Bus. When the user connects to a signal coming from a given // service, we must listen for NameOwnerChanged signals relevant to that // name and update when the owner changes. However, it's possible that we // receive in one chunk from the server both the NameOwnerChanged signal // about the service and the signal we're interested in. Since Qt D-Bus // posts events in order to handle the incoming signals, the update // happens too late. const QString connectionName = "testConnectionName"; const QString serviceName = "org.example.SecondaryName"; QDBusConnection session = QDBusConnection::sessionBus(); QVERIFY(!session.interface()->isServiceRegistered(serviceName)); // connect to the signal: RaceConditionSignalWaiter recv; session.connect(serviceName, "/", "org.qtproject.TestCase", "oneSignal", &recv, SLOT(countUp())); // create a secondary connection and register a name QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, connectionName); QDBusConnection::disconnectFromBus(connectionName); // disconnection happens when "connection" goes out of scope QVERIFY(connection.isConnected()); QVERIFY(connection.registerService(serviceName)); // send a signal QDBusMessage msg = QDBusMessage::createSignal("/", "org.qtproject.TestCase", "oneSignal"); connection.send(msg); // make a blocking call just to be sure that the buffer was flushed msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameHasOwner"); msg << connectionName; connection.call(msg); // ignore result // Now here's the race condition (more info on task QTBUG-15651): // the bus has most likely queued three signals for us to work on: // 1) NameOwnerChanged for the connection we created above // 2) NameOwnerChanged for the service we registered above // 3) The "oneSignal" signal we sent // // We'll most likely receive all three in one go from the server. We must // update the owner of serviceName before we start processing the // "oneSignal" signal. QTestEventLoop::instance().connect(&recv, SIGNAL(done()), SLOT(exitLoop())); QTestEventLoop::instance().enterLoop(1); QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(recv.count, 1); } void tst_QDBusConnection::registerVirtualObject() { QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QString path = "/tree/node"; QString childPath = "/tree/node/child"; QString childChildPath = "/tree/node/child/another"; { // Register VirtualObject that handles child paths. Unregister by going out of scope. VirtualObject obj; QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); QCOMPARE(con.objectRegisteredAt(path), static_cast(&obj)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(&obj)); QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast(&obj)); } QCOMPARE(con.objectRegisteredAt(path), static_cast(0)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(0)); { // Register VirtualObject that handles child paths. Unregister by calling unregister. VirtualObject obj; QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); QCOMPARE(con.objectRegisteredAt(path), static_cast(&obj)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(&obj)); QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast(&obj)); con.unregisterObject(path); QCOMPARE(con.objectRegisteredAt(path), static_cast(0)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(0)); } { // Single node has no sub path handling. VirtualObject obj; QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SingleNode)); QCOMPARE(con.objectRegisteredAt(path), static_cast(&obj)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(0)); } { // Register VirtualObject that handles child paths. Try to register an object on a child path of that. VirtualObject obj; QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); QCOMPARE(con.objectRegisteredAt(path), static_cast(&obj)); QObject objectAtSubPath; QVERIFY(!con.registerObject(path, &objectAtSubPath)); QVERIFY(!con.registerObject(childPath, &objectAtSubPath)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(&obj)); } { // Register object, make sure no SubPath handling object can be registered on a parent path. QObject objectAtSubPath; QVERIFY(con.registerObject(childPath, &objectAtSubPath)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(&objectAtSubPath)); VirtualObject obj; QVERIFY(!con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); QCOMPARE(con.objectRegisteredAt(path), static_cast(0)); } { // Register object, make sure no SubPath handling object can be registered on a parent path. // (same as above, but deeper) QObject objectAtSubPath; QVERIFY(con.registerObject(childChildPath, &objectAtSubPath)); QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast(&objectAtSubPath)); VirtualObject obj; QVERIFY(!con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); QCOMPARE(con.objectRegisteredAt(path), static_cast(0)); } QCOMPARE(con.objectRegisteredAt(path), static_cast(0)); QCOMPARE(con.objectRegisteredAt(childPath), static_cast(0)); QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast(0)); } void tst_QDBusConnection::callVirtualObject() { if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QDBusConnection con2 = QDBusConnection::connectToBus(QDBusConnection::SessionBus, "con2"); QString path = "/tree/node"; QString childPath = "/tree/node/child"; // register one object at root: VirtualObject obj; QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); obj.callCount = 0; obj.replyArguments << 42 << 47u; QObject::connect(&obj, SIGNAL(messageReceived(QDBusMessage)), &QTestEventLoop::instance(), SLOT(exitLoop())); QDBusMessage message = QDBusMessage::createMethodCall(con.baseService(), path, QString(), "hello"); QDBusPendingCall reply = con2.asyncCall(message); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY_HOOKCALLED(); QCOMPARE(obj.callCount, 1); QCOMPARE(obj.lastMessage.service(), con2.baseService()); QCOMPARE(obj.lastMessage.interface(), QString()); QCOMPARE(obj.lastMessage.path(), path); reply.waitForFinished(); QVERIFY(reply.isValid()); QCOMPARE(reply.reply().arguments(), obj.replyArguments); // call sub path QDBusMessage childMessage = QDBusMessage::createMethodCall(con.baseService(), childPath, QString(), "helloChild"); obj.replyArguments.clear(); obj.replyArguments << 99; QDBusPendingCall childReply = con2.asyncCall(childMessage); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY_HOOKCALLED(); QCOMPARE(obj.callCount, 2); QCOMPARE(obj.lastMessage.service(), con2.baseService()); QCOMPARE(obj.lastMessage.interface(), QString()); QCOMPARE(obj.lastMessage.path(), childPath); childReply.waitForFinished(); QVERIFY(childReply.isValid()); QCOMPARE(childReply.reply().arguments(), obj.replyArguments); // let the call fail by having the virtual object return false obj.success = false; QDBusMessage errorMessage = QDBusMessage::createMethodCall(con.baseService(), childPath, QString(), "someFunc"); QDBusPendingCall errorReply = con2.asyncCall(errorMessage); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY_HOOKCALLED(); QTest::qWait(100); QVERIFY(errorReply.isError()); QCOMPARE(errorReply.reply().errorName(), QString("org.freedesktop.DBus.Error.UnknownObject")); QDBusConnection::disconnectFromBus("con2"); } void tst_QDBusConnection::callVirtualObjectLocal() { QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); QString path = "/tree/node"; QString childPath = "/tree/node/child"; // register one object at root: VirtualObject obj; QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); obj.callCount = 0; obj.replyArguments << 42 << 47u; QDBusMessage message = QDBusMessage::createMethodCall(con.baseService(), path, QString(), "hello"); QDBusMessage reply = con.call(message, QDBus::Block, 5000); QCOMPARE(obj.callCount, 1); QCOMPARE(obj.lastMessage.service(), con.baseService()); QCOMPARE(obj.lastMessage.interface(), QString()); QCOMPARE(obj.lastMessage.path(), path); QCOMPARE(obj.replyArguments, reply.arguments()); QVERIFY_HOOKCALLED(); obj.replyArguments << QString("alien abduction"); QDBusMessage subPathMessage = QDBusMessage::createMethodCall(con.baseService(), childPath, QString(), "hello"); QDBusMessage subPathReply = con.call(subPathMessage , QDBus::Block, 5000); QCOMPARE(obj.callCount, 2); QCOMPARE(obj.lastMessage.service(), con.baseService()); QCOMPARE(obj.lastMessage.interface(), QString()); QCOMPARE(obj.lastMessage.path(), childPath); QCOMPARE(obj.replyArguments, subPathReply.arguments()); QVERIFY_HOOKCALLED(); } void tst_QDBusConnection::pendingCallWhenDisconnected() { #if !QT_CONFIG(process) QSKIP("Test requires QProcess"); #else if (!QCoreApplication::instance()) QSKIP("Test requires a QCoreApplication"); QProcess daemon; daemon.start("dbus-daemon", QStringList() << "--session" << "--nofork" << "--print-address"); QVERIFY2(daemon.waitForReadyRead(2000), "Daemon didn't print its address in time; error: \"" + daemon.errorString().toLocal8Bit() + "\"; stderr:\n" + daemon.readAllStandardError()); QString address = QString::fromLocal8Bit(daemon.readAll().trimmed()); QDBusConnection con = QDBusConnection::connectToBus(address, "disconnect"); QVERIFY2(con.isConnected(), (con.lastError().name() + ": " + con.lastError().message()).toLocal8Bit()); // confirm we're connected and we're alone in this bus QCOMPARE(con.baseService(), QString(":1.0")); // kill the bus daemon.terminate(); daemon.waitForFinished(); // send something, which we should get an error with QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.DBus", "/", QString(), "ListNames"); QDBusPendingCall reply = con.asyncCall(message); reply.waitForFinished(); QVERIFY(!con.isConnected()); QVERIFY(reply.isFinished()); QVERIFY(reply.isError()); QCOMPARE(reply.error().type(), QDBusError::Disconnected); #endif } QString MyObject::path; QString MyObjectWithoutInterface::path; QString MyObjectWithoutInterface::interface; #ifndef tst_QDBusConnection QTEST_MAIN(tst_QDBusConnection) #endif