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 | |