1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
45 #include <QCoreApplication>
50 #include <QTextStream>
51 #include <QtTest/QtTest>
55 QT_FORWARD_DECLARE_CLASS(QDomDocument)
56 QT_FORWARD_DECLARE_CLASS(QDomNode)
58 class tst_QDom : public QObject
64 void namespacedAttributes() const;
65 void setContent_data();
67 void toString_01_data();
69 void toString_02_data();
71 void hasAttributes_data();
75 void saveWithSerialization() const;
76 void saveWithSerialization_data() const;
77 void cloneNode_data();
79 void ownerDocument_data();
81 void ownerDocumentTask27424_data();
82 void ownerDocumentTask27424();
83 void parentNode_data();
85 void documentCreationTask27424_data();
86 void documentCreationTask27424();
87 void browseElements();
88 void ownerElementTask45192_data();
89 void ownerElementTask45192();
90 void domNodeMapAndList();
93 void invalidName_data();
95 void invalidQualifiedName_data();
96 void invalidQualifiedName();
97 void invalidCharData_data();
98 void invalidCharData();
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;
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;
126 void taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const;
127 void cloneDTD_QTBUG8398() const;
128 void DTDNotationDecl();
129 void DTDEntityDecl();
131 void cleanupTestCase() const;
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);
143 QList<QByteArray> m_excludedCodecs;
146 Q_DECLARE_METATYPE(QList<QVariant>)
148 void tst_QDom::setContent_data()
151 "<!DOCTYPE a1 [ <!ENTITY blubber 'and'> ]>\n"
156 " <c3>foo & bar</c3>\n"
157 " <c4>foo &blubber; bar</c4>\n"
166 QTest::addColumn<QString>("doc");
167 QTest::addColumn<QStringList>("featuresTrue");
168 QTest::addColumn<QStringList>("featuresFalse");
169 QTest::addColumn<QString>("res");
171 /* QTest::newRow( "01" ) << doc01
173 << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ')
174 << QString("<!DOCTYPE a1>\n"
179 " <c3>foo & bar</c3>\n"
180 " <c4>foo and bar</c4>\n"
188 QTest::newRow( "02" ) << doc01
189 << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ')
191 << QString("<!DOCTYPE a1>\n"
196 " <c3>foo & bar</c3>\n"
197 " <c4>foo and bar</c4>\n"
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"
215 " <c3>foo & bar</c3>\n"
216 " <c4>foo &blubber; bar</c4>\n"
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(' ')
227 << QString("<!DOCTYPE a1 [\n"
228 "<!ENTITY blubber \"and\">\n"
234 " <c3>foo & bar</c3>\n"
235 " <c4>foo &blubber; bar</c4>\n"
243 */ QTest::newRow("05") << QString("<message>\n"
244 " <body><b>foo</b>>]]></body>\n"
246 << QStringList() << QStringList()
247 << QString("<message>\n"
248 " <body><b>foo</b>>]]></body>\n"
253 void tst_QDom::setContent()
255 QFETCH( QString, doc );
257 QXmlInputSource source;
258 source.setData( doc );
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 );
268 for ( it = featuresFalse.begin(); it != featuresFalse.end(); ++it ) {
269 QVERIFY( reader.hasFeature( *it ) );
270 reader.setFeature( *it, false );
274 QVERIFY( domDoc.setContent( &source, &reader ) );
277 QTextStream ts( &eRes, QIODevice::WriteOnly );
278 domDoc.save( ts, 4 );
280 QTEST( eRes, "res" );
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 ) );
290 void tst_QDom::toString_01_data()
292 QTest::addColumn<QString>("fileName");
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");
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");
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.
315 To achieve this, the XML document of the toString() function is parsed again
316 and the two QDomDocuments are compared.
318 void tst_QDom::toString_01()
320 QFETCH(QString, fileName);
323 QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Failed to open file %1, file error: %2").arg(fileName).arg(f.error())));
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 );
338 QVERIFY( res.setContent( toStr ) );
340 QVERIFY( compareDocuments( doc, res ) );
344 void tst_QDom::toString_02_data()
350 Tests the new QDomDocument::toString(int) overload (basically the same test
353 void tst_QDom::toString_02()
355 QFETCH( QString, doc );
356 QFETCH( int, indent );
359 QVERIFY( domDoc.setContent( doc ) );
360 QTEST( domDoc.toString(indent), "res" );
364 void tst_QDom::hasAttributes_data()
366 QTest::addColumn<int>("visitedNodes");
367 QTest::addColumn<QByteArray>("xmlDoc");
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"
374 "<bar fnord=\"snafu\" hmpf=\"grmpf\">\n"
377 "<bar>blubber</bar>\n"
378 "more text, pretty unintresting, though\n"
379 "<hmpfl blubber=\"something\" />\n"
380 "<![CDATA[ foo bar @!<>] ]]>\n"
382 "<!-- just a useless comment -->\n"
385 QTest::newRow( "01" ) << 6 << doc1;
386 QTest::newRow( "02" ) << 6 << doc2;
387 QTest::newRow( "03" ) << 13 << doc3;
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
395 void tst_QDom::hasAttributes()
397 QFETCH( QByteArray, xmlDoc );
400 QVERIFY( doc.setContent( xmlDoc ) );
402 int visitedNodes = hasAttributesHelper( doc );
403 QTEST( visitedNodes, "visitedNodes" );
406 int tst_QDom::hasAttributesHelper( const QDomNode& node )
408 int visitedNodes = 1;
409 if ( node.hasAttributes() ) {
410 if (node.attributes().count() == 0)
412 // QVERIFY( node.attributes().count() > 0 );
414 if (node.attributes().count() != 0)
416 // QVERIFY( node.attributes().count() == 0 );
419 QDomNodeList children = node.childNodes();
420 for ( int i=0; i<children.count(); i++ ) {
421 int j = hasAttributesHelper( children.item(i) );
430 void tst_QDom::save_data()
447 QTest::addColumn<QString>("doc");
448 QTest::addColumn<int>("indent");
449 QTest::addColumn<QString>("res");
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(" "), " " );
457 void tst_QDom::save()
459 QFETCH( QString, doc );
460 QFETCH( int, indent );
463 QVERIFY( domDoc.setContent( doc ) );
466 QTextStream ts( &eRes, QIODevice::WriteOnly );
467 domDoc.save( ts, indent );
469 QTEST( eRes, "res" );
472 void tst_QDom::initTestCase()
474 QFile file(SRCDIR "testdata/excludedCodecs.txt");
475 QVERIFY(file.open(QIODevice::ReadOnly|QIODevice::Text));
477 QByteArray codecName;
479 m_excludedCodecs = file.readAll().split('\n');
483 void tst_QDom::saveWithSerialization() const
485 QFETCH(QString, fileName);
488 QVERIFY(f.open(QIODevice::ReadOnly));
493 QVERIFY(doc.setContent(&f));
495 const QList<QByteArray> codecs(QTextCodec::availableCodecs());
496 QByteArray codecName;
498 foreach(codecName, codecs) {
500 /* Avoid codecs that can't handle the files we have. */
501 if(m_excludedCodecs.contains(codecName.toLower()))
504 /* Write out doc in the specified codec. */
506 QBuffer writeDevice(&storage);
507 QVERIFY(writeDevice.open(QIODevice::WriteOnly));
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()))));
515 doc.save(s, 0, QDomNode::EncodingFromTextStream);
519 QBuffer readDevice(&storage);
520 QVERIFY(readDevice.open(QIODevice::ReadOnly));
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),
534 codec->toUnicode(storage))));
535 if(!compareDocuments(doc, result))
537 QCOMPARE(doc.toString(), result.toString());
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()))));
546 void tst_QDom::saveWithSerialization_data() const
548 QTest::addColumn<QString>("fileName");
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");
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");
564 void tst_QDom::cloneNode_data()
580 QList<QVariant> nodeB1;
583 QList<QVariant> nodeC1;
586 QList<QVariant> nodeC2;
589 QTest::addColumn<QString>("doc");
590 QTest::addColumn<QList<QVariant> >("pathToNode");
591 QTest::addColumn<bool>("deep");
593 QTest::newRow( "noDeep_01" ) << doc01 << nodeB1 << false;
594 QTest::newRow( "noDeep_02" ) << doc01 << nodeC1 << false;
595 QTest::newRow( "noDeep_03" ) << doc01 << nodeC2 << false;
597 QTest::newRow( "deep_01" ) << doc01 << nodeB1 << true;
598 QTest::newRow( "deep_02" ) << doc01 << nodeC1 << true;
599 QTest::newRow( "deep_03" ) << doc01 << nodeC2 << true;
602 void tst_QDom::cloneNode()
604 QFETCH( QString, doc );
605 QFETCH( QList<QVariant>, pathToNode );
606 QFETCH( bool, deep );
608 QVERIFY( domDoc.setContent( doc ) );
609 QDomNode node = findDomNode( domDoc, pathToNode );
610 QVERIFY(!node.isNull());
612 QDomNode clonedNode = node.cloneNode( deep );
613 QVERIFY( compareNodes( node, clonedNode, deep ) );
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 ) );
624 void tst_QDom::ownerElementTask45192_data()
628 " <item name=\"test\" >\n"
633 QTest::addColumn<QString>("doc");
634 QTest::newRow("doc") << doc;
637 void tst_QDom::ownerElementTask45192()
639 QFETCH( QString, doc );
641 QVERIFY( domDoc.setContent( doc ) );
643 QDomNode item = domDoc.documentElement().firstChild();
644 QDomNode clone = item.cloneNode(false);
646 QVERIFY( clone == clone.attributes().namedItem("name").toAttr().ownerElement() );
649 void tst_QDom::ownerDocument_data()
654 #define OWNERDOCUMENT_CREATE_TEST( t, x ) \
657 QVERIFY( n.ownerDocument() == domDoc ); \
660 #define OWNERDOCUMENT_IMPORTNODE_TEST( t, x ) \
662 QDomNode importedNode; \
664 QVERIFY( n.ownerDocument() != domDoc ); \
665 importedNode = domDoc.importNode( n, deep ); \
666 QVERIFY( n.ownerDocument() != domDoc ); \
667 QVERIFY( importedNode.ownerDocument() == domDoc ); \
670 void tst_QDom::ownerDocument()
672 QFETCH( QString, doc );
673 QFETCH( QList<QVariant>, pathToNode );
674 QFETCH( bool, deep );
676 QVERIFY( domDoc.setContent( doc ) );
677 QDomNode node = findDomNode( domDoc, pathToNode );
678 QVERIFY(!node.isNull());
680 QVERIFY( node.ownerDocument() == domDoc );
682 // Does cloneNode() keep the ownerDocument()?
684 QDomNode clonedNode = node.cloneNode( deep );
685 QVERIFY( node.ownerDocument() == domDoc );
686 QVERIFY( clonedNode.ownerDocument() == domDoc );
689 // If the original DOM node is replaced with the cloned node, does this
690 // keep the ownerDocument()?
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 );
701 // test QDomDocument::create...()
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" ) );
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" ) );
731 void tst_QDom::ownerDocumentTask27424_data()
733 QTest::addColumn<bool>("insertLevel1AfterCstr");
734 QTest::addColumn<bool>("insertLevel2AfterCstr");
735 QTest::addColumn<bool>("insertLevel3AfterCstr");
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;
747 void tst_QDom::ownerDocumentTask27424()
749 QFETCH( bool, insertLevel1AfterCstr );
750 QFETCH( bool, insertLevel2AfterCstr );
751 QFETCH( bool, insertLevel3AfterCstr );
753 QDomDocument doc("TestXML");
755 QDomElement level1 = doc.createElement("Level_1");
756 QVERIFY( level1.ownerDocument() == doc );
758 if ( insertLevel1AfterCstr ) {
759 doc.appendChild(level1);
760 QVERIFY( level1.ownerDocument() == doc );
763 QDomElement level2 = level1.ownerDocument().createElement("Level_2");
764 QVERIFY( level1.ownerDocument() == doc );
765 QVERIFY( level2.ownerDocument() == doc );
767 if ( insertLevel2AfterCstr ) {
768 level1.appendChild(level2);
769 QVERIFY( level1.ownerDocument() == doc );
770 QVERIFY( level2.ownerDocument() == doc );
773 QDomElement level3 = level2.ownerDocument().createElement("Level_3");
774 QVERIFY( level1.ownerDocument() == doc );
775 QVERIFY( level2.ownerDocument() == doc );
776 QVERIFY( level3.ownerDocument() == doc );
778 if ( insertLevel3AfterCstr ) {
779 level2.appendChild(level3);
780 QVERIFY( level1.ownerDocument() == doc );
781 QVERIFY( level2.ownerDocument() == doc );
782 QVERIFY( level3.ownerDocument() == doc );
785 QDomNode level4 = level3.ownerDocument().createTextNode("This_is_a_value!");
786 QVERIFY( level4.ownerDocument() == doc );
788 level3.appendChild(level4);
789 QVERIFY( level1.ownerDocument() == doc );
790 QVERIFY( level2.ownerDocument() == doc );
791 QVERIFY( level3.ownerDocument() == doc );
792 QVERIFY( level4.ownerDocument() == doc );
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 );
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 );
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 );
819 void tst_QDom::parentNode_data()
824 #define PARENTNODE_CREATE_TEST( t, x ) \
827 QVERIFY( n.parentNode().isNull() ); \
830 void tst_QDom::parentNode()
832 QFETCH( QString, doc );
833 QFETCH( QList<QVariant>, pathToNode );
834 QFETCH( bool, deep );
836 QVERIFY( domDoc.setContent( doc ) );
837 QDomNode node = findDomNode( domDoc, pathToNode );
838 QVERIFY(!node.isNull());
841 // test QDomDocument::create...()
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" ) );
857 void tst_QDom::documentCreationTask27424_data()
859 QTest::addColumn<bool>("insertLevel1AfterCstr");
860 QTest::addColumn<bool>("insertLevel2AfterCstr");
861 QTest::addColumn<bool>("insertLevel3AfterCstr");
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;
873 void tst_QDom::documentCreationTask27424()
875 QFETCH( bool, insertLevel1AfterCstr );
876 QFETCH( bool, insertLevel2AfterCstr );
877 QFETCH( bool, insertLevel3AfterCstr );
880 QVERIFY( docRes.setContent( QString(
881 "<!DOCTYPE TestXML>\n"
884 " <Level_3>This_is_a_value!</Level_3>\n"
889 QDomDocument doc("TestXML");
891 QDomElement level1 = doc.createElement("Level_1");
892 if ( insertLevel1AfterCstr )
893 doc.appendChild(level1);
895 QDomElement level2 = level1.ownerDocument().createElement("Level_2");
896 if ( insertLevel2AfterCstr )
897 level1.appendChild(level2);
899 QDomElement level3 = level2.ownerDocument().createElement("Level_3");
900 if ( insertLevel3AfterCstr )
901 level2.appendChild(level3);
903 QDomNode level4 = level3.ownerDocument().createTextNode("This_is_a_value!");
904 level3.appendChild(level4);
906 if ( !insertLevel3AfterCstr )
907 level2.appendChild(level3);
908 if ( !insertLevel2AfterCstr )
909 level1.appendChild(level2);
910 if ( !insertLevel1AfterCstr )
911 doc.appendChild(level1);
913 QVERIFY( compareDocuments( doc, docRes ) );
917 bool tst_QDom::isFakeXMLDeclaration(const QDomNode &node)
919 return node.isProcessingInstruction() &&
920 node.nodeName() == QLatin1String("xml");
923 bool tst_QDom::isDeepEqual(const QDomNode &n1, const QDomNode &n2)
925 const QDomNode::NodeType nt = n1.nodeType();
927 if(nt != n2.nodeType())
930 if(n1.nodeName() != n2.nodeName()
931 || n1.namespaceURI() != n2.namespaceURI()
932 || n1.nodeValue() != n2.nodeValue())
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();
943 if(len1 != 0 && isFakeXMLDeclaration(children1.at(0)))
946 if(len2 != 0 && isFakeXMLDeclaration(children2.at(0)))
949 if(len1 - i1 != len2 - i2)
952 // We jump over the first to skip the processing instructions that
953 // are (incorrectly) used as XML declarations.
954 for(; i1 < len1; ++i1)
956 if(!isDeepEqual(children1.at(i1), children2.at(i2)))
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
970 bool tst_QDom::compareDocuments( const QDomDocument &doc1, const QDomDocument &doc2 )
972 return isDeepEqual(doc1, doc2);
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
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.
983 bool tst_QDom::compareNodes( const QDomNode &node1, const QDomNode &node2, bool deep )
988 QTextStream stream( &str1 );
993 QTextStream stream( &str2 );
999 if ( node1.isNull() && node2.isNull() )
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();
1012 \a pathToNode is a list of indices to wanted node in \a doc. Returns the
1015 QDomNode tst_QDom::findDomNode( const QDomDocument &doc, const QList<QVariant> &pathToNode )
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() );
1027 void tst_QDom::browseElements()
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"));
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());
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());
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());
1061 void tst_QDom::domNodeMapAndList()
1063 QString xml_str = QString::fromLatin1("<foo ding='dong'></foo>");
1066 QVERIFY(doc.setContent(xml_str));
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
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
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) \
1082 QVERIFY(doc.isNull()); \
1083 QVERIFY(!doc.func.isNull()); \
1084 QVERIFY(!doc.isNull()); \
1087 void tst_QDom::nullDocument()
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"))
1100 QDomElement elt = doc2.createElement("foo");
1101 doc2.appendChild(elt);
1102 TEST_NULL_DOCUMENT(importNode(elt, true))
1105 #undef TEST_NULL_DOCUMENT
1107 void tst_QDom::invalidName_data()
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");
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();
1124 void tst_QDom::invalidName()
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 );
1132 QDomImplementation impl;
1135 QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars);
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);
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);
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);
1159 QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
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);
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);
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);
1183 QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode);
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);
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);
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);
1208 void tst_QDom::invalidQualifiedName_data()
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");
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();
1226 void tst_QDom::invalidQualifiedName()
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 );
1234 QDomImplementation impl;
1237 QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars);
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);
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);
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);
1258 QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
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);
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);
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);
1279 QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode);
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);
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);
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);
1301 void tst_QDom::invalidCharData_data()
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");
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");
1315 void tst_QDom::invalidCharData()
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 );
1325 QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars);
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);
1335 QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
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);
1345 QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode);
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);
1356 void tst_QDom::roundTripAttributes() const
1358 /* Create an attribute via the QDom API with weird whitespace content. */
1359 QDomImplementation impl;
1361 QDomDocument doc(impl.createDocument("", "localName", QDomDocumentType()));
1363 QDomElement e(doc.documentElement());
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));
1376 e.setAttribute("attr", ws);
1378 QByteArray serialized;
1379 QBuffer buffer(&serialized);
1380 buffer.open(QIODevice::WriteOnly);
1381 QTextStream stream(&buffer);
1383 doc.save(stream, 0);
1386 const QByteArray expected("<localName xmlns=\"\" attr=\" 
	 \"/>\n");
