dbus/test/qt Makefile.am, 1.3, 1.4 tst_hal.cpp, NONE, 1.1 tst_qdbusconnection.cpp, 1.3, 1.4 tst_qdbusinterface.cpp, NONE, 1.1 tst_qdbusobject.cpp, NONE, 1.1 tst_qdbustype.cpp, NONE, 1.1 tst_qdbusxmlparser.cpp, NONE, 1.1

Thiago J. Macieira thiago at freedesktop.org
Wed Feb 15 09:06:43 PST 2006


Update of /cvs/dbus/dbus/test/qt
In directory gabe:/tmp/cvs-serv27712

Modified Files:
	Makefile.am tst_qdbusconnection.cpp 
Added Files:
	tst_hal.cpp tst_qdbusinterface.cpp tst_qdbusobject.cpp 
	tst_qdbustype.cpp tst_qdbusxmlparser.cpp 
Log Message:
Add new tests and update the existing one.


Index: Makefile.am
===================================================================
RCS file: /cvs/dbus/dbus/test/qt/Makefile.am,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- Makefile.am	1 Dec 2005 00:07:20 -0000	1.3
+++ Makefile.am	15 Feb 2006 17:06:41 -0000	1.4
@@ -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 @@
 
 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

--- NEW FILE: tst_hal.cpp ---
#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"

Index: tst_qdbusconnection.cpp
===================================================================
RCS file: /cvs/dbus/dbus/test/qt/tst_qdbusconnection.cpp,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- tst_qdbusconnection.cpp	22 Jan 2006 19:42:18 -0000	1.3
+++ tst_qdbusconnection.cpp	15 Feb 2006 17:06:41 -0000	1.4
@@ -10,11 +10,19 @@
     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 @@
     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::send()
 {
-    QDBusConnection con = QDBusConnection::addConnection(
-            QDBusConnection::SessionBus);
+    QDBusConnection con;
 
     QVERIFY(con.isConnected());
 
@@ -64,7 +86,7 @@
 
 void tst_QDBusConnection::sendAsync()
 {
-    QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+    QDBusConnection con;
     QVERIFY(con.isConnected());
 
     QDBusSpy spy;
@@ -85,10 +107,9 @@
 {
     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::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"

--- NEW FILE: tst_qdbusinterface.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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"


--- NEW FILE: tst_qdbusobject.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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"


--- NEW FILE: tst_qdbustype.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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"

--- NEW FILE: tst_qdbusxmlparser.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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"



More information about the dbus-commit mailing list