tests/auto/qdbusxmlparser/tst_qdbusxmlparser.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qdbusxmlparser/tst_qdbusxmlparser.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/* -*- C++ -*-
+ */
+#include <qcoreapplication.h>
+#include <qmetatype.h>
+#include <QtTest/QtTest>
+#include <QtDBus/QtDBus>
+
+#define USE_PRIVATE_CODE
+#include "../qdbusmarshall/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);
+
+    if (strcmp(QTest::currentDataTag(), "two-in-one-invalid") == 0)
+        QTest::ignoreMessage(QtWarningMsg, "Invalid D-BUS type signature '~' found while parsing introspection");
+    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"