1387 QCOMPARE(QString::fromLatin1(serialized.constData()), QString::fromLatin1(expected.constData()));
1390 void tst_QDom::normalizeEndOfLine() const
1392 QByteArray input("<a>\r\nc\rc\ra\na</a>");
1394 QBuffer buffer(&input);
1395 QVERIFY(buffer.open(QIODevice::ReadOnly));
1398 QVERIFY(doc.setContent(&buffer, true));
1400 const QString expected(QLatin1String("<a>\nc\nc\na\na</a>"));
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);
1407 void tst_QDom::normalizeAttributes() const
1409 QByteArray data("<element attribute=\"a\na\"/>");
1410 QBuffer buffer(&data);
1412 QVERIFY(buffer.open(QIODevice::ReadOnly));
1415 QVERIFY(doc.setContent(&buffer, true));
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"));
1422 void tst_QDom::serializeWeirdEOL() const
1424 QDomImplementation impl;
1426 QDomDocument doc(impl.createDocument("", "name", QDomDocumentType()));
1427 QDomElement ele(doc.documentElement());
1428 ele.appendChild(doc.createTextNode(QLatin1String("\r\nasd\nasd\rasd\n")));
1431 QBuffer writeBuffer(&output);
1432 QVERIFY(writeBuffer.open(QIODevice::WriteOnly));
1433 QTextStream stream(&writeBuffer);
1435 const QByteArray expected("<name xmlns=\"\">
\nasd\nasd
asd\n</name>\n");
1436 doc.save(stream, 0);
1437 QCOMPARE(QString::fromLatin1(output.constData()), QString::fromLatin1(expected.constData()));
1440 void tst_QDom::reparentAttribute() const
1442 QDomImplementation impl;
1443 QDomDocument doc(impl.createDocument("", "localName", QDomDocumentType()));
1445 QDomElement ele(doc.documentElement());
1446 QDomAttr attr(doc.createAttribute("localName"));
1447 ele.setAttributeNode(attr);
1449 QVERIFY(attr.ownerElement() == ele);
1450 QVERIFY(attr.parentNode() == ele);
1453 void tst_QDom::serializeNamespaces() const
1455 const char *const input = "<doc xmlns:b='http://example.com/'>"
1456 "<b:element b:name=''/>"
1459 QByteArray ba(input);
1460 QBuffer buffer(&ba);
1462 QVERIFY(buffer.open(QIODevice::ReadOnly));
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);
1470 QVERIFY(doc.setContent(&source, &reader));
1472 const QByteArray serialized(doc.toByteArray());
1475 QVERIFY(doc2.setContent(doc.toString(), true));
1477 /* Here we test that it roundtrips. */
1478 QVERIFY(isDeepEqual(doc2, doc));
1481 QVERIFY(doc3.setContent(QString::fromLatin1(serialized.constData()), true));
1483 QVERIFY(isDeepEqual(doc3, doc));
1486 void tst_QDom::flagInvalidNamespaces() const
1488 const char *const input = "<doc>"
1489 "<b:element xmlns:b='http://example.com/' b:name='' xmlns:b='http://example.com/'/>"
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)));
1498 void tst_QDom::flagUndeclaredNamespace() const
1500 /* Note, prefix 'a' is not declared. */
1501 const char *const input = "<a:doc xmlns:b='http://example.com/'>"
1502 "<b:element b:name=''/>"
1505 QByteArray ba(input);
1506 QBuffer buffer(&ba);
1508 QVERIFY(buffer.open(QIODevice::ReadOnly));
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);
1516 QEXPECT_FAIL("", "The parser doesn't flag not declared prefixes. Fixing this would change behavior.", Continue);
1517 QVERIFY(!doc.setContent(&source, &reader));
1520 void tst_QDom::indentComments() const
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.
1528 const char *const input = "<e>"
1529 "<!-- A Comment -->"
1530 "<b><!-- deep --></b>"
1532 "<!-- Another Comment -->"
1533 "<!-- Another Comment2 -->"
1536 const char *const expected = "<e>\n"
1537 " <!-- A Comment -->\n"
1542 "<!-- Another Comment -->\n"
1543 " <!-- Another Comment2 -->"
1547 QVERIFY(doc.setContent(QString::fromLatin1(input)));
1549 const QString serialized(doc.toString(5));
1551 QCOMPARE(serialized, QString::fromLatin1(expected));
1554 void tst_QDom::checkLiveness() const
1556 QDomImplementation impl;
1558 QDomDocument doc(impl.createDocument(QString(), "doc", QDomDocumentType()));
1559 QDomElement ele(doc.documentElement());
1561 const QDomElement e1(doc.createElement("name"));
1562 const QDomElement e2(doc.createElement("name"));
1563 const QDomText t1(doc.createTextNode("content"));
1565 ele.appendChild(e1);
1566 ele.appendChild(t1);
1567 ele.appendChild(e2);
1569 const QDomNodeList children(ele.childNodes());
1570 QCOMPARE(children.count(), 3);
1572 ele.removeChild(e1);
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));
1579 void tst_QDom::reportDuplicateAttributes() const
1582 bool isSuccess = dd.setContent(QLatin1String("<test x=\"1\" x=\"2\"/>"));
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.");
1588 void tst_QDom::namespacedAttributes() const
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"
1600 QDomDocument one("document");
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));
1608 QVERIFY(isDeepEqual(one, two));
1611 void tst_QDom::appendChildFromToDocument() const
1614 const QByteArray input("<e/>");
1616 doc.setContent(input);
1618 QDomDocument doc2(doc.documentElement().toDocument());
1619 QDomElement element = doc2.createElement("name");
1620 element.setAttribute("name", "value");
1621 doc.documentElement().appendChild(element);
1624 void tst_QDom::iterateCDATA() const
1626 const QByteArray input("<e><![CDATA[data]]></e>");
1629 QVERIFY(doc.setContent(input));
1630 QCOMPARE(doc.toString(), QString("<e><![CDATA[data]]></e>\n"));
1632 const QDomElement element(doc.documentElement());
1633 QVERIFY(!element.isNull());
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());
1640 QVERIFY(element.childNodes().at(0).isCDATASection());
1646 \brief This function cannot be factored into appendDocumentNode(). The
1647 invocation of constructors/destructors is part of triggering the bug.
1649 QDomDocument tst_QDom::generateRequest()
1652 QDomElement elem = doc.createElement("test_elem");
1654 elem.setAttribute("name", "value");
1655 doc.appendChild(elem);
1659 void tst_QDom::appendDocumentNode() const
1662 QDomDocument xml = generateRequest();
1663 QDomElement elem = doc.createElement("document");
1665 doc.appendChild(elem);
1667 QVERIFY(!xml.isNull());
1668 const QString expected(QLatin1String("<document>\n<test_elem name=\"value\"/>\n</document>\n"));
1670 elem.appendChild(xml);
1671 QCOMPARE(doc.childNodes().count(), 1);
1672 QCOMPARE(doc.toString(0), expected);
1674 elem.appendChild(xml.firstChild());
1675 QCOMPARE(doc.childNodes().count(), 1);
1676 QCOMPARE(doc.toString(0), expected);
1679 static const QChar umlautName[] =
1687 Write a german umlaut to a QByteArray, via a QTextStream.
1689 void tst_QDom::germanUmlautToByteArray() const
1691 QCOMPARE(ulong(sizeof(umlautName) / sizeof(QChar)), ulong(3));
1692 const QString name(umlautName, 3);
1695 d.appendChild(d.createElement(name));
1697 QBuffer buffer(&data);
1698 QVERIFY(buffer.open(QIODevice::WriteOnly));
1699 QTextStream ts(&buffer);
1700 ts.setCodec("UTF-8");
1704 QByteArray baseline("<a");
1706 /* http://www.fileformat.info/info/unicode/char/00FC/index.htm */
1709 baseline += "b/>\n";
1711 QCOMPARE(data, baseline);
1717 Write a german umlaut to a QFile, via a QTextStream.
1719 void tst_QDom::germanUmlautToFile() const
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);
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");
1736 QFile inFile("germanUmlautToFile.xml");
1737 QVERIFY(inFile.open(QIODevice::ReadOnly));
1739 QString baseline(QLatin1String("<!DOCTYPE test>\n<german"));
1740 baseline += QChar(0xFC);
1741 baseline += QLatin1String("umlaut/>\n");
1743 const QByteArray in(inFile.readAll());
1744 /* Check that it was wwritten out correctly. */
1745 QCOMPARE(in.length(), 34);
1746 QCOMPARE(in, baseline.toUtf8());
1749 /* Check that we read it in correctly with QDomDocument::setContent(). */
1750 QVERIFY(inFile.open(QIODevice::ReadOnly));
1752 QVERIFY(dd.setContent(&inFile));
1754 QCOMPARE(dd.toString(), baseline);
1757 void tst_QDom::setInvalidDataPolicy() const
1759 QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode);
1761 QDomElement elem = doc.createElement("invalid name");
1762 QVERIFY(elem.isNull());
1765 void tst_QDom::crashInSetContent() const
1767 QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode);
1768 QDomDocument docImport;
1770 QCOMPARE(docImport.setContent(QLatin1String("<a:>text</a:>"), true), false);
1771 QVERIFY(docImport.setContent(QLatin1String("<?xml version=\"1.0\"?><e/>")));
1774 void tst_QDom::doubleNamespaceDeclarations() const
1778 QFile file(SRCDIR "doubleNamespaces.xml" );
1779 QVERIFY(file.open(QIODevice::ReadOnly));
1781 QXmlSimpleReader reader;
1783 QXmlInputSource source(&file);
1784 QVERIFY(doc.setContent(&source, &reader));
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"));
1790 void tst_QDom::setContentQXmlReaderOverload() const
1794 QXmlSimpleReader reader;
1795 QXmlInputSource data;
1796 data.setData(QByteArray("<e/>"));
1798 doc.setContent(&data, true);
1799 QCOMPARE(doc.documentElement().nodeName(), QString::fromLatin1("e"));
1802 void tst_QDom::cleanupTestCase() const
1804 QFile::remove("germanUmlautToFile.xml");
1807 void tst_QDom::toStringWithoutNewlines() const
1810 doc.setContent(QLatin1String("<doc><e/></doc>"));
1812 QCOMPARE(doc.toString(0), QString::fromLatin1("<doc>\n<e/>\n</doc>\n"));
1813 QCOMPARE(doc.toString(-1), QString::fromLatin1("<doc><e/></doc>"));
1816 void tst_QDom::checkIntOverflow() const
1818 /* This test takes a *very* long time to run, so it is at best a manual
1822 /* QDom used an internal global int which overflowed. So iterate until an
1823 * uint wrapsaround. */
1824 const QString xmlMessage(QLatin1String("<test/>"));
1826 bool hasWrapped = false;
1827 for(uint i = 1; i != 0; ++i)
1829 /* We want to exit the second time, not loop infinitely. */
1830 if(i == 1 && hasWrapped)
1836 QVERIFY(doc.setContent(xmlMessage));
1838 const QDomNodeList nl(doc.elementsByTagName(QLatin1String("test")));
1839 QCOMPARE(nl.length(), uint(1));
1843 void tst_QDom::setContentWhitespace() const
1845 QFETCH(QString, doc);
1846 QFETCH(bool, expectedValidity);
1848 QDomDocument domDoc;
1850 QCOMPARE(domDoc.setContent(doc), expectedValidity);
1852 if(expectedValidity)
1853 QCOMPARE(domDoc.documentElement().nodeName(), QString::fromLatin1("e"));
1856 void tst_QDom::setContentWhitespace_data() const
1858 QTest::addColumn<QString>("doc");
1859 QTest::addColumn<bool>("expectedValidity");
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;
1874 /* With XML prolog. */
1875 QTest::newRow("") << QString::fromLatin1("<?xml version='1.0' ?><e/>") << true;
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;
1891 void tst_QDom::taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const
1893 QString xmlWithUnknownEncoding("<?xml version='1.0' encoding='unknown-encoding'?>"
1895 " <bar>How will this sentence be handled?</bar>"
1898 QVERIFY(d.setContent(xmlWithUnknownEncoding));
1900 QString dontAssert = d.toString(); // this should not assert
1904 void tst_QDom::cloneDTD_QTBUG8398() const
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"
1912 QDomDocument domDocument;
1913 QVERIFY(domDocument.setContent(dtd));
1914 QDomDocument domDocument2 = domDocument.cloneNode(true).toDocument();
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"
1924 QTextStream stream(&output);
1925 domDocument2.save(stream, 0);
1926 QCOMPARE(output, expected);
1929 void tst_QDom::DTDNotationDecl()
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"
1938 QDomDocument domDocument;
1939 QVERIFY(domDocument.setContent(dtd));
1941 const QDomDocumentType doctype = domDocument.doctype();
1942 QCOMPARE(doctype.notations().size(), 2);
1944 QVERIFY(doctype.namedItem(QString("gif")).isNotation());
1945 QCOMPARE(doctype.namedItem(QString("gif")).toNotation().systemId(), QString("image/gif"));
1947 QVERIFY(doctype.namedItem(QString("jpeg")).isNotation());
1948 QCOMPARE(doctype.namedItem(QString("jpeg")).toNotation().systemId(), QString("image/jpeg"));
1951 void tst_QDom::DTDEntityDecl()
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>"
1960 QDomDocument domDocument;
1961 QVERIFY(domDocument.setContent(dtd));
1963 const QDomDocumentType doctype = domDocument.doctype();
1964 QCOMPARE(doctype.entities().count(), 2);
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());
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"));
1975 QTEST_MAIN(tst_QDom)
1976 #include "tst_qdom.moc"