summaryrefslogtreecommitdiffstats
path: root/test/qt/tst_qdbusxmlparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/qt/tst_qdbusxmlparser.cpp')
-rw-r--r--test/qt/tst_qdbusxmlparser.cpp709
1 files changed, 709 insertions, 0 deletions
diff --git a/test/qt/tst_qdbusxmlparser.cpp b/test/qt/tst_qdbusxmlparser.cpp
new file mode 100644
index 00000000..c7337c3a
--- /dev/null
+++ b/test/qt/tst_qdbusxmlparser.cpp
@@ -0,0 +1,709 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <qcoreapplication.h>
+#include <qmetatype.h>
+#include <QtTest/QtTest>
+
+#include <dbus/qdbus.h>
+
+class tst_QDBusXmlParser: public QObject
+{
+ Q_OBJECT
+
+private:
+ void parsing_common(const QString&);
+
+private slots:
+ void parsing_data();
+ void parsing();
+ void parsingWithDoctype_data();
+ void parsingWithDoctype();
+
+ void objectWithContent_data();
+ void objectWithContent();
+
+ void methods_data();
+ void methods();
+ void signals__data();
+ void signals_();
+ void properties_data();
+ void properties();
+};
+
+// just to make it easier:
+typedef QDBusIntrospection::Interfaces InterfaceMap;
+typedef QDBusIntrospection::Objects ObjectMap;
+typedef QDBusIntrospection::Arguments ArgumentList;
+typedef QDBusIntrospection::Annotations AnnotationsMap;
+typedef QDBusIntrospection::Methods MethodMap;
+typedef QDBusIntrospection::Signals SignalMap;
+typedef QDBusIntrospection::Properties PropertyMap;
+
+Q_DECLARE_METATYPE(QDBusIntrospection::Method)
+Q_DECLARE_METATYPE(QDBusIntrospection::Signal)
+Q_DECLARE_METATYPE(QDBusIntrospection::Property)
+Q_DECLARE_METATYPE(MethodMap)
+Q_DECLARE_METATYPE(SignalMap)
+Q_DECLARE_METATYPE(PropertyMap)
+
+inline QDBusIntrospection::Argument arg(const char* type, const char *name = 0)
+{
+ QDBusIntrospection::Argument retval;
+ retval.type = QDBusType(type);
+ retval.name = QLatin1String(name);
+ return retval;
+}
+
+template<typename T>
+inline QMap<QString, T>& operator<<(QMap<QString, T>& map, const T& m)
+{ map.insert(m.name, m); return map; }
+
+inline const char* mapName(const MethodMap&)
+{ return "MethodMap"; }
+
+inline const char* mapName(const SignalMap&)
+{ return "SignalMap"; }
+
+inline const char* mapName(const PropertyMap&)
+{ return "PropertyMap"; }
+
+QString printable(const QDBusIntrospection::Method& m)
+{
+ QString result = m.name + "(";
+ foreach (QDBusIntrospection::Argument arg, m.inputArgs)
+ result += QString("in %1 %2, ")
+ .arg(arg.type.toString(QDBusType::ConventionalNames))
+ .arg(arg.name);
+ foreach (QDBusIntrospection::Argument arg, m.outputArgs)
+ result += QString("out %1 %2, ")
+ .arg(arg.type.toString(QDBusType::ConventionalNames))
+ .arg(arg.name);
+ AnnotationsMap::const_iterator it = m.annotations.begin();
+ for ( ; it != m.annotations.end(); ++it)
+ result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
+
+ if (result.length() > 1)
+ result.truncate(result.length() - 2);
+ result += ")";
+ return result;
+}
+
+QString printable(const QDBusIntrospection::Signal& s)
+{
+ QString result = s.name + "(";
+ foreach (QDBusIntrospection::Argument arg, s.outputArgs)
+ result += QString("out %1 %2, ")
+ .arg(arg.type.toString(QDBusType::ConventionalNames))
+ .arg(arg.name);
+ AnnotationsMap::const_iterator it = s.annotations.begin();
+ for ( ; it != s.annotations.end(); ++it)
+ result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
+
+ if (result.length() > 1)
+ result.truncate(result.length() - 2);
+ result += ")";
+ return result;
+}
+
+QString printable(const QDBusIntrospection::Property& p)
+{
+ QString result;
+ if (p.access == QDBusIntrospection::Property::Read)
+ result = "read %1 %2, ";
+ else if (p.access == QDBusIntrospection::Property::Write)
+ result = "write %1 %2, ";
+ else
+ result = "readwrite %1 %2, ";
+ result = result.arg(p.type.toString(QDBusType::ConventionalNames)).arg(p.name);
+
+ AnnotationsMap::const_iterator it = p.annotations.begin();
+ for ( ; it != p.annotations.end(); ++it)
+ result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
+
+ if (result.length() > 1)
+ result.truncate(result.length() - 2);
+ return result;
+}
+
+template<typename T>
+char* printableMap(const QMap<QString, T>& map)
+{
+ QString contents = "\n";
+ typename QMap<QString, T>::const_iterator it = map.begin();
+ for ( ; it != map.end(); ++it) {
+ if (it.key() != it.value().name)
+ contents += it.value().name + ":";
+ contents += printable(it.value());
+ contents += ";\n";
+ }
+
+ QString result("%1(size = %2): {%3}");
+ return qstrdup(qPrintable(result
+ .arg(mapName(map))
+ .arg(map.size())
+ .arg(contents)));
+}
+
+namespace QTest {
+ template<>
+ inline char* toString(const MethodMap& map)
+ {
+ return printableMap(map);
+ }
+
+ template<>
+ inline char* toString(const SignalMap& map)
+ {
+ return printableMap(map);
+ }
+
+ template<>
+ inline char* toString(const PropertyMap& map)
+ {
+ return printableMap(map);
+ }
+}
+
+void tst_QDBusXmlParser::parsing_data()
+{
+ QTest::addColumn<QString>("xmlData");
+ QTest::addColumn<int>("interfaceCount");
+ QTest::addColumn<int>("objectCount");
+
+ QTest::newRow("null") << QString() << 0 << 0;
+ QTest::newRow("empty") << QString("") << 0 << 0;
+
+ QTest::newRow("junk") << "<junk/>" << 0 << 0;
+ QTest::newRow("interface-inside-junk") << "<junk><interface name=\"iface.iface1\" /></junk>"
+ << 0 << 0;
+ QTest::newRow("object-inside-junk") << "<junk><node name=\"obj1\" /></junk>"
+ << 0 << 0;
+
+ QTest::newRow("zero-interfaces") << "<node/>" << 0 << 0;
+ QTest::newRow("one-interface") << "<node><interface name=\"iface.iface1\" /></node>" << 1 << 0;
+
+
+ QTest::newRow("two-interfaces") << "<node><interface name=\"iface.iface1\" />"
+ "<interface name=\"iface.iface2\"></node>"
+ << 2 << 0;
+
+
+ QTest::newRow("one-object") << "<node><node name=\"obj1\"/></node>" << 0 << 1;
+ QTest::newRow("two-objects") << "<node><node name=\"obj1\"/><node name=\"obj2\"></node>" << 0 << 2;
+
+ QTest::newRow("i1o1") << "<node><interface name=\"iface.iface1\"><node name=\"obj1\"></node>" << 1 << 1;
+
+}
+
+void tst_QDBusXmlParser::parsing_common(const QString &xmlData)
+{
+ QDBusIntrospection::ObjectTree obj =
+ QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/");
+ QFETCH(int, interfaceCount);
+ QFETCH(int, objectCount);
+ QCOMPARE(obj.interfaces.count(), interfaceCount);
+ QCOMPARE(obj.childObjects.count(), objectCount);
+
+ // also verify the naming
+ int i = 0;
+ foreach (QString name, obj.interfaces)
+ QCOMPARE(name, QString("iface.iface%1").arg(++i));
+
+ i = 0;
+ foreach (QString name, obj.childObjects)
+ QCOMPARE(name, QString("obj%1").arg(++i));
+}
+
+void tst_QDBusXmlParser::parsing()
+{
+ QFETCH(QString, xmlData);
+
+ parsing_common(xmlData);
+}
+
+void tst_QDBusXmlParser::parsingWithDoctype_data()
+{
+ parsing_data();
+}
+
+void tst_QDBusXmlParser::parsingWithDoctype()
+{
+ QString docType = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
+ QFETCH(QString, xmlData);
+
+ parsing_common(docType + xmlData);
+}
+
+void tst_QDBusXmlParser::objectWithContent_data()
+{
+ QTest::addColumn<QString>("xmlData");
+ QTest::addColumn<QString>("probedObject");
+ QTest::addColumn<int>("interfaceCount");
+ QTest::addColumn<int>("objectCount");
+
+ QTest::newRow("zero") << "<node><node name=\"obj\"/></node>" << "obj" << 0 << 0;
+
+ QString xmlData = "<node><node name=\"obj\">"
+ "<interface name=\"iface.iface1\" />"
+ "</node></node>";
+ QTest::newRow("one-interface") << xmlData << "obj" << 1 << 0;
+ QTest::newRow("one-interface2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<interface name=\"iface.iface1\" />"
+ "<interface name=\"iface.iface2\" />"
+ "</node></node>";
+ QTest::newRow("two-interfaces") << xmlData << "obj" << 2 << 0;
+ QTest::newRow("two-interfaces2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<interface name=\"iface.iface1\" />"
+ "<interface name=\"iface.iface2\" />"
+ "</node><node name=\"obj2\">"
+ "<interface name=\"iface.iface1\" />"
+ "</node></node>";
+ QTest::newRow("two-nodes-two-interfaces") << xmlData << "obj" << 2 << 0;
+ QTest::newRow("two-nodes-one-interface") << xmlData << "obj2" << 1 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<node name=\"obj1\" />"
+ "</node></node>";
+ QTest::newRow("one-object") << xmlData << "obj" << 0 << 1;
+ QTest::newRow("one-object2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<node name=\"obj1\" />"
+ "<node name=\"obj2\" />"
+ "</node></node>";
+ QTest::newRow("two-objects") << xmlData << "obj" << 0 << 2;
+ QTest::newRow("two-objects2") << xmlData << "obj2" << 0 << 0;
+
+ xmlData = "<node><node name=\"obj\">"
+ "<node name=\"obj1\" />"
+ "<node name=\"obj2\" />"
+ "</node><node name=\"obj2\">"
+ "<node name=\"obj1\" />"
+ "</node></node>";
+ QTest::newRow("two-nodes-two-objects") << xmlData << "obj" << 0 << 2;
+ QTest::newRow("two-nodes-one-object") << xmlData << "obj2" << 0 << 1;
+}
+
+void tst_QDBusXmlParser::objectWithContent()
+{
+ QFETCH(QString, xmlData);
+ QFETCH(QString, probedObject);
+
+ QDBusIntrospection::ObjectTree tree =
+ QDBusIntrospection::parseObjectTree(xmlData, "local.testing", "/");
+
+ const ObjectMap &om = tree.childObjectData;
+
+ if (om.contains(probedObject)) {
+ const QSharedDataPointer<QDBusIntrospection::ObjectTree>& obj = om.value(probedObject);
+ QVERIFY(obj != 0);
+
+ QFETCH(int, interfaceCount);
+ QFETCH(int, objectCount);
+
+ QCOMPARE(obj->interfaces.count(), interfaceCount);
+ QCOMPARE(obj->childObjects.count(), objectCount);
+
+ // verify the object names
+ int i = 0;
+ foreach (QString name, obj->interfaces)
+ QCOMPARE(name, QString("iface.iface%1").arg(++i));
+
+ i = 0;
+ foreach (QString name, obj->childObjects)
+ QCOMPARE(name, QString("obj%1").arg(++i));
+ }
+}
+
+void tst_QDBusXmlParser::methods_data()
+{
+ QTest::addColumn<QString>("xmlDataFragment");
+ QTest::addColumn<MethodMap>("methodMap");
+
+ MethodMap map;
+ QTest::newRow("no-methods") << QString() << map;
+
+ // one method without arguments
+ QDBusIntrospection::Method method;
+ method.name = "Foo";
+ map << method;
+ QTest::newRow("one-method") << "<method name=\"Foo\"/>" << map;
+
+ // add another method without arguments
+ method.name = "Bar";
+ map << method;
+ QTest::newRow("two-methods") << "<method name=\"Foo\"/>"
+ "<method name=\"Bar\"/>"
+ << map;
+
+ // invert the order of the XML declaration
+ QTest::newRow("two-methods-inverse") << "<method name=\"Bar\"/>"
+ "<method name=\"Foo\"/>"
+ << map;
+
+ // add a third, with annotations
+ method.name = "Baz";
+ method.annotations.insert("foo.testing", "nothing to see here");
+ map << method;
+ QTest::newRow("method-with-annotation") <<
+ "<method name=\"Foo\"/>"
+ "<method name=\"Bar\"/>"
+ "<method name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></method>"
+ << map;
+
+ // arguments
+ map.clear();
+ method.annotations.clear();
+
+ method.name = "Method";
+ method.inputArgs << arg("s");
+ map << method;
+ QTest::newRow("one-in") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // two arguments
+ method.inputArgs << arg("v");
+ map.clear();
+ map << method;
+ QTest::newRow("two-in") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "<arg type=\"v\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // one invalid arg
+ QTest::newRow("two-in-one-invalid") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "<arg type=\"~\" name=\"invalid\" direction=\"in\"/>" // this line should be ignored
+ "<arg type=\"v\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // one out argument
+ method.inputArgs.clear();
+ method.outputArgs << arg("s");
+ map.clear();
+ map << method;
+ QTest::newRow("one-out") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "</method>" << map;
+
+ // two in and one out
+ method.inputArgs << arg("s") << arg("v");
+ map.clear();
+ map << method;
+ QTest::newRow("two-in-one-out") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" direction=\"in\"/>"
+ "<arg type=\"v\" direction=\"in\"/>"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "</method>" << map;
+
+ // let's try an arg with name
+ method.outputArgs.clear();
+ method.inputArgs.clear();
+ method.inputArgs << arg("s", "foo");
+ map.clear();
+ map << method;
+ QTest::newRow("one-in-with-name") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" name=\"foo\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // two args with name
+ method.inputArgs << arg("i", "bar");
+ map.clear();
+ map << method;
+ QTest::newRow("two-in-with-name") <<
+ "<method name=\"Method\">"
+ "<arg type=\"s\" name=\"foo\" direction=\"in\"/>"
+ "<arg type=\"i\" name=\"bar\" direction=\"in\"/>"
+ "</method>" << map;
+
+ // one complex
+ map.clear();
+ method = QDBusIntrospection::Method();
+
+ // Method1(in STRING arg1, in BYTE arg2, out ARRAY of STRING)
+ method.inputArgs << arg("s", "arg1") << arg("y", "arg2");
+ method.outputArgs << arg("as");
+ method.name = "Method1";
+ map << method;
+
+ // Method2(in ARRAY of DICT_ENTRY of (STRING,VARIANT) variantMap, in UINT32 index,
+ // out STRING key, out VARIANT value)
+ // with annotation "foo.equivalent":"QVariantMap"
+ method = QDBusIntrospection::Method();
+ method.inputArgs << arg("a{sv}", "variantMap") << arg("u", "index");
+ method.outputArgs << arg("s", "key") << arg("v", "value");
+ method.annotations.insert("foo.equivalent", "QVariantMap");
+ method.name = "Method2";
+ map << method;
+
+ QTest::newRow("complex") <<
+ "<method name=\"Method1\">"
+ "<arg name=\"arg1\" type=\"s\" direction=\"in\"/>"
+ "<arg name=\"arg2\" type=\"y\" direction=\"in\"/>"
+ "<arg type=\"as\" direction=\"out\"/>"
+ "</method>"
+ "<method name=\"Method2\">"
+ "<arg name=\"variantMap\" type=\"a{sv}\" direction=\"in\"/>"
+ "<arg name=\"index\" type=\"u\" direction=\"in\"/>"
+ "<arg name=\"key\" type=\"s\" direction=\"out\"/>"
+ "<arg name=\"value\" type=\"v\" direction=\"out\"/>"
+ "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>"
+ "</method>" << map;
+}
+
+void tst_QDBusXmlParser::methods()
+{
+ QString xmlHeader = "<node>"
+ "<interface name=\"iface.iface1\">",
+ xmlFooter = "</interface>"
+ "</node>";
+
+ QFETCH(QString, xmlDataFragment);
+
+ QDBusIntrospection::Interface iface =
+ QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+ QCOMPARE(iface.name, QString("iface.iface1"));
+
+ QFETCH(MethodMap, methodMap);
+ MethodMap parsedMap = iface.methods;
+
+ QCOMPARE(methodMap.count(), parsedMap.count());
+ QCOMPARE(methodMap, parsedMap);
+}
+
+void tst_QDBusXmlParser::signals__data()
+{
+ QTest::addColumn<QString>("xmlDataFragment");
+ QTest::addColumn<SignalMap>("signalMap");
+
+ SignalMap map;
+ QTest::newRow("no-signals") << QString() << map;
+
+ // one signal without arguments
+ QDBusIntrospection::Signal signal;
+ signal.name = "Foo";
+ map << signal;
+ QTest::newRow("one-signal") << "<signal name=\"Foo\"/>" << map;
+
+ // add another signal without arguments
+ signal.name = "Bar";
+ map << signal;
+ QTest::newRow("two-signals") << "<signal name=\"Foo\"/>"
+ "<signal name=\"Bar\"/>"
+ << map;
+
+ // invert the order of the XML declaration
+ QTest::newRow("two-signals-inverse") << "<signal name=\"Bar\"/>"
+ "<signal name=\"Foo\"/>"
+ << map;
+
+ // add a third, with annotations
+ signal.name = "Baz";
+ signal.annotations.insert("foo.testing", "nothing to see here");
+ map << signal;
+ QTest::newRow("signal-with-annotation") <<
+ "<signal name=\"Foo\"/>"
+ "<signal name=\"Bar\"/>"
+ "<signal name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\"></signal>"
+ << map;
+
+ // one out argument
+ map.clear();
+ signal.annotations.clear();
+ signal.outputArgs << arg("s");
+ signal.name = "Signal";
+ map.clear();
+ map << signal;
+ QTest::newRow("one-out") <<
+ "<signal name=\"Signal\">"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "</signal>" << map;
+
+ // without saying which direction it is
+ QTest::newRow("one-out-no-direction") <<
+ "<signal name=\"Signal\">"
+ "<arg type=\"s\"/>"
+ "</signal>" << map;
+
+ // two args with name
+ signal.outputArgs << arg("i", "bar");
+ map.clear();
+ map << signal;
+ QTest::newRow("two-out-with-name") <<
+ "<signal name=\"Signal\">"
+ "<arg type=\"s\" direction=\"out\"/>"
+ "<arg type=\"i\" name=\"bar\"/>"
+ "</signal>" << map;
+
+ // one complex
+ map.clear();
+ signal = QDBusIntrospection::Signal();
+
+ // Signal1(out ARRAY of STRING)
+ signal.outputArgs << arg("as");
+ signal.name = "Signal1";
+ map << signal;
+
+ // Signal2(out STRING key, out VARIANT value)
+ // with annotation "foo.equivalent":"QVariantMap"
+ signal = QDBusIntrospection::Signal();
+ signal.outputArgs << arg("s", "key") << arg("v", "value");
+ signal.annotations.insert("foo.equivalent", "QVariantMap");
+ signal.name = "Signal2";
+ map << signal;
+
+ QTest::newRow("complex") <<
+ "<signal name=\"Signal1\">"
+ "<arg type=\"as\" direction=\"out\"/>"
+ "</signal>"
+ "<signal name=\"Signal2\">"
+ "<arg name=\"key\" type=\"s\" direction=\"out\"/>"
+ "<arg name=\"value\" type=\"v\" direction=\"out\"/>"
+ "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>"
+ "</signal>" << map;
+}
+
+void tst_QDBusXmlParser::signals_()
+{
+ QString xmlHeader = "<node>"
+ "<interface name=\"iface.iface1\">",
+ xmlFooter = "</interface>"
+ "</node>";
+
+ QFETCH(QString, xmlDataFragment);
+
+ QDBusIntrospection::Interface iface =
+ QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+ QCOMPARE(iface.name, QString("iface.iface1"));
+
+ QFETCH(SignalMap, signalMap);
+ SignalMap parsedMap = iface.signals_;
+
+ QCOMPARE(signalMap.count(), parsedMap.count());
+ QCOMPARE(signalMap, parsedMap);
+}
+
+void tst_QDBusXmlParser::properties_data()
+{
+ QTest::addColumn<QString>("xmlDataFragment");
+ QTest::addColumn<PropertyMap>("propertyMap");
+
+ PropertyMap map;
+ QTest::newRow("no-signals") << QString() << map;
+
+ // one readable signal
+ QDBusIntrospection::Property prop;
+ prop.name = "foo";
+ prop.type = QDBusType("s");
+ prop.access = QDBusIntrospection::Property::Read;
+ map << prop;
+ QTest::newRow("one-readable") << "<property name=\"foo\" type=\"s\" access=\"read\"/>" << map;
+
+ // one writable signal
+ prop.access = QDBusIntrospection::Property::Write;
+ map.clear();
+ map << prop;
+ QTest::newRow("one-writable") << "<property name=\"foo\" type=\"s\" access=\"write\"/>" << map;
+
+ // one read- & writable signal
+ prop.access = QDBusIntrospection::Property::ReadWrite;
+ map.clear();
+ map << prop;
+ QTest::newRow("one-read-writable") << "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>"
+ << map;
+
+ // two, mixed properties
+ prop.name = "bar";
+ prop.type = QDBusType("i");
+ prop.access = QDBusIntrospection::Property::Read;
+ map << prop;
+ QTest::newRow("two") <<
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>"
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>" << map;
+
+ // invert the order of the declaration
+ QTest::newRow("two") <<
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+
+ // add a third with annotations
+ prop.name = "baz";
+ prop.type = QDBusType("as");
+ prop.access = QDBusIntrospection::Property::Write;
+ prop.annotations.insert("foo.annotation", "Hello, World");
+ prop.annotations.insert("foo.annotation2", "Goodbye, World");
+ map << prop;
+ QTest::newRow("complex") <<
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+ "<property name=\"baz\" type=\"as\" access=\"write\">"
+ "<annotation name=\"foo.annotation\" value=\"Hello, World\" />"
+ "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />"
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+
+ // and now change the order
+ QTest::newRow("complex2") <<
+ "<property name=\"baz\" type=\"as\" access=\"write\">"
+ "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />"
+ "<annotation name=\"foo.annotation\" value=\"Hello, World\" />"
+ "<property name=\"bar\" type=\"i\" access=\"read\"/>"
+ "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map;
+}
+
+void tst_QDBusXmlParser::properties()
+{
+ QString xmlHeader = "<node>"
+ "<interface name=\"iface.iface1\">",
+ xmlFooter = "</interface>"
+ "</node>";
+
+ QFETCH(QString, xmlDataFragment);
+
+ QDBusIntrospection::Interface iface =
+ QDBusIntrospection::parseInterface(xmlHeader + xmlDataFragment + xmlFooter);
+
+ QCOMPARE(iface.name, QString("iface.iface1"));
+
+ QFETCH(PropertyMap, propertyMap);
+ PropertyMap parsedMap = iface.properties;
+
+ QCOMPARE(propertyMap.count(), parsedMap.count());
+ QCOMPARE(propertyMap, parsedMap);
+}
+
+QTEST_MAIN(tst_QDBusXmlParser)
+
+#include "tst_qdbusxmlparser.moc"