* test/qt/*: Update the testcases, including testing the new
authorThiago Macieira <thiago@kde.org>
Sun, 11 Jun 2006 12:18:23 +0000 (12:18 +0000)
committerThiago Macieira <thiago@kde.org>
Sun, 11 Jun 2006 12:18:23 +0000 (12:18 +0000)
        functionality of sending null QByteArray and QString over the
        bus. Add new headertest test and restore the old
        qdbusxmlparser test.

ChangeLog
test/qt/Makefile.am
test/qt/common.h
test/qt/qpong.cpp
test/qt/tst_hal.cpp
test/qt/tst_headertest.cpp [new file with mode: 0644]
test/qt/tst_qdbusabstractadaptor.cpp
test/qt/tst_qdbusconnection.cpp
test/qt/tst_qdbusinterface.cpp
test/qt/tst_qdbusmarshall.cpp [moved from test/qt/ping.cpp with 85% similarity]
test/qt/tst_qdbusxmlparser.cpp [new file with mode: 0644]

index 2f1f1a6..8f48ca5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2006-06-11  Thiago Macieira <thiago.macieira@trolltech.com>
 
+       * test/qt/*: Update the testcases, including testing the new
+        functionality of sending null QByteArray and QString over the
+        bus. Add new headertest test and restore the old
+        qdbusxmlparser test.
+
+2006-06-11  Thiago Macieira <thiago.macieira@trolltech.com>
+
        * qt/tools/dbuscpp2xml.cpp: Compile on Windows.
        * qt/tools/dbusidl2cpp.cpp: Add missing newline.
 
index f7a8efa..8ebd332 100644 (file)
@@ -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 qpong ping qdbusinterface qdbusabstractadaptor hal
+TEST_BINARIES = tst_headertest tst_qdbusxmlparser tst_qdbusconnection qpong tst_qdbusmarshall tst_qdbusinterface tst_qdbusabstractadaptor tst_hal
 TESTS=
 else
 TEST_BINARIES=
@@ -11,14 +11,17 @@ endif
 noinst_PROGRAMS= $(TEST_BINARIES)
 
 qpong_SOURCES= qpong.cpp
-ping_SOURCES= ping.cpp
-qdbusconnection_SOURCES= tst_qdbusconnection.cpp
-qdbusinterface_SOURCES= tst_qdbusinterface.cpp
-qdbusabstractadaptor_SOURCES= tst_qdbusabstractadaptor.cpp common.h
-hal_SOURCES = tst_hal.cpp
+tst_headertest_SOURCES = tst_headertest.cpp
+tst_qdbusconnection_SOURCES = tst_qdbusconnection.cpp
+tst_qdbusxmlparser_SOURCES = tst_qdbusxmlparser.cpp
+tst_qdbusmarshall_SOURCES = tst_qdbusmarshall.cpp
+tst_qdbusinterface_SOURCES = tst_qdbusinterface.cpp
+tst_qdbusabstractadaptor_SOURCES = tst_qdbusabstractadaptor.cpp common.h
+tst_hal_SOURCES = tst_hal.cpp
 
 qpong.o: qpong.moc
-ping.o: ping.moc
+tst_qdbusxmlparser.o: tst_qdbusxmlparser.moc
+tst_qdbusmarshall.o: tst_qdbusmarshall.moc
 tst_qdbusconnection.o: tst_qdbusconnection.moc
 tst_qdbusinterface.o: tst_qdbusinterface.moc
 tst_qdbusabstractadaptor.o: tst_qdbusabstractadaptor.moc
@@ -27,7 +30,7 @@ tst_hal.o: tst_hal.moc
 %.moc: %.cpp
        $(QT_MOC) $< > $@
 
-TEST_LIBS=$(DBUS_QTESTLIB_LIBS) $(top_builddir)/qt/libdbus-qt4-1.la
+TEST_LIBS=$(DBUS_QTESTLIB_LIBS) $(top_builddir)/qt/src/libdbus-qt4-1.la
 
 LDADD=$(TEST_LIBS)
 
index 943d3d9..58beae4 100644 (file)
@@ -9,8 +9,8 @@ Q_DECLARE_METATYPE(QList<uint>)
 Q_DECLARE_METATYPE(QList<qlonglong>)
 Q_DECLARE_METATYPE(QList<qulonglong>)
 Q_DECLARE_METATYPE(QList<double>)
-#if 0
-#include "../qdbusintrospection_p.h"
+#ifdef USE_PRIVATE_CODE
+#include "../../qt/src/qdbusintrospection_p.h"
 
 // just to make it easier:
 typedef QDBusIntrospection::Interfaces InterfaceMap;
@@ -157,6 +157,20 @@ bool compare(const QList<double> &l1, const QList<double> &l2)
     return true;
 }
 
+bool compare(const QString &s1, const QString &s2)
+{
+    if (s1.isEmpty() && s2.isEmpty())
+        return true;            // regardless of whether one of them is null
+    return s1 == s2;
+}
+
+bool compare(const QByteArray &ba1, const QByteArray &ba2)
+{
+    if (ba1.isEmpty() && ba2.isEmpty())
+        return true;            // regardless of whether one of them is null
+    return ba1 == ba2;
+}
+
 bool compare(const QVariant &v1, const QVariant &v2)
 {
     if (v1.userType() != v2.userType())
@@ -169,6 +183,12 @@ bool compare(const QVariant &v1, const QVariant &v2)
     else if (id == QVariant::Map)
         return compare(v1.toMap(), v2.toMap());
 
+    else if (id == QVariant::String)
+        return compare(v1.toString(), v2.toString());
+
+    else if (id == QVariant::ByteArray)
+        return compare(v1.toByteArray(), v2.toByteArray());
+
     else if (id < int(QVariant::UserType)) // yes, v1.type()
         // QVariant can compare
         return v1 == v2;
index 4a3e976..cad04eb 100644 (file)
@@ -1,6 +1,5 @@
 #include <QtCore/QtCore>
 #include <dbus/qdbus.h>
-#include <dbus/dbus.h>
 
 class Pong: public QObject
 {
@@ -22,13 +21,10 @@ int main(int argc, char *argv[])
     QCoreApplication app(argc, argv);
 
     QDBusConnection &con = QDBus::sessionBus();
-    QDBusMessage msg = QDBusMessage::methodCall(DBUS_SERVICE_DBUS,
-                                                DBUS_PATH_DBUS,
-                                                DBUS_INTERFACE_DBUS,
-                                                "RequestName");
-    msg << "org.kde.selftest" << 0U;
-    msg = con.sendWithReply(msg);
-    if (msg.type() != QDBusMessage::ReplyMessage)
+    if (!con.isConnected())
+        exit(1);
+
+    if (con.busService()->requestName("org.kde.selftest", QDBusBusService::DoNotQueueName).isError())
         exit(2);
 
     Pong pong;
index f98eb98..a69daf2 100644 (file)
@@ -2,7 +2,6 @@
 #include <qdebug.h>
 
 #include <QtTest/QtTest>
-#define DBUS_API_SUBJECT_TO_CHANGE
 #include <dbus/qdbus.h>
 
 class tst_Hal: public QObject
diff --git a/test/qt/tst_headertest.cpp b/test/qt/tst_headertest.cpp
new file mode 100644 (file)
index 0000000..eb90c55
--- /dev/null
@@ -0,0 +1,16 @@
+#define QT_NO_KEYWORDS
+#define signals Choke!
+#define slots Choke!
+#define emit Choke!
+#define foreach Choke!
+#define forever Choke!
+
+#define QT_NO_CAST_FROM_ASCII
+#define QT_NO_CAST_TO_ASCII
+
+#include <dbus/qdbus.h>
+
+int main(int, char **)
+{
+    return 0;
+}
index ec3f047..d47c543 100644 (file)
@@ -7,6 +7,10 @@
 
 #include "common.h"
 
+#ifdef Q_CC_MSVC
+#define __PRETTY_FUNCTION__ __FUNCDNAME__
+#endif
+
 const char *slotSpy;
 QString valueSpy;
 
@@ -79,7 +83,7 @@ public:
 class Interface1: public QDBusAbstractAdaptor
 {
     Q_OBJECT
-    Q_CLASSINFO("D-Bus Interface", "local.Interface1");
+    Q_CLASSINFO("D-Bus Interface", "local.Interface1")
 public:
     Interface1(QObject *parent) : QDBusAbstractAdaptor(parent)
     { }
@@ -88,9 +92,9 @@ public:
 class Interface2: public QDBusAbstractAdaptor
 {
     Q_OBJECT
-    Q_CLASSINFO("D-Bus Interface", "local.Interface2");
-    Q_PROPERTY(QString prop1 READ prop1);
-    Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2);
+    Q_CLASSINFO("D-Bus Interface", "local.Interface2")
+    Q_PROPERTY(QString prop1 READ prop1)
+    Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2)
 public:
     Interface2(QObject *parent) : QDBusAbstractAdaptor(parent)
     { setAutoRelaySignals(true); }
@@ -117,9 +121,9 @@ signals:
 class Interface3: public QDBusAbstractAdaptor
 {
     Q_OBJECT
-    Q_CLASSINFO("D-Bus Interface", "local.Interface3");
-    Q_PROPERTY(QString prop1 READ prop1);
-    Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2);
+    Q_CLASSINFO("D-Bus Interface", "local.Interface3")
+    Q_PROPERTY(QString prop1 READ prop1)
+    Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2)
 public:
     Interface3(QObject *parent) : QDBusAbstractAdaptor(parent)
     { setAutoRelaySignals(true); }
@@ -157,9 +161,9 @@ signals:
 class Interface4: public QDBusAbstractAdaptor
 {
     Q_OBJECT
-    Q_CLASSINFO("D-Bus Interface", "local.Interface4");
-    Q_PROPERTY(QString prop1 READ prop1);
-    Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2);
+    Q_CLASSINFO("D-Bus Interface", "local.Interface4")
+    Q_PROPERTY(QString prop1 READ prop1)
+    Q_PROPERTY(QString prop2 READ prop2 WRITE setProp2)
 public:
     Interface4(QObject *parent) : QDBusAbstractAdaptor(parent)
     { setAutoRelaySignals(true); }
@@ -452,51 +456,51 @@ void tst_QDBusAbstractAdaptor::methodCalls()
 
     // must fail: no object
     //QCOMPARE(empty->call("method").type(), QDBusMessage::ErrorMessage);
-    QCOMPARE(if1->call("method").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if1->call(QDBusInterface::UseEventLoop, "method").type(), QDBusMessage::ErrorMessage);
 
     QFETCH(int, nInterfaces);
     MyObject obj(nInterfaces);
     con.registerObject("/", &obj);
 
     // must fail: no such method
-    QCOMPARE(if1->call("method").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if1->call(QDBusInterface::UseEventLoop, "method").type(), QDBusMessage::ErrorMessage);
     if (!nInterfaces--)
         return;
     if (!nInterfaces--)
         return;
 
     // simple call: one such method exists
-    QCOMPARE(if2->call("method").type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if2->call(QDBusInterface::UseEventLoop, "method").type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface2::method()");
     if (!nInterfaces--)
         return;
 
     // multiple methods in multiple interfaces, no name overlap
-    QCOMPARE(if1->call("methodVoid").type(), QDBusMessage::ErrorMessage);
-    QCOMPARE(if1->call("methodInt").type(), QDBusMessage::ErrorMessage);
-    QCOMPARE(if1->call("methodString").type(), QDBusMessage::ErrorMessage);
-    QCOMPARE(if2->call("methodVoid").type(), QDBusMessage::ErrorMessage);
-    QCOMPARE(if2->call("methodInt").type(), QDBusMessage::ErrorMessage);
-    QCOMPARE(if2->call("methodString").type(), QDBusMessage::ErrorMessage);
-
-    QCOMPARE(if3->call("methodVoid").type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if1->call(QDBusInterface::UseEventLoop, "methodVoid").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if1->call(QDBusInterface::UseEventLoop, "methodInt").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if1->call(QDBusInterface::UseEventLoop, "methodString").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if2->call(QDBusInterface::UseEventLoop, "methodVoid").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if2->call(QDBusInterface::UseEventLoop, "methodInt").type(), QDBusMessage::ErrorMessage);
+    QCOMPARE(if2->call(QDBusInterface::UseEventLoop, "methodString").type(), QDBusMessage::ErrorMessage);
+
+    QCOMPARE(if3->call(QDBusInterface::UseEventLoop, "methodVoid").type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface3::methodVoid()");
-    QCOMPARE(if3->call("methodInt", 42).type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if3->call(QDBusInterface::UseEventLoop, "methodInt", 42).type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface3::methodInt(int)");
-    QCOMPARE(if3->call("methodString", QString("")).type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if3->call(QDBusInterface::UseEventLoop, "methodString", QString("")).type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface3::methodString(QString)");
 
     if (!nInterfaces--)
         return;
 
     // method overloading: different interfaces
-    QCOMPARE(if4->call("method").type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if4->call(QDBusInterface::UseEventLoop, "method").type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface4::method()");
 
     // method overloading: different parameters
-    QCOMPARE(if4->call("method.i", 42).type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if4->call(QDBusInterface::UseEventLoop, "method.i", 42).type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface4::method(int)");
-    QCOMPARE(if4->call("method.s", QString()).type(), QDBusMessage::ReplyMessage);
+    QCOMPARE(if4->call(QDBusInterface::UseEventLoop, "method.s", QString()).type(), QDBusMessage::ReplyMessage);
     QCOMPARE(slotSpy, "void Interface4::method(QString)");
     
 }
@@ -679,18 +683,19 @@ void tst_QDBusAbstractAdaptor::readProperties()
     MyObject obj;
     con.registerObject("/", &obj);
 
+    QDBusInterfacePtr properties(con, con.baseService(), "/", "org.freedesktop.DBus.Properties");
     for (int i = 2; i <= 4; ++i) {
         QString name = QString("Interface%1").arg(i);
-        QDBusInterface *iface = con.findInterface(con.baseService(), "/", "local." + name);
 
         for (int j = 1; j <= 2; ++j) {
             QString propname = QString("prop%1").arg(j);
-            QVariant value = iface->property(propname.toLatin1());
+            QDBusReply<QVariant> reply =
+                properties->call(QDBusInterface::UseEventLoop, "Get", "local." + name, propname);
+            QVariant value = reply;
 
             QCOMPARE(value.userType(), int(QVariant::String));
             QCOMPARE(value.toString(), QString("QString %1::%2() const").arg(name, propname));
         }
-        iface->deleteLater();
     }
 }
 
@@ -702,21 +707,21 @@ void tst_QDBusAbstractAdaptor::writeProperties()
     MyObject obj;
     con.registerObject("/", &obj);
 
+    QDBusInterfacePtr properties(con, con.baseService(), "/", "org.freedesktop.DBus.Properties");
     for (int i = 2; i <= 4; ++i) {
         QString name = QString("Interface%1").arg(i);
-        QDBusInterface *iface = con.findInterface(con.baseService(), "/", "local." + name);
 
         QVariant value(name);
 
         valueSpy.clear();
-        iface->setProperty("prop1", value);
+        properties->call(QDBusInterface::UseEventLoop, "Set", "local." + name, QString("prop1"),
+                         value);
         QVERIFY(valueSpy.isEmpty()); // call mustn't have succeeded
 
-        iface->setProperty("prop2", value);
+        properties->call(QDBusInterface::UseEventLoop, "Set", "local." + name, QString("prop2"),
+                         value);
         QCOMPARE(valueSpy, name);
         QCOMPARE(QString(slotSpy), QString("void %1::setProp2(const QString&)").arg(name));
-
-        iface->deleteLater();
     }
 }
 
@@ -964,10 +969,11 @@ void tst_QDBusAbstractAdaptor::typeMatching()
     QDBusMessage reply;
     QDBusInterface *iface = con.findInterface(con.baseService(), "/types", "local.TypesInterface");
 
-    reply = iface->callWithArgs("method" + basename + '.' + signature, QVariantList() << value);
+    reply = iface->callWithArgs("method" + basename + '.' + signature, QVariantList() << value,
+                                QDBusInterface::UseEventLoop);
     QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
 
-    reply = iface->call("retrieve" + basename);
+    reply = iface->call(QDBusInterface::UseEventLoop, "retrieve" + basename);
     QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
     QCOMPARE(reply.count(), 1);
 
index 224a02c..a887cd9 100644 (file)
@@ -246,7 +246,7 @@ void tst_QDBusConnection::registerObject()
 bool tst_QDBusConnection::callMethod(const QDBusConnection &conn, const QString &path)
 {
     QDBusMessage msg = QDBusMessage::methodCall(conn.baseService(), path, "local.any", "method");
-    QDBusMessage reply = conn.sendWithReply(msg);
+    QDBusMessage reply = conn.sendWithReply(msg, QDBusConnection::UseEventLoop);
 
     return reply.type() == QDBusMessage::ReplyMessage;
 }    
index 46dde97..a63b8e0 100644 (file)
@@ -34,63 +34,33 @@ Q_DECLARE_METATYPE(QVariantList)
 #define TEST_INTERFACE_NAME "com.trolltech.QtDBus.MyObject"
 #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 IntrospectionAdaptor: public QDBusAbstractAdaptor
-{
-    Q_OBJECT
-    Q_CLASSINFO("D-Bus Interface", "org.freedesktop.DBus.Introspectable")
-public:
-    IntrospectionAdaptor(QObject *parent)
-        : QDBusAbstractAdaptor(parent)
-    { }
-        
-public slots:
-
-    void Introspect(const QDBusMessage &msg)
-    {
-        QDBusMessage reply = QDBusMessage::methodReply(msg);
-        reply << ::introspectionData;
-        if (!msg.connection().send(reply))
-            exit(1);
-    }
-};    
-
 class MyObject: public QObject
 {
     Q_OBJECT
+    Q_CLASSINFO("D-Bus Interface", "com.trolltech.QtDBus.MyObject")
+    Q_CLASSINFO("D-Bus Introspection", ""
+"  <interface name=\"com.trolltech.QtDBus.MyObject\" >\n"
+"    <property access=\"readwrite\" type=\"i\" name=\"prop1\" />\n"
+"    <signal name=\"somethingHappened\" >\n"
+"      <arg direction=\"out\" type=\"s\" />\n"
+"    </signal>\n"
+"    <method name=\"ping\" >\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"ping\" />\n"
+"    </method>\n"
+"    <method name=\"ping\" >\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping1\" />\n"
+"      <arg direction=\"in\" type=\"v\" name=\"ping2\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"pong1\" />\n"
+"      <arg direction=\"out\" type=\"v\" name=\"pong2\" />\n"
+"    </method>\n"
+"  </interface>\n"
+        "")
 public:
     MyObject()
     {
-        new IntrospectionAdaptor(this);
+        QObject *subObject = new QObject(this);
+        subObject->setObjectName("subObject");
     }
 
 public slots:
@@ -152,7 +122,8 @@ void tst_QDBusInterface::initTestCase()
     QDBusConnection &con = QDBus::sessionBus();
     QVERIFY(con.isConnected());
 
-    con.registerObject("/", &obj, QDBusConnection::ExportAdaptors | QDBusConnection::ExportSlots);
+    con.registerObject("/", &obj, QDBusConnection::ExportAdaptors | QDBusConnection::ExportSlots |
+                       QDBusConnection::ExportChildObjects);
 }
 
 void tst_QDBusInterface::call_data()
@@ -241,7 +212,7 @@ void tst_QDBusInterface::call()
     
     QDBusMessage reply;
     // try first callWithArgs:
-    reply = iface->callWithArgs(method, input);
+    reply = iface->callWithArgs(method, input, QDBusInterface::UseEventLoop);
 
     QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
     if (!output.isEmpty()) {
@@ -251,20 +222,20 @@ void tst_QDBusInterface::call()
 
     // try the template methods
     if (input.isEmpty())
-        reply = iface->call(method);
+        reply = iface->call(QDBusInterface::UseEventLoop, method);
     else if (input.count() == 1)
         switch (input.at(0).type())
         {
         case QVariant::Int:
-            reply = iface->call(method, input.at(0).toInt());
+            reply = iface->call(QDBusInterface::UseEventLoop, method, input.at(0).toInt());
             break;
 
         case QVariant::UInt:
-            reply = iface->call(method, input.at(0).toUInt());
+            reply = iface->call(QDBusInterface::UseEventLoop, method, input.at(0).toUInt());
             break;
 
         case QVariant::String:
-            reply = iface->call(method, input.at(0).toString());
+            reply = iface->call(QDBusInterface::UseEventLoop, method, input.at(0).toString());
             break;
 
         default:
@@ -272,7 +243,7 @@ void tst_QDBusInterface::call()
             break;
         }
     else
-        reply = iface->call(method, input.at(0).toString(), input.at(1).toString());
+        reply = iface->call(QDBusInterface::UseEventLoop, method, input.at(0).toString(), input.at(1).toString());
 
     QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
     if (!output.isEmpty()) {
similarity index 85%
rename from test/qt/ping.cpp
rename to test/qt/tst_qdbusmarshall.cpp
index 1cdbfca..306f7b6 100644 (file)
@@ -3,8 +3,9 @@
 #include <dbus/qdbus.h>
 
 #include "common.h"
+#include <limits>
 
-class Ping: public QObject
+class tst_QDBusMarshall: public QObject
 {
     Q_OBJECT
 
@@ -35,19 +36,20 @@ private:
     QProcess proc;
 };
 
-void Ping::initTestCase()
+void tst_QDBusMarshall::initTestCase()
 {
     proc.start("./qpong");
     QVERIFY(proc.waitForStarted());
     QTest::qWait(2000);
 }
 
-void Ping::cleanupTestCase()
+void tst_QDBusMarshall::cleanupTestCase()
 {
     proc.close();
+    proc.kill();
 }
 
-void Ping::sendBasic_data()
+void tst_QDBusMarshall::sendBasic_data()
 {
     QTest::addColumn<QVariant>("value");
     QTest::addColumn<QString>("sig");
@@ -65,9 +67,10 @@ void Ping::sendBasic_data()
     QTest::newRow("double") << QVariant(42.5) << "d";
     QTest::newRow("string") << QVariant("ping") << "s";
     QTest::newRow("emptystring") << QVariant("") << "s";
+    QTest::newRow("nullstring") << QVariant(QString()) << "s";
 }
 
-void Ping::sendVariant_data()
+void tst_QDBusMarshall::sendVariant_data()
 {
     sendBasic_data();
 
@@ -80,7 +83,7 @@ void Ping::sendVariant_data()
     QTest::newRow("variant-variant") << nested2 << "v";
 }
 
-void Ping::sendArrays_data()
+void tst_QDBusMarshall::sendArrays_data()
 {
     QTest::addColumn<QVariant>("value");
     QTest::addColumn<QString>("sig");
@@ -91,10 +94,24 @@ void Ping::sendArrays_data()
     strings << "hello" << "world";
     QTest::newRow("stringlist") << QVariant(strings) << "as";
 
-    QByteArray bytearray("");   // empty, not null
+    strings.clear();
+    strings << "" << "" << "";
+    QTest::newRow("list-of-emptystrings") << QVariant(strings) << "as";
+
+    strings.clear();
+    strings << QString() << QString() << QString() << QString();
+    QTest::newRow("list-of-nullstrings") << QVariant(strings) << "as";
+
+    QByteArray bytearray;
+    QTest::newRow("nullbytearray") << QVariant(bytearray) << "ay";
+    bytearray = "";             // empty, not null
     QTest::newRow("emptybytearray") << QVariant(bytearray) << "ay";
     bytearray = "foo";
     QTest::newRow("bytearray") << QVariant(bytearray) << "ay";
+    bytearray.clear();
+    for (int i = 0; i < 4096; ++i)
+        bytearray += QByteArray(1024, char(i));
+    QTest::newRow("hugebytearray") << QVariant(bytearray) << "ay";
 
     QList<bool> bools; 
     QTest::newRow("emptyboollist") << qVariantFromValue(bools) << "ab";
@@ -152,12 +169,12 @@ void Ping::sendArrays_data()
     QTest::newRow("variantlist") << QVariant(variants) << "av";
 }
 
-void Ping::sendArrayOfArrays_data()
+void tst_QDBusMarshall::sendArrayOfArrays_data()
 {
     sendArrays_data();
 }
 
-void Ping::sendStringMap_data()
+void tst_QDBusMarshall::sendStringMap_data()
 {
     sendBasic_data();
 
@@ -172,12 +189,12 @@ void Ping::sendStringMap_data()
     sendArrays_data();
 }
 
-void Ping::sendStringMapOfMap_data()
+void tst_QDBusMarshall::sendStringMapOfMap_data()
 {
     sendStringMap_data();
 }
 
-void Ping::sendBasic()
+void tst_QDBusMarshall::sendBasic()
 {
     QFETCH(QVariant, value);
 
@@ -198,7 +215,7 @@ void Ping::sendBasic()
         QVERIFY(compare(reply.at(i), msg.at(i)));
 }
 
-void Ping::sendVariant()
+void tst_QDBusMarshall::sendVariant()
 {
     QFETCH(QVariant, value);
     QVariant tmp = value;
@@ -221,7 +238,7 @@ void Ping::sendVariant()
         QVERIFY(compare(reply.at(i), msg.at(i)));
 }
 
-void Ping::sendArrays()
+void tst_QDBusMarshall::sendArrays()
 {
     QFETCH(QVariant, value);
 
@@ -242,7 +259,7 @@ void Ping::sendArrays()
         QVERIFY(compare(reply.at(i), msg.at(i)));
 }
 
-void Ping::sendArrayOfArrays()
+void tst_QDBusMarshall::sendArrayOfArrays()
 {
     QFETCH(QVariant, value);
 
@@ -264,7 +281,7 @@ void Ping::sendArrayOfArrays()
         QVERIFY(compare(reply.at(i), msg.at(i)));
 }
 
-void Ping::sendStringMap()
+void tst_QDBusMarshall::sendStringMap()
 {
     QFETCH(QVariant, value);
 
@@ -290,7 +307,7 @@ void Ping::sendStringMap()
         QVERIFY(compare(reply.at(i), msg.at(i)));
 }
 
-void Ping::sendStringMapOfMap()
+void tst_QDBusMarshall::sendStringMapOfMap()
 {
     QFETCH(QVariant, value);
 
@@ -316,11 +333,10 @@ void Ping::sendStringMapOfMap()
     QFETCH(QString, sig);
     QCOMPARE(reply.signature(), "a{sa{s" + sig + "}}");
 
-    QEXPECT_FAIL("", "libdbus returns an empty set for un unknown reason", Abort);
     for (int i = 0; i < reply.count(); ++i)
         QVERIFY(compare(reply.at(i), msg.at(i)));
 }
 
 
-QTEST_MAIN(Ping)
-#include "ping.moc"
+QTEST_MAIN(tst_QDBusMarshall)
+#include "tst_qdbusmarshall.moc"
diff --git a/test/qt/tst_qdbusxmlparser.cpp b/test/qt/tst_qdbusxmlparser.cpp
new file mode 100644 (file)
index 0000000..bf1ddec
--- /dev/null
@@ -0,0 +1,578 @@
+/* -*- 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>
+
+#define USE_PRIVATE_CODE
+#include "common.h"
+
+class tst_QDBusXmlParser: public QObject
+{
+    Q_OBJECT
+
+private:
+    void parsing_common(const QString&);
+
+private slots:
+    void parsing_data();
+    void parsing();
+    void parsingWithDoctype_data();
+    void parsingWithDoctype();
+
+    void objectWithContent_data();
+    void objectWithContent();
+
+    void methods_data();
+    void methods();
+    void signals__data();
+    void signals_();
+    void properties_data();
+    void properties();
+};
+
+void tst_QDBusXmlParser::parsing_data()
+{
+    QTest::addColumn<QString>("xmlData");
+    QTest::addColumn<int>("interfaceCount");
+    QTest::addColumn<int>("objectCount");
+
+    QTest::newRow("null") << QString() << 0 << 0;
+    QTest::newRow("empty") << QString("") << 0 << 0;
+    
+    QTest::newRow("junk") << "<junk/>" << 0 << 0;
+    QTest::newRow("interface-inside-junk") << "<junk><interface name=\"iface.iface1\" /></junk>"
+                                           << 0 << 0;
+    QTest::newRow("object-inside-junk") << "<junk><node name=\"obj1\" /></junk>"
+                                        << 0 << 0;
+
+    QTest::newRow("zero-interfaces") << "<node/>" << 0 << 0;
+    QTest::newRow("one-interface") << "<node><interface name=\"iface.iface1\" /></node>" << 1 << 0;
+
+    
+    QTest::newRow("two-interfaces") << "<node><interface name=\"iface.iface1\" />"
+                                       "<interface name=\"iface.iface2\"></node>"
+                                    << 2 << 0;        
+
+
+    QTest::newRow("one-object") << "<node><node name=\"obj1\"/></node>" << 0 << 1;
+    QTest::newRow("two-objects") << "<node><node name=\"obj1\"/><node name=\"obj2\"></node>" << 0 << 2;
+
+    QTest::newRow("i1o1") << "<node><interface name=\"iface.iface1\"><node name=\"obj1\"></node>" << 1 << 1;
+
+}
+
+void tst_QDBusXmlParser::parsing_common(const QString &xmlData)
+{
+    QDBusIntrospection::ObjectTree obj =
+        QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/");
+    QFETCH(int, interfaceCount);
+    QFETCH(int, objectCount);
+    QCOMPARE(obj.interfaces.count(), interfaceCount);
+    QCOMPARE(obj.childObjects.count(), objectCount);
+
+    // also verify the naming
+    int i = 0;
+    foreach (QString name, obj.interfaces)
+        QCOMPARE(name, QString("iface.iface%1").arg(++i));
+
+    i = 0;
+    foreach (QString name, obj.childObjects)
+        QCOMPARE(name, QString("obj%1").arg(++i));
+}
+
+void tst_QDBusXmlParser::parsing()
+{
+    QFETCH(QString, xmlData);
+
+    parsing_common(xmlData);
+}
+
+void tst_QDBusXmlParser::parsingWithDoctype_data()
+{
+    parsing_data();
+}
+
+void tst_QDBusXmlParser::parsingWithDoctype()
+{
+    QString docType = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+                      "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
+    QFETCH(QString, xmlData);
+
+    parsing_common(docType + xmlData);
+}    
+
+void tst_QDBusXmlParser::objectWithContent_data()
+{
+    QTest::addColumn<QString>("xmlData");
+    QTest::addColumn<QString>("probedObject");
+    QTest::addColumn<int>("interfaceCount");
+    QTest::addColumn<int>("objectCount");
+
+    QTest::newRow("zero") << "<node><node name=\"obj\"/></node>" << "obj" << 0 << 0;
+
+    QString xmlData = "<node><node name=\"obj\">"
+                      "<interface name=\"iface.iface1\" />"
+                      "</node></node>";
+    QTest::newRow("one-interface") << xmlData << "obj" << 1 << 0;
+    QTest::newRow("one-interface2") << xmlData << "obj2" << 0 << 0;
+
+    xmlData = "<node><node name=\"obj\">"
+              "<interface name=\"iface.iface1\" />"
+              "<interface name=\"iface.iface2\" />"
+              "</node></node>";
+    QTest::newRow("two-interfaces") << xmlData << "obj" << 2 << 0;
+    QTest::newRow("two-interfaces2") << xmlData << "obj2" << 0 << 0;
+
+    xmlData = "<node><node name=\"obj\">"
+              "<interface name=\"iface.iface1\" />"
+              "<interface name=\"iface.iface2\" />"
+              "</node><node name=\"obj2\">"
+              "<interface name=\"iface.iface1\" />"
+              "</node></node>";
+    QTest::newRow("two-nodes-two-interfaces") << xmlData << "obj" << 2 << 0;
+    QTest::newRow("two-nodes-one-interface") << xmlData << "obj2" << 1 << 0;
+
+    xmlData = "<node><node name=\"obj\">"
+              "<node name=\"obj1\" />"
+              "</node></node>";
+    QTest::newRow("one-object") << xmlData << "obj" << 0 << 1;
+    QTest::newRow("one-object2") << xmlData << "obj2" << 0 << 0;
+
+    xmlData = "<node><node name=\"obj\">"
+              "<node name=\"obj1\" />"
+              "<node name=\"obj2\" />"
+              "</node></node>";
+    QTest::newRow("two-objects") << xmlData << "obj" << 0 << 2;
+    QTest::newRow("two-objects2") << xmlData << "obj2" << 0 << 0;
+
+    xmlData = "<node><node name=\"obj\">"
+              "<node name=\"obj1\" />"
+              "<node name=\"obj2\" />"
+              "</node><node name=\"obj2\">"
+              "<node name=\"obj1\" />"
+              "</node></node>";
+    QTest::newRow("two-nodes-two-objects") << xmlData << "obj" << 0 << 2;
+    QTest::newRow("two-nodes-one-object") << xmlData << "obj2" << 0 << 1;
+}
+
+void tst_QDBusXmlParser::objectWithContent()
+{
+    QFETCH(QString, xmlData);
+    QFETCH(QString, probedObject);
+
+    QDBusIntrospection::ObjectTree tree =
+        QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/");
+
+    const ObjectMap &om = tree.childObjectData;
+
+    if (om.contains(probedObject)) {
+        const QSharedDataPointer<QDBusIntrospection::ObjectTree>& obj = om.value(probedObject);
+        QVERIFY(obj != 0);
+    
+        QFETCH(int, interfaceCount);
+        QFETCH(int, objectCount);
+
+        QCOMPARE(obj->interfaces.count(), interfaceCount);
+        QCOMPARE(obj->childObjects.count(), objectCount);
+
+        // verify the object names
+        int i = 0;
+        foreach (QString name, obj->interfaces)
+            QCOMPARE(name, QString("iface.iface%1").arg(++i));
+
+        i = 0;
+        foreach (QString name, obj->childObjects)
+            QCOMPARE(name, QString("obj%1").arg(++i));
+    }
+}
+
+void tst_QDBusXmlParser::methods_data()
+{
+    QTest::addColumn<QString>("xmlDataFragment");
+    QTest::addColumn<MethodMap>("methodMap");
+
+    MethodMap map;
+    QTest::newRow("no-methods") << QString() << map;
+
+    // one method without arguments
+    QDBusIntrospection::Method method;
+    method.name = "Foo";
+    map << method;
+    QTest::newRow("one-method") << "<method name=\"Foo\"/>" << map;
+
+    // add another method without arguments
+    method.name = "Bar";
+    map << method;
+    QTest::newRow("two-methods") << "<method name=\"Foo\"/>"
+                                    "<method name=\"Bar\"/>"
+                                 << map;
+
+    // invert the order of the XML declaration
+    QTest::newRow("two-methods-inverse") << "<method name=\"Bar\"/>"
+                                            "<method name=\"Foo\"/>"
+                                         << map;
+
+    // add a third, with annotations
+    method.name = "Baz";
+    method.annotations.insert("foo.testing", "nothing to see here");
+    map << method;
+    QTest::newRow("method-with-annotation") <<
+        "<method name=\"Foo\"/>"
+        "<method name=\"Bar\"/>"
+        "<method name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></method>"
+                                            << map;
+
+    // arguments
+    map.clear();
+    method.annotations.clear();
+
+    method.name = "Method";
+    method.inputArgs << arg("s");
+    map << method;
+    QTest::newRow("one-in") <<
+        "<method name=\"Method\">"
+        "<arg type=\"s\" direction=\"in\"/>"
+        "</method>" << map;
+
+    // two arguments
+    method.inputArgs << arg("v");
+    map.clear();
+    map << method;
+    QTest::newRow("two-in") <<
+        "<method name=\"Method\">"
+        "<arg type=\"s\" direction=\"in\"/>"
+        "<arg type=\"v\" direction=\"in\"/>"
+        "</method>" << map;
+
+    // one invalid arg
+    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 = "s";
+    prop.access = QDBusIntrospection::Property::Read;
+    map << prop;
+    QTest::newRow("one-readable") << "<property name=\"foo\" type=\"s\" access=\"read\"/>" << map;
+
+    // one writable signal
+    prop.access = QDBusIntrospection::Property::Write;
+    map.clear();
+    map << prop;
+    QTest::newRow("one-writable") << "<property name=\"foo\" type=\"s\" access=\"write\"/>" << map;
+
+    // one read- & writable signal
+    prop.access = QDBusIntrospection::Property::ReadWrite;
+    map.clear();
+    map << prop;
+    QTest::newRow("one-read-writable") << "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>"
+                                       << map;
+
+    // two, mixed properties
+    prop.name = "bar";
+    prop.type = "i";
+    prop.access = QDBusIntrospection::Property::Read;
+    map << prop;
+    QTest::newRow("two") <<
+        "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>"
+        "<property name=\"bar\" type=\"i\" access=\"read\"/>" << map;
+
+    // invert the order of the declaration
+    QTest::newRow("two") <<
+        "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+        "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+
+    // add a third with annotations
+    prop.name = "baz";
+    prop.type = "as";
+    prop.access = QDBusIntrospection::Property::Write;
+    prop.annotations.insert("foo.annotation", "Hello, World");
+    prop.annotations.insert("foo.annotation2", "Goodbye, World");
+    map << prop;
+    QTest::newRow("complex") <<
+        "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+        "<property name=\"baz\" type=\"as\" access=\"write\">"
+        "<annotation name=\"foo.annotation\" value=\"Hello, World\" />"
+        "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />"
+        "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+
+    // and now change the order
+    QTest::newRow("complex2") <<
+        "<property name=\"baz\" type=\"as\" access=\"write\">"
+        "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />"
+        "<annotation name=\"foo.annotation\" value=\"Hello, World\" />"
+        "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+        "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+}
+
+void tst_QDBusXmlParser::properties()
+{
+    QString xmlHeader = "<node>"
+                        "<interface name=\"iface.iface1\">",
+            xmlFooter = "</interface>"
+                        "</node>";
+
+    QFETCH(QString, xmlDataFragment);
+
+    QDBusIntrospection::Interface iface =
+        QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+    QCOMPARE(iface.name, QString("iface.iface1"));
+
+    QFETCH(PropertyMap, propertyMap);
+    PropertyMap parsedMap = iface.properties;
+
+    QCOMPARE(propertyMap.count(), parsedMap.count());
+    QCOMPARE(propertyMap, parsedMap);
+}
+
+QTEST_MAIN(tst_QDBusXmlParser)
+
+#include "tst_qdbusxmlparser.moc"