diff options
| -rw-r--r-- | test/qt/Makefile.am | 23 | ||||
| -rw-r--r-- | test/qt/tst_hal.cpp | 78 | ||||
| -rw-r--r-- | test/qt/tst_qdbusconnection.cpp | 120 | ||||
| -rw-r--r-- | test/qt/tst_qdbusinterface.cpp | 325 | ||||
| -rw-r--r-- | test/qt/tst_qdbusobject.cpp | 198 | ||||
| -rw-r--r-- | test/qt/tst_qdbustype.cpp | 272 | ||||
| -rw-r--r-- | test/qt/tst_qdbusxmlparser.cpp | 709 | 
7 files changed, 1710 insertions, 15 deletions
diff --git a/test/qt/Makefile.am b/test/qt/Makefile.am index 4ada01d6..77f86d85 100644 --- a/test/qt/Makefile.am +++ b/test/qt/Makefile.am @@ -1,7 +1,7 @@  INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/qt $(DBUS_CLIENT_CFLAGS) $(DBUS_QT_CFLAGS) $(DBUS_QTESTLIB_CFLAGS) -DDBUS_COMPILATION  if DBUS_BUILD_TESTS -TEST_BINARIES=qdbusconnection +TEST_BINARIES=qdbusconnection qdbusobject qdbusinterface qdbustype qdbusxmlparser hal  TESTS=  else  TEST_BINARIES= @@ -11,16 +11,25 @@ endif  noinst_PROGRAMS= $(TEST_BINARIES) -qdbusconnection_SOURCES= \ -	tst_qdbusconnection.cpp +qdbusconnection_SOURCES= tst_qdbusconnection.cpp +qdbusobject_SOURCES= tst_qdbusobject.cpp +qdbusinterface_SOURCES= tst_qdbusinterface.cpp +qdbustype_SOURCES= tst_qdbustype.cpp +qdbusxmlparser_SOURCES= tst_qdbusxmlparser.cpp +hal_SOURCES = tst_hal.cpp -tst_qdbusconnection.cpp: tst_qdbusconnection.moc +tst_qdbusconnection.o: tst_qdbusconnection.moc +tst_qdbusobject.o: tst_qdbusobject.moc +tst_qdbusinterface.o: tst_qdbusinterface.moc +tst_qdbustype.o: tst_qdbustype.moc +tst_qdbusxmlparser.o: tst_qdbusxmlparser.moc +tst_hal.o: tst_hal.moc -tst_qdbusconnection.moc: -	$(QT_MOC) $(srcdir)/tst_qdbusconnection.cpp > tst_qdbusconnection.moc +%.moc: %.cpp +	$(QT_MOC) $< > $@  TEST_LIBS=$(DBUS_QTESTLIB_LIBS) $(top_builddir)/qt/libdbus-qt4-1.la -qdbusconnection_LDADD=$(TEST_LIBS) +LDADD=$(TEST_LIBS)  CLEANFILES=tst_qdbusconnection.moc diff --git a/test/qt/tst_hal.cpp b/test/qt/tst_hal.cpp new file mode 100644 index 00000000..36389c26 --- /dev/null +++ b/test/qt/tst_hal.cpp @@ -0,0 +1,78 @@ +#include <qcoreapplication.h> +#include <qdebug.h> + +#include <QtTest/QtTest> +#include <dbus/qdbus.h> + +class tst_Hal: public QObject +{ +    Q_OBJECT + +private slots: +    void getDevices(); +    void lock(); +}; + +class Spy: public QObject +{ +    Q_OBJECT +public: +    int count; +    QDBusConnection conn; + +    Spy(QDBusConnection c) : count(0), conn(c) +    { } + +public slots: +    void spySlot(int, const QVariantList&) +    { +        ++count; +        QDBusMessage msg = QDBusMessage::methodCall("org.freedesktop.Hal", +                        "/org/freedesktop/Hal/devices/acpi_CPU0", +                        "org.freedesktop.Hal.Device", "GetProperty"); +        msg << "info.locked"; + +        QDBusMessage reply = conn.sendWithReply(msg); +        QVERIFY(!reply.isEmpty()); +    } +}; + + +void tst_Hal::getDevices() +{ +    QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SystemBus); +    QVERIFY(con.isConnected()); + +    QDBusMessage msg = QDBusMessage::methodCall("org.freedesktop.Hal", +                "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager", +                "GetAllDevices"); + +    QDBusMessage reply = con.sendWithReply(msg); +    QVERIFY(!reply.isEmpty()); +    qDebug() << reply; +} + +void tst_Hal::lock() +{ +    QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SystemBus); +    QVERIFY(con.isConnected()); + +    Spy spy( con ); + +    con.connect("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/acpi_CPU0", +                "org.freedesktop.Hal.Device", "PropertyModified", +                &spy, SLOT(spySlot(int, QVariantList))); +    QDBusMessage msg = QDBusMessage::methodCall("org.freedesktop.Hal", +                "/org/freedesktop/Hal/devices/acpi_CPU0", "org.freedesktop.Hal.Device", +                "Lock"); +    msg << "No reason..."; + +    QDBusMessage reply = con.sendWithReply(msg); +    qDebug() << reply; +    QCOMPARE(spy.count, 3); +    QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); +} + +QTEST_MAIN(tst_Hal) + +#include "tst_hal.moc" diff --git a/test/qt/tst_qdbusconnection.cpp b/test/qt/tst_qdbusconnection.cpp index e14debdc..c3b15a2a 100644 --- a/test/qt/tst_qdbusconnection.cpp +++ b/test/qt/tst_qdbusconnection.cpp @@ -10,11 +10,19 @@ class tst_QDBusConnection: public QObject      Q_OBJECT  private slots: +    void init(); +    void cleanupTestCase();      void addConnection();      void connect();      void send();      void sendAsync();      void sendSignal(); +    void requestName_data(); +    void requestName(); +    void getNameOwner_data(); +    void getNameOwner(); +    void releaseName_data(); +    void releaseName();  };  class QDBusSpy: public QObject @@ -29,10 +37,25 @@ public:      int serial;  }; +void tst_QDBusConnection::init() +{ +    if (qstrcmp(QTest::currentTestFunction(), "addConnection") == 0) +        return; + +    QDBusConnection::addConnection(QDBusConnection::SessionBus); +    QVERIFY(QDBusConnection().isConnected()); +} + +void tst_QDBusConnection::cleanupTestCase() +{ +    QDBusConnection::closeConnection(); + +    QVERIFY(!QDBusConnection().isConnected()); +} +  void tst_QDBusConnection::sendSignal()  { -    QDBusConnection con = QDBusConnection::addConnection( -            QDBusConnection::SessionBus); +    QDBusConnection con;      QVERIFY(con.isConnected()); @@ -47,8 +70,7 @@ void tst_QDBusConnection::sendSignal()  void tst_QDBusConnection::send()  { -    QDBusConnection con = QDBusConnection::addConnection( -            QDBusConnection::SessionBus); +    QDBusConnection con;      QVERIFY(con.isConnected()); @@ -64,7 +86,7 @@ void tst_QDBusConnection::send()  void tst_QDBusConnection::sendAsync()  { -    QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +    QDBusConnection con;      QVERIFY(con.isConnected());      QDBusSpy spy; @@ -85,10 +107,9 @@ void tst_QDBusConnection::connect()  {      QDBusSpy spy; -    QDBusConnection con = QDBusConnection::addConnection( -            QDBusConnection::SessionBus); +    QDBusConnection con; -    con.connect("/org/kde/selftest", "org.kde.selftest", "ping", &spy, +    con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy,                   SLOT(handlePing(QString)));      QDBusMessage msg = QDBusMessage::signal("/org/kde/selftest", "org.kde.selftest", @@ -158,6 +179,89 @@ void tst_QDBusConnection::addConnection()      }  } +void tst_QDBusConnection::requestName_data() +{ +    QTest::addColumn<QString>("requestedName"); +    QTest::addColumn<int>("flags"); +    QTest::addColumn<bool>("expectedResult"); + +    QTest::newRow("null") << QString() << (int)QDBusConnection::NoReplace << false; +    QTest::newRow("empty") << QString("") << (int)QDBusConnection::NoReplace << false; +    QTest::newRow("invalid") << "./invalid name" << (int)QDBusConnection::NoReplace << false; +//    QTest::newRow("existing") << "org.freedesktop.DBus" +//                              << (int)QDBusConnection::NoReplace << false; + +    QTest::newRow("ok1") << "com.trolltech.QtDBUS.tst_qdbusconnection" +                         << (int)QDBusConnection::NoReplace << true; +} + +void tst_QDBusConnection::requestName() +{ +    QDBusConnection con; + +    QVERIFY(con.isConnected()); +     +    QFETCH(QString, requestedName); +    QFETCH(int, flags); +    QFETCH(bool, expectedResult); + +    bool result = con.requestName(requestedName, (QDBusConnection::NameRequestMode)flags); + +//    QEXPECT_FAIL("existing", "For whatever reason, the bus lets us replace this name", Abort); +    QCOMPARE(result, expectedResult); +} + +void tst_QDBusConnection::getNameOwner_data() +{ +    QTest::addColumn<QString>("name"); +    QTest::addColumn<QString>("expectedResult"); + +    QTest::newRow("null") << QString() << QString(); +    QTest::newRow("empty") << QString("") << QString(); + +    QTest::newRow("invalid") << ".invalid" << QString(); +    QTest::newRow("non-existent") << "com.trolltech.QtDBUS.foo" << QString(); + +    QTest::newRow("bus") << "org.freedesktop.DBus" << "org.freedesktop.DBus"; + +    QString base = QDBusConnection().baseService(); +    QTest::newRow("address") << base << base; +    QTest::newRow("self") << "com.trolltech.QtDBUS.tst_qdbusconnection" << base; +} + +void tst_QDBusConnection::getNameOwner() +{ +    QFETCH(QString, name); +    QFETCH(QString, expectedResult); + +    QDBusConnection con; +    QVERIFY(con.isConnected()); + +    QString result = con.getNameOwner(name); + +    QCOMPARE(result, expectedResult); +} + +void tst_QDBusConnection::releaseName_data() +{ +    requestName_data(); +} + +void tst_QDBusConnection::releaseName() +{ +    QDBusConnection con; + +    QVERIFY(con.isConnected()); +     +    QFETCH(QString, requestedName); +    //QFETCH(int, flags); +    QFETCH(bool, expectedResult); + +    bool result = con.releaseName(requestedName); + +    QCOMPARE(result, expectedResult); +}     +  QTEST_MAIN(tst_QDBusConnection)  #include "tst_qdbusconnection.moc" diff --git a/test/qt/tst_qdbusinterface.cpp b/test/qt/tst_qdbusinterface.cpp new file mode 100644 index 00000000..599b6c0d --- /dev/null +++ b/test/qt/tst_qdbusinterface.cpp @@ -0,0 +1,325 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + *    Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> + +#include <dbus/qdbus.h> +#include <QtCore/qvariant.h> + +Q_DECLARE_METATYPE(QVariantList) + +#define TEST_INTERFACE_NAME "com.trolltech.QtDBus.MyObject" +#define TEST_SERVICE_NAME "com.trolltech.QtDBus.tst_qdbusinterface" +#define TEST_SIGNAL_NAME "somethingHappened" + +const char introspectionData[] = +    "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" +    "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" +    "<node>" + +    "<interface name=\"org.freedesktop.DBus.Introspectable\">" +    "<method name=\"Introspect\">" +    "<arg name=\"data\" direction=\"out\" type=\"s\"/>" +    "</method>" +    "</interface>" + +    "<interface name=\"" TEST_INTERFACE_NAME "\">" +    "<method name=\"ping\">" +    "<arg name=\"ping\" direction=\"in\"  type=\"v\"/>" +    "<arg name=\"pong\" direction=\"out\" type=\"v\"/>" +    "</method>" +    "<method name=\"ping\">" +    "<arg name=\"ping1\" direction=\"in\"  type=\"v\"/>" +    "<arg name=\"ping2\" direction=\"in\"  type=\"v\"/>" +    "<arg name=\"pong1\" direction=\"out\" type=\"v\"/>" +    "<arg name=\"pong2\" direction=\"out\" type=\"v\"/>" +    "</method>" +    "<signal name=\"" TEST_SIGNAL_NAME "\">" +    "<arg type=\"s\"/>" +    "</signal>" +    "<property name=\"prop1\" access=\"readwrite\" type=\"i\" />" +    "</interface>" +    "<node name=\"subObject\"/>" +    "</node>"; + +class MyObject: public QObject +{ +    Q_OBJECT +public slots: + +    void ping(const QDBusMessage &msg) +    { +        QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +        QDBusMessage reply = QDBusMessage::methodReply(msg); +        reply << static_cast<QList<QVariant> >(msg); +        if (!con.send(reply)) +            exit(1); +    } + +    void Introspect(const QDBusMessage &msg) +    { +        QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +        QDBusMessage reply = QDBusMessage::methodReply(msg); +        reply << ::introspectionData; +        if (!con.send(reply)) +            exit(1); +    } +}; + +class Spy: public QObject +{ +    Q_OBJECT +public: +    QString received; +    int count; + +    Spy() : count(0) +    { } + +public slots: +    void spySlot(const QString& arg) +    { +        received = arg; +        ++count; +    } +}; + +// helper function +void emitSignal(const QString &interface, const QString &name, const QString &arg) +{ +    QDBusMessage msg = QDBusMessage::signal("/", interface, name); +    msg << arg; +    QDBusConnection().send(msg); + +    QTest::qWait(200); +} + +class tst_QDBusInterface: public QObject +{ +    Q_OBJECT +    MyObject obj; +private slots: +    void initTestCase(); +    void cleanupTestCase(); + +    void call_data(); +    void call(); + +    void introspect_data(); +    void introspect(); + +    void signal(); +}; + +void tst_QDBusInterface::initTestCase() +{ +    QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +    QVERIFY(con.isConnected()); +    QVERIFY(con.requestName( TEST_SERVICE_NAME )); + +    con.registerObject("/", "org.freedesktop.DBus.Introspectable", &obj); +    con.registerObject("/", TEST_INTERFACE_NAME, &obj); +} + +void tst_QDBusInterface::cleanupTestCase() +{ +    QDBusConnection::closeConnection(); +    QVERIFY(!QDBusConnection().isConnected()); +} + +void tst_QDBusInterface::call_data() +{ +    QTest::addColumn<QString>("method"); +    QTest::addColumn<QVariantList>("input"); +    QTest::addColumn<QVariantList>("output"); + +    QVariantList input; +    QTest::newRow("empty") << "ping" << input << input; + +    input << qVariantFromValue(1); +    QTest::newRow("int") << "ping" << input << input; +    QTest::newRow("int-int") << "ping.i" << input << input; +    QTest::newRow("int-int16") << "ping.n" << input << input; + +    // try doing some conversions +    QVariantList output; +    output << qVariantFromValue(1U); +    QTest::newRow("int-uint") << "ping.u" << input << output; +    QTest::newRow("int-uint16") << "ping.q" << input << output; + +    QTest::newRow("int-int64") << "ping.x" << input << (QVariantList() << qVariantFromValue(1LL)); +    QTest::newRow("int-uint64") << "ping.t" << input << (QVariantList() << qVariantFromValue(1ULL)); +    QTest::newRow("int-double") << "ping.d" << input << (QVariantList() << qVariantFromValue(1.0)); + +    output.clear(); +    output << QString("1"); +    QTest::newRow("int-string") << "ping.s" << input << output; + +    // try from string now +    input = output; +    QTest::newRow("string") << "ping" << input << output; +    QTest::newRow("string-string") << "ping.s" << input << output; + +    output.clear(); +    output << qVariantFromValue(1); +    QTest::newRow("string-int") << "ping.i" << input << input; +    QTest::newRow("string-int16") << "ping.n" << input << input; + +    output.clear(); +    output << qVariantFromValue(1U); +    QTest::newRow("string-uint") << "ping.u" << input << output; +    QTest::newRow("string-uint16") << "ping.q" << input << output; + +    QTest::newRow("string-int64") << "ping.x" << input << (QVariantList() << qVariantFromValue(1LL)); +    QTest::newRow("string-uint64") << "ping.t" << input << (QVariantList() << qVariantFromValue(1ULL)); +    QTest::newRow("string-double") << "ping.d" << input << (QVariantList() << qVariantFromValue(1.0)); + +    // two args (must be strings!) +    input.clear(); +    input << QString("Hello") << QString("World"); +    output = input; +    QTest::newRow("two-strings") << "ping" << input << output; +    QTest::newRow("two-strings") << "ping.ss" << input << output; + +    // this should drop one of the arguments +    output.removeLast(); +    QTest::newRow("last-dropped") << "ping.s" << input << output; +} + +void tst_QDBusInterface::call() +{ +    QDBusConnection con; +    QDBusInterface iface(con, con.baseService(), QLatin1String("/"), +                         TEST_INTERFACE_NAME); + +    QFETCH(QString, method); +    QFETCH(QVariantList, input); +    QFETCH(QVariantList, output); +     +    QDBusMessage reply; +    // try first callWithArgs: +    reply = iface.callWithArgs(method, input); + +    QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); +    if (!output.isEmpty()) { +        QCOMPARE(reply.count(), output.count()); +        QCOMPARE(static_cast<QVariantList>(reply), output); +    } + +    // try the template methods +    if (input.isEmpty()) +        reply = iface.call(method); +    else if (input.count() == 1) +        switch (input.at(0).type()) +        { +        case QVariant::Int: +            reply = iface.call(method, input.at(0).toInt()); +            break; + +        case QVariant::UInt: +            reply = iface.call(method, input.at(0).toUInt()); +            break; + +        case QVariant::String: +            reply = iface.call(method, input.at(0).toString()); +            break; + +        default: +            QFAIL("Unknown type. Please update the test case"); +            break; +        } +    else +        reply = iface.call(method, input.at(0).toString(), input.at(1).toString()); + +    QCOMPARE(reply.type(), QDBusMessage::ReplyMessage); +    if (!output.isEmpty()) { +        QCOMPARE(reply.count(), output.count()); +        QCOMPARE(static_cast<QVariantList>(reply), output); +    } +} + +void tst_QDBusInterface::introspect_data() +{ +    QTest::addColumn<QString>("service"); +    QTest::newRow("base") << QDBusConnection().baseService(); +    QTest::newRow("name") << TEST_SERVICE_NAME; +} + +void tst_QDBusInterface::introspect() +{ +    QFETCH(QString, service); +    QDBusConnection con; +    QDBusInterface iface(con, service, QLatin1String("/"), +                         TEST_INTERFACE_NAME); + +    QDBusIntrospection::Methods mm = iface.methodData(); +    QVERIFY(mm.count() == 2); + +    QDBusIntrospection::Signals sm = iface.signalData(); +    QVERIFY(sm.count() == 1); +    QVERIFY(sm.contains(TEST_SIGNAL_NAME)); + +    QDBusIntrospection::Properties pm = iface.propertyData(); +    QVERIFY(pm.count() == 1); +    QVERIFY(pm.contains("prop1")); +} + +void tst_QDBusInterface::signal() +{ +    QDBusConnection con; +    QDBusInterface iface(con, con.baseService(), QLatin1String("/"), +                         TEST_INTERFACE_NAME); + +    QString signalName = TEST_SIGNAL_NAME; + +    QString arg = "So long and thanks for all the fish"; +    { +        Spy spy; +        iface.connect(signalName, &spy, SLOT(spySlot(QString))); + +        emitSignal(TEST_INTERFACE_NAME, signalName, arg); +        QVERIFY(spy.count == 1); +        QCOMPARE(spy.received, arg); +    } + +    QDBusIntrospection::Signals sm = iface.signalData(); +    QVERIFY(sm.contains(signalName)); + +    const QDBusIntrospection::Signal& signal = sm.value(signalName); +    QCOMPARE(signal.name, signalName); +    QVERIFY(!signal.outputArgs.isEmpty()); +    { +        Spy spy; +        iface.connect(signal, &spy, SLOT(spySlot(QString))); + +        emitSignal(TEST_INTERFACE_NAME, signalName, arg); +        QVERIFY(spy.count == 1); +        QCOMPARE(spy.received, arg); +    } +} + +QTEST_MAIN(tst_QDBusInterface) + +#include "tst_qdbusinterface.moc" + diff --git a/test/qt/tst_qdbusobject.cpp b/test/qt/tst_qdbusobject.cpp new file mode 100644 index 00000000..24d5077e --- /dev/null +++ b/test/qt/tst_qdbusobject.cpp @@ -0,0 +1,198 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + *    Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> + +#include <dbus/qdbus.h> + +const char introspectionData[] = +    "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" +    "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" +    "<node>" + +    "<interface name=\"org.freedesktop.DBus.Introspectable\">" +    "<method name=\"Introspect\">" +    "<arg name=\"data\" direction=\"out\" type=\"s\"/>" +    "</method>" +    "</interface>" + +    "<interface name=\"com.trolltech.tst_qdbusobject.MyObject\">" +    "<method name=\"ping\">" +    "<arg name=\"ping\" direction=\"in\"  type=\"v\"/>" +    "<arg name=\"pong\" direction=\"out\" type=\"v\"/>" +    "</method>" +    "</interface>" +    "<node name=\"subObject\"/>" +    "</node>"; + +class MyObject: public QObject +{ +    Q_OBJECT +public slots: + +    void ping(const QDBusMessage &msg) +    { +        QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +        QDBusMessage reply = QDBusMessage::methodReply(msg); +        reply << static_cast<QList<QVariant> >(msg); +        if (!con.send(reply)) +            exit(1); +    } + +    void Introspect(const QDBusMessage &msg) +    { +        QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +        QDBusMessage reply = QDBusMessage::methodReply(msg); +        reply << ::introspectionData; +        if (!con.send(reply)) +            exit(1); +    } +}; + +class tst_QDBusObject: public QObject +{ +    Q_OBJECT +    MyObject obj; + +private slots: +    void initTestCase();        // connect to D-Bus +    void cleanupTestCase();     // disconnect from D-Bus + +    void construction_data(); +    void construction(); + +    void introspection_data(); +    void introspection(); +}; + +void tst_QDBusObject::initTestCase() +{ +    QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus); +    QVERIFY(con.isConnected()); +    QVERIFY(con.requestName("com.trolltech.tst_qdbusobject")); + +    con.registerObject("/", "org.freedesktop.DBus.Introspectable", &obj); +    con.registerObject("/", "com.trolltech.tst_qdbusobject.MyObject", &obj); +} + +void tst_QDBusObject::cleanupTestCase() +{ +    QDBusConnection::closeConnection(); +    QVERIFY(!QDBusConnection().isConnected()); +} + +void tst_QDBusObject::construction_data() +{ +    QTest::addColumn<QString>("service"); +    QTest::addColumn<QString>("path"); +    QTest::addColumn<bool>("isValid"); +    QTest::addColumn<bool>("exists"); + +    QTest::newRow("null") << QString() << QString() << false << false; + +    QTest::newRow("invalid1") << "foo.foo1" << "" << false << false; +    QTest::newRow("invalid2") << "foo.foo1" << "foo.bar" << false << false; +    QTest::newRow("invalid3") << "foo.foo1" << "/foo.bar" << false << false; +    QTest::newRow("invalid4") << "" << "/" << false << false; +    QTest::newRow("invalid5") << "foo" << "/" << false << false; +    QTest::newRow("invalid6") << ".foo" << "/" << false << false; + +    QTest::newRow("invalid7") << "org.freedesktop.DBus" << "" << false << false; +    QTest::newRow("invalid8") << "org.freedesktop.DBus" << "foo.bar" << false << false; +    QTest::newRow("invalid9") << "org.freedesktop.DBus" << "/foo.bar" << false << false; +     +    QTest::newRow("existing") << "org.freedesktop.DBus" << "/" << true << true; +    QTest::newRow("non-existing") << "org.freedesktop.DBus" << "/foo" << true << false; +} + +void tst_QDBusObject::construction() +{ +    QDBusConnection con;        // default + +    QFETCH(QString, service); +    QFETCH(QString, path); +    QFETCH(bool, isValid); +    //QFETCH(bool, exists); + +    QDBusObject o(con, service, path); +    QCOMPARE(o.isValid(), isValid); + +    if (isValid) { +        QCOMPARE(o.service(), service); +        QCOMPARE(o.path(), path); +    } +    else { +        QVERIFY(o.service().isNull()); +        QVERIFY(o.path().isNull()); +    } +    +    //QCOMPARE(o.exists(), exists); +} + +void tst_QDBusObject::introspection_data() +{ +    QTest::addColumn<QString>("service"); +    QTest::addColumn<QString>("path"); +    QTest::addColumn<QStringList>("interfaces"); + +    QStringList interfaces; +    QTest::newRow("nowhere") << QString() << QString() << interfaces; + +    // IMPORTANT! +    // Keep the interface list sorted! +    interfaces << "org.freedesktop.DBus" << DBUS_INTERFACE_INTROSPECTABLE; +    QTest::newRow("server") << "org.freedesktop.DBus" << "/" << interfaces; + +    QDBusConnection con; +    interfaces.clear(); +    interfaces << "com.trolltech.tst_qdbusobject.MyObject" << DBUS_INTERFACE_INTROSPECTABLE;     + +    QTest::newRow("self1") << con.baseService() << "/" << interfaces; +    QTest::newRow("self2") << "com.trolltech.tst_qdbusobject" << "/" << interfaces; +} + +void tst_QDBusObject::introspection() +{ +    QDBusConnection con; + +    QFETCH(QString, service); +    QFETCH(QString, path); + +    QDBusObject o(con, service, path); + +    if (!o.isValid()) +        QVERIFY(o.introspect().isEmpty()); +    else { +        QFETCH(QStringList, interfaces); +        QStringList parsed = o.interfaces(); +        parsed.sort(); +        QCOMPARE(parsed.count(), interfaces.count()); +        QCOMPARE(parsed, interfaces); +    } +} + +QTEST_MAIN(tst_QDBusObject) + +#include "tst_qdbusobject.moc" + diff --git a/test/qt/tst_qdbustype.cpp b/test/qt/tst_qdbustype.cpp new file mode 100644 index 00000000..124e60cb --- /dev/null +++ b/test/qt/tst_qdbustype.cpp @@ -0,0 +1,272 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + *    Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <qcoreapplication.h> +#include <QtTest/QtTest> + +#include <dbus/qdbus.h> + +class tst_QDBusType: public QObject +{ +    Q_OBJECT + +private slots: +    void fromType_data(); +    void fromType(); +    void fromSignature_data(); +    void fromSignature(); +    void arrayOf_data(); +    void arrayOf(); +    void mapOf_data(); +    void mapOf(); +}; + +inline QTestData &operator<<(QTestData &data, QVariant::Type t) +{ +    return data << int(t); +} + +void tst_QDBusType::fromType_data() +{ +    fromSignature_data(); +} + +void tst_QDBusType:: arrayOf_data() +{ +    fromSignature_data(); +} + +void tst_QDBusType::mapOf_data() +{ +    fromSignature_data(); +} + +void tst_QDBusType::fromSignature_data() +{ +    QTest::addColumn<QString>("signature"); +    QTest::addColumn<char>("type"); +    QTest::addColumn<int>("qvariantType"); +    QTest::addColumn<bool>("isValid"); +    QTest::addColumn<bool>("isBasic"); +    QTest::addColumn<bool>("isContainer"); +    QTest::addColumn<int>("subtypeCount"); + +    QTest::newRow("null") << QString() << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("empty") << QString("") << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("invalid") << QString("~") << '\0' << QVariant::Invalid << false << false << false << 0; + +    // integers: +    QTest::newRow("byte")    << "y" << 'y' << QVariant::UInt << true << true << false << 0; +    QTest::newRow("boolean") << "b" << 'b' << QVariant::Bool << true << true << false << 0; +    QTest::newRow("int16")   << "n" << 'n' << QVariant::Int << true << true << false << 0; +    QTest::newRow("uint16")  << "q" << 'q' << QVariant::UInt << true << true << false << 0; +    QTest::newRow("int32")   << "i" << 'i' << QVariant::Int << true << true << false << 0; +    QTest::newRow("uint32")  << "u" << 'u' << QVariant::UInt << true << true << false << 0; +    QTest::newRow("int64")   << "x" << 'x' << QVariant::LongLong << true << true << false << 0; +    QTest::newRow("uint64")  << "t" << 't' << QVariant::ULongLong << true << true << false << 0; + +    // double: +    QTest::newRow("double")  << "d" << 'd' << QVariant::Double << true << true << false << 0; + +    // string types: +    QTest::newRow("string")  << "s" << 's' << QVariant::String << true << true << false << 0; +    QTest::newRow("objpath") << "o" << 'o' << QVariant::String << true << true << false << 0; +    QTest::newRow("signature")<<"g" << 'g' << QVariant::String << true << true << false << 0; + +    // variant +    QTest::newRow("variant") << "v" << 'v' << QVariant::UserType << true << false << true << 0; + +    // compound types: +    QTest::newRow("struct-empty")       << "()" << '\0' << QVariant::Invalid << false << false << false  << 0; +    QTest::newRow("struct-invalid")     << "(~)" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("struct-unterminated")<< "(iii" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("struct-bad-nest")    << "(i(i)((i)i)" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("struct1")            << "(i)" << 'r' << QVariant::List << true << false << true  << 1; +    QTest::newRow("struct2")            << "(ii)" << 'r' << QVariant::List << true << false << true  << 2; + +    QTest::newRow("array-empty")        << "a" << '\0' << QVariant::Invalid << false << false << false  << 0; +    QTest::newRow("array-invalid")      << "a~" << '\0' << QVariant::Invalid << false << false << false  << 0; +    QTest::newRow("array-simple")       << "ab" << 'a' << QVariant::List << true << false << true  << 1; +    QTest::newRow("bytearray")          << "ay" << 'a' << QVariant::ByteArray << true << false << true << 1; +    QTest::newRow("stringlist")         << "as" << 'a' << QVariant::StringList << true << false << true << 1; +     +    QTest::newRow("map-empty")          << "e" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid1")       << "a{}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid2")       << "a{~}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid3")       << "a{e}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid4")       << "a{i}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid5")       << "a{(i)d}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid6")       << "{}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-invalid7")       << "{i}" << '\0' << QVariant::Invalid << false << false << false << 0; +    //QTest::newRow("map-invalid8")       << "{is}" << '\0' << QVariant::Invalid << false << false << false << 0; // this is valid when "a" is prepended +    QTest::newRow("map-bad-nesting")    << "a{i(s}" << '\0' << QVariant::Invalid << false << false << false << 0; +    QTest::newRow("map-ok1")            << "a{is}" << 'a' << QVariant::Map << true << false << true << 1; +    QTest::newRow("map-ok2")            << "a{sv}" << 'a' << QVariant::Map << true << false << true << 1; + +    // compound of compounds: +    QTest::newRow("struct-struct")      << "((i))" << 'r' << QVariant::List << true << false << true  << 1; +    QTest::newRow("struct-structs")     << "((ii)d(i))" << 'r' << QVariant::List << true << false << true  << 3; +    QTest::newRow("map-struct")         << "a{s(ii)}" << 'a' << QVariant::Map << true << false << true << 1; +    QTest::newRow("map-stringlist")     << "a{sas}" << 'a' << QVariant::Map << true << false << true << 1; +    QTest::newRow("map-map")            << "a{ia{sv}}" << 'a' << QVariant::Map << true << false << true << 1; +    QTest::newRow("array-struct")       << "a(ii)" << 'a' << QVariant::List << true << false << true << 1; +    QTest::newRow("array-array")        << "aai" << 'a' << QVariant::List << true << false << true << 1; +    QTest::newRow("array-map")          << "aa{sv}" << 'a' << QVariant::List << true << false << true << 1; +} + +void tst_QDBusType::fromType() +{ +    QFETCH(QString, signature); +    if (signature.length() != 1) +        // can't transform to typecode +        return; +     +    QFETCH(char, type); +    QFETCH(int, qvariantType); +    QFETCH(bool, isValid); +    QFETCH(bool, isBasic); +    QFETCH(bool, isContainer); + +    QDBusType t(signature.at(0).toLatin1()); + +    QCOMPARE((char)t.dbusType(), type); +    QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType)); +    QCOMPARE(t.isValid(), isValid); +    QCOMPARE(t.isBasic(), isBasic); +    QCOMPARE(t.isContainer(), isContainer); +} + +void tst_QDBusType::fromSignature() +{ +    QFETCH(QString, signature); +    QFETCH(char, type); +    QFETCH(int, qvariantType); +    QFETCH(bool, isValid); +    QFETCH(bool, isBasic); +    QFETCH(bool, isContainer); +    QFETCH(int, subtypeCount); + +    QDBusType t(signature); + +    QCOMPARE((char)t.dbusType(), type); +    QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType)); +    QCOMPARE(t.isValid(), isValid); +    QCOMPARE(t.isBasic(), isBasic); +    QCOMPARE(t.isContainer(), isContainer); + +    if (isValid) +        QCOMPARE(QLatin1String(t.dbusSignature()), signature); + +    QCOMPARE(t.subTypes().count(), subtypeCount); +} + +void tst_QDBusType::arrayOf() +{ +    QFETCH(QString, signature); +    QFETCH(char, type); +    QFETCH(int, qvariantType); +    QFETCH(bool, isValid); +    QFETCH(bool, isBasic); +    QFETCH(bool, isContainer); +    QFETCH(int, subtypeCount); + +    QDBusType arr("a" + signature.toLatin1()); +    QCOMPARE(arr.isValid(), isValid); +    QVERIFY(!arr.isBasic()); + +    if (isValid) { +        QVERIFY(arr.isContainer()); +        QVERIFY(arr.isArray()); +        QCOMPARE((char)arr.dbusType(), 'a'); +        QCOMPARE(arr.subTypes().count(), 1); + +        // handle special cases: +        if (type == 'y') +            QCOMPARE(arr.qvariantType(), QVariant::ByteArray); +        else if (type == 's' || type == 'o' || type == 'g') +            QCOMPARE(arr.qvariantType(), QVariant::StringList); +        else +            QCOMPARE(arr.qvariantType(), QVariant::List); + +        // handle the array element now: +        QDBusType t = arr.arrayElement(); + +        QCOMPARE((char)t.dbusType(), type); +        QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType)); +        QCOMPARE(t.isValid(), isValid); +        QCOMPARE(t.isBasic(), isBasic); +        QCOMPARE(t.isContainer(), isContainer); + +        QCOMPARE(QLatin1String(t.dbusSignature()), signature); + +        QCOMPARE(t.subTypes().count(), subtypeCount); +    } +} + +void tst_QDBusType::mapOf() +{ +    QFETCH(QString, signature); +    QFETCH(char, type); +    QFETCH(int, qvariantType); +    QFETCH(bool, isValid); +    QFETCH(bool, isBasic); +    QFETCH(bool, isContainer); +    QFETCH(int, subtypeCount); + +    QDBusType map("a{s" + signature.toLatin1() + '}'); +    QCOMPARE(map.isValid(), isValid); +    QVERIFY(!map.isBasic()); + +    if (isValid) { +        QVERIFY(map.isContainer()); +        QVERIFY(map.isArray()); +        QVERIFY(map.isMap()); +        QCOMPARE((char)map.dbusType(), 'a'); +        QCOMPARE(map.subTypes().count(), 1); + +        // handle the array element now: +        QDBusType dict_entry = map.arrayElement(); +        QVERIFY(dict_entry.isValid()); +        QVERIFY(dict_entry.isContainer()); +        QVERIFY(!dict_entry.isMap()); +        QVERIFY(!dict_entry.isArray()); + +        QVERIFY(map.mapKey().isBasic()); + +        // handle the value: +        QDBusType t = map.mapValue();         + +        QCOMPARE((char)t.dbusType(), type); +        QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType)); +        QCOMPARE(t.isValid(), isValid); +        QCOMPARE(t.isBasic(), isBasic); +        QCOMPARE(t.isContainer(), isContainer); + +        QCOMPARE(QLatin1String(t.dbusSignature()), signature); + +        QCOMPARE(t.subTypes().count(), subtypeCount); +    } +}     + +QTEST_MAIN(tst_QDBusType) + +#include "tst_qdbustype.moc" diff --git a/test/qt/tst_qdbusxmlparser.cpp b/test/qt/tst_qdbusxmlparser.cpp new file mode 100644 index 00000000..c7337c3a --- /dev/null +++ b/test/qt/tst_qdbusxmlparser.cpp @@ -0,0 +1,709 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + *    Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include <qcoreapplication.h> +#include <qmetatype.h> +#include <QtTest/QtTest> + +#include <dbus/qdbus.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(); +}; + +// 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 = QDBusType(type); +    retval.name = QLatin1String(name); +    return retval; +} + +template<typename T> +inline QMap<QString, T>& operator<<(QMap<QString, T>& map, const T& m) +{ map.insert(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 = m.name + "("; +    foreach (QDBusIntrospection::Argument arg, m.inputArgs) +        result += QString("in %1 %2, ") +        .arg(arg.type.toString(QDBusType::ConventionalNames)) +        .arg(arg.name); +    foreach (QDBusIntrospection::Argument arg, m.outputArgs) +        result += QString("out %1 %2, ") +        .arg(arg.type.toString(QDBusType::ConventionalNames)) +        .arg(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()); + +    if (result.length() > 1) +        result.truncate(result.length() - 2); +    result += ")"; +    return result; +}     + +QString printable(const QDBusIntrospection::Signal& s) +{ +    QString result = s.name + "("; +    foreach (QDBusIntrospection::Argument arg, s.outputArgs) +        result += QString("out %1 %2, ") +        .arg(arg.type.toString(QDBusType::ConventionalNames)) +        .arg(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()); + +    if (result.length() > 1) +        result.truncate(result.length() - 2); +    result += ")"; +    return result; +}     + +QString printable(const QDBusIntrospection::Property& p) +{ +    QString result; +    if (p.access == QDBusIntrospection::Property::Read) +        result = "read %1 %2, "; +    else if (p.access == QDBusIntrospection::Property::Write) +        result = "write %1 %2, "; +    else +        result = "readwrite %1 %2, "; +    result = result.arg(p.type.toString(QDBusType::ConventionalNames)).arg(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()); + +    if (result.length() > 1) +        result.truncate(result.length() - 2); +    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))); +} + +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); +    } +} + +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 +    QTest::newRow("two-in-one-invalid") << +        "<method name=\"Method\">" +        "<arg type=\"s\" direction=\"in\"/>" +        "<arg type=\"~\" name=\"invalid\" direction=\"in\"/>" // this line should be ignored +        "<arg type=\"v\" 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(methodMap.count(), parsedMap.count()); +    QCOMPARE(methodMap, parsedMap); +}              + +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 = QDBusType("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 = QDBusType("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 = QDBusType("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"  | 
