| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the test suite of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | /* -*- C++ -*- |
| 29 | */ |
| 30 | #include <qcoreapplication.h> |
| 31 | #include <qmetatype.h> |
| 32 | #include <QtTest/QtTest> |
| 33 | #include <QtDBus/QtDBus> |
| 34 | #include <QtXml/QDomDocument> |
| 35 | |
| 36 | #define USE_PRIVATE_CODE |
| 37 | #include "../qdbusmarshall/common.h" |
| 38 | |
| 39 | class tst_QDBusXmlParser: public QObject |
| 40 | { |
| 41 | Q_OBJECT |
| 42 | |
| 43 | private: |
| 44 | void parsing_common(const QString&); |
| 45 | QString clean_xml(const QString&); |
| 46 | |
| 47 | private slots: |
| 48 | void initTestCase(); |
| 49 | void parsing_data(); |
| 50 | void parsing(); |
| 51 | void parsingWithDoctype_data(); |
| 52 | void parsingWithDoctype(); |
| 53 | |
| 54 | void methods_data(); |
| 55 | void methods(); |
| 56 | void signals__data(); |
| 57 | void signals_(); |
| 58 | void properties_data(); |
| 59 | void properties(); |
| 60 | }; |
| 61 | |
| 62 | void tst_QDBusXmlParser::initTestCase() |
| 63 | { |
| 64 | // Always initialize the hash seed to 0 to get reliable test results |
| 65 | qSetGlobalQHashSeed(newSeed: 0); |
| 66 | } |
| 67 | |
| 68 | void tst_QDBusXmlParser::parsing_data() |
| 69 | { |
| 70 | QTest::addColumn<QString>(name: "xmlData" ); |
| 71 | QTest::addColumn<int>(name: "interfaceCount" ); |
| 72 | QTest::addColumn<int>(name: "objectCount" ); |
| 73 | QTest::addColumn<int>(name: "annotationCount" ); |
| 74 | QTest::addColumn<QStringList>(name: "introspection" ); |
| 75 | |
| 76 | QStringList introspection; |
| 77 | |
| 78 | QTest::newRow(dataTag: "null" ) << QString() << 0 << 0 << 0 << introspection; |
| 79 | QTest::newRow(dataTag: "empty" ) << QString("" ) << 0 << 0 << 0 << introspection; |
| 80 | |
| 81 | QTest::newRow(dataTag: "junk" ) << "<junk/>" << 0 << 0 << 0 << introspection; |
| 82 | QTest::newRow(dataTag: "interface-inside-junk" ) << "<junk><interface name=\"iface.iface1\" /></junk>" |
| 83 | << 0 << 0 << 0 << introspection; |
| 84 | QTest::newRow(dataTag: "object-inside-junk" ) << "<junk><node name=\"obj1\" /></junk>" |
| 85 | << 0 << 0 << 0 << introspection; |
| 86 | |
| 87 | QTest::newRow(dataTag: "zero-interfaces" ) << "<node/>" << 0 << 0 << 0 << introspection; |
| 88 | |
| 89 | introspection << "<interface name=\"iface.iface1\"/>" ; |
| 90 | QTest::newRow(dataTag: "one-interface" ) << "<node><interface name=\"iface.iface1\" /></node>" |
| 91 | << 1 << 0 << 0 << introspection; |
| 92 | introspection.clear(); |
| 93 | |
| 94 | introspection << "<interface name=\"iface.iface1\"/>" |
| 95 | << "<interface name=\"iface.iface2\"/>" ; |
| 96 | QTest::newRow(dataTag: "two-interfaces" ) << "<node><interface name=\"iface.iface1\" />" |
| 97 | "<interface name=\"iface.iface2\" /></node>" |
| 98 | << 2 << 0 << 0 << introspection; |
| 99 | introspection.clear(); |
| 100 | |
| 101 | |
| 102 | QTest::newRow(dataTag: "one-object" ) << "<node><node name=\"obj1\"/></node>" |
| 103 | << 0 << 1 << 0 << introspection; |
| 104 | QTest::newRow(dataTag: "two-objects" ) << "<node><node name=\"obj1\"/><node name=\"obj2\"/></node>" |
| 105 | << 0 << 2 << 0 << introspection; |
| 106 | |
| 107 | introspection << "<interface name=\"iface.iface1\"/>" ; |
| 108 | QTest::newRow(dataTag: "i1o1" ) << "<node><interface name=\"iface.iface1\"/><node name=\"obj1\"/></node>" |
| 109 | << 1 << 1 << 0 << introspection; |
| 110 | introspection.clear(); |
| 111 | |
| 112 | introspection << "<interface name=\"iface.iface1\">" |
| 113 | " <annotation name=\"foo.testing\" value=\"nothing to see here\"/>" |
| 114 | "</interface>" ; |
| 115 | QTest::newRow(dataTag: "one-interface-annotated" ) << "<node><interface name=\"iface.iface1\">" |
| 116 | "<annotation name=\"foo.testing\" value=\"nothing to see here\" />" |
| 117 | "</interface></node>" << 1 << 0 << 1 << introspection; |
| 118 | introspection.clear(); |
| 119 | |
| 120 | |
| 121 | introspection << "<interface name=\"iface.iface1\"/>" ; |
| 122 | QTest::newRow(dataTag: "one-interface-docnamespace" ) << "<?xml version=\"1.0\" xmlns:doc=\"foo\" ?><node>" |
| 123 | "<interface name=\"iface.iface1\"><doc:something />" |
| 124 | "</interface></node>" << 1 << 0 << 0 << introspection; |
| 125 | introspection.clear(); |
| 126 | } |
| 127 | |
| 128 | void tst_QDBusXmlParser::parsing_common(const QString &xmlData) |
| 129 | { |
| 130 | QDBusIntrospection::Object obj = |
| 131 | QDBusIntrospection::parseObject(xml: xmlData, service: "local.testing" , path: "/" ); |
| 132 | QFETCH(int, interfaceCount); |
| 133 | QFETCH(int, objectCount); |
| 134 | QFETCH(int, annotationCount); |
| 135 | QFETCH(QStringList, introspection); |
| 136 | QCOMPARE(obj.interfaces.count(), interfaceCount); |
| 137 | QCOMPARE(obj.childObjects.count(), objectCount); |
| 138 | QCOMPARE(QDBusIntrospection::parseInterface(xmlData).annotations.count(), annotationCount); |
| 139 | |
| 140 | QDBusIntrospection::Interfaces ifaces = QDBusIntrospection::parseInterfaces(xml: xmlData); |
| 141 | |
| 142 | // also verify the naming |
| 143 | int i = 0; |
| 144 | foreach (QString name, obj.interfaces) { |
| 145 | const QString expectedName = QString("iface.iface%1" ).arg(a: i+1); |
| 146 | QCOMPARE(name, expectedName); |
| 147 | |
| 148 | const QString expectedIntrospection = clean_xml(introspection.at(i: i++)); |
| 149 | const QString resultIntrospection = clean_xml(ifaces.value(akey: expectedName)->introspection); |
| 150 | QCOMPARE(resultIntrospection, expectedIntrospection); |
| 151 | } |
| 152 | |
| 153 | i = 0; |
| 154 | foreach (QString name, obj.childObjects) |
| 155 | QCOMPARE(name, QString("obj%1" ).arg(++i)); |
| 156 | } |
| 157 | |
| 158 | QString tst_QDBusXmlParser::clean_xml(const QString &xmlData) |
| 159 | { |
| 160 | QDomDocument dom; |
| 161 | dom.setContent(text: xmlData); |
| 162 | return dom.toString(); |
| 163 | } |
| 164 | |
| 165 | void tst_QDBusXmlParser::parsing() |
| 166 | { |
| 167 | QFETCH(QString, xmlData); |
| 168 | |
| 169 | parsing_common(xmlData); |
| 170 | } |
| 171 | |
| 172 | void tst_QDBusXmlParser::parsingWithDoctype_data() |
| 173 | { |
| 174 | parsing_data(); |
| 175 | } |
| 176 | |
| 177 | void tst_QDBusXmlParser::parsingWithDoctype() |
| 178 | { |
| 179 | QString docType = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" |
| 180 | "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" ; |
| 181 | QFETCH(QString, xmlData); |
| 182 | |
| 183 | QString toParse; |
| 184 | if (xmlData.startsWith(s: QLatin1String("<?xml" ))) { |
| 185 | int split = xmlData.indexOf(c: QLatin1Char('>')) + 1; |
| 186 | toParse = xmlData.left(n: split) + docType + xmlData.mid(position: split); |
| 187 | } else { |
| 188 | toParse = docType + xmlData; |
| 189 | } |
| 190 | parsing_common(xmlData: toParse); |
| 191 | } |
| 192 | |
| 193 | void tst_QDBusXmlParser::methods_data() |
| 194 | { |
| 195 | QTest::addColumn<QString>(name: "xmlDataFragment" ); |
| 196 | QTest::addColumn<MethodMap>(name: "methodMap" ); |
| 197 | |
| 198 | MethodMap map; |
| 199 | QTest::newRow(dataTag: "no-methods" ) << QString() << map; |
| 200 | |
| 201 | // one method without arguments |
| 202 | QDBusIntrospection::Method method; |
| 203 | method.name = "Foo" ; |
| 204 | map << method; |
| 205 | QTest::newRow(dataTag: "one-method" ) << "<method name=\"Foo\"/>" << map; |
| 206 | |
| 207 | // add another method without arguments |
| 208 | method.name = "Bar" ; |
| 209 | map << method; |
| 210 | QTest::newRow(dataTag: "two-methods" ) << "<method name=\"Foo\"/>" |
| 211 | "<method name=\"Bar\"/>" |
| 212 | << map; |
| 213 | |
| 214 | // invert the order of the XML declaration |
| 215 | QTest::newRow(dataTag: "two-methods-inverse" ) << "<method name=\"Bar\"/>" |
| 216 | "<method name=\"Foo\"/>" |
| 217 | << map; |
| 218 | |
| 219 | // add a third, with annotations |
| 220 | method.name = "Baz" ; |
| 221 | method.annotations.insert(akey: "foo.testing" , avalue: "nothing to see here" ); |
| 222 | map << method; |
| 223 | QTest::newRow(dataTag: "method-with-annotation" ) << |
| 224 | "<method name=\"Foo\"/>" |
| 225 | "<method name=\"Bar\"/>" |
| 226 | "<method name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\" /></method>" |
| 227 | << map; |
| 228 | |
| 229 | // arguments |
| 230 | map.clear(); |
| 231 | method.annotations.clear(); |
| 232 | |
| 233 | method.name = "Method" ; |
| 234 | method.inputArgs << arg(type: "s" ); |
| 235 | map << method; |
| 236 | QTest::newRow(dataTag: "one-in" ) << |
| 237 | "<method name=\"Method\">" |
| 238 | "<arg type=\"s\" direction=\"in\"/>" |
| 239 | "</method>" << map; |
| 240 | |
| 241 | // two arguments |
| 242 | method.inputArgs << arg(type: "v" ); |
| 243 | map.clear(); |
| 244 | map << method; |
| 245 | QTest::newRow(dataTag: "two-in" ) << |
| 246 | "<method name=\"Method\">" |
| 247 | "<arg type=\"s\" direction=\"in\"/>" |
| 248 | "<arg type=\"v\" direction=\"in\"/>" |
| 249 | "</method>" << map; |
| 250 | |
| 251 | // one invalid arg |
| 252 | method.inputArgs << arg(type: "~" , name: "invalid" ); |
| 253 | map.clear(); |
| 254 | map << method; |
| 255 | QTest::newRow(dataTag: "two-in-one-invalid" ) << |
| 256 | "<method name=\"Method\">" |
| 257 | "<arg type=\"s\" direction=\"in\"/>" |
| 258 | "<arg type=\"v\" direction=\"in\"/>" |
| 259 | "<arg type=\"~\" name=\"invalid\" direction=\"in\"/>" |
| 260 | "</method>" << map; |
| 261 | |
| 262 | // one out argument |
| 263 | method.inputArgs.clear(); |
| 264 | method.outputArgs << arg(type: "s" ); |
| 265 | map.clear(); |
| 266 | map << method; |
| 267 | QTest::newRow(dataTag: "one-out" ) << |
| 268 | "<method name=\"Method\">" |
| 269 | "<arg type=\"s\" direction=\"out\"/>" |
| 270 | "</method>" << map; |
| 271 | |
| 272 | // two in and one out |
| 273 | method.inputArgs << arg(type: "s" ) << arg(type: "v" ); |
| 274 | map.clear(); |
| 275 | map << method; |
| 276 | QTest::newRow(dataTag: "two-in-one-out" ) << |
| 277 | "<method name=\"Method\">" |
| 278 | "<arg type=\"s\" direction=\"in\"/>" |
| 279 | "<arg type=\"v\" direction=\"in\"/>" |
| 280 | "<arg type=\"s\" direction=\"out\"/>" |
| 281 | "</method>" << map; |
| 282 | |
| 283 | // let's try an arg with name |
| 284 | method.outputArgs.clear(); |
| 285 | method.inputArgs.clear(); |
| 286 | method.inputArgs << arg(type: "s" , name: "foo" ); |
| 287 | map.clear(); |
| 288 | map << method; |
| 289 | QTest::newRow(dataTag: "one-in-with-name" ) << |
| 290 | "<method name=\"Method\">" |
| 291 | "<arg type=\"s\" name=\"foo\" direction=\"in\"/>" |
| 292 | "</method>" << map; |
| 293 | |
| 294 | // two args with name |
| 295 | method.inputArgs << arg(type: "i" , name: "bar" ); |
| 296 | map.clear(); |
| 297 | map << method; |
| 298 | QTest::newRow(dataTag: "two-in-with-name" ) << |
| 299 | "<method name=\"Method\">" |
| 300 | "<arg type=\"s\" name=\"foo\" direction=\"in\"/>" |
| 301 | "<arg type=\"i\" name=\"bar\" direction=\"in\"/>" |
| 302 | "</method>" << map; |
| 303 | |
| 304 | // one complex |
| 305 | map.clear(); |
| 306 | method = QDBusIntrospection::Method(); |
| 307 | |
| 308 | // Method1(in STRING arg1, in BYTE arg2, out ARRAY of STRING) |
| 309 | method.inputArgs << arg(type: "s" , name: "arg1" ) << arg(type: "y" , name: "arg2" ); |
| 310 | method.outputArgs << arg(type: "as" ); |
| 311 | method.name = "Method1" ; |
| 312 | map << method; |
| 313 | |
| 314 | // Method2(in ARRAY of DICT_ENTRY of (STRING,VARIANT) variantMap, in UINT32 index, |
| 315 | // out STRING key, out VARIANT value) |
| 316 | // with annotation "foo.equivalent":"QVariantMap" |
| 317 | method = QDBusIntrospection::Method(); |
| 318 | method.inputArgs << arg(type: "a{sv}" , name: "variantMap" ) << arg(type: "u" , name: "index" ); |
| 319 | method.outputArgs << arg(type: "s" , name: "key" ) << arg(type: "v" , name: "value" ); |
| 320 | method.annotations.insert(akey: "foo.equivalent" , avalue: "QVariantMap" ); |
| 321 | method.name = "Method2" ; |
| 322 | map << method; |
| 323 | |
| 324 | QTest::newRow(dataTag: "complex" ) << |
| 325 | "<method name=\"Method1\">" |
| 326 | "<arg name=\"arg1\" type=\"s\" direction=\"in\"/>" |
| 327 | "<arg name=\"arg2\" type=\"y\" direction=\"in\"/>" |
| 328 | "<arg type=\"as\" direction=\"out\"/>" |
| 329 | "</method>" |
| 330 | "<method name=\"Method2\">" |
| 331 | "<arg name=\"variantMap\" type=\"a{sv}\" direction=\"in\"/>" |
| 332 | "<arg name=\"index\" type=\"u\" direction=\"in\"/>" |
| 333 | "<arg name=\"key\" type=\"s\" direction=\"out\"/>" |
| 334 | "<arg name=\"value\" type=\"v\" direction=\"out\"/>" |
| 335 | "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>" |
| 336 | "</method>" << map; |
| 337 | } |
| 338 | |
| 339 | void tst_QDBusXmlParser::methods() |
| 340 | { |
| 341 | QString = "<interface name=\"iface.iface1\">" , |
| 342 | = "</interface>" , |
| 343 | = "<node>" + intHeader, |
| 344 | = intFooter + "</node>" ; |
| 345 | |
| 346 | QFETCH(QString, xmlDataFragment); |
| 347 | |
| 348 | QDBusIntrospection::Interface iface = |
| 349 | QDBusIntrospection::parseInterface(xml: xmlHeader + xmlDataFragment + xmlFooter); |
| 350 | |
| 351 | QCOMPARE(iface.name, QString("iface.iface1" )); |
| 352 | QCOMPARE(clean_xml(iface.introspection), clean_xml(intHeader + xmlDataFragment + intFooter)); |
| 353 | |
| 354 | QFETCH(MethodMap, methodMap); |
| 355 | MethodMap parsedMap = iface.methods; |
| 356 | |
| 357 | QCOMPARE(parsedMap.count(), methodMap.count()); |
| 358 | QCOMPARE(parsedMap, methodMap); |
| 359 | } |
| 360 | |
| 361 | void tst_QDBusXmlParser::signals__data() |
| 362 | { |
| 363 | QTest::addColumn<QString>(name: "xmlDataFragment" ); |
| 364 | QTest::addColumn<SignalMap>(name: "signalMap" ); |
| 365 | |
| 366 | SignalMap map; |
| 367 | QTest::newRow(dataTag: "no-signals" ) << QString() << map; |
| 368 | |
| 369 | // one signal without arguments |
| 370 | QDBusIntrospection::Signal signal; |
| 371 | signal.name = "Foo" ; |
| 372 | map << signal; |
| 373 | QTest::newRow(dataTag: "one-signal" ) << "<signal name=\"Foo\"/>" << map; |
| 374 | |
| 375 | // add another signal without arguments |
| 376 | signal.name = "Bar" ; |
| 377 | map << signal; |
| 378 | QTest::newRow(dataTag: "two-signals" ) << "<signal name=\"Foo\"/>" |
| 379 | "<signal name=\"Bar\"/>" |
| 380 | << map; |
| 381 | |
| 382 | // invert the order of the XML declaration |
| 383 | QTest::newRow(dataTag: "two-signals-inverse" ) << "<signal name=\"Bar\"/>" |
| 384 | "<signal name=\"Foo\"/>" |
| 385 | << map; |
| 386 | |
| 387 | // add a third, with annotations |
| 388 | signal.name = "Baz" ; |
| 389 | signal.annotations.insert(akey: "foo.testing" , avalue: "nothing to see here" ); |
| 390 | map << signal; |
| 391 | QTest::newRow(dataTag: "signal-with-annotation" ) << |
| 392 | "<signal name=\"Foo\"/>" |
| 393 | "<signal name=\"Bar\"/>" |
| 394 | "<signal name=\"Baz\"><annotation name=\"foo.testing\" value=\"nothing to see here\" /></signal>" |
| 395 | << map; |
| 396 | |
| 397 | // one out argument |
| 398 | map.clear(); |
| 399 | signal.annotations.clear(); |
| 400 | signal.outputArgs << arg(type: "s" ); |
| 401 | signal.name = "Signal" ; |
| 402 | map.clear(); |
| 403 | map << signal; |
| 404 | QTest::newRow(dataTag: "one-out" ) << |
| 405 | "<signal name=\"Signal\">" |
| 406 | "<arg type=\"s\" direction=\"out\"/>" |
| 407 | "</signal>" << map; |
| 408 | |
| 409 | // without saying which direction it is |
| 410 | QTest::newRow(dataTag: "one-out-no-direction" ) << |
| 411 | "<signal name=\"Signal\">" |
| 412 | "<arg type=\"s\"/>" |
| 413 | "</signal>" << map; |
| 414 | |
| 415 | // two args with name |
| 416 | signal.outputArgs << arg(type: "i" , name: "bar" ); |
| 417 | map.clear(); |
| 418 | map << signal; |
| 419 | QTest::newRow(dataTag: "two-out-with-name" ) << |
| 420 | "<signal name=\"Signal\">" |
| 421 | "<arg type=\"s\" direction=\"out\"/>" |
| 422 | "<arg type=\"i\" name=\"bar\"/>" |
| 423 | "</signal>" << map; |
| 424 | |
| 425 | // one complex |
| 426 | map.clear(); |
| 427 | signal = QDBusIntrospection::Signal(); |
| 428 | |
| 429 | // Signal1(out ARRAY of STRING) |
| 430 | signal.outputArgs << arg(type: "as" ); |
| 431 | signal.name = "Signal1" ; |
| 432 | map << signal; |
| 433 | |
| 434 | // Signal2(out STRING key, out VARIANT value) |
| 435 | // with annotation "foo.equivalent":"QVariantMap" |
| 436 | signal = QDBusIntrospection::Signal(); |
| 437 | signal.outputArgs << arg(type: "s" , name: "key" ) << arg(type: "v" , name: "value" ); |
| 438 | signal.annotations.insert(akey: "foo.equivalent" , avalue: "QVariantMap" ); |
| 439 | signal.name = "Signal2" ; |
| 440 | map << signal; |
| 441 | |
| 442 | QTest::newRow(dataTag: "complex" ) << |
| 443 | "<signal name=\"Signal1\">" |
| 444 | "<arg type=\"as\" direction=\"out\"/>" |
| 445 | "</signal>" |
| 446 | "<signal name=\"Signal2\">" |
| 447 | "<arg name=\"key\" type=\"s\" direction=\"out\"/>" |
| 448 | "<arg name=\"value\" type=\"v\" direction=\"out\"/>" |
| 449 | "<annotation name=\"foo.equivalent\" value=\"QVariantMap\"/>" |
| 450 | "</signal>" << map; |
| 451 | } |
| 452 | |
| 453 | void tst_QDBusXmlParser::signals_() |
| 454 | { |
| 455 | QString = "<interface name=\"iface.iface1\">" , |
| 456 | = "</interface>" , |
| 457 | = "<node>" + intHeader, |
| 458 | = intFooter + "</node>" ; |
| 459 | |
| 460 | QFETCH(QString, xmlDataFragment); |
| 461 | |
| 462 | QDBusIntrospection::Interface iface = |
| 463 | QDBusIntrospection::parseInterface(xml: xmlHeader + xmlDataFragment + xmlFooter); |
| 464 | |
| 465 | QCOMPARE(iface.name, QString("iface.iface1" )); |
| 466 | QCOMPARE(clean_xml(iface.introspection), clean_xml(intHeader + xmlDataFragment + intFooter)); |
| 467 | |
| 468 | QFETCH(SignalMap, signalMap); |
| 469 | SignalMap parsedMap = iface.signals_; |
| 470 | |
| 471 | QCOMPARE(signalMap.count(), parsedMap.count()); |
| 472 | QCOMPARE(signalMap, parsedMap); |
| 473 | } |
| 474 | |
| 475 | void tst_QDBusXmlParser::properties_data() |
| 476 | { |
| 477 | QTest::addColumn<QString>(name: "xmlDataFragment" ); |
| 478 | QTest::addColumn<PropertyMap>(name: "propertyMap" ); |
| 479 | |
| 480 | PropertyMap map; |
| 481 | QTest::newRow(dataTag: "no-signals" ) << QString() << map; |
| 482 | |
| 483 | // one readable signal |
| 484 | QDBusIntrospection::Property prop; |
| 485 | prop.name = "foo" ; |
| 486 | prop.type = "s" ; |
| 487 | prop.access = QDBusIntrospection::Property::Read; |
| 488 | map << prop; |
| 489 | QTest::newRow(dataTag: "one-readable" ) << "<property name=\"foo\" type=\"s\" access=\"read\"/>" << map; |
| 490 | |
| 491 | // one writable signal |
| 492 | prop.access = QDBusIntrospection::Property::Write; |
| 493 | map.clear(); |
| 494 | map << prop; |
| 495 | QTest::newRow(dataTag: "one-writable" ) << "<property name=\"foo\" type=\"s\" access=\"write\"/>" << map; |
| 496 | |
| 497 | // one read- & writable signal |
| 498 | prop.access = QDBusIntrospection::Property::ReadWrite; |
| 499 | map.clear(); |
| 500 | map << prop; |
| 501 | QTest::newRow(dataTag: "one-read-writable" ) << "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" |
| 502 | << map; |
| 503 | |
| 504 | // two, mixed properties |
| 505 | prop.name = "bar" ; |
| 506 | prop.type = "i" ; |
| 507 | prop.access = QDBusIntrospection::Property::Read; |
| 508 | map << prop; |
| 509 | QTest::newRow(dataTag: "two-1" ) << |
| 510 | "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" |
| 511 | "<property name=\"bar\" type=\"i\" access=\"read\"/>" << map; |
| 512 | |
| 513 | // invert the order of the declaration |
| 514 | QTest::newRow(dataTag: "two-2" ) << |
| 515 | "<property name=\"bar\" type=\"i\" access=\"read\"/>" |
| 516 | "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map; |
| 517 | |
| 518 | // add a third with annotations |
| 519 | prop.name = "baz" ; |
| 520 | prop.type = "as" ; |
| 521 | prop.access = QDBusIntrospection::Property::Write; |
| 522 | prop.annotations.insert(akey: "foo.annotation" , avalue: "Hello, World" ); |
| 523 | prop.annotations.insert(akey: "foo.annotation2" , avalue: "Goodbye, World" ); |
| 524 | map << prop; |
| 525 | QTest::newRow(dataTag: "complex" ) << |
| 526 | "<property name=\"bar\" type=\"i\" access=\"read\"/>" |
| 527 | "<property name=\"baz\" type=\"as\" access=\"write\">" |
| 528 | "<annotation name=\"foo.annotation\" value=\"Hello, World\" />" |
| 529 | "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />" |
| 530 | "</property>" |
| 531 | "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map; |
| 532 | |
| 533 | // and now change the order |
| 534 | QTest::newRow(dataTag: "complex2" ) << |
| 535 | "<property name=\"baz\" type=\"as\" access=\"write\">" |
| 536 | "<annotation name=\"foo.annotation2\" value=\"Goodbye, World\" />" |
| 537 | "<annotation name=\"foo.annotation\" value=\"Hello, World\" />" |
| 538 | "</property>" |
| 539 | "<property name=\"bar\" type=\"i\" access=\"read\"/>" |
| 540 | "<property name=\"foo\" type=\"s\" access=\"readwrite\"/>" << map; |
| 541 | } |
| 542 | |
| 543 | void tst_QDBusXmlParser::properties() |
| 544 | { |
| 545 | QString = "<interface name=\"iface.iface1\">" , |
| 546 | = "</interface>" , |
| 547 | = "<node>" + intHeader, |
| 548 | = intFooter + "</node>" ; |
| 549 | |
| 550 | QFETCH(QString, xmlDataFragment); |
| 551 | |
| 552 | QDBusIntrospection::Interface iface = |
| 553 | QDBusIntrospection::parseInterface(xml: xmlHeader + xmlDataFragment + xmlFooter); |
| 554 | |
| 555 | QCOMPARE(iface.name, QString("iface.iface1" )); |
| 556 | QCOMPARE(clean_xml(iface.introspection), clean_xml(intHeader + xmlDataFragment + intFooter)); |
| 557 | |
| 558 | QFETCH(PropertyMap, propertyMap); |
| 559 | PropertyMap parsedMap = iface.properties; |
| 560 | |
| 561 | QCOMPARE(propertyMap.count(), parsedMap.count()); |
| 562 | QCOMPARE(propertyMap, parsedMap); |
| 563 | } |
| 564 | |
| 565 | QTEST_MAIN(tst_QDBusXmlParser) |
| 566 | |
| 567 | #include "tst_qdbusxmlparser.moc" |
| 568 | |