summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@trolltech.com>2006-02-15 17:06:41 +0000
committerThiago Macieira <thiago.macieira@trolltech.com>2006-02-15 17:06:41 +0000
commitbe159c17993e0a829018719917e4163928b89d93 (patch)
tree700d7c64f7e116c6b490f6adca96370491f0b91b
parent79ba5568c1c40287ed57f9a019db732d3a393f8b (diff)
Add new tests and update the existing one.
-rw-r--r--test/qt/Makefile.am23
-rw-r--r--test/qt/tst_hal.cpp78
-rw-r--r--test/qt/tst_qdbusconnection.cpp120
-rw-r--r--test/qt/tst_qdbusinterface.cpp325
-rw-r--r--test/qt/tst_qdbusobject.cpp198
-rw-r--r--test/qt/tst_qdbustype.cpp272
-rw-r--r--test/qt/tst_qdbusxmlparser.cpp709
7 files changed, 1710 insertions, 15 deletions
diff --git a/test/qt/Makefile.am b/test/qt/Makefile.am
index 4ada01d..77f86d8 100644
--- a/test/qt/Makefile.am
+++ b/test/qt/Makefile.am
@@ -1,7 +1,7 @@
INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/qt $(DBUS_CLIENT_CFLAGS) $(DBUS_QT_CFLAGS) $(DBUS_QTESTLIB_CFLAGS) -DDBUS_COMPILATION
if DBUS_BUILD_TESTS
-TEST_BINARIES=qdbusconnection
+TEST_BINARIES=qdbusconnection qdbusobject qdbusinterface qdbustype qdbusxmlparser hal
TESTS=
else
TEST_BINARIES=
@@ -11,16 +11,25 @@ endif
noinst_PROGRAMS= $(TEST_BINARIES)
-qdbusconnection_SOURCES= \
- tst_qdbusconnection.cpp
+qdbusconnection_SOURCES= tst_qdbusconnection.cpp
+qdbusobject_SOURCES= tst_qdbusobject.cpp
+qdbusinterface_SOURCES= tst_qdbusinterface.cpp
+qdbustype_SOURCES= tst_qdbustype.cpp
+qdbusxmlparser_SOURCES= tst_qdbusxmlparser.cpp
+hal_SOURCES = tst_hal.cpp
-tst_qdbusconnection.cpp: tst_qdbusconnection.moc
+tst_qdbusconnection.o: tst_qdbusconnection.moc
+tst_qdbusobject.o: tst_qdbusobject.moc
+tst_qdbusinterface.o: tst_qdbusinterface.moc
+tst_qdbustype.o: tst_qdbustype.moc
+tst_qdbusxmlparser.o: tst_qdbusxmlparser.moc
+tst_hal.o: tst_hal.moc
-tst_qdbusconnection.moc:
- $(QT_MOC) $(srcdir)/tst_qdbusconnection.cpp > tst_qdbusconnection.moc
+%.moc: %.cpp
+ $(QT_MOC) $< > $@
TEST_LIBS=$(DBUS_QTESTLIB_LIBS) $(top_builddir)/qt/libdbus-qt4-1.la
-qdbusconnection_LDADD=$(TEST_LIBS)
+LDADD=$(TEST_LIBS)
CLEANFILES=tst_qdbusconnection.moc
diff --git a/test/qt/tst_hal.cpp b/test/qt/tst_hal.cpp
new file mode 100644
index 0000000..36389c2
--- /dev/null
+++ b/test/qt/tst_hal.cpp
@@ -0,0 +1,78 @@
+#include <qcoreapplication.h>
+#include <qdebug.h>
+
+#include <QtTest/QtTest>
+#include <dbus/qdbus.h>
+
+class tst_Hal: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void getDevices();
+ void lock();
+};
+
+class Spy: public QObject
+{
+ Q_OBJECT
+public:
+ int count;
+ QDBusConnection conn;
+
+ Spy(QDBusConnection c) : count(0), conn(c)
+ { }
+
+public slots:
+ void spySlot(int, const QVariantList&)
+ {
+ ++count;
+ QDBusMessage msg = QDBusMessage::methodCall("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/devices/acpi_CPU0",
+ "org.freedesktop.Hal.Device", "GetProperty");
+ msg << "info.locked";
+
+ QDBusMessage reply = conn.sendWithReply(msg);
+ QVERIFY(!reply.isEmpty());
+ }
+};
+
+
+void tst_Hal::getDevices()
+{
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SystemBus);
+ QVERIFY(con.isConnected());
+
+ QDBusMessage msg = QDBusMessage::methodCall("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
+ "GetAllDevices");
+
+ QDBusMessage reply = con.sendWithReply(msg);
+ QVERIFY(!reply.isEmpty());
+ qDebug() << reply;
+}
+
+void tst_Hal::lock()
+{
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SystemBus);
+ QVERIFY(con.isConnected());
+
+ Spy spy( con );
+
+ con.connect("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/acpi_CPU0",
+ "org.freedesktop.Hal.Device", "PropertyModified",
+ &spy, SLOT(spySlot(int, QVariantList)));
+ QDBusMessage msg = QDBusMessage::methodCall("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/devices/acpi_CPU0", "org.freedesktop.Hal.Device",
+ "Lock");
+ msg << "No reason...";
+
+ QDBusMessage reply = con.sendWithReply(msg);
+ qDebug() << reply;
+ QCOMPARE(spy.count, 3);
+ QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
+}
+
+QTEST_MAIN(tst_Hal)
+
+#include "tst_hal.moc"
diff --git a/test/qt/tst_qdbusconnection.cpp b/test/qt/tst_qdbusconnection.cpp
index e14debd..c3b15a2 100644
--- a/test/qt/tst_qdbusconnection.cpp
+++ b/test/qt/tst_qdbusconnection.cpp
@@ -10,11 +10,19 @@ class tst_QDBusConnection: public QObject
Q_OBJECT
private slots:
+ void init();
+ void cleanupTestCase();
void addConnection();
void connect();
void send();
void sendAsync();
void sendSignal();
+ void requestName_data();
+ void requestName();
+ void getNameOwner_data();
+ void getNameOwner();
+ void releaseName_data();
+ void releaseName();
};
class QDBusSpy: public QObject
@@ -29,10 +37,25 @@ public:
int serial;
};
+void tst_QDBusConnection::init()
+{
+ if (qstrcmp(QTest::currentTestFunction(), "addConnection") == 0)
+ return;
+
+ QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QVERIFY(QDBusConnection().isConnected());
+}
+
+void tst_QDBusConnection::cleanupTestCase()
+{
+ QDBusConnection::closeConnection();
+
+ QVERIFY(!QDBusConnection().isConnected());
+}
+
void tst_QDBusConnection::sendSignal()
{
- QDBusConnection con = QDBusConnection::addConnection(
- QDBusConnection::SessionBus);
+ QDBusConnection con;
QVERIFY(con.isConnected());
@@ -47,8 +70,7 @@ void tst_QDBusConnection::sendSignal()
void tst_QDBusConnection::send()
{
- QDBusConnection con = QDBusConnection::addConnection(
- QDBusConnection::SessionBus);
+ QDBusConnection con;
QVERIFY(con.isConnected());
@@ -64,7 +86,7 @@ void tst_QDBusConnection::send()
void tst_QDBusConnection::sendAsync()
{
- QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QDBusConnection con;
QVERIFY(con.isConnected());
QDBusSpy spy;
@@ -85,10 +107,9 @@ void tst_QDBusConnection::connect()
{
QDBusSpy spy;
- QDBusConnection con = QDBusConnection::addConnection(
- QDBusConnection::SessionBus);
+ QDBusConnection con;
- con.connect("/org/kde/selftest", "org.kde.selftest", "ping", &spy,
+ con.connect(con.baseService(), "/org/kde/selftest", "org.kde.selftest", "ping", &spy,
SLOT(handlePing(QString)));
QDBusMessage msg = QDBusMessage::signal("/org/kde/selftest", "org.kde.selftest",
@@ -158,6 +179,89 @@ void tst_QDBusConnection::addConnection()
}
}
+void tst_QDBusConnection::requestName_data()
+{
+ QTest::addColumn<QString>("requestedName");
+ QTest::addColumn<int>("flags");
+ QTest::addColumn<bool>("expectedResult");
+
+ QTest::newRow("null") << QString() << (int)QDBusConnection::NoReplace << false;
+ QTest::newRow("empty") << QString("") << (int)QDBusConnection::NoReplace << false;
+ QTest::newRow("invalid") << "./invalid name" << (int)QDBusConnection::NoReplace << false;
+// QTest::newRow("existing") << "org.freedesktop.DBus"
+// << (int)QDBusConnection::NoReplace << false;
+
+ QTest::newRow("ok1") << "com.trolltech.QtDBUS.tst_qdbusconnection"
+ << (int)QDBusConnection::NoReplace << true;
+}
+
+void tst_QDBusConnection::requestName()
+{
+ QDBusConnection con;
+
+ QVERIFY(con.isConnected());
+
+ QFETCH(QString, requestedName);
+ QFETCH(int, flags);
+ QFETCH(bool, expectedResult);
+
+ bool result = con.requestName(requestedName, (QDBusConnection::NameRequestMode)flags);
+
+// QEXPECT_FAIL("existing", "For whatever reason, the bus lets us replace this name", Abort);
+ QCOMPARE(result, expectedResult);
+}
+
+void tst_QDBusConnection::getNameOwner_data()
+{
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("expectedResult");
+
+ QTest::newRow("null") << QString() << QString();
+ QTest::newRow("empty") << QString("") << QString();
+
+ QTest::newRow("invalid") << ".invalid" << QString();
+ QTest::newRow("non-existent") << "com.trolltech.QtDBUS.foo" << QString();
+
+ QTest::newRow("bus") << "org.freedesktop.DBus" << "org.freedesktop.DBus";
+
+ QString base = QDBusConnection().baseService();
+ QTest::newRow("address") << base << base;
+ QTest::newRow("self") << "com.trolltech.QtDBUS.tst_qdbusconnection" << base;
+}
+
+void tst_QDBusConnection::getNameOwner()
+{
+ QFETCH(QString, name);
+ QFETCH(QString, expectedResult);
+
+ QDBusConnection con;
+ QVERIFY(con.isConnected());
+
+ QString result = con.getNameOwner(name);
+
+ QCOMPARE(result, expectedResult);
+}
+
+void tst_QDBusConnection::releaseName_data()
+{
+ requestName_data();
+}
+
+void tst_QDBusConnection::releaseName()
+{
+ QDBusConnection con;
+
+ QVERIFY(con.isConnected());
+
+ QFETCH(QString, requestedName);
+ //QFETCH(int, flags);
+ QFETCH(bool, expectedResult);
+
+ bool result = con.releaseName(requestedName);
+
+ QCOMPARE(result, expectedResult);
+}
+
QTEST_MAIN(tst_QDBusConnection)
#include "tst_qdbusconnection.moc"
diff --git a/test/qt/tst_qdbusinterface.cpp b/test/qt/tst_qdbusinterface.cpp
new file mode 100644
index 0000000..599b6c0
--- /dev/null
+++ b/test/qt/tst_qdbusinterface.cpp
@@ -0,0 +1,325 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <qcoreapplication.h>
+#include <qmetatype.h>
+#include <QtTest/QtTest>
+
+#include <dbus/qdbus.h>
+#include <QtCore/qvariant.h>
+
+Q_DECLARE_METATYPE(QVariantList)
+
+#define TEST_INTERFACE_NAME "com.trolltech.QtDBus.MyObject"
+#define TEST_SERVICE_NAME "com.trolltech.QtDBus.tst_qdbusinterface"
+#define TEST_SIGNAL_NAME "somethingHappened"
+
+const char introspectionData[] =
+ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+ "<node>"
+
+ "<interface name=\"org.freedesktop.DBus.Introspectable\">"
+ "<method name=\"Introspect\">"
+ "<arg name=\"data\" direction=\"out\" type=\"s\"/>"
+ "</method>"
+ "</interface>"
+
+ "<interface name=\"" TEST_INTERFACE_NAME "\">"
+ "<method name=\"ping\">"
+ "<arg name=\"ping\" direction=\"in\" type=\"v\"/>"
+ "<arg name=\"pong\" direction=\"out\" type=\"v\"/>"
+ "</method>"
+ "<method name=\"ping\">"
+ "<arg name=\"ping1\" direction=\"in\" type=\"v\"/>"
+ "<arg name=\"ping2\" direction=\"in\" type=\"v\"/>"
+ "<arg name=\"pong1\" direction=\"out\" type=\"v\"/>"
+ "<arg name=\"pong2\" direction=\"out\" type=\"v\"/>"
+ "</method>"
+ "<signal name=\"" TEST_SIGNAL_NAME "\">"
+ "<arg type=\"s\"/>"
+ "</signal>"
+ "<property name=\"prop1\" access=\"readwrite\" type=\"i\" />"
+ "</interface>"
+ "<node name=\"subObject\"/>"
+ "</node>";
+
+class MyObject: public QObject
+{
+ Q_OBJECT
+public slots:
+
+ void ping(const QDBusMessage &msg)
+ {
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply << static_cast<QList<QVariant> >(msg);
+ if (!con.send(reply))
+ exit(1);
+ }
+
+ void Introspect(const QDBusMessage &msg)
+ {
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply << ::introspectionData;
+ if (!con.send(reply))
+ exit(1);
+ }
+};
+
+class Spy: public QObject
+{
+ Q_OBJECT
+public:
+ QString received;
+ int count;
+
+ Spy() : count(0)
+ { }
+
+public slots:
+ void spySlot(const QString& arg)
+ {
+ received = arg;
+ ++count;
+ }
+};
+
+// helper function
+void emitSignal(const QString &interface, const QString &name, const QString &arg)
+{
+ QDBusMessage msg = QDBusMessage::signal("/", interface, name);
+ msg << arg;
+ QDBusConnection().send(msg);
+
+ QTest::qWait(200);
+}
+
+class tst_QDBusInterface: public QObject
+{
+ Q_OBJECT
+ MyObject obj;
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void call_data();
+ void call();
+
+ void introspect_data();
+ void introspect();
+
+ void signal();
+};
+
+void tst_QDBusInterface::initTestCase()
+{
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QVERIFY(con.isConnected());
+ QVERIFY(con.requestName( TEST_SERVICE_NAME ));
+
+ con.registerObject("/", "org.freedesktop.DBus.Introspectable", &obj);
+ con.registerObject("/", TEST_INTERFACE_NAME, &obj);
+}
+
+void tst_QDBusInterface::cleanupTestCase()
+{
+ QDBusConnection::closeConnection();
+ QVERIFY(!QDBusConnection().isConnected());
+}
+
+void tst_QDBusInterface::call_data()
+{
+ QTest::addColumn<QString>("method");
+ QTest::addColumn<QVariantList>("input");
+ QTest::addColumn<QVariantList>("output");
+
+ QVariantList input;
+ QTest::newRow("empty") << "ping" << input << input;
+
+ input << qVariantFromValue(1);
+ QTest::newRow("int") << "ping" << input << input;
+ QTest::newRow("int-int") << "ping.i" << input << input;
+ QTest::newRow("int-int16") << "ping.n" << input << input;
+
+ // try doing some conversions
+ QVariantList output;
+ output << qVariantFromValue(1U);
+ QTest::newRow("int-uint") << "ping.u" << input << output;
+ QTest::newRow("int-uint16") << "ping.q" << input << output;
+
+ QTest::newRow("int-int64") << "ping.x" << input << (QVariantList() << qVariantFromValue(1LL));
+ QTest::newRow("int-uint64") << "ping.t" << input << (QVariantList() << qVariantFromValue(1ULL));
+ QTest::newRow("int-double") << "ping.d" << input << (QVariantList() << qVariantFromValue(1.0));
+
+ output.clear();
+ output << QString("1");
+ QTest::newRow("int-string") << "ping.s" << input << output;
+
+ // try from string now
+ input = output;
+ QTest::newRow("string") << "ping" << input << output;
+ QTest::newRow("string-string") << "ping.s" << input << output;
+
+ output.clear();
+ output << qVariantFromValue(1);
+ QTest::newRow("string-int") << "ping.i" << input << input;
+ QTest::newRow("string-int16") << "ping.n" << input << input;
+
+ output.clear();
+ output << qVariantFromValue(1U);
+ QTest::newRow("string-uint") << "ping.u" << input << output;
+ QTest::newRow("string-uint16") << "ping.q" << input << output;
+
+ QTest::newRow("string-int64") << "ping.x" << input << (QVariantList() << qVariantFromValue(1LL));
+ QTest::newRow("string-uint64") << "ping.t" << input << (QVariantList() << qVariantFromValue(1ULL));
+ QTest::newRow("string-double") << "ping.d" << input << (QVariantList() << qVariantFromValue(1.0));
+
+ // two args (must be strings!)
+ input.clear();
+ input << QString("Hello") << QString("World");
+ output = input;
+ QTest::newRow("two-strings") << "ping" << input << output;
+ QTest::newRow("two-strings") << "ping.ss" << input << output;
+
+ // this should drop one of the arguments
+ output.removeLast();
+ QTest::newRow("last-dropped") << "ping.s" << input << output;
+}
+
+void tst_QDBusInterface::call()
+{
+ QDBusConnection con;
+ QDBusInterface iface(con, con.baseService(), QLatin1String("/"),
+ TEST_INTERFACE_NAME);
+
+ QFETCH(QString, method);
+ QFETCH(QVariantList, input);
+ QFETCH(QVariantList, output);
+
+ QDBusMessage reply;
+ // try first callWithArgs:
+ reply = iface.callWithArgs(method, input);
+
+ QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
+ if (!output.isEmpty()) {
+ QCOMPARE(reply.count(), output.count());
+ QCOMPARE(static_cast<QVariantList>(reply), output);
+ }
+
+ // try the template methods
+ if (input.isEmpty())
+ reply = iface.call(method);
+ else if (input.count() == 1)
+ switch (input.at(0).type())
+ {
+ case QVariant::Int:
+ reply = iface.call(method, input.at(0).toInt());
+ break;
+
+ case QVariant::UInt:
+ reply = iface.call(method, input.at(0).toUInt());
+ break;
+
+ case QVariant::String:
+ reply = iface.call(method, input.at(0).toString());
+ break;
+
+ default:
+ QFAIL("Unknown type. Please update the test case");
+ break;
+ }
+ else
+ reply = iface.call(method, input.at(0).toString(), input.at(1).toString());
+
+ QCOMPARE(reply.type(), QDBusMessage::ReplyMessage);
+ if (!output.isEmpty()) {
+ QCOMPARE(reply.count(), output.count());
+ QCOMPARE(static_cast<QVariantList>(reply), output);
+ }
+}
+
+void tst_QDBusInterface::introspect_data()
+{
+ QTest::addColumn<QString>("service");
+ QTest::newRow("base") << QDBusConnection().baseService();
+ QTest::newRow("name") << TEST_SERVICE_NAME;
+}
+
+void tst_QDBusInterface::introspect()
+{
+ QFETCH(QString, service);
+ QDBusConnection con;
+ QDBusInterface iface(con, service, QLatin1String("/"),
+ TEST_INTERFACE_NAME);
+
+ QDBusIntrospection::Methods mm = iface.methodData();
+ QVERIFY(mm.count() == 2);
+
+ QDBusIntrospection::Signals sm = iface.signalData();
+ QVERIFY(sm.count() == 1);
+ QVERIFY(sm.contains(TEST_SIGNAL_NAME));
+
+ QDBusIntrospection::Properties pm = iface.propertyData();
+ QVERIFY(pm.count() == 1);
+ QVERIFY(pm.contains("prop1"));
+}
+
+void tst_QDBusInterface::signal()
+{
+ QDBusConnection con;
+ QDBusInterface iface(con, con.baseService(), QLatin1String("/"),
+ TEST_INTERFACE_NAME);
+
+ QString signalName = TEST_SIGNAL_NAME;
+
+ QString arg = "So long and thanks for all the fish";
+ {
+ Spy spy;
+ iface.connect(signalName, &spy, SLOT(spySlot(QString)));
+
+ emitSignal(TEST_INTERFACE_NAME, signalName, arg);
+ QVERIFY(spy.count == 1);
+ QCOMPARE(spy.received, arg);
+ }
+
+ QDBusIntrospection::Signals sm = iface.signalData();
+ QVERIFY(sm.contains(signalName));
+
+ const QDBusIntrospection::Signal& signal = sm.value(signalName);
+ QCOMPARE(signal.name, signalName);
+ QVERIFY(!signal.outputArgs.isEmpty());
+ {
+ Spy spy;
+ iface.connect(signal, &spy, SLOT(spySlot(QString)));
+
+ emitSignal(TEST_INTERFACE_NAME, signalName, arg);
+ QVERIFY(spy.count == 1);
+ QCOMPARE(spy.received, arg);
+ }
+}
+
+QTEST_MAIN(tst_QDBusInterface)
+
+#include "tst_qdbusinterface.moc"
+
diff --git a/test/qt/tst_qdbusobject.cpp b/test/qt/tst_qdbusobject.cpp
new file mode 100644
index 0000000..24d5077
--- /dev/null
+++ b/test/qt/tst_qdbusobject.cpp
@@ -0,0 +1,198 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <qcoreapplication.h>
+#include <qmetatype.h>
+#include <QtTest/QtTest>
+
+#include <dbus/qdbus.h>
+
+const char introspectionData[] =
+ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+ "<node>"
+
+ "<interface name=\"org.freedesktop.DBus.Introspectable\">"
+ "<method name=\"Introspect\">"
+ "<arg name=\"data\" direction=\"out\" type=\"s\"/>"
+ "</method>"
+ "</interface>"
+
+ "<interface name=\"com.trolltech.tst_qdbusobject.MyObject\">"
+ "<method name=\"ping\">"
+ "<arg name=\"ping\" direction=\"in\" type=\"v\"/>"
+ "<arg name=\"pong\" direction=\"out\" type=\"v\"/>"
+ "</method>"
+ "</interface>"
+ "<node name=\"subObject\"/>"
+ "</node>";
+
+class MyObject: public QObject
+{
+ Q_OBJECT
+public slots:
+
+ void ping(const QDBusMessage &msg)
+ {
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply << static_cast<QList<QVariant> >(msg);
+ if (!con.send(reply))
+ exit(1);
+ }
+
+ void Introspect(const QDBusMessage &msg)
+ {
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply << ::introspectionData;
+ if (!con.send(reply))
+ exit(1);
+ }
+};
+
+class tst_QDBusObject: public QObject
+{
+ Q_OBJECT
+ MyObject obj;
+
+private slots:
+ void initTestCase(); // connect to D-Bus
+ void cleanupTestCase(); // disconnect from D-Bus
+
+ void construction_data();
+ void construction();
+
+ void introspection_data();
+ void introspection();
+};
+
+void tst_QDBusObject::initTestCase()
+{
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+ QVERIFY(con.isConnected());
+ QVERIFY(con.requestName("com.trolltech.tst_qdbusobject"));
+
+ con.registerObject("/", "org.freedesktop.DBus.Introspectable", &obj);
+ con.registerObject("/", "com.trolltech.tst_qdbusobject.MyObject", &obj);
+}
+
+void tst_QDBusObject::cleanupTestCase()
+{
+ QDBusConnection::closeConnection();
+ QVERIFY(!QDBusConnection().isConnected());
+}
+
+void tst_QDBusObject::construction_data()
+{
+ QTest::addColumn<QString>("service");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<bool>("isValid");
+ QTest::addColumn<bool>("exists");
+
+ QTest::newRow("null") << QString() << QString() << false << false;
+
+ QTest::newRow("invalid1") << "foo.foo1" << "" << false << false;
+ QTest::newRow("invalid2") << "foo.foo1" << "foo.bar" << false << false;
+ QTest::newRow("invalid3") << "foo.foo1" << "/foo.bar" << false << false;
+ QTest::newRow("invalid4") << "" << "/" << false << false;
+ QTest::newRow("invalid5") << "foo" << "/" << false << false;
+ QTest::newRow("invalid6") << ".foo" << "/" << false << false;
+
+ QTest::newRow("invalid7") << "org.freedesktop.DBus" << "" << false << false;
+ QTest::newRow("invalid8") << "org.freedesktop.DBus" << "foo.bar" << false << false;
+ QTest::newRow("invalid9") << "org.freedesktop.DBus" << "/foo.bar" << false << false;
+
+ QTest::newRow("existing") << "org.freedesktop.DBus" << "/" << true << true;
+ QTest::newRow("non-existing") << "org.freedesktop.DBus" << "/foo" << true << false;
+}
+
+void tst_QDBusObject::construction()
+{
+ QDBusConnection con; // default
+
+ QFETCH(QString, service);
+ QFETCH(QString, path);
+ QFETCH(bool, isValid);
+ //QFETCH(bool, exists);
+
+ QDBusObject o(con, service, path);
+ QCOMPARE(o.isValid(), isValid);
+
+ if (isValid) {
+ QCOMPARE(o.service(), service);
+ QCOMPARE(o.path(), path);
+ }
+ else {
+ QVERIFY(o.service().isNull());
+ QVERIFY(o.path().isNull());
+ }
+
+ //QCOMPARE(o.exists(), exists);
+}
+
+void tst_QDBusObject::introspection_data()
+{
+ QTest::addColumn<QString>("service");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QStringList>("interfaces");
+
+ QStringList interfaces;
+ QTest::newRow("nowhere") << QString() << QString() << interfaces;
+
+ // IMPORTANT!
+ // Keep the interface list sorted!
+ interfaces << "org.freedesktop.DBus" << DBUS_INTERFACE_INTROSPECTABLE;
+ QTest::newRow("server") << "org.freedesktop.DBus" << "/" << interfaces;
+
+ QDBusConnection con;
+ interfaces.clear();
+ interfaces << "com.trolltech.tst_qdbusobject.MyObject" << DBUS_INTERFACE_INTROSPECTABLE;
+
+ QTest::newRow("self1") << con.baseService() << "/" << interfaces;
+ QTest::newRow("self2") << "com.trolltech.tst_qdbusobject" << "/" << interfaces;
+}
+
+void tst_QDBusObject::introspection()
+{
+ QDBusConnection con;
+
+ QFETCH(QString, service);
+ QFETCH(QString, path);
+
+ QDBusObject o(con, service, path);
+
+ if (!o.isValid())
+ QVERIFY(o.introspect().isEmpty());
+ else {
+ QFETCH(QStringList, interfaces);
+ QStringList parsed = o.interfaces();
+ parsed.sort();
+ QCOMPARE(parsed.count(), interfaces.count());
+ QCOMPARE(parsed, interfaces);
+ }
+}
+
+QTEST_MAIN(tst_QDBusObject)
+
+#include "tst_qdbusobject.moc"
+
diff --git a/test/qt/tst_qdbustype.cpp b/test/qt/tst_qdbustype.cpp
new file mode 100644
index 0000000..124e60c
--- /dev/null
+++ b/test/qt/tst_qdbustype.cpp
@@ -0,0 +1,272 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <qcoreapplication.h>
+#include <QtTest/QtTest>
+
+#include <dbus/qdbus.h>
+
+class tst_QDBusType: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void fromType_data();
+ void fromType();
+ void fromSignature_data();
+ void fromSignature();
+ void arrayOf_data();
+ void arrayOf();
+ void mapOf_data();
+ void mapOf();
+};
+
+inline QTestData &operator<<(QTestData &data, QVariant::Type t)
+{
+ return data << int(t);
+}
+
+void tst_QDBusType::fromType_data()
+{
+ fromSignature_data();
+}
+
+void tst_QDBusType:: arrayOf_data()
+{
+ fromSignature_data();
+}
+
+void tst_QDBusType::mapOf_data()
+{
+ fromSignature_data();
+}
+
+void tst_QDBusType::fromSignature_data()
+{
+ QTest::addColumn<QString>("signature");
+ QTest::addColumn<char>("type");
+ QTest::addColumn<int>("qvariantType");
+ QTest::addColumn<bool>("isValid");
+ QTest::addColumn<bool>("isBasic");
+ QTest::addColumn<bool>("isContainer");
+ QTest::addColumn<int>("subtypeCount");
+
+ QTest::newRow("null") << QString() << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("empty") << QString("") << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("invalid") << QString("~") << '\0' << QVariant::Invalid << false << false << false << 0;
+
+ // integers:
+ QTest::newRow("byte") << "y" << 'y' << QVariant::UInt << true << true << false << 0;
+ QTest::newRow("boolean") << "b" << 'b' << QVariant::Bool << true << true << false << 0;
+ QTest::newRow("int16") << "n" << 'n' << QVariant::Int << true << true << false << 0;
+ QTest::newRow("uint16") << "q" << 'q' << QVariant::UInt << true << true << false << 0;
+ QTest::newRow("int32") << "i" << 'i' << QVariant::Int << true << true << false << 0;
+ QTest::newRow("uint32") << "u" << 'u' << QVariant::UInt << true << true << false << 0;
+ QTest::newRow("int64") << "x" << 'x' << QVariant::LongLong << true << true << false << 0;
+ QTest::newRow("uint64") << "t" << 't' << QVariant::ULongLong << true << true << false << 0;
+
+ // double:
+ QTest::newRow("double") << "d" << 'd' << QVariant::Double << true << true << false << 0;
+
+ // string types:
+ QTest::newRow("string") << "s" << 's' << QVariant::String << true << true << false << 0;
+ QTest::newRow("objpath") << "o" << 'o' << QVariant::String << true << true << false << 0;
+ QTest::newRow("signature")<<"g" << 'g' << QVariant::String << true << true << false << 0;
+
+ // variant
+ QTest::newRow("variant") << "v" << 'v' << QVariant::UserType << true << false << true << 0;
+
+ // compound types:
+ QTest::newRow("struct-empty") << "()" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("struct-invalid") << "(~)" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("struct-unterminated")<< "(iii" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("struct-bad-nest") << "(i(i)((i)i)" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("struct1") << "(i)" << 'r' << QVariant::List << true << false << true << 1;
+ QTest::newRow("struct2") << "(ii)" << 'r' << QVariant::List << true << false << true << 2;
+
+ QTest::newRow("array-empty") << "a" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("array-invalid") << "a~" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("array-simple") << "ab" << 'a' << QVariant::List << true << false << true << 1;
+ QTest::newRow("bytearray") << "ay" << 'a' << QVariant::ByteArray << true << false << true << 1;
+ QTest::newRow("stringlist") << "as" << 'a' << QVariant::StringList << true << false << true << 1;
+
+ QTest::newRow("map-empty") << "e" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid1") << "a{}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid2") << "a{~}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid3") << "a{e}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid4") << "a{i}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid5") << "a{(i)d}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid6") << "{}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-invalid7") << "{i}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ //QTest::newRow("map-invalid8") << "{is}" << '\0' << QVariant::Invalid << false << false << false << 0; // this is valid when "a" is prepended
+ QTest::newRow("map-bad-nesting") << "a{i(s}" << '\0' << QVariant::Invalid << false << false << false << 0;
+ QTest::newRow("map-ok1") << "a{is}" << 'a' << QVariant::Map << true << false << true << 1;
+ QTest::newRow("map-ok2") << "a{sv}" << 'a' << QVariant::Map << true << false << true << 1;
+
+ // compound of compounds:
+ QTest::newRow("struct-struct") << "((i))" << 'r' << QVariant::List << true << false << true << 1;
+ QTest::newRow("struct-structs") << "((ii)d(i))" << 'r' << QVariant::List << true << false << true << 3;
+ QTest::newRow("map-struct") << "a{s(ii)}" << 'a' << QVariant::Map << true << false << true << 1;
+ QTest::newRow("map-stringlist") << "a{sas}" << 'a' << QVariant::Map << true << false << true << 1;
+ QTest::newRow("map-map") << "a{ia{sv}}" << 'a' << QVariant::Map << true << false << true << 1;
+ QTest::newRow("array-struct") << "a(ii)" << 'a' << QVariant::List << true << false << true << 1;
+ QTest::newRow("array-array") << "aai" << 'a' << QVariant::List << true << false << true << 1;
+ QTest::newRow("array-map") << "aa{sv}" << 'a' << QVariant::List << true << false << true << 1;
+}
+
+void tst_QDBusType::fromType()
+{
+ QFETCH(QString, signature);
+ if (signature.length() != 1)
+ // can't transform to typecode
+ return;
+
+ QFETCH(char, type);
+ QFETCH(int, qvariantType);
+ QFETCH(bool, isValid);
+ QFETCH(bool, isBasic);
+ QFETCH(bool, isContainer);
+
+ QDBusType t(signature.at(0).toLatin1());
+
+ QCOMPARE((char)t.dbusType(), type);
+ QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType));
+ QCOMPARE(t.isValid(), isValid);
+ QCOMPARE(t.isBasic(), isBasic);
+ QCOMPARE(t.isContainer(), isContainer);
+}
+
+void tst_QDBusType::fromSignature()
+{
+ QFETCH(QString, signature);
+ QFETCH(char, type);
+ QFETCH(int, qvariantType);
+ QFETCH(bool, isValid);
+ QFETCH(bool, isBasic);
+ QFETCH(bool, isContainer);
+ QFETCH(int, subtypeCount);
+
+ QDBusType t(signature);
+
+ QCOMPARE((char)t.dbusType(), type);
+ QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType));
+ QCOMPARE(t.isValid(), isValid);
+ QCOMPARE(t.isBasic(), isBasic);
+ QCOMPARE(t.isContainer(), isContainer);
+
+ if (isValid)
+ QCOMPARE(QLatin1String(t.dbusSignature()), signature);
+
+ QCOMPARE(t.subTypes().count(), subtypeCount);
+}
+
+void tst_QDBusType::arrayOf()
+{
+ QFETCH(QString, signature);
+ QFETCH(char, type);
+ QFETCH(int, qvariantType);
+ QFETCH(bool, isValid);
+ QFETCH(bool, isBasic);
+ QFETCH(bool, isContainer);
+ QFETCH(int, subtypeCount);
+
+ QDBusType arr("a" + signature.toLatin1());
+ QCOMPARE(arr.isValid(), isValid);
+ QVERIFY(!arr.isBasic());
+
+ if (isValid) {
+ QVERIFY(arr.isContainer());
+ QVERIFY(arr.isArray());
+ QCOMPARE((char)arr.dbusType(), 'a');
+ QCOMPARE(arr.subTypes().count(), 1);
+
+ // handle special cases:
+ if (type == 'y')
+ QCOMPARE(arr.qvariantType(), QVariant::ByteArray);
+ else if (type == 's' || type == 'o' || type == 'g')
+ QCOMPARE(arr.qvariantType(), QVariant::StringList);
+ else
+ QCOMPARE(arr.qvariantType(), QVariant::List);
+
+ // handle the array element now:
+ QDBusType t = arr.arrayElement();
+
+ QCOMPARE((char)t.dbusType(), type);
+ QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType));
+ QCOMPARE(t.isValid(), isValid);
+ QCOMPARE(t.isBasic(), isBasic);
+ QCOMPARE(t.isContainer(), isContainer);
+
+ QCOMPARE(QLatin1String(t.dbusSignature()), signature);
+
+ QCOMPARE(t.subTypes().count(), subtypeCount);
+ }
+}
+
+void tst_QDBusType::mapOf()
+{
+ QFETCH(QString, signature);
+ QFETCH(char, type);
+ QFETCH(int, qvariantType);
+ QFETCH(bool, isValid);
+ QFETCH(bool, isBasic);
+ QFETCH(bool, isContainer);
+ QFETCH(int, subtypeCount);
+
+ QDBusType map("a{s" + signature.toLatin1() + '}');
+ QCOMPARE(map.isValid(), isValid);
+ QVERIFY(!map.isBasic());
+
+ if (isValid) {
+ QVERIFY(map.isContainer());
+ QVERIFY(map.isArray());
+ QVERIFY(map.isMap());
+ QCOMPARE((char)map.dbusType(), 'a');
+ QCOMPARE(map.subTypes().count(), 1);
+
+ // handle the array element now:
+ QDBusType dict_entry = map.arrayElement();
+ QVERIFY(dict_entry.isValid());
+ QVERIFY(dict_entry.isContainer());
+ QVERIFY(!dict_entry.isMap());
+ QVERIFY(!dict_entry.isArray());
+
+ QVERIFY(map.mapKey().isBasic());
+
+ // handle the value:
+ QDBusType t = map.mapValue();
+
+ QCOMPARE((char)t.dbusType(), type);
+ QCOMPARE(t.qvariantType(), QVariant::Type(qvariantType));
+ QCOMPARE(t.isValid(), isValid);
+ QCOMPARE(t.isBasic(), isBasic);
+ QCOMPARE(t.isContainer(), isContainer);
+
+ QCOMPARE(QLatin1String(t.dbusSignature()), signature);
+
+ QCOMPARE(t.subTypes().count(), subtypeCount);
+ }
+}
+
+QTEST_MAIN(tst_QDBusType)
+
+#include "tst_qdbustype.moc"
diff --git a/test/qt/tst_qdbusxmlparser.cpp b/test/qt/tst_qdbusxmlparser.cpp
new file mode 100644
index 0000000..c7337c3
--- /dev/null
+++ b/test/qt/tst_qdbusxmlparser.cpp
@@ -0,0 +1,709 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <qcoreapplication.h>
+#include <qmetatype.h>
+#include <QtTest/QtTest>
+
+#include <dbus/qdbus.h>
+
+class tst_QDBusXmlParser: public QObject
+{
+ Q_OBJECT
+
+private:
+ void parsing_common(const QString&);
+
+private slots:
+ void parsing_data();
+ void parsing();
+ void parsingWithDoctype_data();
+ void parsingWithDoctype();
+
+ void objectWithContent_data();
+ void objectWithContent();
+
+ void methods_data();
+ void methods();
+ void signals__data();
+ void signals_();
+ void properties_data();
+ void properties();
+};
+
+// just to make it easier:
+typedef QDBusIntrospection::Interfaces InterfaceMap;
+typedef QDBusIntrospection::Objects ObjectMap;
+typedef QDBusIntrospection::Arguments ArgumentList;
+typedef QDBusIntrospection::Annotations AnnotationsMap;
+typedef QDBusIntrospection::Methods MethodMap;
+typedef QDBusIntrospection::Signals SignalMap;
+typedef QDBusIntrospection::Properties PropertyMap;
+
+Q_DECLARE_METATYPE(QDBusIntrospection::Method)
+Q_DECLARE_METATYPE(QDBusIntrospection::Signal)
+Q_DECLARE_METATYPE(QDBusIntrospection::Property)
+Q_DECLARE_METATYPE(MethodMap)
+Q_DECLARE_METATYPE(SignalMap)
+Q_DECLARE_METATYPE(PropertyMap)
+
+inline QDBusIntrospection::Argument arg(const char* type, const char *name = 0)
+{
+ QDBusIntrospection::Argument retval;
+ retval.type = QDBusType(type);
+ retval.name = QLatin1String(name);
+ return retval;
+}
+
+template<typename T>
+inline QMap<QString, T>& operator<<(QMap<QString, T>& map, const T& m)
+{ map.insert(m.name, m); return map; }
+
+inline const char* mapName(const MethodMap&)
+{ return "MethodMap"; }
+
+inline const char* mapName(const SignalMap&)
+{ return "SignalMap"; }
+
+inline const char* mapName(const PropertyMap&)
+{ return "PropertyMap"; }
+
+QString printable(const QDBusIntrospection::Method& m)
+{
+ QString result = m.name + "(";
+ foreach (QDBusIntrospection::Argument arg, m.inputArgs)
+ result += QString("in %1 %2, ")
+ .arg(arg.type.toString(QDBusType::ConventionalNames))
+ .arg(arg.name);
+ foreach (QDBusIntrospection::Argument arg, m.outputArgs)
+ result += QString("out %1 %2, ")
+ .arg(arg.type.toString(QDBusType::ConventionalNames))
+ .arg(arg.name);
+ AnnotationsMap::const_iterator it = m.annotations.begin();
+ for ( ; it != m.annotations.end(); ++it)
+ result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
+
+ if (result.length() > 1)
+ result.truncate(result.length() - 2);
+ result += ")";
+ return result;
+}
+
+QString printable(const QDBusIntrospection::Signal& s)
+{
+ QString result = s.name + "(";
+ foreach (QDBusIntrospection::Argument arg, s.outputArgs)
+ result += QString("out %1 %2, ")
+ .arg(arg.type.toString(QDBusType::ConventionalNames))
+ .arg(arg.name);
+ AnnotationsMap::const_iterator it = s.annotations.begin();
+ for ( ; it != s.annotations.end(); ++it)
+ result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
+
+ if (result.length() > 1)
+ result.truncate(result.length() - 2);
+ result += ")";
+ return result;
+}
+
+QString printable(const QDBusIntrospection::Property& p)
+{
+ QString result;
+ if (p.access == QDBusIntrospection::Property::Read)
+ result = "read %1 %2, ";
+ else if (p.access == QDBusIntrospection::Property::Write)
+ result = "write %1 %2, ";
+ else
+ result = "readwrite %1 %2, ";
+ result = result.arg(p.type.toString(QDBusType::ConventionalNames)).arg(p.name);
+
+ AnnotationsMap::const_iterator it = p.annotations.begin();
+ for ( ; it != p.annotations.end(); ++it)
+ result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
+
+ if (result.length() > 1)
+ result.truncate(result.length() - 2);
+ return result;
+}
+
+template<typename T>
+char* printableMap(const QMap<QString, T>& map)
+{
+ QString contents = "\n";
+ typename QMap<QString, T>::const_iterator it = map.begin();
+ for ( ; it != map.end(); ++it) {
+ if (it.key() != it.value().name)
+ contents += it.value().name + ":";
+ contents += printable(it.value());
+ contents += ";\n";
+ }
+
+ QString result("%1(size = %2): {%3}");
+ return qstrdup(qPrintable(result
+ .arg(mapName(map))
+ .arg(map.size())
+ .arg(contents)));
+}
+
+namespace QTest {
+ template<>
+ inline char* toString(const MethodMap& map)
+ {
+ return printableMap(map);
+ }
+
+ template<>
+ inline char* toString(const SignalMap& map)
+ {
+ return printableMap(map);
+ }
+
+ template<>
+ inline char* toString(const PropertyMap& map)
+ {
+ return printableMap(map);
+ }
+}
+
+void tst_QDBusXmlParser::parsing_data()
+{
+ QTest::addColumn<QString>("xmlData");
+ QTest::addColumn<int>("interfaceCount");
+ QTest::addColumn<int>("objectCount");
+
+ QTest::newRow("null") << QString() << 0 << 0;
+ QTest::newRow("empty") << QString("") << 0 << 0;
+
+ QTest::newRow("junk") << "<junk/>" << 0 << 0;
+ QTest::newRow("interface-inside-junk") << "<junk><interface name=\"iface.iface1\" /></junk>"
+ << 0 << 0;
+ QTest::newRow("object-inside-junk") << "<junk><node name=\"obj1\" /></junk>"
+ << 0 << 0;
+
+ QTest::newRow("zero-interfaces") << "<node/>" << 0 << 0;
+ QTest::newRow("one-interface") << "<node><interface name=\"iface.iface1\" /></node>" << 1 << 0;
+
+
+ QTest::newRow("two-interfaces") << "<node><interface name=\"iface.iface1\" />"
+ "<interface name=\"iface.iface2\"></node>"
+ << 2 << 0;
+
+
+ QTest::newRow("one-object") << "<node><node name=\"obj1\"/></node>" << 0 << 1;
+ QTest::newRow("two-objects") << "<node><node name=\"obj1\"/><node name=\"obj2\"></node>" << 0 << 2;
+
+ QTest::newRow("i1o1") << "<node><interface name=\"iface.iface1\"><node name=\"obj1\"></node>" << 1 << 1;
+
+}
+
+void tst_QDBusXmlParser::parsing_common(const QString &xmlData)
+{
+ QDBusIntrospection::ObjectTree obj =
+ QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/");
+ QFETCH(int, interfaceCount);
+ QFETCH(int, objectCount);
+ QCOMPARE(obj.interfaces.count(), interfaceCount);
+ QCOMPARE(obj.childObjects.count(), objectCount);
+
+ // also verify the naming
+ int i = 0;
+ foreach (QString name, obj.interfaces)
+ QCOMPARE(name, QString("iface.iface%1").arg(++i));
+
+ i = 0;
+ foreach (QString name, obj.childObjects)
+ QCOMPARE(name, QString("obj%1").arg(++i));
+}
+
+void tst_QDBusXmlParser::parsing()
+{
+ QFETCH(QString, xmlData);
+
+ parsing_common(xmlData);
+}
+
+void tst_QDBusXmlParser::parsingWithDoctype_data()
+{
+ parsing_data();
+}
+
+void tst_QDBusXmlParser::parsingWithDoctype()
+{
+ QString docType = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
+ QFETCH(QString, xmlData);
+
+ parsing_common(docType + xmlData);
+}
+
+void tst_QDBusXmlParser::objectWithContent_data()
+{
+ QTest::addColumn<QString>("xmlData");
+ QTest::addColumn<QString>("probedObject");
+ QTest::addColumn<int>("interfaceCount");
+ QTest::addColumn<int>("objectCount");
+
+ QTest::newRow("zero") << "<node><node name=\"obj\"/></node>" << "obj" << 0 << 0;
+
+ QString xmlData = "<node><node name=\"obj\">"
+ "<interface name=\"iface.iface1\" />"
+ "</node></node>";
+ QTest::newRow("one-interface") << xmlData << "obj" << 1 << 0;
+ QTest::newRow("one-interface2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<interface name=\"iface.iface1\" />"
+ "<interface name=\"iface.iface2\" />"
+ "</node></node>";
+ QTest::newRow("two-interfaces") << xmlData << "obj" << 2 << 0;
+ QTest::newRow("two-interfaces2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<interface name=\"iface.iface1\" />"
+ "<interface name=\"iface.iface2\" />"
+ "</node><node name=\"obj2\">"
+ "<interface name=\"iface.iface1\" />"
+ "</node></node>";
+ QTest::newRow("two-nodes-two-interfaces") << xmlData << "obj" << 2 << 0;
+ QTest::newRow("two-nodes-one-interface") << xmlData << "obj2" << 1 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<node name=\"obj1\" />"
+ "</node></node>";
+ QTest::newRow("one-object") << xmlData << "obj" << 0 << 1;
+ QTest::newRow("one-object2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<node name=\"obj1\" />"
+ "<node name=\"obj2\" />"
+ "</node></node>";
+ QTest::newRow("two-objects") << xmlData << "obj" << 0 << 2;
+ QTest::newRow("two-objects2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<node name=\"obj1\" />"
+ "<node name=\"obj2\" />"
+ "</node><node name=\"obj2\">"
+ "<node name=\"obj1\" />"
+ "</node></node>";
+ QTest::newRow("two-nodes-two-objects") << xmlData << "obj" << 0 << 2;
+ QTest::newRow("two-nodes-one-object") << xmlData << "obj2" << 0 << 1;
+}
+
+void tst_QDBusXmlParser::objectWithContent()
+{
+ QFETCH(QString, xmlData);
+ QFETCH(QString, probedObject);
+
+ QDBusIntrospection::ObjectTree tree =
+ QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/");
+
+ const ObjectMap &om = tree.childObjectData;
+
+ if (om.contains(probedObject)) {
+ const QSharedDataPointer<QDBusIntrospection::ObjectTree>& obj = om.value(probedObject);
+ QVERIFY(obj != 0);
+
+ QFETCH(int, interfaceCount);
+ QFETCH(int, objectCount);
+
+ QCOMPARE(obj->interfaces.count(), interfaceCount);
+ QCOMPARE(obj->childObjects.count(), objectCount);
+
+ // verify the object names
+ int i = 0;
+ foreach (QString name, obj->interfaces)
+ QCOMPARE(name, QString("iface.iface%1").arg(++i));
+
+ i = 0;
+ foreach (QString name, obj->childObjects)
+ QCOMPARE(name, QString("obj%1").arg(++i));
+ }
+}
+
+void tst_QDBusXmlParser::methods_data()
+{
+ QTest::addColumn<QString>("xmlDataFragment");
+ QTest::addColumn<MethodMap>("methodMap");
+
+ MethodMap map;
+ QTest::newRow("no-methods") << QString() << map;
+
+ // one method without arguments
+ QDBusIntrospection::Method method;
+ method.name = "Foo";
+ map << method;
+ QTest::newRow("one-method") << "<method name=\"Foo\"/>" << map;
+
+ // add another method without arguments
+ method.name = "Bar";
+ map << method;
+ QTest::newRow("two-methods") << "<method name=\"Foo\"/>"
+ "<method name=\"Bar\"/>"
+ << map;
+
+ // invert the order of the XML declaration
+ QTest::newRow("two-methods-inverse") << "<method name=\"Bar\"/>"
+ "<method name=\"Foo\"/>"
+ << map;
+
+ // add a third, with annotations
+ method.name = "Baz";
+ method.annotations.insert("foo.testing", "nothing to see here");
+ map << method;
+ QTest::newRow("method-with-annotation") <<
+ "<method name=\"Foo\"/>"
+ "<method name=\"Bar\"/>"
+ "<method name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></method>"
+ << map;
+
+ // arguments
+ map.clear();
+ method.annotations.clear();
+
+ method.name = "Method";
+ method.inputArgs << arg("s");
+ map << method;
+ QTest::newRow("one-in") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // two arguments
+ method.inputArgs << arg("v");
+ map.clear();
+ map << method;
+ QTest::newRow("two-in") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "<arg type=\"v\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // one invalid arg
+ QTest::newRow("two-in-one-invalid") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "<arg type=\"~\" name=\"invalid\" direction=\"in\"/>" // this line should be ignored
+ "<arg type=\"v\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // one out argument
+ method.inputArgs.clear();
+ method.outputArgs << arg("s");
+ map.clear();
+ map << method;
+ QTest::newRow("one-out") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "</method>" << map;
+
+ // two in and one out
+ method.inputArgs << arg("s") << arg("v");
+ map.clear();
+ map << method;
+ QTest::newRow("two-in-one-out") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "<arg type=\"v\" direction=\"in\"/>"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "</method>" << map;
+
+ // let's try an arg with name
+ method.outputArgs.clear();
+ method.inputArgs.clear();
+ method.inputArgs << arg("s", "foo");
+ map.clear();
+ map << method;
+ QTest::newRow("one-in-with-name") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" name=\"foo\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // two args with name
+ method.inputArgs << arg("i", "bar");
+ map.clear();
+ map << method;
+ QTest::newRow("two-in-with-name") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" name=\"foo\" direction=\"in\"/>"
+ "<arg type=\"i\" name=\"bar\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // one complex
+ map.clear();
+ method = QDBusIntrospection::Method();
+
+ // Method1(in STRING arg1, in BYTE arg2, out ARRAY of STRING)
+ method.inputArgs << arg("s", "arg1") << arg("y", "arg2");
+ method.outputArgs << arg("as");
+ method.name = "Method1";
+ map << method;
+
+ // Method2(in ARRAY of DICT_ENTRY of (STRING,VARIANT) variantMap, in UINT32 index,
+ // out STRING key, out VARIANT value)
+ // with annotation "foo.equivalent":"QVariantMap"
+ method = QDBusIntrospection::Method();
+ method.inputArgs << arg("a{sv}", "variantMap") << arg("u", "index");
+ method.outputArgs << arg("s", "key") << arg("v", "value");
+ method.annotations.insert("foo.equivalent", "QVariantMap");
+ method.name = "Method2";
+ map << method;
+
+ QTest::newRow("complex") <<
+ "<method name=\"Method1\">"
+ "<arg name=\"arg1\" type=\"s\" direction=\"in\"/>"
+ "<arg name=\"arg2\" type=\"y\" direction=\"in\"/>"
+ "<arg type=\"as\" direction=\"out\"/>"
+ "</method>"
+ "<method name=\"Method2\">"
+ "<arg name=\"variantMap\" type=\"a{sv}\" direction=\"in\"/>"
+ "<arg name=\"index\" type=\"u\" direction=\"in\"/>"
+ "<arg name=\"key\" type=\"s\" direction=\"out\"/>"
+ "<arg name=\"value\" type=\"v\" direction=\"out\"/>"
+ "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>"
+ "</method>" << map;
+}
+
+void tst_QDBusXmlParser::methods()
+{
+ QString xmlHeader = "<node>"
+ "<interface name=\"iface.iface1\">",
+ xmlFooter = "</interface>"
+ "</node>";
+
+ QFETCH(QString, xmlDataFragment);
+
+ QDBusIntrospection::Interface iface =
+ QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+ QCOMPARE(iface.name, QString("iface.iface1"));
+
+ QFETCH(MethodMap, methodMap);
+ MethodMap parsedMap = iface.methods;
+
+ QCOMPARE(methodMap.count(), parsedMap.count());
+ QCOMPARE(methodMap, parsedMap);
+}
+
+void tst_QDBusXmlParser::signals__data()
+{
+ QTest::addColumn<QString>("xmlDataFragment");
+ QTest::addColumn<SignalMap>("signalMap");
+
+ SignalMap map;
+ QTest::newRow("no-signals") << QString() << map;
+
+ // one signal without arguments
+ QDBusIntrospection::Signal signal;
+ signal.name = "Foo";
+ map << signal;
+ QTest::newRow("one-signal") << "<signal name=\"Foo\"/>" << map;
+
+ // add another signal without arguments
+ signal.name = "Bar";
+ map << signal;
+ QTest::newRow("two-signals") << "<signal name=\"Foo\"/>"
+ "<signal name=\"Bar\"/>"
+ << map;
+
+ // invert the order of the XML declaration
+ QTest::newRow("two-signals-inverse") << "<signal name=\"Bar\"/>"
+ "<signal name=\"Foo\"/>"
+ << map;
+
+ // add a third, with annotations
+ signal.name = "Baz";
+ signal.annotations.insert("foo.testing", "nothing to see here");
+ map << signal;
+ QTest::newRow("signal-with-annotation") <<
+ "<signal name=\"Foo\"/>"
+ "<signal name=\"Bar\"/>"
+ "<signal name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></signal>"
+ << map;
+
+ // one out argument
+ map.clear();
+ signal.annotations.clear();
+ signal.outputArgs << arg("s");
+ signal.name = "Signal";
+ map.clear();
+ map << signal;
+ QTest::newRow("one-out") <<
+ "<signal name=\"Signal\">"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "</signal>" << map;
+
+ // without saying which direction it is
+ QTest::newRow("one-out-no-direction") <<
+ "<signal name=\"Signal\">"
+ "<arg type=\"s\"/>"
+ "</signal>" << map;
+
+ // two args with name
+ signal.outputArgs << arg("i", "bar");
+ map.clear();
+ map << signal;
+ QTest::newRow("two-out-with-name") <<
+ "<signal name=\"Signal\">"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "<arg type=\"i\" name=\"bar\"/>"
+ "</signal>" << map;
+
+ // one complex
+ map.clear();
+ signal = QDBusIntrospection::Signal();
+
+ // Signal1(out ARRAY of STRING)
+ signal.outputArgs << arg("as");
+ signal.name = "Signal1";
+ map << signal;
+
+ // Signal2(out STRING key, out VARIANT value)
+ // with annotation "foo.equivalent":"QVariantMap"
+ signal = QDBusIntrospection::Signal();
+ signal.outputArgs << arg("s", "key") << arg("v", "value");
+ signal.annotations.insert("foo.equivalent", "QVariantMap");
+ signal.name = "Signal2";
+ map << signal;
+
+ QTest::newRow("complex") <<
+ "<signal name=\"Signal1\">"
+ "<arg type=\"as\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"Signal2\">"
+ "<arg name=\"key\" type=\"s\" direction=\"out\"/>"
+ "<arg name=\"value\" type=\"v\" direction=\"out\"/>"
+ "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>"
+ "</signal>" << map;
+}
+
+void tst_QDBusXmlParser::signals_()
+{
+ QString xmlHeader = "<node>"
+ "<interface name=\"iface.iface1\">",
+ xmlFooter = "</interface>"
+ "</node>";
+
+ QFETCH(QString, xmlDataFragment);
+
+ QDBusIntrospection::Interface iface =
+ QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+ QCOMPARE(iface.name, QString("iface.iface1"));
+
+ QFETCH(SignalMap, signalMap);
+ SignalMap parsedMap = iface.signals_;
+
+ QCOMPARE(signalMap.count(), parsedMap.count());
+ QCOMPARE(signalMap, parsedMap);
+}
+
+void tst_QDBusXmlParser::properties_data()
+{
+ QTest::addColumn<QString>("xmlDataFragment");
+ QTest::addColumn<PropertyMap>("propertyMap");
+
+ PropertyMap map;
+ QTest::newRow("no-signals") << QString() << map;
+
+ // one readable signal
+ QDBusIntrospection::Property prop;
+ prop.name = "foo";
+ prop.type = QDBusType("s");
+ prop.access = QDBusIntrospection::Property::Read;
+ map << prop;
+ QTest::newRow("one-readable") << "<property name=\"foo\" type=\"s\" access=\"read\"/>" << map;
+
+ // one writable signal
+ prop.access = QDBusIntrospection::Property::Write;
+ map.clear();
+ map << prop;
+ QTest::newRow("one-writable") << "<property name=\"foo\" type=\"s\" access=\"write\"/>" << map;
+
+ // one read- & writable signal
+ prop.access = QDBusIntrospection::Property::ReadWrite;
+ map.clear();
+ map << prop;
+ QTest::newRow("one-read-writable") << "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>"
+ << map;
+
+ // two, mixed properties
+ prop.name = "bar";
+ prop.type = QDBusType("i");
+ prop.access = QDBusIntrospection::Property::Read;
+ map << prop;
+ QTest::newRow("two") <<
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>"
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>" << map;
+
+ // invert the order of the declaration
+ QTest::newRow("two") <<
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+
+ // add a third with annotations
+ prop.name = "baz";
+ prop.type = QDBusType("as");
+ prop.access = QDBusIntrospection::Property::Write;
+ prop.annotations.insert("foo.annotation", "Hello, World");
+ prop.annotations.insert("foo.annotation2", "Goodbye, World");
+ map << prop;
+ QTest::newRow("complex") <<
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+ "<property name=\"baz\" type=\"as\" access=\"write\">"
+ "<annotation name=\"foo.annotation\" value=\"Hello, World\" />"
+ "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />"
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+
+ // and now change the order
+ QTest::newRow("complex2") <<
+ "<property name=\"baz\" type=\"as\" access=\"write\">"
+ "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />"
+ "<annotation name=\"foo.annotation\" value=\"Hello, World\" />"
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+}
+
+void tst_QDBusXmlParser::properties()
+{
+ QString xmlHeader = "<node>"
+ "<interface name=\"iface.iface1\">",
+ xmlFooter = "</interface>"
+ "</node>";
+
+ QFETCH(QString, xmlDataFragment);
+
+ QDBusIntrospection::Interface iface =
+ QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+ QCOMPARE(iface.name, QString("iface.iface1"));
+
+ QFETCH(PropertyMap, propertyMap);
+ PropertyMap parsedMap = iface.properties;
+
+ QCOMPARE(propertyMap.count(), parsedMap.count());
+ QCOMPARE(propertyMap, parsedMap);
+}
+
+QTEST_MAIN(tst_QDBusXmlParser)
+
+#include "tst_qdbusxmlparser.moc"