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