diff options
Diffstat (limited to 'tests/auto/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp')
-rw-r--r-- | tests/auto/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp | 1894 |
1 files changed, 1894 insertions, 0 deletions
diff --git a/tests/auto/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp b/tests/auto/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp new file mode 100644 index 0000000000..92503509fb --- /dev/null +++ b/tests/auto/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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <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" |