diff options
Diffstat (limited to 'tests/auto/dbus')
69 files changed, 14094 insertions, 0 deletions
diff --git a/tests/auto/dbus/dbus.pro b/tests/auto/dbus/dbus.pro new file mode 100644 index 0000000000..6dd8ce325c --- /dev/null +++ b/tests/auto/dbus/dbus.pro @@ -0,0 +1,20 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qdbusabstractadaptor \ + qdbusabstractinterface \ + qdbusconnection \ + qdbusconnection_no_bus \ + qdbuscontext \ + qdbusinterface \ + qdbuslocalcalls \ + qdbusmarshall \ + qdbusmetaobject \ + qdbusmetatype \ + qdbuspendingcall \ + qdbuspendingreply \ + qdbusreply \ + qdbusservicewatcher \ + qdbustype \ + qdbusthreading \ + qdbusxmlparser \ + diff --git a/tests/auto/dbus/qdbusabstractadaptor/.gitignore b/tests/auto/dbus/qdbusabstractadaptor/.gitignore new file mode 100644 index 0000000000..201dd9cda0 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/.gitignore @@ -0,0 +1 @@ +tst_qdbusabstractadaptor diff --git a/tests/auto/dbus/qdbusabstractadaptor/myobject.h b/tests/auto/dbus/qdbusabstractadaptor/myobject.h new file mode 100644 index 0000000000..2aaaa7ecc5 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/myobject.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MYOBJECT_H +#define MYOBJECT_H + +#include <QtCore/QObject> +#include <QtDBus/QtDBus> + +extern const char *slotSpy; +extern QString valueSpy; + +class QDBusSignalSpy: public QObject +{ + Q_OBJECT + +public slots: + void slot(const QDBusMessage &msg) + { + ++count; + interface = msg.interface(); + name = msg.member(); + signature = msg.signature(); + path = msg.path(); + value.clear(); + if (msg.arguments().count()) + value = msg.arguments().at(0); + } + +public: + QDBusSignalSpy() : count(0) { } + + int count; + QString interface; + QString name; + QString signature; + QString path; + QVariant value; +}; + +class Interface1: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.Interface1") +public: + Interface1(QObject *parent) : QDBusAbstractAdaptor(parent) + { } +}; + +class Interface2: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.Interface2") + Q_PROPERTY(QString prop1 READ prop1) + Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2 SCRIPTABLE true) + Q_PROPERTY(QUrl nonDBusProperty READ nonDBusProperty) +public: + Interface2(QObject *parent) : QDBusAbstractAdaptor(parent) + { setAutoRelaySignals(true); } + + QString prop1() const + { return QLatin1String("QString Interface2::prop1() const"); } + + QString prop2() const + { return QLatin1String("QString Interface2::prop2() const"); } + + void setProp2(const QString &value) + { + slotSpy = "void Interface2::setProp2(const QString &)"; + valueSpy = value; + } + + QUrl nonDBusProperty() const + { return QUrl(); } + + void emitSignal(const QString &, const QVariant &) + { emit signal(); } + +public slots: + void method() + { + slotSpy = "void Interface2::method()"; + } + + Q_SCRIPTABLE void scriptableMethod() + { + slotSpy = "void Interface2::scriptableMethod()"; + } + +signals: + void signal(); +}; + +class Interface3: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.Interface3") + Q_PROPERTY(QString prop1 READ prop1) + Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2) + Q_PROPERTY(QString interface3prop READ interface3prop) +public: + Interface3(QObject *parent) : QDBusAbstractAdaptor(parent) + { setAutoRelaySignals(true); } + + QString prop1() const + { return QLatin1String("QString Interface3::prop1() const"); } + + QString prop2() const + { return QLatin1String("QString Interface3::prop2() const"); } + + void setProp2(const QString &value) + { + slotSpy = "void Interface3::setProp2(const QString &)"; + valueSpy = value; + } + + QString interface3prop() const + { return QLatin1String("QString Interface3::interface3prop() const"); } + + void emitSignal(const QString &name, const QVariant &value) + { + if (name == "signalVoid") + emit signalVoid(); + else if (name == "signalInt") + emit signalInt(value.toInt()); + else if (name == "signalString") + emit signalString(value.toString()); + } + +public slots: + void methodVoid() { slotSpy = "void Interface3::methodVoid()"; } + void methodInt(int) { slotSpy = "void Interface3::methodInt(int)"; } + void methodString(QString) { slotSpy = "void Interface3::methodString(QString)"; } + + int methodStringString(const QString &s, QString &out) + { + slotSpy = "int Interface3::methodStringString(const QString &, QString &)"; + out = s; + return 42; + } + +signals: + void signalVoid(); + void signalInt(int); + void signalString(const QString &); +}; + +class Interface4: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.Interface4") + Q_PROPERTY(QString prop1 READ prop1) + Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2) + Q_PROPERTY(QString interface4prop READ interface4prop) +public: + Interface4(QObject *parent) : QDBusAbstractAdaptor(parent) + { setAutoRelaySignals(true); } + + QString prop1() const + { return QLatin1String("QString Interface4::prop1() const"); } + + QString prop2() const + { return QLatin1String("QString Interface4::prop2() const"); } + + QString interface4prop() const + { return QLatin1String("QString Interface4::interface4prop() const"); } + + void setProp2(const QString &value) + { + slotSpy = "void Interface4::setProp2(const QString &)"; + valueSpy = value; + } + + void emitSignal(const QString &, const QVariant &value) + { + switch (value.type()) + { + case QVariant::Invalid: + emit signal(); + break; + case QVariant::Int: + emit signal(value.toInt()); + break; + case QVariant::String: + emit signal(value.toString()); + break; + default: + break; + } + } + +public slots: + void method() { slotSpy = "void Interface4::method()"; } + void method(int) { slotSpy = "void Interface4::method(int)"; } + void method(QString) { slotSpy = "void Interface4::method(QString)"; } + +signals: + void signal(); + void signal(int); + void signal(const QString &); +}; + +class MyObject: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.MyObject") +public: + Interface1 *if1; + Interface2 *if2; + Interface3 *if3; + Interface4 *if4; + + MyObject(int n = 4) + : if1(0), if2(0), if3(0), if4(0) + { + switch (n) + { + case 4: + if4 = new Interface4(this); + case 3: + if3 = new Interface3(this); + case 2: + if2 = new Interface2(this); + case 1: + if1 = new Interface1(this); + } + } + + void emitSignal(const QString &name, const QVariant &value) + { + if (name == "scriptableSignalVoid") + emit scriptableSignalVoid(); + else if (name == "scriptableSignalInt") + emit scriptableSignalInt(value.toInt()); + else if (name == "scriptableSignalString") + emit scriptableSignalString(value.toString()); + else if (name == "nonScriptableSignalVoid") + emit nonScriptableSignalVoid(); + } + +signals: + Q_SCRIPTABLE void scriptableSignalVoid(); + Q_SCRIPTABLE void scriptableSignalInt(int); + Q_SCRIPTABLE void scriptableSignalString(QString); + void nonScriptableSignalVoid(); +}; + +#endif // MYOBJECT_H
\ No newline at end of file diff --git a/tests/auto/dbus/qdbusabstractadaptor/qdbusabstractadaptor.pro b/tests/auto/dbus/qdbusabstractadaptor/qdbusabstractadaptor.pro new file mode 100644 index 0000000000..480509853f --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/qdbusabstractadaptor.pro @@ -0,0 +1,9 @@ +load(qttest_p4) +QT = core core-private +contains(QT_CONFIG,dbus): { + TEMPLATE = subdirs + CONFIG += ordered + SUBDIRS = qmyserver test +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} diff --git a/tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver.cpp b/tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver.cpp new file mode 100644 index 0000000000..a06232c4bd --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QtCore> +#include <QtDBus/QtDBus> + +#include "../myobject.h" + +static const char serviceName[] = "com.trolltech.autotests.qmyserver"; +static const char objectPath[] = "/com/trolltech/qmyserver"; +//static const char *interfaceName = serviceName; + +const char *slotSpy; +QString valueSpy; + +Q_DECLARE_METATYPE(QDBusConnection::RegisterOptions) + +class MyServer : public QDBusServer +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.autotests.qmyserver") + +public: + MyServer(QString addr = "unix:tmpdir=/tmp", QObject* parent = 0) + : QDBusServer(addr, parent), + m_conn("none"), + obj(NULL) + { + connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&))); + } + + ~MyServer() + { + if (obj) + obj->deleteLater(); + } + +public slots: + QString address() const + { + return QDBusServer::address(); + } + + bool isConnected() const + { + return m_conn.isConnected(); + } + + void emitSignal(const QString& interface, const QString& name, const QDBusVariant& parameter) + { + if (interface.endsWith('2')) + obj->if2->emitSignal(name, parameter.variant()); + else if (interface.endsWith('3')) + obj->if3->emitSignal(name, parameter.variant()); + else if (interface.endsWith('4')) + obj->if4->emitSignal(name, parameter.variant()); + else + obj->emitSignal(name, parameter.variant()); + } + + void emitSignal2(const QString& interface, const QString& name) + { + if (interface.endsWith('2')) + obj->if2->emitSignal(name, QVariant()); + else if (interface.endsWith('3')) + obj->if3->emitSignal(name, QVariant()); + else if (interface.endsWith('4')) + obj->if4->emitSignal(name, QVariant()); + else + obj->emitSignal(name, QVariant()); + } + + void newMyObject(int nInterfaces = 4) + { + if (obj) + obj->deleteLater(); + obj = new MyObject(nInterfaces); + } + + void registerMyObject(const QString & path, int options) + { + m_conn.registerObject(path, obj, (QDBusConnection::RegisterOptions)options); + } + + QString slotSpyServer() + { + return QLatin1String(slotSpy); + } + + QString valueSpyServer() + { + return valueSpy; + } + + void clearValueSpy() + { + valueSpy.clear(); + } + +private slots: + void handleConnection(const QDBusConnection& con) + { + m_conn = con; + } + +private: + QDBusConnection m_conn; + MyObject* obj; +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QDBusConnection con = QDBusConnection::sessionBus(); + if (!con.isConnected()) + exit(1); + + if (!con.registerService(serviceName)) + exit(2); + + MyServer server; + con.registerObject(objectPath, &server, QDBusConnection::ExportAllSlots); + + printf("ready.\n"); + + return app.exec(); +} + +#include "qmyserver.moc"
\ No newline at end of file diff --git a/tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver.pro b/tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver.pro new file mode 100644 index 0000000000..f4fe02c4eb --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/qmyserver/qmyserver.pro @@ -0,0 +1,5 @@ +SOURCES = qmyserver.cpp +HEADERS = ../myobject.h +TARGET = qmyserver +QT += dbus +QT -= gui diff --git a/tests/auto/dbus/qdbusabstractadaptor/test/test.pro b/tests/auto/dbus/qdbusabstractadaptor/test/test.pro new file mode 100644 index 0000000000..52aa578fd4 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/test/test.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES += ../tst_qdbusabstractadaptor.cpp +HEADERS += ../myobject.h +TARGET = ../tst_qdbusabstractadaptor + +QT = core core-private dbus diff --git a/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp b/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp new file mode 100644 index 0000000000..ff684ff522 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp @@ -0,0 +1,1894 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qdebug.h> + +#include <QtTest/QtTest> + +#include <QtDBus> + +#include "../qdbusmarshall/common.h" +#include "myobject.h" + +static const char serviceName[] = "com.trolltech.autotests.qmyserver"; +static const char objectPath[] = "/com/trolltech/qmyserver"; +static const char *interfaceName = serviceName; + +const char *slotSpy; +QString valueSpy; + +QT_BEGIN_NAMESPACE +namespace QTest { + char *toString(QDBusMessage::MessageType t) + { + switch (t) + { + case QDBusMessage::InvalidMessage: + return qstrdup("InvalidMessage"); + case QDBusMessage::MethodCallMessage: + return qstrdup("MethodCallMessage"); + case QDBusMessage::ReplyMessage: + return qstrdup("ReplyMessage"); + case QDBusMessage::ErrorMessage: + return qstrdup("ErrorMessage"); + case QDBusMessage::SignalMessage: + return qstrdup("SignalMessage"); + default: + return 0; + } + } +} +QT_END_NAMESPACE + +class TypesInterface: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.TypesInterface") +public: + TypesInterface(QObject *parent) + : QDBusAbstractAdaptor(parent) + { } + + union + { + bool b; + uchar uc; + short s; + ushort us; + int i; + uint ui; + qlonglong ll; + qulonglong ull; + double d; + } dataSpy; + QVariant variantSpy; + QString stringSpy; + QVariantList listSpy; + QStringList stringlistSpy; + QByteArray bytearraySpy; + QVariantMap mapSpy; + StringStringMap ssmapSpy; + LLDateTimeMap lldtmapSpy; + MyStruct structSpy; + +public slots: + void methodBool(bool b) + { + slotSpy = "void TypesInterface::methodBool(bool)"; + dataSpy.b = b; + } + + void methodUChar(uchar uc) + { + slotSpy = "void TypesInterface::methodUChar(uchar)"; + dataSpy.uc = uc; + } + + void methodShort(short s) + { + slotSpy = "void TypesInterface::methodShort(short)"; + dataSpy.s = s; + } + + void methodUShort(ushort us) + { + slotSpy = "void TypesInterface::methodUShort(ushort)"; + dataSpy.us = us; + } + + void methodInt(int i) + { + slotSpy = "void TypesInterface::methodInt(int)"; + dataSpy.i = i; + } + + void methodUInt(uint ui) + { + slotSpy = "void TypesInterface::methodUInt(uint)"; + dataSpy.ui = ui; + } + + void methodLongLong(qlonglong ll) + { + slotSpy = "void TypesInterface::methodLongLong(qlonglong)"; + dataSpy.ll = ll; + } + + void methodULongLong(qulonglong ull) + { + slotSpy = "void TypesInterface::methodULongLong(qulonglong)"; + dataSpy.ull = ull; + } + + void methodDouble(double d) + { + slotSpy = "void TypesInterface::methodDouble(double)"; + dataSpy.d = d; + } + + void methodString(const QString &s) + { + slotSpy = "void TypesInterface::methodString(const QString &)"; + stringSpy = s; + } + + void methodObjectPath(const QDBusObjectPath &op) + { + slotSpy = "void TypesInterface::methodObjectPath(const QDBusObjectPath &)"; + stringSpy = op.path(); + } + + void methodSignature(const QDBusSignature &s) + { + slotSpy = "void TypesInterface::methodSignature(const QDBusSignature &)"; + stringSpy = s.signature(); + } + + void methodVariant(const QDBusVariant &v) + { + slotSpy = "void TypesInterface::methodVariant(const QDBusVariant &)"; + variantSpy = v.variant(); + } + + void methodList(const QVariantList &l) + { + slotSpy = "void TypesInterface::methodList(const QVariantList &)"; + listSpy = l; + } + + void methodStringList(const QStringList &sl) + { + slotSpy = "void TypesInterface::methodStringList(const QStringList &)"; + stringlistSpy = sl; + } + + void methodByteArray(const QByteArray &ba) + { + slotSpy = "void TypesInterface::methodByteArray(const QByteArray &)"; + bytearraySpy = ba; + } + + void methodMap(const QVariantMap &m) + { + slotSpy = "void TypesInterface::methodMap(const QVariantMap &)"; + mapSpy = m; + } + + void methodSSMap(const StringStringMap &ssmap) + { + slotSpy = "void TypesInterface::methodSSMap(const StringStringMap &)"; + ssmapSpy = ssmap; + } + + void methodLLDateTimeMap(const LLDateTimeMap &lldtmap) + { + slotSpy = "void TypesInterface::methodLLDateTimeMap(const LLDateTimeMap &)"; + lldtmapSpy = lldtmap; + } + + void methodStruct(const MyStruct &s) + { + slotSpy = "void TypesInterface::methodStruct(const MyStruct &)"; + structSpy = s; + } + + bool retrieveBool() + { + return dataSpy.b; + } + + uchar retrieveUChar() + { + return dataSpy.uc; + } + + short retrieveShort() + { + return dataSpy.s; + } + + ushort retrieveUShort() + { + return dataSpy.us; + } + + int retrieveInt() + { + return dataSpy.i; + } + + uint retrieveUInt() + { + return dataSpy.ui; + } + + qlonglong retrieveLongLong() + { + return dataSpy.ll; + } + + qulonglong retrieveULongLong() + { + return dataSpy.ull; + } + + double retrieveDouble() + { + return dataSpy.d; + } + + QString retrieveString() + { + return stringSpy; + } + + QDBusObjectPath retrieveObjectPath() + { + return QDBusObjectPath(stringSpy); + } + + QDBusSignature retrieveSignature() + { + return QDBusSignature(stringSpy); + } + + QDBusVariant retrieveVariant() + { + return QDBusVariant(variantSpy); + } + + QVariantList retrieveList() + { + return listSpy; + } + + QStringList retrieveStringList() + { + return stringlistSpy; + } + + QByteArray retrieveByteArray() + { + return bytearraySpy; + } + + QVariantMap retrieveMap() + { + return mapSpy; + } + + StringStringMap retrieveSSMap() + { + return ssmapSpy; + } + + LLDateTimeMap retrieveLLDateTimeMap() + { + return lldtmapSpy; + } + + MyStruct retrieveStruct() + { + return structSpy; + } +}; + +void newMyObjectPeer(int nInterfaces = 4) +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "newMyObject"); + req << nInterfaces; + QDBusMessage reply = QDBusConnection::sessionBus().call(req); +} + +void registerMyObjectPeer(const QString & path, QDBusConnection::RegisterOptions options = QDBusConnection::ExportAdaptors) +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "registerMyObject"); + req << path; + req << (int)options; + QDBusMessage reply = QDBusConnection::sessionBus().call(req); +} + +void emitSignalPeer(const QString &interface, const QString &name, const QVariant ¶meter) +{ + if (parameter.isValid()) + { + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "emitSignal"); + req << interface; + req << name; + req << QVariant::fromValue(QDBusVariant(parameter)); + QDBusConnection::sessionBus().send(req); + } + else + { + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "emitSignal2"); + req << interface; + req << name; + QDBusConnection::sessionBus().send(req); + } + + QTest::qWait(1000); +} + +const char* slotSpyPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "slotSpyServer"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); + return reply.arguments().at(0).toString().toLatin1().data(); +} + +QString valueSpyPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "valueSpyServer"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); + return reply.arguments().at(0).toString(); +} + +void clearValueSpyPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "clearValueSpy"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); +} + +class tst_QDBusAbstractAdaptor: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void methodCalls_data(); + void methodCalls(); + void methodCallScriptable(); + void signalEmissions_data(); + void signalEmissions(); + void sameSignalDifferentPaths(); + void sameObjectDifferentPaths(); + void scriptableSignalOrNot(); + void overloadedSignalEmission_data(); + void overloadedSignalEmission(); + void readProperties(); + void readPropertiesInvalidInterface(); + void readPropertiesEmptyInterface_data(); + void readPropertiesEmptyInterface(); + void readAllProperties(); + void readAllPropertiesInvalidInterface(); + void readAllPropertiesEmptyInterface_data(); + void readAllPropertiesEmptyInterface(); + void writeProperties(); + + void methodCallsPeer_data(); + void methodCallsPeer(); + void methodCallScriptablePeer(); + void signalEmissionsPeer_data(); + void signalEmissionsPeer(); + void sameSignalDifferentPathsPeer(); + void sameObjectDifferentPathsPeer(); + void scriptableSignalOrNotPeer(); + void overloadedSignalEmissionPeer_data(); + void overloadedSignalEmissionPeer(); + void readPropertiesPeer(); + void readPropertiesInvalidInterfacePeer(); + void readPropertiesEmptyInterfacePeer_data(); + void readPropertiesEmptyInterfacePeer(); + void readAllPropertiesPeer(); + void readAllPropertiesInvalidInterfacePeer(); + void readAllPropertiesEmptyInterfacePeer_data(); + void readAllPropertiesEmptyInterfacePeer(); + void writePropertiesPeer(); + + void typeMatching_data(); + void typeMatching(); + + void methodWithMoreThanOneReturnValue(); + void methodWithMoreThanOneReturnValuePeer(); +private: + QProcess proc; +}; + +class WaitForQMyServer: public QObject +{ + Q_OBJECT +public: + WaitForQMyServer(); + bool ok(); +public Q_SLOTS: + void ownerChange(const QString &name) + { + if (name == serviceName) + loop.quit(); + } + +private: + QEventLoop loop; +}; + +WaitForQMyServer::WaitForQMyServer() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + if (!ok()) { + connect(con.interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), + SLOT(ownerChange(QString))); + QTimer::singleShot(2000, &loop, SLOT(quit())); + loop.exec(); + } +} + +bool WaitForQMyServer::ok() +{ + return QDBusConnection::sessionBus().isConnected() && + QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName); +} + +void tst_QDBusAbstractAdaptor::initTestCase() +{ + commonInit(); + + // start peer server + #ifdef Q_OS_WIN + proc.start("qmyserver"); + #else + proc.start("./qmyserver/qmyserver"); + #endif + QVERIFY(proc.waitForStarted()); + + WaitForQMyServer w; + QVERIFY(w.ok()); + //QTest::qWait(2000); + + // get peer server address + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "address"); + QDBusMessage rpl = QDBusConnection::sessionBus().call(req); + QVERIFY(rpl.type() == QDBusMessage::ReplyMessage); + QString address = rpl.arguments().at(0).toString(); + + // connect to peer server + QDBusConnection peercon = QDBusConnection::connectToPeer(address, "peer"); + QVERIFY(peercon.isConnected()); + + QDBusMessage req2 = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "isConnected"); + QDBusMessage rpl2 = QDBusConnection::sessionBus().call(req2); + QVERIFY(rpl2.type() == QDBusMessage::ReplyMessage); + QVERIFY(rpl2.arguments().at(0).toBool()); +} + +void tst_QDBusAbstractAdaptor::cleanupTestCase() +{ + proc.close(); + proc.kill(); +} + +void tst_QDBusAbstractAdaptor::methodCalls_data() +{ + QTest::addColumn<int>("nInterfaces"); + QTest::newRow("0") << 0; + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("3") << 3; + QTest::newRow("4") << 4; +} + +void tst_QDBusAbstractAdaptor::methodCalls() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + //QDBusInterface emptycon.baseService(), "/", QString()); + + { + // must fail: no object + QDBusInterface if1(con.baseService(), "/", "local.Interface1", con); + QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage); + } + + QFETCH(int, nInterfaces); + MyObject obj(nInterfaces); + con.registerObject("/", &obj); + + QDBusInterface if1(con.baseService(), "/", "local.Interface1", con); + QDBusInterface if2(con.baseService(), "/", "local.Interface2", con); + QDBusInterface if3(con.baseService(), "/", "local.Interface3", con); + QDBusInterface if4(con.baseService(), "/", "local.Interface4", con); + + // must fail: no such method + QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage); + if (!nInterfaces--) + return; + if (!nInterfaces--) + return; + + // simple call: one such method exists + QCOMPARE(if2.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface2::method()"); + if (!nInterfaces--) + return; + + // multiple methods in multiple interfaces, no name overlap + QCOMPARE(if1.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if1.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if1.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if2.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if2.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if2.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage); + + QCOMPARE(if3.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface3::methodVoid()"); + QCOMPARE(if3.call(QDBus::BlockWithGui, "methodInt", 42).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface3::methodInt(int)"); + QCOMPARE(if3.call(QDBus::BlockWithGui, "methodString", QString("")).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface3::methodString(QString)"); + + if (!nInterfaces--) + return; + + // method overloading: different interfaces + QCOMPARE(if4.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface4::method()"); + + // method overloading: different parameters + QCOMPARE(if4.call(QDBus::BlockWithGui, "method.i", 42).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface4::method(int)"); + QCOMPARE(if4.call(QDBus::BlockWithGui, "method.s", QString()).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface4::method(QString)"); + +} + +void tst_QDBusAbstractAdaptor::methodCallScriptable() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj(2); + con.registerObject("/", &obj); + + QDBusInterface if2(con.baseService(), "/", "local.Interface2", con); + + QCOMPARE(if2.call(QDBus::BlockWithGui,"scriptableMethod").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpy, "void Interface2::scriptableMethod()"); +} + +static void emitSignal(MyObject *obj, const QString &iface, const QString &name, + const QVariant ¶meter) +{ + if (iface.endsWith('2')) + obj->if2->emitSignal(name, parameter); + else if (iface.endsWith('3')) + obj->if3->emitSignal(name, parameter); + else if (iface.endsWith('4')) + obj->if4->emitSignal(name, parameter); + else + obj->emitSignal(name, parameter); + + QTest::qWait(200); +} + +void tst_QDBusAbstractAdaptor::signalEmissions_data() +{ + QTest::addColumn<QString>("interface"); + QTest::addColumn<QString>("name"); + QTest::addColumn<QString>("signature"); + QTest::addColumn<QVariant>("parameter"); + + QTest::newRow("Interface2.signal") << "local.Interface2" << "signal" << QString() << QVariant(); + QTest::newRow("Interface3.signalVoid") << "local.Interface3" << "signalVoid" << QString() << QVariant(); + QTest::newRow("Interface3.signalInt") << "local.Interface3" << "signalInt" << "i" << QVariant(1); + QTest::newRow("Interface3.signalString") << "local.Interface3" << "signalString" << "s" << QVariant("foo"); + QTest::newRow("MyObject.scriptableSignalVoid") << "local.MyObject" << "scriptableSignalVoid" << QString() << QVariant(); + QTest::newRow("MyObject.scriptableSignalInt") << "local.MyObject" << "scriptableSignalInt" << "i" << QVariant(1); + QTest::newRow("MyObject.nySignalString") << "local.MyObject" << "scriptableSignalString" << "s" << QVariant("foo"); +} + +void tst_QDBusAbstractAdaptor::signalEmissions() +{ + QFETCH(QString, interface); + QFETCH(QString, name); + QFETCH(QVariant, parameter); + + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + con.registerService("com.trolltech.tst_QDBusAbstractAdaptor"); + + MyObject obj(3); + con.registerObject("/", &obj, QDBusConnection::ExportAdaptors + | QDBusConnection::ExportScriptableSignals); + + // connect all signals and emit only one + { + QDBusSignalSpy spy; + con.connect(con.baseService(), "/", "local.Interface2", "signal", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.Interface3", "signalVoid", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.Interface3", "signalInt", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.Interface3", "signalString", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.MyObject", "scriptableSignalVoid", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.MyObject", "scriptableSignalInt", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.MyObject", "scriptableSignalString", + &spy, SLOT(slot(QDBusMessage))); + + emitSignal(&obj, interface, name, parameter); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } + + // connect one signal and emit them all + { + QDBusSignalSpy spy; + con.connect(con.baseService(), "/", interface, name, &spy, SLOT(slot(QDBusMessage))); + emitSignal(&obj, "local.Interface2", "signal", QVariant()); + emitSignal(&obj, "local.Interface3", "signalVoid", QVariant()); + emitSignal(&obj, "local.Interface3", "signalInt", QVariant(1)); + emitSignal(&obj, "local.Interface3", "signalString", QVariant("foo")); + emitSignal(&obj, "local.MyObject", "scriptableSignalVoid", QVariant()); + emitSignal(&obj, "local.MyObject", "scriptableSignalInt", QVariant(1)); + emitSignal(&obj, "local.MyObject", "scriptableSignalString", QVariant("foo")); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } +} + +void tst_QDBusAbstractAdaptor::sameSignalDifferentPaths() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj(2); + + con.registerObject("/p1",&obj); + con.registerObject("/p2",&obj); + + QDBusSignalSpy spy; + con.connect(con.baseService(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + obj.if2->emitSignal(QString(), QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, QString("local.Interface2")); + QCOMPARE(spy.name, QString("signal")); + QVERIFY(spy.signature.isEmpty()); + + // now connect the other one + spy.count = 0; + con.connect(con.baseService(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + obj.if2->emitSignal(QString(), QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 2); +} + +void tst_QDBusAbstractAdaptor::sameObjectDifferentPaths() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj(2); + + con.registerObject("/p1",&obj); + con.registerObject("/p2",&obj, 0); // don't export anything + + QDBusSignalSpy spy; + con.connect(con.baseService(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + obj.if2->emitSignal(QString(), QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, QString("local.Interface2")); + QCOMPARE(spy.name, QString("signal")); + QVERIFY(spy.signature.isEmpty()); +} + +void tst_QDBusAbstractAdaptor::scriptableSignalOrNot() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + { + MyObject obj(0); + + con.registerObject("/p1",&obj, QDBusConnection::ExportScriptableSignals); + con.registerObject("/p2",&obj, 0); // don't export anything + + QDBusSignalSpy spy; + con.connect(con.baseService(), "/p1", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/p2", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + obj.emitSignal("scriptableSignalVoid", QVariant()); + obj.emitSignal("nonScriptableSignalVoid", QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); // only /p1 must have emitted + QCOMPARE(spy.interface, QString("local.MyObject")); + QCOMPARE(spy.name, QString("scriptableSignalVoid")); + QCOMPARE(spy.path, QString("/p1")); + QVERIFY(spy.signature.isEmpty()); + } + + { + MyObject obj(0); + + con.registerObject("/p1",&obj, QDBusConnection::ExportScriptableSignals); + con.registerObject("/p2",&obj, QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals); + + QDBusSignalSpy spy; + con.connect(con.baseService(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + obj.emitSignal("nonScriptableSignalVoid", QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); // only /p2 must have emitted now + QCOMPARE(spy.interface, QString("local.MyObject")); + QCOMPARE(spy.name, QString("nonScriptableSignalVoid")); + QCOMPARE(spy.path, QString("/p2")); + QVERIFY(spy.signature.isEmpty()); + } + + { + QDBusSignalSpy spy; + con.connect(con.baseService(), "/p1", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/p2", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage))); + + { + MyObject obj(0); + + con.registerObject("/p1",&obj, QDBusConnection::ExportScriptableSignals); + con.registerObject("/p2",&obj, QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals); + } // <--- QObject emits the destroyed(QObject*) signal at this point + + QTest::qWait(200); + + QCOMPARE(spy.count, 0); + } +} + +void tst_QDBusAbstractAdaptor::overloadedSignalEmission_data() +{ + QTest::addColumn<QString>("signature"); + QTest::addColumn<QVariant>("parameter"); + QTest::newRow("void") << QString("") << QVariant(); + QTest::newRow("int") << "i" << QVariant(1); + QTest::newRow("string") << "s" << QVariant("foo"); +} + +void tst_QDBusAbstractAdaptor::overloadedSignalEmission() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QString interface = "local.Interface4"; + QString name = "signal"; + QFETCH(QVariant, parameter); + //QDBusInterface *if4 = new QDBusInterface(con.baseService(), "/", interface, con); + + // connect all signals and emit only one + { + QDBusSignalSpy spy; + con.connect(con.baseService(), "/", "local.Interface4", "signal", "", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.Interface4", "signal", "i", + &spy, SLOT(slot(QDBusMessage))); + con.connect(con.baseService(), "/", "local.Interface4", "signal", "s", + &spy, SLOT(slot(QDBusMessage))); + + emitSignal(&obj, interface, name, parameter); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } + + QFETCH(QString, signature); + // connect one signal and emit them all + { + QDBusSignalSpy spy; + con.connect(con.baseService(), "/", interface, name, signature, &spy, SLOT(slot(QDBusMessage))); + emitSignal(&obj, "local.Interface4", "signal", QVariant()); + emitSignal(&obj, "local.Interface4", "signal", QVariant(1)); + emitSignal(&obj, "local.Interface4", "signal", QVariant("foo")); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } +} + +void tst_QDBusAbstractAdaptor::readProperties() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + for (int i = 2; i <= 4; ++i) { + QString name = QString("Interface%1").arg(i); + + for (int j = 1; j <= 2; ++j) { + QString propname = QString("prop%1").arg(j); + QDBusReply<QVariant> reply = + properties.call(QDBus::BlockWithGui, "Get", "local." + name, propname); + QVariant value = reply; + + QCOMPARE(value.userType(), int(QVariant::String)); + QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname)); + } + } +} + +void tst_QDBusAbstractAdaptor::readPropertiesInvalidInterface() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + + // test an invalid interface: + QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "local.DoesntExist", "prop1"); + QVERIFY(!reply.isValid()); +} + +void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterface_data() +{ + QTest::addColumn<QVariantMap>("expectedProperties"); + QTest::addColumn<bool>("existing"); + + QVariantMap expectedProperties; + expectedProperties["prop1"] = QVariant(); + expectedProperties["prop2"] = QVariant(); + expectedProperties["interface3prop"] = "QString Interface3::interface3prop() const"; + expectedProperties["interface4prop"] = "QString Interface4::interface4prop() const"; + QTest::newRow("existing") << expectedProperties << true; + + expectedProperties.clear(); + expectedProperties["prop5"] = QVariant(); + expectedProperties["foobar"] = QVariant(); + QTest::newRow("non-existing") << expectedProperties << false; +} + +void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterface() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + + QFETCH(QVariantMap, expectedProperties); + QFETCH(bool, existing); + + QVariantMap::ConstIterator it = expectedProperties.constBegin(); + for ( ; it != expectedProperties.constEnd(); ++it) { + QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "", it.key()); + + if (existing) { + QVERIFY2(reply.isValid(), qPrintable(it.key())); + } else { + QVERIFY2(!reply.isValid(), qPrintable(it.key())); + continue; + } + + QCOMPARE(int(reply.value().type()), int(QVariant::String)); + if (it.value().isValid()) + QCOMPARE(reply.value().toString(), it.value().toString()); + } +} + +void tst_QDBusAbstractAdaptor::readAllProperties() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + for (int i = 2; i <= 4; ++i) { + QString name = QString("Interface%1").arg(i); + QDBusReply<QVariantMap> reply = + properties.call(QDBus::BlockWithGui, "GetAll", "local." + name); + + for (int j = 1; j <= 2; ++j) { + QString propname = QString("prop%1").arg(j); + QVERIFY2(reply.value().contains(propname), + qPrintable(propname + " on " + name)); + QVariant value = reply.value().value(propname); + + QCOMPARE(value.userType(), int(QVariant::String)); + QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname)); + } + } +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesInvalidInterface() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + + // test an invalid interface: + QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", "local.DoesntExist"); + QVERIFY(!reply.isValid()); +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterface_data() +{ + readPropertiesEmptyInterface_data(); +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterface() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + + QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", ""); + QVERIFY(reply.isValid()); + + QVariantMap allprops = reply; + + QFETCH(QVariantMap, expectedProperties); + QFETCH(bool, existing); + + QVariantMap::ConstIterator it = expectedProperties.constBegin(); + if (existing) { + for ( ; it != expectedProperties.constEnd(); ++it) { + QVERIFY2(allprops.contains(it.key()), qPrintable(it.key())); + + QVariant propvalue = allprops.value(it.key()); + QVERIFY2(!propvalue.isNull(), qPrintable(it.key())); + QVERIFY2(propvalue.isValid(), qPrintable(it.key())); + + QString stringvalue = propvalue.toString(); + QVERIFY2(!stringvalue.isEmpty(), qPrintable(it.key())); + + if (it.value().isValid()) + QCOMPARE(stringvalue, it.value().toString()); + + // remove this property from the map + allprops.remove(it.key()); + } + + QVERIFY2(allprops.isEmpty(), + qPrintable(QStringList(allprops.keys()).join(" "))); + } else { + for ( ; it != expectedProperties.constEnd(); ++it) + QVERIFY2(!allprops.contains(it.key()), qPrintable(it.key())); + } +} + +void tst_QDBusAbstractAdaptor::writeProperties() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QDBusInterface properties(con.baseService(), "/", "org.freedesktop.DBus.Properties", con); + for (int i = 2; i <= 4; ++i) { + QString name = QString("Interface%1").arg(i); + + valueSpy.clear(); + properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop1"), + qVariantFromValue(QDBusVariant(name))); + QVERIFY(valueSpy.isEmpty()); // call mustn't have succeeded + + properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop2"), + qVariantFromValue(QDBusVariant(name))); + QCOMPARE(valueSpy, name); + QCOMPARE(QString(slotSpy), QString("void %1::setProp2(const QString &)").arg(name)); + } +} + +void tst_QDBusAbstractAdaptor::methodCallsPeer_data() +{ + methodCalls_data(); +} + +void tst_QDBusAbstractAdaptor::methodCallsPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + { + // must fail: no object + QDBusInterface if1(QString(), "/", "local.Interface1", con); + QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage); + } + + QFETCH(int, nInterfaces); + newMyObjectPeer(nInterfaces); + registerMyObjectPeer("/"); + + QDBusInterface if1(QString(), "/", "local.Interface1", con); + QDBusInterface if2(QString(), "/", "local.Interface2", con); + QDBusInterface if3(QString(), "/", "local.Interface3", con); + QDBusInterface if4(QString(), "/", "local.Interface4", con); + + // must fail: no such method + QCOMPARE(if1.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ErrorMessage); + if (!nInterfaces--) + return; + if (!nInterfaces--) + return; + + // simple call: one such method exists + QCOMPARE(if2.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface2::method()"); + if (!nInterfaces--) + return; + + // multiple methods in multiple interfaces, no name overlap + QCOMPARE(if1.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if1.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if1.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if2.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if2.call(QDBus::BlockWithGui, "methodInt").type(), QDBusMessage::ErrorMessage); + QCOMPARE(if2.call(QDBus::BlockWithGui, "methodString").type(), QDBusMessage::ErrorMessage); + + QCOMPARE(if3.call(QDBus::BlockWithGui, "methodVoid").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface3::methodVoid()"); + QCOMPARE(if3.call(QDBus::BlockWithGui, "methodInt", 42).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface3::methodInt(int)"); + QCOMPARE(if3.call(QDBus::BlockWithGui, "methodString", QString("")).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface3::methodString(QString)"); + + if (!nInterfaces--) + return; + + // method overloading: different interfaces + QCOMPARE(if4.call(QDBus::BlockWithGui, "method").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface4::method()"); + + // method overloading: different parameters + QCOMPARE(if4.call(QDBus::BlockWithGui, "method.i", 42).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface4::method(int)"); + QCOMPARE(if4.call(QDBus::BlockWithGui, "method.s", QString()).type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface4::method(QString)"); +} + +void tst_QDBusAbstractAdaptor::methodCallScriptablePeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(2); + registerMyObjectPeer("/"); + + QDBusInterface if2(QString(), "/", "local.Interface2", con); + + QCOMPARE(if2.call(QDBus::BlockWithGui,"scriptableMethod").type(), QDBusMessage::ReplyMessage); + QCOMPARE(slotSpyPeer(), "void Interface2::scriptableMethod()"); +} + +void tst_QDBusAbstractAdaptor::signalEmissionsPeer_data() +{ + signalEmissions_data(); +} + +void tst_QDBusAbstractAdaptor::signalEmissionsPeer() +{ + QFETCH(QString, interface); + QFETCH(QString, name); + QFETCH(QVariant, parameter); + + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(3); + registerMyObjectPeer("/", QDBusConnection::ExportAdaptors + | QDBusConnection::ExportScriptableSignals); + + // connect all signals and emit only one + { + QDBusSignalSpy spy; + con.connect(QString(), "/", "local.Interface2", "signal", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.Interface3", "signalVoid", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.Interface3", "signalInt", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.Interface3", "signalString", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.MyObject", "scriptableSignalVoid", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.MyObject", "scriptableSignalInt", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.MyObject", "scriptableSignalString", + &spy, SLOT(slot(QDBusMessage))); + + emitSignalPeer(interface, name, parameter); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } + + // connect one signal and emit them all + { + QDBusSignalSpy spy; + con.connect(QString(), "/", interface, name, &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.Interface2", "signal", QVariant()); + emitSignalPeer("local.Interface3", "signalVoid", QVariant()); + emitSignalPeer("local.Interface3", "signalInt", QVariant(1)); + emitSignalPeer("local.Interface3", "signalString", QVariant("foo")); + emitSignalPeer("local.MyObject", "scriptableSignalVoid", QVariant()); + emitSignalPeer("local.MyObject", "scriptableSignalInt", QVariant(1)); + emitSignalPeer("local.MyObject", "scriptableSignalString", QVariant("foo")); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } +} + +void tst_QDBusAbstractAdaptor::sameSignalDifferentPathsPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(2); + + registerMyObjectPeer("/p1"); + registerMyObjectPeer("/p2"); + + QDBusSignalSpy spy; + con.connect(QString(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.Interface2", QString(), QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, QString("local.Interface2")); + QCOMPARE(spy.name, QString("signal")); + QVERIFY(spy.signature.isEmpty()); + + // now connect the other one + spy.count = 0; + con.connect(QString(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.Interface2", QString(), QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 2); +} + +void tst_QDBusAbstractAdaptor::sameObjectDifferentPathsPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(2); + + registerMyObjectPeer("/p1"); + registerMyObjectPeer("/p2", 0); // don't export anything + + QDBusSignalSpy spy; + con.connect(QString(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.Interface2", QString(), QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, QString("local.Interface2")); + QCOMPARE(spy.name, QString("signal")); + QVERIFY(spy.signature.isEmpty()); +} + +void tst_QDBusAbstractAdaptor::scriptableSignalOrNotPeer() +{ + QDBusConnection con("peer");; + QVERIFY(con.isConnected()); + + { + newMyObjectPeer(0); + + registerMyObjectPeer("/p1", QDBusConnection::ExportScriptableSignals); + registerMyObjectPeer("/p2", 0); // don't export anything + + QDBusSignalSpy spy; + con.connect(QString(), "/p1", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/p2", "local.MyObject", "scriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.MyObject", "scriptableSignalVoid", QVariant()); + emitSignalPeer("local.MyObject", "nonScriptableSignalVoid", QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); // only /p1 must have emitted + QCOMPARE(spy.interface, QString("local.MyObject")); + QCOMPARE(spy.name, QString("scriptableSignalVoid")); + QCOMPARE(spy.path, QString("/p1")); + QVERIFY(spy.signature.isEmpty()); + } + + { + newMyObjectPeer(0); + + registerMyObjectPeer("/p1", QDBusConnection::ExportScriptableSignals); + registerMyObjectPeer("/p2", QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals); + + QDBusSignalSpy spy; + con.connect(QString(), "/p1", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/p2", "local.MyObject", "nonScriptableSignalVoid", &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.MyObject", "nonScriptableSignalVoid", QVariant()); + QTest::qWait(200); + + QCOMPARE(spy.count, 1); // only /p2 must have emitted now + QCOMPARE(spy.interface, QString("local.MyObject")); + QCOMPARE(spy.name, QString("nonScriptableSignalVoid")); + QCOMPARE(spy.path, QString("/p2")); + QVERIFY(spy.signature.isEmpty()); + } + + { + QDBusSignalSpy spy; + con.connect(QString(), "/p1", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/p2", "local.MyObject", "destroyed", &spy, SLOT(slot(QDBusMessage))); + + { + newMyObjectPeer(0); + + registerMyObjectPeer("/p1", QDBusConnection::ExportScriptableSignals); + registerMyObjectPeer("/p2", QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals); + } // <--- QObject emits the destroyed(QObject*) signal at this point + + QTest::qWait(200); + + QCOMPARE(spy.count, 0); + } +} + +void tst_QDBusAbstractAdaptor::overloadedSignalEmissionPeer_data() +{ + overloadedSignalEmission_data(); +} + +void tst_QDBusAbstractAdaptor::overloadedSignalEmissionPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QString interface = "local.Interface4"; + QString name = "signal"; + QFETCH(QVariant, parameter); + //QDBusInterface *if4 = new QDBusInterface(QString(), "/", interface, con); + + // connect all signals and emit only one + { + QDBusSignalSpy spy; + con.connect(QString(), "/", "local.Interface4", "signal", "", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.Interface4", "signal", "i", + &spy, SLOT(slot(QDBusMessage))); + con.connect(QString(), "/", "local.Interface4", "signal", "s", + &spy, SLOT(slot(QDBusMessage))); + + emitSignalPeer(interface, name, parameter); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } + + QFETCH(QString, signature); + // connect one signal and emit them all + { + QDBusSignalSpy spy; + con.connect(QString(), "/", interface, name, signature, &spy, SLOT(slot(QDBusMessage))); + emitSignalPeer("local.Interface4", "signal", QVariant()); + emitSignalPeer("local.Interface4", "signal", QVariant(1)); + emitSignalPeer("local.Interface4", "signal", QVariant("foo")); + + QCOMPARE(spy.count, 1); + QCOMPARE(spy.interface, interface); + QCOMPARE(spy.name, name); + QTEST(spy.signature, "signature"); + QCOMPARE(spy.value, parameter); + } +} + +void tst_QDBusAbstractAdaptor::readPropertiesPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + for (int i = 2; i <= 4; ++i) { + QString name = QString("Interface%1").arg(i); + + for (int j = 1; j <= 2; ++j) { + QString propname = QString("prop%1").arg(j); + QDBusReply<QVariant> reply = + properties.call(QDBus::BlockWithGui, "Get", "local." + name, propname); + QVariant value = reply; + + QCOMPARE(value.userType(), int(QVariant::String)); + QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname)); + } + } +} + +void tst_QDBusAbstractAdaptor::readPropertiesInvalidInterfacePeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + + // test an invalid interface: + QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "local.DoesntExist", "prop1"); + QVERIFY(!reply.isValid()); +} + +void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterfacePeer_data() +{ + readPropertiesEmptyInterface_data(); +} + +void tst_QDBusAbstractAdaptor::readPropertiesEmptyInterfacePeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + + QFETCH(QVariantMap, expectedProperties); + QFETCH(bool, existing); + + QVariantMap::ConstIterator it = expectedProperties.constBegin(); + for ( ; it != expectedProperties.constEnd(); ++it) { + QDBusReply<QVariant> reply = properties.call(QDBus::BlockWithGui, "Get", "", it.key()); + + if (existing) { + QVERIFY2(reply.isValid(), qPrintable(it.key())); + } else { + QVERIFY2(!reply.isValid(), qPrintable(it.key())); + continue; + } + + QCOMPARE(int(reply.value().type()), int(QVariant::String)); + if (it.value().isValid()) + QCOMPARE(reply.value().toString(), it.value().toString()); + } +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + for (int i = 2; i <= 4; ++i) { + QString name = QString("Interface%1").arg(i); + QDBusReply<QVariantMap> reply = + properties.call(QDBus::BlockWithGui, "GetAll", "local." + name); + + for (int j = 1; j <= 2; ++j) { + QString propname = QString("prop%1").arg(j); + QVERIFY2(reply.value().contains(propname), + qPrintable(propname + " on " + name)); + QVariant value = reply.value().value(propname); + + QCOMPARE(value.userType(), int(QVariant::String)); + QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname)); + } + } +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesInvalidInterfacePeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + + // test an invalid interface: + QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", "local.DoesntExist"); + QVERIFY(!reply.isValid()); +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterfacePeer_data() +{ + readAllPropertiesEmptyInterface_data(); +} + +void tst_QDBusAbstractAdaptor::readAllPropertiesEmptyInterfacePeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + + QDBusReply<QVariantMap> reply = properties.call(QDBus::BlockWithGui, "GetAll", ""); + QVERIFY(reply.isValid()); + + QVariantMap allprops = reply; + + QFETCH(QVariantMap, expectedProperties); + QFETCH(bool, existing); + + QVariantMap::ConstIterator it = expectedProperties.constBegin(); + if (existing) { + for ( ; it != expectedProperties.constEnd(); ++it) { + QVERIFY2(allprops.contains(it.key()), qPrintable(it.key())); + + QVariant propvalue = allprops.value(it.key()); + QVERIFY2(!propvalue.isNull(), qPrintable(it.key())); + QVERIFY2(propvalue.isValid(), qPrintable(it.key())); + + QString stringvalue = propvalue.toString(); + QVERIFY2(!stringvalue.isEmpty(), qPrintable(it.key())); + + if (it.value().isValid()) + QCOMPARE(stringvalue, it.value().toString()); + + // remove this property from the map + allprops.remove(it.key()); + } + + QVERIFY2(allprops.isEmpty(), + qPrintable(QStringList(allprops.keys()).join(" "))); + } else { + for ( ; it != expectedProperties.constEnd(); ++it) + QVERIFY2(!allprops.contains(it.key()), qPrintable(it.key())); + } +} + +void tst_QDBusAbstractAdaptor::writePropertiesPeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QDBusInterface properties(QString(), "/", "org.freedesktop.DBus.Properties", con); + for (int i = 2; i <= 4; ++i) { + QString name = QString("Interface%1").arg(i); + + clearValueSpyPeer(); + properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop1"), + qVariantFromValue(QDBusVariant(name))); + QVERIFY(valueSpyPeer().isEmpty()); // call mustn't have succeeded + + properties.call(QDBus::BlockWithGui, "Set", "local." + name, QString("prop2"), + qVariantFromValue(QDBusVariant(name))); + QCOMPARE(valueSpyPeer(), name); + QCOMPARE(QString(slotSpyPeer()), QString("void %1::setProp2(const QString &)").arg(name)); + } +} + +#if 0 +void tst_QDBusAbstractAdaptor::adaptorIntrospection_data() +{ + methodCalls_data(); +} + +void tst_QDBusAbstractAdaptor::adaptorIntrospection() +{ + QDBusConnection con = QDBus::sessionBus(); + QVERIFY(con.isConnected()); + + QObject obj; + con.registerObject("/", &obj); + + QFETCH(int, nInterfaces); + switch (nInterfaces) + { + case 4: + new Interface4(&obj); + case 3: + new Interface3(&obj); + case 2: + new Interface2(&obj); + case 1: + new Interface1(&obj); + } + + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QVERIFY(dobj.isValid()); + + QString xml = dobj.introspect(); + QVERIFY(!xml.isEmpty()); + + QStringList interfaces = dobj.interfaces(); + QCOMPARE(interfaces.count(), nInterfaces + 2); + switch (nInterfaces) + { + case 4: { + QVERIFY(interfaces.contains("local.Interface4")); + QDBusInterface iface(dobj, "local.Interface4"); + QCOMPARE(iface.methodData(), Interface4::methodData); + QCOMPARE(iface.signalData(), Interface4::signalData); + QCOMPARE(iface.propertyData(), Interface4::propertyData); + } + case 3: { + QVERIFY(interfaces.contains("local.Interface3")); + QDBusInterface iface(dobj, "local.Interface3"); + QCOMPARE(iface.methodData(), Interface3::methodData); + QCOMPARE(iface.signalData(), Interface3::signalData); + QCOMPARE(iface.propertyData(), Interface3::propertyData); + } + case 2: { + QVERIFY(interfaces.contains("local.Interface2")); + QDBusInterface iface(dobj, "local.Interface2"); + QCOMPARE(iface.methodData(), Interface2::methodData); + QCOMPARE(iface.signalData(), Interface2::signalData); + QCOMPARE(iface.propertyData(), Interface2::propertyData); + } + case 1: { + QVERIFY(interfaces.contains("local.Interface1")); + QDBusInterface iface(dobj, "local.Interface1"); + QCOMPARE(iface.methodData(), Interface1::methodData); + QCOMPARE(iface.signalData(), Interface1::signalData); + QCOMPARE(iface.propertyData(), Interface1::propertyData); + } + } +} + +void tst_QDBusAbstractAdaptor::objectTreeIntrospection() +{ + QDBusConnection con = QDBus::sessionBus(); + QVERIFY(con.isConnected()); + + { + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.isEmpty()); + } + + QObject root; + con.registerObject("/", &root); + { + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.isEmpty()); + } + + QObject p1; + con.registerObject("/p1", &p1); + { + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.contains("p1")); + } + + con.unregisterObject("/"); + { + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.contains("p1")); + } + + con.registerObject("/p1/q/r", &root); + { + QDBusObject dobj = con.findObject(con.baseService(), "/p1"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.contains("q")); + } + { + QDBusObject dobj = con.findObject(con.baseService(), "/p1/q"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.contains("r")); + } + + con.unregisterObject("/p1", QDBusConnection::UnregisterTree); + { + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.isEmpty()); + } + + QObject p2; + con.registerObject("/p2", &p2, QDBusConnection::ExportChildObjects); + { + QDBusObject dobj = con.findObject(con.baseService(), "/"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(!tree.childObjects.contains("p1")); + QVERIFY(tree.childObjects.contains("p2")); + } + + QObject q; + q.setParent(&p2); + { + QDBusObject dobj = con.findObject(con.baseService(), "/p2"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(!tree.childObjects.contains("q")); + } + + q.setObjectName("q"); + { + QDBusObject dobj = con.findObject(con.baseService(), "/p2"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(tree.childObjects.contains("q")); + } + + q.setParent(0); + { + QDBusObject dobj = con.findObject(con.baseService(), "/p2"); + QString xml = dobj.introspect(); + + QDBusIntrospection::Object tree = + QDBusIntrospection::parseObject(xml); + QVERIFY(!tree.childObjects.contains("q")); + } +} +#endif + +void tst_QDBusAbstractAdaptor::typeMatching_data() +{ + QTest::addColumn<QString>("basename"); + QTest::addColumn<QString>("signature"); + QTest::addColumn<QVariant>("value"); + + QTest::newRow("bool") << "Bool" << "b" << QVariant(true); + QTest::newRow("byte") << "UChar" << "y" << qVariantFromValue(uchar(42)); + QTest::newRow("short") << "Short" << "n" << qVariantFromValue(short(-43)); + QTest::newRow("ushort") << "UShort" << "q" << qVariantFromValue(ushort(44)); + QTest::newRow("int") << "Int" << "i" << QVariant(42); + QTest::newRow("uint") << "UInt" << "u" << QVariant(42U); + QTest::newRow("qlonglong") << "LongLong" << "x" << QVariant(Q_INT64_C(42)); + QTest::newRow("qulonglong") << "ULongLong" << "t" << QVariant(Q_UINT64_C(42)); + QTest::newRow("double") << "Double" << "d" << QVariant(2.5); + QTest::newRow("string") << "String" << "s" << QVariant("Hello, World!"); + + QTest::newRow("variant") << "Variant" << "v" << qVariantFromValue(QDBusVariant("Hello again!")); + QTest::newRow("list") << "List" << "av" << QVariant(QVariantList() + << 42 + << QString("foo") + << QByteArray("bar") + << qVariantFromValue(QDBusVariant(QString("baz")))); + QTest::newRow("stringlist") << "StringList" << "as" << QVariant(QStringList() << "Hello" << "world"); + QTest::newRow("bytearray") << "ByteArray" << "ay" << QVariant(QByteArray("foo")); + + QVariantMap map; + map["one"] = 1; // int + map["The answer to life, the Universe and everything"] = 42u; // uint + map["In the beginning..."] = QString("There was nothing"); // string + map["but Unix came and said"] = QByteArray("\"Hello, World\""); // bytearray + map["two"] = qVariantFromValue(short(2)); // short + QTest::newRow("map") << "Map" << "a{sv}" << QVariant(map); + + StringStringMap ssmap; + ssmap["a"] = "A"; + ssmap["A"] = "a"; + QTest::newRow("ssmap") << "SSMap" << "a{ss}" << qVariantFromValue(ssmap); + + LLDateTimeMap lldtmap; + lldtmap[-1] = QDateTime(); + QDateTime now = QDateTime::currentDateTime(); + lldtmap[now.toTime_t()] = now; // array of struct of int64 and struct of 3 ints and struct of 4 ints and int + QTest::newRow("lldtmap") << "LLDateTimeMap" << "a{x((iii)(iiii)i)}" << qVariantFromValue(lldtmap); + + MyStruct s; + s.i = 42; + s.s = "A value"; + QTest::newRow("struct") << "Struct" << "(is)" << qVariantFromValue(s); +} + +void tst_QDBusAbstractAdaptor::typeMatching() +{ + QObject obj; + new TypesInterface(&obj); + + QDBusConnection con = QDBusConnection::sessionBus(); + con.registerObject("/types", &obj); + + QFETCH(QString, basename); + QFETCH(QString, signature); + QFETCH(QVariant, value); + + QDBusMessage reply; + QDBusInterface iface(con.baseService(), "/types", "local.TypesInterface", con); + + reply = iface.callWithArgumentList(QDBus::BlockWithGui, "method" + basename, + QVariantList() << value); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + + reply = iface.call(QDBus::BlockWithGui, "retrieve" + basename); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + QCOMPARE(reply.arguments().count(), 1); + + const QVariant &retval = reply.arguments().at(0); + QVERIFY(compare(retval, value)); +} + +void tst_QDBusAbstractAdaptor::methodWithMoreThanOneReturnValue() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + MyObject obj; + con.registerObject("/", &obj); + + QString testString = "This is a test string."; + + QDBusInterface remote(con.baseService(), "/", "local.Interface3", con); + QDBusMessage reply = remote.call(QDBus::BlockWithGui, "methodStringString", testString); + QVERIFY(reply.arguments().count() == 2); + + QDBusReply<int> intreply = reply; + QVERIFY(intreply.isValid()); + QCOMPARE(intreply.value(), 42); + + QCOMPARE(reply.arguments().at(1).userType(), int(QVariant::String)); + QCOMPARE(qdbus_cast<QString>(reply.arguments().at(1)), testString); +} + +void tst_QDBusAbstractAdaptor::methodWithMoreThanOneReturnValuePeer() +{ + QDBusConnection con("peer"); + QVERIFY(con.isConnected()); + + newMyObjectPeer(); + registerMyObjectPeer("/"); + + QString testString = "This is a test string."; + + QDBusInterface remote(QString(), "/", "local.Interface3", con); + QDBusMessage reply = remote.call(QDBus::BlockWithGui, "methodStringString", testString); + QVERIFY(reply.arguments().count() == 2); + + QDBusReply<int> intreply = reply; + QVERIFY(intreply.isValid()); + QCOMPARE(intreply.value(), 42); + + QCOMPARE(reply.arguments().at(1).userType(), int(QVariant::String)); + QCOMPARE(qdbus_cast<QString>(reply.arguments().at(1)), testString); +} + +QTEST_MAIN(tst_QDBusAbstractAdaptor) + +#include "tst_qdbusabstractadaptor.moc" diff --git a/tests/auto/dbus/qdbusabstractinterface/com.trolltech.QtDBus.Pinger.xml b/tests/auto/dbus/qdbusabstractinterface/com.trolltech.QtDBus.Pinger.xml new file mode 100644 index 0000000000..d945ec9b43 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/com.trolltech.QtDBus.Pinger.xml @@ -0,0 +1,34 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="com.trolltech.QtDBus.Pinger"> + <property name="stringProp" type="s" access="readwrite"/> + <property name="variantProp" type="v" access="readwrite"/> + <property name="complexProp" type="(s)" access="readwrite"> + <annotation name="com.trolltech.QtDBus.QtTypeName" value="RegisteredType"/> + </property> + <signal name="voidSignal"/> + <signal name="stringSignal"> + <arg type="s"/> + </signal> + <signal name="complexSignal"> + <arg name="" type="(s)"/> + <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="RegisteredType"/> + </signal> + <method name="voidMethod" /> + <method name="sleepMethod"> + <arg type="i" /> + <arg type="i" direction="out"/> + </method> + <method name="stringMethod"> + <arg type="s" direction="out"/> + </method> + <method name="complexMethod"> + <arg type="(s)" direction="out"/> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="RegisteredType"/> + </method> + <method name="multiOutMethod"> + <arg type="s" direction="out"/> + <arg type="i" direction="out"/> + </method> + </interface> +</node> diff --git a/tests/auto/dbus/qdbusabstractinterface/interface.cpp b/tests/auto/dbus/qdbusabstractinterface/interface.cpp new file mode 100644 index 0000000000..849db93084 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/interface.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "interface.h" +#include <QThread> + +Interface::Interface() +{ +} + +// Export the sleep function +// TODO QT5: remove this class, QThread::msleep is now public +class FriendlySleepyThread : public QThread { +public: + using QThread::msleep; +}; + +int Interface::sleepMethod(int msec) +{ + FriendlySleepyThread::msleep(msec); + return 42; +} + +#include "moc_interface.cpp" diff --git a/tests/auto/dbus/qdbusabstractinterface/interface.h b/tests/auto/dbus/qdbusabstractinterface/interface.h new file mode 100644 index 0000000000..0fb15fe6c5 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/interface.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INTERFACE_H +#define INTERFACE_H + +#include <QtCore/QObject> +#include <QtDBus/QDBusArgument> + +struct RegisteredType +{ + inline RegisteredType(const QString &str = QString()) : s(str) {} + inline bool operator==(const RegisteredType &other) const { return s == other.s; } + QString s; +}; +Q_DECLARE_METATYPE(RegisteredType) + +inline QDBusArgument &operator<<(QDBusArgument &s, const RegisteredType &data) +{ + s.beginStructure(); + s << data.s; + s.endStructure(); + return s; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &s, RegisteredType &data) +{ + s.beginStructure(); + s >> data.s; + s.endStructure(); + return s; +} + +struct UnregisteredType +{ + QString s; +}; +Q_DECLARE_METATYPE(UnregisteredType) + +class Interface: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.Pinger") + Q_PROPERTY(QString stringProp READ stringProp WRITE setStringProp SCRIPTABLE true) + Q_PROPERTY(QDBusVariant variantProp READ variantProp WRITE setVariantProp SCRIPTABLE true) + Q_PROPERTY(RegisteredType complexProp READ complexProp WRITE setComplexProp SCRIPTABLE true) + + friend class tst_QDBusAbstractInterface; + friend class PingerServer; + QString m_stringProp; + QDBusVariant m_variantProp; + RegisteredType m_complexProp; + +public: + Interface(); + + QString stringProp() const { return m_stringProp; } + void setStringProp(const QString &s) { m_stringProp = s; } + QDBusVariant variantProp() const { return m_variantProp; } + void setVariantProp(const QDBusVariant &v) { m_variantProp = v; } + RegisteredType complexProp() const { return m_complexProp; } + void setComplexProp(const RegisteredType &r) { m_complexProp = r; } + +public slots: + Q_SCRIPTABLE void voidMethod() {} + Q_SCRIPTABLE int sleepMethod(int); + Q_SCRIPTABLE QString stringMethod() { return "Hello, world"; } + Q_SCRIPTABLE RegisteredType complexMethod() { return RegisteredType("Hello, world"); } + Q_SCRIPTABLE QString multiOutMethod(int &value) { value = 42; return "Hello, world"; } + +signals: + Q_SCRIPTABLE void voidSignal(); + Q_SCRIPTABLE void stringSignal(const QString &); + Q_SCRIPTABLE void complexSignal(RegisteredType); +}; + +#endif // INTERFACE_H diff --git a/tests/auto/dbus/qdbusabstractinterface/pinger.cpp b/tests/auto/dbus/qdbusabstractinterface/pinger.cpp new file mode 100644 index 0000000000..24113fbb1b --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/pinger.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp -i interface.h -p pinger com.trolltech.QtDBus.Pinger.xml + * + * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "pinger.h" + +/* + * Implementation of interface class ComTrolltechQtDBusPingerInterface + */ + +ComTrolltechQtDBusPingerInterface::ComTrolltechQtDBusPingerInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +ComTrolltechQtDBusPingerInterface::~ComTrolltechQtDBusPingerInterface() +{ +} + diff --git a/tests/auto/dbus/qdbusabstractinterface/pinger.h b/tests/auto/dbus/qdbusabstractinterface/pinger.h new file mode 100644 index 0000000000..739a14229f --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/pinger.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp -i interface.h -p pinger com.trolltech.QtDBus.Pinger.xml + * + * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef PINGER_H_1246463415 +#define PINGER_H_1246463415 + +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtDBus/QtDBus> +#include "interface.h" + +/* + * Proxy class for interface com.trolltech.QtDBus.Pinger + */ +class ComTrolltechQtDBusPingerInterface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "com.trolltech.QtDBus.Pinger"; } + +public: + ComTrolltechQtDBusPingerInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~ComTrolltechQtDBusPingerInterface(); + + Q_PROPERTY(RegisteredType complexProp READ complexProp WRITE setComplexProp) + inline RegisteredType complexProp() const + { return qvariant_cast< RegisteredType >(property("complexProp")); } + inline void setComplexProp(RegisteredType value) + { setProperty("complexProp", qVariantFromValue(value)); } + + Q_PROPERTY(QString stringProp READ stringProp WRITE setStringProp) + inline QString stringProp() const + { return qvariant_cast< QString >(property("stringProp")); } + inline void setStringProp(const QString &value) + { setProperty("stringProp", qVariantFromValue(value)); } + + Q_PROPERTY(QDBusVariant variantProp READ variantProp WRITE setVariantProp) + inline QDBusVariant variantProp() const + { return qvariant_cast< QDBusVariant >(property("variantProp")); } + inline void setVariantProp(const QDBusVariant &value) + { setProperty("variantProp", qVariantFromValue(value)); } + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<RegisteredType> complexMethod() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("complexMethod"), argumentList); + } + + inline QDBusPendingReply<QString, int> multiOutMethod() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("multiOutMethod"), argumentList); + } + inline QDBusReply<QString> multiOutMethod(int &out1) + { + QList<QVariant> argumentList; + QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("multiOutMethod"), argumentList); + if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 2) { + out1 = qdbus_cast<int>(reply.arguments().at(1)); + } + return reply; + } + + inline QDBusPendingReply<int> sleepMethod(int in0) + { + QList<QVariant> argumentList; + argumentList << qVariantFromValue(in0); + return asyncCallWithArgumentList(QLatin1String("sleepMethod"), argumentList); + } + + inline QDBusPendingReply<QString> stringMethod() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("stringMethod"), argumentList); + } + + inline QDBusPendingReply<> voidMethod() + { + QList<QVariant> argumentList; + return asyncCallWithArgumentList(QLatin1String("voidMethod"), argumentList); + } + +Q_SIGNALS: // SIGNALS + void complexSignal(RegisteredType in0); + void stringSignal(const QString &in0); + void voidSignal(); +}; + +namespace com { + namespace trolltech { + namespace QtDBus { + typedef ::ComTrolltechQtDBusPingerInterface Pinger; + } + } +} +#endif diff --git a/tests/auto/dbus/qdbusabstractinterface/qdbusabstractinterface.pro b/tests/auto/dbus/qdbusabstractinterface/qdbusabstractinterface.pro new file mode 100644 index 0000000000..f9077b92cf --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/qdbusabstractinterface.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +contains(QT_CONFIG,dbus): { + TEMPLATE = subdirs + CONFIG += ordered + SUBDIRS = qpinger test +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + +OTHER_FILES += com.trolltech.QtDBus.Pinger.xml diff --git a/tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger.cpp b/tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger.cpp new file mode 100644 index 0000000000..393d6470c8 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QtCore> +#include <QtDBus/QtDBus> +#include "../interface.h" + +static const char serviceName[] = "com.trolltech.autotests.qpinger"; +static const char objectPath[] = "/com/trolltech/qpinger"; +//static const char *interfaceName = serviceName; + +class PingerServer : public QDBusServer +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.autotests.qpinger") +public: + PingerServer(QString addr = "unix:tmpdir=/tmp", QObject* parent = 0) + : QDBusServer(addr, parent), + m_conn("none") + { + connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&))); + reset(); + } + +public slots: + QString address() const + { + return QDBusServer::address(); + } + + bool isConnected() const + { + return m_conn.isConnected(); + } + + void reset() + { + targetObj.m_stringProp = "This is a test"; + targetObj.m_variantProp = QDBusVariant(QVariant(42)); + targetObj.m_complexProp = RegisteredType("This is a test"); + } + + void voidSignal() + { + emit targetObj.voidSignal(); + } + + void stringSignal(const QString& value) + { + emit targetObj.stringSignal(value); + } + + void complexSignal(const QString& value) + { + RegisteredType reg(value); + emit targetObj.complexSignal(reg); + } + +private slots: + void handleConnection(const QDBusConnection& con) + { + m_conn = con; + m_conn.registerObject("/", &targetObj, QDBusConnection::ExportScriptableContents); + } + +private: + Interface targetObj; + QDBusConnection m_conn; +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + // register the meta types + qDBusRegisterMetaType<RegisteredType>(); + qRegisterMetaType<UnregisteredType>(); + + QDBusConnection con = QDBusConnection::sessionBus(); + if (!con.isConnected()) + exit(1); + + if (!con.registerService(serviceName)) + exit(2); + + PingerServer server; + con.registerObject(objectPath, &server, QDBusConnection::ExportAllSlots); + + printf("ready.\n"); + + return app.exec(); +} + +#include "qpinger.moc" diff --git a/tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger.pro b/tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger.pro new file mode 100644 index 0000000000..27545bbb0f --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/qpinger/qpinger.pro @@ -0,0 +1,5 @@ +SOURCES = qpinger.cpp ../interface.cpp +HEADERS = ../interface.h +TARGET = qpinger +QT += dbus +QT -= gui diff --git a/tests/auto/dbus/qdbusabstractinterface/test/test.pro b/tests/auto/dbus/qdbusabstractinterface/test/test.pro new file mode 100644 index 0000000000..98bcaa70f5 --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/test/test.pro @@ -0,0 +1,13 @@ +load(qttest_p4) +SOURCES += ../tst_qdbusabstractinterface.cpp ../interface.cpp +HEADERS += ../interface.h + +# These are generated sources +# To regenerate, see the command-line at the top of the files +SOURCES += ../pinger.cpp +HEADERS += ../pinger.h + +TARGET = ../tst_qdbusabstractinterface + +QT = core +QT += dbus diff --git a/tests/auto/dbus/qdbusabstractinterface/tst_qdbusabstractinterface.cpp b/tests/auto/dbus/qdbusabstractinterface/tst_qdbusabstractinterface.cpp new file mode 100644 index 0000000000..994df058fa --- /dev/null +++ b/tests/auto/dbus/qdbusabstractinterface/tst_qdbusabstractinterface.cpp @@ -0,0 +1,1301 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qsharedpointer.h> + +#include <QtTest/QtTest> + +#include <QtDBus> + +#include "interface.h" +#include "pinger.h" + +static const char serviceName[] = "com.trolltech.autotests.qpinger"; +static const char objectPath[] = "/com/trolltech/qpinger"; +static const char *interfaceName = serviceName; + +typedef QSharedPointer<com::trolltech::QtDBus::Pinger> Pinger; + +class tst_QDBusAbstractInterface: public QObject +{ + Q_OBJECT + Interface targetObj; + + Pinger getPinger(QString service = "", const QString &path = "/") + { + QDBusConnection con = QDBusConnection::sessionBus(); + if (!con.isConnected()) + return Pinger(); + if (service.isEmpty() && !service.isNull()) + service = con.baseService(); + return Pinger(new com::trolltech::QtDBus::Pinger(service, path, con)); + } + + Pinger getPingerPeer(const QString &path = "/") + { + QDBusConnection con = QDBusConnection("peer"); + if (!con.isConnected()) + return Pinger(); + return Pinger(new com::trolltech::QtDBus::Pinger("", path, con)); + } + + void resetServer() + { + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "reset"); + QDBusConnection::sessionBus().send(req); + } + +public: + tst_QDBusAbstractInterface(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void makeVoidCall(); + void makeStringCall(); + void makeComplexCall(); + void makeMultiOutCall(); + + void makeVoidCallPeer(); + void makeStringCallPeer(); + void makeComplexCallPeer(); + void makeMultiOutCallPeer(); + + void makeAsyncVoidCall(); + void makeAsyncStringCall(); + void makeAsyncComplexCall(); + void makeAsyncMultiOutCall(); + + void makeAsyncVoidCallPeer(); + void makeAsyncStringCallPeer(); + void makeAsyncComplexCallPeer(); + void makeAsyncMultiOutCallPeer(); + + void callWithTimeout(); + + void stringPropRead(); + void stringPropWrite(); + void variantPropRead(); + void variantPropWrite(); + void complexPropRead(); + void complexPropWrite(); + + void stringPropReadPeer(); + void stringPropWritePeer(); + void variantPropReadPeer(); + void variantPropWritePeer(); + void complexPropReadPeer(); + void complexPropWritePeer(); + + void stringPropDirectRead(); + void stringPropDirectWrite(); + void variantPropDirectRead(); + void variantPropDirectWrite(); + void complexPropDirectRead(); + void complexPropDirectWrite(); + + void stringPropDirectReadPeer(); + void stringPropDirectWritePeer(); + void variantPropDirectReadPeer(); + void variantPropDirectWritePeer(); + void complexPropDirectReadPeer(); + void complexPropDirectWritePeer(); + + void getVoidSignal_data(); + void getVoidSignal(); + void getStringSignal_data(); + void getStringSignal(); + void getComplexSignal_data(); + void getComplexSignal(); + + void getVoidSignalPeer_data(); + void getVoidSignalPeer(); + void getStringSignalPeer_data(); + void getStringSignalPeer(); + void getComplexSignalPeer_data(); + void getComplexSignalPeer(); + + void followSignal(); + + void createErrors_data(); + void createErrors(); + + void createErrorsPeer_data(); + void createErrorsPeer(); + + void callErrors_data(); + void callErrors(); + void asyncCallErrors_data(); + void asyncCallErrors(); + + void callErrorsPeer_data(); + void callErrorsPeer(); + void asyncCallErrorsPeer_data(); + void asyncCallErrorsPeer(); + + void propertyReadErrors_data(); + void propertyReadErrors(); + void propertyWriteErrors_data(); + void propertyWriteErrors(); + void directPropertyReadErrors_data(); + void directPropertyReadErrors(); + void directPropertyWriteErrors_data(); + void directPropertyWriteErrors(); + + void propertyReadErrorsPeer_data(); + void propertyReadErrorsPeer(); + void propertyWriteErrorsPeer_data(); + void propertyWriteErrorsPeer(); + void directPropertyReadErrorsPeer_data(); + void directPropertyReadErrorsPeer(); + void directPropertyWriteErrorsPeer_data(); + void directPropertyWriteErrorsPeer(); +private: + QProcess proc; +}; + +class WaitForQPinger: public QObject +{ + Q_OBJECT +public: + WaitForQPinger(); + bool ok(); +public Q_SLOTS: + void ownerChange(const QString &name) + { + if (name == serviceName) + loop.quit(); + } + +private: + QEventLoop loop; +}; + +WaitForQPinger::WaitForQPinger() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + if (!ok()) { + connect(con.interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), + SLOT(ownerChange(QString))); + QTimer::singleShot(2000, &loop, SLOT(quit())); + loop.exec(); + } +} + +bool WaitForQPinger::ok() +{ + return QDBusConnection::sessionBus().isConnected() && + QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName); +} + +tst_QDBusAbstractInterface::tst_QDBusAbstractInterface() +{ + // register the meta types + qDBusRegisterMetaType<RegisteredType>(); + qRegisterMetaType<UnregisteredType>(); +} + +void tst_QDBusAbstractInterface::initTestCase() +{ + // enable debugging temporarily: + //putenv("QDBUS_DEBUG=1"); + + // register the object + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + con.registerObject("/", &targetObj, QDBusConnection::ExportScriptableContents); + + // start peer server + #ifdef Q_OS_WIN + proc.start("qpinger"); + #else + proc.start("./qpinger/qpinger"); + #endif + QVERIFY(proc.waitForStarted()); + + WaitForQPinger w; + QVERIFY(w.ok()); + //QTest::qWait(2000); + + // get peer server address + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "address"); + QDBusMessage rpl = con.call(req); + QVERIFY(rpl.type() == QDBusMessage::ReplyMessage); + QString address = rpl.arguments().at(0).toString(); + + // connect to peer server + QDBusConnection peercon = QDBusConnection::connectToPeer(address, "peer"); + QVERIFY(peercon.isConnected()); + + QDBusMessage req2 = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "isConnected"); + QDBusMessage rpl2 = con.call(req2); + QVERIFY(rpl2.type() == QDBusMessage::ReplyMessage); + QVERIFY(rpl2.arguments().at(0).toBool()); +} + +void tst_QDBusAbstractInterface::cleanupTestCase() +{ + proc.close(); + proc.kill(); +} + +void tst_QDBusAbstractInterface::makeVoidCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusReply<void> r = p->voidMethod(); + QVERIFY(r.isValid()); +} + +void tst_QDBusAbstractInterface::makeStringCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusReply<QString> r = p->stringMethod(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.stringMethod()); +} + +void tst_QDBusAbstractInterface::makeComplexCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusReply<RegisteredType> r = p->complexMethod(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.complexMethod()); +} + +void tst_QDBusAbstractInterface::makeMultiOutCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + int value; + QDBusReply<QString> r = p->multiOutMethod(value); + QVERIFY(r.isValid()); + + int expectedValue; + QCOMPARE(r.value(), targetObj.multiOutMethod(expectedValue)); + QCOMPARE(value, expectedValue); +} + +void tst_QDBusAbstractInterface::makeVoidCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusReply<void> r = p->voidMethod(); + QVERIFY(r.isValid()); +} + +void tst_QDBusAbstractInterface::makeStringCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusReply<QString> r = p->stringMethod(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.stringMethod()); +} + +void tst_QDBusAbstractInterface::makeComplexCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusReply<RegisteredType> r = p->complexMethod(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.complexMethod()); +} + +void tst_QDBusAbstractInterface::makeMultiOutCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + int value; + QDBusReply<QString> r = p->multiOutMethod(value); + QVERIFY(r.isValid()); + + int expectedValue; + QCOMPARE(r.value(), targetObj.multiOutMethod(expectedValue)); + QCOMPARE(value, expectedValue); +} + +void tst_QDBusAbstractInterface::makeAsyncVoidCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<void> r = p->voidMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); +} + +void tst_QDBusAbstractInterface::makeAsyncStringCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<QString> r = p->stringMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.stringMethod()); +} + +void tst_QDBusAbstractInterface::makeAsyncComplexCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<RegisteredType> r = p->complexMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.complexMethod()); +} + +void tst_QDBusAbstractInterface::makeAsyncMultiOutCall() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<QString, int> r = p->multiOutMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); + + int expectedValue; + QCOMPARE(r.value(), targetObj.multiOutMethod(expectedValue)); + QCOMPARE(r.argumentAt<1>(), expectedValue); +} + +void tst_QDBusAbstractInterface::makeAsyncVoidCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<void> r = p->voidMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); +} +void tst_QDBusAbstractInterface::makeAsyncStringCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusMessage reply = p->call(QDBus::BlockWithGui, QLatin1String("voidMethod")); + QVERIFY(reply.type() == QDBusMessage::ReplyMessage); + + QDBusPendingReply<QString> r = p->stringMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.stringMethod()); +} + +void tst_QDBusAbstractInterface::makeAsyncComplexCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<RegisteredType> r = p->complexMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); + QCOMPARE(r.value(), targetObj.complexMethod()); +} + +void tst_QDBusAbstractInterface::makeAsyncMultiOutCallPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusPendingReply<QString, int> r = p->multiOutMethod(); + r.waitForFinished(); + QVERIFY(r.isValid()); + + int expectedValue; + QCOMPARE(r.value(), targetObj.multiOutMethod(expectedValue)); + QCOMPARE(r.argumentAt<1>(), expectedValue); + QCoreApplication::instance()->processEvents(); +} + +static const char server_serviceName[] = "com.trolltech.autotests.dbusserver"; +static const char server_objectPath[] = "/com/trolltech/server"; +static const char server_interfaceName[] = "com.trolltech.QtDBus.Pinger"; + +class DBusServerThread : public QThread +{ +public: + DBusServerThread() { + start(); + m_ready.acquire(); + } + ~DBusServerThread() { + quit(); + wait(); + } + + void run() + { + QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus, "ThreadConnection"); + if (!con.isConnected()) + qWarning("Error registering to DBus"); + if (!con.registerService(server_serviceName)) + qWarning("Error registering service name"); + Interface targetObj; + con.registerObject(server_objectPath, &targetObj, QDBusConnection::ExportScriptableContents); + m_ready.release(); + exec(); + + QDBusConnection::disconnectFromBus( con.name() ); + } +private: + QSemaphore m_ready; +}; + +void tst_QDBusAbstractInterface::callWithTimeout() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY2(con.isConnected(), "Not connected to D-Bus"); + + DBusServerThread serverThread; + + QDBusMessage msg = QDBusMessage::createMethodCall(server_serviceName, + server_objectPath, server_interfaceName, "sleepMethod"); + msg << 100; + + { + // Call with no timeout -> works + QDBusMessage reply = con.call(msg); + QCOMPARE((int)reply.type(), (int)QDBusMessage::ReplyMessage); + QCOMPARE(reply.arguments().at(0).toInt(), 42); + } + + { + // Call with 1 sec timeout -> fails + QDBusMessage reply = con.call(msg, QDBus::Block, 1); + QCOMPARE(reply.type(), QDBusMessage::ErrorMessage); + } + + // Now using QDBusInterface + + QDBusInterface iface(server_serviceName, server_objectPath, server_interfaceName, con); + { + // Call with no timeout + QDBusMessage reply = iface.call("sleepMethod", 100); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + QCOMPARE(reply.arguments().at(0).toInt(), 42); + } + { + // Call with 1 sec timeout -> fails + iface.setTimeout(1); + QDBusMessage reply = iface.call("sleepMethod", 100); + QCOMPARE(reply.type(), QDBusMessage::ErrorMessage); + } + + // Now using generated code + com::trolltech::QtDBus::Pinger p(server_serviceName, server_objectPath, QDBusConnection::sessionBus()); + { + // Call with no timeout + QDBusReply<int> reply = p.sleepMethod(100); + QVERIFY(reply.isValid()); + QCOMPARE(int(reply), 42); + } + { + // Call with 1 sec timeout -> fails + p.setTimeout(1); + QDBusReply<int> reply = p.sleepMethod(100); + QVERIFY(!reply.isValid()); + } +} + +void tst_QDBusAbstractInterface::stringPropRead() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QString expectedValue = targetObj.m_stringProp = "This is a test"; + QVariant v = p->property("stringProp"); + QVERIFY(v.isValid()); + QCOMPARE(v.toString(), expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropWrite() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QString expectedValue = "This is a value"; + QVERIFY(p->setProperty("stringProp", expectedValue)); + QCOMPARE(targetObj.m_stringProp, expectedValue); +} + +void tst_QDBusAbstractInterface::variantPropRead() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusVariant expectedValue = targetObj.m_variantProp = QDBusVariant(QVariant(42)); + QVariant v = p->property("variantProp"); + QVERIFY(v.isValid()); + QDBusVariant value = v.value<QDBusVariant>(); + QCOMPARE(value.variant().userType(), expectedValue.variant().userType()); + QCOMPARE(value.variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::variantPropWrite() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusVariant expectedValue = QDBusVariant(Q_INT64_C(-47)); + QVERIFY(p->setProperty("variantProp", qVariantFromValue(expectedValue))); + QCOMPARE(targetObj.m_variantProp.variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::complexPropRead() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + RegisteredType expectedValue = targetObj.m_complexProp = RegisteredType("This is a test"); + QVariant v = p->property("complexProp"); + QVERIFY(v.userType() == qMetaTypeId<RegisteredType>()); + QCOMPARE(v.value<RegisteredType>(), targetObj.m_complexProp); +} + +void tst_QDBusAbstractInterface::complexPropWrite() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + RegisteredType expectedValue = RegisteredType("This is a value"); + QVERIFY(p->setProperty("complexProp", qVariantFromValue(expectedValue))); + QCOMPARE(targetObj.m_complexProp, expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropReadPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QString expectedValue = "This is a test"; + QVariant v = p->property("stringProp"); + QVERIFY(v.isValid()); + QCOMPARE(v.toString(), expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropWritePeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QString expectedValue = "This is a value"; + QVERIFY(p->setProperty("stringProp", expectedValue)); + QCOMPARE(targetObj.m_stringProp, expectedValue); +} + +void tst_QDBusAbstractInterface::variantPropReadPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QDBusVariant expectedValue = QDBusVariant(QVariant(42)); + QVariant v = p->property("variantProp"); + QVERIFY(v.isValid()); + QDBusVariant value = v.value<QDBusVariant>(); + QCOMPARE(value.variant().userType(), expectedValue.variant().userType()); + QCOMPARE(value.variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::variantPropWritePeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QDBusVariant expectedValue = QDBusVariant(Q_INT64_C(-47)); + QVERIFY(p->setProperty("variantProp", qVariantFromValue(expectedValue))); + QCOMPARE(targetObj.m_variantProp.variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::complexPropReadPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + RegisteredType expectedValue = RegisteredType("This is a test"); + QVariant v = p->property("complexProp"); + QVERIFY(v.userType() == qMetaTypeId<RegisteredType>()); + QCOMPARE(v.value<RegisteredType>(), expectedValue); +} + +void tst_QDBusAbstractInterface::complexPropWritePeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + RegisteredType expectedValue = RegisteredType("This is a value"); + QVERIFY(p->setProperty("complexProp", qVariantFromValue(expectedValue))); + QCOMPARE(targetObj.m_complexProp, expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropDirectRead() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QString expectedValue = targetObj.m_stringProp = "This is a test"; + QCOMPARE(p->stringProp(), expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropDirectWrite() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QString expectedValue = "This is a value"; + p->setStringProp(expectedValue); + QCOMPARE(targetObj.m_stringProp, expectedValue); +} + +void tst_QDBusAbstractInterface::variantPropDirectRead() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusVariant expectedValue = targetObj.m_variantProp = QDBusVariant(42); + QCOMPARE(p->variantProp().variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::variantPropDirectWrite() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusVariant expectedValue = QDBusVariant(Q_INT64_C(-47)); + p->setVariantProp(expectedValue); + QCOMPARE(targetObj.m_variantProp.variant().userType(), expectedValue.variant().userType()); + QCOMPARE(targetObj.m_variantProp.variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::complexPropDirectRead() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + RegisteredType expectedValue = targetObj.m_complexProp = RegisteredType("This is a test"); + QCOMPARE(p->complexProp(), targetObj.m_complexProp); +} + +void tst_QDBusAbstractInterface::complexPropDirectWrite() +{ + Pinger p = getPinger(); + QVERIFY2(p, "Not connected to D-Bus"); + + RegisteredType expectedValue = RegisteredType("This is a value"); + p->setComplexProp(expectedValue); + QCOMPARE(targetObj.m_complexProp, expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropDirectReadPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QString expectedValue = "This is a test"; + QCOMPARE(p->stringProp(), expectedValue); +} + +void tst_QDBusAbstractInterface::stringPropDirectWritePeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QString expectedValue = "This is a value"; + p->setStringProp(expectedValue); + QCOMPARE(targetObj.m_stringProp, expectedValue); +} + +void tst_QDBusAbstractInterface::variantPropDirectReadPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QDBusVariant expectedValue = QDBusVariant(42); + QCOMPARE(p->variantProp().variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::variantPropDirectWritePeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + QDBusVariant expectedValue = QDBusVariant(Q_INT64_C(-47)); + p->setVariantProp(expectedValue); + QCOMPARE(targetObj.m_variantProp.variant().userType(), expectedValue.variant().userType()); + QCOMPARE(targetObj.m_variantProp.variant(), expectedValue.variant()); +} + +void tst_QDBusAbstractInterface::complexPropDirectReadPeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + RegisteredType expectedValue = RegisteredType("This is a test"); + QCOMPARE(p->complexProp(), expectedValue); +} + +void tst_QDBusAbstractInterface::complexPropDirectWritePeer() +{ + Pinger p = getPingerPeer(); + QVERIFY2(p, "Not connected to D-Bus"); + resetServer(); + + RegisteredType expectedValue = RegisteredType("This is a value"); + p->setComplexProp(expectedValue); + QCOMPARE(targetObj.m_complexProp, expectedValue); +} + +void tst_QDBusAbstractInterface::getVoidSignal_data() +{ + QTest::addColumn<QString>("service"); + QTest::addColumn<QString>("path"); + + QTest::newRow("specific") << QDBusConnection::sessionBus().baseService() << "/"; + QTest::newRow("service-wildcard") << QString() << "/"; + QTest::newRow("path-wildcard") << QDBusConnection::sessionBus().baseService() << QString(); + QTest::newRow("full-wildcard") << QString() << QString(); +} + +void tst_QDBusAbstractInterface::getVoidSignal() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(voidSignal()), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(voidSignal())); + + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(s.size() == 1); + QVERIFY(s.at(0).size() == 0); +} + +void tst_QDBusAbstractInterface::getStringSignal_data() +{ + getVoidSignal_data(); +} + +void tst_QDBusAbstractInterface::getStringSignal() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(stringSignal(QString)), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(stringSignal(QString))); + + QString expectedValue = "Good morning"; + emit targetObj.stringSignal(expectedValue); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(s.size() == 1); + QVERIFY(s[0].size() == 1); + QCOMPARE(s[0][0].userType(), int(QVariant::String)); + QCOMPARE(s[0][0].toString(), expectedValue); +} + +void tst_QDBusAbstractInterface::getComplexSignal_data() +{ + getVoidSignal_data(); +} + +void tst_QDBusAbstractInterface::getComplexSignal() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(complexSignal(RegisteredType)), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(complexSignal(RegisteredType))); + + RegisteredType expectedValue("Good evening"); + emit targetObj.complexSignal(expectedValue); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(s.size() == 1); + QVERIFY(s[0].size() == 1); + QCOMPARE(s[0][0].userType(), qMetaTypeId<RegisteredType>()); + QCOMPARE(s[0][0].value<RegisteredType>(), expectedValue); +} + +void tst_QDBusAbstractInterface::getVoidSignalPeer_data() +{ + QTest::addColumn<QString>("path"); + + QTest::newRow("specific") << "/"; + QTest::newRow("wildcard") << QString(); +} + +void tst_QDBusAbstractInterface::getVoidSignalPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(voidSignal()), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(voidSignal())); + + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "voidSignal"); + QVERIFY(QDBusConnection::sessionBus().send(req)); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(s.size() == 1); + QVERIFY(s.at(0).size() == 0); +} + +void tst_QDBusAbstractInterface::getStringSignalPeer_data() +{ + getVoidSignalPeer_data(); +} + +void tst_QDBusAbstractInterface::getStringSignalPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(stringSignal(QString)), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(stringSignal(QString))); + + QString expectedValue = "Good morning"; + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "stringSignal"); + req << expectedValue; + QVERIFY(QDBusConnection::sessionBus().send(req)); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(s.size() == 1); + QVERIFY(s[0].size() == 1); + QCOMPARE(s[0][0].userType(), int(QVariant::String)); + QCOMPARE(s[0][0].toString(), expectedValue); +} + +void tst_QDBusAbstractInterface::getComplexSignalPeer_data() +{ + getVoidSignalPeer_data(); +} + +void tst_QDBusAbstractInterface::getComplexSignalPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(complexSignal(RegisteredType)), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(complexSignal(RegisteredType))); + + RegisteredType expectedValue("Good evening"); + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "complexSignal"); + req << "Good evening"; + QVERIFY(QDBusConnection::sessionBus().send(req)); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(s.size() == 1); + QVERIFY(s[0].size() == 1); + QCOMPARE(s[0][0].userType(), qMetaTypeId<RegisteredType>()); + QCOMPARE(s[0][0].value<RegisteredType>(), expectedValue); +} + +void tst_QDBusAbstractInterface::followSignal() +{ + const QString serviceToFollow = "com.trolltech.tst_qdbusabstractinterface.FollowMe"; + Pinger p = getPinger(serviceToFollow); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusConnection con = p->connection(); + QVERIFY(!con.interface()->isServiceRegistered(serviceToFollow)); + Pinger control = getPinger(""); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(voidSignal()), SLOT(exitLoop())); + QTestEventLoop::instance().connect(control.data(), SIGNAL(voidSignal()), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(voidSignal())); + + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(200); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // signal must not have been received because the service isn't registered + QVERIFY(s.isEmpty()); + + // now register the service + QDBusReply<QDBusConnectionInterface::RegisterServiceReply> r = + con.interface()->registerService(serviceToFollow, QDBusConnectionInterface::DontQueueService, + QDBusConnectionInterface::DontAllowReplacement); + QVERIFY(r.isValid() && r.value() == QDBusConnectionInterface::ServiceRegistered); + QVERIFY(con.interface()->isServiceRegistered(serviceToFollow)); + QCoreApplication::instance()->processEvents(); + + // emit the signal again: + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // now the signal must have been received: + QCOMPARE(s.size(), 1); + QVERIFY(s.at(0).size() == 0); + s.clear(); + + // disconnect the signal + disconnect(p.data(), SIGNAL(voidSignal()), &QTestEventLoop::instance(), 0); + + // emit the signal again: + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // and now it mustn't have been received + QVERIFY(s.isEmpty()); + + // cleanup: + con.interface()->unregisterService(serviceToFollow); +} + +void tst_QDBusAbstractInterface::createErrors_data() +{ + QTest::addColumn<QString>("service"); + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("errorName"); + + QTest::newRow("invalid-service") << "this isn't valid" << "/" << "com.trolltech.QtDBus.Error.InvalidService"; + QTest::newRow("invalid-path") << QDBusConnection::sessionBus().baseService() << "this isn't valid" + << "com.trolltech.QtDBus.Error.InvalidObjectPath"; +} + +void tst_QDBusAbstractInterface::createErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + QVERIFY(!p->isValid()); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::createErrorsPeer_data() +{ + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("errorName"); + + QTest::newRow("invalid-path") << "this isn't valid" << "com.trolltech.QtDBus.Error.InvalidObjectPath"; +} + +void tst_QDBusAbstractInterface::createErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + QVERIFY(!p->isValid()); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::callErrors_data() +{ + createErrors_data(); + QTest::newRow("service-wildcard") << QString() << "/" << "com.trolltech.QtDBus.Error.InvalidService"; + QTest::newRow("path-wildcard") << QDBusConnection::sessionBus().baseService() << QString() + << "com.trolltech.QtDBus.Error.InvalidObjectPath"; + QTest::newRow("full-wildcard") << QString() << QString() << "com.trolltech.QtDBus.Error.InvalidService"; +} + +void tst_QDBusAbstractInterface::callErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to make this call: + QDBusReply<QString> r = p->stringMethod(); + QVERIFY(!r.isValid()); + QTEST(r.error().name(), "errorName"); + QCOMPARE(p->lastError().name(), r.error().name()); +} + +void tst_QDBusAbstractInterface::asyncCallErrors_data() +{ + callErrors_data(); +} + +void tst_QDBusAbstractInterface::asyncCallErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to make this call: + QDBusPendingReply<QString> r = p->stringMethod(); + QVERIFY(r.isError()); + QTEST(r.error().name(), "errorName"); + QCOMPARE(p->lastError().name(), r.error().name()); +} + +void tst_QDBusAbstractInterface::callErrorsPeer_data() +{ + createErrorsPeer_data(); + QTest::newRow("path-wildcard") << QString() << "com.trolltech.QtDBus.Error.InvalidObjectPath"; +} + +void tst_QDBusAbstractInterface::callErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to make this call: + QDBusReply<QString> r = p->stringMethod(); + QVERIFY(!r.isValid()); + QTEST(r.error().name(), "errorName"); + QCOMPARE(p->lastError().name(), r.error().name()); +} + +void tst_QDBusAbstractInterface::asyncCallErrorsPeer_data() +{ + callErrorsPeer_data(); +} + +void tst_QDBusAbstractInterface::asyncCallErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to make this call: + QDBusPendingReply<QString> r = p->stringMethod(); + QVERIFY(r.isError()); + QTEST(r.error().name(), "errorName"); + QCOMPARE(p->lastError().name(), r.error().name()); +} + +void tst_QDBusAbstractInterface::propertyReadErrors_data() +{ + callErrors_data(); +} + +void tst_QDBusAbstractInterface::propertyReadErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + QVariant v = p->property("stringProp"); + QVERIFY(v.isNull()); + QVERIFY(!v.isValid()); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::propertyWriteErrors_data() +{ + callErrors_data(); +} + +void tst_QDBusAbstractInterface::propertyWriteErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + if (p->isValid()) + QCOMPARE(int(p->lastError().type()), int(QDBusError::NoError)); + QVERIFY(!p->setProperty("stringProp", "")); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::directPropertyReadErrors_data() +{ + callErrors_data(); +} + +void tst_QDBusAbstractInterface::directPropertyReadErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + QString v = p->stringProp(); + QVERIFY(v.isNull()); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::directPropertyWriteErrors_data() +{ + callErrors_data(); +} + +void tst_QDBusAbstractInterface::directPropertyWriteErrors() +{ + QFETCH(QString, service); + QFETCH(QString, path); + Pinger p = getPinger(service, path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + // but there's no direct way of verifying that the setting failed + if (p->isValid()) + QCOMPARE(int(p->lastError().type()), int(QDBusError::NoError)); + p->setStringProp(""); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::propertyReadErrorsPeer_data() +{ + callErrorsPeer_data(); +} + +void tst_QDBusAbstractInterface::propertyReadErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + QVariant v = p->property("stringProp"); + QVERIFY(v.isNull()); + QVERIFY(!v.isValid()); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::propertyWriteErrorsPeer_data() +{ + callErrorsPeer_data(); +} + +void tst_QDBusAbstractInterface::propertyWriteErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + if (p->isValid()) + QCOMPARE(int(p->lastError().type()), int(QDBusError::NoError)); + QVERIFY(!p->setProperty("stringProp", "")); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::directPropertyReadErrorsPeer_data() +{ + callErrorsPeer_data(); +} + +void tst_QDBusAbstractInterface::directPropertyReadErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + QString v = p->stringProp(); + QVERIFY(v.isNull()); + QTEST(p->lastError().name(), "errorName"); +} + +void tst_QDBusAbstractInterface::directPropertyWriteErrorsPeer_data() +{ + callErrorsPeer_data(); +} + +void tst_QDBusAbstractInterface::directPropertyWriteErrorsPeer() +{ + QFETCH(QString, path); + Pinger p = getPingerPeer(path); + QVERIFY2(p, "Not connected to D-Bus"); + + // we shouldn't be able to get this value: + // but there's no direct way of verifying that the setting failed + if (p->isValid()) + QCOMPARE(int(p->lastError().type()), int(QDBusError::NoError)); + p->setStringProp(""); + QTEST(p->lastError().name(), "errorName"); +} + +QTEST_MAIN(tst_QDBusAbstractInterface) +#include "tst_qdbusabstractinterface.moc" diff --git a/tests/auto/dbus/qdbusconnection/.gitignore b/tests/auto/dbus/qdbusconnection/.gitignore new file mode 100644 index 0000000000..2995d60244 --- /dev/null +++ b/tests/auto/dbus/qdbusconnection/.gitignore @@ -0,0 +1 @@ +tst_qdbusconnection diff --git a/tests/auto/dbus/qdbusconnection/qdbusconnection.pro b/tests/auto/dbus/qdbusconnection/qdbusconnection.pro new file mode 100644 index 0000000000..d33ab0d95b --- /dev/null +++ b/tests/auto/dbus/qdbusconnection/qdbusconnection.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +QT = core + +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusconnection.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp b/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp new file mode 100644 index 0000000000..6490bfe973 --- /dev/null +++ b/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp @@ -0,0 +1,1305 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qdebug.h> + +#include <QtTest/QtTest> +#include <QtDBus/QtDBus> + +class BaseObject: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.BaseObject") +public: + BaseObject(QObject *parent = 0) : QObject(parent) { } +public slots: + void anotherMethod() { } +}; + +class MyObject: public BaseObject +{ + Q_OBJECT +public slots: + void method(const QDBusMessage &msg); + +public: + static QString path; + int callCount; + MyObject(QObject *parent = 0) : BaseObject(parent), callCount(0) {} +}; + +void MyObject::method(const QDBusMessage &msg) +{ + path = msg.path(); + ++callCount; + //qDebug() << msg; +} + +class tst_QDBusConnection: public QObject +{ + Q_OBJECT + + int signalsReceived; +public slots: + void oneSlot() { ++signalsReceived; } + void exitLoop() { ++signalsReceived; QTestEventLoop::instance().exitLoop(); } + void secondCallWithCallback(); + +private slots: + void noConnection(); + void connectToBus(); + void connectToPeer(); + void connect(); + void send(); + void sendWithGui(); + void sendAsync(); + void sendSignal(); + + void registerObject_data(); + void registerObject(); + void registerObjectPeer_data(); + void registerObjectPeer(); + void registerObject2(); + void registerObjectPeer2(); + + void registerQObjectChildren(); + void registerQObjectChildrenPeer(); + + void callSelf(); + void callSelfByAnotherName_data(); + void callSelfByAnotherName(); + void multipleInterfacesInQObject(); + + void slotsWithLessParameters(); + void nestedCallWithCallback(); + + 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); + bool callMethodPeer(const QDBusConnection &conn, const QString &path); +}; + +class QDBusSpy: public QObject +{ + Q_OBJECT +public slots: + void handlePing(const QString &str) { args.clear(); args << str; } + void asyncReply(const QDBusMessage &msg) { args = msg.arguments(); } + +public: + QList<QVariant> args; +}; + +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); + QVERIFY(reply.type() == QDBusMessage::ErrorMessage); + + QDBusReply<void> 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)); + + QTest::qWait(1000); +} + +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() +{ + 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() +{ + 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)))); + + QTest::qWait(1000); + + 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(); + + 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)); + + QTest::qWait(1000); + + QCOMPARE(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()); + } + + QDBusServer server("unix:tmpdir=/tmp", 0); + + { + QDBusConnection con = QDBusConnection::connectToPeer( + "unix:abstract=/tmp/dbus-XXXXXXXXXX,guid=00000000000000000000000000000000", "newconn2"); + QVERIFY(!con.isConnected()); + QVERIFY(con.lastError().isValid()); + } + + { + 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<QString>("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<QObject *>(&obj)); + QVERIFY(callMethod(con, path)); + QCOMPARE(obj.path, path); + } + // make sure it's gone + QVERIFY(!callMethod(con, path)); +} + +class MyServer : public QDBusServer +{ + Q_OBJECT +public: + MyServer(QString path, QString addr, QObject* parent) : QDBusServer(addr, parent), + m_path(path), + m_conn("none") + { + connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&))); + } + + bool registerObject() + { + if( !m_conn.registerObject(m_path, &m_obj, QDBusConnection::ExportAllSlots) ) + return false; + if(! (m_conn.objectRegisteredAt(m_path) == &m_obj)) + return false; + return true; + } + + void unregisterObject() + { + m_conn.unregisterObject(m_path); + } + +public slots: + void handleConnection(const QDBusConnection& c) + { + m_conn = c; + QVERIFY(isConnected()); + QVERIFY(m_conn.isConnected()); + QVERIFY(registerObject()); + } + +private: + MyObject m_obj; + QString m_path; + QDBusConnection m_conn; +}; + + +void tst_QDBusConnection::registerObjectPeer_data() +{ + QTest::addColumn<QString>("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() +{ + QFETCH(QString, path); + + MyServer server(path, "unix:tmpdir=/tmp", 0); + + { + QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo"); + + QCoreApplication::processEvents(); + QVERIFY(con.isConnected()); + + MyObject obj; + QVERIFY(callMethodPeer(con, path)); + QCOMPARE(obj.path, path); + } + + { + QDBusConnection con("foo"); + QVERIFY(con.isConnected()); + QVERIFY(callMethodPeer(con, path)); + } + + server.unregisterObject(); + + { + QDBusConnection con("foo"); + QVERIFY(con.isConnected()); + QVERIFY(!callMethodPeer(con, path)); + } + + server.registerObject(); + + { + QDBusConnection con("foo"); + QVERIFY(con.isConnected()); + QVERIFY(callMethodPeer(con, path)); + } + + QDBusConnection::disconnectFromPeer("foo"); + + { + QDBusConnection con("foo"); + QVERIFY(!con.isConnected()); + QVERIFY(!callMethodPeer(con, path)); + } +} + +void tst_QDBusConnection::registerObject2() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + // make sure nothing is using our paths: + QVERIFY(!callMethod(con, "/")); + QVERIFY(!callMethod(con, "/p1")); + QVERIFY(!callMethod(con, "/p2")); + QVERIFY(!callMethod(con, "/p1/q")); + QVERIFY(!callMethod(con, "/p1/q/r")); + + { + // register one object at root: + MyObject obj; + QVERIFY(con.registerObject("/", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethod(con, "/")); + qDebug() << obj.path; + QCOMPARE(obj.path, QString("/")); + } + // make sure it's gone + QVERIFY(!callMethod(con, "/")); + + { + // register one at an element: + MyObject obj; + QVERIFY(con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(!callMethod(con, "/")); + QVERIFY(callMethod(con, "/p1")); + qDebug() << obj.path; + QCOMPARE(obj.path, QString("/p1")); + + // re-register it somewhere else + QVERIFY(con.registerObject("/p2", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethod(con, "/p1")); + QCOMPARE(obj.path, QString("/p1")); + QVERIFY(callMethod(con, "/p2")); + QCOMPARE(obj.path, QString("/p2")); + } + // make sure it's gone + QVERIFY(!callMethod(con, "/p1")); + QVERIFY(!callMethod(con, "/p2")); + + { + // register at a deep path + MyObject obj; + QVERIFY(con.registerObject("/p1/q/r", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(!callMethod(con, "/")); + QVERIFY(!callMethod(con, "/p1")); + QVERIFY(!callMethod(con, "/p1/q")); + QVERIFY(callMethod(con, "/p1/q/r")); + QCOMPARE(obj.path, QString("/p1/q/r")); + } + // make sure it's gone + QVERIFY(!callMethod(con, "/p1/q/r")); + + { + MyObject obj; + QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethod(con, "/p1/q2")); + QCOMPARE(obj.path, QString("/p1/q2")); + + // try unregistering + con.unregisterObject("/p1/q2"); + QVERIFY(!callMethod(con, "/p1/q2")); + + // register it again + QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethod(con, "/p1/q2")); + QCOMPARE(obj.path, QString("/p1/q2")); + + // now try removing things around it: + con.unregisterObject("/p2"); + QVERIFY(callMethod(con, "/p1/q2")); // unrelated object shouldn't affect + + con.unregisterObject("/p1"); + QVERIFY(callMethod(con, "/p1/q2")); // unregistering just the parent shouldn't affect it + + con.unregisterObject("/p1/q2/r"); + QVERIFY(callMethod(con, "/p1/q2")); // unregistering non-existing child shouldn't affect it either + + con.unregisterObject("/p1/q"); + QVERIFY(callMethod(con, "/p1/q2")); // unregistering sibling (before) shouldn't affect + + con.unregisterObject("/p1/r"); + QVERIFY(callMethod(con, "/p1/q2")); // unregistering sibling (after) shouldn't affect + + // now remove it: + con.unregisterObject("/p1", QDBusConnection::UnregisterTree); + QVERIFY(!callMethod(con, "/p1/q2")); // we removed the full tree + } +} + +class MyServer2 : public QDBusServer +{ + Q_OBJECT +public: + MyServer2(QString addr, QObject* parent) : QDBusServer(addr, parent), + m_conn("none") + { + connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&))); + } + + QDBusConnection connection() + { + return m_conn; + } + +public slots: + void handleConnection(const QDBusConnection& c) + { + m_conn = c; + QVERIFY(isConnected()); + QVERIFY(m_conn.isConnected()); + } + +private: + MyObject m_obj; + QDBusConnection m_conn; +}; + +void tst_QDBusConnection::registerObjectPeer2() +{ + MyServer2 server("unix:tmpdir=/tmp", 0); + QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo"); + QCoreApplication::processEvents(); + QVERIFY(con.isConnected()); + + QDBusConnection srv_con = server.connection(); + + // make sure nothing is using our paths: + QVERIFY(!callMethodPeer(srv_con, "/")); + QVERIFY(!callMethodPeer(srv_con, "/p1")); + QVERIFY(!callMethodPeer(srv_con, "/p2")); + QVERIFY(!callMethodPeer(srv_con, "/p1/q")); + QVERIFY(!callMethodPeer(srv_con, "/p1/q/r")); + + { + // register one object at root: + MyObject obj; + QVERIFY(con.registerObject("/", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethodPeer(srv_con, "/")); + qDebug() << obj.path; + QCOMPARE(obj.path, QString("/")); + } + // make sure it's gone + QVERIFY(!callMethodPeer(srv_con, "/")); + + { + // register one at an element: + MyObject obj; + QVERIFY(con.registerObject("/p1", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(!callMethodPeer(srv_con, "/")); + QVERIFY(callMethodPeer(srv_con, "/p1")); + qDebug() << obj.path; + QCOMPARE(obj.path, QString("/p1")); + + // re-register it somewhere else + QVERIFY(con.registerObject("/p2", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethodPeer(srv_con, "/p1")); + QCOMPARE(obj.path, QString("/p1")); + QVERIFY(callMethodPeer(srv_con, "/p2")); + QCOMPARE(obj.path, QString("/p2")); + } + // make sure it's gone + QVERIFY(!callMethodPeer(srv_con, "/p1")); + QVERIFY(!callMethodPeer(srv_con, "/p2")); + + { + // register at a deep path + MyObject obj; + QVERIFY(con.registerObject("/p1/q/r", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(!callMethodPeer(srv_con, "/")); + QVERIFY(!callMethodPeer(srv_con, "/p1")); + QVERIFY(!callMethodPeer(srv_con, "/p1/q")); + QVERIFY(callMethodPeer(srv_con, "/p1/q/r")); + QCOMPARE(obj.path, QString("/p1/q/r")); + } + // make sure it's gone + QVERIFY(!callMethodPeer(srv_con, "/p1/q/r")); + + { + MyObject obj; + QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); + QCOMPARE(obj.path, QString("/p1/q2")); + + // try unregistering + con.unregisterObject("/p1/q2"); + QVERIFY(!callMethodPeer(srv_con, "/p1/q2")); + + // register it again + QVERIFY(con.registerObject("/p1/q2", &obj, QDBusConnection::ExportAllSlots)); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); + QCOMPARE(obj.path, QString("/p1/q2")); + + // now try removing things around it: + con.unregisterObject("/p2"); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unrelated object shouldn't affect + + con.unregisterObject("/p1"); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering just the parent shouldn't affect it + + con.unregisterObject("/p1/q2/r"); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering non-existing child shouldn't affect it either + + con.unregisterObject("/p1/q"); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering sibling (before) shouldn't affect + + con.unregisterObject("/p1/r"); + QVERIFY(callMethodPeer(srv_con, "/p1/q2")); // unregistering sibling (after) shouldn't affect + + // now remove it: + con.unregisterObject("/p1", QDBusConnection::UnregisterTree); + QVERIFY(!callMethodPeer(srv_con, "/p1/q2")); // we removed the full tree + } + + QDBusConnection::disconnectFromPeer("foo"); +} + + +void tst_QDBusConnection::registerQObjectChildren() +{ + // make sure no one is there + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(!callMethod(con, "/p1")); + + { + 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(callMethod(con, "/p1/a")); + QCOMPARE(a->callCount, 1); + QVERIFY(callMethod(con, "/p1/b")); + QCOMPARE(b->callCount, 1); + QVERIFY(callMethod(con, "/p1/c")); + QCOMPARE(c->callCount, 1); + QVERIFY(callMethod(con, "/p1/c/cc")); + QCOMPARE(cc->callCount, 1); + + QVERIFY(!callMethod(con, "/p1/d")); + QVERIFY(!callMethod(con, "/p1/c/abc")); + + // pull an object, see if it goes away: + delete b; + QVERIFY(!callMethod(con, "/p1/b")); + + delete c; + QVERIFY(!callMethod(con, "/p1/c")); + QVERIFY(!callMethod(con, "/p1/c/cc")); + } + + QVERIFY(!callMethod(con, "/p1")); + QVERIFY(!callMethod(con, "/p1/a")); + QVERIFY(!callMethod(con, "/p1/b")); + QVERIFY(!callMethod(con, "/p1/c")); + QVERIFY(!callMethod(con, "/p1/c/cc")); +} + +void tst_QDBusConnection::registerQObjectChildrenPeer() +{ + MyServer2 server("unix:tmpdir=/tmp", 0); + QDBusConnection con = QDBusConnection::connectToPeer(server.address(), "foo"); + QCoreApplication::processEvents(); + QVERIFY(con.isConnected()); + + QDBusConnection srv_con = server.connection(); + + QVERIFY(!callMethodPeer(srv_con, "/p1")); + + { + 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(callMethodPeer(srv_con, "/p1/a")); + QCOMPARE(a->callCount, 1); + QVERIFY(callMethodPeer(srv_con, "/p1/b")); + QCOMPARE(b->callCount, 1); + QVERIFY(callMethodPeer(srv_con, "/p1/c")); + QCOMPARE(c->callCount, 1); + QVERIFY(callMethodPeer(srv_con, "/p1/c/cc")); + QCOMPARE(cc->callCount, 1); + + QVERIFY(!callMethodPeer(srv_con, "/p1/d")); + QVERIFY(!callMethodPeer(srv_con, "/p1/c/abc")); + + // pull an object, see if it goes away: + delete b; + QVERIFY(!callMethodPeer(srv_con, "/p1/b")); + + delete c; + QVERIFY(!callMethodPeer(srv_con, "/p1/c")); + QVERIFY(!callMethodPeer(srv_con, "/p1/c/cc")); + } + + QVERIFY(!callMethodPeer(srv_con, "/p1")); + QVERIFY(!callMethodPeer(srv_con, "/p1/a")); + QVERIFY(!callMethodPeer(srv_con, "/p1/b")); + QVERIFY(!callMethodPeer(srv_con, "/p1/c")); + QVERIFY(!callMethodPeer(srv_con, "/p1/c/cc")); + + 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; + if (MyObject::path == path) { + QTest::compare_helper(true, "COMPARE()", __FILE__, __LINE__); + } else { + QTest::compare_helper(false, "Compared values are not the same", + QTest::toString(MyObject::path), QTest::toString(path), + "MyObject::path", "path", __FILE__, __LINE__); + return false; + } + + return true; +} + +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; + if (MyObject::path == path) { + QTest::compare_helper(true, "COMPARE()", __FILE__, __LINE__); + } else { + QTest::compare_helper(false, "Compared values are not the same", + QTest::toString(MyObject::path), QTest::toString(path), + "MyObject::path", "path", __FILE__, __LINE__); + return false; + } + + return true; +} + +class TestObject : public QObject +{ +Q_OBJECT +public: + TestObject(QObject *parent = 0) : QObject(parent) {} + ~TestObject() {} + + QString func; + +public slots: + void test0() { func = "test0"; } + void test1(int i) { func = "test1 " + QString::number(i); } + int test2() { func = "test2"; return 43; } + int test3(int i) { func = "test2"; return i + 1; } +}; + +void tst_QDBusConnection::callSelf() +{ + TestObject testObject; + QDBusConnection connection = QDBusConnection::sessionBus(); + QVERIFY(connection.registerObject("/test", &testObject, + QDBusConnection::ExportAllContents)); + QCOMPARE(connection.objectRegisteredAt("/test"), static_cast<QObject *>(&testObject)); + QVERIFY(connection.registerService(serviceName())); + QDBusInterface interface(serviceName(), "/test"); + QVERIFY(interface.isValid()); + + interface.call(QDBus::Block, "test0"); + QCOMPARE(testObject.func, QString("test0")); + interface.call(QDBus::Block, "test1", 42); + QCOMPARE(testObject.func, QString("test1 42")); + QDBusMessage reply = interface.call(QDBus::Block, "test2"); + QCOMPARE(testObject.func, QString("test2")); + QCOMPARE(reply.arguments().value(0).toInt(), 43); + + QDBusMessage msg = QDBusMessage::createMethodCall(serviceName(), "/test", + QString(), "test3"); + msg << 44; + reply = connection.call(msg); + QCOMPARE(reply.arguments().value(0).toInt(), 45); +} + +void tst_QDBusConnection::callSelfByAnotherName_data() +{ + QTest::addColumn<int>("registerMethod"); + QTest::newRow("connection") << 0; + QTest::newRow("connection-interface") << 1; + QTest::newRow("direct") << 2; +} + +void tst_QDBusConnection::callSelfByAnotherName() +{ + 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: + QVERIFY(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<uint> reply = con.interface()->call("RequestName", sname, 4u); + QVERIFY(reply.value() == 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); + + QVERIFY(reply.type() == QDBusMessage::ReplyMessage); +} + +void tst_QDBusConnection::multipleInterfacesInQObject() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(!callMethod(con, "/p1")); + + 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); + QVERIFY(reply.arguments().count() == 0); +} + +void tst_QDBusConnection::slotsWithLessParameters() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + + QDBusMessage signal = QDBusMessage::createSignal("/", "com.trolltech.TestCase", + "oneSignal"); + signal << "one parameter"; + + signalsReceived = 0; + QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(), + signal.member(), this, SLOT(oneSlot()))); + QVERIFY(con.send(signal)); + QTest::qWait(100); + QCOMPARE(signalsReceived, 1); + + // disconnect and try with a signature + signalsReceived = 0; + QVERIFY(con.disconnect(con.baseService(), signal.path(), signal.interface(), + signal.member(), this, SLOT(oneSlot()))); + QVERIFY(con.connect(con.baseService(), signal.path(), signal.interface(), + signal.member(), "s", this, SLOT(oneSlot()))); + QVERIFY(con.send(signal)); + QTest::qWait(100); + QCOMPARE(signalsReceived, 1); +} + +void tst_QDBusConnection::secondCallWithCallback() +{ + qDebug("Hello"); + 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() +{ + TestObject testObject; + QDBusConnection connection = QDBusConnection::sessionBus(); + QVERIFY(connection.registerObject("/test", &testObject, + QDBusConnection::ExportAllContents)); + + QDBusMessage msg = QDBusMessage::createMethodCall(connection.baseService(), "/test", QString(), + "ThisFunctionDoesntExist"); + signalsReceived = 0; + + connection.callWithCallback(msg, this, SLOT(exitLoop()), SLOT(secondCallWithCallback()), 10); + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(signalsReceived, 1); +} + +class RaceConditionSignalWaiter : public QObject +{ + Q_OBJECT +public: + int count; + RaceConditionSignalWaiter() : count (0) {} + virtual ~RaceConditionSignalWaiter() {} + +public slots: + void countUp() { ++count; emit done(); } +signals: + void done(); +}; + +void tst_QDBusConnection::serviceRegistrationRaceCondition() +{ + // There was a race condition in the updating of list of name owners in + // QtDBus. 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 QtDBus + // 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, "/", "com.trolltech.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("/", "com.trolltech.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); +} + +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) + +#include "tst_qdbusconnection.moc" + diff --git a/tests/auto/dbus/qdbusconnection_no_bus/qdbusconnection_no_bus.pro b/tests/auto/dbus/qdbusconnection_no_bus/qdbusconnection_no_bus.pro new file mode 100644 index 0000000000..86d08ea0d4 --- /dev/null +++ b/tests/auto/dbus/qdbusconnection_no_bus/qdbusconnection_no_bus.pro @@ -0,0 +1,9 @@ +load(qttest_p4) +QT = core + +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusconnection_no_bus.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} diff --git a/tests/auto/dbus/qdbusconnection_no_bus/tst_qdbusconnection_no_bus.cpp b/tests/auto/dbus/qdbusconnection_no_bus/tst_qdbusconnection_no_bus.cpp new file mode 100644 index 0000000000..acd27386f5 --- /dev/null +++ b/tests/auto/dbus/qdbusconnection_no_bus/tst_qdbusconnection_no_bus.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qdebug.h> + +#include <QtTest/QtTest> +#include <QtDBus/QtDBus> + +#include <stdlib.h> + +/* This test uses an appless main, to ensure that no D-Bus stuff is implicitly done + It also sets the magic "QT_SIMULATE_DBUS_LIBFAIL" env variable, that is only available + in developer builds. That env variable simulates a D-Bus library load fail. + + In no case should the QDBus module crash because D-Bus libs couldn't be loaded */ + +class tst_QDBusConnectionNoBus : public QObject +{ + Q_OBJECT + +public: + tst_QDBusConnectionNoBus() + { + ::setenv("DBUS_SESSION_BUS_ADDRESS", "unix:abstract=/tmp/does_not_exist", 1); + ::setenv("QT_SIMULATE_DBUS_LIBFAIL", "1", 1); + } + +private slots: + void connectToBus(); +}; + + +void tst_QDBusConnectionNoBus::connectToBus() +{ + int argc = 0; + QCoreApplication app(argc, 0); + + QDBusConnection con = QDBusConnection::sessionBus(); + + QVERIFY(true); // if we didn't crash here, the test passed :) +} + +QTEST_APPLESS_MAIN(tst_QDBusConnectionNoBus) + +#include "tst_qdbusconnection_no_bus.moc" + diff --git a/tests/auto/dbus/qdbuscontext/.gitignore b/tests/auto/dbus/qdbuscontext/.gitignore new file mode 100644 index 0000000000..64fff94d76 --- /dev/null +++ b/tests/auto/dbus/qdbuscontext/.gitignore @@ -0,0 +1 @@ +tst_qdbuscontext diff --git a/tests/auto/dbus/qdbuscontext/qdbuscontext.pro b/tests/auto/dbus/qdbuscontext/qdbuscontext.pro new file mode 100644 index 0000000000..ddd340c37e --- /dev/null +++ b/tests/auto/dbus/qdbuscontext/qdbuscontext.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +QT = core + +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbuscontext.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbuscontext/tst_qdbuscontext.cpp b/tests/auto/dbus/qdbuscontext/tst_qdbuscontext.cpp new file mode 100644 index 0000000000..8041708151 --- /dev/null +++ b/tests/auto/dbus/qdbuscontext/tst_qdbuscontext.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtDBus> +#include <QtTest> + +const char errorName[] = "com.trolltech.tst_QDBusContext.Error"; +const char errorMsg[] = "A generic error"; + +class TestObject: public QObject, protected QDBusContext +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.tst_QDBusContext.TestObject") +public: + inline TestObject(QObject *parent) : QObject(parent) { } +public Q_SLOTS: + void generateError(); +}; + +class tst_QDBusContext: public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void sendErrorReply(); +}; + +void TestObject::generateError() +{ + sendErrorReply(errorName, errorMsg); +} + +void tst_QDBusContext::initTestCase() +{ + TestObject *obj = new TestObject(this); + QVERIFY(QDBusConnection::sessionBus().isConnected()); + QVERIFY(QDBusConnection::sessionBus().registerObject("/TestObject", obj, + QDBusConnection::ExportAllSlots)); +} + +void tst_QDBusContext::sendErrorReply() +{ + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), "/TestObject"); + QVERIFY(iface.isValid()); + + QDBusReply<void> reply = iface.call("generateError"); + QVERIFY(!reply.isValid()); + + const QDBusError &error = reply.error(); + QCOMPARE(error.name(), QString::fromLatin1(errorName)); + QCOMPARE(error.message(), QString::fromLatin1(errorMsg)); +} + +QTEST_MAIN(tst_QDBusContext) + +#include "tst_qdbuscontext.moc" diff --git a/tests/auto/dbus/qdbusinterface/.gitignore b/tests/auto/dbus/qdbusinterface/.gitignore new file mode 100644 index 0000000000..98313ecf04 --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/.gitignore @@ -0,0 +1 @@ +tst_qdbusinterface diff --git a/tests/auto/dbus/qdbusinterface/myobject.h b/tests/auto/dbus/qdbusinterface/myobject.h new file mode 100644 index 0000000000..3959823a77 --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/myobject.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MYOBJECT_H +#define MYOBJECT_H + +#include <QtCore/QObject> +#include <QtDBus/QtDBus> + +Q_DECLARE_METATYPE(QVariantList) + +class MyObject: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.MyObject") + Q_CLASSINFO("D-Bus Introspection", "" +" <interface name=\"com.trolltech.QtDBus.MyObject\" >\n" +" <property access=\"readwrite\" type=\"i\" name=\"prop1\" />\n" +" <property name=\"complexProp\" type=\"ai\" access=\"readwrite\">\n" +" <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"QList<int>\"/>\n" +" </property>\n" +" <signal name=\"somethingHappened\" >\n" +" <arg direction=\"out\" type=\"s\" />\n" +" </signal>\n" +" <method name=\"ping\" >\n" +" <arg direction=\"in\" type=\"v\" name=\"ping\" />\n" +" <arg direction=\"out\" type=\"v\" name=\"ping\" />\n" +" </method>\n" +" <method name=\"ping_invokable\" >\n" +" <arg direction=\"in\" type=\"v\" name=\"ping_invokable\" />\n" +" <arg direction=\"out\" type=\"v\" name=\"ping_invokable\" />\n" +" </method>\n" +" <method name=\"ping\" >\n" +" <arg direction=\"in\" type=\"v\" name=\"ping1\" />\n" +" <arg direction=\"in\" type=\"v\" name=\"ping2\" />\n" +" <arg direction=\"out\" type=\"v\" name=\"pong1\" />\n" +" <arg direction=\"out\" type=\"v\" name=\"pong2\" />\n" +" </method>\n" +" <method name=\"ping_invokable\" >\n" +" <arg direction=\"in\" type=\"v\" name=\"ping1_invokable\" />\n" +" <arg direction=\"in\" type=\"v\" name=\"ping2_invokable\" />\n" +" <arg direction=\"out\" type=\"v\" name=\"pong1_invokable\" />\n" +" <arg direction=\"out\" type=\"v\" name=\"pong2_invokable\" />\n" +" </method>\n" +" <method name=\"ping\" >\n" +" <arg direction=\"in\" type=\"ai\" name=\"ping\" />\n" +" <arg direction=\"out\" type=\"ai\" name=\"ping\" />\n" +" <annotation name=\"com.trolltech.QtDBus.QtTypeName.In0\" value=\"QList<int>\"/>\n" +" <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QList<int>\"/>\n" +" </method>\n" +" <method name=\"ping_invokable\" >\n" +" <arg direction=\"in\" type=\"ai\" name=\"ping_invokable\" />\n" +" <arg direction=\"out\" type=\"ai\" name=\"ping_invokable\" />\n" +" <annotation name=\"com.trolltech.QtDBus.QtTypeName.In0\" value=\"QList<int>\"/>\n" +" <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QList<int>\"/>\n" +" </method>\n" +" </interface>\n" + "") + Q_PROPERTY(int prop1 READ prop1 WRITE setProp1) + Q_PROPERTY(QList<int> complexProp READ complexProp WRITE setComplexProp) + +public: + static int callCount; + static QVariantList callArgs; + MyObject() + { + QObject *subObject = new QObject(this); + subObject->setObjectName("subObject"); + } + + int m_prop1; + int prop1() const + { + ++callCount; + return m_prop1; + } + void setProp1(int value) + { + ++callCount; + m_prop1 = value; + } + + QList<int> m_complexProp; + QList<int> complexProp() const + { + ++callCount; + return m_complexProp; + } + void setComplexProp(const QList<int> &value) + { + ++callCount; + m_complexProp = value; + } + + Q_INVOKABLE void ping_invokable(QDBusMessage msg) + { + QDBusConnection sender = QDBusConnection::sender(); + if (!sender.isConnected()) + exit(1); + + ++callCount; + callArgs = msg.arguments(); + + msg.setDelayedReply(true); + if (!sender.send(msg.createReply(callArgs))) + exit(1); + } + +public slots: + + void ping(QDBusMessage msg) + { + QDBusConnection sender = QDBusConnection::sender(); + if (!sender.isConnected()) + exit(1); + + ++callCount; + callArgs = msg.arguments(); + + msg.setDelayedReply(true); + if (!sender.send(msg.createReply(callArgs))) + exit(1); + } +}; + +#endif // INTERFACE_H diff --git a/tests/auto/dbus/qdbusinterface/qdbusinterface.pro b/tests/auto/dbus/qdbusinterface/qdbusinterface.pro new file mode 100644 index 0000000000..0aca06ceb4 --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/qdbusinterface.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +QT = core +contains(QT_CONFIG,dbus): { + TEMPLATE = subdirs + CONFIG += ordered + SUBDIRS = qmyserver test +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusinterface/qmyserver/qmyserver.cpp b/tests/auto/dbus/qdbusinterface/qmyserver/qmyserver.cpp new file mode 100644 index 0000000000..dd15012222 --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/qmyserver/qmyserver.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QtCore> +#include <QtDBus/QtDBus> + +#include "../myobject.h" + +static const char serviceName[] = "com.trolltech.autotests.qmyserver"; +static const char objectPath[] = "/com/trolltech/qmyserver"; +//static const char *interfaceName = serviceName; + +int MyObject::callCount = 0; +QVariantList MyObject::callArgs; + +class MyServer : public QDBusServer +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.autotests.qmyserver") + +public: + MyServer(QString addr = "unix:tmpdir=/tmp", QObject* parent = 0) + : QDBusServer(addr, parent), + m_conn("none") + { + connect(this, SIGNAL(newConnection(const QDBusConnection&)), SLOT(handleConnection(const QDBusConnection&))); + } + +public slots: + QString address() const + { + return QDBusServer::address(); + } + + bool isConnected() const + { + return m_conn.isConnected(); + } + + void emitSignal(const QString &interface, const QString &name, const QString &arg) + { + QDBusMessage msg = QDBusMessage::createSignal("/", interface, name); + msg << arg; + m_conn.send(msg); + } + + void reset() + { + MyObject::callCount = 0; + obj.m_complexProp.clear(); + } + + int callCount() + { + return MyObject::callCount; + } + + QVariantList callArgs() + { + qDebug() << "callArgs" << MyObject::callArgs.count(); + return MyObject::callArgs; + } + + void setProp1(int val) + { + obj.m_prop1 = val; + } + + int prop1() + { + return obj.m_prop1; + } + + void setComplexProp(QList<int> val) + { + obj.m_complexProp = val; + } + + QList<int> complexProp() + { + return obj.m_complexProp; + } + + +private slots: + void handleConnection(const QDBusConnection& con) + { + m_conn = con; + m_conn.registerObject("/", &obj, QDBusConnection::ExportAllProperties + | QDBusConnection::ExportAllSlots + | QDBusConnection::ExportAllInvokables); + } + +private: + QDBusConnection m_conn; + MyObject obj; +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QDBusConnection con = QDBusConnection::sessionBus(); + if (!con.isConnected()) + exit(1); + + if (!con.registerService(serviceName)) + exit(2); + + MyServer server; + con.registerObject(objectPath, &server, QDBusConnection::ExportAllSlots); + + printf("ready.\n"); + + return app.exec(); +} + +#include "qmyserver.moc"
\ No newline at end of file diff --git a/tests/auto/dbus/qdbusinterface/qmyserver/qmyserver.pro b/tests/auto/dbus/qdbusinterface/qmyserver/qmyserver.pro new file mode 100644 index 0000000000..f4fe02c4eb --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/qmyserver/qmyserver.pro @@ -0,0 +1,5 @@ +SOURCES = qmyserver.cpp +HEADERS = ../myobject.h +TARGET = qmyserver +QT += dbus +QT -= gui diff --git a/tests/auto/dbus/qdbusinterface/test/test.pro b/tests/auto/dbus/qdbusinterface/test/test.pro new file mode 100644 index 0000000000..2ef7a89c56 --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/test/test.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES += ../tst_qdbusinterface.cpp +HEADERS += ../myobject.h +TARGET = ../tst_qdbusinterface + +QT = core core-private dbus diff --git a/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp b/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp new file mode 100644 index 0000000000..9156818158 --- /dev/null +++ b/tests/auto/dbus/qdbusinterface/tst_qdbusinterface.cpp @@ -0,0 +1,1139 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +/* -*- C++ -*- + */ + +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> +#include <QtCore/qvariant.h> +#include <QtDBus/QtDBus> + +#include "../qdbusmarshall/common.h" +#include "myobject.h" + +#define TEST_INTERFACE_NAME "com.trolltech.QtDBus.MyObject" +#define TEST_SIGNAL_NAME "somethingHappened" + +static const char serviceName[] = "com.trolltech.autotests.qmyserver"; +static const char objectPath[] = "/com/trolltech/qmyserver"; +static const char *interfaceName = serviceName; + +int MyObject::callCount = 0; +QVariantList MyObject::callArgs; + +class MyObjectUnknownType: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.MyObject") + Q_CLASSINFO("D-Bus Introspection", "" +" <interface name=\"com.trolltech.QtDBus.MyObjectUnknownTypes\" >\n" +" <property access=\"readwrite\" type=\"~\" name=\"prop1\" />\n" +" <signal name=\"somethingHappened\" >\n" +" <arg direction=\"out\" type=\"~\" />\n" +" </signal>\n" +" <method name=\"ping\" >\n" +" <arg direction=\"in\" type=\"~\" name=\"ping\" />\n" +" <arg direction=\"out\" type=\"~\" name=\"ping\" />\n" +" </method>\n" +" <method name=\"regularMethod\" />\n" +" </interface>\n" + "") +}; + +class Spy: public QObject +{ + Q_OBJECT +public: + QString received; + int count; + + Spy() : count(0) + { } + +public slots: + void spySlot(const QString& arg) + { + received = arg; + ++count; + } +}; + +class DerivedFromQDBusInterface: public QDBusInterface +{ + Q_OBJECT +public: + DerivedFromQDBusInterface() + : QDBusInterface("com.example.Test", "/") + {} + +public slots: + void method() {} +}; + +// helper function +void emitSignal(const QString &interface, const QString &name, const QString &arg) +{ + QDBusMessage msg = QDBusMessage::createSignal("/", interface, name); + msg << arg; + QDBusConnection::sessionBus().send(msg); + + QTest::qWait(1000); +} + +void emitSignalPeer(const QString &interface, const QString &name, const QString &arg) +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "emitSignal"); + req << interface; + req << name; + req << arg; + QDBusConnection::sessionBus().send(req); + + QTest::qWait(1000); +} + +int callCountPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "callCount"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); + return reply.arguments().at(0).toInt(); +} + +QVariantList callArgsPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "callArgs"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); + return qdbus_cast<QVariantList>(reply.arguments().at(0)); +} + +void setProp1Peer(int val) +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "setProp1"); + req << val; + QDBusMessage reply = QDBusConnection::sessionBus().call(req); +} + +int prop1Peer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "prop1"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); + return reply.arguments().at(0).toInt(); +} + +void setComplexPropPeer(QList<int> val) +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "setComplexProp"); + req << qVariantFromValue(val); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); +} + +QList<int> complexPropPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "complexProp"); + QDBusMessage reply = QDBusConnection::sessionBus().call(req); + return qdbus_cast<QList<int> >(reply.arguments().at(0)); +} + +void resetPeer() +{ + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "reset"); + QDBusConnection::sessionBus().call(req); +} + +class tst_QDBusInterface: public QObject +{ + Q_OBJECT + MyObject obj; + +public slots: + void testServiceOwnerChanged(const QString &service) + { + if (service == "com.example.Test") + QTestEventLoop::instance().exitLoop(); + } + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void notConnected(); + void notValid(); + void notValidDerived(); + void invalidAfterServiceOwnerChanged(); + void introspect(); + void introspectUnknownTypes(); + void introspectVirtualObject(); + void callMethod(); + void invokeMethod(); + void invokeMethodWithReturn(); + void invokeMethodWithMultiReturn(); + void invokeMethodWithComplexReturn(); + + void introspectPeer(); + void callMethodPeer(); + void invokeMethodPeer(); + void invokeMethodWithReturnPeer(); + void invokeMethodWithMultiReturnPeer(); + void invokeMethodWithComplexReturnPeer(); + + void signal(); + void signalPeer(); + + void propertyRead(); + void propertyWrite(); + void complexPropertyRead(); + void complexPropertyWrite(); + + void propertyReadPeer(); + void propertyWritePeer(); + void complexPropertyReadPeer(); + void complexPropertyWritePeer(); +private: + QProcess proc; +}; + +class WaitForQMyServer: public QObject +{ + Q_OBJECT +public: + WaitForQMyServer(); + bool ok(); +public Q_SLOTS: + void ownerChange(const QString &name) + { + if (name == serviceName) + loop.quit(); + } + +private: + QEventLoop loop; +}; + +WaitForQMyServer::WaitForQMyServer() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + if (!ok()) { + connect(con.interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), + SLOT(ownerChange(QString))); + QTimer::singleShot(2000, &loop, SLOT(quit())); + loop.exec(); + } +} + +bool WaitForQMyServer::ok() +{ + return QDBusConnection::sessionBus().isConnected() && + QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName); +} + +void tst_QDBusInterface::initTestCase() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + QTest::qWait(500); + + con.registerObject("/", &obj, QDBusConnection::ExportAllProperties + | QDBusConnection::ExportAllSlots + | QDBusConnection::ExportAllInvokables); + + // start peer server + #ifdef Q_OS_WIN + proc.start("qmyserver"); + #else + proc.start("./qmyserver/qmyserver"); + #endif + QVERIFY(proc.waitForStarted()); + + WaitForQMyServer w; + QVERIFY(w.ok()); + //QTest::qWait(2000); + + // get peer server address + QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "address"); + QDBusMessage rpl = con.call(req); + QVERIFY(rpl.type() == QDBusMessage::ReplyMessage); + QString address = rpl.arguments().at(0).toString(); + + // connect to peer server + QDBusConnection peercon = QDBusConnection::connectToPeer(address, "peer"); + QVERIFY(peercon.isConnected()); + + QDBusMessage req2 = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "isConnected"); + QDBusMessage rpl2 = con.call(req2); + QVERIFY(rpl2.type() == QDBusMessage::ReplyMessage); + QVERIFY(rpl2.arguments().at(0).toBool()); +} + +void tst_QDBusInterface::cleanupTestCase() +{ + proc.close(); + proc.kill(); +} + +void tst_QDBusInterface::notConnected() +{ + QDBusConnection connection(""); + QVERIFY(!connection.isConnected()); + + QDBusInterface interface("org.freedesktop.DBus", "/", "org.freedesktop.DBus", + connection); + + QVERIFY(!interface.isValid()); + QVERIFY(!QMetaObject::invokeMethod(&interface, "ListNames", Qt::DirectConnection)); +} + +void tst_QDBusInterface::notValid() +{ + QDBusConnection connection(""); + QVERIFY(!connection.isConnected()); + + QDBusInterface interface("com.example.Test", QString(), "org.example.Test", + connection); + + QVERIFY(!interface.isValid()); + QVERIFY(!QMetaObject::invokeMethod(&interface, "ListNames", Qt::DirectConnection)); +} + +void tst_QDBusInterface::notValidDerived() +{ + DerivedFromQDBusInterface c; + QVERIFY(!c.isValid()); + QMetaObject::invokeMethod(&c, "method", Qt::DirectConnection); +} + +void tst_QDBusInterface::invalidAfterServiceOwnerChanged() +{ + // this test is technically the same as tst_QDBusAbstractInterface::followSignal + QDBusConnection conn = QDBusConnection::sessionBus(); + QDBusConnectionInterface *connIface = conn.interface(); + + QDBusInterface validInterface(conn.baseService(), "/"); + QVERIFY(validInterface.isValid()); + QDBusInterface invalidInterface("com.example.Test", "/"); + QVERIFY(!invalidInterface.isValid()); + + QTestEventLoop::instance().connect(connIface, SIGNAL(serviceOwnerChanged(QString, QString, QString)), + SLOT(exitLoop())); + QVERIFY(connIface->registerService("com.example.Test") == QDBusConnectionInterface::ServiceRegistered); + + QTestEventLoop::instance().enterLoop(5); + + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(invalidInterface.isValid()); +} + +void tst_QDBusInterface::introspect() +{ + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + const QMetaObject *mo = iface.metaObject(); + + QCOMPARE(mo->methodCount() - mo->methodOffset(), 7); + QVERIFY(mo->indexOfSignal(TEST_SIGNAL_NAME "(QString)") != -1); + + QCOMPARE(mo->propertyCount() - mo->propertyOffset(), 2); + QVERIFY(mo->indexOfProperty("prop1") != -1); + QVERIFY(mo->indexOfProperty("complexProp") != -1); +} + +void tst_QDBusInterface::introspectUnknownTypes() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + MyObjectUnknownType obj; + con.registerObject("/unknownTypes", &obj, QDBusConnection::ExportAllContents); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/unknownTypes"), + "com.trolltech.QtDBus.MyObjectUnknownTypes"); + + const QMetaObject *mo = iface.metaObject(); + QVERIFY(mo->indexOfMethod("regularMethod()") != -1); // this is the control + QVERIFY(mo->indexOfMethod("somethingHappened(QDBusRawType<0x7e>*)") != -1); + + QVERIFY(mo->indexOfMethod("ping(QDBusRawType<0x7e>*)") != -1); + int midx = mo->indexOfMethod("ping(QDBusRawType<0x7e>*)"); + QCOMPARE(mo->method(midx).typeName(), "QDBusRawType<0x7e>*"); + + 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() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + MyObject::callCount = 0; + + // call a SLOT method + QDBusMessage reply = iface.call("ping", qVariantFromValue(QDBusVariant("foo"))); + QCOMPARE(MyObject::callCount, 1); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + QVariant v = MyObject::callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("foo")); + + // verify reply + QCOMPARE(reply.arguments().count(), 1); + v = reply.arguments().at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("foo")); + + // call an INVOKABLE method + reply = iface.call("ping_invokable", qVariantFromValue(QDBusVariant("bar"))); + QCOMPARE(MyObject::callCount, 2); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + v = MyObject::callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("bar")); + + // verify reply + QCOMPARE(reply.arguments().count(), 1); + v = reply.arguments().at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("bar")); +} + +void tst_QDBusInterface::invokeMethod() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + MyObject::callCount = 0; + + // make the SLOT call without a return type + QDBusVariant arg("foo"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_ARG(QDBusVariant, arg))); + QCOMPARE(MyObject::callCount, 1); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + QVariant v = MyObject::callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("foo")); + + // make the INVOKABLE call without a return type + QDBusVariant arg2("bar"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", Q_ARG(QDBusVariant, arg2))); + QCOMPARE(MyObject::callCount, 2); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + v = MyObject::callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("bar")); +} + +void tst_QDBusInterface::invokeMethodWithReturn() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + MyObject::callCount = 0; + QDBusVariant retArg; + + // make the SLOT call without a return type + QDBusVariant arg("foo"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QDBusVariant, retArg), Q_ARG(QDBusVariant, arg))); + QCOMPARE(MyObject::callCount, 1); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + QVariant v = MyObject::callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg.variant().toString()); + + // verify that we got the reply as expected + QCOMPARE(retArg.variant(), arg.variant()); + + // make the INVOKABLE call without a return type + QDBusVariant arg2("bar"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", Q_RETURN_ARG(QDBusVariant, retArg), Q_ARG(QDBusVariant, arg2))); + QCOMPARE(MyObject::callCount, 2); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + v = MyObject::callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg2.variant().toString()); + + // verify that we got the reply as expected + QCOMPARE(retArg.variant(), arg2.variant()); +} + +void tst_QDBusInterface::invokeMethodWithMultiReturn() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + MyObject::callCount = 0; + QDBusVariant retArg, retArg2; + + // make the SLOT call without a return type + QDBusVariant arg("foo"), arg2("bar"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", + Q_RETURN_ARG(QDBusVariant, retArg), + Q_ARG(QDBusVariant, arg), + Q_ARG(QDBusVariant, arg2), + Q_ARG(QDBusVariant&, retArg2))); + QCOMPARE(MyObject::callCount, 1); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 2); + QVariant v = MyObject::callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg.variant().toString()); + + v = MyObject::callArgs.at(1); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg2.variant().toString()); + + // verify that we got the replies as expected + QCOMPARE(retArg.variant(), arg.variant()); + QCOMPARE(retArg2.variant(), arg2.variant()); + + // make the INVOKABLE call without a return type + QDBusVariant arg3("hello"), arg4("world"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", + Q_RETURN_ARG(QDBusVariant, retArg), + Q_ARG(QDBusVariant, arg3), + Q_ARG(QDBusVariant, arg4), + Q_ARG(QDBusVariant&, retArg2))); + QCOMPARE(MyObject::callCount, 2); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 2); + v = MyObject::callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg3.variant().toString()); + + v = MyObject::callArgs.at(1); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg4.variant().toString()); + + // verify that we got the replies as expected + QCOMPARE(retArg.variant(), arg3.variant()); + QCOMPARE(retArg2.variant(), arg4.variant()); +} + +void tst_QDBusInterface::invokeMethodWithComplexReturn() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + MyObject::callCount = 0; + QList<int> retArg; + + // make the SLOT call without a return type + QList<int> arg = QList<int>() << 42 << -47; + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg))); + QCOMPARE(MyObject::callCount, 1); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + QVariant v = MyObject::callArgs.at(0); + QCOMPARE(v.userType(), qMetaTypeId<QDBusArgument>()); + QCOMPARE(qdbus_cast<QList<int> >(v), arg); + + // verify that we got the reply as expected + QCOMPARE(retArg, arg); + + // make the INVOKABLE call without a return type + QList<int> arg2 = QList<int>() << 24 << -74; + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg2))); + QCOMPARE(MyObject::callCount, 2); + + // verify what the callee received + QCOMPARE(MyObject::callArgs.count(), 1); + v = MyObject::callArgs.at(0); + QCOMPARE(v.userType(), qMetaTypeId<QDBusArgument>()); + QCOMPARE(qdbus_cast<QList<int> >(v), arg2); + + // verify that we got the reply as expected + QCOMPARE(retArg, arg2); +} + +void tst_QDBusInterface::introspectPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + const QMetaObject *mo = iface.metaObject(); + + QCOMPARE(mo->methodCount() - mo->methodOffset(), 7); + QVERIFY(mo->indexOfSignal(TEST_SIGNAL_NAME "(QString)") != -1); + + QCOMPARE(mo->propertyCount() - mo->propertyOffset(), 2); + QVERIFY(mo->indexOfProperty("prop1") != -1); + QVERIFY(mo->indexOfProperty("complexProp") != -1); +} + +void tst_QDBusInterface::callMethodPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + + // call a SLOT method + QDBusMessage reply = iface.call("ping", qVariantFromValue(QDBusVariant("foo"))); + QCOMPARE(callCountPeer(), 1); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + + // verify what the callee received + QVariantList callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + QVariant v = callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("foo")); + + // verify reply + QCOMPARE(reply.arguments().count(), 1); + v = reply.arguments().at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("foo")); + + // call an INVOKABLE method + reply = iface.call("ping_invokable", qVariantFromValue(QDBusVariant("bar"))); + QCOMPARE(callCountPeer(), 2); + QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); + + // verify what the callee received + callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + v = callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("bar")); + + // verify reply + QCOMPARE(reply.arguments().count(), 1); + v = reply.arguments().at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("bar")); +} + +void tst_QDBusInterface::invokeMethodPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + + // make the SLOT call without a return type + QDBusVariant arg("foo"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_ARG(QDBusVariant, arg))); + QCOMPARE(callCountPeer(), 1); + + // verify what the callee received + QVariantList callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + QVariant v = callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("foo")); + + // make the INVOKABLE call without a return type + QDBusVariant arg2("bar"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", Q_ARG(QDBusVariant, arg2))); + QCOMPARE(callCountPeer(), 2); + + // verify what the callee received + callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + v = callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), QString("bar")); +} + +void tst_QDBusInterface::invokeMethodWithReturnPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + QDBusVariant retArg; + + // make the SLOT call without a return type + QDBusVariant arg("foo"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QDBusVariant, retArg), Q_ARG(QDBusVariant, arg))); + QCOMPARE(callCountPeer(), 1); + + // verify what the callee received + QVariantList callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + QVariant v = callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg.variant().toString()); + + // verify that we got the reply as expected + QCOMPARE(retArg.variant(), arg.variant()); + + // make the INVOKABLE call without a return type + QDBusVariant arg2("bar"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", Q_RETURN_ARG(QDBusVariant, retArg), Q_ARG(QDBusVariant, arg2))); + QCOMPARE(callCountPeer(), 2); + + // verify what the callee received + callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + v = callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg2.variant().toString()); + + // verify that we got the reply as expected + QCOMPARE(retArg.variant(), arg2.variant()); +} + +void tst_QDBusInterface::invokeMethodWithMultiReturnPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + QDBusVariant retArg, retArg2; + + // make the SLOT call without a return type + QDBusVariant arg("foo"), arg2("bar"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", + Q_RETURN_ARG(QDBusVariant, retArg), + Q_ARG(QDBusVariant, arg), + Q_ARG(QDBusVariant, arg2), + Q_ARG(QDBusVariant&, retArg2))); + QCOMPARE(callCountPeer(), 1); + + // verify what the callee received + QVariantList callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 2); + QVariant v = callArgs.at(0); + QDBusVariant dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg.variant().toString()); + + v = callArgs.at(1); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg2.variant().toString()); + + // verify that we got the replies as expected + QCOMPARE(retArg.variant(), arg.variant()); + QCOMPARE(retArg2.variant(), arg2.variant()); + + // make the INVOKABLE call without a return type + QDBusVariant arg3("hello"), arg4("world"); + QVERIFY(QMetaObject::invokeMethod(&iface, "ping_invokable", + Q_RETURN_ARG(QDBusVariant, retArg), + Q_ARG(QDBusVariant, arg3), + Q_ARG(QDBusVariant, arg4), + Q_ARG(QDBusVariant&, retArg2))); + QCOMPARE(callCountPeer(), 2); + + // verify what the callee received + callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 2); + v = callArgs.at(0); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg3.variant().toString()); + + v = callArgs.at(1); + dv = qdbus_cast<QDBusVariant>(v); + QCOMPARE(dv.variant().type(), QVariant::String); + QCOMPARE(dv.variant().toString(), arg4.variant().toString()); + + // verify that we got the replies as expected + QCOMPARE(retArg.variant(), arg3.variant()); + QCOMPARE(retArg2.variant(), arg4.variant()); +} + +void tst_QDBusInterface::invokeMethodWithComplexReturnPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + QList<int> retArg; + + // make the SLOT call without a return type + QList<int> arg = QList<int>() << 42 << -47; + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg))); + QCOMPARE(callCountPeer(), 1); + + // verify what the callee received + QVariantList callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + QVariant v = callArgs.at(0); + QCOMPARE(v.userType(), qMetaTypeId<QDBusArgument>()); + QCOMPARE(qdbus_cast<QList<int> >(v), arg); + + // verify that we got the reply as expected + QCOMPARE(retArg, arg); + + // make the INVOKABLE call without a return type + QList<int> arg2 = QList<int>() << 24 << -74; + QVERIFY(QMetaObject::invokeMethod(&iface, "ping", Q_RETURN_ARG(QList<int>, retArg), Q_ARG(QList<int>, arg2))); + QCOMPARE(callCountPeer(), 2); + + // verify what the callee received + callArgs = callArgsPeer(); + QCOMPARE(callArgs.count(), 1); + v = callArgs.at(0); + QCOMPARE(v.userType(), qMetaTypeId<QDBusArgument>()); + QCOMPARE(qdbus_cast<QList<int> >(v), arg2); + + // verify that we got the reply as expected + QCOMPARE(retArg, arg2); +} + +void tst_QDBusInterface::signal() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + QString arg = "So long and thanks for all the fish"; + { + Spy spy; + spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + + emitSignal(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg); + QCOMPARE(spy.count, 1); + QCOMPARE(spy.received, arg); + } + + QDBusInterface iface2(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + { + Spy spy; + spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + spy.connect(&iface2, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + + emitSignal(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg); + QCOMPARE(spy.count, 2); + QCOMPARE(spy.received, arg); + } + + { + Spy spy, spy2; + spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + spy2.connect(&iface2, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + + emitSignal(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg); + QCOMPARE(spy.count, 1); + QCOMPARE(spy.received, arg); + QCOMPARE(spy2.count, 1); + QCOMPARE(spy2.received, arg); + } +} + +void tst_QDBusInterface::signalPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + QString arg = "So long and thanks for all the fish"; + { + Spy spy; + spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + + emitSignalPeer(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg); + QCOMPARE(spy.count, 1); + QCOMPARE(spy.received, arg); + } + + QDBusInterface iface2(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + { + Spy spy; + spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + spy.connect(&iface2, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + + emitSignalPeer(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg); + QCOMPARE(spy.count, 2); + QCOMPARE(spy.received, arg); + } + + { + Spy spy, spy2; + spy.connect(&iface, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + spy2.connect(&iface2, SIGNAL(somethingHappened(QString)), SLOT(spySlot(QString))); + + emitSignalPeer(TEST_INTERFACE_NAME, TEST_SIGNAL_NAME, arg); + QCOMPARE(spy.count, 1); + QCOMPARE(spy.received, arg); + QCOMPARE(spy2.count, 1); + QCOMPARE(spy2.received, arg); + } +} + +void tst_QDBusInterface::propertyRead() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + int arg = obj.m_prop1 = 42; + MyObject::callCount = 0; + + QVariant v = iface.property("prop1"); + QVERIFY(v.isValid()); + QCOMPARE(v.userType(), int(QVariant::Int)); + QCOMPARE(v.toInt(), arg); + QCOMPARE(MyObject::callCount, 1); +} + +void tst_QDBusInterface::propertyWrite() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + int arg = 42; + obj.m_prop1 = 0; + MyObject::callCount = 0; + + QVERIFY(iface.setProperty("prop1", arg)); + QCOMPARE(MyObject::callCount, 1); + QCOMPARE(obj.m_prop1, arg); +} + +void tst_QDBusInterface::complexPropertyRead() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + QList<int> arg = obj.m_complexProp = QList<int>() << 42 << -47; + MyObject::callCount = 0; + + QVariant v = iface.property("complexProp"); + QVERIFY(v.isValid()); + QCOMPARE(v.userType(), qMetaTypeId<QList<int> >()); + QCOMPARE(v.value<QList<int> >(), arg); + QCOMPARE(MyObject::callCount, 1); +} + +void tst_QDBusInterface::complexPropertyWrite() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + + QList<int> arg = QList<int>() << -47 << 42; + obj.m_complexProp.clear(); + MyObject::callCount = 0; + + QVERIFY(iface.setProperty("complexProp", qVariantFromValue(arg))); + QCOMPARE(MyObject::callCount, 1); + QCOMPARE(obj.m_complexProp, arg); +} + +void tst_QDBusInterface::propertyReadPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + int arg = 42; + setProp1Peer(42); + + QVariant v = iface.property("prop1"); + QVERIFY(v.isValid()); + QCOMPARE(v.userType(), int(QVariant::Int)); + QCOMPARE(v.toInt(), arg); + QCOMPARE(callCountPeer(), 1); +} + +void tst_QDBusInterface::propertyWritePeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + int arg = 42; + setProp1Peer(0); + + QVERIFY(iface.setProperty("prop1", arg)); + QCOMPARE(callCountPeer(), 1); + QCOMPARE(prop1Peer(), arg); +} + +void tst_QDBusInterface::complexPropertyReadPeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + QList<int> arg = QList<int>() << 42 << -47; + setComplexPropPeer(arg); + + QVariant v = iface.property("complexProp"); + QVERIFY(v.isValid()); + QCOMPARE(v.userType(), qMetaTypeId<QList<int> >()); + QCOMPARE(v.value<QList<int> >(), arg); + QCOMPARE(callCountPeer(), 1); +} + +void tst_QDBusInterface::complexPropertyWritePeer() +{ + QDBusConnection con("peer"); + QDBusInterface iface(QString(), QLatin1String("/"), + TEST_INTERFACE_NAME, con); + + resetPeer(); + QList<int> arg = QList<int>() << -47 << 42; + + QVERIFY(iface.setProperty("complexProp", qVariantFromValue(arg))); + QCOMPARE(callCountPeer(), 1); + QCOMPARE(complexPropPeer(), arg); +} + +QTEST_MAIN(tst_QDBusInterface) + +#include "tst_qdbusinterface.moc" diff --git a/tests/auto/dbus/qdbuslocalcalls/.gitignore b/tests/auto/dbus/qdbuslocalcalls/.gitignore new file mode 100644 index 0000000000..15bdea69c0 --- /dev/null +++ b/tests/auto/dbus/qdbuslocalcalls/.gitignore @@ -0,0 +1 @@ +tst_qdbuslocalcalls diff --git a/tests/auto/dbus/qdbuslocalcalls/qdbuslocalcalls.pro b/tests/auto/dbus/qdbuslocalcalls/qdbuslocalcalls.pro new file mode 100644 index 0000000000..cef3ddcb28 --- /dev/null +++ b/tests/auto/dbus/qdbuslocalcalls/qdbuslocalcalls.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +QT = core + +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbuslocalcalls.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbuslocalcalls/tst_qdbuslocalcalls.cpp b/tests/auto/dbus/qdbuslocalcalls/tst_qdbuslocalcalls.cpp new file mode 100644 index 0000000000..c89fd7dde4 --- /dev/null +++ b/tests/auto/dbus/qdbuslocalcalls/tst_qdbuslocalcalls.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QObject> +#include <QtCore/QVariant> +#include <QtCore/QList> +#include <QtCore/QVector> +#include <QtTest/QtTest> +#include <QtDBus> + +Q_DECLARE_METATYPE(QVariant) +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QVector<int>) + +class tst_QDBusLocalCalls: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.tst_QDBusLocalCalls") + + QDBusConnection conn; +public: + tst_QDBusLocalCalls(); + +public Q_SLOTS: + Q_SCRIPTABLE int echo(int value) + { return value; } + + Q_SCRIPTABLE QString echo(const QString &value) + { return value; } + + Q_SCRIPTABLE QDBusVariant echo(const QDBusVariant &value) + { return value; } + + Q_SCRIPTABLE QVector<int> echo(const QVector<int> &value) + { return value; } + + Q_SCRIPTABLE QString echo2(const QStringList &list, QString &out) + { out = list[1]; return list[0]; } + + Q_SCRIPTABLE void delayed(const QDBusMessage &msg) + { msg.setDelayedReply(true); } + +protected Q_SLOTS: + void replyReceived(QDBusPendingCallWatcher *watcher); + +private Q_SLOTS: + void initTestCase(); + void makeInvalidCalls(); + void makeCalls_data(); + void makeCalls(); + void makeCallsVariant_data(); + void makeCallsVariant(); + void makeCallsTwoRets(); + void makeCallsComplex(); + void makeDelayedCalls(); + void asyncReplySignal(); + +private: + QVariantList asyncReplyArgs; + QDBusMessage doCall(const QDBusMessage &call); +}; + +tst_QDBusLocalCalls::tst_QDBusLocalCalls() + : conn(QDBusConnection::sessionBus()) +{ +} + +QDBusMessage tst_QDBusLocalCalls::doCall(const QDBusMessage &call) +{ + QFETCH_GLOBAL(bool, useAsync); + if (useAsync) { + QDBusPendingCall ac = conn.asyncCall(call); + ac.waitForFinished(); + return ac.reply(); + } else { + return conn.call(call); + } +} + +void tst_QDBusLocalCalls::replyReceived(QDBusPendingCallWatcher *watcher) +{ + asyncReplyArgs = watcher->reply().arguments(); + QTestEventLoop::instance().exitLoop(); +} + +void tst_QDBusLocalCalls::initTestCase() +{ + QVERIFY(conn.isConnected()); + QVERIFY(conn.registerObject("/", this, QDBusConnection::ExportScriptableSlots)); + + QTest::addColumn<bool>("useAsync"); + QTest::newRow("sync") << false; + QTest::newRow("async") << true; +} + +void tst_QDBusLocalCalls::makeCalls_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::newRow("int") << QVariant(42); + QTest::newRow("string") << QVariant("Hello, world"); +} + +void tst_QDBusLocalCalls::makeCallsVariant_data() +{ + makeCalls_data(); +} + +void tst_QDBusLocalCalls::makeInvalidCalls() +{ + { + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "echo"); + QDBusMessage replyMsg = doCall(callMsg); + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ErrorMessage)); + + QDBusError error(replyMsg); + QCOMPARE(int(error.type()), int(QDBusError::UnknownMethod)); + } + + { + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/no_object", QString(), "echo"); + QDBusMessage replyMsg = doCall(callMsg); + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ErrorMessage)); + + QDBusError error(replyMsg); + QCOMPARE(int(error.type()), int(QDBusError::UnknownObject)); + } +} + +void tst_QDBusLocalCalls::makeCalls() +{ + QFETCH(QVariant, value); + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "echo"); + callMsg << value; + QDBusMessage replyMsg = doCall(callMsg); + + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ReplyMessage)); + + QVariantList replyArgs = replyMsg.arguments(); + QCOMPARE(replyArgs.count(), 1); + QCOMPARE(replyArgs.at(0), value); +} + +void tst_QDBusLocalCalls::makeCallsVariant() +{ + QFETCH(QVariant, value); + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "echo"); + callMsg << qVariantFromValue(QDBusVariant(value)); + QDBusMessage replyMsg = doCall(callMsg); + + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ReplyMessage)); + + QVariantList replyArgs = replyMsg.arguments(); + QCOMPARE(replyArgs.count(), 1); + + const QVariant &reply = replyArgs.at(0); + QCOMPARE(reply.userType(), qMetaTypeId<QDBusVariant>()); + QCOMPARE(qvariant_cast<QDBusVariant>(reply).variant(), value); +} + +void tst_QDBusLocalCalls::makeCallsTwoRets() +{ + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "echo2"); + callMsg << (QStringList() << "One" << "Two"); + QDBusMessage replyMsg = doCall(callMsg); + + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ReplyMessage)); + + QVariantList replyArgs = replyMsg.arguments(); + QCOMPARE(replyArgs.count(), 2); + QCOMPARE(replyArgs.at(0).toString(), QString::fromLatin1("One")); + QCOMPARE(replyArgs.at(1).toString(), QString::fromLatin1("Two")); +} + +void tst_QDBusLocalCalls::makeCallsComplex() +{ + qDBusRegisterMetaType<QList<int> >(); + qDBusRegisterMetaType<QVector<int> >(); + + QList<int> value; + value << 1 << -42 << 47; + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "echo"); + callMsg << qVariantFromValue(value); + QDBusMessage replyMsg = doCall(callMsg); + + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ReplyMessage)); + + QVariantList replyArgs = replyMsg.arguments(); + QCOMPARE(replyArgs.count(), 1); + const QVariant &reply = replyArgs.at(0); + QCOMPARE(reply.userType(), qMetaTypeId<QDBusArgument>()); + QCOMPARE(qdbus_cast<QList<int> >(reply), value); +} + +void tst_QDBusLocalCalls::makeDelayedCalls() +{ + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "delayed"); + QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: cannot call local method 'delayed' at object / (with signature '') on blocking mode"); + QDBusMessage replyMsg = doCall(callMsg); + QCOMPARE(int(replyMsg.type()), int(QDBusMessage::ErrorMessage)); + + QDBusError error(replyMsg); + QCOMPARE(int(error.type()), int(QDBusError::InternalError)); +} + +void tst_QDBusLocalCalls::asyncReplySignal() +{ + QFETCH_GLOBAL(bool, useAsync); + if (!useAsync) + return; // this test only works in async mode + + QDBusMessage callMsg = QDBusMessage::createMethodCall(conn.baseService(), + "/", QString(), "echo"); + callMsg << "Hello World"; + QDBusPendingCall ac = conn.asyncCall(callMsg); + if (ac.isFinished()) + QSKIP("Test ignored: the local-loop async call is already finished", SkipAll); + + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(replyReceived(QDBusPendingCallWatcher*))); + + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QVERIFY(!asyncReplyArgs.isEmpty()); + QCOMPARE(asyncReplyArgs.at(0).toString(), QString("Hello World")); +} + +QTEST_MAIN(tst_QDBusLocalCalls) +#include "tst_qdbuslocalcalls.moc" diff --git a/tests/auto/dbus/qdbusmarshall/.gitignore b/tests/auto/dbus/qdbusmarshall/.gitignore new file mode 100644 index 0000000000..d227388521 --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/.gitignore @@ -0,0 +1,2 @@ +tst_qdbusmarshall +qpong/qpong diff --git a/tests/auto/dbus/qdbusmarshall/common.h b/tests/auto/dbus/qdbusmarshall/common.h new file mode 100644 index 0000000000..35fe5f1bf7 --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/common.h @@ -0,0 +1,755 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <math.h> // isnan +#include <qvariant.h> + +#ifdef Q_OS_UNIX +# include <private/qcore_unix_p.h> + +static bool compareFileDescriptors(int fd1, int fd2) +{ + QT_STATBUF st1, st2; + if (QT_FSTAT(fd1, &st1) == -1 || QT_FSTAT(fd2, &st2) == -1) { + perror("fstat"); + return false; + } + + return (st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino); +} +#endif + +Q_DECLARE_METATYPE(QVariant) +Q_DECLARE_METATYPE(QList<bool>) +Q_DECLARE_METATYPE(QList<short>) +Q_DECLARE_METATYPE(QList<ushort>) +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<uint>) +Q_DECLARE_METATYPE(QList<qlonglong>) +Q_DECLARE_METATYPE(QList<qulonglong>) +Q_DECLARE_METATYPE(QList<double>) +Q_DECLARE_METATYPE(QList<QDBusVariant>) +Q_DECLARE_METATYPE(QList<QDateTime>) + +Q_DECLARE_METATYPE(QList<QByteArray>) +Q_DECLARE_METATYPE(QList<QVariantList>) +Q_DECLARE_METATYPE(QList<QStringList>) +Q_DECLARE_METATYPE(QList<QList<bool> >) +Q_DECLARE_METATYPE(QList<QList<short> >) +Q_DECLARE_METATYPE(QList<QList<ushort> >) +Q_DECLARE_METATYPE(QList<QList<int> >) +Q_DECLARE_METATYPE(QList<QList<uint> >) +Q_DECLARE_METATYPE(QList<QList<qlonglong> >) +Q_DECLARE_METATYPE(QList<QList<qulonglong> >) +Q_DECLARE_METATYPE(QList<QList<double> >) +Q_DECLARE_METATYPE(QList<QList<QDBusObjectPath> >) +Q_DECLARE_METATYPE(QList<QList<QDBusSignature> >) + +typedef QMap<int, QString> IntStringMap; +typedef QMap<QString, QString> StringStringMap; +typedef QMap<QDBusObjectPath, QString> ObjectPathStringMap; +typedef QHash<qlonglong, QDateTime> LLDateTimeMap; +typedef QHash<QDBusSignature, QString> SignatureStringMap; +Q_DECLARE_METATYPE(IntStringMap) +Q_DECLARE_METATYPE(StringStringMap) +Q_DECLARE_METATYPE(ObjectPathStringMap) +Q_DECLARE_METATYPE(LLDateTimeMap) +Q_DECLARE_METATYPE(SignatureStringMap) + +static bool compare(const QDBusUnixFileDescriptor &t1, const QDBusUnixFileDescriptor &t2) +{ + int fd1 = t1.fileDescriptor(); + int fd2 = t2.fileDescriptor(); + if ((fd1 == -1 || fd2 == -1) && fd1 != fd2) { + // one is valid, the other isn't + return false; + } + +#ifdef Q_OS_UNIX + return compareFileDescriptors(fd1, fd2); +#else + return true; +#endif +} + +struct MyStruct +{ + int i; + QString s; + + inline bool operator==(const MyStruct &other) const + { return i == other.i && s == other.s; } +}; +Q_DECLARE_METATYPE(MyStruct) + +QDBusArgument &operator<<(QDBusArgument &arg, const MyStruct &ms) +{ + arg.beginStructure(); + arg << ms.i << ms.s; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, MyStruct &ms) +{ + arg.beginStructure(); + arg >> ms.i >> ms.s; + arg.endStructure(); + return arg; +} + +struct MyVariantMapStruct +{ + QString s; + QVariantMap map; + + inline bool operator==(const MyVariantMapStruct &other) const + { return s == other.s && map == other.map; } +}; +Q_DECLARE_METATYPE(MyVariantMapStruct) +Q_DECLARE_METATYPE(QList<MyVariantMapStruct>) + +QDBusArgument &operator<<(QDBusArgument &arg, const MyVariantMapStruct &ms) +{ + arg.beginStructure(); + arg << ms.s << ms.map; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, MyVariantMapStruct &ms) +{ + arg.beginStructure(); + arg >> ms.s >> ms.map; + arg.endStructure(); + return arg; +} + +struct MyFileDescriptorStruct +{ + QDBusUnixFileDescriptor fd; + + inline bool operator==(const MyFileDescriptorStruct &other) const + { return compare(fd, other.fd); } +}; +Q_DECLARE_METATYPE(MyFileDescriptorStruct) +Q_DECLARE_METATYPE(QList<MyFileDescriptorStruct>) + +QDBusArgument &operator<<(QDBusArgument &arg, const MyFileDescriptorStruct &ms) +{ + arg.beginStructure(); + arg << ms.fd; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, MyFileDescriptorStruct &ms) +{ + arg.beginStructure(); + arg >> ms.fd; + arg.endStructure(); + return arg; +} + + +void commonInit() +{ + qDBusRegisterMetaType<QList<QDateTime> >(); + qDBusRegisterMetaType<QList<QStringList> >(); + qDBusRegisterMetaType<QList<QByteArray> >(); + qDBusRegisterMetaType<QList<QList<bool> > >(); + qDBusRegisterMetaType<QList<QList<short> > >(); + qDBusRegisterMetaType<QList<QList<ushort> > >(); + qDBusRegisterMetaType<QList<QList<int> > >(); + qDBusRegisterMetaType<QList<QList<uint> > >(); + qDBusRegisterMetaType<QList<QList<qlonglong> > >(); + qDBusRegisterMetaType<QList<QList<qulonglong> > >(); + qDBusRegisterMetaType<QList<QList<double> > >(); + qDBusRegisterMetaType<QList<QList<QDBusObjectPath> > >(); + qDBusRegisterMetaType<QList<QList<QDBusSignature> > >(); + qDBusRegisterMetaType<QList<QVariantList> >(); + + qDBusRegisterMetaType<QMap<int, QString> >(); + qDBusRegisterMetaType<QMap<QString, QString> >(); + qDBusRegisterMetaType<QMap<QDBusObjectPath, QString> >(); + qDBusRegisterMetaType<QHash<qlonglong, QDateTime> >(); + qDBusRegisterMetaType<QHash<QDBusSignature, QString> >(); + + qDBusRegisterMetaType<MyStruct>(); + qDBusRegisterMetaType<MyVariantMapStruct>(); + qDBusRegisterMetaType<QList<MyVariantMapStruct> >(); + qDBusRegisterMetaType<MyFileDescriptorStruct>(); + qDBusRegisterMetaType<QList<MyFileDescriptorStruct> >(); +} +#ifdef USE_PRIVATE_CODE +#include "private/qdbusintrospection_p.h" + +// just to make it easier: +typedef QDBusIntrospection::Interfaces InterfaceMap; +typedef QDBusIntrospection::Objects ObjectMap; +typedef QDBusIntrospection::Arguments ArgumentList; +typedef QDBusIntrospection::Annotations AnnotationsMap; +typedef QDBusIntrospection::Methods MethodMap; +typedef QDBusIntrospection::Signals SignalMap; +typedef QDBusIntrospection::Properties PropertyMap; + +Q_DECLARE_METATYPE(QDBusIntrospection::Method) +Q_DECLARE_METATYPE(QDBusIntrospection::Signal) +Q_DECLARE_METATYPE(QDBusIntrospection::Property) +Q_DECLARE_METATYPE(MethodMap) +Q_DECLARE_METATYPE(SignalMap) +Q_DECLARE_METATYPE(PropertyMap) + +inline QDBusIntrospection::Argument arg(const char* type, const char *name = 0) +{ + QDBusIntrospection::Argument retval; + retval.type = QLatin1String(type); + retval.name = QLatin1String(name); + return retval; +} + +template<typename T> +inline QMap<QString, T>& operator<<(QMap<QString, T>& map, const T& m) +{ map.insertMulti(m.name, m); return map; } + +inline const char* mapName(const MethodMap&) +{ return "MethodMap"; } + +inline const char* mapName(const SignalMap&) +{ return "SignalMap"; } + +inline const char* mapName(const PropertyMap&) +{ return "PropertyMap"; } + +QString printable(const QDBusIntrospection::Method& m) +{ + QString result = "method " + m.name + "("; + foreach (QDBusIntrospection::Argument arg, m.inputArgs) + result += QString("in %1 %2, ") + .arg(arg.type, arg.name); + foreach (QDBusIntrospection::Argument arg, m.outputArgs) + result += QString("out %1 %2, ") + .arg(arg.type, arg.name); + AnnotationsMap::const_iterator it = m.annotations.begin(); + for ( ; it != m.annotations.end(); ++it) + result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value()); + + result += ")"; + return result; +} + +QString printable(const QDBusIntrospection::Signal& s) +{ + QString result = "signal " + s.name + "("; + foreach (QDBusIntrospection::Argument arg, s.outputArgs) + result += QString("out %1 %2, ") + .arg(arg.type, arg.name); + AnnotationsMap::const_iterator it = s.annotations.begin(); + for ( ; it != s.annotations.end(); ++it) + result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value()); + + result += ")"; + return result; +} + +QString printable(const QDBusIntrospection::Property& p) +{ + QString result; + if (p.access == QDBusIntrospection::Property::Read) + result = "property read %1 %2, "; + else if (p.access == QDBusIntrospection::Property::Write) + result = "property write %1 %2, "; + else + result = "property readwrite %1 %2, "; + result = result.arg(p.type, p.name); + + AnnotationsMap::const_iterator it = p.annotations.begin(); + for ( ; it != p.annotations.end(); ++it) + result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value()); + + return result; +} + +template<typename T> +char* printableMap(const QMap<QString, T>& map) +{ + QString contents = "\n"; + typename QMap<QString, T>::const_iterator it = map.begin(); + for ( ; it != map.end(); ++it) { + if (it.key() != it.value().name) + contents += it.value().name + ":"; + contents += printable(it.value()); + contents += ";\n"; + } + + QString result("%1(size = %2): {%3}"); + return qstrdup(qPrintable(result + .arg(mapName(map)) + .arg(map.size()) + .arg(contents))); +} + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + inline char* toString(const MethodMap& map) + { + return printableMap(map); + } + + template<> + inline char* toString(const SignalMap& map) + { + return printableMap(map); + } + + template<> + inline char* toString(const PropertyMap& map) + { + return printableMap(map); + } +} +QT_END_NAMESPACE + +#endif + +//bool compare(const QVariantList &l1, const QVariantList &l2); +//bool compare(const QVariantMap &m1, const QVariantMap &m2); +template<typename T> +bool compare(const T &t1, const T &t2) +{ return t1 == t2; } + +template<> +bool compare(const QVariant &v1, const QVariant &v2); + +bool compare(double d1, double d2) +{ + if (isnan(d1) && isnan(d2)) + return true; + return d1 == d2; +} + +template<> +bool compare(const QString &s1, const QString &s2) +{ + if (s1.isEmpty() && s2.isEmpty()) + return true; // regardless of whether one of them is null + return s1 == s2; +} + +template<> +bool compare(const QByteArray &ba1, const QByteArray &ba2) +{ + if (ba1.isEmpty() && ba2.isEmpty()) + return true; // regardless of whether one of them is null + return ba1 == ba2; +} + +template<> +bool compare(const QDBusVariant &s1, const QDBusVariant &s2) +{ + return compare(s1.variant(), s2.variant()); +} + +template<typename T> +bool compare(const QList<T> &l1, const QList<T> &l2) +{ + if (l1.count() != l2.count()) + return false; + + typename QList<T>::ConstIterator it1 = l1.constBegin(); + typename QList<T>::ConstIterator it2 = l2.constBegin(); + typename QList<T>::ConstIterator end = l1.constEnd(); + for ( ; it1 != end; ++it1, ++it2) + if (!compare(*it1, *it2)) + return false; + return true; +} + +template<typename Key, typename T> +bool compare(const QMap<Key, T> &m1, const QMap<Key, T> &m2) +{ + if (m1.count() != m2.size()) + return false; + typename QMap<Key, T>::ConstIterator i1 = m1.constBegin(); + typename QMap<Key, T>::ConstIterator end = m1.constEnd(); + for ( ; i1 != end; ++i1) { + typename QMap<Key, T>::ConstIterator i2 = m2.find(i1.key()); + if (i2 == m2.constEnd()) + return false; + if (!compare(*i1, *i2)) + return false; + } + return true; +} + +template<typename Key, typename T> +bool compare(const QHash<Key, T> &m1, const QHash<Key, T> &m2) +{ + if (m1.count() != m2.size()) + return false; + typename QHash<Key, T>::ConstIterator i1 = m1.constBegin(); + typename QHash<Key, T>::ConstIterator end = m1.constEnd(); + for ( ; i1 != end; ++i1) { + typename QHash<Key, T>::ConstIterator i2 = m2.find(i1.key()); + if (i2 == m2.constEnd()) + return false; + if (!compare(*i1, *i2)) + return false; + } + return true; +} + +template<typename T> +inline bool compare(const QDBusArgument &arg, const QVariant &v2, T * = 0) +{ + return compare(qdbus_cast<T>(arg), qvariant_cast<T>(v2)); +} + +bool compareToArgument(const QDBusArgument &arg, const QVariant &v2) +{ + if (arg.currentSignature() != QDBusMetaType::typeToSignature(v2.userType())) + return false; + + // try to demarshall the arg according to v2 + switch (v2.userType()) + { + case QVariant::Bool: + return compare<bool>(arg, v2); + case QMetaType::UChar: + return compare<uchar>(arg, v2); + case QMetaType::Short: + return compare<short>(arg, v2); + case QMetaType::UShort: + return compare<ushort>(arg, v2); + case QVariant::Int: + return compare<int>(arg, v2); + case QVariant::UInt: + return compare<uint>(arg, v2); + case QVariant::LongLong: + return compare<qlonglong>(arg, v2); + case QVariant::ULongLong: + return compare<qulonglong>(arg, v2); + case QVariant::Double: + return compare<double>(arg, v2); + case QVariant::String: + return compare<QString>(arg, v2); + case QVariant::ByteArray: + return compare<QByteArray>(arg, v2); + case QVariant::List: + return compare<QVariantList>(arg, v2); + case QVariant::Map: + return compare<QVariantMap>(arg, v2); + case QVariant::Point: + return compare<QPoint>(arg, v2); + case QVariant::PointF: + return compare<QPointF>(arg, v2); + case QVariant::Size: + return compare<QSize>(arg, v2); + case QVariant::SizeF: + return compare<QSizeF>(arg, v2); + case QVariant::Line: + return compare<QLine>(arg, v2); + case QVariant::LineF: + return compare<QLineF>(arg, v2); + case QVariant::Rect: + return compare<QRect>(arg, v2); + case QVariant::RectF: + return compare<QRectF>(arg, v2); + case QVariant::Date: + return compare<QDate>(arg, v2); + case QVariant::Time: + return compare<QTime>(arg, v2); + case QVariant::DateTime: + return compare<QDateTime>(arg, v2); + default: + register int id = v2.userType(); + if (id == qMetaTypeId<QDBusObjectPath>()) + return compare<QDBusObjectPath>(arg, v2); + else if (id == qMetaTypeId<QDBusSignature>()) + return compare<QDBusSignature>(arg, v2); + else if (id == qMetaTypeId<QDBusVariant>()) + return compare<QDBusVariant>(arg, v2); + else if (id == qMetaTypeId<QList<bool> >()) + return compare<QList<bool> >(arg, v2); + else if (id == qMetaTypeId<QList<short> >()) + return compare<QList<short> >(arg, v2); + else if (id == qMetaTypeId<QList<ushort> >()) + return compare<QList<ushort> >(arg, v2); + else if (id == qMetaTypeId<QList<int> >()) + return compare<QList<int> >(arg, v2); + else if (id == qMetaTypeId<QList<uint> >()) + return compare<QList<uint> >(arg, v2); + else if (id == qMetaTypeId<QList<qlonglong> >()) + return compare<QList<qlonglong> >(arg, v2); + else if (id == qMetaTypeId<QList<qulonglong> >()) + return compare<QList<qulonglong> >(arg, v2); + else if (id == qMetaTypeId<QList<double> >()) + return compare<QList<double> >(arg, v2); + else if (id == qMetaTypeId<QList<QDBusObjectPath> >()) + return compare<QList<QDBusObjectPath> >(arg, v2); + else if (id == qMetaTypeId<QList<QDBusSignature> >()) + return compare<QList<QDBusSignature> >(arg, v2); + else if (id == qMetaTypeId<QList<QDBusUnixFileDescriptor> >()) + return compare<QList<QDBusUnixFileDescriptor> >(arg, v2); + else if (id == qMetaTypeId<QList<QDateTime> >()) + return compare<QList<QDateTime> >(arg, v2); + + else if (id == qMetaTypeId<QMap<int, QString> >()) + return compare<QMap<int, QString> >(arg, v2); + else if (id == qMetaTypeId<QMap<QString, QString> >()) + return compare<QMap<QString, QString> >(arg, v2); + else if (id == qMetaTypeId<QMap<QDBusObjectPath, QString> >()) + return compare<QMap<QDBusObjectPath, QString> >(arg, v2); + else if (id == qMetaTypeId<QHash<qlonglong, QDateTime> >()) + return compare<QHash<qlonglong, QDateTime> >(arg, v2); + else if (id == qMetaTypeId<QHash<QDBusSignature, QString> >()) + return compare<QHash<QDBusSignature, QString> >(arg, v2); + + else if (id == qMetaTypeId<QList<QByteArray> >()) + return compare<QList<QByteArray> >(arg, v2); + else if (id == qMetaTypeId<QList<QList<bool> > >()) + return compare<QList<QList<bool> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<short> > >()) + return compare<QList<QList<short> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<ushort> > >()) + return compare<QList<QList<ushort> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<int> > >()) + return compare<QList<QList<int> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<uint> > >()) + return compare<QList<QList<uint> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<qlonglong> > >()) + return compare<QList<QList<qlonglong> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<qulonglong> > >()) + return compare<QList<QList<qulonglong> > >(arg, v2); + else if (id == qMetaTypeId<QList<QList<double> > >()) + return compare<QList<QList<double> > >(arg, v2); + else if (id == qMetaTypeId<QList<QStringList> >()) + return compare<QList<QStringList> >(arg, v2); + else if (id == qMetaTypeId<QList<QVariantList> >()) + return compare<QList<QVariantList> >(arg, v2); + + else if (id == qMetaTypeId<MyStruct>()) + return compare<MyStruct>(arg, v2); + + else if (id == qMetaTypeId<MyVariantMapStruct>()) + return compare<MyVariantMapStruct>(arg, v2); + else if (id == qMetaTypeId<QList<MyVariantMapStruct> >()) + return compare<QList<MyVariantMapStruct> >(arg, v2); + else if (id == qMetaTypeId<MyFileDescriptorStruct>()) + return compare<MyFileDescriptorStruct>(arg, v2); + else if (id == qMetaTypeId<QList<MyFileDescriptorStruct> >()) + return compare<QList<MyFileDescriptorStruct> >(arg, v2); + } + + qWarning() << "Unexpected QVariant type" << v2.userType() + << QByteArray(QDBusMetaType::typeToSignature(v2.userType())) + << QVariant::typeToName(QVariant::Type(v2.userType())); + return false; +} + +template<> bool compare(const QVariant &v1, const QVariant &v2) +{ + // v1 is the one that came from the network + // v2 is the one that we sent + + if (v1.userType() == qMetaTypeId<QDBusArgument>()) + // this argument has been left un-demarshalled + return compareToArgument(qvariant_cast<QDBusArgument>(v1), v2); + + if (v1.userType() != v2.userType()) + return false; + + int id = v1.userType(); + if (id == QVariant::List) + return compare(v1.toList(), v2.toList()); + + else if (id == QVariant::Map) + return compare(v1.toMap(), v2.toMap()); + + else if (id == QVariant::String) + return compare(v1.toString(), v2.toString()); + + else if (id == QVariant::ByteArray) + return compare(v1.toByteArray(), v2.toByteArray()); + + else if (id < int(QVariant::UserType)) // yes, v1.type() + // QVariant can compare + return v1 == v2; + + else if (id == QMetaType::UChar) + return qvariant_cast<uchar>(v1) == qvariant_cast<uchar>(v2); + + else if (id == QMetaType::Short) + return qvariant_cast<short>(v1) == qvariant_cast<short>(v2); + + else if (id == QMetaType::UShort) + return qvariant_cast<ushort>(v1) == qvariant_cast<ushort>(v2); + + else if (id == qMetaTypeId<QDBusObjectPath>()) + return qvariant_cast<QDBusObjectPath>(v1).path() == qvariant_cast<QDBusObjectPath>(v2).path(); + + else if (id == qMetaTypeId<QDBusSignature>()) + return qvariant_cast<QDBusSignature>(v1).signature() == qvariant_cast<QDBusSignature>(v2).signature(); + + else if (id == qMetaTypeId<QDBusUnixFileDescriptor>()) + return compare(qvariant_cast<QDBusUnixFileDescriptor>(v1), qvariant_cast<QDBusUnixFileDescriptor>(v2)); + + else if (id == qMetaTypeId<QDBusVariant>()) + return compare(qvariant_cast<QDBusVariant>(v1).variant(), qvariant_cast<QDBusVariant>(v2).variant()); + + else if (id == qMetaTypeId<QVariant>()) + return compare(qvariant_cast<QVariant>(v1), qvariant_cast<QVariant>(v2)); + + else if (id == qMetaTypeId<QList<bool> >()) + return qvariant_cast<QList<bool> >(v1) == qvariant_cast<QList<bool> >(v2); + + else if (id == qMetaTypeId<QList<short> >()) + return qvariant_cast<QList<short> >(v1) == qvariant_cast<QList<short> >(v2); + + else if (id == qMetaTypeId<QList<ushort> >()) + return qvariant_cast<QList<ushort> >(v1) == qvariant_cast<QList<ushort> >(v2); + + else if (id == qMetaTypeId<QList<int> >()) + return qvariant_cast<QList<int> >(v1) == qvariant_cast<QList<int> >(v2); + + else if (id == qMetaTypeId<QList<uint> >()) + return qvariant_cast<QList<uint> >(v1) == qvariant_cast<QList<uint> >(v2); + + else if (id == qMetaTypeId<QList<qlonglong> >()) + return qvariant_cast<QList<qlonglong> >(v1) == qvariant_cast<QList<qlonglong> >(v2); + + else if (id == qMetaTypeId<QList<qulonglong> >()) + return qvariant_cast<QList<qulonglong> >(v2) == qvariant_cast<QList<qulonglong> >(v2); + + else if (id == qMetaTypeId<QList<double> >()) + return compare(qvariant_cast<QList<double> >(v1), qvariant_cast<QList<double> >(v2)); + + else if (id == qMetaTypeId<QVariant>()) + return compare(qvariant_cast<QVariant>(v1), qvariant_cast<QVariant>(v2)); + + else if (id == qMetaTypeId<QList<QList<bool> > >()) + return qvariant_cast<QList<QList<bool> > >(v1) == qvariant_cast<QList<QList<bool> > >(v2); + + else if (id == qMetaTypeId<QList<QList<short> > >()) + return qvariant_cast<QList<QList<short> > >(v1) == qvariant_cast<QList<QList<short> > >(v2); + + else if (id == qMetaTypeId<QList<QList<ushort> > >()) + return qvariant_cast<QList<QList<ushort> > >(v1) == qvariant_cast<QList<QList<ushort> > >(v2); + + else if (id == qMetaTypeId<QList<QList<int> > >()) + return qvariant_cast<QList<QList<int> > >(v1) == qvariant_cast<QList<QList<int> > >(v2); + + else if (id == qMetaTypeId<QList<QList<uint> > >()) + return qvariant_cast<QList<QList<uint> > >(v1) == qvariant_cast<QList<QList<uint> > >(v2); + + else if (id == qMetaTypeId<QList<QList<qlonglong> > >()) + return qvariant_cast<QList<QList<qlonglong> > >(v1) == qvariant_cast<QList<QList<qlonglong> > >(v2); + + else if (id == qMetaTypeId<QList<QList<qulonglong> > >()) + return qvariant_cast<QList<QList<qulonglong> > >(v1) == qvariant_cast<QList<QList<qulonglong> > >(v2); + + else if (id == qMetaTypeId<QList<QList<double> > >()) + return compare(qvariant_cast<QList<QList<double> > >(v1), qvariant_cast<QList<QList<double> > >(v2)); + + else if (id == qMetaTypeId<QList<QStringList> >()) + return qvariant_cast<QList<QStringList> >(v1) == qvariant_cast<QList<QStringList> >(v2); + + else if (id == qMetaTypeId<QList<QByteArray> >()) + return qvariant_cast<QList<QByteArray> >(v1) == qvariant_cast<QList<QByteArray> >(v2); + + else if (id == qMetaTypeId<QList<QVariantList> >()) + return compare(qvariant_cast<QList<QVariantList> >(v1), qvariant_cast<QList<QVariantList> >(v2)); + + else if (id == qMetaTypeId<QMap<int, QString> >()) + return compare(qvariant_cast<QMap<int, QString> >(v1), qvariant_cast<QMap<int, QString> >(v2)); + + else if (id == qMetaTypeId<QMap<QString, QString> >()) // ssmap + return compare(qvariant_cast<QMap<QString, QString> >(v1), qvariant_cast<QMap<QString, QString> >(v2)); + + else if (id == qMetaTypeId<QMap<QDBusObjectPath, QString> >()) + return compare(qvariant_cast<QMap<QDBusObjectPath, QString> >(v1), qvariant_cast<QMap<QDBusObjectPath, QString> >(v2)); + + else if (id == qMetaTypeId<QHash<qlonglong, QDateTime> >()) // lldtmap + return compare(qvariant_cast<QHash<qint64, QDateTime> >(v1), qvariant_cast<QHash<qint64, QDateTime> >(v2)); + + else if (id == qMetaTypeId<QHash<QDBusSignature, QString> >()) + return compare(qvariant_cast<QHash<QDBusSignature, QString> >(v1), qvariant_cast<QHash<QDBusSignature, QString> >(v2)); + + else if (id == qMetaTypeId<MyStruct>()) // (is) + return qvariant_cast<MyStruct>(v1) == qvariant_cast<MyStruct>(v2); + + else { + qWarning() << "Please write a comparison case for type" << v1.typeName(); + return false; // unknown type + } +} + +#if 0 +bool compare(const QVariantList &l1, const QVariantList &l2) +{ + if (l1.count() != l2.size()) + return false; + QVariantList::ConstIterator i1 = l1.constBegin(); + QVariantList::ConstIterator i2 = l2.constBegin(); + QVariantList::ConstIterator end = l1.constEnd(); + for ( ; i1 != end; ++i1, ++i2) { + if (!compare(*i1, *i2)) + return false; + } + return true; +} + +bool compare(const QVariantMap &m1, const QVariantMap &m2) +{ + if (m1.count() != m2.size()) + return false; + QVariantMap::ConstIterator i1 = m1.constBegin(); + QVariantMap::ConstIterator end = m1.constEnd(); + for ( ; i1 != end; ++i1) { + QVariantMap::ConstIterator i2 = m2.find(i1.key()); + if (i2 == m2.constEnd()) + return false; + if (!compare(*i1, *i2)) + return false; + } + return true; +} +#endif diff --git a/tests/auto/dbus/qdbusmarshall/dummy.cpp b/tests/auto/dbus/qdbusmarshall/dummy.cpp new file mode 100644 index 0000000000..0b403c3585 --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/dummy.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> + +QTEST_NOOP_MAIN + diff --git a/tests/auto/dbus/qdbusmarshall/qdbusmarshall.pro b/tests/auto/dbus/qdbusmarshall/qdbusmarshall.pro new file mode 100644 index 0000000000..1b6408a2f9 --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/qdbusmarshall.pro @@ -0,0 +1,14 @@ +load(qttest_p4) +contains(QT_CONFIG,dbus): { + TEMPLATE = subdirs + CONFIG += ordered + SUBDIRS = qpong test + + QT += core-private + + requires(contains(QT_CONFIG,private_tests)) +} else { + SOURCES += dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusmarshall/qpong/qpong.cpp b/tests/auto/dbus/qdbusmarshall/qpong/qpong.cpp new file mode 100644 index 0000000000..fce6f59344 --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/qpong/qpong.cpp @@ -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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QtCore> +#include <QtDBus/QtDBus> + +static const char serviceName[] = "com.trolltech.autotests.qpong"; +static const char objectPath[] = "/com/trolltech/qpong"; +//static const char *interfaceName = serviceName; + +class Pong: public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.autotests.qpong") +public slots: + + void ping(QDBusMessage msg) + { + msg.setDelayedReply(true); + if (!QDBusConnection::sessionBus().send(msg.createReply(msg.arguments()))) + exit(1); + } +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QDBusConnection con = QDBusConnection::sessionBus(); + if (!con.isConnected()) + exit(1); + + if (!con.registerService(serviceName)) + exit(2); + + Pong pong; + con.registerObject(objectPath, &pong, QDBusConnection::ExportAllSlots); + + printf("ready.\n"); + + return app.exec(); +} + +#include "qpong.moc" diff --git a/tests/auto/dbus/qdbusmarshall/qpong/qpong.pro b/tests/auto/dbus/qdbusmarshall/qpong/qpong.pro new file mode 100644 index 0000000000..5cd9acdc1c --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/qpong/qpong.pro @@ -0,0 +1,6 @@ +SOURCES = qpong.cpp +TARGET = qpong +QT += dbus +QT -= gui + + diff --git a/tests/auto/dbus/qdbusmarshall/test/test.pro b/tests/auto/dbus/qdbusmarshall/test/test.pro new file mode 100644 index 0000000000..ba4d99b57d --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/test/test.pro @@ -0,0 +1,9 @@ +load(qttest_p4) +SOURCES += ../tst_qdbusmarshall.cpp +TARGET = ../tst_qdbusmarshall + +QT = core +QT += core-private dbus-private + +LIBS += $$QT_LIBS_DBUS +QMAKE_CXXFLAGS += $$QT_CFLAGS_DBUS diff --git a/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp b/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp new file mode 100644 index 0000000000..cca212e278 --- /dev/null +++ b/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp @@ -0,0 +1,1172 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QtCore> +#include <QtTest/QtTest> +#include <QtDBus/QtDBus> +#include <QtDBus/private/qdbusutil_p.h> +#include <QtDBus/private/qdbusconnection_p.h> + +#include "common.h" +#include <limits> + +#include <dbus/dbus.h> + +static const char serviceName[] = "com.trolltech.autotests.qpong"; +static const char objectPath[] = "/com/trolltech/qpong"; +static const char *interfaceName = serviceName; + +class tst_QDBusMarshall: public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + +private slots: + void sendBasic_data(); + void sendBasic(); + + void sendVariant_data(); + void sendVariant(); + + void sendArrays_data(); + void sendArrays(); + + void sendArrayOfArrays_data(); + void sendArrayOfArrays(); + + void sendMaps_data(); + void sendMaps(); + + void sendStructs_data(); + void sendStructs(); + + void sendComplex_data(); + void sendComplex(); + + void sendArgument_data(); + void sendArgument(); + + void sendSignalErrors(); + void sendCallErrors_data(); + void sendCallErrors(); + + void receiveUnknownType_data(); + void receiveUnknownType(); + +private: + int fileDescriptorForTest(); + + QProcess proc; + QTemporaryFile tempFile; + bool fileDescriptorPassing; +}; + +class QDBusMessageSpy: public QObject +{ + Q_OBJECT +public slots: + Q_SCRIPTABLE int theSlot(const QDBusMessage &msg) + { + list << msg; + return 42; + } +public: + QList<QDBusMessage> list; +}; + +struct UnregisteredType { }; +Q_DECLARE_METATYPE(UnregisteredType) + +void tst_QDBusMarshall::initTestCase() +{ + commonInit(); + QDBusConnection con = QDBusConnection::sessionBus(); + fileDescriptorPassing = con.connectionCapabilities() & QDBusConnection::UnixFileDescriptorPassing; +#ifdef Q_OS_WIN + proc.start("qpong"); +#else + proc.start("./qpong/qpong"); +#endif + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName)) { + QVERIFY(proc.waitForStarted()); + + QVERIFY(con.isConnected()); + con.connect("org.freedesktop.DBus", QString(), "org.freedesktop.DBus", "NameOwnerChanged", + QStringList() << serviceName << QString(""), QString(), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered(serviceName)); + con.disconnect("org.freedesktop.DBus", QString(), "org.freedesktop.DBus", "NameOwnerChanged", + QStringList() << serviceName << QString(""), QString(), + &QTestEventLoop::instance(), SLOT(exitLoop())); + } +} + +void tst_QDBusMarshall::cleanupTestCase() +{ + proc.close(); + proc.terminate(); + proc.waitForFinished(200); +} + +int tst_QDBusMarshall::fileDescriptorForTest() +{ + if (!tempFile.isOpen()) { + tempFile.setFileTemplate(QDir::tempPath() + "/qdbusmarshalltestXXXXXX.tmp"); + tempFile.open(); + } + return tempFile.handle(); +} + +void tst_QDBusMarshall::sendBasic_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<QString>("stringResult"); + + // basic types: + QTest::newRow("bool") << QVariant(false) << "b" << "false"; +#if 1 + QTest::newRow("bool2") << QVariant(true) << "b" << "true"; + QTest::newRow("byte") << qVariantFromValue(uchar(1)) << "y" << "1"; + QTest::newRow("int16") << qVariantFromValue(short(2)) << "n" << "2"; + QTest::newRow("uint16") << qVariantFromValue(ushort(3)) << "q" << "3"; + QTest::newRow("int") << QVariant(1) << "i" << "1"; + QTest::newRow("uint") << QVariant(2U) << "u" << "2"; + QTest::newRow("int64") << QVariant(Q_INT64_C(3)) << "x" << "3"; + QTest::newRow("uint64") << QVariant(Q_UINT64_C(4)) << "t" << "4"; + QTest::newRow("double") << QVariant(42.5) << "d" << "42.5"; + QTest::newRow("string") << QVariant("ping") << "s" << "\"ping\""; + QTest::newRow("objectpath") << qVariantFromValue(QDBusObjectPath("/org/kde")) << "o" << "[ObjectPath: /org/kde]"; + QTest::newRow("signature") << qVariantFromValue(QDBusSignature("g")) << "g" << "[Signature: g]"; + QTest::newRow("emptystring") << QVariant("") << "s" << "\"\""; + QTest::newRow("nullstring") << QVariant(QString()) << "s" << "\"\""; + + if (fileDescriptorPassing) + QTest::newRow("file-descriptor") << qVariantFromValue(QDBusUnixFileDescriptor(fileDescriptorForTest())) << "h" << "[Unix FD: valid]"; +#endif +} + +void tst_QDBusMarshall::sendVariant_data() +{ + sendBasic_data(); + + QTest::newRow("variant") << qVariantFromValue(QDBusVariant(1)) << "v" << "[Variant(int): 1]"; + + QDBusVariant nested(1); + QTest::newRow("variant-variant") << qVariantFromValue(QDBusVariant(qVariantFromValue(nested))) << "v" + << "[Variant(QDBusVariant): [Variant(int): 1]]"; +} + +void tst_QDBusMarshall::sendArrays_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<QString>("stringResult"); + + // arrays + QStringList strings; + QTest::newRow("emptystringlist") << QVariant(strings) << "as" << "{}"; + strings << "hello" << "world"; + QTest::newRow("stringlist") << QVariant(strings) << "as" << "{\"hello\", \"world\"}"; + + strings.clear(); + strings << "" << "" << ""; + QTest::newRow("list-of-emptystrings") << QVariant(strings) << "as" << "{\"\", \"\", \"\"}"; + + strings.clear(); + strings << QString() << QString() << QString() << QString(); + QTest::newRow("list-of-nullstrings") << QVariant(strings) << "as" << "{\"\", \"\", \"\", \"\"}"; + + QByteArray bytearray; + QTest::newRow("nullbytearray") << QVariant(bytearray) << "ay" << "{}"; + bytearray = ""; // empty, not null + QTest::newRow("emptybytearray") << QVariant(bytearray) << "ay" << "{}"; + bytearray = "foo"; + QTest::newRow("bytearray") << QVariant(bytearray) << "ay" << "{102, 111, 111}"; + + QList<bool> bools; + QTest::newRow("emptyboollist") << qVariantFromValue(bools) << "ab" << "[Argument: ab {}]"; + bools << false << true << false; + QTest::newRow("boollist") << qVariantFromValue(bools) << "ab" << "[Argument: ab {false, true, false}]"; + + QList<short> shorts; + QTest::newRow("emptyshortlist") << qVariantFromValue(shorts) << "an" << "[Argument: an {}]"; + shorts << 42 << -43 << 44 << 45 << -32768 << 32767; + QTest::newRow("shortlist") << qVariantFromValue(shorts) << "an" + << "[Argument: an {42, -43, 44, 45, -32768, 32767}]"; + + QList<ushort> ushorts; + QTest::newRow("emptyushortlist") << qVariantFromValue(ushorts) << "aq" << "[Argument: aq {}]"; + ushorts << 12u << 13u << 14u << 15 << 65535; + QTest::newRow("ushortlist") << qVariantFromValue(ushorts) << "aq" << "[Argument: aq {12, 13, 14, 15, 65535}]"; + + QList<int> ints; + QTest::newRow("emptyintlist") << qVariantFromValue(ints) << "ai" << "[Argument: ai {}]"; + ints << 42 << -43 << 44 << 45 << 2147483647 << -2147483647-1; + QTest::newRow("intlist") << qVariantFromValue(ints) << "ai" << "[Argument: ai {42, -43, 44, 45, 2147483647, -2147483648}]"; + + QList<uint> uints; + QTest::newRow("emptyuintlist") << qVariantFromValue(uints) << "au" << "[Argument: au {}]"; + uints << uint(12) << uint(13) << uint(14) << 4294967295U; + QTest::newRow("uintlist") << qVariantFromValue(uints) << "au" << "[Argument: au {12, 13, 14, 4294967295}]"; + + QList<qlonglong> llints; + QTest::newRow("emptyllintlist") << qVariantFromValue(llints) << "ax" << "[Argument: ax {}]"; + llints << Q_INT64_C(99) << Q_INT64_C(-100) + << Q_INT64_C(-9223372036854775807)-1 << Q_INT64_C(9223372036854775807); + QTest::newRow("llintlist") << qVariantFromValue(llints) << "ax" + << "[Argument: ax {99, -100, -9223372036854775808, 9223372036854775807}]"; + + QList<qulonglong> ullints; + QTest::newRow("emptyullintlist") << qVariantFromValue(ullints) << "at" << "[Argument: at {}]"; + ullints << Q_UINT64_C(66) << Q_UINT64_C(67) + << Q_UINT64_C(18446744073709551615); + QTest::newRow("ullintlist") << qVariantFromValue(ullints) << "at" << "[Argument: at {66, 67, 18446744073709551615}]"; + + QList<double> doubles; + QTest::newRow("emptydoublelist") << qVariantFromValue(doubles) << "ad" << "[Argument: ad {}]"; + doubles << 1.2 << 2.2 << 4.4 + << -std::numeric_limits<double>::infinity() + << std::numeric_limits<double>::infinity() + << std::numeric_limits<double>::quiet_NaN(); + QTest::newRow("doublelist") << qVariantFromValue(doubles) << "ad" << "[Argument: ad {1.2, 2.2, 4.4, -inf, inf, nan}]"; + + QList<QDBusObjectPath> objectPaths; + QTest::newRow("emptyobjectpathlist") << qVariantFromValue(objectPaths) << "ao" << "[Argument: ao {}]"; + objectPaths << QDBusObjectPath("/") << QDBusObjectPath("/foo"); + QTest::newRow("objectpathlist") << qVariantFromValue(objectPaths) << "ao" << "[Argument: ao {[ObjectPath: /], [ObjectPath: /foo]}]"; + + if (fileDescriptorPassing) { + QList<QDBusUnixFileDescriptor> fileDescriptors; + QTest::newRow("emptyfiledescriptorlist") << qVariantFromValue(fileDescriptors) << "ah" << "[Argument: ah {}]"; + fileDescriptors << QDBusUnixFileDescriptor(fileDescriptorForTest()) << QDBusUnixFileDescriptor(1); + QTest::newRow("filedescriptorlist") << qVariantFromValue(fileDescriptors) << "ah" << "[Argument: ah {[Unix FD: valid], [Unix FD: valid]}]"; + } + + QVariantList variants; + QTest::newRow("emptyvariantlist") << QVariant(variants) << "av" << "[Argument: av {}]"; + variants << QString("Hello") << QByteArray("World") << 42 << -43.0 << 44U << Q_INT64_C(-45) + << Q_UINT64_C(46) << true << qVariantFromValue(short(-47)) + << qVariantFromValue(QDBusSignature("av")) + << qVariantFromValue(QDBusVariant(qVariantFromValue(QDBusObjectPath("/")))); + QTest::newRow("variantlist") << QVariant(variants) << "av" + << "[Argument: av {[Variant(QString): \"Hello\"], [Variant(QByteArray): {87, 111, 114, 108, 100}], [Variant(int): 42], [Variant(double): -43], [Variant(uint): 44], [Variant(qlonglong): -45], [Variant(qulonglong): 46], [Variant(bool): true], [Variant(short): -47], [Variant: [Signature: av]], [Variant: [Variant: [ObjectPath: /]]]}]"; +} + +void tst_QDBusMarshall::sendArrayOfArrays_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<QString>("stringResult"); + + // arrays: + QList<QStringList> strings; + QTest::newRow("empty-list-of-stringlist") << qVariantFromValue(strings) << "aas" + << "[Argument: aas {}]"; + strings << QStringList(); + QTest::newRow("list-of-emptystringlist") << qVariantFromValue(strings) << "aas" + << "[Argument: aas {{}}]"; + strings << (QStringList() << "hello" << "world") + << (QStringList() << "hi" << "there") + << (QStringList() << QString()); + QTest::newRow("stringlist") << qVariantFromValue(strings) << "aas" + << "[Argument: aas {{}, {\"hello\", \"world\"}, {\"hi\", \"there\"}, {\"\"}}]"; + + QList<QByteArray> bytearray; + QTest::newRow("empty-list-of-bytearray") << qVariantFromValue(bytearray) << "aay" + << "[Argument: aay {}]"; + bytearray << QByteArray(); + QTest::newRow("list-of-emptybytearray") << qVariantFromValue(bytearray) << "aay" + << "[Argument: aay {{}}]"; + bytearray << "foo" << "bar" << "baz" << "" << QByteArray(); + QTest::newRow("bytearray") << qVariantFromValue(bytearray) << "aay" + << "[Argument: aay {{}, {102, 111, 111}, {98, 97, 114}, {98, 97, 122}, {}, {}}]"; + + QList<QList<bool> > bools; + QTest::newRow("empty-list-of-boollist") << qVariantFromValue(bools) << "aab" + << "[Argument: aab {}]"; + bools << QList<bool>(); + QTest::newRow("list-of-emptyboollist") << qVariantFromValue(bools) << "aab" + << "[Argument: aab {[Argument: ab {}]}]"; + bools << (QList<bool>() << false << true) << (QList<bool>() << false) << (QList<bool>()); + QTest::newRow("boollist") << qVariantFromValue(bools) << "aab" + << "[Argument: aab {[Argument: ab {}], [Argument: ab {false, true}], [Argument: ab {false}], [Argument: ab {}]}]"; + QList<QList<short> > shorts; + QTest::newRow("empty-list-of-shortlist") << qVariantFromValue(shorts) << "aan" + << "[Argument: aan {}]"; + shorts << QList<short>(); + QTest::newRow("list-of-emptyshortlist") << qVariantFromValue(shorts) << "aan" + << "[Argument: aan {[Argument: an {}]}]"; + shorts << (QList<short>() << 42 << -43 << 44 << 45) + << (QList<short>() << -32768 << 32767) + << (QList<short>()); + QTest::newRow("shortlist") << qVariantFromValue(shorts) << "aan" + << "[Argument: aan {[Argument: an {}], [Argument: an {42, -43, 44, 45}], [Argument: an {-32768, 32767}], [Argument: an {}]}]"; + + QList<QList<ushort> > ushorts; + QTest::newRow("empty-list-of-ushortlist") << qVariantFromValue(ushorts) << "aaq" + << "[Argument: aaq {}]"; + ushorts << QList<ushort>(); + QTest::newRow("list-of-emptyushortlist") << qVariantFromValue(ushorts) << "aaq" + << "[Argument: aaq {[Argument: aq {}]}]"; + ushorts << (QList<ushort>() << 12u << 13u << 14u << 15) + << (QList<ushort>() << 65535) + << (QList<ushort>()); + QTest::newRow("ushortlist") << qVariantFromValue(ushorts) << "aaq" + << "[Argument: aaq {[Argument: aq {}], [Argument: aq {12, 13, 14, 15}], [Argument: aq {65535}], [Argument: aq {}]}]"; + + QList<QList<int> > ints; + QTest::newRow("empty-list-of-intlist") << qVariantFromValue(ints) << "aai" + << "[Argument: aai {}]"; + ints << QList<int>(); + QTest::newRow("list-of-emptyintlist") << qVariantFromValue(ints) << "aai" + << "[Argument: aai {[Argument: ai {}]}]"; + ints << (QList<int>() << 42 << -43 << 44 << 45) + << (QList<int>() << 2147483647 << -2147483647-1) + << (QList<int>()); + QTest::newRow("intlist") << qVariantFromValue(ints) << "aai" + << "[Argument: aai {[Argument: ai {}], [Argument: ai {42, -43, 44, 45}], [Argument: ai {2147483647, -2147483648}], [Argument: ai {}]}]"; + + QList<QList<uint> > uints; + QTest::newRow("empty-list-of-uintlist") << qVariantFromValue(uints) << "aau" + << "[Argument: aau {}]"; + uints << QList<uint>(); + QTest::newRow("list-of-emptyuintlist") << qVariantFromValue(uints) << "aau" + << "[Argument: aau {[Argument: au {}]}]"; + uints << (QList<uint>() << uint(12) << uint(13) << uint(14)) + << (QList<uint>() << 4294967295U) + << (QList<uint>()); + QTest::newRow("uintlist") << qVariantFromValue(uints) << "aau" + << "[Argument: aau {[Argument: au {}], [Argument: au {12, 13, 14}], [Argument: au {4294967295}], [Argument: au {}]}]"; + + QList<QList<qlonglong> > llints; + QTest::newRow("empty-list-of-llintlist") << qVariantFromValue(llints) << "aax" + << "[Argument: aax {}]"; + llints << QList<qlonglong>(); + QTest::newRow("list-of-emptyllintlist") << qVariantFromValue(llints) << "aax" + << "[Argument: aax {[Argument: ax {}]}]"; + llints << (QList<qlonglong>() << Q_INT64_C(99) << Q_INT64_C(-100)) + << (QList<qlonglong>() << Q_INT64_C(-9223372036854775807)-1 << Q_INT64_C(9223372036854775807)) + << (QList<qlonglong>()); + QTest::newRow("llintlist") << qVariantFromValue(llints) << "aax" + << "[Argument: aax {[Argument: ax {}], [Argument: ax {99, -100}], [Argument: ax {-9223372036854775808, 9223372036854775807}], [Argument: ax {}]}]"; + + QList<QList<qulonglong> > ullints; + QTest::newRow("empty-list-of-ullintlist") << qVariantFromValue(ullints) << "aat" + << "[Argument: aat {}]"; + ullints << QList<qulonglong>(); + QTest::newRow("list-of-emptyullintlist") << qVariantFromValue(ullints) << "aat" + << "[Argument: aat {[Argument: at {}]}]"; + ullints << (QList<qulonglong>() << Q_UINT64_C(66) << Q_UINT64_C(67)) + << (QList<qulonglong>() << Q_UINT64_C(18446744073709551615)) + << (QList<qulonglong>()); + QTest::newRow("ullintlist") << qVariantFromValue(ullints) << "aat" + << "[Argument: aat {[Argument: at {}], [Argument: at {66, 67}], [Argument: at {18446744073709551615}], [Argument: at {}]}]"; + + QList<QList<double> > doubles; + QTest::newRow("empty-list-ofdoublelist") << qVariantFromValue(doubles) << "aad" + << "[Argument: aad {}]"; + doubles << QList<double>(); + QTest::newRow("list-of-emptydoublelist") << qVariantFromValue(doubles) << "aad" + << "[Argument: aad {[Argument: ad {}]}]"; + doubles << (QList<double>() << 1.2 << 2.2 << 4.4) + << (QList<double>() << -std::numeric_limits<double>::infinity() + << std::numeric_limits<double>::infinity() + << std::numeric_limits<double>::quiet_NaN()) + << (QList<double>()); + QTest::newRow("doublelist") << qVariantFromValue(doubles) << "aad" + << "[Argument: aad {[Argument: ad {}], [Argument: ad {1.2, 2.2, 4.4}], [Argument: ad {-inf, inf, nan}], [Argument: ad {}]}]"; + + QList<QVariantList> variants; + QTest::newRow("emptyvariantlist") << qVariantFromValue(variants) << "aav" + << "[Argument: aav {}]"; + variants << QVariantList(); + QTest::newRow("emptyvariantlist") << qVariantFromValue(variants) << "aav" + << "[Argument: aav {[Argument: av {}]}]"; + variants << (QVariantList() << QString("Hello") << QByteArray("World")) + << (QVariantList() << 42 << -43.0 << 44U << Q_INT64_C(-45)) + << (QVariantList() << Q_UINT64_C(46) << true << qVariantFromValue(short(-47))); + QTest::newRow("variantlist") << qVariantFromValue(variants) << "aav" + << "[Argument: aav {[Argument: av {}], [Argument: av {[Variant(QString): \"Hello\"], [Variant(QByteArray): {87, 111, 114, 108, 100}]}], [Argument: av {[Variant(int): 42], [Variant(double): -43], [Variant(uint): 44], [Variant(qlonglong): -45]}], [Argument: av {[Variant(qulonglong): 46], [Variant(bool): true], [Variant(short): -47]}]}]"; +} + +void tst_QDBusMarshall::sendMaps_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<QString>("stringResult"); + + QMap<int, QString> ismap; + QTest::newRow("empty-is-map") << qVariantFromValue(ismap) << "a{is}" + << "[Argument: a{is} {}]"; + ismap[1] = "a"; + ismap[2000] = "b"; + ismap[-47] = "c"; + QTest::newRow("is-map") << qVariantFromValue(ismap) << "a{is}" + << "[Argument: a{is} {-47 = \"c\", 1 = \"a\", 2000 = \"b\"}]"; + + QMap<QString, QString> ssmap; + QTest::newRow("empty-ss-map") << qVariantFromValue(ssmap) << "a{ss}" + << "[Argument: a{ss} {}]"; + ssmap["a"] = "a"; + ssmap["c"] = "b"; + ssmap["b"] = "c"; + QTest::newRow("ss-map") << qVariantFromValue(ssmap) << "a{ss}" + << "[Argument: a{ss} {\"a\" = \"a\", \"b\" = \"c\", \"c\" = \"b\"}]"; + + QVariantMap svmap; + QTest::newRow("empty-sv-map") << qVariantFromValue(svmap) << "a{sv}" + << "[Argument: a{sv} {}]"; + svmap["a"] = 1; + svmap["c"] = "b"; + svmap["b"] = QByteArray("c"); + svmap["d"] = 42U; + svmap["e"] = qVariantFromValue(short(-47)); + svmap["f"] = qVariantFromValue(QDBusVariant(0)); + QTest::newRow("sv-map1") << qVariantFromValue(svmap) << "a{sv}" + << "[Argument: a{sv} {\"a\" = [Variant(int): 1], \"b\" = [Variant(QByteArray): {99}], \"c\" = [Variant(QString): \"b\"], \"d\" = [Variant(uint): 42], \"e\" = [Variant(short): -47], \"f\" = [Variant: [Variant(int): 0]]}]"; + + QMap<QDBusObjectPath, QString> osmap; + QTest::newRow("empty-os-map") << qVariantFromValue(osmap) << "a{os}" + << "[Argument: a{os} {}]"; + osmap[QDBusObjectPath("/")] = "root"; + osmap[QDBusObjectPath("/foo")] = "foo"; + osmap[QDBusObjectPath("/bar/baz")] = "bar and baz"; + QTest::newRow("os-map") << qVariantFromValue(osmap) << "a{os}" + << "[Argument: a{os} {[ObjectPath: /] = \"root\", [ObjectPath: /bar/baz] = \"bar and baz\", [ObjectPath: /foo] = \"foo\"}]"; + + QHash<QDBusSignature, QString> gsmap; + QTest::newRow("empty-gs-map") << qVariantFromValue(gsmap) << "a{gs}" + << "[Argument: a{gs} {}]"; + gsmap[QDBusSignature("i")] = "int32"; + gsmap[QDBusSignature("s")] = "string"; + gsmap[QDBusSignature("a{gs}")] = "array of dict_entry of (signature, string)"; + QTest::newRow("gs-map") << qVariantFromValue(gsmap) << "a{gs}" + << "[Argument: a{gs} {[Signature: a{gs}] = \"array of dict_entry of (signature, string)\", [Signature: i] = \"int32\", [Signature: s] = \"string\"}]"; + + if (fileDescriptorPassing) { + svmap["zzfiledescriptor"] = qVariantFromValue(QDBusUnixFileDescriptor(fileDescriptorForTest())); + QTest::newRow("sv-map1-fd") << qVariantFromValue(svmap) << "a{sv}" + << "[Argument: a{sv} {\"a\" = [Variant(int): 1], \"b\" = [Variant(QByteArray): {99}], \"c\" = [Variant(QString): \"b\"], \"d\" = [Variant(uint): 42], \"e\" = [Variant(short): -47], \"f\" = [Variant: [Variant(int): 0]], \"zzfiledescriptor\" = [Variant(QDBusUnixFileDescriptor): [Unix FD: valid]]}]"; + } + + svmap.clear(); + svmap["ismap"] = qVariantFromValue(ismap); + svmap["ssmap"] = qVariantFromValue(ssmap); + svmap["osmap"] = qVariantFromValue(osmap); + svmap["gsmap"] = qVariantFromValue(gsmap); + QTest::newRow("sv-map2") << qVariantFromValue(svmap) << "a{sv}" + << "[Argument: a{sv} {\"gsmap\" = [Variant: [Argument: a{gs} {[Signature: a{gs}] = \"array of dict_entry of (signature, string)\", [Signature: i] = \"int32\", [Signature: s] = \"string\"}]], \"ismap\" = [Variant: [Argument: a{is} {-47 = \"c\", 1 = \"a\", 2000 = \"b\"}]], \"osmap\" = [Variant: [Argument: a{os} {[ObjectPath: /] = \"root\", [ObjectPath: /bar/baz] = \"bar and baz\", [ObjectPath: /foo] = \"foo\"}]], \"ssmap\" = [Variant: [Argument: a{ss} {\"a\" = \"a\", \"b\" = \"c\", \"c\" = \"b\"}]]}]"; +} + +void tst_QDBusMarshall::sendStructs_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<QString>("stringResult"); + + QTest::newRow("point") << QVariant(QPoint(1, 2)) << "(ii)" << "[Argument: (ii) 1, 2]"; + QTest::newRow("pointf") << QVariant(QPointF(1.5, -1.5)) << "(dd)" << "[Argument: (dd) 1.5, -1.5]"; + + QTest::newRow("size") << QVariant(QSize(1, 2)) << "(ii)" << "[Argument: (ii) 1, 2]"; + QTest::newRow("sizef") << QVariant(QSizeF(1.5, 1.5)) << "(dd)" << "[Argument: (dd) 1.5, 1.5]"; + + QTest::newRow("rect") << QVariant(QRect(1, 2, 3, 4)) << "(iiii)" << "[Argument: (iiii) 1, 2, 3, 4]"; + QTest::newRow("rectf") << QVariant(QRectF(0.5, 0.5, 1.5, 1.5)) << "(dddd)" << "[Argument: (dddd) 0.5, 0.5, 1.5, 1.5]"; + + QTest::newRow("line") << QVariant(QLine(1, 2, 3, 4)) << "((ii)(ii))" + << "[Argument: ((ii)(ii)) [Argument: (ii) 1, 2], [Argument: (ii) 3, 4]]"; + QTest::newRow("linef") << QVariant(QLineF(0.5, 0.5, 1.5, 1.5)) << "((dd)(dd))" + << "[Argument: ((dd)(dd)) [Argument: (dd) 0.5, 0.5], [Argument: (dd) 1.5, 1.5]]"; + + QDate date(2006, 6, 18); + QTime time(12, 25, 00); // the date I wrote this test on :-) + QTest::newRow("date") << QVariant(date) << "(iii)" << "[Argument: (iii) 2006, 6, 18]"; + QTest::newRow("time") << QVariant(time) << "(iiii)" << "[Argument: (iiii) 12, 25, 0, 0]"; + QTest::newRow("datetime") << QVariant(QDateTime(date, time)) << "((iii)(iiii)i)" + << "[Argument: ((iii)(iiii)i) [Argument: (iii) 2006, 6, 18], [Argument: (iiii) 12, 25, 0, 0], 0]"; + + MyStruct ms = { 1, "Hello, World" }; + QTest::newRow("int-string") << qVariantFromValue(ms) << "(is)" << "[Argument: (is) 1, \"Hello, World\"]"; + + MyVariantMapStruct mvms = { "Hello, World", QVariantMap() }; + QTest::newRow("string-variantmap") << qVariantFromValue(mvms) << "(sa{sv})" << "[Argument: (sa{sv}) \"Hello, World\", [Argument: a{sv} {}]]"; + + // use only basic types, otherwise comparison will fail + mvms.map["int"] = 42; + mvms.map["uint"] = 42u; + mvms.map["short"] = qVariantFromValue<short>(-47); + mvms.map["bytearray"] = QByteArray("Hello, world"); + QTest::newRow("string-variantmap2") << qVariantFromValue(mvms) << "(sa{sv})" << "[Argument: (sa{sv}) \"Hello, World\", [Argument: a{sv} {\"bytearray\" = [Variant(QByteArray): {72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100}], \"int\" = [Variant(int): 42], \"short\" = [Variant(short): -47], \"uint\" = [Variant(uint): 42]}]]"; + + QList<MyVariantMapStruct> list; + QTest::newRow("empty-list-of-string-variantmap") << qVariantFromValue(list) << "a(sa{sv})" << "[Argument: a(sa{sv}) {}]"; + list << mvms; + QTest::newRow("list-of-string-variantmap") << qVariantFromValue(list) << "a(sa{sv})" << "[Argument: a(sa{sv}) {[Argument: (sa{sv}) \"Hello, World\", [Argument: a{sv} {\"bytearray\" = [Variant(QByteArray): {72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100}], \"int\" = [Variant(int): 42], \"short\" = [Variant(short): -47], \"uint\" = [Variant(uint): 42]}]]}]"; + + if (fileDescriptorPassing) { + MyFileDescriptorStruct fds; + fds.fd = QDBusUnixFileDescriptor(fileDescriptorForTest()); + QTest::newRow("fdstruct") << qVariantFromValue(fds) << "(h)" << "[Argument: (h) [Unix FD: valid]]"; + + QList<MyFileDescriptorStruct> fdlist; + QTest::newRow("empty-list-of-fdstruct") << qVariantFromValue(fdlist) << "a(h)" << "[Argument: a(h) {}]"; + + fdlist << fds; + QTest::newRow("list-of-fdstruct") << qVariantFromValue(fdlist) << "a(h)" << "[Argument: a(h) {[Argument: (h) [Unix FD: valid]]}]"; + } +} + +void tst_QDBusMarshall::sendComplex_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<QString>("stringResult"); + + QList<QDateTime> dtlist; + QTest::newRow("empty-datetimelist") << qVariantFromValue(dtlist) << "a((iii)(iiii)i)" + << "[Argument: a((iii)(iiii)i) {}]"; + dtlist << QDateTime(); + QTest::newRow("list-of-emptydatetime") << qVariantFromValue(dtlist) << "a((iii)(iiii)i)" + << "[Argument: a((iii)(iiii)i) {[Argument: ((iii)(iiii)i) [Argument: (iii) 0, 0, 0], [Argument: (iiii) -1, -1, -1, -1], 0]}]"; + dtlist << QDateTime(QDate(1977, 9, 13), QTime(0, 0, 0)) + << QDateTime(QDate(2006, 6, 18), QTime(13, 14, 0)); + QTest::newRow("datetimelist") << qVariantFromValue(dtlist) << "a((iii)(iiii)i)" + << "[Argument: a((iii)(iiii)i) {[Argument: ((iii)(iiii)i) [Argument: (iii) 0, 0, 0], [Argument: (iiii) -1, -1, -1, -1], 0], [Argument: ((iii)(iiii)i) [Argument: (iii) 1977, 9, 13], [Argument: (iiii) 0, 0, 0, 0], 0], [Argument: ((iii)(iiii)i) [Argument: (iii) 2006, 6, 18], [Argument: (iiii) 13, 14, 0, 0], 0]}]"; + + QHash<qlonglong, QDateTime> lldtmap; + QTest::newRow("empty-lldtmap") << qVariantFromValue(lldtmap) << "a{x((iii)(iiii)i)}" + << "[Argument: a{x((iii)(iiii)i)} {}]"; + lldtmap[0] = QDateTime(); + lldtmap[1] = QDateTime(QDate(1970, 1, 1), QTime(0, 0, 1), Qt::UTC); + lldtmap[1150629776] = QDateTime(QDate(2006, 6, 18), QTime(11, 22, 56), Qt::UTC); + QTest::newRow("lldtmap") << qVariantFromValue(lldtmap) << "a{x((iii)(iiii)i)}" + << "[Argument: a{x((iii)(iiii)i)} {0 = [Argument: ((iii)(iiii)i) [Argument: (iii) 0, 0, 0], [Argument: (iiii) -1, -1, -1, -1], 0], 1 = [Argument: ((iii)(iiii)i) [Argument: (iii) 1970, 1, 1], [Argument: (iiii) 0, 0, 1, 0], 1], 1150629776 = [Argument: ((iii)(iiii)i) [Argument: (iii) 2006, 6, 18], [Argument: (iiii) 11, 22, 56, 0], 1]}]"; + + + QMap<int, QString> ismap; + ismap[1] = "a"; + ismap[2000] = "b"; + ismap[-47] = "c"; + + QMap<QString, QString> ssmap; + ssmap["a"] = "a"; + ssmap["c"] = "b"; + ssmap["b"] = "c"; + + QHash<QDBusSignature, QString> gsmap; + gsmap[QDBusSignature("i")] = "int32"; + gsmap[QDBusSignature("s")] = "string"; + gsmap[QDBusSignature("a{gs}")] = "array of dict_entry of (signature, string)"; + + QVariantMap svmap; + svmap["a"] = 1; + svmap["c"] = "b"; + svmap["b"] = QByteArray("c"); + svmap["d"] = 42U; + svmap["e"] = qVariantFromValue(short(-47)); + svmap["f"] = qVariantFromValue(QDBusVariant(0)); + svmap["date"] = QDate(1977, 1, 1); + svmap["time"] = QTime(8, 58, 0); + svmap["datetime"] = QDateTime(QDate(13, 9, 2008), QTime(8, 59, 31)); + svmap["pointf"] = QPointF(0.5, -0.5); + svmap["ismap"] = qVariantFromValue(ismap); + svmap["ssmap"] = qVariantFromValue(ssmap); + svmap["gsmap"] = qVariantFromValue(gsmap); + svmap["dtlist"] = qVariantFromValue(dtlist); + svmap["lldtmap"] = qVariantFromValue(lldtmap); + QTest::newRow("sv-map") << qVariantFromValue(svmap) << "a{sv}" + << "[Argument: a{sv} {\"a\" = [Variant(int): 1], \"b\" = [Variant(QByteArray): {99}], \"c\" = [Variant(QString): \"b\"], \"d\" = [Variant(uint): 42], \"date\" = [Variant: [Argument: (iii) 1977, 1, 1]], \"datetime\" = [Variant: [Argument: ((iii)(iiii)i) [Argument: (iii) 0, 0, 0], [Argument: (iiii) 8, 59, 31, 0], 0]], \"dtlist\" = [Variant: [Argument: a((iii)(iiii)i) {[Argument: ((iii)(iiii)i) [Argument: (iii) 0, 0, 0], [Argument: (iiii) -1, -1, -1, -1], 0], [Argument: ((iii)(iiii)i) [Argument: (iii) 1977, 9, 13], [Argument: (iiii) 0, 0, 0, 0], 0], [Argument: ((iii)(iiii)i) [Argument: (iii) 2006, 6, 18], [Argument: (iiii) 13, 14, 0, 0], 0]}]], \"e\" = [Variant(short): -47], \"f\" = [Variant: [Variant(int): 0]], \"gsmap\" = [Variant: [Argument: a{gs} {[Signature: a{gs}] = \"array of dict_entry of (signature, string)\", [Signature: i] = \"int32\", [Signature: s] = \"string\"}]], \"ismap\" = [Variant: [Argument: a{is} {-47 = \"c\", 1 = \"a\", 2000 = \"b\"}]], \"lldtmap\" = [Variant: [Argument: a{x((iii)(iiii)i)} {0 = [Argument: ((iii)(iiii)i) [Argument: (iii) 0, 0, 0], [Argument: (iiii) -1, -1, -1, -1], 0], 1 = [Argument: ((iii)(iiii)i) [Argument: (iii) 1970, 1, 1], [Argument: (iiii) 0, 0, 1, 0], 1], 1150629776 = [Argument: ((iii)(iiii)i) [Argument: (iii) 2006, 6, 18], [Argument: (iiii) 11, 22, 56, 0], 1]}]], \"pointf\" = [Variant: [Argument: (dd) 0.5, -0.5]], \"ssmap\" = [Variant: [Argument: a{ss} {\"a\" = \"a\", \"b\" = \"c\", \"c\" = \"b\"}]], \"time\" = [Variant: [Argument: (iiii) 8, 58, 0, 0]]}]"; +} + +void tst_QDBusMarshall::sendArgument_data() +{ + QTest::addColumn<QVariant>("value"); + QTest::addColumn<QString>("sig"); + QTest::addColumn<int>("classification"); + + QDBusArgument(); + QDBusArgument arg; + + arg = QDBusArgument(); + arg << true; + QTest::newRow("bool") << qVariantFromValue(arg) << "b" << int(QDBusArgument::BasicType);; + + arg = QDBusArgument(); + arg << false; + QTest::newRow("bool2") << qVariantFromValue(arg) << "b" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << uchar(1); + QTest::newRow("byte") << qVariantFromValue(arg) << "y" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << short(2); + QTest::newRow("int16") << qVariantFromValue(arg) << "n" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << ushort(3); + QTest::newRow("uint16") << qVariantFromValue(arg) << "q" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << 1; + QTest::newRow("int32") << qVariantFromValue(arg) << "i" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << 2U; + QTest::newRow("uint32") << qVariantFromValue(arg) << "u" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << Q_INT64_C(3); + QTest::newRow("int64") << qVariantFromValue(arg) << "x" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << Q_UINT64_C(4); + QTest::newRow("uint64") << qVariantFromValue(arg) << "t" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << 42.5; + QTest::newRow("double") << qVariantFromValue(arg) << "d" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << QLatin1String("ping"); + QTest::newRow("string") << qVariantFromValue(arg) << "s" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << QDBusObjectPath("/org/kde"); + QTest::newRow("objectpath") << qVariantFromValue(arg) << "o" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << QDBusSignature("g"); + QTest::newRow("signature") << qVariantFromValue(arg) << "g" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << QLatin1String(""); + QTest::newRow("emptystring") << qVariantFromValue(arg) << "s" << int(QDBusArgument::BasicType); + + arg = QDBusArgument(); + arg << QString(); + QTest::newRow("nullstring") << qVariantFromValue(arg) << "s" << int(QDBusArgument::BasicType); + + if (fileDescriptorPassing) { + arg = QDBusArgument(); + arg << QDBusUnixFileDescriptor(fileDescriptorForTest()); + QTest::newRow("filedescriptor") << qVariantFromValue(arg) << "h" << int(QDBusArgument::BasicType); + } + + arg = QDBusArgument(); + arg << QDBusVariant(1); + QTest::newRow("variant") << qVariantFromValue(arg) << "v" << int(QDBusArgument::VariantType); + + arg = QDBusArgument(); + arg << QDBusVariant(qVariantFromValue(QDBusVariant(1))); + QTest::newRow("variant-variant") << qVariantFromValue(arg) << "v" << int(QDBusArgument::VariantType); + + arg = QDBusArgument(); + arg.beginArray(QVariant::Int); + arg << 1 << 2 << 3 << -4; + arg.endArray(); + QTest::newRow("array-of-int") << qVariantFromValue(arg) << "ai" << int(QDBusArgument::ArrayType); + + arg = QDBusArgument(); + arg.beginMap(QVariant::Int, QVariant::UInt); + arg.beginMapEntry(); + arg << 1 << 2U; + arg.endMapEntry(); + arg.beginMapEntry(); + arg << 3 << 4U; + arg.endMapEntry(); + arg.endMap(); + QTest::newRow("map") << qVariantFromValue(arg) << "a{iu}" << int(QDBusArgument::MapType); + + arg = QDBusArgument(); + arg.beginStructure(); + arg << 1 << 2U << short(-3) << ushort(4) << 5.0 << false; + arg.endStructure(); + QTest::newRow("structure") << qVariantFromValue(arg) << "(iunqdb)" << int(QDBusArgument::StructureType); + +#if 0 + // this is now unsupported + arg << 1 << 2U << short(-3) << ushort(4) << 5.0 << false; + QTest::newRow("many-args") << qVariantFromValue(arg) << "(iunqdb)iunqdb"; +#endif +} + +void tst_QDBusMarshall::sendBasic() +{ + QFETCH(QVariant, value); + QFETCH(QString, stringResult); + + QDBusConnection con = QDBusConnection::sessionBus(); + + QVERIFY(con.isConnected()); + + QDBusMessage msg = QDBusMessage::createMethodCall(serviceName, + objectPath, interfaceName, "ping"); + msg << value; + + QDBusMessage reply = con.call(msg); + QVERIFY2(reply.type() == QDBusMessage::ReplyMessage, + qPrintable(reply.errorName() + ": " + reply.errorMessage())); + //qDebug() << reply; + + QCOMPARE(reply.arguments().count(), msg.arguments().count()); + QTEST(reply.signature(), "sig"); + for (int i = 0; i < reply.arguments().count(); ++i) { + QVERIFY(compare(reply.arguments().at(i), msg.arguments().at(i))); + //printf("\n! %s\n* %s\n", qPrintable(qDBusArgumentToString(reply.arguments().at(i))), qPrintable(stringResult)); + QCOMPARE(QDBusUtil::argumentToString(reply.arguments().at(i)), stringResult); + } +} + +void tst_QDBusMarshall::sendVariant() +{ + QFETCH(QVariant, value); + + QDBusConnection con = QDBusConnection::sessionBus(); + + QVERIFY(con.isConnected()); + + QDBusMessage msg = QDBusMessage::createMethodCall(serviceName, + objectPath, interfaceName, "ping"); + msg << qVariantFromValue(QDBusVariant(value)); + + QDBusMessage reply = con.call(msg); + // qDebug() << reply; + + QCOMPARE(reply.arguments().count(), msg.arguments().count()); + QCOMPARE(reply.signature(), QString("v")); + for (int i = 0; i < reply.arguments().count(); ++i) + QVERIFY(compare(reply.arguments().at(i), msg.arguments().at(i))); +} + +void tst_QDBusMarshall::sendArrays() +{ + sendBasic(); +} + +void tst_QDBusMarshall::sendArrayOfArrays() +{ + sendBasic(); +} + +void tst_QDBusMarshall::sendMaps() +{ + sendBasic(); +} + +void tst_QDBusMarshall::sendStructs() +{ + sendBasic(); +} + +void tst_QDBusMarshall::sendComplex() +{ + sendBasic(); +} + +void tst_QDBusMarshall::sendArgument() +{ + QFETCH(QVariant, value); + QFETCH(QString, sig); + + QDBusConnection con = QDBusConnection::sessionBus(); + + QVERIFY(con.isConnected()); + + QDBusMessage msg = QDBusMessage::createMethodCall(serviceName, objectPath, + interfaceName, "ping"); + msg << value; + + QDBusMessage reply = con.call(msg); + +// QCOMPARE(reply.arguments().count(), msg.arguments().count()); + QCOMPARE(reply.signature(), sig); +// for (int i = 0; i < reply.arguments().count(); ++i) +// QVERIFY(compare(reply.arguments().at(i), msg.arguments().at(i))); + + // do it again inside a STRUCT now + QDBusArgument sendArg; + sendArg.beginStructure(); + sendArg.appendVariant(value); + sendArg.endStructure(); + msg.setArguments(QVariantList() << qVariantFromValue(sendArg)); + reply = con.call(msg); + + QCOMPARE(reply.signature(), QString("(%1)").arg(sig)); + QCOMPARE(reply.arguments().at(0).userType(), qMetaTypeId<QDBusArgument>()); + + const QDBusArgument arg = qvariant_cast<QDBusArgument>(reply.arguments().at(0)); + QCOMPARE(int(arg.currentType()), int(QDBusArgument::StructureType)); + + arg.beginStructure(); + QVERIFY(!arg.atEnd()); + QCOMPARE(arg.currentSignature(), sig); + QTEST(int(arg.currentType()), "classification"); + + QVariant extracted = arg.asVariant(); + QVERIFY(arg.atEnd()); + + arg.endStructure(); + QVERIFY(arg.atEnd()); + QCOMPARE(arg.currentType(), QDBusArgument::UnknownType); + + if (value.type() != QVariant::UserType) + QCOMPARE(extracted, value); +} + +void tst_QDBusMarshall::sendSignalErrors() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + + QVERIFY(con.isConnected()); + QDBusMessage msg = QDBusMessage::createSignal("/foo", "local.interfaceName", + "signalName"); + msg << qVariantFromValue(QDBusObjectPath()); + + QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: error: could not send signal path \"/foo\" interface \"local.interfaceName\" member \"signalName\": Marshalling failed: Invalid object path passed in arguments"); + QVERIFY(!con.send(msg)); + + msg.setArguments(QVariantList()); + QDBusObjectPath path; + + QTest::ignoreMessage(QtWarningMsg, "QDBusObjectPath: invalid path \"abc\""); + path.setPath("abc"); + msg << qVariantFromValue(path); + + QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: error: could not send signal path \"/foo\" interface \"local.interfaceName\" member \"signalName\": Marshalling failed: Invalid object path passed in arguments"); + QVERIFY(!con.send(msg)); + + QDBusSignature sig; + msg.setArguments(QVariantList() << qVariantFromValue(sig)); + QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: error: could not send signal path \"/foo\" interface \"local.interfaceName\" member \"signalName\": Marshalling failed: Invalid signature passed in arguments"); + QVERIFY(!con.send(msg)); + + QTest::ignoreMessage(QtWarningMsg, "QDBusSignature: invalid signature \"a\""); + sig.setSignature("a"); + msg.setArguments(QVariantList()); + msg << qVariantFromValue(sig); + QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: error: could not send signal path \"/foo\" interface \"local.interfaceName\" member \"signalName\": Marshalling failed: Invalid signature passed in arguments"); + QVERIFY(!con.send(msg)); +} + +void tst_QDBusMarshall::sendCallErrors_data() +{ + QTest::addColumn<QString>("service"); + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("interface"); + QTest::addColumn<QString>("method"); + QTest::addColumn<QVariantList>("arguments"); + QTest::addColumn<QString>("errorName"); + QTest::addColumn<QString>("errorMsg"); + QTest::addColumn<QString>("ignoreMsg"); + + // this error comes from the bus server + QTest::newRow("empty-service") << "" << objectPath << interfaceName << "ping" << QVariantList() + << "org.freedesktop.DBus.Error.UnknownMethod" + << "Method \"ping\" with signature \"\" on interface \"com.trolltech.autotests.qpong\" doesn't exist\n" << (const char*)0; + + QTest::newRow("invalid-service") << "this isn't valid" << objectPath << interfaceName << "ping" << QVariantList() + << "com.trolltech.QtDBus.Error.InvalidService" + << "Invalid service name: this isn't valid" << ""; + + QTest::newRow("empty-path") << serviceName << "" << interfaceName << "ping" << QVariantList() + << "com.trolltech.QtDBus.Error.InvalidObjectPath" + << "Object path cannot be empty" << ""; + QTest::newRow("invalid-path") << serviceName << "//" << interfaceName << "ping" << QVariantList() + << "com.trolltech.QtDBus.Error.InvalidObjectPath" + << "Invalid object path: //" << ""; + + // empty interfaces are valid + QTest::newRow("invalid-interface") << serviceName << objectPath << "this isn't valid" << "ping" << QVariantList() + << "com.trolltech.QtDBus.Error.InvalidInterface" + << "Invalid interface class: this isn't valid" << ""; + + QTest::newRow("empty-method") << serviceName << objectPath << interfaceName << "" << QVariantList() + << "com.trolltech.QtDBus.Error.InvalidMember" + << "method name cannot be empty" << ""; + QTest::newRow("invalid-method") << serviceName << objectPath << interfaceName << "this isn't valid" << QVariantList() + << "com.trolltech.QtDBus.Error.InvalidMember" + << "Invalid method name: this isn't valid" << ""; + + QTest::newRow("invalid-variant1") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << QVariant()) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Variant containing QVariant::Invalid passed in arguments" + << "QDBusMarshaller: cannot add an invalid QVariant"; + QTest::newRow("invalid-variant1") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << qVariantFromValue(QDBusVariant())) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Variant containing QVariant::Invalid passed in arguments" + << "QDBusMarshaller: cannot add a null QDBusVariant"; + + QTest::newRow("builtin-unregistered") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << QLocale::c()) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Unregistered type QLocale passed in arguments" + << "QDBusMarshaller: type `QLocale' (18) is not registered with D-BUS. Use qDBusRegisterMetaType to register it"; + + // this type is known to the meta type system, but not registered with D-Bus + qRegisterMetaType<UnregisteredType>(); + QTest::newRow("extra-unregistered") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << qVariantFromValue(UnregisteredType())) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Unregistered type UnregisteredType passed in arguments" + << QString("QDBusMarshaller: type `UnregisteredType' (%1) is not registered with D-BUS. Use qDBusRegisterMetaType to register it") + .arg(qMetaTypeId<UnregisteredType>()); + + QTest::newRow("invalid-object-path-arg") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << qVariantFromValue(QDBusObjectPath())) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Invalid object path passed in arguments" + << ""; + + QTest::newRow("invalid-signature-arg") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << qVariantFromValue(QDBusSignature())) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Invalid signature passed in arguments" + << ""; + + // invalid file descriptor + if (fileDescriptorPassing) { + QTest::newRow("invalid-file-descriptor") << serviceName << objectPath << interfaceName << "ping" + << (QVariantList() << qVariantFromValue(QDBusUnixFileDescriptor(-1))) + << "org.freedesktop.DBus.Error.Failed" + << "Marshalling failed: Invalid file descriptor passed in arguments" + << ""; + } +} + +void tst_QDBusMarshall::sendCallErrors() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QFETCH(QString, service); + QFETCH(QString, path); + QFETCH(QString, interface); + QFETCH(QString, method); + QFETCH(QVariantList, arguments); + QFETCH(QString, errorMsg); + + QFETCH(QString, ignoreMsg); + if (!ignoreMsg.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, ignoreMsg.toLatin1()); + if (!ignoreMsg.isNull()) + QTest::ignoreMessage(QtWarningMsg, + QString("QDBusConnection: error: could not send message to service \"%1\" path \"%2\" interface \"%3\" member \"%4\": %5") + .arg(service, path, interface, method, errorMsg) + .toLatin1()); + + QDBusMessage msg = QDBusMessage::createMethodCall(service, path, interface, method); + msg.setArguments(arguments); + + QDBusMessage reply = con.call(msg, QDBus::Block); + QCOMPARE(reply.type(), QDBusMessage::ErrorMessage); + QTEST(reply.errorName(), "errorName"); + QCOMPARE(reply.errorMessage(), errorMsg); +} + +void tst_QDBusMarshall::receiveUnknownType_data() +{ + QTest::addColumn<int>("receivedTypeId"); + QTest::newRow("in-call") << qMetaTypeId<void*>(); + QTest::newRow("type-variant") << qMetaTypeId<QDBusVariant>(); + QTest::newRow("type-array") << qMetaTypeId<QDBusArgument>(); + QTest::newRow("type-struct") << qMetaTypeId<QDBusArgument>(); + QTest::newRow("type-naked") << qMetaTypeId<void *>(); +} + +struct DisconnectRawDBus { + static void cleanup(DBusConnection *connection) + { + if (!connection) + return; + dbus_connection_close(connection); + dbus_connection_unref(connection); + } +}; +template <typename T, void (*unref)(T *)> struct GenericUnref +{ + static void cleanup(T *type) + { + if (!type) return; + unref(type); + } +}; + +// use these scoped types to avoid memory leaks if QVERIFY or QCOMPARE fails +typedef QScopedPointer<DBusConnection, DisconnectRawDBus> ScopedDBusConnection; +typedef QScopedPointer<DBusMessage, GenericUnref<DBusMessage, dbus_message_unref> > ScopedDBusMessage; +typedef QScopedPointer<DBusPendingCall, GenericUnref<DBusPendingCall, dbus_pending_call_unref> > ScopedDBusPendingCall; + +template <typename T> struct SetResetValue +{ + const T oldValue; + T &value; +public: + SetResetValue(T &v, T newValue) : oldValue(v), value(v) + { + value = newValue; + } + ~SetResetValue() + { + value = oldValue; + } +}; + +void tst_QDBusMarshall::receiveUnknownType() +{ +#ifndef DBUS_TYPE_UNIX_FD + QSKIP("Your system's D-Bus library is too old for this test", SkipAll); +#else + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + // this needs to be implemented in raw + // open a new connection to the bus daemon + DBusError error; + dbus_error_init(&error); + ScopedDBusConnection rawcon(dbus_bus_get_private(DBUS_BUS_SESSION, &error)); + QVERIFY2(rawcon.data(), error.name); + + // check if this bus supports passing file descriptors + if (!dbus_connection_can_send_type(rawcon.data(), DBUS_TYPE_UNIX_FD)) + QSKIP("Your session bus does not allow sending Unix file descriptors", SkipAll); + + // make sure this QDBusConnection won't handle Unix file descriptors + QDBusConnection::ConnectionCapabilities &capabRef = QDBusConnectionPrivate::d(con)->capabilities; + SetResetValue<QDBusConnection::ConnectionCapabilities> resetter(capabRef, capabRef & ~QDBusConnection::UnixFileDescriptorPassing); + + if (qstrcmp(QTest::currentDataTag(), "in-call") == 0) { + // create a call back to us containing a file descriptor + QDBusMessageSpy spy; + con.registerObject("/spyObject", &spy, QDBusConnection::ExportAllSlots); + ScopedDBusMessage msg(dbus_message_new_method_call(con.baseService().toLatin1(), "/spyObject", NULL, "theSlot")); + + int fd = fileno(stdout); + dbus_message_append_args(msg.data(), DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); + + // try to send to us + DBusPendingCall *pending_ptr; + dbus_connection_send_with_reply(rawcon.data(), msg.data(), &pending_ptr, 1000); + ScopedDBusPendingCall pending(pending_ptr); + + // check that it got sent + while (dbus_connection_dispatch(rawcon.data()) == DBUS_DISPATCH_DATA_REMAINS) + ; + + // now spin our event loop. We don't catch this call, so let's get the reply + QEventLoop loop; + QTimer::singleShot(200, &loop, SLOT(quit())); + loop.exec(); + + // now try to receive the reply + dbus_pending_call_block(pending.data()); + + // check that the spy received what it was supposed to receive + QCOMPARE(spy.list.size(), 1); + QCOMPARE(spy.list.at(0).arguments().size(), 1); + QFETCH(int, receivedTypeId); + QCOMPARE(spy.list.at(0).arguments().at(0).userType(), receivedTypeId); + + msg.reset(dbus_pending_call_steal_reply(pending.data())); + QVERIFY(msg); + QCOMPARE(dbus_message_get_type(msg.data()), DBUS_MESSAGE_TYPE_METHOD_RETURN); + QCOMPARE(dbus_message_get_signature(msg.data()), DBUS_TYPE_INT32_AS_STRING); + + int retval; + QVERIFY(dbus_message_get_args(msg.data(), &error, DBUS_TYPE_INT32, &retval, DBUS_TYPE_INVALID)); + QCOMPARE(retval, 42); + } else { + // create a signal that we'll emit + static const char signalName[] = "signalName"; + static const char interfaceName[] = "local.interface.name"; + ScopedDBusMessage msg(dbus_message_new_signal("/", interfaceName, signalName)); + con.connect(dbus_bus_get_unique_name(rawcon.data()), QString(), interfaceName, signalName, &QTestEventLoop::instance(), SLOT(exitLoop())); + + QDBusMessageSpy spy; + con.connect(dbus_bus_get_unique_name(rawcon.data()), QString(), interfaceName, signalName, &spy, SLOT(theSlot(QDBusMessage))); + + DBusMessageIter iter; + dbus_message_iter_init_append(msg.data(), &iter); + int fd = fileno(stdout); + + if (qstrcmp(QTest::currentDataTag(), "type-naked") == 0) { + // send naked + dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd); + } else { + DBusMessageIter subiter; + if (qstrcmp(QTest::currentDataTag(), "type-variant") == 0) + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, DBUS_TYPE_UNIX_FD_AS_STRING, &subiter); + else if (qstrcmp(QTest::currentDataTag(), "type-array") == 0) + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UNIX_FD_AS_STRING, &subiter); + else if (qstrcmp(QTest::currentDataTag(), "type-struct") == 0) + dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, 0, &subiter); + dbus_message_iter_append_basic(&subiter, DBUS_TYPE_UNIX_FD, &fd); + dbus_message_iter_close_container(&iter, &subiter); + } + + // send it + dbus_connection_send(rawcon.data(), msg.data(), 0); + + // check that it got sent + while (dbus_connection_dispatch(rawcon.data()) == DBUS_DISPATCH_DATA_REMAINS) + ; + + // now let's see what happens + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(spy.list.size(), 1); + QCOMPARE(spy.list.at(0).arguments().count(), 1); + QFETCH(int, receivedTypeId); + //qDebug() << spy.list.at(0).arguments().at(0).typeName(); + QCOMPARE(spy.list.at(0).arguments().at(0).userType(), receivedTypeId); + } +#endif +} + +QTEST_MAIN(tst_QDBusMarshall) +#include "tst_qdbusmarshall.moc" diff --git a/tests/auto/dbus/qdbusmetaobject/.gitignore b/tests/auto/dbus/qdbusmetaobject/.gitignore new file mode 100644 index 0000000000..6696c0def7 --- /dev/null +++ b/tests/auto/dbus/qdbusmetaobject/.gitignore @@ -0,0 +1 @@ +tst_qdbusmetaobject diff --git a/tests/auto/dbus/qdbusmetaobject/qdbusmetaobject.pro b/tests/auto/dbus/qdbusmetaobject/qdbusmetaobject.pro new file mode 100644 index 0000000000..fa59c06c8e --- /dev/null +++ b/tests/auto/dbus/qdbusmetaobject/qdbusmetaobject.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +QT = core +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusmetaobject.cpp + QT += dbus dbus-private +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp new file mode 100644 index 0000000000..7711e819a0 --- /dev/null +++ b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> + +#include <QtDBus/QtDBus> +#include <private/qdbusmetaobject_p.h> + +class tst_QDBusMetaObject: public QObject +{ + Q_OBJECT + + QHash<QString, QDBusMetaObject *> map; +public slots: + void init(); + +private slots: + void initTestCase(); + void types_data(); + void types(); + void methods_data(); + void methods(); + void _signals_data(); + void _signals(); + void properties_data(); + void properties(); +}; + +typedef QPair<QString,QString> StringPair; + +struct Struct1 { }; // (s) +struct Struct4 // (ssa(ss)sayasx) +{ + QString m1; + QString m2; + QList<StringPair> m3; + QString m4; + QByteArray m5; + QStringList m6; + qlonglong m7; +}; + +Q_DECLARE_METATYPE(Struct1) +Q_DECLARE_METATYPE(Struct4) +Q_DECLARE_METATYPE(StringPair) + +Q_DECLARE_METATYPE(QList<Struct1>) +Q_DECLARE_METATYPE(QList<Struct4>) + +Q_DECLARE_METATYPE(const QMetaObject*) + +QT_BEGIN_NAMESPACE +QDBusArgument &operator<<(QDBusArgument &arg, const Struct1 &) +{ + arg.beginStructure(); + arg << QString(); + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const StringPair &s) +{ + arg.beginStructure(); + arg << s.first << s.second; + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Struct4 &s) +{ + arg.beginStructure(); + arg << s.m1 << s.m2 << s.m3 << s.m4 << s.m5 << s.m6 << s.m7; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, Struct1 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Struct4 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, StringPair &) +{ return arg; } +QT_END_NAMESPACE + +void tst_QDBusMetaObject::initTestCase() +{ + qDBusRegisterMetaType<Struct1>(); + qDBusRegisterMetaType<Struct4>(); + qDBusRegisterMetaType<StringPair>(); + + qDBusRegisterMetaType<QList<Struct1> >(); + qDBusRegisterMetaType<QList<Struct4> >(); +} + +void tst_QDBusMetaObject::init() +{ + qDeleteAll(map); + map.clear(); +} + +// test classes +class TypesTest1: public QObject +{ + Q_OBJECT + +signals: + void signal(uchar); +}; +const char TypesTest1_xml[] = + "<signal name=\"signal\"><arg type=\"y\"/></signal>"; + +class TypesTest2: public QObject +{ + Q_OBJECT + +signals: + void signal(bool); +}; +const char TypesTest2_xml[] = + "<signal name=\"signal\"><arg type=\"b\"/></signal>"; + +class TypesTest3: public QObject +{ + Q_OBJECT + +signals: + void signal(short); +}; +const char TypesTest3_xml[] = + "<signal name=\"signal\"><arg type=\"n\"/></signal>"; + +class TypesTest4: public QObject +{ + Q_OBJECT + +signals: + void signal(ushort); +}; +const char TypesTest4_xml[] = + "<signal name=\"signal\"><arg type=\"q\"/></signal>"; + +class TypesTest5: public QObject +{ + Q_OBJECT + +signals: + void signal(int); +}; +const char TypesTest5_xml[] = + "<signal name=\"signal\"><arg type=\"i\"/></signal>"; + +class TypesTest6: public QObject +{ + Q_OBJECT + +signals: + void signal(uint); +}; +const char TypesTest6_xml[] = + "<signal name=\"signal\"><arg type=\"u\"/></signal>"; + +class TypesTest7: public QObject +{ + Q_OBJECT + +signals: + void signal(qlonglong); +}; +const char TypesTest7_xml[] = + "<signal name=\"signal\"><arg type=\"x\"/></signal>"; + +class TypesTest8: public QObject +{ + Q_OBJECT + +signals: + void signal(qulonglong); +}; +const char TypesTest8_xml[] = + "<signal name=\"signal\"><arg type=\"t\"/></signal>"; + +class TypesTest9: public QObject +{ + Q_OBJECT + +signals: + void signal(double); +}; +const char TypesTest9_xml[] = + "<signal name=\"signal\"><arg type=\"d\"/></signal>"; + +class TypesTest10: public QObject +{ + Q_OBJECT + +signals: + void signal(QString); +}; +const char TypesTest10_xml[] = + "<signal name=\"signal\"><arg type=\"s\"/></signal>"; + +class TypesTest11: public QObject +{ + Q_OBJECT + +signals: + void signal(QDBusObjectPath); +}; +const char TypesTest11_xml[] = + "<signal name=\"signal\"><arg type=\"o\"/></signal>"; + +class TypesTest12: public QObject +{ + Q_OBJECT + +signals: + void signal(QDBusSignature); +}; +const char TypesTest12_xml[] = + "<signal name=\"signal\"><arg type=\"g\"/></signal>"; + +class TypesTest13: public QObject +{ + Q_OBJECT + +signals: + void signal(QDBusVariant); +}; +const char TypesTest13_xml[] = + "<signal name=\"signal\"><arg type=\"v\"/></signal>"; + +class TypesTest14: public QObject +{ + Q_OBJECT + +signals: + void signal(QStringList); +}; +const char TypesTest14_xml[] = + "<signal name=\"signal\"><arg type=\"as\"/></signal>"; + +class TypesTest15: public QObject +{ + Q_OBJECT + +signals: + void signal(QByteArray); +}; +const char TypesTest15_xml[] = + "<signal name=\"signal\"><arg type=\"ay\"/></signal>"; + +class TypesTest16: public QObject +{ + Q_OBJECT + +signals: + void signal(StringPair); +}; +const char TypesTest16_xml[] = + "<signal name=\"signal\"><arg type=\"(ss)\"/>" + "<annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"StringPair\"></signal>"; + +class TypesTest17: public QObject +{ + Q_OBJECT + +signals: + void signal(Struct1); +}; +const char TypesTest17_xml[] = + "<signal name=\"signal\"><arg type=\"(s)\"/>" + "<annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"Struct1\"></signal>"; + +class TypesTest18: public QObject +{ + Q_OBJECT + +signals: + void signal(Struct4); +}; +const char TypesTest18_xml[] = + "<signal name=\"signal\"><arg type=\"(ssa(ss)sayasx)\"/>" + "<annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"Struct4\"></signal>"; + +class TypesTest19: public QObject +{ + Q_OBJECT + +signals: + void signal(QVariantList); +}; +const char TypesTest19_xml[] = + "<signal name=\"signal\"><arg type=\"av\"/>" + "<annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QVariantList\"></signal>"; + +class TypesTest20: public QObject +{ + Q_OBJECT + +signals: + void signal(QVariantMap); +}; +const char TypesTest20_xml[] = + "<signal name=\"signal\"><arg type=\"a{sv}\"/>" + "<annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QVariantMap\"></signal>"; + +void tst_QDBusMetaObject::types_data() +{ + QTest::addColumn<const QMetaObject *>("metaobject"); + QTest::addColumn<QString>("xml"); + + QTest::newRow("byte") << &TypesTest1::staticMetaObject << QString(TypesTest1_xml); + QTest::newRow("bool") << &TypesTest2::staticMetaObject << QString(TypesTest2_xml); + QTest::newRow("short") << &TypesTest3::staticMetaObject << QString(TypesTest3_xml); + QTest::newRow("ushort") << &TypesTest4::staticMetaObject << QString(TypesTest4_xml); + QTest::newRow("int") << &TypesTest5::staticMetaObject << QString(TypesTest5_xml); + QTest::newRow("uint") << &TypesTest6::staticMetaObject << QString(TypesTest6_xml); + QTest::newRow("qlonglong") << &TypesTest7::staticMetaObject << QString(TypesTest7_xml); + QTest::newRow("qulonglong") << &TypesTest8::staticMetaObject << QString(TypesTest8_xml); + QTest::newRow("double") << &TypesTest9::staticMetaObject << QString(TypesTest9_xml); + QTest::newRow("QString") << &TypesTest10::staticMetaObject << QString(TypesTest10_xml); + QTest::newRow("QDBusObjectPath") << &TypesTest11::staticMetaObject << QString(TypesTest11_xml); + QTest::newRow("QDBusSignature") << &TypesTest12::staticMetaObject << QString(TypesTest12_xml); + QTest::newRow("QDBusVariant") << &TypesTest13::staticMetaObject << QString(TypesTest13_xml); + QTest::newRow("QStringList") << &TypesTest14::staticMetaObject << QString(TypesTest14_xml); + QTest::newRow("QByteArray") << &TypesTest15::staticMetaObject << QString(TypesTest15_xml); + QTest::newRow("StringPair") << &TypesTest16::staticMetaObject << QString(TypesTest16_xml); + QTest::newRow("Struct1") << &TypesTest17::staticMetaObject << QString(TypesTest17_xml); + QTest::newRow("Struct4") << &TypesTest18::staticMetaObject << QString(TypesTest18_xml); + QTest::newRow("QVariantList") << &TypesTest19::staticMetaObject << QString(TypesTest19_xml); + QTest::newRow("QVariantMap") << &TypesTest20::staticMetaObject << QString(TypesTest20_xml); +} + +void tst_QDBusMetaObject::types() +{ + QFETCH(const QMetaObject*, metaobject); + QFETCH(QString, xml); + + // add the rest of the XML tags + xml = QString("<node><interface name=\"local.Interface\">%1</interface></node>") + .arg(xml); + + QDBusError error; + + QMetaObject *result = QDBusMetaObject::createMetaObject("local.Interface", xml, + map, error); + QVERIFY2(result, qPrintable(error.message())); + + QCOMPARE(result->enumeratorCount(), 0); + QCOMPARE(result->classInfoCount(), 0); + + // compare the meta objects + QCOMPARE(result->methodCount() - result->methodOffset(), + metaobject->methodCount() - metaobject->methodOffset()); + QCOMPARE(result->propertyCount() - result->propertyOffset(), + metaobject->propertyCount() - metaobject->propertyOffset()); + + for (int i = metaobject->methodOffset(); i < metaobject->methodCount(); ++i) { + QMetaMethod expected = metaobject->method(i); + + int methodIdx = result->indexOfMethod(expected.signature()); + QVERIFY(methodIdx != -1); + QMetaMethod constructed = result->method(methodIdx); + + QCOMPARE(int(constructed.access()), int(expected.access())); + QCOMPARE(int(constructed.methodType()), int(expected.methodType())); + QCOMPARE(constructed.parameterNames(), expected.parameterNames()); + QCOMPARE(constructed.parameterTypes(), expected.parameterTypes()); + QCOMPARE(constructed.tag(), expected.tag()); + QCOMPARE(constructed.typeName(), expected.typeName()); + } + + for (int i = metaobject->propertyOffset(); i < metaobject->propertyCount(); ++i) { + QMetaProperty expected = metaobject->property(i); + + int propIdx = result->indexOfProperty(expected.name()); + QVERIFY(propIdx != -1); + QMetaProperty constructed = result->property(propIdx); + + QCOMPARE(constructed.isDesignable(), expected.isDesignable()); + QCOMPARE(constructed.isEditable(), expected.isEditable()); + QCOMPARE(constructed.isEnumType(), expected.isEnumType()); + QCOMPARE(constructed.isFlagType(), expected.isFlagType()); + QCOMPARE(constructed.isReadable(), expected.isReadable()); + QCOMPARE(constructed.isResettable(), expected.isResettable()); + QCOMPARE(constructed.isScriptable(), expected.isScriptable()); + QCOMPARE(constructed.isStored(), expected.isStored()); + QCOMPARE(constructed.isUser(), expected.isUser()); + QCOMPARE(constructed.isWritable(), expected.isWritable()); + QCOMPARE(constructed.typeName(), expected.typeName()); + } +} + +class MethodTest1: public QObject +{ + Q_OBJECT + +public slots: + void method() { } +}; +const char MethodTest1_xml[] = + "<method name=\"method\" />"; + +class MethodTest2: public QObject +{ + Q_OBJECT + +public slots: + void method(int) { } +}; +const char MethodTest2_xml[] = + "<method name=\"method\"><arg direction=\"in\" type=\"i\"/></method>"; + +class MethodTest3: public QObject +{ + Q_OBJECT + +public slots: + void method(int input0) { Q_UNUSED(input0); } +}; +const char MethodTest3_xml[] = + "<method name=\"method\"><arg direction=\"in\" type=\"i\" name=\"input0\"/></method>"; + +class MethodTest4: public QObject +{ + Q_OBJECT + +public slots: + int method() { return 0; } +}; +const char MethodTest4_xml[] = + "<method name=\"method\"><arg direction=\"out\" type=\"i\"/></method>"; +const char MethodTest4_xml2[] = + "<method name=\"method\"><arg direction=\"out\" type=\"i\" name=\"thisShouldNeverBeSeen\"/></method>"; + +class MethodTest5: public QObject +{ + Q_OBJECT + +public slots: + int method(int input0) { return input0; } +}; +const char MethodTest5_xml[] = + "<method name=\"method\">" + "<arg direction=\"in\" type=\"i\" name=\"input0\"/>" + "<arg direction=\"out\" type=\"i\"/>" + "</method>"; + +class MethodTest6: public QObject +{ + Q_OBJECT + +public slots: + int method(int input0, int input1) { Q_UNUSED(input0); return input1; } +}; +const char MethodTest6_xml[] = + "<method name=\"method\">" + "<arg direction=\"in\" type=\"i\" name=\"input0\"/>" + "<arg direction=\"out\" type=\"i\"/>" + "<arg direction=\"in\" type=\"i\" name=\"input1\"/>" + "</method>"; + +class MethodTest7: public QObject +{ + Q_OBJECT + +public slots: + int method(int input0, int input1, int &output1) { output1 = input1; return input0; } +}; +const char MethodTest7_xml[] = + "<method name=\"method\">" + "<arg direction=\"in\" type=\"i\" name=\"input0\"/>" + "<arg direction=\"in\" type=\"i\" name=\"input1\"/>" + "<arg direction=\"out\" type=\"i\"/>" + "<arg direction=\"out\" type=\"i\" name=\"output1\"/>" + "</method>"; + +class MethodTest8: public QObject +{ + Q_OBJECT + +public slots: + int method(int input0, int input1, int &output1, int &output2) { output1 = output2 = input1; return input0; } +}; +const char MethodTest8_xml[] = + "<method name=\"method\">" + "<arg direction=\"in\" type=\"i\" name=\"input0\"/>" + "<arg direction=\"in\" type=\"i\" name=\"input1\"/>" + "<arg direction=\"out\" type=\"i\"/>" + "<arg direction=\"out\" type=\"i\" name=\"output1\"/>" + "<arg direction=\"out\" type=\"i\" name=\"output2\"/>" + "</method>"; + +class MethodTest9: public QObject +{ + Q_OBJECT + +public slots: + Q_NOREPLY void method(int) { } +}; +const char MethodTest9_xml[] = + "<method name=\"method\">" + "<arg direction=\"in\" type=\"i\"/>" + "<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>" + "</method>"; + +void tst_QDBusMetaObject::methods_data() +{ + QTest::addColumn<const QMetaObject *>("metaobject"); + QTest::addColumn<QString>("xml"); + + QTest::newRow("void-void") << &MethodTest1::staticMetaObject << QString(MethodTest1_xml); + QTest::newRow("void-int") << &MethodTest2::staticMetaObject << QString(MethodTest2_xml); + QTest::newRow("void-int-with-name") << &MethodTest3::staticMetaObject << QString(MethodTest3_xml); + QTest::newRow("int-void") << &MethodTest4::staticMetaObject << QString(MethodTest4_xml); + QTest::newRow("int-void2") << &MethodTest4::staticMetaObject << QString(MethodTest4_xml2); + QTest::newRow("int-int") << &MethodTest5::staticMetaObject << QString(MethodTest5_xml); + QTest::newRow("int-int,int") << &MethodTest6::staticMetaObject << QString(MethodTest6_xml); + QTest::newRow("int,int-int,int") << &MethodTest7::staticMetaObject << QString(MethodTest7_xml); + QTest::newRow("int,int,int-int,int") << &MethodTest8::staticMetaObject << QString(MethodTest8_xml); + QTest::newRow("Q_ASYNC") << &MethodTest9::staticMetaObject << QString(MethodTest9_xml); +} + +void tst_QDBusMetaObject::methods() +{ + types(); +} + +class SignalTest1: public QObject +{ + Q_OBJECT + +signals: + void signal(); +}; +const char SignalTest1_xml[] = + "<signal name=\"signal\" />"; + +class SignalTest2: public QObject +{ + Q_OBJECT + +signals: + void signal(int); +}; +const char SignalTest2_xml[] = + "<signal name=\"signal\"><arg type=\"i\"/></signal>"; + +class SignalTest3: public QObject +{ + Q_OBJECT + +signals: + void signal(int output0); +}; +const char SignalTest3_xml[] = + "<signal name=\"signal\"><arg type=\"i\" name=\"output0\"/></signal>"; + +class SignalTest4: public QObject +{ + Q_OBJECT + +signals: + void signal(int output0, int); +}; +const char SignalTest4_xml[] = + "<signal name=\"signal\"><arg type=\"i\" name=\"output0\"/><arg type=\"i\"/></signal>"; + +void tst_QDBusMetaObject::_signals_data() +{ + QTest::addColumn<const QMetaObject *>("metaobject"); + QTest::addColumn<QString>("xml"); + + QTest::newRow("empty") << &SignalTest1::staticMetaObject << QString(SignalTest1_xml); + QTest::newRow("int") << &SignalTest2::staticMetaObject << QString(SignalTest2_xml); + QTest::newRow("int output0") << &SignalTest3::staticMetaObject << QString(SignalTest3_xml); + QTest::newRow("int output0,int") << &SignalTest4::staticMetaObject << QString(SignalTest4_xml); +} + +void tst_QDBusMetaObject::_signals() +{ + types(); +} + +class PropertyTest1: public QObject +{ + Q_OBJECT + Q_PROPERTY(int property READ property) +public: + int property() { return 0; } + void setProperty(int) { } +}; +const char PropertyTest1_xml[] = + "<property name=\"property\" type=\"i\" access=\"read\"/>"; + +class PropertyTest2: public QObject +{ + Q_OBJECT + Q_PROPERTY(int property READ property WRITE setProperty) +public: + int property() { return 0; } + void setProperty(int) { } +}; +const char PropertyTest2_xml[] = + "<property name=\"property\" type=\"i\" access=\"readwrite\"/>"; + +class PropertyTest3: public QObject +{ + Q_OBJECT + Q_PROPERTY(int property WRITE setProperty) +public: + int property() { return 0; } + void setProperty(int) { } +}; +const char PropertyTest3_xml[] = + "<property name=\"property\" type=\"i\" access=\"write\"/>"; + +class PropertyTest4: public QObject +{ + Q_OBJECT + Q_PROPERTY(Struct1 property WRITE setProperty) +public: + Struct1 property() { return Struct1(); } + void setProperty(Struct1) { } +}; +const char PropertyTest4_xml[] = + "<property name=\"property\" type=\"(s)\" access=\"write\">" + "<annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"Struct1\"/>" + "</property>"; + +void tst_QDBusMetaObject::properties_data() +{ + QTest::addColumn<const QMetaObject *>("metaobject"); + QTest::addColumn<QString>("xml"); + + QTest::newRow("read") << &PropertyTest1::staticMetaObject << QString(PropertyTest1_xml); + QTest::newRow("readwrite") << &PropertyTest2::staticMetaObject << QString(PropertyTest2_xml); + QTest::newRow("write") << &PropertyTest3::staticMetaObject << QString(PropertyTest3_xml); + QTest::newRow("customtype") << &PropertyTest4::staticMetaObject << QString(PropertyTest4_xml); +} + +void tst_QDBusMetaObject::properties() +{ + types(); +} + + +QTEST_MAIN(tst_QDBusMetaObject) +#include "tst_qdbusmetaobject.moc" diff --git a/tests/auto/dbus/qdbusmetatype/.gitignore b/tests/auto/dbus/qdbusmetatype/.gitignore new file mode 100644 index 0000000000..75de5c26cf --- /dev/null +++ b/tests/auto/dbus/qdbusmetatype/.gitignore @@ -0,0 +1 @@ +tst_qdbusmetatype diff --git a/tests/auto/dbus/qdbusmetatype/qdbusmetatype.pro b/tests/auto/dbus/qdbusmetatype/qdbusmetatype.pro new file mode 100644 index 0000000000..da8fbec01a --- /dev/null +++ b/tests/auto/dbus/qdbusmetatype/qdbusmetatype.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +QT = core +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusmetatype.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusmetatype/tst_qdbusmetatype.cpp b/tests/auto/dbus/qdbusmetatype/tst_qdbusmetatype.cpp new file mode 100644 index 0000000000..7e1f651a1b --- /dev/null +++ b/tests/auto/dbus/qdbusmetatype/tst_qdbusmetatype.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> + +#include <QtDBus/QtDBus> + +class tst_QDBusMetaType: public QObject +{ + Q_OBJECT +public: + int intStringMap; + int stringStringMap; + int stringStruct1Map; + +private slots: + void initTestCase(); + void staticTypes_data(); + void staticTypes(); + void dynamicTypes_data(); + void dynamicTypes(); + void invalidTypes_data(); + void invalidTypes(); +}; + +typedef QPair<QString,QString> StringPair; + +struct Struct1 { }; // (s) +struct Struct2 { }; // (sos) +struct Struct3 { }; // (sas) +struct Struct4 // (ssa(ss)sayasx) +{ + QString m1; + QString m2; + QList<StringPair> m3; + QString m4; + QByteArray m5; + QStringList m6; + qlonglong m7; +}; + +struct Invalid0 { }; // empty +struct Invalid1 { }; // s +struct Invalid2 { }; // o +struct Invalid3 { }; // as +struct Invalid4 { }; // ay +struct Invalid5 { }; // ii +struct Invalid6 { }; // <invalid> +struct Invalid7 { }; // (<invalid>) + +Q_DECLARE_METATYPE(Struct1) +Q_DECLARE_METATYPE(Struct2) +Q_DECLARE_METATYPE(Struct3) +Q_DECLARE_METATYPE(Struct4) +Q_DECLARE_METATYPE(StringPair) + +Q_DECLARE_METATYPE(QList<Struct1>) +Q_DECLARE_METATYPE(QList<Struct2>) +Q_DECLARE_METATYPE(QList<Struct3>) +Q_DECLARE_METATYPE(QList<Struct4>) + +Q_DECLARE_METATYPE(Invalid0) +Q_DECLARE_METATYPE(Invalid1) +Q_DECLARE_METATYPE(Invalid2) +Q_DECLARE_METATYPE(Invalid3) +Q_DECLARE_METATYPE(Invalid4) +Q_DECLARE_METATYPE(Invalid5) +Q_DECLARE_METATYPE(Invalid6) +Q_DECLARE_METATYPE(Invalid7) + +Q_DECLARE_METATYPE(QList<Invalid0>) + +typedef QMap<int, QString> IntStringMap; +typedef QMap<QString, QString> StringStringMap; +typedef QMap<QString, Struct1> StringStruct1Map; +Q_DECLARE_METATYPE(IntStringMap) +Q_DECLARE_METATYPE(StringStringMap) +Q_DECLARE_METATYPE(StringStruct1Map) + +Q_DECLARE_METATYPE(QVariant::Type) + +QT_BEGIN_NAMESPACE +QDBusArgument &operator<<(QDBusArgument &arg, const Struct1 &) +{ + arg.beginStructure(); + arg << QString(); + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Struct2 &) +{ + arg.beginStructure(); + arg << QString() << QDBusObjectPath() << QString(); + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Struct3 &) +{ + arg.beginStructure(); + arg << QString() << QStringList(); + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const StringPair &s) +{ + arg.beginStructure(); + arg << s.first << s.second; + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Struct4 &s) +{ + arg.beginStructure(); + arg << s.m1 << s.m2 << s.m3 << s.m4 << s.m5 << s.m6 << s.m7; + arg.endStructure(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid0 &) +{ + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid1 &) +{ + arg << QString(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid2 &) +{ + arg << QDBusObjectPath(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid3 &) +{ + arg << QStringList(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid4 &) +{ + arg << QByteArray(); + return arg; +} + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid5 &) +{ + arg << 1 << 2; + return arg; +} + +// no Invalid6 + +QDBusArgument &operator<<(QDBusArgument &arg, const Invalid7 &) +{ + arg.beginStructure(); + arg << Invalid0(); + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, Struct1 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Struct2 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Struct3 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Struct4 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, StringPair &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid0 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid1 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid2 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid3 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid4 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid5 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid6 &) +{ return arg; } +const QDBusArgument &operator>>(const QDBusArgument &arg, Invalid7 &) +{ return arg; } +QT_END_NAMESPACE + +void tst_QDBusMetaType::initTestCase() +{ + qDBusRegisterMetaType<Struct1>(); + qDBusRegisterMetaType<Struct2>(); + qDBusRegisterMetaType<Struct3>(); + qDBusRegisterMetaType<Struct4>(); + qDBusRegisterMetaType<StringPair>(); + + qDBusRegisterMetaType<QList<Struct1> >(); + qDBusRegisterMetaType<QList<Struct2> >(); + qDBusRegisterMetaType<QList<Struct3> >(); + qDBusRegisterMetaType<QList<Struct4> >(); + + qDBusRegisterMetaType<Invalid0>(); + qDBusRegisterMetaType<Invalid1>(); + qDBusRegisterMetaType<Invalid2>(); + qDBusRegisterMetaType<Invalid3>(); + qDBusRegisterMetaType<Invalid4>(); + qDBusRegisterMetaType<Invalid5>(); + // don't register Invalid6 + qDBusRegisterMetaType<Invalid7>(); + + qDBusRegisterMetaType<QList<Invalid0> >(); + + intStringMap = qDBusRegisterMetaType<QMap<int, QString> >(); + stringStringMap = qDBusRegisterMetaType<QMap<QString, QString> >(); + stringStruct1Map = qDBusRegisterMetaType<QMap<QString, Struct1> >(); +} + +void tst_QDBusMetaType::staticTypes_data() +{ + QTest::addColumn<QVariant::Type>("typeId"); + QTest::addColumn<QString>("signature"); + + QTest::newRow("uchar") << QVariant::Type(QMetaType::UChar) << "y"; + QTest::newRow("bool") << QVariant::Bool << "b"; + QTest::newRow("short") << QVariant::Type(QMetaType::Short) << "n"; + QTest::newRow("ushort") << QVariant::Type(QMetaType::UShort) << "q"; + QTest::newRow("int") << QVariant::Int << "i"; + QTest::newRow("uint") << QVariant::UInt << "u"; + QTest::newRow("qlonglong") << QVariant::LongLong << "x"; + QTest::newRow("qulonglong") << QVariant::ULongLong << "t"; + QTest::newRow("double") << QVariant::Double << "d"; + QTest::newRow("QString") << QVariant::String << "s"; + QTest::newRow("QDBusObjectPath") << QVariant::Type(qMetaTypeId<QDBusObjectPath>()) << "o"; + QTest::newRow("QDBusSignature") << QVariant::Type(qMetaTypeId<QDBusSignature>()) << "g"; + QTest::newRow("QDBusVariant") << QVariant::Type(qMetaTypeId<QDBusVariant>()) << "v"; + + QTest::newRow("QByteArray") << QVariant::ByteArray << "ay"; + QTest::newRow("QStringList") << QVariant::StringList << "as"; +} + +void tst_QDBusMetaType::dynamicTypes_data() +{ + QTest::addColumn<QVariant::Type>("typeId"); + QTest::addColumn<QString>("signature"); + + QTest::newRow("QVariantList") << QVariant::List << "av"; + QTest::newRow("QVariantMap") << QVariant::Map << "a{sv}"; + QTest::newRow("QDate") << QVariant::Date << "(iii)"; + QTest::newRow("QTime") << QVariant::Time << "(iiii)"; + QTest::newRow("QDateTime") << QVariant::DateTime << "((iii)(iiii)i)"; + QTest::newRow("QRect") << QVariant::Rect << "(iiii)"; + QTest::newRow("QRectF") << QVariant::RectF << "(dddd)"; + QTest::newRow("QSize") << QVariant::Size << "(ii)"; + QTest::newRow("QSizeF") << QVariant::SizeF << "(dd)"; + QTest::newRow("QPoint") << QVariant::Point << "(ii)"; + QTest::newRow("QPointF") << QVariant::PointF << "(dd)"; + QTest::newRow("QLine") << QVariant::Line << "((ii)(ii))"; + QTest::newRow("QLineF") << QVariant::LineF << "((dd)(dd))"; + + QTest::newRow("Struct1") << QVariant::Type(qMetaTypeId<Struct1>()) << "(s)"; + QTest::newRow("QList<Struct1>") << QVariant::Type(qMetaTypeId<QList<Struct1> >()) << "a(s)"; + + QTest::newRow("Struct2") << QVariant::Type(qMetaTypeId<Struct2>()) << "(sos)"; + QTest::newRow("QList<Struct2>") << QVariant::Type(qMetaTypeId<QList<Struct2> >()) << "a(sos)"; + + QTest::newRow("QList<Struct3>") << QVariant::Type(qMetaTypeId<QList<Struct3> >()) << "a(sas)"; + QTest::newRow("Struct3") << QVariant::Type(qMetaTypeId<Struct3>()) << "(sas)"; + + QTest::newRow("Struct4") << QVariant::Type(qMetaTypeId<Struct4>()) << "(ssa(ss)sayasx)"; + QTest::newRow("QList<Struct4>") << QVariant::Type(qMetaTypeId<QList<Struct4> >()) << "a(ssa(ss)sayasx)"; + + QTest::newRow("QMap<int,QString>") << QVariant::Type(intStringMap) << "a{is}"; + QTest::newRow("QMap<QString,QString>") << QVariant::Type(stringStringMap) << "a{ss}"; + QTest::newRow("QMap<QString,Struct1>") << QVariant::Type(stringStruct1Map) << "a{s(s)}"; +} + +void tst_QDBusMetaType::staticTypes() +{ + QFETCH(QVariant::Type, typeId); + + QString result = QDBusMetaType::typeToSignature(typeId); + QTEST(result, "signature"); +} + +void tst_QDBusMetaType::dynamicTypes() +{ + // same test + staticTypes(); +} + +void tst_QDBusMetaType::invalidTypes_data() +{ + QTest::addColumn<QVariant::Type>("typeId"); + QTest::addColumn<QString>("signature"); + + QTest::newRow("Invalid0") << QVariant::Type(qMetaTypeId<Invalid0>()) << ""; + QTest::newRow("Invalid1") << QVariant::Type(qMetaTypeId<Invalid1>()) << ""; + QTest::newRow("Invalid2") << QVariant::Type(qMetaTypeId<Invalid2>()) << ""; + QTest::newRow("Invalid3") << QVariant::Type(qMetaTypeId<Invalid3>()) << ""; + QTest::newRow("Invalid4") << QVariant::Type(qMetaTypeId<Invalid4>()) << ""; + QTest::newRow("Invalid5") << QVariant::Type(qMetaTypeId<Invalid5>()) << ""; + QTest::newRow("Invalid6") << QVariant::Type(qMetaTypeId<Invalid6>()) << ""; + QTest::newRow("Invalid7") << QVariant::Type(qMetaTypeId<Invalid7>()) << ""; + + QTest::newRow("QList<Invalid0>") << QVariant::Type(qMetaTypeId<QList<Invalid0> >()) << ""; + + QTest::newRow("long") << QVariant::Type(QMetaType::Long) << ""; + QTest::newRow("void*") << QVariant::Type(QMetaType::VoidStar) << ""; +} + +void tst_QDBusMetaType::invalidTypes() +{ + // same test + if (qstrcmp(QTest::currentDataTag(), "Invalid0") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid0' produces invalid D-BUS signature `<empty>' (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "Invalid1") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid1' attempts to redefine basic D-BUS type 's' (QString) (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "Invalid2") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid2' attempts to redefine basic D-BUS type 'o' (QDBusObjectPath) (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "Invalid3") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid3' attempts to redefine basic D-BUS type 'as' (QStringList) (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "Invalid4") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid4' attempts to redefine basic D-BUS type 'ay' (QByteArray) (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "Invalid5") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid5' produces invalid D-BUS signature `ii' (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "Invalid7") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `Invalid7' produces invalid D-BUS signature `()' (Did you forget to call beginStructure() ?)"); + else if (qstrcmp(QTest::currentDataTag(), "QList<Invalid0>") == 0) + QTest::ignoreMessage(QtWarningMsg, "QDBusMarshaller: type `QList<Invalid0>' produces invalid D-BUS signature `a' (Did you forget to call beginStructure() ?)"); + + staticTypes(); + staticTypes(); // run twice: the error messages should be printed once only +} + +QTEST_MAIN(tst_QDBusMetaType) + +#include "tst_qdbusmetatype.moc" diff --git a/tests/auto/dbus/qdbuspendingcall/.gitignore b/tests/auto/dbus/qdbuspendingcall/.gitignore new file mode 100644 index 0000000000..a216d5804a --- /dev/null +++ b/tests/auto/dbus/qdbuspendingcall/.gitignore @@ -0,0 +1 @@ +tst_qdbuspendingcall diff --git a/tests/auto/dbus/qdbuspendingcall/qdbuspendingcall.pro b/tests/auto/dbus/qdbuspendingcall/qdbuspendingcall.pro new file mode 100644 index 0000000000..74708c8103 --- /dev/null +++ b/tests/auto/dbus/qdbuspendingcall/qdbuspendingcall.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +requires(contains(QT_CONFIG, dbus)) +QT = core dbus +SOURCES += tst_qdbuspendingcall.cpp + +CONFIG+=insignificant_test diff --git a/tests/auto/dbus/qdbuspendingcall/tst_qdbuspendingcall.cpp b/tests/auto/dbus/qdbuspendingcall/tst_qdbuspendingcall.cpp new file mode 100644 index 0000000000..7bdf9c561b --- /dev/null +++ b/tests/auto/dbus/qdbuspendingcall/tst_qdbuspendingcall.cpp @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QObject> +#include <QtCore/QVariant> +#include <QtCore/QList> +#include <QtCore/QThread> +#include <QtCore/QVector> +#include <QtTest/QtTest> +#ifndef QT_NO_DBUS +#include <QtDBus> + +#define TEST_INTERFACE_NAME "com.trolltech.QtDBus.MyObject" + +class MyObject : public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.MyObject") + +public: + MyObject(QObject* parent =0) + : QDBusAbstractAdaptor(parent) + {} + +public slots: + QStringList returnFoo() const + { return QStringList() << QString::fromLatin1("foo"); } + + void returnError(const QDBusMessage &msg) const + { + msg.setDelayedReply(true); + QDBusConnection::sessionBus().send(msg.createErrorReply("dbuspendingcall_error", "")); + } +}; + +class tst_QDBusPendingCall: public QObject +{ + Q_OBJECT + +public: + tst_QDBusPendingCall(); + +public Q_SLOTS: + void callback(const QStringList &list); + void errorCallback(const QDBusError &error); + void finished(QDBusPendingCallWatcher *call); + void makeCall(); + +private Q_SLOTS: + void initTestCase(); + void waitForFinished(); + void waitForFinished_error(); +// void setReplyCallback(); + void watcher(); + void watcher_error(); + void watcher_waitForFinished(); + void watcher_waitForFinished_threaded(); + void watcher_waitForFinished_alreadyFinished(); + void watcher_waitForFinished_alreadyFinished_eventLoop(); + void watcher_waitForFinished_error(); + void callInsideWaitForFinished(); + + void callWithCallback_localLoop(); + void callWithCallback_localLoop_errorReply(); + +private: + QDBusPendingCall sendMessage(); + QDBusPendingCall sendError(); + + QDBusConnection conn; + + enum { CallbackCalled, ErrorCallbackCalled, FinishCalled, MakeCallCalled }; + int slotCalled; + int callCount; + QStringList callbackArgument; + QDBusError errorArgument; + QDBusPendingCallWatcher *watchArgument; + MyObject *obj; +}; + +tst_QDBusPendingCall::tst_QDBusPendingCall() + : conn(QDBusConnection::sessionBus()) + , obj(new MyObject(this)) +{ +} + +void tst_QDBusPendingCall::finished(QDBusPendingCallWatcher *call) +{ + slotCalled = FinishCalled; + ++callCount; + watchArgument = call; + if (QThread::currentThread() == thread()) + QTestEventLoop::instance().exitLoop(); +} + +void tst_QDBusPendingCall::callback(const QStringList &list) +{ + slotCalled = CallbackCalled; + ++callCount; + callbackArgument = list; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QDBusPendingCall::errorCallback(const QDBusError &error) +{ + slotCalled = ErrorCallbackCalled; + ++callCount; + errorArgument = error; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QDBusPendingCall::makeCall() +{ + // make an external call to D-Bus to make sure we haven't left any locks + slotCalled = MakeCallCalled; + ++callCount; + + sendMessage().waitForFinished(); +} + +void tst_QDBusPendingCall::initTestCase() +{ + QVERIFY(conn.isConnected()); + QVERIFY(conn.registerObject("/", this)); +} + +QDBusPendingCall tst_QDBusPendingCall::sendMessage() +{ + QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "ListNames"); + return conn.asyncCall(msg); +} + +QDBusPendingCall tst_QDBusPendingCall::sendError() +{ + QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "ThisNameWontExist"); + return conn.asyncCall(msg); +} + +void tst_QDBusPendingCall::waitForFinished() +{ + QDBusPendingCall ac = sendMessage(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + ac.waitForFinished(); + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + const QDBusMessage reply = ac.reply(); + QVERIFY(reply.type() == QDBusMessage::ReplyMessage); + QCOMPARE(reply.signature(), QString("as")); + + const QVariantList args = ac.reply().arguments(); + QCOMPARE(args.count(), 1); + + const QVariant &arg = args.at(0); + QCOMPARE(arg.type(), QVariant::StringList); + QVERIFY(arg.toStringList().contains(conn.baseService())); +} + +void tst_QDBusPendingCall::waitForFinished_error() +{ + QDBusPendingCall ac = sendError(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + ac.waitForFinished(); + QVERIFY(ac.isFinished()); + QVERIFY(ac.isError()); + + QDBusError error = ac.error(); + QVERIFY(error.isValid()); + QCOMPARE(error.name(), QString("org.freedesktop.DBus.Error.UnknownMethod")); + QCOMPARE(error.type(), QDBusError::UnknownMethod); +} + +void tst_QDBusPendingCall::callWithCallback_localLoop() +{ + // Verify that a callback actually gets called when the call is dispatched locally. + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + QVERIFY(iface.isValid()); + + QVERIFY(iface.callWithCallback("returnFoo", QVariantList(), this, SLOT(callback(QStringList)))); + + // May be called synchronously or asynchronously... + if (callbackArgument != (QStringList() << QString::fromLatin1("foo"))) { + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + + QCOMPARE(callbackArgument, QStringList() << QString::fromLatin1("foo")); +} + +void tst_QDBusPendingCall::callWithCallback_localLoop_errorReply() +{ + // Verify that an error callback actually gets called when the call is + // dispatched locally and the called method returns an error + + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), QLatin1String("/"), + TEST_INTERFACE_NAME); + QVERIFY(iface.isValid()); + + callbackArgument.clear(); + + QVERIFY(iface.callWithCallback("returnError", QVariantList(), this, + SLOT(callback(QStringList)), SLOT(errorCallback(QDBusError)))); + + // May be called synchronously or asynchronously... + if (errorArgument.name() != "dbuspendingcall_error") { + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + } + + QCOMPARE(errorArgument.name(), QString::fromLatin1("dbuspendingcall_error")); + QVERIFY(callbackArgument.isEmpty()); +} + +#if 0 +// This function was removed from the public API +void tst_QDBusPendingCall::setReplyCallback() +{ + QDBusPendingCall ac = sendMessage(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + callCount = 0; + callbackArgument.clear(); + QVERIFY(ac.setReplyCallback(this, SLOT(callback(const QStringList &)))); + QVERIFY(callCount == 0); + QVERIFY(callbackArgument.isEmpty()); + + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)CallbackCalled); + QVERIFY(!callbackArgument.isEmpty()); + QVERIFY(callbackArgument.contains(conn.baseService())); + + const QVariantList args = ac.reply().arguments(); + QVERIFY(!args.isEmpty()); + QCOMPARE(args.at(0).toStringList(), callbackArgument); +} +#endif + +void tst_QDBusPendingCall::watcher() +{ + QDBusPendingCall ac = sendMessage(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + callCount = 0; + watchArgument = 0; + + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*))); + + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)FinishCalled); + QCOMPARE(watchArgument, &watch); + QVERIFY(!watch.isError()); + + const QVariantList args2 = ac.reply().arguments(); + QVERIFY(!args2.isEmpty()); + QVERIFY(args2.at(0).toStringList().contains(conn.baseService())); +} + +void tst_QDBusPendingCall::watcher_error() +{ + QDBusPendingCall ac = sendError(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + callCount = 0; + watchArgument = 0; + + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*))); + + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(ac.isFinished()); + QVERIFY(ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)FinishCalled); + QCOMPARE(watchArgument, &watch); + + QVERIFY(watch.isError()); + QVERIFY(watch.error().isValid()); +} + +void tst_QDBusPendingCall::watcher_waitForFinished() +{ + QDBusPendingCall ac = sendMessage(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + callCount = 0; + watchArgument = 0; + + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*))); + + watch.waitForFinished(); + + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)FinishCalled); + QCOMPARE(watchArgument, &watch); + QVERIFY(!watch.isError()); + + const QVariantList args2 = ac.reply().arguments(); + QVERIFY(!args2.isEmpty()); + QVERIFY(args2.at(0).toStringList().contains(conn.baseService())); +} + +void tst_QDBusPendingCall::watcher_waitForFinished_threaded() +{ + callCount = 0; + watchArgument = 0; + slotCalled = 0; + + class WorkerThread: public QThread { + public: + tst_QDBusPendingCall *tst; + WorkerThread(tst_QDBusPendingCall *tst) : tst(tst) {} + void run() + { + QDBusPendingCall ac = tst->sendMessage(); +// QVERIFY(!ac.isFinished()); +// QVERIFY(!ac.isError()); +// QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + QDBusPendingCallWatcher watch(ac); + tst->connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*)), Qt::DirectConnection); + + QTest::qSleep(100); // don't process events in this thread + +// QVERIFY(!ac.isFinished()); +// QVERIFY(!ac.isError()); +// QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + QCOMPARE(tst->callCount, 0); + QCOMPARE(tst->slotCalled, 0); + + watch.waitForFinished(); + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QCOMPARE(tst->callCount, 1); + QCOMPARE(tst->slotCalled, (int)FinishCalled); + QCOMPARE(tst->watchArgument, &watch); + QVERIFY(!watch.isError()); + + const QVariantList args2 = ac.reply().arguments(); + QVERIFY(!args2.isEmpty()); + QVERIFY(args2.at(0).toStringList().contains(tst->conn.baseService())); + } + } thread(this); + QTestEventLoop::instance().connect(&thread, SIGNAL(finished()), SLOT(exitLoop())); + thread.start(); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!thread.isRunning()); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_QDBusPendingCall::watcher_waitForFinished_alreadyFinished() +{ + QDBusPendingCall ac = sendMessage(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + ac.waitForFinished(); + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + callCount = 0; + watchArgument = 0; + + // create a watcher on an already-finished reply + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*))); + + watch.waitForFinished(); + + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)FinishCalled); + QCOMPARE(watchArgument, &watch); + QVERIFY(!watch.isError()); + + const QVariantList args2 = ac.reply().arguments(); + QVERIFY(!args2.isEmpty()); + QVERIFY(args2.at(0).toStringList().contains(conn.baseService())); +} + +void tst_QDBusPendingCall::watcher_waitForFinished_alreadyFinished_eventLoop() +{ + QDBusPendingCall ac = sendMessage(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + ac.waitForFinished(); + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + callCount = 0; + watchArgument = 0; + + // create a watcher on an already-finished reply + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*))); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QVERIFY(ac.isFinished()); + QVERIFY(!ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)FinishCalled); + QCOMPARE(watchArgument, &watch); + QVERIFY(!watch.isError()); + + const QVariantList args2 = ac.reply().arguments(); + QVERIFY(!args2.isEmpty()); + QVERIFY(args2.at(0).toStringList().contains(conn.baseService())); +} + +void tst_QDBusPendingCall::watcher_waitForFinished_error() +{ + QDBusPendingCall ac = sendError(); + QVERIFY(!ac.isFinished()); + QVERIFY(!ac.isError()); + QVERIFY(ac.reply().type() == QDBusMessage::InvalidMessage); + + callCount = 0; + watchArgument = 0; + + QDBusPendingCallWatcher watch(ac); + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(finished(QDBusPendingCallWatcher*))); + + watch.waitForFinished(); + + QVERIFY(ac.isFinished()); + QVERIFY(ac.isError()); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)FinishCalled); + QCOMPARE(watchArgument, &watch); + + QVERIFY(watch.isError()); + QVERIFY(watch.error().isValid()); +} + +void tst_QDBusPendingCall::callInsideWaitForFinished() +{ + QDBusPendingCall ac = sendMessage(); + QDBusPendingCallWatcher watch(ac); + + callCount = 0; + + connect(&watch, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(makeCall())); + + watch.waitForFinished(); + + QCOMPARE(callCount, 1); + QCOMPARE(slotCalled, (int)MakeCallCalled); + QVERIFY(!watch.isError()); + + const QVariantList args2 = ac.reply().arguments(); + QVERIFY(!args2.isEmpty()); + QVERIFY(args2.at(0).toStringList().contains(conn.baseService())); +} + +QTEST_MAIN(tst_QDBusPendingCall) +#include "tst_qdbuspendingcall.moc" +#else +QTEST_NOOP_MAIN +#endif diff --git a/tests/auto/dbus/qdbuspendingreply/.gitignore b/tests/auto/dbus/qdbuspendingreply/.gitignore new file mode 100644 index 0000000000..d2537acca6 --- /dev/null +++ b/tests/auto/dbus/qdbuspendingreply/.gitignore @@ -0,0 +1 @@ +tst_qdbuspendingreply diff --git a/tests/auto/dbus/qdbuspendingreply/qdbuspendingreply.pro b/tests/auto/dbus/qdbuspendingreply/qdbuspendingreply.pro new file mode 100644 index 0000000000..c649e4aaf5 --- /dev/null +++ b/tests/auto/dbus/qdbuspendingreply/qdbuspendingreply.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +requires(contains(QT_CONFIG, dbus)) +QT = core dbus +SOURCES += tst_qdbuspendingreply.cpp diff --git a/tests/auto/dbus/qdbuspendingreply/tst_qdbuspendingreply.cpp b/tests/auto/dbus/qdbuspendingreply/tst_qdbuspendingreply.cpp new file mode 100644 index 0000000000..18177181fb --- /dev/null +++ b/tests/auto/dbus/qdbuspendingreply/tst_qdbuspendingreply.cpp @@ -0,0 +1,597 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtCore/QObject> +#include <QtCore/QVariant> +#include <QtCore/QList> +#include <QtCore/QVector> +#include <QtTest/QtTest> +#ifndef QT_NO_DBUS +#include <QtDBus> + +typedef QMap<int,QString> IntStringMap; +Q_DECLARE_METATYPE(IntStringMap) + +struct MyStruct +{ + int i; + QString s; + + MyStruct() : i(1), s("String") { } + bool operator==(const MyStruct &other) const + { return i == other.i && s == other.s; } +}; +Q_DECLARE_METATYPE(MyStruct) + +QDBusArgument &operator<<(QDBusArgument &arg, const MyStruct &ms) +{ + arg.beginStructure(); + arg << ms.i << ms.s; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, MyStruct &ms) +{ + arg.beginStructure(); + arg >> ms.i >> ms.s; + arg.endStructure(); + return arg; +} + +class TypesInterface; +class tst_QDBusPendingReply: public QObject +{ + Q_OBJECT + QDBusInterface *iface; + TypesInterface *adaptor; +public: + tst_QDBusPendingReply(); + +private slots: + void initTestCase() + { + qDBusRegisterMetaType<IntStringMap>(); + qDBusRegisterMetaType<MyStruct>(); + } + + void init(); + void unconnected(); + void simpleTypes(); + void complexTypes(); + void wrongTypes(); + void multipleTypes(); + + void synchronousSimpleTypes(); + + void errors(); +}; + +class TypesInterface: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.Qt.Autotests.TypesInterface") +public: + TypesInterface(QObject *parent) + : QDBusAbstractAdaptor(parent) + { } + +public slots: + void retrieveVoid() + { } + + bool retrieveBool() + { + return true; + } + + uchar retrieveUChar() + { + return 'A'; + } + + short retrieveShort() + { + return -47; + } + + ushort retrieveUShort() + { + return 42U; + } + + int retrieveInt() + { + return -470000; + } + + void retrieveIntInt(int &i1, int &i2) + { + i1 = -424242; + i2 = 434343; + } + + uint retrieveUInt() + { + return 42424242; + } + + qlonglong retrieveLongLong() + { + return -(Q_INT64_C(1) << 32); + } + + qulonglong retrieveULongLong() + { + return Q_INT64_C(1) << 32; + } + + double retrieveDouble() + { + return 1.5; + } + + QString retrieveString() + { + return "This string you should see"; + } + + QDBusObjectPath retrieveObjectPath() + { + return QDBusObjectPath("/"); + } + + QDBusSignature retrieveSignature() + { + return QDBusSignature("g"); + } + + QDBusVariant retrieveVariant() + { + return QDBusVariant(retrieveString()); + } + + QStringList retrieveStringList() + { + return QStringList() << "one" << "two"; + } + + QByteArray retrieveByteArray() + { + return "Hello, World"; + } + + QVariantList retrieveList() + { + return QVariantList() << retrieveInt() << retrieveString() + << retrieveByteArray(); + } + + QVariantMap retrieveMap() + { + QVariantMap map; + map["one"] = 1; + map["two"] = 2U; + map["string"] = retrieveString(); + map["stringlist"] = retrieveStringList(); + return map; + } + + IntStringMap retrieveIntStringMap() + { + IntStringMap map; + map[1] = "1"; + map[2] = "2"; + map[-1231456] = "foo"; + return map; + } + + MyStruct retrieveStruct() + { + return MyStruct(); + } + + void sendError(const QDBusMessage &msg) + { + msg.setDelayedReply(true); + QDBusConnection::sessionBus() + .send(msg.createErrorReply("local.AnErrorName", "You've got an error!")); + } +}; + +tst_QDBusPendingReply::tst_QDBusPendingReply() +{ + adaptor = new TypesInterface(this); + QDBusConnection::sessionBus().registerObject("/", this); + + iface = new QDBusInterface(QDBusConnection::sessionBus().baseService(), "/", + "com.trolltech.Qt.Autotests.TypesInterface", + QDBusConnection::sessionBus(), + this); +} + +void tst_QDBusPendingReply::init() +{ + QVERIFY(iface); + QVERIFY(iface->isValid()); +} + +void tst_QDBusPendingReply::unconnected() +{ + QDBusConnection con("invalid stored connection"); + QVERIFY(!con.isConnected()); + QDBusInterface iface("doesnt.matter", "/", "doesnt.matter", con); + QVERIFY(!iface.isValid()); + + QDBusPendingReply<> rvoid = iface.asyncCall("ReloadConfig"); + QVERIFY(rvoid.isFinished()); + QVERIFY(!rvoid.isValid()); + QVERIFY(rvoid.isError()); + rvoid.waitForFinished(); + QVERIFY(!rvoid.isValid()); + QVERIFY(rvoid.isError()); + + QDBusPendingReply<QString> rstring = iface.asyncCall("GetId"); + QVERIFY(rstring.isFinished()); + QVERIFY(!rstring.isValid()); + QVERIFY(rstring.isError()); + rstring.waitForFinished(); + QVERIFY(!rstring.isValid()); + QVERIFY(rstring.isError()); +} + +void tst_QDBusPendingReply::simpleTypes() +{ + QDBusPendingReply<> rvoid = iface->asyncCall("retrieveVoid"); + rvoid.waitForFinished(); + QVERIFY(rvoid.isFinished()); + QVERIFY(!rvoid.isError()); + + QDBusPendingReply<bool> rbool = iface->asyncCall("retrieveBool"); + rbool.waitForFinished(); + QVERIFY(rbool.isFinished()); + QCOMPARE(rbool.argumentAt<0>(), adaptor->retrieveBool()); + + QDBusPendingReply<uchar> ruchar = iface->asyncCall("retrieveUChar"); + ruchar.waitForFinished(); + QVERIFY(ruchar.isFinished()); + QCOMPARE(ruchar.argumentAt<0>(), adaptor->retrieveUChar()); + + QDBusPendingReply<short> rshort = iface->asyncCall("retrieveShort"); + rshort.waitForFinished(); + QVERIFY(rshort.isFinished()); + QCOMPARE(rshort.argumentAt<0>(), adaptor->retrieveShort()); + + QDBusPendingReply<ushort> rushort = iface->asyncCall("retrieveUShort"); + rushort.waitForFinished(); + QVERIFY(rushort.isFinished()); + QCOMPARE(rushort.argumentAt<0>(), adaptor->retrieveUShort()); + + QDBusPendingReply<int> rint = iface->asyncCall("retrieveInt"); + rint.waitForFinished(); + QVERIFY(rint.isFinished()); + QCOMPARE(rint.argumentAt<0>(), adaptor->retrieveInt()); + + QDBusPendingReply<uint> ruint = iface->asyncCall("retrieveUInt"); + ruint.waitForFinished(); + QVERIFY(ruint.isFinished()); + QCOMPARE(ruint.argumentAt<0>(), adaptor->retrieveUInt()); + + QDBusPendingReply<qlonglong> rqlonglong = iface->asyncCall("retrieveLongLong"); + rqlonglong.waitForFinished(); + QVERIFY(rqlonglong.isFinished()); + QCOMPARE(rqlonglong.argumentAt<0>(), adaptor->retrieveLongLong()); + + QDBusPendingReply<qulonglong> rqulonglong = iface->asyncCall("retrieveULongLong"); + rqulonglong.waitForFinished(); + QVERIFY(rqulonglong.isFinished()); + QCOMPARE(rqulonglong.argumentAt<0>(), adaptor->retrieveULongLong()); + + QDBusPendingReply<double> rdouble = iface->asyncCall("retrieveDouble"); + rdouble.waitForFinished(); + QVERIFY(rdouble.isFinished()); + QCOMPARE(rdouble.argumentAt<0>(), adaptor->retrieveDouble()); + + QDBusPendingReply<QString> rstring = iface->asyncCall("retrieveString"); + rstring.waitForFinished(); + QVERIFY(rstring.isFinished()); + QCOMPARE(rstring.argumentAt<0>(), adaptor->retrieveString()); + + QDBusPendingReply<QDBusObjectPath> robjectpath = iface->asyncCall("retrieveObjectPath"); + robjectpath.waitForFinished(); + QVERIFY(robjectpath.isFinished()); + QCOMPARE(robjectpath.argumentAt<0>().path(), adaptor->retrieveObjectPath().path()); + + QDBusPendingReply<QDBusSignature> rsignature = iface->asyncCall("retrieveSignature"); + rsignature.waitForFinished(); + QVERIFY(rsignature.isFinished()); + QCOMPARE(rsignature.argumentAt<0>().signature(), adaptor->retrieveSignature().signature()); + + QDBusPendingReply<QDBusVariant> rdbusvariant = iface->asyncCall("retrieveVariant"); + rdbusvariant.waitForFinished(); + QVERIFY(rdbusvariant.isFinished()); + QCOMPARE(rdbusvariant.argumentAt<0>().variant(), adaptor->retrieveVariant().variant()); + + QDBusPendingReply<QVariant> rvariant = iface->asyncCall("retrieveVariant"); + rvariant.waitForFinished(); + QVERIFY(rvariant.isFinished()); + QCOMPARE(rvariant.argumentAt<0>(), adaptor->retrieveVariant().variant()); + + QDBusPendingReply<QByteArray> rbytearray = iface->asyncCall("retrieveByteArray"); + rbytearray.waitForFinished(); + QVERIFY(rbytearray.isFinished()); + QCOMPARE(rbytearray.argumentAt<0>(), adaptor->retrieveByteArray()); + + QDBusPendingReply<QStringList> rstringlist = iface->asyncCall("retrieveStringList"); + rstringlist.waitForFinished(); + QVERIFY(rstringlist.isFinished()); + QCOMPARE(rstringlist.argumentAt<0>(), adaptor->retrieveStringList()); +} + +void tst_QDBusPendingReply::complexTypes() +{ + QDBusPendingReply<QVariantList> rlist = iface->asyncCall("retrieveList"); + rlist.waitForFinished(); + QVERIFY(rlist.isFinished()); + QCOMPARE(rlist.argumentAt<0>(), adaptor->retrieveList()); + + QDBusPendingReply<QVariantMap> rmap = iface->asyncCall("retrieveMap"); + rmap.waitForFinished(); + QVERIFY(rmap.isFinished()); + QCOMPARE(rmap.argumentAt<0>(), adaptor->retrieveMap()); + + QDBusPendingReply<IntStringMap> rismap = iface->asyncCall("retrieveIntStringMap"); + rismap.waitForFinished(); + QVERIFY(rismap.isFinished()); + QCOMPARE(rismap.argumentAt<0>(), adaptor->retrieveIntStringMap()); + + QDBusPendingReply<MyStruct> rstruct = iface->asyncCall("retrieveStruct"); + rstruct.waitForFinished(); + QVERIFY(rstruct.isFinished()); + QCOMPARE(rstruct.argumentAt<0>(), adaptor->retrieveStruct()); +} + +#define VERIFY_WRONG_TYPE(error) \ + QVERIFY(error.isValid()); \ + QCOMPARE(error.type(), QDBusError::InvalidSignature) + +void tst_QDBusPendingReply::wrongTypes() +{ + QDBusError error; + + QDBusPendingReply<bool> rbool = iface->asyncCall("retrieveInt"); + rbool.waitForFinished(); + QVERIFY(rbool.isFinished()); + QVERIFY(rbool.isError()); + error = rbool.error(); + VERIFY_WRONG_TYPE(error); + + rbool = iface->asyncCall("retrieveShort"); + rbool.waitForFinished(); + QVERIFY(rbool.isFinished()); + QVERIFY(rbool.isError()); + error = rbool.error(); + VERIFY_WRONG_TYPE(error); + + rbool = iface->asyncCall("retrieveStruct"); + rbool.waitForFinished(); + QVERIFY(rbool.isFinished()); + QVERIFY(rbool.isError()); + error = rbool.error(); + VERIFY_WRONG_TYPE(error); + + QDBusPendingReply<short> rshort = iface->asyncCall("retrieveInt"); + rshort.waitForFinished(); + QVERIFY(rshort.isFinished()); + QVERIFY(rshort.isError()); + error = rshort.error(); + VERIFY_WRONG_TYPE(error); + + rshort = iface->asyncCall("retrieveBool"); + rshort.waitForFinished(); + QVERIFY(rshort.isFinished()); + QVERIFY(rshort.isError()); + error = rshort.error(); + VERIFY_WRONG_TYPE(error); + + rshort = iface->asyncCall("retrieveStruct"); + rshort.waitForFinished(); + QVERIFY(rshort.isFinished()); + QVERIFY(rshort.isError()); + error = rshort.error(); + VERIFY_WRONG_TYPE(error); + + QDBusPendingReply<MyStruct> rstruct = iface->asyncCall("retrieveInt"); + rstruct.waitForFinished(); + QVERIFY(rstruct.isFinished()); + QVERIFY(rstruct.isError()); + error = rstruct.error(); + VERIFY_WRONG_TYPE(error); + + rstruct = iface->asyncCall("retrieveShort"); + rstruct.waitForFinished(); + QVERIFY(rstruct.isFinished()); + QVERIFY(rstruct.isError()); + error = rstruct.error(); + VERIFY_WRONG_TYPE(error); + + rstruct = iface->asyncCall("retrieveIntStringMap"); + rstruct.waitForFinished(); + QVERIFY(rstruct.isFinished()); + QVERIFY(rstruct.isError()); + error = rstruct.error(); + VERIFY_WRONG_TYPE(error); +} + +void tst_QDBusPendingReply::multipleTypes() +{ + QDBusPendingReply<int, int> rintint = iface->asyncCall("retrieveIntInt"); + rintint.waitForFinished(); + QVERIFY(rintint.isFinished()); + QVERIFY(!rintint.isError()); + + int i1, i2; + adaptor->retrieveIntInt(i1, i2); + QCOMPARE(rintint.argumentAt<0>(), i1); + QCOMPARE(rintint.argumentAt<1>(), i2); +} + +void tst_QDBusPendingReply::synchronousSimpleTypes() +{ + QDBusPendingReply<bool> rbool = iface->call("retrieveBool"); + rbool.waitForFinished(); + QVERIFY(rbool.isFinished()); + QCOMPARE(rbool.argumentAt<0>(), adaptor->retrieveBool()); + + QDBusPendingReply<uchar> ruchar = iface->call("retrieveUChar"); + ruchar.waitForFinished(); + QVERIFY(ruchar.isFinished()); + QCOMPARE(ruchar.argumentAt<0>(), adaptor->retrieveUChar()); + + QDBusPendingReply<short> rshort = iface->call("retrieveShort"); + rshort.waitForFinished(); + QVERIFY(rshort.isFinished()); + QCOMPARE(rshort.argumentAt<0>(), adaptor->retrieveShort()); + + QDBusPendingReply<ushort> rushort = iface->call("retrieveUShort"); + rushort.waitForFinished(); + QVERIFY(rushort.isFinished()); + QCOMPARE(rushort.argumentAt<0>(), adaptor->retrieveUShort()); + + QDBusPendingReply<int> rint = iface->call("retrieveInt"); + rint.waitForFinished(); + QVERIFY(rint.isFinished()); + QCOMPARE(rint.argumentAt<0>(), adaptor->retrieveInt()); + + QDBusPendingReply<uint> ruint = iface->call("retrieveUInt"); + ruint.waitForFinished(); + QVERIFY(ruint.isFinished()); + QCOMPARE(ruint.argumentAt<0>(), adaptor->retrieveUInt()); + + QDBusPendingReply<qlonglong> rqlonglong = iface->call("retrieveLongLong"); + rqlonglong.waitForFinished(); + QVERIFY(rqlonglong.isFinished()); + QCOMPARE(rqlonglong.argumentAt<0>(), adaptor->retrieveLongLong()); + + QDBusPendingReply<qulonglong> rqulonglong = iface->call("retrieveULongLong"); + rqulonglong.waitForFinished(); + QVERIFY(rqulonglong.isFinished()); + QCOMPARE(rqulonglong.argumentAt<0>(), adaptor->retrieveULongLong()); + + QDBusPendingReply<double> rdouble = iface->call("retrieveDouble"); + rdouble.waitForFinished(); + QVERIFY(rdouble.isFinished()); + QCOMPARE(rdouble.argumentAt<0>(), adaptor->retrieveDouble()); + + QDBusPendingReply<QString> rstring = iface->call("retrieveString"); + rstring.waitForFinished(); + QVERIFY(rstring.isFinished()); + QCOMPARE(rstring.argumentAt<0>(), adaptor->retrieveString()); + + QDBusPendingReply<QDBusObjectPath> robjectpath = iface->call("retrieveObjectPath"); + robjectpath.waitForFinished(); + QVERIFY(robjectpath.isFinished()); + QCOMPARE(robjectpath.argumentAt<0>().path(), adaptor->retrieveObjectPath().path()); + + QDBusPendingReply<QDBusSignature> rsignature = iface->call("retrieveSignature"); + rsignature.waitForFinished(); + QVERIFY(rsignature.isFinished()); + QCOMPARE(rsignature.argumentAt<0>().signature(), adaptor->retrieveSignature().signature()); + + QDBusPendingReply<QDBusVariant> rdbusvariant = iface->call("retrieveVariant"); + rdbusvariant.waitForFinished(); + QVERIFY(rdbusvariant.isFinished()); + QCOMPARE(rdbusvariant.argumentAt<0>().variant(), adaptor->retrieveVariant().variant()); + + QDBusPendingReply<QByteArray> rbytearray = iface->call("retrieveByteArray"); + rbytearray.waitForFinished(); + QVERIFY(rbytearray.isFinished()); + QCOMPARE(rbytearray.argumentAt<0>(), adaptor->retrieveByteArray()); + + QDBusPendingReply<QStringList> rstringlist = iface->call("retrieveStringList"); + rstringlist.waitForFinished(); + QVERIFY(rstringlist.isFinished()); + QCOMPARE(rstringlist.argumentAt<0>(), adaptor->retrieveStringList()); +} + +#define VERIFY_ERROR(error) \ + QVERIFY(error.isValid()); \ + QCOMPARE(error.name(), QString("local.AnErrorName")); \ + QCOMPARE(error.type(), QDBusError::Other) + +void tst_QDBusPendingReply::errors() +{ + QDBusError error; + + QDBusPendingReply<> rvoid = iface->asyncCall("sendError"); + rvoid.waitForFinished(); + QVERIFY(rvoid.isFinished()); + QVERIFY(rvoid.isError()); + error = rvoid.error(); + VERIFY_ERROR(error); + + QDBusPendingReply<int> rint = iface->asyncCall("sendError"); + rint.waitForFinished(); + QVERIFY(rint.isFinished()); + QVERIFY(rint.isError()); + error = rint.error(); + VERIFY_ERROR(error); + + QDBusPendingReply<int,int> rintint = iface->asyncCall("sendError"); + rintint.waitForFinished(); + QVERIFY(rintint.isFinished()); + QVERIFY(rintint.isError()); + error = rintint.error(); + VERIFY_ERROR(error); + + QDBusPendingReply<QString> rstring = iface->asyncCall("sendError"); + rstring.waitForFinished(); + QVERIFY(rstring.isFinished()); + QVERIFY(rstring.isError()); + error = rstring.error(); + VERIFY_ERROR(error); +} + +QTEST_MAIN(tst_QDBusPendingReply) + +#include "tst_qdbuspendingreply.moc" +#else +QTEST_NOOP_MAIN +#endif diff --git a/tests/auto/dbus/qdbusreply/.gitignore b/tests/auto/dbus/qdbusreply/.gitignore new file mode 100644 index 0000000000..886db18592 --- /dev/null +++ b/tests/auto/dbus/qdbusreply/.gitignore @@ -0,0 +1 @@ +tst_qdbusreply diff --git a/tests/auto/dbus/qdbusreply/qdbusreply.pro b/tests/auto/dbus/qdbusreply/qdbusreply.pro new file mode 100644 index 0000000000..c96116c0ab --- /dev/null +++ b/tests/auto/dbus/qdbusreply/qdbusreply.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +QT = core +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusreply.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusreply/tst_qdbusreply.cpp b/tests/auto/dbus/qdbusreply/tst_qdbusreply.cpp new file mode 100644 index 0000000000..8d18657c56 --- /dev/null +++ b/tests/auto/dbus/qdbusreply/tst_qdbusreply.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qvariant.h> + +#include <QtTest/QtTest> + +#include <QtDBus> + +typedef QMap<int,QString> IntStringMap; +Q_DECLARE_METATYPE(IntStringMap) + +struct MyStruct +{ + int i; + QString s; + + MyStruct() : i(1), s("String") { } + bool operator==(const MyStruct &other) const + { return i == other.i && s == other.s; } +}; +Q_DECLARE_METATYPE(MyStruct) + +QDBusArgument &operator<<(QDBusArgument &arg, const MyStruct &ms) +{ + arg.beginStructure(); + arg << ms.i << ms.s; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, MyStruct &ms) +{ + arg.beginStructure(); + arg >> ms.i >> ms.s; + arg.endStructure(); + return arg; +} + +class TypesInterface; +class tst_QDBusReply: public QObject +{ + Q_OBJECT + QDBusInterface *iface; + TypesInterface *adaptor; +public: + tst_QDBusReply(); + +private slots: + void initTestCase() + { + qDBusRegisterMetaType<IntStringMap>(); + qDBusRegisterMetaType<MyStruct>(); + } + + void init(); + void unconnected(); + void simpleTypes(); + void complexTypes(); + void wrongTypes(); +}; + +class TypesInterface: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.Qt.Autotests.TypesInterface") +public: + TypesInterface(QObject *parent) + : QDBusAbstractAdaptor(parent) + { } + +public slots: + bool retrieveBool() + { + return true; + } + + uchar retrieveUChar() + { + return 'A'; + } + + short retrieveShort() + { + return -47; + } + + ushort retrieveUShort() + { + return 42U; + } + + int retrieveInt() + { + return -470000; + } + + uint retrieveUInt() + { + return 42424242; + } + + qlonglong retrieveLongLong() + { + return -(Q_INT64_C(1) << 32); + } + + qulonglong retrieveULongLong() + { + return Q_INT64_C(1) << 32; + } + + double retrieveDouble() + { + return 1.5; + } + + QString retrieveString() + { + return "This string you should see"; + } + + QDBusObjectPath retrieveObjectPath() + { + return QDBusObjectPath("/"); + } + + QDBusSignature retrieveSignature() + { + return QDBusSignature("g"); + } + + QDBusVariant retrieveVariant() + { + return QDBusVariant(retrieveString()); + } + + QStringList retrieveStringList() + { + return QStringList() << "one" << "two"; + } + + QByteArray retrieveByteArray() + { + return "Hello, World"; + } + + QVariantList retrieveList() + { + return QVariantList() << retrieveInt() << retrieveString() + << retrieveByteArray(); + } + + QList<QDBusObjectPath> retrieveObjectPathList() + { + return QList<QDBusObjectPath>() << QDBusObjectPath("/") << QDBusObjectPath("/foo"); + } + + QVariantMap retrieveMap() + { + QVariantMap map; + map["one"] = 1; + map["two"] = 2U; + map["string"] = retrieveString(); + map["stringlist"] = retrieveStringList(); + return map; + } + + IntStringMap retrieveIntStringMap() + { + IntStringMap map; + map[1] = "1"; + map[2] = "2"; + map[-1231456] = "foo"; + return map; + } + + MyStruct retrieveStruct() + { + return MyStruct(); + } +}; + +tst_QDBusReply::tst_QDBusReply() +{ + adaptor = new TypesInterface(this); + QDBusConnection::sessionBus().registerObject("/", this); + + iface = new QDBusInterface(QDBusConnection::sessionBus().baseService(), "/", + "com.trolltech.Qt.Autotests.TypesInterface", + QDBusConnection::sessionBus(), + this); +} + +void tst_QDBusReply::init() +{ + QVERIFY(iface); + QVERIFY(iface->isValid()); +} + +void tst_QDBusReply::unconnected() +{ + QDBusConnection con("invalid stored connection"); + QVERIFY(!con.isConnected()); + QDBusInterface iface("doesnt.matter", "/", "doesnt.matter", con); + QVERIFY(!iface.isValid()); + + QDBusReply<void> rvoid = iface.asyncCall("ReloadConfig"); + QVERIFY(!rvoid.isValid()); + + QDBusReply<QString> rstring = iface.asyncCall("GetId"); + QVERIFY(!rstring.isValid()); + QVERIFY(rstring.value().isEmpty()); +} + +void tst_QDBusReply::simpleTypes() +{ + QDBusReply<bool> rbool = iface->call(QDBus::BlockWithGui, "retrieveBool"); + QVERIFY(rbool.isValid()); + QCOMPARE(rbool.value(), adaptor->retrieveBool()); + + QDBusReply<uchar> ruchar = iface->call(QDBus::BlockWithGui, "retrieveUChar"); + QVERIFY(ruchar.isValid()); + QCOMPARE(ruchar.value(), adaptor->retrieveUChar()); + + QDBusReply<short> rshort = iface->call(QDBus::BlockWithGui, "retrieveShort"); + QVERIFY(rshort.isValid()); + QCOMPARE(rshort.value(), adaptor->retrieveShort()); + + QDBusReply<ushort> rushort = iface->call(QDBus::BlockWithGui, "retrieveUShort"); + QVERIFY(rushort.isValid()); + QCOMPARE(rushort.value(), adaptor->retrieveUShort()); + + QDBusReply<int> rint = iface->call(QDBus::BlockWithGui, "retrieveInt"); + QVERIFY(rint.isValid()); + QCOMPARE(rint.value(), adaptor->retrieveInt()); + + QDBusReply<uint> ruint = iface->call(QDBus::BlockWithGui, "retrieveUInt"); + QVERIFY(ruint.isValid()); + QCOMPARE(ruint.value(), adaptor->retrieveUInt()); + + QDBusReply<qlonglong> rqlonglong = iface->call(QDBus::BlockWithGui, "retrieveLongLong"); + QVERIFY(rqlonglong.isValid()); + QCOMPARE(rqlonglong.value(), adaptor->retrieveLongLong()); + + QDBusReply<qulonglong> rqulonglong = iface->call(QDBus::BlockWithGui, "retrieveULongLong"); + QVERIFY(rqulonglong.isValid()); + QCOMPARE(rqulonglong.value(), adaptor->retrieveULongLong()); + + QDBusReply<double> rdouble = iface->call(QDBus::BlockWithGui, "retrieveDouble"); + QVERIFY(rdouble.isValid()); + QCOMPARE(rdouble.value(), adaptor->retrieveDouble()); + + QDBusReply<QString> rstring = iface->call(QDBus::BlockWithGui, "retrieveString"); + QVERIFY(rstring.isValid()); + QCOMPARE(rstring.value(), adaptor->retrieveString()); + + QDBusReply<QDBusObjectPath> robjectpath = iface->call(QDBus::BlockWithGui, "retrieveObjectPath"); + QVERIFY(robjectpath.isValid()); + QCOMPARE(robjectpath.value().path(), adaptor->retrieveObjectPath().path()); + + QDBusReply<QDBusSignature> rsignature = iface->call(QDBus::BlockWithGui, "retrieveSignature"); + QVERIFY(rsignature.isValid()); + QCOMPARE(rsignature.value().signature(), adaptor->retrieveSignature().signature()); + + QDBusReply<QDBusVariant> rdbusvariant = iface->call(QDBus::BlockWithGui, "retrieveVariant"); + QVERIFY(rdbusvariant.isValid()); + QCOMPARE(rdbusvariant.value().variant(), adaptor->retrieveVariant().variant()); + + QDBusReply<QVariant> rvariant = iface->call(QDBus::BlockWithGui, "retrieveVariant"); + QVERIFY(rvariant.isValid()); + QCOMPARE(rvariant.value(), adaptor->retrieveVariant().variant()); + + QDBusReply<QByteArray> rbytearray = iface->call(QDBus::BlockWithGui, "retrieveByteArray"); + QVERIFY(rbytearray.isValid()); + QCOMPARE(rbytearray.value(), adaptor->retrieveByteArray()); + + QDBusReply<QStringList> rstringlist = iface->call(QDBus::BlockWithGui, "retrieveStringList"); + QVERIFY(rstringlist.isValid()); + QCOMPARE(rstringlist.value(), adaptor->retrieveStringList()); +} + +void tst_QDBusReply::complexTypes() +{ + QDBusReply<QVariantList> rlist = iface->call(QDBus::BlockWithGui, "retrieveList"); + QVERIFY(rlist.isValid()); + QCOMPARE(rlist.value(), adaptor->retrieveList()); + + QDBusReply<QList<QDBusObjectPath> > rolist = iface->call(QDBus::BlockWithGui, "retrieveObjectPathList"); + QVERIFY(rolist.isValid()); + QCOMPARE(rolist.value(), adaptor->retrieveObjectPathList()); + + QDBusReply<QVariantMap> rmap = iface->call(QDBus::BlockWithGui, "retrieveMap"); + QVERIFY(rmap.isValid()); + QCOMPARE(rmap.value(), adaptor->retrieveMap()); + + QDBusReply<IntStringMap> rismap = iface->call(QDBus::BlockWithGui, "retrieveIntStringMap"); + QVERIFY(rismap.isValid()); + QCOMPARE(rismap.value(), adaptor->retrieveIntStringMap()); + + QDBusReply<MyStruct> rstruct = iface->call(QDBus::BlockWithGui, "retrieveStruct"); + QVERIFY(rstruct.isValid()); + QCOMPARE(rstruct.value(), adaptor->retrieveStruct()); +} + +void tst_QDBusReply::wrongTypes() +{ + QDBusReply<bool> rbool = iface->call(QDBus::BlockWithGui, "retrieveInt"); + QVERIFY(!rbool.isValid()); + + rbool = iface->call(QDBus::BlockWithGui, "retrieveShort"); + QVERIFY(!rbool.isValid()); + + rbool = iface->call(QDBus::BlockWithGui, "retrieveStruct"); + QVERIFY(!rbool.isValid()); + + QDBusReply<short> rshort = iface->call(QDBus::BlockWithGui, "retrieveInt"); + QVERIFY(!rshort.isValid()); + + rshort = iface->call(QDBus::BlockWithGui, "retrieveBool"); + QVERIFY(!rshort.isValid()); + + rshort = iface->call(QDBus::BlockWithGui, "retrieveStruct"); + QVERIFY(!rshort.isValid()); + + QDBusReply<MyStruct> rstruct = iface->call(QDBus::BlockWithGui, "retrieveInt"); + QVERIFY(!rstruct.isValid()); + + rstruct = iface->call(QDBus::BlockWithGui, "retrieveShort"); + QVERIFY(!rstruct.isValid()); + + rstruct = iface->call(QDBus::BlockWithGui, "retrieveIntStringMap"); + QVERIFY(!rstruct.isValid()); +} + +QTEST_MAIN(tst_QDBusReply) + +#include "tst_qdbusreply.moc" diff --git a/tests/auto/dbus/qdbusservicewatcher/qdbusservicewatcher.pro b/tests/auto/dbus/qdbusservicewatcher/qdbusservicewatcher.pro new file mode 100644 index 0000000000..4970f16863 --- /dev/null +++ b/tests/auto/dbus/qdbusservicewatcher/qdbusservicewatcher.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +QT = core +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusservicewatcher.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} diff --git a/tests/auto/dbus/qdbusservicewatcher/tst_qdbusservicewatcher.cpp b/tests/auto/dbus/qdbusservicewatcher/tst_qdbusservicewatcher.cpp new file mode 100644 index 0000000000..99aeefa8ee --- /dev/null +++ b/tests/auto/dbus/qdbusservicewatcher/tst_qdbusservicewatcher.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtDBus/QDBusServiceWatcher> +#include <QtDBus> +#include <QtTest> + +class tst_QDBusServiceWatcher: public QObject +{ + Q_OBJECT + QString serviceName; + int testCounter; +public: + tst_QDBusServiceWatcher(); + +private slots: + void initTestCase(); + void init(); + + void watchForCreation(); + void watchForDisappearance(); + void watchForOwnerChange(); + void modeChange(); +}; + +tst_QDBusServiceWatcher::tst_QDBusServiceWatcher() + : testCounter(0) +{ +} + +void tst_QDBusServiceWatcher::initTestCase() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); +} + +void tst_QDBusServiceWatcher::init() +{ + // change the service name from test to test + serviceName = "com.example.TestService" + QString::number(testCounter++); +} + +void tst_QDBusServiceWatcher::watchForCreation() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForRegistration); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceRegistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); + + spyR.clear(); + spyU.clear(); + spyO.clear(); + + // unregister it: + con.unregisterService(serviceName); + + // and register again + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); +} + +void tst_QDBusServiceWatcher::watchForDisappearance() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForUnregistration); + watcher.setObjectName("watcher for disappearance"); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceUnregistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + // unregister it: + con.unregisterService(serviceName); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 0); + + QCOMPARE(spyU.count(), 1); + QCOMPARE(spyU.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QCOMPARE(spyO.at(0).at(1).toString(), con.baseService()); + QVERIFY(spyO.at(0).at(2).toString().isEmpty()); +} + +void tst_QDBusServiceWatcher::watchForOwnerChange() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForOwnerChange); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceRegistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); + + spyR.clear(); + spyU.clear(); + spyO.clear(); + + // unregister it: + con.unregisterService(serviceName); + + // and register again + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 1); + QCOMPARE(spyU.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyO.count(), 2); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QCOMPARE(spyO.at(0).at(1).toString(), con.baseService()); + QVERIFY(spyO.at(0).at(2).toString().isEmpty()); + QCOMPARE(spyO.at(1).at(0).toString(), serviceName); + QVERIFY(spyO.at(1).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(1).at(2).toString(), con.baseService()); +} + +void tst_QDBusServiceWatcher::modeChange() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForRegistration); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceRegistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); + + spyR.clear(); + spyU.clear(); + spyO.clear(); + + watcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + + // unregister it: + con.unregisterService(serviceName); + + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceUnregistered(QString)), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 0); + + QCOMPARE(spyU.count(), 1); + QCOMPARE(spyU.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QCOMPARE(spyO.at(0).at(1).toString(), con.baseService()); + QVERIFY(spyO.at(0).at(2).toString().isEmpty()); +} + +QTEST_MAIN(tst_QDBusServiceWatcher) +#include "tst_qdbusservicewatcher.moc" diff --git a/tests/auto/dbus/qdbusthreading/.gitignore b/tests/auto/dbus/qdbusthreading/.gitignore new file mode 100644 index 0000000000..70c71f3ab7 --- /dev/null +++ b/tests/auto/dbus/qdbusthreading/.gitignore @@ -0,0 +1 @@ +tst_qdbusthreading diff --git a/tests/auto/dbus/qdbusthreading/qdbusthreading.pro b/tests/auto/dbus/qdbusthreading/qdbusthreading.pro new file mode 100644 index 0000000000..e239e9861e --- /dev/null +++ b/tests/auto/dbus/qdbusthreading/qdbusthreading.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +QT = core + +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusthreading.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusthreading/tst_qdbusthreading.cpp b/tests/auto/dbus/qdbusthreading/tst_qdbusthreading.cpp new file mode 100644 index 0000000000..e37e4c3ae7 --- /dev/null +++ b/tests/auto/dbus/qdbusthreading/tst_qdbusthreading.cpp @@ -0,0 +1,613 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest> +#include <QtDBus> +#include <QtCore/QVarLengthArray> +#include <QtCore/QThread> +#include <QtCore/QObject> +#include <QtCore/QSemaphore> +#include <QtCore/QMutex> +#include <QtCore/QWaitCondition> +#include <QtCore/QMap> + +class Thread : public QThread +{ + Q_OBJECT + static int counter; +public: + Thread(bool automatic = true); + void run(); + + using QThread::exec; +}; +int Thread::counter; + +class tst_QDBusThreading : public QObject +{ + Q_OBJECT + static tst_QDBusThreading *_self; + QAtomicInt threadJoinCount; + QSemaphore threadJoin; +public: + QSemaphore sem1, sem2; + volatile bool success; + QEventLoop *loop; + enum FunctionSpy { + NoMethod = 0, + Adaptor_method, + Object_method + } functionSpy; + + QThread *threadSpy; + int signalSpy; + + tst_QDBusThreading(); + static inline tst_QDBusThreading *self() { return _self; } + + void joinThreads(); + bool waitForSignal(QObject *obj, const char *signal, int delay = 1); + +public Q_SLOTS: + void cleanup(); + void signalSpySlot() { ++signalSpy; } + void threadStarted() { threadJoinCount.ref(); } + void threadFinished() { threadJoin.release(); } + + void dyingThread_thread(); + void lastInstanceInOtherThread_thread(); + void concurrentCreation_thread(); + void disconnectAnothersConnection_thread(); + void accessMainsConnection_thread(); + void accessOthersConnection_thread(); + void registerObjectInOtherThread_thread(); + void registerAdaptorInOtherThread_thread(); + void callbackInMainThread_thread(); + void callbackInAuxThread_thread(); + void callbackInAnotherAuxThread_thread(); + +private Q_SLOTS: + void initTestCase(); + void dyingThread(); + void lastInstanceInOtherThread(); + void concurrentCreation(); + void disconnectAnothersConnection(); + void accessMainsConnection(); + void accessOthersConnection(); + void registerObjectInOtherThread(); + void registerAdaptorInOtherThread(); + void callbackInMainThread(); + void callbackInAuxThread(); + void callbackInAnotherAuxThread(); +}; +tst_QDBusThreading *tst_QDBusThreading::_self; + +class Adaptor : public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.Adaptor") +public: + Adaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) + { + } + +public Q_SLOTS: + void method() + { + tst_QDBusThreading::self()->functionSpy = tst_QDBusThreading::Adaptor_method; + tst_QDBusThreading::self()->threadSpy = QThread::currentThread(); + emit signal(); + } + +Q_SIGNALS: + void signal(); +}; + +class Object : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "local.Object") +public: + Object(bool useAdaptor) + { + if (useAdaptor) + new Adaptor(this); + } + + ~Object() + { + QMetaObject::invokeMethod(QThread::currentThread(), "quit", Qt::QueuedConnection); + } + +public Q_SLOTS: + void method() + { + tst_QDBusThreading::self()->functionSpy = tst_QDBusThreading::Object_method; + tst_QDBusThreading::self()->threadSpy = QThread::currentThread(); + emit signal(); + deleteLater(); + } + +Q_SIGNALS: + void signal(); +}; + +#if 0 +typedef void (*qdbusThreadDebugFunc)(int, int, QDBusConnectionPrivate *); +QDBUS_EXPORT void qdbusDefaultThreadDebug(int, int, QDBusConnectionPrivate *); +extern QDBUS_EXPORT qdbusThreadDebugFunc qdbusThreadDebug; + +static void threadDebug(int action, int condition, QDBusConnectionPrivate *p) +{ + qdbusDefaultThreadDebug(action, condition, p); +} +#endif + +Thread::Thread(bool automatic) +{ + setObjectName(QString::fromLatin1("Aux thread %1").arg(++counter)); + connect(this, SIGNAL(started()), tst_QDBusThreading::self(), SLOT(threadStarted())); + connect(this, SIGNAL(finished()), tst_QDBusThreading::self(), SLOT(threadFinished()), + Qt::DirectConnection); + connect(this, SIGNAL(finished()), this, SLOT(deleteLater()), Qt::DirectConnection); + if (automatic) + start(); +} + +void Thread::run() +{ + QVarLengthArray<char, 56> name; + name.append(QTest::currentTestFunction(), qstrlen(QTest::currentTestFunction())); + name.append("_thread", sizeof "_thread"); + QMetaObject::invokeMethod(tst_QDBusThreading::self(), name.constData(), Qt::DirectConnection); +} + +static const char myConnectionName[] = "connection"; + +tst_QDBusThreading::tst_QDBusThreading() + : loop(0), functionSpy(NoMethod), threadSpy(0) +{ + _self = this; + QCoreApplication::instance()->thread()->setObjectName("Main thread"); +} + +void tst_QDBusThreading::joinThreads() +{ + threadJoin.acquire(threadJoinCount); + threadJoinCount = 0; +} + +bool tst_QDBusThreading::waitForSignal(QObject *obj, const char *signal, int delay) +{ + QObject::connect(obj, signal, &QTestEventLoop::instance(), SLOT(exitLoop())); + QPointer<QObject> safe = obj; + + QTestEventLoop::instance().enterLoop(delay); + if (!safe.isNull()) + QObject::disconnect(safe, signal, &QTestEventLoop::instance(), SLOT(exitLoop())); + return QTestEventLoop::instance().timeout(); +} + +void tst_QDBusThreading::cleanup() +{ + joinThreads(); + + if (sem1.available()) + sem1.acquire(sem1.available()); + if (sem2.available()) + sem2.acquire(sem2.available()); + + if (QDBusConnection(myConnectionName).isConnected()) + QDBusConnection::disconnectFromBus(myConnectionName); + + delete loop; + loop = 0; + + QTest::qWait(500); +} + +void tst_QDBusThreading::initTestCase() +{ +} + +void tst_QDBusThreading::dyingThread_thread() +{ + QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName); +} + +void tst_QDBusThreading::dyingThread() +{ + Thread *th = new Thread(false); + QTestEventLoop::instance().connect(th, SIGNAL(destroyed(QObject*)), SLOT(exitLoop())); + th->start(); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QDBusConnection con(myConnectionName); + QDBusConnection::disconnectFromBus(myConnectionName); + + QVERIFY(con.isConnected()); + QDBusReply<QStringList> reply = con.interface()->registeredServiceNames(); + QVERIFY(reply.isValid()); + QVERIFY(!reply.value().isEmpty()); + QVERIFY(reply.value().contains(con.baseService())); + + con.interface()->callWithCallback("ListNames", QVariantList(), + &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_QDBusThreading::lastInstanceInOtherThread_thread() +{ + QDBusConnection con(myConnectionName); + QVERIFY(con.isConnected()); + + QDBusConnection::disconnectFromBus(myConnectionName); + + // con is being destroyed in the wrong thread +} + +void tst_QDBusThreading::lastInstanceInOtherThread() +{ + Thread *th = new Thread(false); + // create the connection: + QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName); + + th->start(); + th->wait(); +} + +void tst_QDBusThreading::concurrentCreation_thread() +{ + sem1.acquire(); + QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus, + myConnectionName); + sem2.release(); +} + +void tst_QDBusThreading::concurrentCreation() +{ + Thread *th = new Thread; + + { + sem1.release(); + QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus, + myConnectionName); + QVERIFY(con.isConnected()); + sem2.acquire(); + } + waitForSignal(th, SIGNAL(finished())); + QDBusConnection::disconnectFromBus(myConnectionName); + + QVERIFY(!QDBusConnection(myConnectionName).isConnected()); +} + +void tst_QDBusThreading::disconnectAnothersConnection_thread() +{ + QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus, + myConnectionName); + sem2.release(); +} + +void tst_QDBusThreading::disconnectAnothersConnection() +{ + new Thread; + sem2.acquire(); + + QVERIFY(QDBusConnection(myConnectionName).isConnected()); + QDBusConnection::disconnectFromBus(myConnectionName); +} + +void tst_QDBusThreading::accessMainsConnection_thread() +{ + sem1.acquire(); + QDBusConnection con = QDBusConnection::sessionBus(); + con.interface()->registeredServiceNames(); + sem2.release(); +} + +void tst_QDBusThreading::accessMainsConnection() +{ + QVERIFY(QDBusConnection::sessionBus().isConnected()); + + new Thread; + sem1.release(); + sem2.acquire(); +}; + +void tst_QDBusThreading::accessOthersConnection_thread() +{ + QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName); + sem2.release(); + + // wait for main thread to be done + sem1.acquire(); + QDBusConnection::disconnectFromBus(myConnectionName); + sem2.release(); +} + +void tst_QDBusThreading::accessOthersConnection() +{ + new Thread; + + // wait for the connection to be created + sem2.acquire(); + + { + QDBusConnection con(myConnectionName); + QVERIFY(con.isConnected()); + QVERIFY(con.baseService() != QDBusConnection::sessionBus().baseService()); + + QDBusReply<QStringList> reply = con.interface()->registeredServiceNames(); + if (!reply.isValid()) + qDebug() << reply.error().name() << reply.error().message(); + QVERIFY(reply.isValid()); + QVERIFY(!reply.value().isEmpty()); + QVERIFY(reply.value().contains(con.baseService())); + QVERIFY(reply.value().contains(QDBusConnection::sessionBus().baseService())); + } + + // tell it to destroy: + sem1.release(); + sem2.acquire(); + + QDBusConnection con(myConnectionName); + QVERIFY(!con.isConnected()); +} + +void tst_QDBusThreading::registerObjectInOtherThread_thread() +{ + { + Object *obj = new Object(false); + QDBusConnection::sessionBus().registerObject("/", obj, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals); + + sem2.release(); + static_cast<Thread *>(QThread::currentThread())->exec(); + } + + sem2.release(); +} + +void tst_QDBusThreading::registerObjectInOtherThread() +{ + QVERIFY(QDBusConnection::sessionBus().isConnected()); + QThread *th = new Thread; + sem2.acquire(); + + signalSpy = 0; + + QDBusInterface iface(QDBusConnection::sessionBus().baseService(), "/", "local.Object"); + QVERIFY(iface.isValid()); + + connect(&iface, SIGNAL(signal()), SLOT(signalSpySlot())); + + QTest::qWait(100); + QCOMPARE(signalSpy, 0); + + functionSpy = NoMethod; + threadSpy = 0; + QDBusReply<void> reply = iface.call("method"); + QVERIFY(reply.isValid()); + QCOMPARE(functionSpy, Object_method); + QCOMPARE(threadSpy, th); + + QTest::qWait(100); + QCOMPARE(signalSpy, 1); + + sem2.acquire(); // the object is gone + functionSpy = NoMethod; + threadSpy = 0; + reply = iface.call("method"); + QVERIFY(!reply.isValid()); + QCOMPARE(functionSpy, NoMethod); + QCOMPARE(threadSpy, (QThread*)0); +} + +void tst_QDBusThreading::registerAdaptorInOtherThread_thread() +{ + { + Object *obj = new Object(true); + QDBusConnection::sessionBus().registerObject("/", obj, QDBusConnection::ExportAdaptors | + QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals); + + sem2.release(); + static_cast<Thread *>(QThread::currentThread())->exec(); + } + + sem2.release(); +} + +void tst_QDBusThreading::registerAdaptorInOtherThread() +{ + QVERIFY(QDBusConnection::sessionBus().isConnected()); + QThread *th = new Thread; + sem2.acquire(); + + QDBusInterface object(QDBusConnection::sessionBus().baseService(), "/", "local.Object"); + QDBusInterface adaptor(QDBusConnection::sessionBus().baseService(), "/", "local.Adaptor"); + QVERIFY(object.isValid()); + QVERIFY(adaptor.isValid()); + + signalSpy = 0; + connect(&adaptor, SIGNAL(signal()), SLOT(signalSpySlot())); + QCOMPARE(signalSpy, 0); + + functionSpy = NoMethod; + threadSpy = 0; + QDBusReply<void> reply = adaptor.call("method"); + QVERIFY(reply.isValid()); + QCOMPARE(functionSpy, Adaptor_method); + QCOMPARE(threadSpy, th); + + QTest::qWait(100); + QCOMPARE(signalSpy, 1); + + functionSpy = NoMethod; + threadSpy = 0; + reply = object.call("method"); + QVERIFY(reply.isValid()); + QCOMPARE(functionSpy, Object_method); + QCOMPARE(threadSpy, th); + + QTest::qWait(100); + QCOMPARE(signalSpy, 1); + + sem2.acquire(); // the object is gone + functionSpy = NoMethod; + threadSpy = 0; + reply = adaptor.call("method"); + QVERIFY(!reply.isValid()); + QCOMPARE(functionSpy, NoMethod); + QCOMPARE(threadSpy, (QThread*)0); + reply = object.call("method"); + QVERIFY(!reply.isValid()); + QCOMPARE(functionSpy, NoMethod); + QCOMPARE(threadSpy, (QThread*)0); +} + +void tst_QDBusThreading::callbackInMainThread_thread() +{ + QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName); + sem2.release(); + + static_cast<Thread *>(QThread::currentThread())->exec(); + QDBusConnection::disconnectFromBus(myConnectionName); +} + +void tst_QDBusThreading::callbackInMainThread() +{ + Thread *th = new Thread; + + // wait for it to be connected + sem2.acquire(); + + QDBusConnection con(myConnectionName); + con.interface()->callWithCallback("ListNames", QVariantList(), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QMetaObject::invokeMethod(th, "quit"); + waitForSignal(th, SIGNAL(finished())); +} + +void tst_QDBusThreading::callbackInAuxThread_thread() +{ + QDBusConnection con(QDBusConnection::sessionBus()); + QTestEventLoop ownLoop; + con.interface()->callWithCallback("ListNames", QVariantList(), + &ownLoop, SLOT(exitLoop())); + ownLoop.enterLoop(10); + loop->exit(ownLoop.timeout() ? 1 : 0); +} + +void tst_QDBusThreading::callbackInAuxThread() +{ + QVERIFY(QDBusConnection::sessionBus().isConnected()); + + loop = new QEventLoop; + + new Thread; + QCOMPARE(loop->exec(), 0); +} + +void tst_QDBusThreading::callbackInAnotherAuxThread_thread() +{ + sem1.acquire(); + if (!loop) { + // first thread + // create the connection and just wait + QDBusConnection con = QDBusConnection::connectToBus(QDBusConnection::SessionBus, myConnectionName); + loop = new QEventLoop; + + // tell the main thread we have created the loop and connection + sem2.release(); + + // wait for the main thread to connect its signal + sem1.acquire(); + success = loop->exec() == 0; + sem2.release(); + + // clean up + QDBusConnection::disconnectFromBus(myConnectionName); + } else { + // second thread + // try waiting for a message + QDBusConnection con(myConnectionName); + QTestEventLoop ownLoop; + con.interface()->callWithCallback("ListNames", QVariantList(), + &ownLoop, SLOT(exitLoop())); + ownLoop.enterLoop(1); + loop->exit(ownLoop.timeout() ? 1 : 0); + } +} + +void tst_QDBusThreading::callbackInAnotherAuxThread() +{ + // create first thread + success = false; + new Thread; + + // wait for the event loop + sem1.release(); + sem2.acquire(); + QVERIFY(loop); + + // create the second thread + new Thread; + sem1.release(2); + + // wait for loop thread to finish executing: + sem2.acquire(); + + QVERIFY(success); +} + +// Next tests: +// - unexport an object at the moment the call is being delivered +// - delete an object at the moment the call is being delivered +// - keep a global-static QDBusConnection for a thread-created connection + +QTEST_MAIN(tst_QDBusThreading) +#include "tst_qdbusthreading.moc" diff --git a/tests/auto/dbus/qdbustype/qdbustype.pro b/tests/auto/dbus/qdbustype/qdbustype.pro new file mode 100644 index 0000000000..9b6808b562 --- /dev/null +++ b/tests/auto/dbus/qdbustype/qdbustype.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +QT = core core-private +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbustype.cpp + QT += dbus dbus-private + QMAKE_CXXFLAGS += $$QT_CFLAGS_DBUS + LIBS_PRIVATE += $$QT_LIBS_DBUS +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} diff --git a/tests/auto/dbus/qdbustype/tst_qdbustype.cpp b/tests/auto/dbus/qdbustype/tst_qdbustype.cpp new file mode 100644 index 0000000000..5c46b4684a --- /dev/null +++ b/tests/auto/dbus/qdbustype/tst_qdbustype.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** 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 FOO module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/QCoreApplication> + +#include <QtDBus/private/qdbusutil_p.h> + +#include <dbus/dbus.h> + +class tst_QDBusType : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void isValidFixedType_data(); + void isValidFixedType(); + void isValidBasicType_data(); + void isValidBasicType(); + void isValidSingleSignature_data(); + void isValidSingleSignature(); + void isValidArray_data(); + void isValidArray(); + void isValidSignature_data(); + void isValidSignature(); +}; + +enum { Invalid = false, Valid = true }; + +static void addColumns() +{ + // All tests use these two columns only + QTest::addColumn<QString>("data"); + QTest::addColumn<bool>("result"); + QTest::addColumn<bool>("isValid"); +} + +// ---- type adds --- +static void addFixedTypes() +{ + QTest::newRow("bool") << DBUS_TYPE_BOOLEAN_AS_STRING << true << true; + QTest::newRow("byte") << DBUS_TYPE_BYTE_AS_STRING << true << true; + QTest::newRow("int16") << DBUS_TYPE_INT16_AS_STRING << true << true; + QTest::newRow("uint16") << DBUS_TYPE_UINT16_AS_STRING << true << true; + QTest::newRow("int32") << DBUS_TYPE_INT32_AS_STRING << true << true; + QTest::newRow("uint32") << DBUS_TYPE_UINT32_AS_STRING << true << true; + QTest::newRow("int64") << DBUS_TYPE_INT64_AS_STRING << true << true; + QTest::newRow("uint64") << DBUS_TYPE_UINT64_AS_STRING << true << true; + QTest::newRow("double") << DBUS_TYPE_DOUBLE_AS_STRING << true << true; +#ifdef DBUS_TYPE_UNIX_FD_AS_STRING + QTest::newRow("unixfd") << DBUS_TYPE_UNIX_FD_AS_STRING << true << true; +#endif +} + +static void addInvalidSingleLetterTypes() +{ + QChar nulString[] = { 0 }; + QTest::newRow("nul") << QString(nulString, 1) << false << false; + QTest::newRow("tilde") << "~" << false << false; + QTest::newRow("struct-begin") << "(" << false << false; + QTest::newRow("struct-end") << ")" << false << false; + QTest::newRow("dict-entry-begin") << "{" << false << false; + QTest::newRow("dict-entry-end") << "}" << false << false; + QTest::newRow("array-no-element") << "a" << false << false; +} + +static void addBasicTypes(bool basicsAreValid) +{ + addFixedTypes(); + QTest::newRow("string") << DBUS_TYPE_STRING_AS_STRING << basicsAreValid << true; + QTest::newRow("object-path") << DBUS_TYPE_OBJECT_PATH_AS_STRING << basicsAreValid << true; + QTest::newRow("signature") << DBUS_TYPE_SIGNATURE_AS_STRING << basicsAreValid << true; +} + +static void addVariant(bool variantIsValid) +{ + QTest::newRow("variant") << "v" << variantIsValid << true; +} + +static void addSingleSignatures() +{ + addBasicTypes(Valid); + addVariant(Valid); + QTest::newRow("struct-1") << "(y)" << true; + QTest::newRow("struct-2") << "(yy)" << true; + QTest::newRow("struct-3") << "(yyv)" << true; + + QTest::newRow("struct-nested-1") << "((y))" << true; + QTest::newRow("struct-nested-2") << "((yy))" << true; + QTest::newRow("struct-nested-3") << "(y(y))" << true; + QTest::newRow("struct-nested-4") << "((y)y)" << true; + QTest::newRow("struct-nested-5") << "(y(y)y)" << true; + QTest::newRow("struct-nested-6") << "((y)(y))" << true; + + QTest::newRow("array-1") << "as" << true; + QTest::newRow("struct-array-1") << "(as)" << true; + QTest::newRow("struct-array-2") << "(yas)" << true; + QTest::newRow("struct-array-3") << "(asy)" << true; + QTest::newRow("struct-array-4") << "(yasy)" << true; + + QTest::newRow("dict-1") << "a{sy}" << true; + QTest::newRow("dict-2") << "a{sv}" << true; + QTest::newRow("dict-struct-1") << "a{s(y)}" << true; + QTest::newRow("dict-struct-2") << "a{s(yyyy)}" << true; + QTest::newRow("dict-struct-array") << "a{s(ay)}" << true; + QTest::newRow("dict-array") << "a{sas}" << true; + QTest::newRow("dict-array-struct") << "a{sa(y)}" << true; + + addInvalidSingleLetterTypes(); + QTest::newRow("naked-dict-empty") << "{}" << false; + QTest::newRow("naked-dict-missing-value") << "{i}" << false; + + QTest::newRow("dict-empty") << "a{}" << false; + QTest::newRow("dict-missing-value") << "a{i}" << false; + QTest::newRow("dict-non-basic-key") << "a{vi}" << false; + QTest::newRow("dict-struct-key") << "a{(y)y}" << false; + QTest::newRow("dict-missing-close") << "a{sv" << false; + QTest::newRow("dict-mismatched-close") << "a{sv)" << false; + QTest::newRow("dict-missing-value-close") << "a{s" << false; + + QTest::newRow("empty-struct") << "()" << false; + QTest::newRow("struct-missing-close") << "(s" << false; + QTest::newRow("struct-nested-missing-close-1") << "((s)" << false; + QTest::newRow("struct-nested-missing-close-2") << "((s" << false; + + QTest::newRow("struct-ending-array-no-element") << "(a)" << false; +} + +static void addNakedDictEntry() +{ + QTest::newRow("naked-dict-entry") << "{sv}" << false; +} + +// ---- tests ---- + +void tst_QDBusType::isValidFixedType_data() +{ + addColumns(); + addFixedTypes(); + addBasicTypes(Invalid); + addVariant(Invalid); + addInvalidSingleLetterTypes(); +} + +void tst_QDBusType::isValidFixedType() +{ + QFETCH(QString, data); + QFETCH(bool, result); + QFETCH(bool, isValid); + QVERIFY2(data.length() == 1, "Test is malformed, this function must test only one-letter types"); + QVERIFY(isValid || (!isValid && !result)); + + int type = data.at(0).unicode(); + if (isValid) + QCOMPARE(bool(dbus_type_is_fixed(type)), result); + QCOMPARE(QDBusUtil::isValidFixedType(type), result); +} + +void tst_QDBusType::isValidBasicType_data() +{ + addColumns(); + addBasicTypes(Valid); + addVariant(Invalid); + addInvalidSingleLetterTypes(); +} + +void tst_QDBusType::isValidBasicType() +{ + QFETCH(QString, data); + QFETCH(bool, result); + QFETCH(bool, isValid); + QVERIFY2(data.length() == 1, "Test is malformed, this function must test only one-letter types"); + QVERIFY(isValid || (!isValid && !result)); + + int type = data.at(0).unicode(); + if (isValid) + QCOMPARE(bool(dbus_type_is_basic(type)), result); + QCOMPARE(QDBusUtil::isValidBasicType(type), result); +} + +void tst_QDBusType::isValidSingleSignature_data() +{ + addColumns(); + addSingleSignatures(); + addNakedDictEntry(); +} + +void tst_QDBusType::isValidSingleSignature() +{ + QFETCH(QString, data); + QFETCH(bool, result); + + QCOMPARE(bool(dbus_signature_validate_single(data.toLatin1(), 0)), result); + QCOMPARE(QDBusUtil::isValidSingleSignature(data), result); +} + +void tst_QDBusType::isValidArray_data() +{ + addColumns(); + addSingleSignatures(); +} + +void tst_QDBusType::isValidArray() +{ + QFETCH(QString, data); + QFETCH(bool, result); + + data.prepend("a"); + QCOMPARE(bool(dbus_signature_validate_single(data.toLatin1(), 0)), result); + QCOMPARE(QDBusUtil::isValidSingleSignature(data), result); + + data.prepend("a"); + QCOMPARE(bool(dbus_signature_validate_single(data.toLatin1(), 0)), result); + QCOMPARE(QDBusUtil::isValidSingleSignature(data), result); +} + +void tst_QDBusType::isValidSignature_data() +{ + isValidSingleSignature_data(); +} + +void tst_QDBusType::isValidSignature() +{ + QFETCH(QString, data); + QFETCH(bool, result); + + data.append(data); + if (data.at(0).unicode()) + QCOMPARE(bool(dbus_signature_validate(data.toLatin1(), 0)), result); + QCOMPARE(QDBusUtil::isValidSignature(data), result); +} + +QTEST_MAIN(tst_QDBusType) + +#include "tst_qdbustype.moc" diff --git a/tests/auto/dbus/qdbusxmlparser/.gitignore b/tests/auto/dbus/qdbusxmlparser/.gitignore new file mode 100644 index 0000000000..e6a1cd4700 --- /dev/null +++ b/tests/auto/dbus/qdbusxmlparser/.gitignore @@ -0,0 +1 @@ +tst_qdbusxmlparser diff --git a/tests/auto/dbus/qdbusxmlparser/qdbusxmlparser.pro b/tests/auto/dbus/qdbusxmlparser/qdbusxmlparser.pro new file mode 100644 index 0000000000..0ac0d9e198 --- /dev/null +++ b/tests/auto/dbus/qdbusxmlparser/qdbusxmlparser.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +QT = core core-private +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusxmlparser.cpp + QT += dbus dbus-private +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} + + diff --git a/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp b/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp new file mode 100644 index 0000000000..e0f03307f3 --- /dev/null +++ b/tests/auto/dbus/qdbusxmlparser/tst_qdbusxmlparser.cpp @@ -0,0 +1,600 @@ +/**************************************************************************** +** +** 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 test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +/* -*- C++ -*- + */ +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> +#include <QtDBus/QtDBus> + +#define USE_PRIVATE_CODE +#include "../qdbusmarshall/common.h" + +class tst_QDBusXmlParser: public QObject +{ + Q_OBJECT + +private: + void parsing_common(const QString&); + +private slots: + void parsing_data(); + void parsing(); + void parsingWithDoctype_data(); + void parsingWithDoctype(); + + void objectWithContent_data(); + void objectWithContent(); + + void methods_data(); + void methods(); + void signals__data(); + void signals_(); + void properties_data(); + void properties(); +}; + +void tst_QDBusXmlParser::parsing_data() +{ + QTest::addColumn<QString>("xmlData"); + QTest::addColumn<int>("interfaceCount"); + QTest::addColumn<int>("objectCount"); + + QTest::newRow("null") << QString() << 0 << 0; + QTest::newRow("empty") << QString("") << 0 << 0; + + QTest::newRow("junk") << "<junk/>" << 0 << 0; + QTest::newRow("interface-inside-junk") << "<junk><interface name=\"iface.iface1\" /></junk>" + << 0 << 0; + QTest::newRow("object-inside-junk") << "<junk><node name=\"obj1\" /></junk>" + << 0 << 0; + + QTest::newRow("zero-interfaces") << "<node/>" << 0 << 0; + QTest::newRow("one-interface") << "<node><interface name=\"iface.iface1\" /></node>" << 1 << 0; + + + QTest::newRow("two-interfaces") << "<node><interface name=\"iface.iface1\" />" + "<interface name=\"iface.iface2\"></node>" + << 2 << 0; + + + QTest::newRow("one-object") << "<node><node name=\"obj1\"/></node>" << 0 << 1; + QTest::newRow("two-objects") << "<node><node name=\"obj1\"/><node name=\"obj2\"></node>" << 0 << 2; + + QTest::newRow("i1o1") << "<node><interface name=\"iface.iface1\"><node name=\"obj1\"></node>" << 1 << 1; + +} + +void tst_QDBusXmlParser::parsing_common(const QString &xmlData) +{ + QDBusIntrospection::ObjectTree obj = + QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/"); + QFETCH(int, interfaceCount); + QFETCH(int, objectCount); + QCOMPARE(obj.interfaces.count(), interfaceCount); + QCOMPARE(obj.childObjects.count(), objectCount); + + // also verify the naming + int i = 0; + foreach (QString name, obj.interfaces) + QCOMPARE(name, QString("iface.iface%1").arg(++i)); + + i = 0; + foreach (QString name, obj.childObjects) + QCOMPARE(name, QString("obj%1").arg(++i)); +} + +void tst_QDBusXmlParser::parsing() +{ + QFETCH(QString, xmlData); + + parsing_common(xmlData); +} + +void tst_QDBusXmlParser::parsingWithDoctype_data() +{ + parsing_data(); +} + +void tst_QDBusXmlParser::parsingWithDoctype() +{ + QString docType = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" + "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"; + QFETCH(QString, xmlData); + + parsing_common(docType + xmlData); +} + +void tst_QDBusXmlParser::objectWithContent_data() +{ + QTest::addColumn<QString>("xmlData"); + QTest::addColumn<QString>("probedObject"); + QTest::addColumn<int>("interfaceCount"); + QTest::addColumn<int>("objectCount"); + + QTest::newRow("zero") << "<node><node name=\"obj\"/></node>" << "obj" << 0 << 0; + + QString xmlData = "<node><node name=\"obj\">" + "<interface name=\"iface.iface1\" />" + "</node></node>"; + QTest::newRow("one-interface") << xmlData << "obj" << 1 << 0; + QTest::newRow("one-interface2") << xmlData << "obj2" << 0 << 0; + + xmlData = "<node><node name=\"obj\">" + "<interface name=\"iface.iface1\" />" + "<interface name=\"iface.iface2\" />" + "</node></node>"; + QTest::newRow("two-interfaces") << xmlData << "obj" << 2 << 0; + QTest::newRow("two-interfaces2") << xmlData << "obj2" << 0 << 0; + + xmlData = "<node><node name=\"obj\">" + "<interface name=\"iface.iface1\" />" + "<interface name=\"iface.iface2\" />" + "</node><node name=\"obj2\">" + "<interface name=\"iface.iface1\" />" + "</node></node>"; + QTest::newRow("two-nodes-two-interfaces") << xmlData << "obj" << 2 << 0; + QTest::newRow("two-nodes-one-interface") << xmlData << "obj2" << 1 << 0; + + xmlData = "<node><node name=\"obj\">" + "<node name=\"obj1\" />" + "</node></node>"; + QTest::newRow("one-object") << xmlData << "obj" << 0 << 1; + QTest::newRow("one-object2") << xmlData << "obj2" << 0 << 0; + + xmlData = "<node><node name=\"obj\">" + "<node name=\"obj1\" />" + "<node name=\"obj2\" />" + "</node></node>"; + QTest::newRow("two-objects") << xmlData << "obj" << 0 << 2; + QTest::newRow("two-objects2") << xmlData << "obj2" << 0 << 0; + + xmlData = "<node><node name=\"obj\">" + "<node name=\"obj1\" />" + "<node name=\"obj2\" />" + "</node><node name=\"obj2\">" + "<node name=\"obj1\" />" + "</node></node>"; + QTest::newRow("two-nodes-two-objects") << xmlData << "obj" << 0 << 2; + QTest::newRow("two-nodes-one-object") << xmlData << "obj2" << 0 << 1; +} + +void tst_QDBusXmlParser::objectWithContent() +{ + QFETCH(QString, xmlData); + QFETCH(QString, probedObject); + + QDBusIntrospection::ObjectTree tree = + QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/"); + + const ObjectMap &om = tree.childObjectData; + + if (om.contains(probedObject)) { + const QSharedDataPointer<QDBusIntrospection::ObjectTree>& obj = om.value(probedObject); + QVERIFY(obj != 0); + + QFETCH(int, interfaceCount); + QFETCH(int, objectCount); + + QCOMPARE(obj->interfaces.count(), interfaceCount); + QCOMPARE(obj->childObjects.count(), objectCount); + + // verify the object names + int i = 0; + foreach (QString name, obj->interfaces) + QCOMPARE(name, QString("iface.iface%1").arg(++i)); + + i = 0; + foreach (QString name, obj->childObjects) + QCOMPARE(name, QString("obj%1").arg(++i)); + } +} + +void tst_QDBusXmlParser::methods_data() +{ + QTest::addColumn<QString>("xmlDataFragment"); + QTest::addColumn<MethodMap>("methodMap"); + + MethodMap map; + QTest::newRow("no-methods") << QString() << map; + + // one method without arguments + QDBusIntrospection::Method method; + method.name = "Foo"; + map << method; + QTest::newRow("one-method") << "<method name=\"Foo\"/>" << map; + + // add another method without arguments + method.name = "Bar"; + map << method; + QTest::newRow("two-methods") << "<method name=\"Foo\"/>" + "<method name=\"Bar\"/>" + << map; + + // invert the order of the XML declaration + QTest::newRow("two-methods-inverse") << "<method name=\"Bar\"/>" + "<method name=\"Foo\"/>" + << map; + + // add a third, with annotations + method.name = "Baz"; + method.annotations.insert("foo.testing", "nothing to see here"); + map << method; + QTest::newRow("method-with-annotation") << + "<method name=\"Foo\"/>" + "<method name=\"Bar\"/>" + "<method name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></method>" + << map; + + // arguments + map.clear(); + method.annotations.clear(); + + method.name = "Method"; + method.inputArgs << arg("s"); + map << method; + QTest::newRow("one-in") << + "<method name=\"Method\">" + "<arg type=\"s\" direction=\"in\"/>" + "</method>" << map; + + // two arguments + method.inputArgs << arg("v"); + map.clear(); + map << method; + QTest::newRow("two-in") << + "<method name=\"Method\">" + "<arg type=\"s\" direction=\"in\"/>" + "<arg type=\"v\" direction=\"in\"/>" + "</method>" << map; + + // one invalid arg + method.inputArgs << arg("~", "invalid"); + map.clear(); + map << method; + QTest::newRow("two-in-one-invalid") << + "<method name=\"Method\">" + "<arg type=\"s\" direction=\"in\"/>" + "<arg type=\"v\" direction=\"in\"/>" + "<arg type=\"~\" name=\"invalid\" direction=\"in\"/>" + "</method>" << map; + + // one out argument + method.inputArgs.clear(); + method.outputArgs << arg("s"); + map.clear(); + map << method; + QTest::newRow("one-out") << + "<method name=\"Method\">" + "<arg type=\"s\" direction=\"out\"/>" + "</method>" << map; + + // two in and one out + method.inputArgs << arg("s") << arg("v"); + map.clear(); + map << method; + QTest::newRow("two-in-one-out") << + "<method name=\"Method\">" + "<arg type=\"s\" direction=\"in\"/>" + "<arg type=\"v\" direction=\"in\"/>" + "<arg type=\"s\" direction=\"out\"/>" + "</method>" << map; + + // let's try an arg with name + method.outputArgs.clear(); + method.inputArgs.clear(); + method.inputArgs << arg("s", "foo"); + map.clear(); + map << method; + QTest::newRow("one-in-with-name") << + "<method name=\"Method\">" + "<arg type=\"s\" name=\"foo\" direction=\"in\"/>" + "</method>" << map; + + // two args with name + method.inputArgs << arg("i", "bar"); + map.clear(); + map << method; + QTest::newRow("two-in-with-name") << + "<method name=\"Method\">" + "<arg type=\"s\" name=\"foo\" direction=\"in\"/>" + "<arg type=\"i\" name=\"bar\" direction=\"in\"/>" + "</method>" << map; + + // one complex + map.clear(); + method = QDBusIntrospection::Method(); + + // Method1(in STRING arg1, in BYTE arg2, out ARRAY of STRING) + method.inputArgs << arg("s", "arg1") << arg("y", "arg2"); + method.outputArgs << arg("as"); + method.name = "Method1"; + map << method; + + // Method2(in ARRAY of DICT_ENTRY of (STRING,VARIANT) variantMap, in UINT32 index, + // out STRING key, out VARIANT value) + // with annotation "foo.equivalent":"QVariantMap" + method = QDBusIntrospection::Method(); + method.inputArgs << arg("a{sv}", "variantMap") << arg("u", "index"); + method.outputArgs << arg("s", "key") << arg("v", "value"); + method.annotations.insert("foo.equivalent", "QVariantMap"); + method.name = "Method2"; + map << method; + + QTest::newRow("complex") << + "<method name=\"Method1\">" + "<arg name=\"arg1\" type=\"s\" direction=\"in\"/>" + "<arg name=\"arg2\" type=\"y\" direction=\"in\"/>" + "<arg type=\"as\" direction=\"out\"/>" + "</method>" + "<method name=\"Method2\">" + "<arg name=\"variantMap\" type=\"a{sv}\" direction=\"in\"/>" + "<arg name=\"index\" type=\"u\" direction=\"in\"/>" + "<arg name=\"key\" type=\"s\" direction=\"out\"/>" + "<arg name=\"value\" type=\"v\" direction=\"out\"/>" + "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>" + "</method>" << map; +} + +void tst_QDBusXmlParser::methods() +{ + QString xmlHeader = "<node>" + "<interface name=\"iface.iface1\">", + xmlFooter = "</interface>" + "</node>"; + + QFETCH(QString, xmlDataFragment); + + QDBusIntrospection::Interface iface = + QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter); + + QCOMPARE(iface.name, QString("iface.iface1")); + + QFETCH(MethodMap, methodMap); + MethodMap parsedMap = iface.methods; + + QCOMPARE(parsedMap.count(), methodMap.count()); + QCOMPARE(parsedMap, methodMap); +} + +void tst_QDBusXmlParser::signals__data() +{ + QTest::addColumn<QString>("xmlDataFragment"); + QTest::addColumn<SignalMap>("signalMap"); + + SignalMap map; + QTest::newRow("no-signals") << QString() << map; + + // one signal without arguments + QDBusIntrospection::Signal signal; + signal.name = "Foo"; + map << signal; + QTest::newRow("one-signal") << "<signal name=\"Foo\"/>" << map; + + // add another signal without arguments + signal.name = "Bar"; + map << signal; + QTest::newRow("two-signals") << "<signal name=\"Foo\"/>" + "<signal name=\"Bar\"/>" + << map; + + // invert the order of the XML declaration + QTest::newRow("two-signals-inverse") << "<signal name=\"Bar\"/>" + "<signal name=\"Foo\"/>" + << map; + + // add a third, with annotations + signal.name = "Baz"; + signal.annotations.insert("foo.testing", "nothing to see here"); + map << signal; + QTest::newRow("signal-with-annotation") << + "<signal name=\"Foo\"/>" + "<signal name=\"Bar\"/>" + "<signal name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></signal>" + << map; + + // one out argument + map.clear(); + signal.annotations.clear(); + signal.outputArgs << arg("s"); + signal.name = "Signal"; + map.clear(); + map << signal; + QTest::newRow("one-out") << + "<signal name=\"Signal\">" + "<arg type=\"s\" direction=\"out\"/>" + "</signal>" << map; + + // without saying which direction it is + QTest::newRow("one-out-no-direction") << + "<signal name=\"Signal\">" + "<arg type=\"s\"/>" + "</signal>" << map; + + // two args with name + signal.outputArgs << arg("i", "bar"); + map.clear(); + map << signal; + QTest::newRow("two-out-with-name") << + "<signal name=\"Signal\">" + "<arg type=\"s\" direction=\"out\"/>" + "<arg type=\"i\" name=\"bar\"/>" + "</signal>" << map; + + // one complex + map.clear(); + signal = QDBusIntrospection::Signal(); + + // Signal1(out ARRAY of STRING) + signal.outputArgs << arg("as"); + signal.name = "Signal1"; + map << signal; + + // Signal2(out STRING key, out VARIANT value) + // with annotation "foo.equivalent":"QVariantMap" + signal = QDBusIntrospection::Signal(); + signal.outputArgs << arg("s", "key") << arg("v", "value"); + signal.annotations.insert("foo.equivalent", "QVariantMap"); + signal.name = "Signal2"; + map << signal; + + QTest::newRow("complex") << + "<signal name=\"Signal1\">" + "<arg type=\"as\" direction=\"out\"/>" + "</signal>" + "<signal name=\"Signal2\">" + "<arg name=\"key\" type=\"s\" direction=\"out\"/>" + "<arg name=\"value\" type=\"v\" direction=\"out\"/>" + "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>" + "</signal>" << map; +} + +void tst_QDBusXmlParser::signals_() +{ + QString xmlHeader = "<node>" + "<interface name=\"iface.iface1\">", + xmlFooter = "</interface>" + "</node>"; + + QFETCH(QString, xmlDataFragment); + + QDBusIntrospection::Interface iface = + QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter); + + QCOMPARE(iface.name, QString("iface.iface1")); + + QFETCH(SignalMap, signalMap); + SignalMap parsedMap = iface.signals_; + + QCOMPARE(signalMap.count(), parsedMap.count()); + QCOMPARE(signalMap, parsedMap); +} + +void tst_QDBusXmlParser::properties_data() +{ + QTest::addColumn<QString>("xmlDataFragment"); + QTest::addColumn<PropertyMap>("propertyMap"); + + PropertyMap map; + QTest::newRow("no-signals") << QString() << map; + + // one readable signal + QDBusIntrospection::Property prop; + prop.name = "foo"; + prop.type = "s"; + prop.access = QDBusIntrospection::Property::Read; + map << prop; + QTest::newRow("one-readable") << "<property name=\"foo\" type=\"s\" access=\"read\"/>" << map; + + // one writable signal + prop.access = QDBusIntrospection::Property::Write; + map.clear(); + map << prop; + QTest::newRow("one-writable") << "<property name=\"foo\" type=\"s\" access=\"write\"/>" << map; + + // one read- & writable signal + prop.access = QDBusIntrospection::Property::ReadWrite; + map.clear(); + map << prop; + QTest::newRow("one-read-writable") << "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" + << map; + + // two, mixed properties + prop.name = "bar"; + prop.type = "i"; + prop.access = QDBusIntrospection::Property::Read; + map << prop; + QTest::newRow("two") << + "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" + "<property name=\"bar\" type=\"i\" access=\"read\"/>" << map; + + // invert the order of the declaration + QTest::newRow("two") << + "<property name=\"bar\" type=\"i\" access=\"read\"/>" + "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map; + + // add a third with annotations + prop.name = "baz"; + prop.type = "as"; + prop.access = QDBusIntrospection::Property::Write; + prop.annotations.insert("foo.annotation", "Hello, World"); + prop.annotations.insert("foo.annotation2", "Goodbye, World"); + map << prop; + QTest::newRow("complex") << + "<property name=\"bar\" type=\"i\" access=\"read\"/>" + "<property name=\"baz\" type=\"as\" access=\"write\">" + "<annotation name=\"foo.annotation\" value=\"Hello, World\" />" + "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />" + "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map; + + // and now change the order + QTest::newRow("complex2") << + "<property name=\"baz\" type=\"as\" access=\"write\">" + "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />" + "<annotation name=\"foo.annotation\" value=\"Hello, World\" />" + "<property name=\"bar\" type=\"i\" access=\"read\"/>" + "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map; +} + +void tst_QDBusXmlParser::properties() +{ + QString xmlHeader = "<node>" + "<interface name=\"iface.iface1\">", + xmlFooter = "</interface>" + "</node>"; + + QFETCH(QString, xmlDataFragment); + + QDBusIntrospection::Interface iface = + QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter); + + QCOMPARE(iface.name, QString("iface.iface1")); + + QFETCH(PropertyMap, propertyMap); + PropertyMap parsedMap = iface.properties; + + QCOMPARE(propertyMap.count(), parsedMap.count()); + QCOMPARE(propertyMap, parsedMap); +} + +QTEST_MAIN(tst_QDBusXmlParser) + +#include "tst_qdbusxmlparser.moc" |