diff options
-rw-r--r-- | src/dbus/dbus.pro | 9 | ||||
-rw-r--r-- | src/dbus/qdbusconnection.cpp | 52 | ||||
-rw-r--r-- | src/dbus/qdbusconnection.h | 16 | ||||
-rw-r--r-- | src/dbus/qdbusconnection_p.h | 13 | ||||
-rw-r--r-- | src/dbus/qdbusintegrator.cpp | 18 | ||||
-rw-r--r-- | src/dbus/qdbusinternalfilters.cpp | 8 | ||||
-rw-r--r-- | src/dbus/qdbusvirtualobject.cpp | 97 | ||||
-rw-r--r-- | src/dbus/qdbusvirtualobject.h | 81 | ||||
-rw-r--r-- | tests/auto/qdbusconnection/tst_qdbusconnection.cpp | 202 | ||||
-rw-r--r-- | tests/auto/qdbusinterface/tst_qdbusinterface.cpp | 71 |
10 files changed, 555 insertions, 12 deletions
diff --git a/src/dbus/dbus.pro b/src/dbus/dbus.pro index 08c9ea1..d21b380 100644 --- a/src/dbus/dbus.pro +++ b/src/dbus/dbus.pro @@ -44,7 +44,8 @@ PUB_HEADERS = qdbusargument.h \ qdbusmetatype.h \ qdbuspendingcall.h \ qdbuspendingreply.h \ - qdbuscontext.h + qdbuscontext.h \ + qdbusvirtualobject.h HEADERS += $$PUB_HEADERS \ qdbusconnection_p.h \ qdbusmessage_p.h \ @@ -60,7 +61,8 @@ HEADERS += $$PUB_HEADERS \ qdbuspendingcall_p.h \ qdbus_symbols_p.h \ qdbusservicewatcher.h \ - qdbusunixfiledescriptor.h + qdbusunixfiledescriptor.h \ + qdbusvirtualobject.h SOURCES += qdbusconnection.cpp \ qdbusconnectioninterface.cpp \ qdbuserror.cpp \ @@ -87,4 +89,5 @@ SOURCES += qdbusconnection.cpp \ qdbuspendingreply.cpp \ qdbus_symbols.cpp \ qdbusservicewatcher.cpp \ - qdbusunixfiledescriptor.cpp + qdbusunixfiledescriptor.cpp \ + qdbusvirtualobject.cpp diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index 0b4133c..e524103 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -222,6 +222,18 @@ void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionP */ /*! + \internal + \since 4.8 + \enum QDBusConnection::VirtualObjectRegisterOption + Specifies the options for registering virtual objects with the connection. The possible values are: + + \value SingleNode register a virtual object to handle one path only + \value SubPath register a virtual object so that it handles all sub paths + + \sa registerVirtualObject(), QDBusVirtualObject +*/ + +/*! \enum QDBusConnection::UnregisterMode The mode for unregistering an object path: @@ -801,9 +813,21 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis // this node exists // consider it free if there's no object here and the user is not trying to // replace the object sub-tree - if ((options & ExportChildObjects && !node->children.isEmpty()) || node->obj) + if (node->obj) return false; + if (options & QDBusConnectionPrivate::VirtualObject) { + // technically the check for children needs to go even deeper + if (options & SubPath) { + foreach (const QDBusConnectionPrivate::ObjectTreeNode &child, node->children) { + if (child.obj) + return false; + } + } + } else { + if ((options & ExportChildObjects && !node->children.isEmpty())) + return false; + } // we can add the object here node->obj = object; node->flags = options; @@ -813,6 +837,13 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis return true; } + // if a virtual object occupies this path, return false + if (node->obj && (node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) { + qDebug("Cannot register object at %s because QDBusVirtualObject handles all sub-paths.", + qPrintable(path)); + return false; + } + // find the position where we'd insert the node QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i)); @@ -841,6 +872,21 @@ bool QDBusConnection::registerObject(const QString &path, QObject *object, Regis } /*! + \internal + \since 4.8 + Registers a QDBusTreeNode for a path. It can handle a path including all child paths, thus + handling multiple DBus nodes. + + To unregister a QDBusTreeNode use the unregisterObject() function with its path. +*/ +bool QDBusConnection::registerVirtualObject(const QString &path, QDBusVirtualObject *treeNode, + VirtualObjectRegisterOption options) +{ + int opts = options | QDBusConnectionPrivate::VirtualObject; + return registerObject(path, (QObject*) treeNode, (RegisterOptions) opts); +} + +/*! Unregisters an object that was registered with the registerObject() at the object path given by \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too. @@ -905,6 +951,8 @@ QObject *QDBusConnection::objectRegisteredAt(const QString &path) const while (node) { if (pathComponents.count() == i) return node->obj; + if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) + return node->obj; QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i)); @@ -917,6 +965,8 @@ QObject *QDBusConnection::objectRegisteredAt(const QString &path) const return 0; } + + /*! Returns a QDBusConnectionInterface object that represents the D-Bus server interface on this connection. diff --git a/src/dbus/qdbusconnection.h b/src/dbus/qdbusconnection.h index 4bdd055..8b126af 100644 --- a/src/dbus/qdbusconnection.h +++ b/src/dbus/qdbusconnection.h @@ -69,6 +69,7 @@ class QDBusError; class QDBusMessage; class QDBusPendingCall; class QDBusConnectionInterface; +class QDBusVirtualObject; class QObject; class QDBusConnectionPrivate; @@ -104,8 +105,8 @@ public: // Qt 4.2 had a misspelling here ExportAllSignal = ExportAllSignals, #endif - ExportChildObjects = 0x1000 + // Reserved = 0xff000000 }; enum UnregisterMode { UnregisterNode, @@ -113,6 +114,15 @@ public: }; Q_DECLARE_FLAGS(RegisterOptions, RegisterOption) + enum VirtualObjectRegisterOption { + SingleNode = 0x0, + SubPath = 0x1 + // Reserved = 0xff000000 + }; +#ifndef Q_QDOC + Q_DECLARE_FLAGS(VirtualObjectRegisterOptions, VirtualObjectRegisterOption) +#endif + enum ConnectionCapability { UnixFileDescriptorPassing = 0x0001 }; @@ -163,6 +173,9 @@ public: void unregisterObject(const QString &path, UnregisterMode mode = UnregisterNode); QObject *objectRegisteredAt(const QString &path) const; + bool registerVirtualObject(const QString &path, QDBusVirtualObject *object, + VirtualObjectRegisterOption options = SingleNode); + bool registerService(const QString &serviceName); bool unregisterService(const QString &serviceName); @@ -192,6 +205,7 @@ private: }; Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::VirtualObjectRegisterOptions) QT_END_NAMESPACE diff --git a/src/dbus/qdbusconnection_p.h b/src/dbus/qdbusconnection_p.h index 12bcb21..d481cf1 100644 --- a/src/dbus/qdbusconnection_p.h +++ b/src/dbus/qdbusconnection_p.h @@ -129,6 +129,11 @@ public: QByteArray matchRule; }; + enum TreeNodeType { + Object = 0x0, + VirtualObject = 0x01000000 + }; + struct ObjectTreeNode { typedef QVector<ObjectTreeNode> DataList; @@ -143,8 +148,12 @@ public: { return QStringRef(&name) < other; } QString name; - QObject* obj; + union { + QObject *obj; + QDBusVirtualObject *treeNode; + }; int flags; + DataList children; }; @@ -333,7 +342,7 @@ extern bool qDBusInterfaceInObject(QObject *obj, const QString &interface_name); extern QString qDBusInterfaceFromMetaObject(const QMetaObject *mo); // in qdbusinternalfilters.cpp -extern QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node); +extern QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node, const QString &path); extern QDBusMessage qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode &node, const QDBusMessage &msg); extern QDBusMessage qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode &node, diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index f3f3d0b..2cde07e 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -57,6 +57,7 @@ #include "qdbusabstractadaptor.h" #include "qdbusabstractadaptor_p.h" #include "qdbusutil_p.h" +#include "qdbusvirtualobject.h" #include "qdbusmessage_p.h" #include "qdbuscontext_p.h" #include "qdbuspendingcall_p.h" @@ -442,7 +443,11 @@ static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, // walk the object tree const QDBusConnectionPrivate::ObjectTreeNode *node = root; - while (start < length && node && !(node->flags & QDBusConnection::ExportChildObjects)) { + while (start < length && node) { + if (node->flags & QDBusConnection::ExportChildObjects) + break; + if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) + break; int end = fullpath.indexOf(QLatin1Char('/'), start); end = (end == -1 ? length : end); QStringRef pathComponent(&fullpath, start, end - start); @@ -1328,7 +1333,7 @@ bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node, if (interface.isEmpty() || interface == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) { if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) { //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg; - QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node)); + QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node, msg.path())); send(reply); return true; } @@ -1375,6 +1380,15 @@ void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMes // object may be null + if (node.flags & QDBusConnectionPrivate::VirtualObject) { + if (node.treeNode->handleMessage(msg, q(this))) { + return; + } else { + if (activateInternalFilters(node, msg)) + return; + } + } + if (pathStartPos != msg.path().length()) { node.flags &= ~QDBusConnection::ExportAllSignals; node.obj = findChildObject(&node, msg.path(), pathStartPos); diff --git a/src/dbus/qdbusinternalfilters.cpp b/src/dbus/qdbusinternalfilters.cpp index 2b142a7..b874a64 100644 --- a/src/dbus/qdbusinternalfilters.cpp +++ b/src/dbus/qdbusinternalfilters.cpp @@ -56,6 +56,7 @@ #include "qdbusmetatype_p.h" #include "qdbusmessage_p.h" #include "qdbusutil_p.h" +#include "qdbusvirtualobject.h" #ifndef QT_NO_DBUS @@ -108,7 +109,7 @@ static QString generateSubObjectXml(QObject *object) // declared as extern in qdbusconnection_p.h -QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node) +QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node, const QString &path) { // object may be null @@ -155,6 +156,11 @@ QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node } } + // is it a virtual node that handles introspection itself? + if (node.flags & QDBusConnectionPrivate::VirtualObject) { + xml_data += node.treeNode->introspect(path); + } + xml_data += QLatin1String( propertiesInterfaceXml ); } diff --git a/src/dbus/qdbusvirtualobject.cpp b/src/dbus/qdbusvirtualobject.cpp new file mode 100644 index 0000000..f1c17b5 --- /dev/null +++ b/src/dbus/qdbusvirtualobject.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusvirtualobject.h" + +#ifndef QT_NO_DBUS + +QT_BEGIN_NAMESPACE + +QDBusVirtualObject::QDBusVirtualObject(QObject *parent) : + QObject(parent) +{ +} + +QDBusVirtualObject::~QDBusVirtualObject() +{ +} + +QT_END_NAMESPACE + + +/*! + \internal + \class QDBusVirtualObject + \inmodule QtDBus + \since 4.8 + + \brief The QDBusVirtualObject class is used to handle several DBus paths with one class. +*/ + +/*! + \internal + \fn bool QDBusVirtualObject::handleMessage(const QDBusMessage &message, const QDBusConnection &connection) = 0 + + This function needs to handle all messages to the path of the + virtual object, when the SubPath option is specified. + The service, path, interface and methos are all part of the message. + Must return true when the message is handled, otherwise false (will generate dbus error message). +*/ + + +/*! + \internal + \fn QString QDBusVirtualObject::introspect(const QString &path) const + + This function needs to handle the introspection of the + virtual object. It must return xml of the form: + + \code +<interface name="com.trolltech.QtDBus.MyObject" > + <property access="readwrite" type="i" name="prop1" /> +</interface> + \endcode + + If you pass the SubPath option, this introspection has to include all child nodes. + Otherwise QDBus handles the introspection of the child nodes. +*/ + +#endif // QT_NO_DBUS diff --git a/src/dbus/qdbusvirtualobject.h b/src/dbus/qdbusvirtualobject.h new file mode 100644 index 0000000..e51dca5 --- /dev/null +++ b/src/dbus/qdbusvirtualobject.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSTREENODE_H +#define QDBUSTREENODE_H + +#include <QtDBus/qdbusmacros.h> +#include <QtCore/qstring.h> +#include <QtCore/qobject.h> + +#ifndef QT_NO_DBUS + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusMessage; +class QDBusConnection; + +class QDBusVirtualObjectPrivate; +class Q_DBUS_EXPORT QDBusVirtualObject : public QObject +{ + Q_OBJECT +public: + explicit QDBusVirtualObject(QObject *parent = 0); + virtual ~QDBusVirtualObject(); + + virtual QString introspect(const QString &path) const = 0; + virtual bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) = 0; + +private: + Q_DECLARE_PRIVATE(QDBusVirtualObject) + Q_DISABLE_COPY(QDBusVirtualObject) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_DBUS +#endif diff --git a/tests/auto/qdbusconnection/tst_qdbusconnection.cpp b/tests/auto/qdbusconnection/tst_qdbusconnection.cpp index e06e3a8..6490bfe 100644 --- a/tests/auto/qdbusconnection/tst_qdbusconnection.cpp +++ b/tests/auto/qdbusconnection/tst_qdbusconnection.cpp @@ -113,6 +113,10 @@ private slots: void serviceRegistrationRaceCondition(); + void registerVirtualObject(); + void callVirtualObject(); + void callVirtualObjectLocal(); + public: QString serviceName() const { return "com.trolltech.Qt.Autotests.QDBusConnection"; } bool callMethod(const QDBusConnection &conn, const QString &path); @@ -823,7 +827,6 @@ bool tst_QDBusConnection::callMethod(const QDBusConnection &conn, const QString { QDBusMessage msg = QDBusMessage::createMethodCall(conn.baseService(), path, "", "method"); QDBusMessage reply = conn.call(msg, QDBus::Block/*WithGui*/); - if (reply.type() != QDBusMessage::ReplyMessage) return false; if (MyObject::path == path) { @@ -1098,6 +1101,203 @@ void tst_QDBusConnection::serviceRegistrationRaceCondition() QCOMPARE(recv.count, 1); } +class VirtualObject: public QDBusVirtualObject +{ + Q_OBJECT +public: + VirtualObject() :success(true) {} + + QString introspect(const QString &path) const + { + return QString(); + } + + bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) { + ++callCount; + lastMessage = message; + + if (success) { + QDBusMessage reply = message.createReply(replyArguments); + connection.send(reply); + } + emit messageReceived(message); + return success; + } +signals: + void messageReceived(const QDBusMessage &message) const; + +public: + mutable QDBusMessage lastMessage; + QVariantList replyArguments; + mutable int callCount; + bool success; +}; + + +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<QObject *>(&obj)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&obj)); + QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(&obj)); + } + QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(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<QObject *>(&obj)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&obj)); + QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(&obj)); + con.unregisterObject(path); + QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0)); + } + + { + // Single node has no sub path handling. + VirtualObject obj; + QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SingleNode)); + QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(&obj)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(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<QObject *>(&obj)); + + QObject objectAtSubPath; + QVERIFY(!con.registerObject(path, &objectAtSubPath)); + QVERIFY(!con.registerObject(childPath, &objectAtSubPath)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(&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<QObject *>(&objectAtSubPath)); + + VirtualObject obj; + QVERIFY(!con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); + QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0)); + } + QCOMPARE(con.objectRegisteredAt(path), static_cast<QObject *>(0)); + QCOMPARE(con.objectRegisteredAt(childPath), static_cast<QObject *>(0)); + QCOMPARE(con.objectRegisteredAt(childChildPath), static_cast<QObject *>(0)); +} + +void tst_QDBusConnection::callVirtualObject() +{ + 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()); + + 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()); + + 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()); + QTest::qWait(100); + QVERIFY(errorReply.isError()); + qDebug() << errorReply.reply().arguments(); + 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()); + + 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()); +} + QString MyObject::path; QTEST_MAIN(tst_QDBusConnection) diff --git a/tests/auto/qdbusinterface/tst_qdbusinterface.cpp b/tests/auto/qdbusinterface/tst_qdbusinterface.cpp index 96ab311..9156818 100644 --- a/tests/auto/qdbusinterface/tst_qdbusinterface.cpp +++ b/tests/auto/qdbusinterface/tst_qdbusinterface.cpp @@ -200,6 +200,7 @@ private slots: void invalidAfterServiceOwnerChanged(); void introspect(); void introspectUnknownTypes(); + void introspectVirtualObject(); void callMethod(); void invokeMethod(); void invokeMethodWithReturn(); @@ -361,7 +362,6 @@ void tst_QDBusInterface::invalidAfterServiceOwnerChanged() void tst_QDBusInterface::introspect() { - QDBusConnection con = QDBusConnection::sessionBus(); QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), TEST_INTERFACE_NAME); @@ -394,6 +394,75 @@ void tst_QDBusInterface::introspectUnknownTypes() QVERIFY(mo->indexOfProperty("prop1") != -1); int pidx = mo->indexOfProperty("prop1"); QCOMPARE(mo->property(pidx).typeName(), "QDBusRawType<0x7e>*"); + + + + QDBusMessage message = QDBusMessage::createMethodCall(con.baseService(), "/unknownTypes", "org.freedesktop.DBus.Introspectable", "Introspect"); + QDBusMessage reply = con.call(message, QDBus::Block, 5000); + qDebug() << "REPL: " << reply.arguments(); + +} + + +class VirtualObject: public QDBusVirtualObject +{ + Q_OBJECT +public: + VirtualObject() :success(true) {} + + QString introspect(const QString &path) const { + if (path == "/some/path/superNode") + return "zitroneneis"; + if (path == "/some/path/superNode/foo") + return " <interface name=\"com.trolltech.QtDBus.VirtualObject\">\n" + " <method name=\"klingeling\" />\n" + " </interface>\n" ; + return QString(); + } + + bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) { + ++callCount; + lastMessage = message; + + if (success) { + QDBusMessage reply = message.createReply(replyArguments); + connection.send(reply); + } + emit messageReceived(message); + return success; + } +signals: + void messageReceived(const QDBusMessage &message) const; + +public: + mutable QDBusMessage lastMessage; + QVariantList replyArguments; + mutable int callCount; + bool success; +}; + +void tst_QDBusInterface::introspectVirtualObject() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + VirtualObject obj; + + obj.success = false; + + QString path = "/some/path/superNode"; + QVERIFY(con.registerVirtualObject(path, &obj, QDBusConnection::SubPath)); + + QDBusMessage message = QDBusMessage::createMethodCall(con.baseService(), path, "org.freedesktop.DBus.Introspectable", "Introspect"); + QDBusMessage reply = con.call(message, QDBus::Block, 5000); + QVERIFY(reply.arguments().at(0).toString().contains( + QRegExp("<node>.*zitroneneis.*<interface name=") )); + + QDBusMessage message2 = QDBusMessage::createMethodCall(con.baseService(), path + "/foo", "org.freedesktop.DBus.Introspectable", "Introspect"); + QDBusMessage reply2 = con.call(message2, QDBus::Block, 5000); + QVERIFY(reply2.arguments().at(0).toString().contains( + QRegExp("<node>.*<interface name=\"com.trolltech.QtDBus.VirtualObject\">" + ".*<method name=\"klingeling\" />\n" + ".*</interface>.*<interface name=") )); } void tst_QDBusInterface::callMethod() |