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 | |
29 | |
30 | #include <QBuffer> |
31 | #include <QByteArray> |
32 | #include <QCoreApplication> |
33 | #include <QDebug> |
34 | #include <QFile> |
35 | #include <QList> |
36 | #include <QRegExp> |
37 | #include <QTextStream> |
38 | #include <QtTest/QtTest> |
39 | #include <QtXml> |
40 | #include <QVariant> |
41 | #include <cmath> |
42 | |
43 | QT_FORWARD_DECLARE_CLASS(QDomDocument) |
44 | QT_FORWARD_DECLARE_CLASS(QDomNode) |
45 | |
46 | class tst_QDom : public QObject |
47 | { |
48 | Q_OBJECT |
49 | |
50 | private slots: |
51 | void initTestCase(); |
52 | void namespacedAttributes() const; |
53 | void setContent_data(); |
54 | void setContent(); |
55 | void toString_01_data(); |
56 | void toString_01(); |
57 | void toString_02_data(); |
58 | void toString_02(); |
59 | void hasAttributes_data(); |
60 | void hasAttributes(); |
61 | void setGetAttributes(); |
62 | void save_data(); |
63 | void save(); |
64 | void saveWithSerialization() const; |
65 | void saveWithSerialization_data() const; |
66 | void cloneNode_data(); |
67 | void cloneNode(); |
68 | void ownerDocument_data(); |
69 | void ownerDocument(); |
70 | void ownerDocumentTask27424_data(); |
71 | void ownerDocumentTask27424(); |
72 | void parentNode_data(); |
73 | void parentNode(); |
74 | void documentCreationTask27424_data(); |
75 | void documentCreationTask27424(); |
76 | void browseElements(); |
77 | void ownerElementTask45192_data(); |
78 | void ownerElementTask45192(); |
79 | void domNodeMapAndList(); |
80 | |
81 | void nullDocument(); |
82 | void invalidName_data(); |
83 | void invalidName(); |
84 | void invalidQualifiedName_data(); |
85 | void invalidQualifiedName(); |
86 | void invalidCharData_data(); |
87 | void invalidCharData(); |
88 | |
89 | void roundTripAttributes() const; |
90 | void normalizeEndOfLine() const; |
91 | void normalizeAttributes() const; |
92 | void serializeWeirdEOL() const; |
93 | void reparentAttribute() const; |
94 | void serializeNamespaces() const; |
95 | void flagInvalidNamespaces() const; |
96 | void flagUndeclaredNamespace() const; |
97 | |
98 | void indentComments() const; |
99 | void checkLiveness() const; |
100 | void reportDuplicateAttributes() const; |
101 | void appendChildFromToDocument() const; |
102 | void iterateCDATA() const; |
103 | void appendDocumentNode() const; |
104 | void germanUmlautToByteArray() const; |
105 | void germanUmlautToFile() const; |
106 | void setInvalidDataPolicy() const; |
107 | void crashInSetContent() const; |
108 | void doubleNamespaceDeclarations() const; |
109 | void setContentQXmlReaderOverload() const; |
110 | void toStringWithoutNewlines() const; |
111 | void checkIntOverflow() const; |
112 | void setContentWhitespace() const; |
113 | void setContentWhitespace_data() const; |
114 | |
115 | void taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const; |
116 | void cloneDTD_QTBUG8398() const; |
117 | void DTDNotationDecl(); |
118 | void DTDEntityDecl(); |
119 | void QTBUG49113_dontCrashWithNegativeIndex() const; |
120 | |
121 | void cleanupTestCase() const; |
122 | |
123 | private: |
124 | static QDomDocument generateRequest(); |
125 | static int hasAttributesHelper( const QDomNode& node ); |
126 | static bool compareDocuments( const QDomDocument &doc1, const QDomDocument &doc2 ); |
127 | static bool compareNodes( const QDomNode &node1, const QDomNode &node2, bool deep ); |
128 | static QDomNode findDomNode( const QDomDocument &doc, const QList<QVariant> &pathToNode ); |
129 | static QString onNullWarning(const char *const functionName); |
130 | static bool isDeepEqual(const QDomNode &n1, const QDomNode &n2); |
131 | static bool isFakeXMLDeclaration(const QDomNode &node); |
132 | |
133 | QList<QByteArray> m_testCodecs; |
134 | }; |
135 | |
136 | |
137 | void tst_QDom::setContent_data() |
138 | { |
139 | const QString doc01( |
140 | "<!DOCTYPE a1 [ <!ENTITY blubber 'and'> ]>\n" |
141 | "<a1>\n" |
142 | " <b1>\n" |
143 | " <c1>foo</c1>\n" |
144 | " <c2>bar</c2>\n" |
145 | " <c3>foo & bar</c3>\n" |
146 | " <c4>foo &blubber; bar</c4>\n" |
147 | " </b1>\n" |
148 | " <b2> </b2>\n" |
149 | " <b3>\n" |
150 | " <c1/>\n" |
151 | " </b3>\n" |
152 | "</a1>\n" |
153 | ); |
154 | |
155 | QTest::addColumn<QString>(name: "doc" ); |
156 | QTest::addColumn<QStringList>(name: "featuresTrue" ); |
157 | QTest::addColumn<QStringList>(name: "featuresFalse" ); |
158 | QTest::addColumn<QString>(name: "res" ); |
159 | |
160 | QTest::newRow( dataTag: "01" ) << doc01 |
161 | << QStringList() |
162 | << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData" ).split(sep: ' ') |
163 | << QString("<!DOCTYPE a1>\n" |
164 | "<a1>\n" |
165 | " <b1>\n" |
166 | " <c1>foo</c1>\n" |
167 | " <c2>bar</c2>\n" |
168 | " <c3>foo & bar</c3>\n" |
169 | " <c4>foo and bar</c4>\n" |
170 | " </b1>\n" |
171 | " <b2/>\n" |
172 | " <b3>\n" |
173 | " <c1/>\n" |
174 | " </b3>\n" |
175 | "</a1>\n" ); |
176 | |
177 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
178 | // These configurations cannot be supported by the QXmlStreamReader-based implementation |
179 | QTest::newRow( dataTag: "02" ) << doc01 |
180 | << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData" ).split(sep: ' ') |
181 | << QStringList() |
182 | << QString("<!DOCTYPE a1>\n" |
183 | "<a1>\n" |
184 | " <b1>\n" |
185 | " <c1>foo</c1>\n" |
186 | " <c2>bar</c2>\n" |
187 | " <c3>foo & bar</c3>\n" |
188 | " <c4>foo and bar</c4>\n" |
189 | " </b1>\n" |
190 | " <b2> </b2>\n" |
191 | " <b3>\n" |
192 | " <c1/>\n" |
193 | " </b3>\n" |
194 | "</a1>\n" ); |
195 | |
196 | QTest::newRow( dataTag: "03" ) << doc01 |
197 | << QString("http://trolltech.com/xml/features/report-start-end-entity" ).split(sep: ' ') |
198 | << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData" ).split(sep: ' ') |
199 | << QString("<!DOCTYPE a1 [\n" |
200 | "<!ENTITY blubber \"and\">\n" |
201 | "]>\n" |
202 | "<a1>\n" |
203 | " <b1>\n" |
204 | " <c1>foo</c1>\n" |
205 | " <c2>bar</c2>\n" |
206 | " <c3>foo & bar</c3>\n" |
207 | " <c4>foo &blubber; bar</c4>\n" |
208 | " </b1>\n" |
209 | " <b2/>\n" |
210 | " <b3>\n" |
211 | " <c1/>\n" |
212 | " </b3>\n" |
213 | "</a1>\n" ); |
214 | |
215 | QTest::newRow( dataTag: "04" ) << doc01 |
216 | << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData http://trolltech.com/xml/features/report-start-end-entity" ).split(sep: ' ') |
217 | << QStringList() |
218 | << QString("<!DOCTYPE a1 [\n" |
219 | "<!ENTITY blubber \"and\">\n" |
220 | "]>\n" |
221 | "<a1>\n" |
222 | " <b1>\n" |
223 | " <c1>foo</c1>\n" |
224 | " <c2>bar</c2>\n" |
225 | " <c3>foo & bar</c3>\n" |
226 | " <c4>foo &blubber; bar</c4>\n" |
227 | " </b1>\n" |
228 | " <b2> </b2>\n" |
229 | " <b3>\n" |
230 | " <c1/>\n" |
231 | " </b3>\n" |
232 | "</a1>\n" ); |
233 | #endif |
234 | |
235 | QTest::newRow(dataTag: "05" ) << QString("<message>\n" |
236 | " <body><b>foo</b>>]]></body>\n" |
237 | "</message>\n" ) |
238 | << QStringList() << QStringList() |
239 | << QString("<message>\n" |
240 | " <body><b>foo</b>>]]></body>\n" |
241 | "</message>\n" ); |
242 | |
243 | } |
244 | |
245 | void tst_QDom::setContent() |
246 | { |
247 | QFETCH( QString, doc ); |
248 | |
249 | QDomDocument domDoc; |
250 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
251 | QT_WARNING_PUSH |
252 | QT_WARNING_DISABLE_DEPRECATED |
253 | QXmlInputSource source; |
254 | source.setData( doc ); |
255 | |
256 | QFETCH( QStringList, featuresTrue ); |
257 | QFETCH( QStringList, featuresFalse ); |
258 | QXmlSimpleReader reader; |
259 | QStringList::Iterator it; |
260 | for ( it = featuresTrue.begin(); it != featuresTrue.end(); ++it ) { |
261 | QVERIFY( reader.hasFeature( *it ) ); |
262 | reader.setFeature( name: *it, value: true ); |
263 | } |
264 | for ( it = featuresFalse.begin(); it != featuresFalse.end(); ++it ) { |
265 | QVERIFY( reader.hasFeature( *it ) ); |
266 | reader.setFeature( name: *it, value: false ); |
267 | } |
268 | |
269 | QVERIFY( domDoc.setContent( &source, &reader ) ); |
270 | QT_WARNING_POP |
271 | #else |
272 | QXmlStreamReader reader(doc); |
273 | QVERIFY(domDoc.setContent(&reader, true)); |
274 | #endif |
275 | |
276 | QString eRes; |
277 | QTextStream ts( &eRes, QIODevice::WriteOnly ); |
278 | domDoc.save( ts, 4 ); |
279 | |
280 | QTEST( eRes, "res" ); |
281 | |
282 | // make sure that if we parse our output again, we get the same document |
283 | QDomDocument domDoc1; |
284 | QDomDocument domDoc2; |
285 | QVERIFY( domDoc1.setContent( doc ) ); |
286 | QVERIFY( domDoc2.setContent( eRes ) ); |
287 | QVERIFY( compareDocuments( domDoc1, domDoc2 ) ); |
288 | } |
289 | |
290 | void tst_QDom::toString_01_data() |
291 | { |
292 | QTest::addColumn<QString>(name: "fileName" ); |
293 | const QString prefix = QFINDTESTDATA("testdata/toString_01" ); |
294 | if (prefix.isEmpty()) |
295 | QFAIL("Cannot find testdata directory!" ); |
296 | QTest::newRow( dataTag: "01" ) << QString(prefix + "/doc01.xml" ); |
297 | QTest::newRow( dataTag: "02" ) << QString(prefix + "/doc02.xml" ); |
298 | QTest::newRow( dataTag: "03" ) << QString(prefix + "/doc03.xml" ); |
299 | QTest::newRow( dataTag: "04" ) << QString(prefix + "/doc04.xml" ); |
300 | QTest::newRow( dataTag: "05" ) << QString(prefix + "/doc05.xml" ); |
301 | |
302 | QTest::newRow( dataTag: "euc-jp" ) << QString(prefix + "/doc_euc-jp.xml" ); |
303 | QTest::newRow( dataTag: "iso-2022-jp" ) << QString(prefix + "/doc_iso-2022-jp.xml" ); |
304 | QTest::newRow( dataTag: "little-endian" ) << QString(prefix + "/doc_little-endian.xml" ); |
305 | QTest::newRow( dataTag: "utf-16" ) << QString(prefix + "/doc_utf-16.xml" ); |
306 | QTest::newRow( dataTag: "utf-8" ) << QString(prefix + "/doc_utf-8.xml" ); |
307 | |
308 | } |
309 | |
310 | /*! \internal |
311 | |
312 | This function tests that the QDomDocument::toString() function results in the |
313 | same XML document. The meaning of "same" in this context means that the |
314 | "information" in the resulting XML file is the same as in the original, i.e. |
315 | we are not intrested in different formatting, etc. |
316 | |
317 | To achieve this, the XML document of the toString() function is parsed again |
318 | and the two QDomDocuments are compared. |
319 | */ |
320 | void tst_QDom::toString_01() |
321 | { |
322 | QFETCH(QString, fileName); |
323 | |
324 | QFile f(fileName); |
325 | QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Failed to open file %1, file error: %2" ).arg(fileName).arg(f.error()))); |
326 | |
327 | QDomDocument doc; |
328 | QString errorMsg; |
329 | int errorLine; |
330 | int errorCol; |
331 | |
332 | QVERIFY(doc.setContent( &f, &errorMsg, &errorLine, &errorCol )); /*, |
333 | QString("QDomDocument::setContent() failed: %1 in line %2, column %3") |
334 | .arg( errorMsg ).arg( errorLine ).arg( errorCol )); */ |
335 | // test toString()'s invariant with different indenting depths |
336 | for ( int i=0; i<5; i++ ) { |
337 | QString toStr = doc.toString( i ); |
338 | |
339 | QDomDocument res; |
340 | QVERIFY( res.setContent( toStr ) ); |
341 | |
342 | QVERIFY( compareDocuments( doc, res ) ); |
343 | } |
344 | } |
345 | |
346 | void tst_QDom::toString_02_data() |
347 | { |
348 | save_data(); |
349 | } |
350 | |
351 | /* |
352 | Tests the new QDomDocument::toString(int) overload (basically the same test |
353 | as save()). |
354 | */ |
355 | void tst_QDom::toString_02() |
356 | { |
357 | QFETCH( QString, doc ); |
358 | QFETCH( int, indent ); |
359 | |
360 | QDomDocument domDoc; |
361 | QVERIFY( domDoc.setContent( doc ) ); |
362 | QTEST( domDoc.toString(indent), "res" ); |
363 | } |
364 | |
365 | |
366 | void tst_QDom::hasAttributes_data() |
367 | { |
368 | QTest::addColumn<int>(name: "visitedNodes" ); |
369 | QTest::addColumn<QByteArray>(name: "xmlDoc" ); |
370 | |
371 | QByteArray doc1("<top>Make a <blubb>stupid</blubb>, useless test sentence.</top>" ); |
372 | QByteArray doc2("<top a=\"a\">Make a <blubb a=\"a\">stupid</blubb>, useless test sentence.</top>" ); |
373 | QByteArray doc3("<!-- just a useless comment -->\n" |
374 | "<?pi foo bar?>\n" |
375 | "<foo>\n" |
376 | "<bar fnord=\"snafu\" hmpf=\"grmpf\">\n" |
377 | "<foobar/>\n" |
378 | "</bar>\n" |
379 | "<bar>blubber</bar>\n" |
380 | "more text, pretty unintresting, though\n" |
381 | "<hmpfl blubber=\"something\" />\n" |
382 | "<![CDATA[ foo bar @!<>] ]]>\n" |
383 | "</foo>\n" |
384 | "<!-- just a useless comment -->\n" |
385 | "<?pi foo bar?>\n" ); |
386 | |
387 | QTest::newRow( dataTag: "01" ) << 6 << doc1; |
388 | QTest::newRow( dataTag: "02" ) << 6 << doc2; |
389 | QTest::newRow( dataTag: "03" ) << 13 << doc3; |
390 | } |
391 | |
392 | /* |
393 | This function tests that QDomNode::hasAttributes() returns true if and only |
394 | if the node has attributes (i.e. QDomNode::attributes() returns a list with |
395 | attributes in it). |
396 | */ |
397 | void tst_QDom::hasAttributes() |
398 | { |
399 | QFETCH( QByteArray, xmlDoc ); |
400 | |
401 | QDomDocument doc; |
402 | QVERIFY( doc.setContent( xmlDoc ) ); |
403 | |
404 | int visitedNodes = hasAttributesHelper( node: doc ); |
405 | QTEST( visitedNodes, "visitedNodes" ); |
406 | } |
407 | |
408 | void tst_QDom::setGetAttributes() |
409 | { |
410 | QDomDocument doc; |
411 | QDomElement rootNode = doc.createElement(tagName: "Root" ); |
412 | doc.appendChild(newChild: rootNode); |
413 | |
414 | const QLocale oldLocale = QLocale(); |
415 | QLocale::setDefault(QLocale::German); // decimal separator != '.' |
416 | |
417 | const QString qstringVal("QString" ); |
418 | const qlonglong qlonglongVal = std::numeric_limits<qlonglong>::min(); |
419 | const qulonglong qulonglongVal = std::numeric_limits<qulonglong>::max(); |
420 | const int intVal = std::numeric_limits<int>::min(); |
421 | const uint uintVal = std::numeric_limits<uint>::max(); |
422 | const float floatVal = 0.1234f; |
423 | const double doubleVal1 = 1./6.; |
424 | const double doubleVal2 = std::nextafter(x: doubleVal1, y: 1.); |
425 | const double doubleVal3 = std::nextafter(x: doubleVal2, y: 1.); |
426 | |
427 | rootNode.setAttribute(name: "qstringVal" , value: qstringVal); |
428 | rootNode.setAttribute(name: "qlonglongVal" , value: qlonglongVal); |
429 | rootNode.setAttribute(name: "qulonglongVal" , value: qulonglongVal); |
430 | rootNode.setAttribute(name: "intVal" , value: intVal); |
431 | rootNode.setAttribute(name: "uintVal" , value: uintVal); |
432 | rootNode.setAttribute(name: "floatVal" , value: floatVal); |
433 | rootNode.setAttribute(name: "doubleVal1" , value: doubleVal1); |
434 | rootNode.setAttribute(name: "doubleVal2" , value: doubleVal2); |
435 | rootNode.setAttribute(name: "doubleVal3" , value: doubleVal3); |
436 | |
437 | QDomElement nsNode = doc.createElement(tagName: "NS" ); |
438 | rootNode.appendChild(newChild: nsNode); |
439 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "qstringVal" , value: qstringVal); |
440 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "qlonglongVal" , value: qlonglongVal); |
441 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "qulonglongVal" , value: qulonglongVal); |
442 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "intVal" , value: intVal); |
443 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "uintVal" , value: uintVal); |
444 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "floatVal" , value: floatVal); // not available atm |
445 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "doubleVal1" , value: doubleVal1); |
446 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "doubleVal2" , value: doubleVal2); |
447 | nsNode.setAttributeNS(nsURI: "namespace" , qName: "doubleVal3" , value: doubleVal3); |
448 | |
449 | bool bOk; |
450 | QCOMPARE(rootNode.attribute("qstringVal" ), qstringVal); |
451 | QCOMPARE(rootNode.attribute("qlonglongVal" ).toLongLong(&bOk), qlonglongVal); |
452 | QVERIFY(bOk); |
453 | QCOMPARE(rootNode.attribute("qulonglongVal" ).toULongLong(&bOk), qulonglongVal); |
454 | QVERIFY(bOk); |
455 | QCOMPARE(rootNode.attribute("intVal" ).toInt(&bOk), intVal); |
456 | QVERIFY(bOk); |
457 | QCOMPARE(rootNode.attribute("uintVal" ).toUInt(&bOk), uintVal); |
458 | QVERIFY(bOk); |
459 | QCOMPARE(rootNode.attribute("floatVal" ).toFloat(&bOk), floatVal); |
460 | QVERIFY(bOk); |
461 | |
462 | QVERIFY(rootNode.attribute("doubleVal1" ).toDouble(&bOk) == doubleVal1 && bOk); |
463 | QVERIFY(rootNode.attribute("doubleVal2" ).toDouble(&bOk) == doubleVal2 && bOk); |
464 | QVERIFY(rootNode.attribute("doubleVal3" ).toDouble(&bOk) == doubleVal3 && bOk); |
465 | |
466 | QCOMPARE(nsNode.attributeNS("namespace" , "qstringVal" ), qstringVal); |
467 | QCOMPARE(nsNode.attributeNS("namespace" , "qlonglongVal" ).toLongLong(&bOk), qlonglongVal); |
468 | QVERIFY(bOk); |
469 | QCOMPARE(nsNode.attributeNS("namespace" , "qulonglongVal" ).toULongLong(&bOk), qulonglongVal); |
470 | QVERIFY(bOk); |
471 | QCOMPARE(nsNode.attributeNS("namespace" , "intVal" ).toInt(&bOk), intVal); |
472 | QVERIFY(bOk); |
473 | QCOMPARE(nsNode.attributeNS("namespace" , "uintVal" ).toUInt(&bOk), uintVal); |
474 | QVERIFY(bOk); |
475 | QCOMPARE(nsNode.attributeNS("namespace" , "floatVal" ).toFloat(&bOk), floatVal); |
476 | QVERIFY(bOk); |
477 | QVERIFY(nsNode.attributeNS("namespace" , "doubleVal1" ).toDouble(&bOk) == doubleVal1 && bOk); |
478 | QVERIFY(nsNode.attributeNS("namespace" , "doubleVal2" ).toDouble(&bOk) == doubleVal2 && bOk); |
479 | QVERIFY(nsNode.attributeNS("namespace" , "doubleVal3" ).toDouble(&bOk) == doubleVal3 && bOk); |
480 | |
481 | QLocale::setDefault(oldLocale); |
482 | } |
483 | |
484 | |
485 | int tst_QDom::hasAttributesHelper( const QDomNode& node ) |
486 | { |
487 | int visitedNodes = 1; |
488 | if ( node.hasAttributes() ) { |
489 | if (node.attributes().count() == 0) |
490 | return -1; |
491 | // QVERIFY( node.attributes().count() > 0 ); |
492 | } else { |
493 | if (node.attributes().count() != 0) |
494 | return -1; |
495 | // QVERIFY( node.attributes().count() == 0 ); |
496 | } |
497 | |
498 | QDomNodeList children = node.childNodes(); |
499 | for ( int i=0; i<children.count(); i++ ) { |
500 | int j = hasAttributesHelper( node: children.item(index: i) ); |
501 | if (j < 0) |
502 | return -1; |
503 | visitedNodes += j; |
504 | } |
505 | return visitedNodes; |
506 | } |
507 | |
508 | |
509 | void tst_QDom::save_data() |
510 | { |
511 | const QString doc01( |
512 | "<a1>\n" |
513 | " <b1>\n" |
514 | " <c1>\n" |
515 | " <d1/>\n" |
516 | " </c1>\n" |
517 | " <c2/>\n" |
518 | " </b1>\n" |
519 | " <b2/>\n" |
520 | " <b3>\n" |
521 | " <c1/>\n" |
522 | " </b3>\n" |
523 | "</a1>\n" |
524 | ); |
525 | |
526 | QTest::addColumn<QString>(name: "doc" ); |
527 | QTest::addColumn<int>(name: "indent" ); |
528 | QTest::addColumn<QString>(name: "res" ); |
529 | |
530 | QTest::newRow( dataTag: "01" ) << doc01 << 0 << QString(doc01).replace( rx: QRegExp(" " ), after: "" ); |
531 | QTest::newRow( dataTag: "02" ) << doc01 << 1 << doc01; |
532 | QTest::newRow( dataTag: "03" ) << doc01 << 2 << QString(doc01).replace( rx: QRegExp(" " ), after: " " ); |
533 | QTest::newRow( dataTag: "04" ) << doc01 << 10 << QString(doc01).replace( rx: QRegExp(" " ), after: " " ); |
534 | } |
535 | |
536 | void tst_QDom::save() |
537 | { |
538 | QFETCH( QString, doc ); |
539 | QFETCH( int, indent ); |
540 | |
541 | QDomDocument domDoc; |
542 | QVERIFY( domDoc.setContent( doc ) ); |
543 | |
544 | QString eRes; |
545 | QTextStream ts( &eRes, QIODevice::WriteOnly ); |
546 | domDoc.save( ts, indent ); |
547 | |
548 | QTEST( eRes, "res" ); |
549 | } |
550 | |
551 | void tst_QDom::initTestCase() |
552 | { |
553 | QString testFile = QFINDTESTDATA("testdata/testCodecs.txt" ); |
554 | if (testFile.isEmpty()) |
555 | QFAIL("Cannot find testdata/testCodecs.txt" ); |
556 | QFile file(testFile); |
557 | QVERIFY(file.open(QIODevice::ReadOnly|QIODevice::Text)); |
558 | |
559 | QByteArray codecName; |
560 | |
561 | m_testCodecs = file.readAll().split(sep: '\n'); |
562 | if (m_testCodecs.last().isEmpty()) |
563 | m_testCodecs.removeLast(); |
564 | |
565 | } |
566 | |
567 | void tst_QDom::saveWithSerialization() const |
568 | { |
569 | QFETCH(QString, fileName); |
570 | |
571 | QFile f(fileName); |
572 | QVERIFY(f.open(QIODevice::ReadOnly)); |
573 | |
574 | QDomDocument doc; |
575 | |
576 | // Read the document |
577 | QVERIFY(doc.setContent(&f)); |
578 | |
579 | QByteArray codecName; |
580 | |
581 | foreach (codecName, m_testCodecs) { |
582 | |
583 | /* Write out doc in the specified codec. */ |
584 | QByteArray storage; |
585 | QBuffer writeDevice(&storage); |
586 | QVERIFY(writeDevice.open(QIODevice::WriteOnly)); |
587 | |
588 | QTextStream s(&writeDevice); |
589 | QTextCodec *codec = QTextCodec::codecForName(name: codecName); |
590 | QVERIFY2(codec, qPrintable(QString::fromLatin1("Failed to load codec %1" ).arg(QString::fromLatin1(codecName.constData())))); |
591 | s.setCodec(codec); |
592 | |
593 | doc.save(s, 0, QDomNode::EncodingFromTextStream); |
594 | s.flush(); |
595 | writeDevice.close(); |
596 | |
597 | QBuffer readDevice(&storage); |
598 | QVERIFY(readDevice.open(QIODevice::ReadOnly)); |
599 | |
600 | QDomDocument result; |
601 | |
602 | QString msg; |
603 | int line = 0; |
604 | int column = 0; |
605 | |
606 | QVERIFY2(result.setContent(&readDevice, &msg, &line, &column), |
607 | qPrintable(QString::fromLatin1("Failed for codec %1: line %2, column %3: %4, content: %5" ) |
608 | .arg(QString::fromLatin1(codecName.constData()), |
609 | QString::number(line), |
610 | QString::number(column), |
611 | msg, |
612 | codec->toUnicode(storage)))); |
613 | if(!compareDocuments(doc1: doc, doc2: result)) |
614 | { |
615 | QCOMPARE(doc.toString(), result.toString()); |
616 | |
617 | /* We put this one here as well, in case the QCOMPARE above for some strange reason |
618 | * nevertheless succeeds. */ |
619 | QVERIFY2(false, qPrintable(QString::fromLatin1("Failed for codec %1" ).arg(QString::fromLatin1(codecName.constData())))); |
620 | } |
621 | } |
622 | } |
623 | |
624 | void tst_QDom::saveWithSerialization_data() const |
625 | { |
626 | QTest::addColumn<QString>(name: "fileName" ); |
627 | const QString prefix = QFINDTESTDATA("testdata/toString_01" ); |
628 | if (prefix.isEmpty()) |
629 | QFAIL("Cannot find testdata!" ); |
630 | QTest::newRow(dataTag: "doc01.xml" ) << QString(prefix + "/doc01.xml" ); |
631 | QTest::newRow(dataTag: "doc02.xml" ) << QString(prefix + "/doc02.xml" ); |
632 | QTest::newRow(dataTag: "doc03.xml" ) << QString(prefix + "/doc03.xml" ); |
633 | QTest::newRow(dataTag: "doc04.xml" ) << QString(prefix + "/doc04.xml" ); |
634 | QTest::newRow(dataTag: "doc05.xml" ) << QString(prefix + "/doc05.xml" ); |
635 | |
636 | QTest::newRow(dataTag: "doc_euc-jp.xml" ) << QString(prefix + "/doc_euc-jp.xml" ); |
637 | QTest::newRow(dataTag: "doc_iso-2022-jp.xml" ) << QString(prefix + "/doc_iso-2022-jp.xml" ); |
638 | QTest::newRow(dataTag: "doc_little-endian.xml" ) << QString(prefix + "/doc_little-endian.xml" ); |
639 | QTest::newRow(dataTag: "doc_utf-16.xml" ) << QString(prefix + "/doc_utf-16.xml" ); |
640 | QTest::newRow(dataTag: "doc_utf-8.xml" ) << QString(prefix + "/doc_utf-8.xml" ); |
641 | } |
642 | |
643 | void tst_QDom::cloneNode_data() |
644 | { |
645 | const QString doc01( |
646 | "<a1>\n" |
647 | " <b1>\n" |
648 | " <c1>\n" |
649 | " <d1/>\n" |
650 | " </c1>\n" |
651 | " <c2/>\n" |
652 | " </b1>\n" |
653 | " <b2/>\n" |
654 | " <b3>\n" |
655 | " <c1/>\n" |
656 | " </b3>\n" |
657 | "</a1>\n" |
658 | ); |
659 | QList<QVariant> nodeB1; |
660 | nodeB1 << 0; |
661 | |
662 | QList<QVariant> nodeC1; |
663 | nodeC1 << 0 << 0; |
664 | |
665 | QList<QVariant> nodeC2; |
666 | nodeC2 << 0 << 1; |
667 | |
668 | QTest::addColumn<QString>(name: "doc" ); |
669 | QTest::addColumn<QList<QVariant> >(name: "pathToNode" ); |
670 | QTest::addColumn<bool>(name: "deep" ); |
671 | |
672 | QTest::newRow( dataTag: "noDeep_01" ) << doc01 << nodeB1 << false; |
673 | QTest::newRow( dataTag: "noDeep_02" ) << doc01 << nodeC1 << false; |
674 | QTest::newRow( dataTag: "noDeep_03" ) << doc01 << nodeC2 << false; |
675 | |
676 | QTest::newRow( dataTag: "deep_01" ) << doc01 << nodeB1 << true; |
677 | QTest::newRow( dataTag: "deep_02" ) << doc01 << nodeC1 << true; |
678 | QTest::newRow( dataTag: "deep_03" ) << doc01 << nodeC2 << true; |
679 | } |
680 | |
681 | void tst_QDom::cloneNode() |
682 | { |
683 | QFETCH( QString, doc ); |
684 | QFETCH( QList<QVariant>, pathToNode ); |
685 | QFETCH( bool, deep ); |
686 | QDomDocument domDoc; |
687 | QVERIFY( domDoc.setContent( doc ) ); |
688 | QDomNode node = findDomNode( doc: domDoc, pathToNode ); |
689 | QVERIFY(!node.isNull()); |
690 | |
691 | QDomNode clonedNode = node.cloneNode( deep ); |
692 | QVERIFY( compareNodes( node, clonedNode, deep ) ); |
693 | |
694 | QDomNode parent = node.parentNode(); |
695 | if ( !parent.isNull() ) { |
696 | node = parent.replaceChild( newChild: clonedNode, oldChild: node ); // swap the nodes |
697 | QVERIFY( !node.isNull() ); |
698 | QVERIFY( compareNodes( node, clonedNode, deep ) ); |
699 | } |
700 | } |
701 | |
702 | |
703 | void tst_QDom::ownerElementTask45192_data() |
704 | { |
705 | const QString doc( |
706 | "<root>\n" |
707 | " <item name=\"test\" >\n" |
708 | " </item>\n" |
709 | "</root>" |
710 | ); |
711 | |
712 | QTest::addColumn<QString>(name: "doc" ); |
713 | QTest::newRow(dataTag: "doc" ) << doc; |
714 | } |
715 | |
716 | void tst_QDom::ownerElementTask45192() |
717 | { |
718 | QFETCH( QString, doc ); |
719 | QDomDocument domDoc; |
720 | QVERIFY( domDoc.setContent( doc ) ); |
721 | |
722 | QDomNode item = domDoc.documentElement().firstChild(); |
723 | QDomNode clone = item.cloneNode(deep: false); |
724 | |
725 | QVERIFY( clone == clone.attributes().namedItem("name" ).toAttr().ownerElement() ); |
726 | } |
727 | |
728 | void tst_QDom::ownerDocument_data() |
729 | { |
730 | cloneNode_data(); |
731 | } |
732 | |
733 | #define OWNERDOCUMENT_CREATE_TEST( t, x ) \ |
734 | { \ |
735 | t n = x; \ |
736 | QVERIFY( n.ownerDocument() == domDoc ); \ |
737 | } |
738 | |
739 | #define OWNERDOCUMENT_IMPORTNODE_TEST( t, x ) \ |
740 | { \ |
741 | QDomNode importedNode; \ |
742 | t n = x; \ |
743 | QVERIFY( n.ownerDocument() != domDoc ); \ |
744 | importedNode = domDoc.importNode( n, deep ); \ |
745 | QVERIFY( n.ownerDocument() != domDoc ); \ |
746 | QVERIFY( importedNode.ownerDocument() == domDoc ); \ |
747 | } |
748 | |
749 | void tst_QDom::ownerDocument() |
750 | { |
751 | QFETCH( QString, doc ); |
752 | QFETCH( QList<QVariant>, pathToNode ); |
753 | QFETCH( bool, deep ); |
754 | QDomDocument domDoc; |
755 | QVERIFY( domDoc.setContent( doc ) ); |
756 | QDomNode node = findDomNode( doc: domDoc, pathToNode ); |
757 | QVERIFY(!node.isNull()); |
758 | |
759 | QVERIFY( node.ownerDocument() == domDoc ); |
760 | |
761 | // Does cloneNode() keep the ownerDocument()? |
762 | { |
763 | QDomNode clonedNode = node.cloneNode( deep ); |
764 | QVERIFY( node.ownerDocument() == domDoc ); |
765 | QVERIFY( clonedNode.ownerDocument() == domDoc ); |
766 | } |
767 | |
768 | // If the original DOM node is replaced with the cloned node, does this |
769 | // keep the ownerDocument()? |
770 | { |
771 | QDomNode clonedNode = node.cloneNode( deep ); |
772 | QDomNode parent = node.parentNode(); |
773 | if ( !parent.isNull() ) { |
774 | node = parent.replaceChild( newChild: clonedNode, oldChild: node ); // swap the nodes |
775 | QVERIFY( node.ownerDocument() == domDoc ); |
776 | QVERIFY( clonedNode.ownerDocument() == domDoc ); |
777 | } |
778 | } |
779 | |
780 | // test QDomDocument::create...() |
781 | { |
782 | OWNERDOCUMENT_CREATE_TEST( QDomAttr, domDoc.createAttribute( "foo" ) ); |
783 | OWNERDOCUMENT_CREATE_TEST( QDomAttr, domDoc.createAttributeNS( "foo" , "bar" ) ); |
784 | OWNERDOCUMENT_CREATE_TEST( QDomCDATASection, domDoc.createCDATASection( "foo" ) ); |
785 | OWNERDOCUMENT_CREATE_TEST( QDomComment, domDoc.createComment( "foo" ) ); |
786 | OWNERDOCUMENT_CREATE_TEST( QDomDocumentFragment, domDoc.createDocumentFragment() ); |
787 | OWNERDOCUMENT_CREATE_TEST( QDomElement, domDoc.createElement( "foo" ) ); |
788 | OWNERDOCUMENT_CREATE_TEST( QDomElement, domDoc.createElementNS( "foo" , "bar" ) ); |
789 | OWNERDOCUMENT_CREATE_TEST( QDomEntityReference, domDoc.createEntityReference( "foo" ) ); |
790 | OWNERDOCUMENT_CREATE_TEST( QDomProcessingInstruction, domDoc.createProcessingInstruction( "foo" , "bar" ) ); |
791 | OWNERDOCUMENT_CREATE_TEST( QDomText, domDoc.createTextNode( "foo" ) ); |
792 | } |
793 | |
794 | // test importNode() |
795 | { |
796 | QDomDocument doc2; |
797 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomAttr, doc2.createAttribute( "foo" ) ); |
798 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomAttr, doc2.createAttributeNS( "foo" , "bar" ) ); |
799 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomCDATASection, doc2.createCDATASection( "foo" ) ); |
800 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomComment, doc2.createComment( "foo" ) ); |
801 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomDocumentFragment, doc2.createDocumentFragment() ); |
802 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomElement, doc2.createElement( "foo" ) ); |
803 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomElement, doc2.createElementNS( "foo" , "bar" ) ); |
804 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomEntityReference, doc2.createEntityReference( "foo" ) ); |
805 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomProcessingInstruction, doc2.createProcessingInstruction( "foo" , "bar" ) ); |
806 | OWNERDOCUMENT_IMPORTNODE_TEST( QDomText, doc2.createTextNode( "foo" ) ); |
807 | |
808 | // QTBUG-12927 |
809 | QVERIFY(doc2.importNode(QDomNode(), deep).isNull()); |
810 | } |
811 | } |
812 | |
813 | void tst_QDom::ownerDocumentTask27424_data() |
814 | { |
815 | QTest::addColumn<bool>(name: "insertLevel1AfterCstr" ); |
816 | QTest::addColumn<bool>(name: "insertLevel2AfterCstr" ); |
817 | QTest::addColumn<bool>(name: "insertLevel3AfterCstr" ); |
818 | |
819 | QTest::newRow( dataTag: "000" ) << false << false << false; |
820 | QTest::newRow( dataTag: "001" ) << false << false << true; |
821 | QTest::newRow( dataTag: "010" ) << false << true << false; |
822 | QTest::newRow( dataTag: "011" ) << false << true << true; |
823 | QTest::newRow( dataTag: "100" ) << true << false << false; |
824 | QTest::newRow( dataTag: "101" ) << true << false << true; |
825 | QTest::newRow( dataTag: "110" ) << true << true << false; |
826 | QTest::newRow( dataTag: "111" ) << true << true << true; |
827 | } |
828 | |
829 | void tst_QDom::ownerDocumentTask27424() |
830 | { |
831 | QFETCH( bool, insertLevel1AfterCstr ); |
832 | QFETCH( bool, insertLevel2AfterCstr ); |
833 | QFETCH( bool, insertLevel3AfterCstr ); |
834 | |
835 | QDomDocument doc("TestXML" ); |
836 | |
837 | QDomElement level1 = doc.createElement(tagName: "Level_1" ); |
838 | QVERIFY( level1.ownerDocument() == doc ); |
839 | |
840 | if ( insertLevel1AfterCstr ) { |
841 | doc.appendChild(newChild: level1); |
842 | QVERIFY( level1.ownerDocument() == doc ); |
843 | } |
844 | |
845 | QDomElement level2 = level1.ownerDocument().createElement(tagName: "Level_2" ); |
846 | QVERIFY( level1.ownerDocument() == doc ); |
847 | QVERIFY( level2.ownerDocument() == doc ); |
848 | |
849 | if ( insertLevel2AfterCstr ) { |
850 | level1.appendChild(newChild: level2); |
851 | QVERIFY( level1.ownerDocument() == doc ); |
852 | QVERIFY( level2.ownerDocument() == doc ); |
853 | } |
854 | |
855 | QDomElement level3 = level2.ownerDocument().createElement(tagName: "Level_3" ); |
856 | QVERIFY( level1.ownerDocument() == doc ); |
857 | QVERIFY( level2.ownerDocument() == doc ); |
858 | QVERIFY( level3.ownerDocument() == doc ); |
859 | |
860 | if ( insertLevel3AfterCstr ) { |
861 | level2.appendChild(newChild: level3); |
862 | QVERIFY( level1.ownerDocument() == doc ); |
863 | QVERIFY( level2.ownerDocument() == doc ); |
864 | QVERIFY( level3.ownerDocument() == doc ); |
865 | } |
866 | |
867 | QDomNode level4 = level3.ownerDocument().createTextNode(data: "This_is_a_value!" ); |
868 | QVERIFY( level4.ownerDocument() == doc ); |
869 | |
870 | level3.appendChild(newChild: level4); |
871 | QVERIFY( level1.ownerDocument() == doc ); |
872 | QVERIFY( level2.ownerDocument() == doc ); |
873 | QVERIFY( level3.ownerDocument() == doc ); |
874 | QVERIFY( level4.ownerDocument() == doc ); |
875 | |
876 | if ( !insertLevel3AfterCstr ) { |
877 | level2.appendChild(newChild: level3); |
878 | QVERIFY( level1.ownerDocument() == doc ); |
879 | QVERIFY( level2.ownerDocument() == doc ); |
880 | QVERIFY( level3.ownerDocument() == doc ); |
881 | QVERIFY( level4.ownerDocument() == doc ); |
882 | } |
883 | |
884 | if ( !insertLevel2AfterCstr ) { |
885 | level1.appendChild(newChild: level2); |
886 | QVERIFY( level1.ownerDocument() == doc ); |
887 | QVERIFY( level2.ownerDocument() == doc ); |
888 | QVERIFY( level3.ownerDocument() == doc ); |
889 | QVERIFY( level4.ownerDocument() == doc ); |
890 | } |
891 | |
892 | if ( !insertLevel1AfterCstr ) { |
893 | doc.appendChild(newChild: level1); |
894 | QVERIFY( level1.ownerDocument() == doc ); |
895 | QVERIFY( level2.ownerDocument() == doc ); |
896 | QVERIFY( level3.ownerDocument() == doc ); |
897 | QVERIFY( level4.ownerDocument() == doc ); |
898 | } |
899 | } |
900 | |
901 | void tst_QDom::parentNode_data() |
902 | { |
903 | cloneNode_data(); |
904 | } |
905 | |
906 | #define PARENTNODE_CREATE_TEST( t, x ) \ |
907 | { \ |
908 | t n = x; \ |
909 | QVERIFY( n.parentNode().isNull() ); \ |
910 | } |
911 | |
912 | void tst_QDom::parentNode() |
913 | { |
914 | QFETCH( QString, doc ); |
915 | QFETCH( QList<QVariant>, pathToNode ); |
916 | QFETCH( bool, deep ); |
917 | QDomDocument domDoc; |
918 | QVERIFY( domDoc.setContent( doc ) ); |
919 | QDomNode node = findDomNode( doc: domDoc, pathToNode ); |
920 | QVERIFY(!node.isNull()); |
921 | Q_UNUSED(deep); |
922 | |
923 | // test QDomDocument::create...() |
924 | { |
925 | PARENTNODE_CREATE_TEST( QDomAttr, domDoc.createAttribute( "foo" ) ); |
926 | PARENTNODE_CREATE_TEST( QDomAttr, domDoc.createAttributeNS( "foo" , "bar" ) ); |
927 | PARENTNODE_CREATE_TEST( QDomCDATASection, domDoc.createCDATASection( "foo" ) ); |
928 | PARENTNODE_CREATE_TEST( QDomComment, domDoc.createComment( "foo" ) ); |
929 | PARENTNODE_CREATE_TEST( QDomDocumentFragment, domDoc.createDocumentFragment() ); |
930 | PARENTNODE_CREATE_TEST( QDomElement, domDoc.createElement( "foo" ) ); |
931 | PARENTNODE_CREATE_TEST( QDomElement, domDoc.createElementNS( "foo" , "bar" ) ); |
932 | PARENTNODE_CREATE_TEST( QDomEntityReference, domDoc.createEntityReference( "foo" ) ); |
933 | PARENTNODE_CREATE_TEST( QDomProcessingInstruction, domDoc.createProcessingInstruction( "foo" , "bar" ) ); |
934 | PARENTNODE_CREATE_TEST( QDomText, domDoc.createTextNode( "foo" ) ); |
935 | } |
936 | } |
937 | |
938 | |
939 | void tst_QDom::documentCreationTask27424_data() |
940 | { |
941 | QTest::addColumn<bool>(name: "insertLevel1AfterCstr" ); |
942 | QTest::addColumn<bool>(name: "insertLevel2AfterCstr" ); |
943 | QTest::addColumn<bool>(name: "insertLevel3AfterCstr" ); |
944 | |
945 | QTest::newRow( dataTag: "000" ) << false << false << false; |
946 | QTest::newRow( dataTag: "001" ) << false << false << true; |
947 | QTest::newRow( dataTag: "010" ) << false << true << false; |
948 | QTest::newRow( dataTag: "011" ) << false << true << true; |
949 | QTest::newRow( dataTag: "100" ) << true << false << false; |
950 | QTest::newRow( dataTag: "101" ) << true << false << true; |
951 | QTest::newRow( dataTag: "110" ) << true << true << false; |
952 | QTest::newRow( dataTag: "111" ) << true << true << true; |
953 | } |
954 | |
955 | void tst_QDom::documentCreationTask27424() |
956 | { |
957 | QFETCH( bool, insertLevel1AfterCstr ); |
958 | QFETCH( bool, insertLevel2AfterCstr ); |
959 | QFETCH( bool, insertLevel3AfterCstr ); |
960 | |
961 | QDomDocument docRes; |
962 | QVERIFY( docRes.setContent( QString( |
963 | "<!DOCTYPE TestXML>\n" |
964 | "<Level_1>\n" |
965 | " <Level_2>\n" |
966 | " <Level_3>This_is_a_value!</Level_3>\n" |
967 | " </Level_2>\n" |
968 | "</Level_1>" |
969 | ) ) ); |
970 | |
971 | QDomDocument doc("TestXML" ); |
972 | |
973 | QDomElement level1 = doc.createElement(tagName: "Level_1" ); |
974 | if ( insertLevel1AfterCstr ) |
975 | doc.appendChild(newChild: level1); |
976 | |
977 | QDomElement level2 = level1.ownerDocument().createElement(tagName: "Level_2" ); |
978 | if ( insertLevel2AfterCstr ) |
979 | level1.appendChild(newChild: level2); |
980 | |
981 | QDomElement level3 = level2.ownerDocument().createElement(tagName: "Level_3" ); |
982 | if ( insertLevel3AfterCstr ) |
983 | level2.appendChild(newChild: level3); |
984 | |
985 | QDomNode level4 = level3.ownerDocument().createTextNode(data: "This_is_a_value!" ); |
986 | level3.appendChild(newChild: level4); |
987 | |
988 | if ( !insertLevel3AfterCstr ) |
989 | level2.appendChild(newChild: level3); |
990 | if ( !insertLevel2AfterCstr ) |
991 | level1.appendChild(newChild: level2); |
992 | if ( !insertLevel1AfterCstr ) |
993 | doc.appendChild(newChild: level1); |
994 | |
995 | QVERIFY( compareDocuments( doc, docRes ) ); |
996 | } |
997 | |
998 | |
999 | bool tst_QDom::isFakeXMLDeclaration(const QDomNode &node) |
1000 | { |
1001 | return node.isProcessingInstruction() && |
1002 | node.nodeName() == QLatin1String("xml" ); |
1003 | } |
1004 | |
1005 | bool tst_QDom::isDeepEqual(const QDomNode &n1, const QDomNode &n2) |
1006 | { |
1007 | const QDomNode::NodeType nt = n1.nodeType(); |
1008 | |
1009 | if(nt != n2.nodeType()) |
1010 | return false; |
1011 | |
1012 | if(n1.nodeName() != n2.nodeName() |
1013 | || n1.namespaceURI() != n2.namespaceURI() |
1014 | || n1.nodeValue() != n2.nodeValue()) |
1015 | return false; |
1016 | |
1017 | /* Check the children. */ |
1018 | const QDomNodeList children1(n1.childNodes()); |
1019 | const QDomNodeList children2(n2.childNodes()); |
1020 | uint len1 = children1.length(); |
1021 | uint len2 = children2.length(); |
1022 | uint i1 = 0; |
1023 | uint i2 = 0; |
1024 | |
1025 | if(len1 != 0 && isFakeXMLDeclaration(node: children1.at(index: 0))) |
1026 | ++i1; |
1027 | |
1028 | if(len2 != 0 && isFakeXMLDeclaration(node: children2.at(index: 0))) |
1029 | ++i2; |
1030 | |
1031 | if(len1 - i1 != len2 - i2) |
1032 | return false; |
1033 | |
1034 | // We jump over the first to skip the processing instructions that |
1035 | // are (incorrectly) used as XML declarations. |
1036 | for(; i1 < len1; ++i1) |
1037 | { |
1038 | if(!isDeepEqual(n1: children1.at(index: i1), n2: children2.at(index: i2))) |
1039 | return false; |
1040 | |
1041 | ++i2; |
1042 | } |
1043 | |
1044 | return true; |
1045 | } |
1046 | |
1047 | /* |
1048 | Returns true if \a doc1 and \a doc2 represent the same XML document, i.e. |
1049 | they have the same informational content. Otherwise, this function returns |
1050 | false. |
1051 | */ |
1052 | bool tst_QDom::compareDocuments( const QDomDocument &doc1, const QDomDocument &doc2 ) |
1053 | { |
1054 | return isDeepEqual(n1: doc1, n2: doc2); |
1055 | } |
1056 | |
1057 | /* |
1058 | Returns true if \a node1 and \a node2 represent the same XML node, i.e. |
1059 | they have the same informational content. Otherwise, this function returns |
1060 | false. |
1061 | |
1062 | If \a deep is true, children of the nodes are also tested. If \a deep is |
1063 | false, only \a node1 and \a node 2 are compared. |
1064 | */ |
1065 | bool tst_QDom::compareNodes( const QDomNode &node1, const QDomNode &node2, bool deep ) |
1066 | { |
1067 | if ( deep ) { |
1068 | QString str1; |
1069 | { |
1070 | QTextStream stream( &str1 ); |
1071 | stream << node1; |
1072 | } |
1073 | QString str2; |
1074 | { |
1075 | QTextStream stream( &str2 ); |
1076 | stream << node2; |
1077 | } |
1078 | return str1 == str2; |
1079 | } |
1080 | |
1081 | if ( node1.isNull() && node2.isNull() ) |
1082 | return true; |
1083 | // ### I am not sure if this test is complete |
1084 | bool equal = node1.nodeName() == node2.nodeName(); |
1085 | equal = equal && node1.nodeType() == node2.nodeType(); |
1086 | equal = equal && node1.localName() == node2.localName(); |
1087 | equal = equal && node1.nodeValue() == node2.nodeValue(); |
1088 | equal = equal && node1.prefix() == node2.prefix(); |
1089 | |
1090 | return equal; |
1091 | } |
1092 | |
1093 | /* |
1094 | \a pathToNode is a list of indices to wanted node in \a doc. Returns the |
1095 | wanted node. |
1096 | */ |
1097 | QDomNode tst_QDom::findDomNode( const QDomDocument &doc, const QList<QVariant> &pathToNode ) |
1098 | { |
1099 | QDomNode node = doc; |
1100 | QList<QVariant>::const_iterator it; |
1101 | for ( it = pathToNode.begin(); it != pathToNode.end(); ++it ) { |
1102 | QDomNodeList children = node.childNodes(); |
1103 | node = children.item( index: (*it).toInt() ); |
1104 | // QVERIFY( !node.isNull() ); |
1105 | } |
1106 | return node; |
1107 | } |
1108 | |
1109 | void tst_QDom::browseElements() |
1110 | { |
1111 | QDomDocument doc; |
1112 | QDomElement root = doc.createElement(tagName: "foo" ); |
1113 | doc.appendChild(newChild: root); |
1114 | root.appendChild(newChild: doc.createElement(tagName: "bar" )); |
1115 | root.appendChild(newChild: doc.createElement(tagName: "bop" )); |
1116 | root.appendChild(newChild: doc.createElement(tagName: "bar" )); |
1117 | root.appendChild(newChild: doc.createElement(tagName: "bop" )); |
1118 | |
1119 | QVERIFY(doc.firstChildElement("ding" ).isNull()); |
1120 | QDomElement foo = doc.firstChildElement(tagName: "foo" ); |
1121 | QVERIFY(!foo.isNull()); |
1122 | QVERIFY(foo.firstChildElement("ding" ).isNull()); |
1123 | QVERIFY(foo.nextSiblingElement("foo" ).isNull()); |
1124 | QVERIFY(foo.previousSiblingElement("bar" ).isNull()); |
1125 | QVERIFY(foo.nextSiblingElement().isNull()); |
1126 | QVERIFY(foo.previousSiblingElement().isNull()); |
1127 | |
1128 | QDomElement bar = foo.firstChildElement(tagName: "bar" ); |
1129 | QVERIFY(!bar.isNull()); |
1130 | QVERIFY(bar.previousSiblingElement("bar" ).isNull()); |
1131 | QVERIFY(bar.previousSiblingElement().isNull()); |
1132 | QCOMPARE(bar.nextSiblingElement("bar" ).tagName(), QLatin1String("bar" )); |
1133 | QVERIFY(bar.nextSiblingElement("bar" ).nextSiblingElement("bar" ).isNull()); |
1134 | |
1135 | QDomElement bop = foo.firstChildElement(tagName: "bop" ); |
1136 | QVERIFY(!bop.isNull()); |
1137 | QCOMPARE(bar.nextSiblingElement(), bop); |
1138 | QCOMPARE(bop.nextSiblingElement("bop" ), foo.lastChildElement("bop" )); |
1139 | QCOMPARE(bop.previousSiblingElement("bar" ), foo.firstChildElement("bar" )); |
1140 | QCOMPARE(bop.previousSiblingElement("bar" ), foo.firstChildElement()); |
1141 | } |
1142 | |
1143 | void tst_QDom::domNodeMapAndList() |
1144 | { |
1145 | QString xml_str = QString::fromLatin1(str: "<foo ding='dong'></foo>" ); |
1146 | |
1147 | QDomDocument doc; |
1148 | QVERIFY(doc.setContent(xml_str)); |
1149 | |
1150 | QDomNamedNodeMap map = doc.documentElement().attributes(); |
1151 | QCOMPARE(map.item(0).nodeName(), QString("ding" )); |
1152 | QCOMPARE(map.item(1).nodeName(), QString()); // Make sure we don't assert |
1153 | |
1154 | QDomNodeList list = doc.elementsByTagName(tagname: "foo" ); |
1155 | QCOMPARE(list.item(0).nodeName(), QString("foo" )); |
1156 | QCOMPARE(list.item(1).nodeName(), QString()); // Make sure we don't assert |
1157 | } |
1158 | |
1159 | // Verifies that a default-constructed QDomDocument is null, and that calling |
1160 | // any of the factory functions causes it to be non-null. |
1161 | #define TEST_NULL_DOCUMENT(func) \ |
1162 | { \ |
1163 | QDomDocument doc; \ |
1164 | QVERIFY(doc.isNull()); \ |
1165 | QVERIFY(!doc.func.isNull()); \ |
1166 | QVERIFY(!doc.isNull()); \ |
1167 | } |
1168 | |
1169 | void tst_QDom::nullDocument() |
1170 | { |
1171 | TEST_NULL_DOCUMENT(createAttribute("foo" )) |
1172 | TEST_NULL_DOCUMENT(createAttributeNS("http://foo/" , "bar" )) |
1173 | TEST_NULL_DOCUMENT(createCDATASection("foo" )) |
1174 | TEST_NULL_DOCUMENT(createComment("foo" )) |
1175 | TEST_NULL_DOCUMENT(createDocumentFragment()) |
1176 | TEST_NULL_DOCUMENT(createElement("foo" )) |
1177 | TEST_NULL_DOCUMENT(createElementNS("http://foo/" , "foo" )) |
1178 | TEST_NULL_DOCUMENT(createEntityReference("foo" )) |
1179 | TEST_NULL_DOCUMENT(createProcessingInstruction("foo" , "bar" )) |
1180 | TEST_NULL_DOCUMENT(createTextNode("foo" )) |
1181 | QDomDocument doc2; |
1182 | QDomElement elt = doc2.createElement(tagName: "foo" ); |
1183 | doc2.appendChild(newChild: elt); |
1184 | TEST_NULL_DOCUMENT(importNode(elt, true)) |
1185 | } |
1186 | |
1187 | #undef TEST_NULL_DOCUMENT |
1188 | |
1189 | void tst_QDom::invalidName_data() |
1190 | { |
1191 | QTest::addColumn<QString>(name: "in_name" ); |
1192 | QTest::addColumn<bool>(name: "ok_AcceptInvalidChars" ); |
1193 | QTest::addColumn<bool>(name: "ok_DropInvalidChars" ); |
1194 | QTest::addColumn<bool>(name: "ok_ReturnNullNode" ); |
1195 | QTest::addColumn<QString>(name: "out_name" ); |
1196 | |
1197 | QTest::newRow( dataTag: "foo" ) << QString("foo" ) << true << true << true << QString("foo" ); |
1198 | QTest::newRow( dataTag: "_f.o-o:" ) << QString("_f.o-o:" ) << true << true << true << QString("_f.o-o:" ); |
1199 | QTest::newRow( dataTag: "...:." ) << QString("...:." ) << true << true << false << QString(":." ); |
1200 | QTest::newRow( dataTag: "empty" ) << QString() << false << false << false << QString(); |
1201 | QTest::newRow( dataTag: "~f~o~o~" ) << QString("~f~o~o~" ) << true << true << false << QString("foo" ); |
1202 | QTest::newRow( dataTag: "~" ) << QString("~" ) << true << false << false << QString(); |
1203 | QTest::newRow( dataTag: "..." ) << QString("..." ) << true << false << false << QString(); |
1204 | } |
1205 | |
1206 | void tst_QDom::invalidName() |
1207 | { |
1208 | QFETCH( QString, in_name ); |
1209 | QFETCH( bool, ok_AcceptInvalidChars ); |
1210 | QFETCH( bool, ok_DropInvalidChars ); |
1211 | QFETCH( bool, ok_ReturnNullNode ); |
1212 | QFETCH( QString, out_name ); |
1213 | |
1214 | QDomImplementation impl; |
1215 | QDomDocument doc; |
1216 | |
1217 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars); |
1218 | |
1219 | { |
1220 | QDomElement elt = doc.createElement(tagName: in_name); |
1221 | QDomElement elt_ns = doc.createElementNS(nsURI: "foo" , qName: "foo:" + in_name); |
1222 | QDomAttr attr = doc.createAttribute(name: in_name); |
1223 | QDomAttr attr_ns = doc.createAttributeNS(nsURI: "foo" , qName: "foo:" + in_name); |
1224 | QDomEntityReference ref = doc.createEntityReference(name: in_name); |
1225 | |
1226 | QCOMPARE(!elt.isNull(), ok_AcceptInvalidChars); |
1227 | QCOMPARE(!elt_ns.isNull(), ok_AcceptInvalidChars); |
1228 | QCOMPARE(!attr.isNull(), ok_AcceptInvalidChars); |
1229 | QCOMPARE(!attr_ns.isNull(), ok_AcceptInvalidChars); |
1230 | QCOMPARE(!ref.isNull(), ok_AcceptInvalidChars); |
1231 | |
1232 | if (ok_AcceptInvalidChars) { |
1233 | QCOMPARE(elt.tagName(), in_name); |
1234 | QCOMPARE(elt_ns.tagName(), in_name); |
1235 | QCOMPARE(attr.name(), in_name); |
1236 | QCOMPARE(attr_ns.name(), in_name); |
1237 | QCOMPARE(ref.nodeName(), in_name); |
1238 | } |
1239 | } |
1240 | |
1241 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars); |
1242 | |
1243 | { |
1244 | QDomElement elt = doc.createElement(tagName: in_name); |
1245 | QDomElement elt_ns = doc.createElementNS(nsURI: "foo" , qName: "foo:" + in_name); |
1246 | QDomAttr attr = doc.createAttribute(name: in_name); |
1247 | QDomAttr attr_ns = doc.createAttributeNS(nsURI: "foo" , qName: "foo:" + in_name); |
1248 | QDomEntityReference ref = doc.createEntityReference(name: in_name); |
1249 | |
1250 | QCOMPARE(!elt.isNull(), ok_DropInvalidChars); |
1251 | QCOMPARE(!elt_ns.isNull(), ok_DropInvalidChars); |
1252 | QCOMPARE(!attr.isNull(), ok_DropInvalidChars); |
1253 | QCOMPARE(!attr_ns.isNull(), ok_DropInvalidChars); |
1254 | QCOMPARE(!ref.isNull(), ok_DropInvalidChars); |
1255 | |
1256 | if (ok_DropInvalidChars) { |
1257 | QCOMPARE(elt.tagName(), out_name); |
1258 | QCOMPARE(elt_ns.tagName(), out_name); |
1259 | QCOMPARE(attr.name(), out_name); |
1260 | QCOMPARE(attr_ns.name(), out_name); |
1261 | QCOMPARE(ref.nodeName(), out_name); |
1262 | } |
1263 | } |
1264 | |
1265 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); |
1266 | |
1267 | { |
1268 | QDomElement elt = doc.createElement(tagName: in_name); |
1269 | QDomElement elt_ns = doc.createElementNS(nsURI: "foo" , qName: "foo:" + in_name); |
1270 | QDomAttr attr = doc.createAttribute(name: in_name); |
1271 | QDomAttr attr_ns = doc.createAttributeNS(nsURI: "foo" , qName: "foo:" + in_name); |
1272 | QDomEntityReference ref = doc.createEntityReference(name: in_name); |
1273 | |
1274 | QCOMPARE(!elt.isNull(), ok_ReturnNullNode); |
1275 | QCOMPARE(!elt_ns.isNull(), ok_ReturnNullNode); |
1276 | QCOMPARE(!attr.isNull(), ok_ReturnNullNode); |
1277 | QCOMPARE(!attr_ns.isNull(), ok_ReturnNullNode); |
1278 | QCOMPARE(!ref.isNull(), ok_ReturnNullNode); |
1279 | |
1280 | if (ok_ReturnNullNode) { |
1281 | QCOMPARE(elt.tagName(), in_name); |
1282 | QCOMPARE(elt_ns.tagName(), in_name); |
1283 | QCOMPARE(attr.name(), in_name); |
1284 | QCOMPARE(attr_ns.name(), in_name); |
1285 | QCOMPARE(ref.nodeName(), in_name); |
1286 | } |
1287 | } |
1288 | } |
1289 | |
1290 | void tst_QDom::invalidQualifiedName_data() |
1291 | { |
1292 | QTest::addColumn<QString>(name: "in_name" ); |
1293 | QTest::addColumn<bool>(name: "ok_AcceptInvalidChars" ); |
1294 | QTest::addColumn<bool>(name: "ok_DropInvalidChars" ); |
1295 | QTest::addColumn<bool>(name: "ok_ReturnNullNode" ); |
1296 | QTest::addColumn<QString>(name: "out_name" ); |
1297 | |
1298 | QTest::newRow( dataTag: "foo" ) << QString("foo" ) << true << true << true << QString("foo" ); |
1299 | QTest::newRow( dataTag: "foo:bar" ) << QString("foo:bar" ) << true << true << true << QString("foo:bar" ); |
1300 | QTest::newRow( dataTag: "bar:" ) << QString("bar:" ) << false << false << false << QString(); |
1301 | QTest::newRow( dataTag: ":" ) << QString(":" ) << false << false << false << QString(); |
1302 | QTest::newRow( dataTag: "empty" ) << QString() << false << false << false << QString(); |
1303 | QTest::newRow(dataTag: "foo:...:." ) << QString("foo:...:." )<< true << true << false << QString("foo::." ); |
1304 | QTest::newRow(dataTag: "foo:~" ) << QString("foo:~" ) << true << false << false << QString(); |
1305 | QTest::newRow(dataTag: "foo:.~" ) << QString("foo:.~" ) << true << false << false << QString(); |
1306 | } |
1307 | |
1308 | void tst_QDom::invalidQualifiedName() |
1309 | { |
1310 | QFETCH( QString, in_name ); |
1311 | QFETCH( bool, ok_AcceptInvalidChars ); |
1312 | QFETCH( bool, ok_DropInvalidChars ); |
1313 | QFETCH( bool, ok_ReturnNullNode ); |
1314 | QFETCH( QString, out_name ); |
1315 | |
1316 | QDomImplementation impl; |
1317 | QDomDocument doc; |
1318 | |
1319 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars); |
1320 | |
1321 | { |
1322 | QDomElement elt_ns = doc.createElementNS(nsURI: "foo" , qName: in_name); |
1323 | QDomAttr attr_ns = doc.createAttributeNS(nsURI: "foo" , qName: in_name); |
1324 | QDomDocumentType doctype = impl.createDocumentType(qName: in_name, publicId: "foo" , systemId: "bar" ); |
1325 | QDomDocument doc2 = impl.createDocument(nsURI: "foo" , qName: in_name, doctype); |
1326 | |
1327 | QCOMPARE(!elt_ns.isNull(), ok_AcceptInvalidChars); |
1328 | QCOMPARE(!attr_ns.isNull(), ok_AcceptInvalidChars); |
1329 | QCOMPARE(!doctype.isNull(), ok_AcceptInvalidChars); |
1330 | QCOMPARE(!doc2.isNull(), ok_AcceptInvalidChars); |
1331 | |
1332 | if (ok_AcceptInvalidChars) { |
1333 | QCOMPARE(elt_ns.nodeName(), in_name); |
1334 | QCOMPARE(attr_ns.nodeName(), in_name); |
1335 | QCOMPARE(doctype.name(), in_name); |
1336 | QCOMPARE(doc2.documentElement().nodeName(), in_name); |
1337 | } |
1338 | } |
1339 | |
1340 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars); |
1341 | |
1342 | { |
1343 | QDomElement elt_ns = doc.createElementNS(nsURI: "foo" , qName: in_name); |
1344 | QDomAttr attr_ns = doc.createAttributeNS(nsURI: "foo" , qName: in_name); |
1345 | QDomDocumentType doctype = impl.createDocumentType(qName: in_name, publicId: "foo" , systemId: "bar" ); |
1346 | QDomDocument doc2 = impl.createDocument(nsURI: "foo" , qName: in_name, doctype); |
1347 | |
1348 | QCOMPARE(!elt_ns.isNull(), ok_DropInvalidChars); |
1349 | QCOMPARE(!attr_ns.isNull(), ok_DropInvalidChars); |
1350 | QCOMPARE(!doctype.isNull(), ok_DropInvalidChars); |
1351 | QCOMPARE(!doc2.isNull(), ok_DropInvalidChars); |
1352 | |
1353 | if (ok_DropInvalidChars) { |
1354 | QCOMPARE(elt_ns.nodeName(), out_name); |
1355 | QCOMPARE(attr_ns.nodeName(), out_name); |
1356 | QCOMPARE(doctype.name(), out_name); |
1357 | QCOMPARE(doc2.documentElement().nodeName(), out_name); |
1358 | } |
1359 | } |
1360 | |
1361 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); |
1362 | |
1363 | { |
1364 | QDomElement elt_ns = doc.createElementNS(nsURI: "foo" , qName: in_name); |
1365 | QDomAttr attr_ns = doc.createAttributeNS(nsURI: "foo" , qName: in_name); |
1366 | QDomDocumentType doctype = impl.createDocumentType(qName: in_name, publicId: "foo" , systemId: "bar" ); |
1367 | QDomDocument doc2 = impl.createDocument(nsURI: "foo" , qName: in_name, doctype); |
1368 | |
1369 | QCOMPARE(!elt_ns.isNull(), ok_ReturnNullNode); |
1370 | QCOMPARE(!attr_ns.isNull(), ok_ReturnNullNode); |
1371 | QCOMPARE(!doctype.isNull(), ok_ReturnNullNode); |
1372 | QCOMPARE(!doc2.isNull(), ok_ReturnNullNode); |
1373 | |
1374 | if (ok_ReturnNullNode) { |
1375 | QCOMPARE(elt_ns.nodeName(), in_name); |
1376 | QCOMPARE(attr_ns.nodeName(), in_name); |
1377 | QCOMPARE(doctype.name(), in_name); |
1378 | QCOMPARE(doc2.documentElement().nodeName(), in_name); |
1379 | } |
1380 | } |
1381 | } |
1382 | |
1383 | void tst_QDom::invalidCharData_data() |
1384 | { |
1385 | QTest::addColumn<QString>(name: "in_text" ); |
1386 | QTest::addColumn<bool>(name: "ok_AcceptInvalidChars" ); |
1387 | QTest::addColumn<bool>(name: "ok_DropInvalidChars" ); |
1388 | QTest::addColumn<bool>(name: "ok_ReturnNullNode" ); |
1389 | QTest::addColumn<QString>(name: "out_text" ); |
1390 | |
1391 | QTest::newRow( dataTag: "foo" ) << QString("foo" ) << true << true << true << QString("foo" ); |
1392 | QTest::newRow( dataTag: "f<o&o" ) << QString("f<o&o" ) << true << true << true << QString("f<o&o" ); |
1393 | QTest::newRow( dataTag: "empty" ) << QString() << true << true << true << QString(); |
1394 | QTest::newRow(dataTag: "f\\x07o\\x02" )<< QString("f\x07o\x02" )<< true << true << false << QString("fo" ); |
1395 | } |
1396 | |
1397 | void tst_QDom::invalidCharData() |
1398 | { |
1399 | QFETCH( QString, in_text ); |
1400 | QFETCH( bool, ok_AcceptInvalidChars ); |
1401 | QFETCH( bool, ok_DropInvalidChars ); |
1402 | QFETCH( bool, ok_ReturnNullNode ); |
1403 | QFETCH( QString, out_text ); |
1404 | |
1405 | QDomDocument doc; |
1406 | |
1407 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars); |
1408 | |
1409 | { |
1410 | QDomText text_elt = doc.createTextNode(data: in_text); |
1411 | QCOMPARE(!text_elt.isNull(), ok_AcceptInvalidChars); |
1412 | if (ok_AcceptInvalidChars) { |
1413 | QCOMPARE(text_elt.nodeValue(), in_text); |
1414 | } |
1415 | } |
1416 | |
1417 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars); |
1418 | |
1419 | { |
1420 | QDomText text_elt = doc.createTextNode(data: in_text); |
1421 | QCOMPARE(!text_elt.isNull(), ok_DropInvalidChars); |
1422 | if (ok_DropInvalidChars) { |
1423 | QCOMPARE(text_elt.nodeValue(), out_text); |
1424 | } |
1425 | } |
1426 | |
1427 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); |
1428 | |
1429 | { |
1430 | QDomText text_elt = doc.createTextNode(data: in_text); |
1431 | QCOMPARE(!text_elt.isNull(), ok_ReturnNullNode); |
1432 | if (ok_ReturnNullNode) { |
1433 | QCOMPARE(text_elt.nodeValue(), in_text); |
1434 | } |
1435 | } |
1436 | } |
1437 | |
1438 | void tst_QDom::roundTripAttributes() const |
1439 | { |
1440 | /* Create an attribute via the QDom API with weird whitespace content. */ |
1441 | QDomImplementation impl; |
1442 | |
1443 | QDomDocument doc(impl.createDocument(nsURI: "" , qName: "localName" , doctype: QDomDocumentType())); |
1444 | |
1445 | QDomElement e(doc.documentElement()); |
1446 | |
1447 | QString ws; |
1448 | ws.reserve(asize: 8); |
1449 | ws.append(c: QChar(0x20)); |
1450 | ws.append(c: QChar(0x20)); |
1451 | ws.append(c: QChar(0x20)); |
1452 | ws.append(c: QChar(0xD)); |
1453 | ws.append(c: QChar(0xA)); |
1454 | ws.append(c: QChar(0x9)); |
1455 | ws.append(c: QChar(0x20)); |
1456 | ws.append(c: QChar(0x20)); |
1457 | |
1458 | e.setAttribute(name: "attr" , value: ws); |
1459 | |
1460 | QByteArray serialized; |
1461 | QBuffer buffer(&serialized); |
1462 | buffer.open(openMode: QIODevice::WriteOnly); |
1463 | QTextStream stream(&buffer); |
1464 | |
1465 | doc.save(stream, 0); |
1466 | stream.flush(); |
1467 | |
1468 | const QByteArray expected("<localName xmlns=\"\" attr=\" 
	 \"/>\n" ); |
1469 | QCOMPARE(QString::fromLatin1(serialized.constData()), QString::fromLatin1(expected.constData())); |
1470 | } |
1471 | |
1472 | void tst_QDom::normalizeEndOfLine() const |
1473 | { |
1474 | QByteArray input("<a>\r\nc\rc\ra\na</a>" ); |
1475 | |
1476 | QBuffer buffer(&input); |
1477 | QVERIFY(buffer.open(QIODevice::ReadOnly)); |
1478 | |
1479 | QDomDocument doc; |
1480 | QVERIFY(doc.setContent(&buffer, true)); |
1481 | |
1482 | const QString expected(QLatin1String("<a>\nc\nc\na\na</a>" )); |
1483 | |
1484 | // ### Qt 6: fix this, if we keep QDom at all |
1485 | QEXPECT_FAIL("" , "The parser doesn't perform newline normalization. Fixing that would change behavior." , Continue); |
1486 | QCOMPARE(doc.documentElement().text(), expected); |
1487 | } |
1488 | |
1489 | void tst_QDom::normalizeAttributes() const |
1490 | { |
1491 | QByteArray data("<element attribute=\"a\na\"/>" ); |
1492 | QBuffer buffer(&data); |
1493 | |
1494 | QVERIFY(buffer.open(QIODevice::ReadOnly)); |
1495 | |
1496 | QDomDocument doc; |
1497 | QVERIFY(doc.setContent(&buffer, true)); |
1498 | |
1499 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1500 | QEXPECT_FAIL("" , "The parser doesn't perform Attribute Value Normalization. Fixing that would change behavior." , Continue); |
1501 | #endif |
1502 | QCOMPARE(doc.documentElement().attribute(QLatin1String("attribute" )), QString::fromLatin1("a a" )); |
1503 | } |
1504 | |
1505 | void tst_QDom::serializeWeirdEOL() const |
1506 | { |
1507 | QDomImplementation impl; |
1508 | |
1509 | QDomDocument doc(impl.createDocument(nsURI: "" , qName: "name" , doctype: QDomDocumentType())); |
1510 | QDomElement ele(doc.documentElement()); |
1511 | ele.appendChild(newChild: doc.createTextNode(data: QLatin1String("\r\nasd\nasd\rasd\n" ))); |
1512 | |
1513 | QByteArray output; |
1514 | QBuffer writeBuffer(&output); |
1515 | QVERIFY(writeBuffer.open(QIODevice::WriteOnly)); |
1516 | QTextStream stream(&writeBuffer); |
1517 | |
1518 | const QByteArray expected("<name xmlns=\"\">
\nasd\nasd
asd\n</name>\n" ); |
1519 | doc.save(stream, 0); |
1520 | QCOMPARE(QString::fromLatin1(output.constData()), QString::fromLatin1(expected.constData())); |
1521 | } |
1522 | |
1523 | void tst_QDom::reparentAttribute() const |
1524 | { |
1525 | QDomImplementation impl; |
1526 | QDomDocument doc(impl.createDocument(nsURI: "" , qName: "localName" , doctype: QDomDocumentType())); |
1527 | |
1528 | QDomElement ele(doc.documentElement()); |
1529 | QDomAttr attr(doc.createAttribute(name: "localName" )); |
1530 | ele.setAttributeNode(attr); |
1531 | |
1532 | QVERIFY(attr.ownerElement() == ele); |
1533 | QVERIFY(attr.parentNode() == ele); |
1534 | } |
1535 | |
1536 | void tst_QDom::serializeNamespaces() const |
1537 | { |
1538 | const char *const input = "<doc xmlns:b='http://example.com/'>" |
1539 | "<b:element b:name=''/>" |
1540 | "</doc>" ; |
1541 | |
1542 | QDomDocument doc; |
1543 | QByteArray ba(input); |
1544 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1545 | QT_WARNING_PUSH |
1546 | QT_WARNING_DISABLE_DEPRECATED |
1547 | |
1548 | QBuffer buffer(&ba); |
1549 | QVERIFY(buffer.open(QIODevice::ReadOnly)); |
1550 | |
1551 | QXmlInputSource source(&buffer); |
1552 | QXmlSimpleReader reader; |
1553 | reader.setFeature(name: "http://xml.org/sax/features/namespaces" , value: true); |
1554 | reader.setFeature(name: "http://xml.org/sax/features/namespace-prefixes" , value: false); |
1555 | |
1556 | QVERIFY(doc.setContent(&source, &reader)); |
1557 | QT_WARNING_POP |
1558 | #else |
1559 | QXmlStreamReader streamReader(input); |
1560 | QVERIFY(doc.setContent(&streamReader, true)); |
1561 | #endif |
1562 | |
1563 | const QByteArray serialized(doc.toByteArray()); |
1564 | |
1565 | QDomDocument doc2; |
1566 | QVERIFY(doc2.setContent(doc.toString(), true)); |
1567 | |
1568 | /* Here we test that it roundtrips. */ |
1569 | QVERIFY(isDeepEqual(doc2, doc)); |
1570 | |
1571 | QDomDocument doc3; |
1572 | QVERIFY(doc3.setContent(QString::fromLatin1(serialized.constData()), true)); |
1573 | |
1574 | QVERIFY(isDeepEqual(doc3, doc)); |
1575 | } |
1576 | |
1577 | void tst_QDom::flagInvalidNamespaces() const |
1578 | { |
1579 | const char *const input = "<doc>" |
1580 | "<b:element xmlns:b='http://example.com/' b:name='' xmlns:b='http://example.com/'/>" |
1581 | "</doc>" ; |
1582 | |
1583 | QDomDocument doc; |
1584 | QVERIFY(!doc.setContent(QString::fromLatin1(input, true))); |
1585 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1586 | QEXPECT_FAIL("" , "The parser doesn't flag identical qualified attribute names. Fixing this would change behavior." , Continue); |
1587 | #endif |
1588 | QVERIFY(!doc.setContent(QString::fromLatin1(input))); |
1589 | } |
1590 | |
1591 | void tst_QDom::flagUndeclaredNamespace() const |
1592 | { |
1593 | /* Note, prefix 'a' is not declared. */ |
1594 | const char *const input = "<a:doc xmlns:b='http://example.com/'>" |
1595 | "<b:element b:name=''/>" |
1596 | "</a:doc>" ; |
1597 | |
1598 | QDomDocument doc; |
1599 | QByteArray ba(input); |
1600 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1601 | QT_WARNING_PUSH |
1602 | QT_WARNING_DISABLE_DEPRECATED |
1603 | QBuffer buffer(&ba); |
1604 | |
1605 | QVERIFY(buffer.open(QIODevice::ReadOnly)); |
1606 | |
1607 | QXmlInputSource source(&buffer); |
1608 | QXmlSimpleReader reader; |
1609 | reader.setFeature(name: "http://xml.org/sax/features/namespaces" , value: true); |
1610 | reader.setFeature(name: "http://xml.org/sax/features/namespace-prefixes" , value: false); |
1611 | |
1612 | QEXPECT_FAIL("" , "The parser doesn't flag not declared prefixes. Fixing this would change behavior." , Continue); |
1613 | QVERIFY(!doc.setContent(&source, &reader)); |
1614 | QT_WARNING_POP |
1615 | #else |
1616 | QXmlStreamReader streamReader(ba); |
1617 | QVERIFY(!doc.setContent(&streamReader, true)); |
1618 | #endif |
1619 | } |
1620 | |
1621 | void tst_QDom::() const |
1622 | { |
1623 | /* We test that: |
1624 | * |
1625 | * - Whitespace is not added if a text node appears after a comment. |
1626 | * - Whitespace is not added if a text node appears before a comment. |
1627 | * - Indentation depth is linear with level depth. |
1628 | */ |
1629 | const char *const input = "<e>" |
1630 | "<!-- A Comment -->" |
1631 | "<b><!-- deep --></b>" |
1632 | "textNode" |
1633 | "<!-- Another Comment -->" |
1634 | "<!-- Another Comment2 -->" |
1635 | "textNode2" |
1636 | "</e>" ; |
1637 | const char *const expected = "<e>\n" |
1638 | " <!-- A Comment -->\n" |
1639 | " <b>\n" |
1640 | " <!-- deep -->\n" |
1641 | " </b>" |
1642 | "textNode" |
1643 | "<!-- Another Comment -->\n" |
1644 | " <!-- Another Comment2 -->" |
1645 | "textNode2" |
1646 | "</e>\n" ; |
1647 | QDomDocument doc; |
1648 | QVERIFY(doc.setContent(QString::fromLatin1(input))); |
1649 | |
1650 | const QString serialized(doc.toString(5)); |
1651 | |
1652 | QCOMPARE(serialized, QString::fromLatin1(expected)); |
1653 | } |
1654 | |
1655 | void tst_QDom::checkLiveness() const |
1656 | { |
1657 | QDomImplementation impl; |
1658 | |
1659 | QDomDocument doc(impl.createDocument(nsURI: QString(), qName: "doc" , doctype: QDomDocumentType())); |
1660 | QDomElement ele(doc.documentElement()); |
1661 | |
1662 | const QDomElement e1(doc.createElement(tagName: "name" )); |
1663 | const QDomElement e2(doc.createElement(tagName: "name" )); |
1664 | const QDomText t1(doc.createTextNode(data: "content" )); |
1665 | |
1666 | ele.appendChild(newChild: e1); |
1667 | ele.appendChild(newChild: t1); |
1668 | ele.appendChild(newChild: e2); |
1669 | |
1670 | const QDomNodeList children(ele.childNodes()); |
1671 | QCOMPARE(children.count(), 3); |
1672 | |
1673 | ele.removeChild(oldChild: e1); |
1674 | |
1675 | QCOMPARE(children.count(), 2); |
1676 | QCOMPARE(children.at(0), static_cast<const QDomNode &>(t1)); |
1677 | QCOMPARE(children.at(1), static_cast<const QDomNode &>(e2)); |
1678 | } |
1679 | |
1680 | void tst_QDom::reportDuplicateAttributes() const |
1681 | { |
1682 | QDomDocument dd; |
1683 | bool isSuccess = dd.setContent(text: QLatin1String("<test x=\"1\" x=\"2\"/>" )); |
1684 | |
1685 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1686 | QEXPECT_FAIL("" , "The parser doesn't flag duplicate attributes. Fixing this would change behavior." , Continue); |
1687 | #endif |
1688 | QVERIFY2(!isSuccess, "Duplicate attributes are well-formedness errors, and should be reported as such." ); |
1689 | } |
1690 | |
1691 | void tst_QDom::namespacedAttributes() const |
1692 | { |
1693 | static const char *const xml = |
1694 | "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n" |
1695 | "<xan:td xmlns:xan=\"http://www.someurl.com/Something\" " |
1696 | " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |
1697 | " xsi:schemaLocation=\"http://www.someurl.com/Something/../../xml/td.xsd\" " |
1698 | " xmlns:xi=\"http://www.w3.org/2001/XInclude\" " |
1699 | " xmlns=\"http://www.someurl.com/Something\">\n" |
1700 | " <Title displayLabel='Title' >>>> SIMPLE BASIC OP - SEND - DUT AS SINK</Title>\n" |
1701 | "</xan:td>\n" ; |
1702 | |
1703 | QDomDocument one("document" ); |
1704 | QString error; |
1705 | bool docParsed = one.setContent(text: QByteArray(xml), namespaceProcessing: true, errorMsg: &error); |
1706 | QVERIFY2(docParsed, qPrintable(error)); |
1707 | QDomDocument two("document2" ); |
1708 | docParsed = two.setContent(text: one.toByteArray(2), namespaceProcessing: true, errorMsg: &error); |
1709 | QVERIFY2(docParsed, qPrintable(error)); |
1710 | |
1711 | QVERIFY(isDeepEqual(one, two)); |
1712 | } |
1713 | |
1714 | void tst_QDom::appendChildFromToDocument() const |
1715 | { |
1716 | QDomDocument doc; |
1717 | const QByteArray input("<e/>" ); |
1718 | |
1719 | doc.setContent(text: input); |
1720 | |
1721 | QDomDocument doc2(doc.documentElement().toDocument()); |
1722 | QDomElement element = doc2.createElement(tagName: "name" ); |
1723 | element.setAttribute(name: "name" , value: "value" ); |
1724 | doc.documentElement().appendChild(newChild: element); |
1725 | } |
1726 | |
1727 | void tst_QDom::iterateCDATA() const |
1728 | { |
1729 | const QByteArray input("<e><![CDATA[data]]></e>" ); |
1730 | |
1731 | QDomDocument doc; |
1732 | QVERIFY(doc.setContent(input)); |
1733 | QCOMPARE(doc.toString(), QString("<e><![CDATA[data]]></e>\n" )); |
1734 | |
1735 | const QDomElement element(doc.documentElement()); |
1736 | QVERIFY(!element.isNull()); |
1737 | |
1738 | /* The node at element.childNodes().at(0) is not an element, |
1739 | * it's a CDATA section. */ |
1740 | const QDomElement child(element.childNodes().at(index: 0).toElement()); |
1741 | QVERIFY(child.isNull()); |
1742 | |
1743 | QVERIFY(element.childNodes().at(0).isCDATASection()); |
1744 | } |
1745 | |
1746 | /*! |
1747 | \internal |
1748 | \since 4.4 |
1749 | \brief This function cannot be factored into appendDocumentNode(). The |
1750 | invocation of constructors/destructors is part of triggering the bug. |
1751 | */ |
1752 | QDomDocument tst_QDom::generateRequest() |
1753 | { |
1754 | QDomDocument doc; |
1755 | QDomElement elem = doc.createElement(tagName: "test_elem" ); |
1756 | |
1757 | elem.setAttribute(name: "name" , value: "value" ); |
1758 | doc.appendChild(newChild: elem); |
1759 | return doc; |
1760 | } |
1761 | |
1762 | void tst_QDom::appendDocumentNode() const |
1763 | { |
1764 | QDomDocument doc; |
1765 | QDomDocument xml = generateRequest(); |
1766 | QDomElement elem = doc.createElement(tagName: "document" ); |
1767 | |
1768 | doc.appendChild(newChild: elem); |
1769 | |
1770 | QVERIFY(!xml.isNull()); |
1771 | const QString expected(QLatin1String("<document>\n<test_elem name=\"value\"/>\n</document>\n" )); |
1772 | |
1773 | elem.appendChild(newChild: xml); |
1774 | QCOMPARE(doc.childNodes().count(), 1); |
1775 | QCOMPARE(doc.toString(0), expected); |
1776 | |
1777 | elem.appendChild(newChild: xml.firstChild()); |
1778 | QCOMPARE(doc.childNodes().count(), 1); |
1779 | QCOMPARE(doc.toString(0), expected); |
1780 | } |
1781 | |
1782 | static const QChar umlautName[] = |
1783 | { |
1784 | 'a', 0xfc, 'b' |
1785 | }; |
1786 | |
1787 | /*! |
1788 | \internal |
1789 | |
1790 | Write a german umlaut to a QByteArray, via a QTextStream. |
1791 | */ |
1792 | void tst_QDom::germanUmlautToByteArray() const |
1793 | { |
1794 | QCOMPARE(ulong(sizeof(umlautName) / sizeof(QChar)), ulong(3)); |
1795 | const QString name(umlautName, 3); |
1796 | |
1797 | QDomDocument d; |
1798 | d.appendChild(newChild: d.createElement(tagName: name)); |
1799 | QByteArray data; |
1800 | QBuffer buffer(&data); |
1801 | QVERIFY(buffer.open(QIODevice::WriteOnly)); |
1802 | QTextStream ts(&buffer); |
1803 | ts.setCodec("UTF-8" ); |
1804 | ts << d.toString(); |
1805 | buffer.close(); |
1806 | |
1807 | QByteArray baseline("<a" ); |
1808 | |
1809 | /* http://www.fileformat.info/info/unicode/char/00FC/index.htm */ |
1810 | baseline += char(0xC3); |
1811 | baseline += char(0xBC); |
1812 | baseline += "b/>\n" ; |
1813 | |
1814 | QCOMPARE(data, baseline); |
1815 | } |
1816 | |
1817 | /*! |
1818 | \internal |
1819 | |
1820 | Write a german umlaut to a QFile, via a QTextStream. |
1821 | */ |
1822 | void tst_QDom::germanUmlautToFile() const |
1823 | { |
1824 | /* http://www.fileformat.info/info/unicode/char/00FC/index.htm */ |
1825 | QString name(QLatin1String("german" )); |
1826 | name += QChar(0xFC); |
1827 | name += QLatin1String("umlaut" ); |
1828 | QCOMPARE(name.length(), 13); |
1829 | |
1830 | QDomDocument d("test" ); |
1831 | d.appendChild(newChild: d.createElement(tagName: name)); |
1832 | QTemporaryFile file; |
1833 | QVERIFY(file.open()); |
1834 | QTextStream ts(&file); |
1835 | ts.setCodec("UTF-8" ); |
1836 | ts << d.toString(); |
1837 | file.close(); |
1838 | |
1839 | QFile inFile(file.fileName()); |
1840 | QVERIFY(inFile.open(QIODevice::ReadOnly)); |
1841 | |
1842 | QString baseline(QLatin1String("<!DOCTYPE test>\n<german" )); |
1843 | baseline += QChar(0xFC); |
1844 | baseline += QLatin1String("umlaut/>\n" ); |
1845 | |
1846 | const QByteArray in(inFile.readAll()); |
1847 | /* Check that it was wwritten out correctly. */ |
1848 | QCOMPARE(in.length(), 34); |
1849 | QCOMPARE(in, baseline.toUtf8()); |
1850 | inFile.close(); |
1851 | |
1852 | /* Check that we read it in correctly with QDomDocument::setContent(). */ |
1853 | QVERIFY(inFile.open(QIODevice::ReadOnly)); |
1854 | QDomDocument dd; |
1855 | QVERIFY(dd.setContent(&inFile)); |
1856 | |
1857 | QCOMPARE(dd.toString(), baseline); |
1858 | } |
1859 | |
1860 | void tst_QDom::setInvalidDataPolicy() const |
1861 | { |
1862 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); |
1863 | QDomDocument doc; |
1864 | QDomElement elem = doc.createElement(tagName: "invalid name" ); |
1865 | QVERIFY(elem.isNull()); |
1866 | } |
1867 | |
1868 | void tst_QDom::crashInSetContent() const |
1869 | { |
1870 | QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); |
1871 | QDomDocument docImport; |
1872 | |
1873 | QCOMPARE(docImport.setContent(QLatin1String("<a:>text</a:>" ), true), false); |
1874 | QVERIFY(docImport.setContent(QLatin1String("<?xml version=\"1.0\"?><e/>" ))); |
1875 | } |
1876 | |
1877 | void tst_QDom::doubleNamespaceDeclarations() const |
1878 | { |
1879 | QDomDocument doc; |
1880 | |
1881 | QString testFile = QFINDTESTDATA("doubleNamespaces.xml" ); |
1882 | if (testFile.isEmpty()) |
1883 | QFAIL("Cannot find test file doubleNamespaces.xml!" ); |
1884 | QFile file(testFile); |
1885 | QVERIFY(file.open(QIODevice::ReadOnly)); |
1886 | |
1887 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1888 | QT_WARNING_PUSH |
1889 | QT_WARNING_DISABLE_DEPRECATED |
1890 | QXmlSimpleReader reader; |
1891 | |
1892 | QXmlInputSource source(&file); |
1893 | QVERIFY(doc.setContent(&source, &reader)); |
1894 | QT_WARNING_POP |
1895 | #else |
1896 | QXmlStreamReader streamReader(&file); |
1897 | QVERIFY(doc.setContent(&streamReader, true)); |
1898 | #endif |
1899 | |
1900 | // tst_QDom relies on a specific QHash ordering, see QTBUG-25071 |
1901 | QString docAsString = doc.toString(0); |
1902 | QVERIFY(docAsString == QString::fromLatin1("<a>\n<b p:c=\"\" xmlns:p=\"NS\" p:d=\"\"/>\n</a>\n" ) || |
1903 | docAsString == QString::fromLatin1("<a>\n<b p:c=\"\" p:d=\"\" xmlns:p=\"NS\"/>\n</a>\n" ) || |
1904 | docAsString == QString::fromLatin1("<a>\n<b p:d=\"\" p:c=\"\" xmlns:p=\"NS\"/>\n</a>\n" ) || |
1905 | docAsString == QString::fromLatin1("<a>\n<b p:d=\"\" xmlns:p=\"NS\" p:c=\"\"/>\n</a>\n" ) || |
1906 | docAsString == QString::fromLatin1("<a>\n<b xmlns:p=\"NS\" p:c=\"\" p:d=\"\"/>\n</a>\n" ) || |
1907 | docAsString == QString::fromLatin1("<a>\n<b xmlns:p=\"NS\" p:d=\"\" p:c=\"\"/>\n</a>\n" ) |
1908 | ); |
1909 | } |
1910 | |
1911 | void tst_QDom::setContentQXmlReaderOverload() const |
1912 | { |
1913 | QDomDocument doc; |
1914 | |
1915 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
1916 | QT_WARNING_PUSH |
1917 | QT_WARNING_DISABLE_DEPRECATED |
1918 | QXmlSimpleReader reader; |
1919 | QXmlInputSource data; |
1920 | data.setData(QByteArray("<e/>" )); |
1921 | doc.setContent(source: &data, namespaceProcessing: true); |
1922 | QT_WARNING_POP |
1923 | #else |
1924 | QXmlStreamReader streamReader(QByteArray("<e/>" )); |
1925 | doc.setContent(&streamReader, true); |
1926 | #endif |
1927 | QCOMPARE(doc.documentElement().nodeName(), QString::fromLatin1("e" )); |
1928 | } |
1929 | |
1930 | void tst_QDom::cleanupTestCase() const |
1931 | { |
1932 | QFile::remove(fileName: "germanUmlautToFile.xml" ); |
1933 | } |
1934 | |
1935 | void tst_QDom::toStringWithoutNewlines() const |
1936 | { |
1937 | QDomDocument doc; |
1938 | doc.setContent(text: QLatin1String("<doc><e/></doc>" )); |
1939 | |
1940 | QCOMPARE(doc.toString(0), QString::fromLatin1("<doc>\n<e/>\n</doc>\n" )); |
1941 | QCOMPARE(doc.toString(-1), QString::fromLatin1("<doc><e/></doc>" )); |
1942 | } |
1943 | |
1944 | void tst_QDom::checkIntOverflow() const |
1945 | { |
1946 | /* This test takes a *very* long time to run, so it is at best a manual |
1947 | * test. */ |
1948 | return; |
1949 | |
1950 | /* QDom used an internal global int which overflowed. So iterate until an |
1951 | * uint wrapsaround. */ |
1952 | const QString xmlMessage(QLatin1String("<test/>" )); |
1953 | |
1954 | bool hasWrapped = false; |
1955 | for(uint i = 1; i != 0; ++i) |
1956 | { |
1957 | /* We want to exit the second time, not loop infinitely. */ |
1958 | if(i == 1 && hasWrapped) |
1959 | break; |
1960 | else |
1961 | hasWrapped = true; |
1962 | |
1963 | QDomDocument doc; |
1964 | QVERIFY(doc.setContent(xmlMessage)); |
1965 | |
1966 | const QDomNodeList nl(doc.elementsByTagName(tagname: QLatin1String("test" ))); |
1967 | QCOMPARE(nl.length(), 1); |
1968 | } |
1969 | } |
1970 | |
1971 | void tst_QDom::setContentWhitespace() const |
1972 | { |
1973 | QFETCH(QString, doc); |
1974 | QFETCH(bool, expectedValidity); |
1975 | |
1976 | QDomDocument domDoc; |
1977 | |
1978 | QCOMPARE(domDoc.setContent(doc), expectedValidity); |
1979 | |
1980 | if(expectedValidity) |
1981 | QCOMPARE(domDoc.documentElement().nodeName(), QString::fromLatin1("e" )); |
1982 | } |
1983 | |
1984 | void tst_QDom::setContentWhitespace_data() const |
1985 | { |
1986 | QTest::addColumn<QString>(name: "doc" ); |
1987 | QTest::addColumn<bool>(name: "expectedValidity" ); |
1988 | |
1989 | QTest::newRow(dataTag: "data1" ) << QString::fromLatin1(str: " <e/>" ) << true; |
1990 | QTest::newRow(dataTag: "data2" ) << QString::fromLatin1(str: " <e/>" ) << true; |
1991 | QTest::newRow(dataTag: "data3" ) << QString::fromLatin1(str: " <e/>" ) << true; |
1992 | QTest::newRow(dataTag: "data4" ) << QString::fromLatin1(str: " <e/>" ) << true; |
1993 | QTest::newRow(dataTag: "data5" ) << QString::fromLatin1(str: "\n<e/>" ) << true; |
1994 | QTest::newRow(dataTag: "data6" ) << QString::fromLatin1(str: "\n\n<e/>" ) << true; |
1995 | QTest::newRow(dataTag: "data7" ) << QString::fromLatin1(str: "\n\n\n<e/>" ) << true; |
1996 | QTest::newRow(dataTag: "data8" ) << QString::fromLatin1(str: "\n\n\n\n<e/>" ) << true; |
1997 | QTest::newRow(dataTag: "data9" ) << QString::fromLatin1(str: "\t<e/>" ) << true; |
1998 | QTest::newRow(dataTag: "data10" ) << QString::fromLatin1(str: "\t\t<e/>" ) << true; |
1999 | QTest::newRow(dataTag: "data11" ) << QString::fromLatin1(str: "\t\t\t<e/>" ) << true; |
2000 | QTest::newRow(dataTag: "data12" ) << QString::fromLatin1(str: "\t\t\t\t<e/>" ) << true; |
2001 | |
2002 | /* With XML prolog. */ |
2003 | QTest::newRow(dataTag: "data13" ) << QString::fromLatin1(str: "<?xml version='1.0' ?><e/>" ) << true; |
2004 | |
2005 | QTest::newRow(dataTag: "data14" ) << QString::fromLatin1(str: " <?xml version='1.0' ?><e/>" ) << false; |
2006 | QTest::newRow(dataTag: "data15" ) << QString::fromLatin1(str: " <?xml version='1.0' ?><e/>" ) << false; |
2007 | QTest::newRow(dataTag: "data16" ) << QString::fromLatin1(str: " <?xml version='1.0' ?><e/>" ) << false; |
2008 | QTest::newRow(dataTag: "data17" ) << QString::fromLatin1(str: " <?xml version='1.0' ?><e/>" ) << false; |
2009 | QTest::newRow(dataTag: "data18" ) << QString::fromLatin1(str: "\n<?xml version='1.0' ?><e/>" ) << false; |
2010 | QTest::newRow(dataTag: "data19" ) << QString::fromLatin1(str: "\n\n<?xml version='1.0' ?><e/>" ) << false; |
2011 | QTest::newRow(dataTag: "data20" ) << QString::fromLatin1(str: "\n\n\n<?xml version='1.0' ?><e/>" ) << false; |
2012 | QTest::newRow(dataTag: "data21" ) << QString::fromLatin1(str: "\n\n\n\n<?xml version='1.0' ?><e/>" ) << false; |
2013 | QTest::newRow(dataTag: "data22" ) << QString::fromLatin1(str: "\t<?xml version='1.0' ?><e/>" ) << false; |
2014 | QTest::newRow(dataTag: "data23" ) << QString::fromLatin1(str: "\t\t<?xml version='1.0' ?><e/>" ) << false; |
2015 | QTest::newRow(dataTag: "data24" ) << QString::fromLatin1(str: "\t\t\t<?xml version='1.0' ?><e/>" ) << false; |
2016 | QTest::newRow(dataTag: "data25" ) << QString::fromLatin1(str: "\t\t\t\t<?xml version='1.0' ?><e/>" ) << false; |
2017 | } |
2018 | |
2019 | void tst_QDom::taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const |
2020 | { |
2021 | // QXmlStreamReader fails to read XML documents with unknown encoding. It |
2022 | // needs to be modified if we want to support this case with the QXmlStreamReader-based |
2023 | // implementation. |
2024 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15) |
2025 | QString xmlWithUnknownEncoding("<?xml version='1.0' encoding='unknown-encoding'?>" |
2026 | "<foo>" |
2027 | " <bar>How will this sentence be handled?</bar>" |
2028 | "</foo>" ); |
2029 | QDomDocument d; |
2030 | QVERIFY(d.setContent(xmlWithUnknownEncoding)); |
2031 | |
2032 | QString dontAssert = d.toString(); // this should not assert |
2033 | QVERIFY(true); |
2034 | #endif |
2035 | } |
2036 | |
2037 | void tst_QDom::cloneDTD_QTBUG8398() const |
2038 | { |
2039 | QString dtd("<?xml version='1.0' encoding='UTF-8'?>\n" |
2040 | "<!DOCTYPE first [\n" |
2041 | "<!ENTITY secondFile SYSTEM 'second.xml'>\n" |
2042 | "<!ENTITY thirdFile SYSTEM 'third.xml'>\n" |
2043 | "]>\n" |
2044 | "<first/>\n" ); |
2045 | QDomDocument domDocument; |
2046 | QVERIFY(domDocument.setContent(dtd)); |
2047 | QDomDocument domDocument2 = domDocument.cloneNode(deep: true).toDocument(); |
2048 | |
2049 | // this string is relying on a specific QHash ordering, QTBUG-25071 |
2050 | QString expected("<?xml version='1.0' encoding='UTF-8'?>\n" |
2051 | "<!DOCTYPE first [\n" |
2052 | "<!ENTITY thirdFile SYSTEM 'third.xml'>\n" |
2053 | "<!ENTITY secondFile SYSTEM 'second.xml'>\n" |
2054 | "]>\n" |
2055 | "<first/>\n" ); |
2056 | QString output; |
2057 | QTextStream stream(&output); |
2058 | domDocument2.save(stream, 0); |
2059 | // check against the original string and the expected one, QTBUG-25071 |
2060 | QVERIFY(output == dtd || output == expected); |
2061 | } |
2062 | |
2063 | void tst_QDom::DTDNotationDecl() |
2064 | { |
2065 | QString dtd("<?xml version='1.0' encoding='UTF-8'?>\n" |
2066 | "<!DOCTYPE first [\n" |
2067 | "<!NOTATION gif SYSTEM 'image/gif'>\n" |
2068 | "<!NOTATION jpeg SYSTEM 'image/jpeg'>\n" |
2069 | "]>\n" |
2070 | "<first/>\n" ); |
2071 | |
2072 | QDomDocument domDocument; |
2073 | QVERIFY(domDocument.setContent(dtd)); |
2074 | |
2075 | const QDomDocumentType doctype = domDocument.doctype(); |
2076 | QCOMPARE(doctype.notations().size(), 2); |
2077 | |
2078 | QVERIFY(doctype.namedItem(QString("gif" )).isNotation()); |
2079 | QCOMPARE(doctype.namedItem(QString("gif" )).toNotation().systemId(), QString("image/gif" )); |
2080 | |
2081 | QVERIFY(doctype.namedItem(QString("jpeg" )).isNotation()); |
2082 | QCOMPARE(doctype.namedItem(QString("jpeg" )).toNotation().systemId(), QString("image/jpeg" )); |
2083 | } |
2084 | |
2085 | void tst_QDom::DTDEntityDecl() |
2086 | { |
2087 | QString dtd("<?xml version='1.0' encoding='UTF-8'?>\n" |
2088 | "<!DOCTYPE first [\n" |
2089 | "<!ENTITY secondFile SYSTEM 'second.xml'>\n" |
2090 | "<!ENTITY logo SYSTEM \"http://www.w3c.org/logo.gif\" NDATA gif>" |
2091 | "]>\n" |
2092 | "<first/>\n" ); |
2093 | |
2094 | QDomDocument domDocument; |
2095 | QVERIFY(domDocument.setContent(dtd)); |
2096 | |
2097 | const QDomDocumentType doctype = domDocument.doctype(); |
2098 | QCOMPARE(doctype.entities().count(), 2); |
2099 | |
2100 | QVERIFY(doctype.namedItem(QString("secondFile" )).isEntity()); |
2101 | QCOMPARE(doctype.namedItem(QString("secondFile" )).toEntity().systemId(), QString("second.xml" )); |
2102 | QCOMPARE(doctype.namedItem(QString("secondFile" )).toEntity().notationName(), QString()); |
2103 | |
2104 | QVERIFY(doctype.namedItem(QString("logo" )).isEntity()); |
2105 | QCOMPARE(doctype.namedItem(QString("logo" )).toEntity().systemId(), QString("http://www.w3c.org/logo.gif" )); |
2106 | QCOMPARE(doctype.namedItem(QString("logo" )).toEntity().notationName(), QString("gif" )); |
2107 | } |
2108 | |
2109 | void tst_QDom::QTBUG49113_dontCrashWithNegativeIndex() const |
2110 | { |
2111 | QDomDocument doc; |
2112 | QDomElement elem = doc.appendChild(newChild: doc.createElement(tagName: "root" )).toElement(); |
2113 | QDomNode node = elem.attributes().item(index: -1); |
2114 | QVERIFY(node.isNull()); |
2115 | } |
2116 | |
2117 | QTEST_MAIN(tst_QDom) |
2118 | #include "tst_qdom.moc" |
2119 | |