/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include QT_FORWARD_DECLARE_CLASS(QDomDocument) QT_FORWARD_DECLARE_CLASS(QDomNode) class tst_QDom : public QObject { Q_OBJECT private slots: void initTestCase(); void namespacedAttributes() const; void setContent_data(); void setContent(); void toString_01_data(); void toString_01(); void toString_02_data(); void toString_02(); void hasAttributes_data(); void hasAttributes(); void save_data(); void save(); void saveWithSerialization() const; void saveWithSerialization_data() const; void cloneNode_data(); void cloneNode(); void ownerDocument_data(); void ownerDocument(); void ownerDocumentTask27424_data(); void ownerDocumentTask27424(); void parentNode_data(); void parentNode(); void documentCreationTask27424_data(); void documentCreationTask27424(); void browseElements(); void ownerElementTask45192_data(); void ownerElementTask45192(); void domNodeMapAndList(); void nullDocument(); void invalidName_data(); void invalidName(); void invalidQualifiedName_data(); void invalidQualifiedName(); void invalidCharData_data(); void invalidCharData(); void roundTripAttributes() const; void normalizeEndOfLine() const; void normalizeAttributes() const; void serializeWeirdEOL() const; void reparentAttribute() const; void serializeNamespaces() const; void flagInvalidNamespaces() const; void flagUndeclaredNamespace() const; void indentComments() const; void checkLiveness() const; void reportDuplicateAttributes() const; void appendChildFromToDocument() const; void iterateCDATA() const; void appendDocumentNode() const; void germanUmlautToByteArray() const; void germanUmlautToFile() const; void setInvalidDataPolicy() const; void crashInSetContent() const; void doubleNamespaceDeclarations() const; void setContentQXmlReaderOverload() const; void toStringWithoutNewlines() const; void checkIntOverflow() const; void setContentWhitespace() const; void setContentWhitespace_data() const; void taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const; void cloneDTD_QTBUG8398() const; void DTDNotationDecl(); void DTDEntityDecl(); void cleanupTestCase() const; private: static QDomDocument generateRequest(); static int hasAttributesHelper( const QDomNode& node ); static bool compareDocuments( const QDomDocument &doc1, const QDomDocument &doc2 ); static bool compareNodes( const QDomNode &node1, const QDomNode &node2, bool deep ); static QDomNode findDomNode( const QDomDocument &doc, const QList &pathToNode ); static QString onNullWarning(const char *const functionName); static bool isDeepEqual(const QDomNode &n1, const QDomNode &n2); static bool isFakeXMLDeclaration(const QDomNode &node); QList m_testCodecs; }; void tst_QDom::setContent_data() { const QString doc01( " ]>\n" "\n" " \n" " foo\n" " bar\n" " foo & bar\n" " foo &blubber; bar\n" " \n" " \n" " \n" " \n" " \n" "\n" ); QTest::addColumn("doc"); QTest::addColumn("featuresTrue"); QTest::addColumn("featuresFalse"); QTest::addColumn("res"); QTest::newRow( "01" ) << doc01 << QStringList() << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ') << QString("\n" "\n" " \n" " foo\n" " bar\n" " foo & bar\n" " foo and bar\n" " \n" " \n" " \n" " \n" " \n" "\n"); QTest::newRow( "02" ) << doc01 << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ') << QStringList() << QString("\n" "\n" " \n" " foo\n" " bar\n" " foo & bar\n" " foo and bar\n" " \n" " \n" " \n" " \n" " \n" "\n"); QTest::newRow( "03" ) << doc01 << QString("http://trolltech.com/xml/features/report-start-end-entity").split(' ') << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData").split(' ') << QString("\n" "]>\n" "\n" " \n" " foo\n" " bar\n" " foo & bar\n" " foo &blubber; bar\n" " \n" " \n" " \n" " \n" " \n" "\n"); QTest::newRow( "04" ) << doc01 << QString("http://trolltech.com/xml/features/report-whitespace-only-CharData http://trolltech.com/xml/features/report-start-end-entity").split(' ') << QStringList() << QString("\n" "]>\n" "\n" " \n" " foo\n" " bar\n" " foo & bar\n" " foo &blubber; bar\n" " \n" " \n" " \n" " \n" " \n" "\n"); QTest::newRow("05") << QString("\n" " <b>foo</b>>]]>\n" "\n") << QStringList() << QStringList() << QString("\n" " <b>foo</b>>]]>\n" "\n"); } void tst_QDom::setContent() { QFETCH( QString, doc ); QXmlInputSource source; source.setData( doc ); QFETCH( QStringList, featuresTrue ); QFETCH( QStringList, featuresFalse ); QXmlSimpleReader reader; QStringList::Iterator it; for ( it = featuresTrue.begin(); it != featuresTrue.end(); ++it ) { QVERIFY( reader.hasFeature( *it ) ); reader.setFeature( *it, true ); } for ( it = featuresFalse.begin(); it != featuresFalse.end(); ++it ) { QVERIFY( reader.hasFeature( *it ) ); reader.setFeature( *it, false ); } QDomDocument domDoc; QVERIFY( domDoc.setContent( &source, &reader ) ); QString eRes; QTextStream ts( &eRes, QIODevice::WriteOnly ); domDoc.save( ts, 4 ); QTEST( eRes, "res" ); // make sure that if we parse our output again, we get the same document QDomDocument domDoc1; QDomDocument domDoc2; QVERIFY( domDoc1.setContent( doc ) ); QVERIFY( domDoc2.setContent( eRes ) ); QVERIFY( compareDocuments( domDoc1, domDoc2 ) ); } void tst_QDom::toString_01_data() { QTest::addColumn("fileName"); const QString prefix = QFINDTESTDATA("testdata/toString_01"); if (prefix.isEmpty()) QFAIL("Cannot find testdata directory!"); QTest::newRow( "01" ) << QString(prefix + "/doc01.xml"); QTest::newRow( "02" ) << QString(prefix + "/doc02.xml"); QTest::newRow( "03" ) << QString(prefix + "/doc03.xml"); QTest::newRow( "04" ) << QString(prefix + "/doc04.xml"); QTest::newRow( "05" ) << QString(prefix + "/doc05.xml"); QTest::newRow( "euc-jp" ) << QString(prefix + "/doc_euc-jp.xml"); QTest::newRow( "iso-2022-jp" ) << QString(prefix + "/doc_iso-2022-jp.xml"); QTest::newRow( "little-endian" ) << QString(prefix + "/doc_little-endian.xml"); QTest::newRow( "utf-16" ) << QString(prefix + "/doc_utf-16.xml"); QTest::newRow( "utf-8" ) << QString(prefix + "/doc_utf-8.xml"); } /*! \internal This function tests that the QDomDocument::toString() function results in the same XML document. The meaning of "same" in this context means that the "information" in the resulting XML file is the same as in the original, i.e. we are not intrested in different formatting, etc. To achieve this, the XML document of the toString() function is parsed again and the two QDomDocuments are compared. */ void tst_QDom::toString_01() { QFETCH(QString, fileName); QFile f(fileName); QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Failed to open file %1, file error: %2").arg(fileName).arg(f.error()))); QDomDocument doc; QString errorMsg; int errorLine; int errorCol; QVERIFY(doc.setContent( &f, &errorMsg, &errorLine, &errorCol )); /*, QString("QDomDocument::setContent() failed: %1 in line %2, column %3") .arg( errorMsg ).arg( errorLine ).arg( errorCol )); */ // test toString()'s invariant with different indenting depths for ( int i=0; i<5; i++ ) { QString toStr = doc.toString( i ); QDomDocument res; QVERIFY( res.setContent( toStr ) ); QVERIFY( compareDocuments( doc, res ) ); } } void tst_QDom::toString_02_data() { save_data(); } /* Tests the new QDomDocument::toString(int) overload (basically the same test as save()). */ void tst_QDom::toString_02() { QFETCH( QString, doc ); QFETCH( int, indent ); QDomDocument domDoc; QVERIFY( domDoc.setContent( doc ) ); QTEST( domDoc.toString(indent), "res" ); } void tst_QDom::hasAttributes_data() { QTest::addColumn("visitedNodes"); QTest::addColumn("xmlDoc"); QByteArray doc1("Make a stupid, useless test sentence."); QByteArray doc2("Make a stupid, useless test sentence."); QByteArray doc3("\n" "\n" "\n" "\n" "\n" "\n" "blubber\n" "more text, pretty unintresting, though\n" "\n" "] ]]>\n" "\n" "\n" "\n"); QTest::newRow( "01" ) << 6 << doc1; QTest::newRow( "02" ) << 6 << doc2; QTest::newRow( "03" ) << 13 << doc3; } /* This function tests that QDomNode::hasAttributes() returns true if and only if the node has attributes (i.e. QDomNode::attributes() returns a list with attributes in it). */ void tst_QDom::hasAttributes() { QFETCH( QByteArray, xmlDoc ); QDomDocument doc; QVERIFY( doc.setContent( xmlDoc ) ); int visitedNodes = hasAttributesHelper( doc ); QTEST( visitedNodes, "visitedNodes" ); } int tst_QDom::hasAttributesHelper( const QDomNode& node ) { int visitedNodes = 1; if ( node.hasAttributes() ) { if (node.attributes().count() == 0) return -1; // QVERIFY( node.attributes().count() > 0 ); } else { if (node.attributes().count() != 0) return -1; // QVERIFY( node.attributes().count() == 0 ); } QDomNodeList children = node.childNodes(); for ( int i=0; i\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" ); QTest::addColumn("doc"); QTest::addColumn("indent"); QTest::addColumn("res"); QTest::newRow( "01" ) << doc01 << 0 << QString(doc01).replace( QRegExp(" "), "" ); QTest::newRow( "02" ) << doc01 << 1 << doc01; QTest::newRow( "03" ) << doc01 << 2 << QString(doc01).replace( QRegExp(" "), " " ); QTest::newRow( "04" ) << doc01 << 10 << QString(doc01).replace( QRegExp(" "), " " ); } void tst_QDom::save() { QFETCH( QString, doc ); QFETCH( int, indent ); QDomDocument domDoc; QVERIFY( domDoc.setContent( doc ) ); QString eRes; QTextStream ts( &eRes, QIODevice::WriteOnly ); domDoc.save( ts, indent ); QTEST( eRes, "res" ); } void tst_QDom::initTestCase() { QString testFile = QFINDTESTDATA("testdata/testCodecs.txt"); if (testFile.isEmpty()) QFAIL("Cannot find testdata/testCodecs.txt"); QFile file(testFile); QVERIFY(file.open(QIODevice::ReadOnly|QIODevice::Text)); QByteArray codecName; m_testCodecs = file.readAll().split('\n'); if (m_testCodecs.last().isEmpty()) m_testCodecs.removeLast(); } void tst_QDom::saveWithSerialization() const { QFETCH(QString, fileName); QFile f(fileName); QVERIFY(f.open(QIODevice::ReadOnly)); QDomDocument doc; // Read the document QVERIFY(doc.setContent(&f)); QByteArray codecName; foreach (codecName, m_testCodecs) { /* Write out doc in the specified codec. */ QByteArray storage; QBuffer writeDevice(&storage); QVERIFY(writeDevice.open(QIODevice::WriteOnly)); QTextStream s(&writeDevice); QTextCodec *codec = QTextCodec::codecForName(codecName); QVERIFY2(codec, qPrintable(QString::fromLatin1("Failed to load codec %1").arg(QString::fromLatin1(codecName.constData())))); s.setCodec(codec); doc.save(s, 0, QDomNode::EncodingFromTextStream); s.flush(); writeDevice.close(); QBuffer readDevice(&storage); QVERIFY(readDevice.open(QIODevice::ReadOnly)); QDomDocument result; QString msg; int line = 0; int column = 0; QVERIFY2(result.setContent(&readDevice, &msg, &line, &column), qPrintable(QString::fromLatin1("Failed for codec %1: line %2, column %3: %4, content: %5") .arg(QString::fromLatin1(codecName.constData()), QString::number(line), QString::number(column), msg, codec->toUnicode(storage)))); if(!compareDocuments(doc, result)) { QCOMPARE(doc.toString(), result.toString()); /* We put this one here as well, in case the QCOMPARE above for some strange reason * nevertheless succeeds. */ QVERIFY2(false, qPrintable(QString::fromLatin1("Failed for codec %1").arg(QString::fromLatin1(codecName.constData())))); } } } void tst_QDom::saveWithSerialization_data() const { QTest::addColumn("fileName"); const QString prefix = QFINDTESTDATA("testdata/toString_01"); if (prefix.isEmpty()) QFAIL("Cannot find testdata!"); QTest::newRow("doc01.xml") << QString(prefix + "/doc01.xml"); QTest::newRow("doc02.xml") << QString(prefix + "/doc02.xml"); QTest::newRow("doc03.xml") << QString(prefix + "/doc03.xml"); QTest::newRow("doc04.xml") << QString(prefix + "/doc04.xml"); QTest::newRow("doc05.xml") << QString(prefix + "/doc05.xml"); QTest::newRow("doc_euc-jp.xml") << QString(prefix + "/doc_euc-jp.xml"); QTest::newRow("doc_iso-2022-jp.xml") << QString(prefix + "/doc_iso-2022-jp.xml"); QTest::newRow("doc_little-endian.xml") << QString(prefix + "/doc_little-endian.xml"); QTest::newRow("doc_utf-16.xml") << QString(prefix + "/doc_utf-16.xml"); QTest::newRow("doc_utf-8.xml") << QString(prefix + "/doc_utf-8.xml"); } void tst_QDom::cloneNode_data() { const QString doc01( "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" ); QList nodeB1; nodeB1 << 0; QList nodeC1; nodeC1 << 0 << 0; QList nodeC2; nodeC2 << 0 << 1; QTest::addColumn("doc"); QTest::addColumn >("pathToNode"); QTest::addColumn("deep"); QTest::newRow( "noDeep_01" ) << doc01 << nodeB1 << false; QTest::newRow( "noDeep_02" ) << doc01 << nodeC1 << false; QTest::newRow( "noDeep_03" ) << doc01 << nodeC2 << false; QTest::newRow( "deep_01" ) << doc01 << nodeB1 << true; QTest::newRow( "deep_02" ) << doc01 << nodeC1 << true; QTest::newRow( "deep_03" ) << doc01 << nodeC2 << true; } void tst_QDom::cloneNode() { QFETCH( QString, doc ); QFETCH( QList, pathToNode ); QFETCH( bool, deep ); QDomDocument domDoc; QVERIFY( domDoc.setContent( doc ) ); QDomNode node = findDomNode( domDoc, pathToNode ); QVERIFY(!node.isNull()); QDomNode clonedNode = node.cloneNode( deep ); QVERIFY( compareNodes( node, clonedNode, deep ) ); QDomNode parent = node.parentNode(); if ( !parent.isNull() ) { node = parent.replaceChild( clonedNode, node ); // swap the nodes QVERIFY( !node.isNull() ); QVERIFY( compareNodes( node, clonedNode, deep ) ); } } void tst_QDom::ownerElementTask45192_data() { const QString doc( "\n" " \n" " \n" "" ); QTest::addColumn("doc"); QTest::newRow("doc") << doc; } void tst_QDom::ownerElementTask45192() { QFETCH( QString, doc ); QDomDocument domDoc; QVERIFY( domDoc.setContent( doc ) ); QDomNode item = domDoc.documentElement().firstChild(); QDomNode clone = item.cloneNode(false); QVERIFY( clone == clone.attributes().namedItem("name").toAttr().ownerElement() ); } void tst_QDom::ownerDocument_data() { cloneNode_data(); } #define OWNERDOCUMENT_CREATE_TEST( t, x ) \ { \ t n = x; \ QVERIFY( n.ownerDocument() == domDoc ); \ } #define OWNERDOCUMENT_IMPORTNODE_TEST( t, x ) \ { \ QDomNode importedNode; \ t n = x; \ QVERIFY( n.ownerDocument() != domDoc ); \ importedNode = domDoc.importNode( n, deep ); \ QVERIFY( n.ownerDocument() != domDoc ); \ QVERIFY( importedNode.ownerDocument() == domDoc ); \ } void tst_QDom::ownerDocument() { QFETCH( QString, doc ); QFETCH( QList, pathToNode ); QFETCH( bool, deep ); QDomDocument domDoc; QVERIFY( domDoc.setContent( doc ) ); QDomNode node = findDomNode( domDoc, pathToNode ); QVERIFY(!node.isNull()); QVERIFY( node.ownerDocument() == domDoc ); // Does cloneNode() keep the ownerDocument()? { QDomNode clonedNode = node.cloneNode( deep ); QVERIFY( node.ownerDocument() == domDoc ); QVERIFY( clonedNode.ownerDocument() == domDoc ); } // If the original DOM node is replaced with the cloned node, does this // keep the ownerDocument()? { QDomNode clonedNode = node.cloneNode( deep ); QDomNode parent = node.parentNode(); if ( !parent.isNull() ) { node = parent.replaceChild( clonedNode, node ); // swap the nodes QVERIFY( node.ownerDocument() == domDoc ); QVERIFY( clonedNode.ownerDocument() == domDoc ); } } // test QDomDocument::create...() { OWNERDOCUMENT_CREATE_TEST( QDomAttr, domDoc.createAttribute( "foo" ) ); OWNERDOCUMENT_CREATE_TEST( QDomAttr, domDoc.createAttributeNS( "foo", "bar" ) ); OWNERDOCUMENT_CREATE_TEST( QDomCDATASection, domDoc.createCDATASection( "foo" ) ); OWNERDOCUMENT_CREATE_TEST( QDomComment, domDoc.createComment( "foo" ) ); OWNERDOCUMENT_CREATE_TEST( QDomDocumentFragment, domDoc.createDocumentFragment() ); OWNERDOCUMENT_CREATE_TEST( QDomElement, domDoc.createElement( "foo" ) ); OWNERDOCUMENT_CREATE_TEST( QDomElement, domDoc.createElementNS( "foo", "bar" ) ); OWNERDOCUMENT_CREATE_TEST( QDomEntityReference, domDoc.createEntityReference( "foo" ) ); OWNERDOCUMENT_CREATE_TEST( QDomProcessingInstruction, domDoc.createProcessingInstruction( "foo", "bar" ) ); OWNERDOCUMENT_CREATE_TEST( QDomText, domDoc.createTextNode( "foo" ) ); } // test importNode() { QDomDocument doc2; OWNERDOCUMENT_IMPORTNODE_TEST( QDomAttr, doc2.createAttribute( "foo" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomAttr, doc2.createAttributeNS( "foo", "bar" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomCDATASection, doc2.createCDATASection( "foo" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomComment, doc2.createComment( "foo" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomDocumentFragment, doc2.createDocumentFragment() ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomElement, doc2.createElement( "foo" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomElement, doc2.createElementNS( "foo", "bar" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomEntityReference, doc2.createEntityReference( "foo" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomProcessingInstruction, doc2.createProcessingInstruction( "foo", "bar" ) ); OWNERDOCUMENT_IMPORTNODE_TEST( QDomText, doc2.createTextNode( "foo" ) ); } } void tst_QDom::ownerDocumentTask27424_data() { QTest::addColumn("insertLevel1AfterCstr"); QTest::addColumn("insertLevel2AfterCstr"); QTest::addColumn("insertLevel3AfterCstr"); QTest::newRow( "000" ) << false << false << false; QTest::newRow( "001" ) << false << false << true; QTest::newRow( "010" ) << false << true << false; QTest::newRow( "011" ) << false << true << true; QTest::newRow( "100" ) << true << false << false; QTest::newRow( "101" ) << true << false << true; QTest::newRow( "110" ) << true << true << false; QTest::newRow( "111" ) << true << true << true; } void tst_QDom::ownerDocumentTask27424() { QFETCH( bool, insertLevel1AfterCstr ); QFETCH( bool, insertLevel2AfterCstr ); QFETCH( bool, insertLevel3AfterCstr ); QDomDocument doc("TestXML"); QDomElement level1 = doc.createElement("Level_1"); QVERIFY( level1.ownerDocument() == doc ); if ( insertLevel1AfterCstr ) { doc.appendChild(level1); QVERIFY( level1.ownerDocument() == doc ); } QDomElement level2 = level1.ownerDocument().createElement("Level_2"); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); if ( insertLevel2AfterCstr ) { level1.appendChild(level2); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); } QDomElement level3 = level2.ownerDocument().createElement("Level_3"); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); QVERIFY( level3.ownerDocument() == doc ); if ( insertLevel3AfterCstr ) { level2.appendChild(level3); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); QVERIFY( level3.ownerDocument() == doc ); } QDomNode level4 = level3.ownerDocument().createTextNode("This_is_a_value!"); QVERIFY( level4.ownerDocument() == doc ); level3.appendChild(level4); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); QVERIFY( level3.ownerDocument() == doc ); QVERIFY( level4.ownerDocument() == doc ); if ( !insertLevel3AfterCstr ) { level2.appendChild(level3); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); QVERIFY( level3.ownerDocument() == doc ); QVERIFY( level4.ownerDocument() == doc ); } if ( !insertLevel2AfterCstr ) { level1.appendChild(level2); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); QVERIFY( level3.ownerDocument() == doc ); QVERIFY( level4.ownerDocument() == doc ); } if ( !insertLevel1AfterCstr ) { doc.appendChild(level1); QVERIFY( level1.ownerDocument() == doc ); QVERIFY( level2.ownerDocument() == doc ); QVERIFY( level3.ownerDocument() == doc ); QVERIFY( level4.ownerDocument() == doc ); } } void tst_QDom::parentNode_data() { cloneNode_data(); } #define PARENTNODE_CREATE_TEST( t, x ) \ { \ t n = x; \ QVERIFY( n.parentNode().isNull() ); \ } void tst_QDom::parentNode() { QFETCH( QString, doc ); QFETCH( QList, pathToNode ); QFETCH( bool, deep ); QDomDocument domDoc; QVERIFY( domDoc.setContent( doc ) ); QDomNode node = findDomNode( domDoc, pathToNode ); QVERIFY(!node.isNull()); Q_UNUSED(deep); // test QDomDocument::create...() { PARENTNODE_CREATE_TEST( QDomAttr, domDoc.createAttribute( "foo" ) ); PARENTNODE_CREATE_TEST( QDomAttr, domDoc.createAttributeNS( "foo", "bar" ) ); PARENTNODE_CREATE_TEST( QDomCDATASection, domDoc.createCDATASection( "foo" ) ); PARENTNODE_CREATE_TEST( QDomComment, domDoc.createComment( "foo" ) ); PARENTNODE_CREATE_TEST( QDomDocumentFragment, domDoc.createDocumentFragment() ); PARENTNODE_CREATE_TEST( QDomElement, domDoc.createElement( "foo" ) ); PARENTNODE_CREATE_TEST( QDomElement, domDoc.createElementNS( "foo", "bar" ) ); PARENTNODE_CREATE_TEST( QDomEntityReference, domDoc.createEntityReference( "foo" ) ); PARENTNODE_CREATE_TEST( QDomProcessingInstruction, domDoc.createProcessingInstruction( "foo", "bar" ) ); PARENTNODE_CREATE_TEST( QDomText, domDoc.createTextNode( "foo" ) ); } } void tst_QDom::documentCreationTask27424_data() { QTest::addColumn("insertLevel1AfterCstr"); QTest::addColumn("insertLevel2AfterCstr"); QTest::addColumn("insertLevel3AfterCstr"); QTest::newRow( "000" ) << false << false << false; QTest::newRow( "001" ) << false << false << true; QTest::newRow( "010" ) << false << true << false; QTest::newRow( "011" ) << false << true << true; QTest::newRow( "100" ) << true << false << false; QTest::newRow( "101" ) << true << false << true; QTest::newRow( "110" ) << true << true << false; QTest::newRow( "111" ) << true << true << true; } void tst_QDom::documentCreationTask27424() { QFETCH( bool, insertLevel1AfterCstr ); QFETCH( bool, insertLevel2AfterCstr ); QFETCH( bool, insertLevel3AfterCstr ); QDomDocument docRes; QVERIFY( docRes.setContent( QString( "\n" "\n" " \n" " This_is_a_value!\n" " \n" "" ) ) ); QDomDocument doc("TestXML"); QDomElement level1 = doc.createElement("Level_1"); if ( insertLevel1AfterCstr ) doc.appendChild(level1); QDomElement level2 = level1.ownerDocument().createElement("Level_2"); if ( insertLevel2AfterCstr ) level1.appendChild(level2); QDomElement level3 = level2.ownerDocument().createElement("Level_3"); if ( insertLevel3AfterCstr ) level2.appendChild(level3); QDomNode level4 = level3.ownerDocument().createTextNode("This_is_a_value!"); level3.appendChild(level4); if ( !insertLevel3AfterCstr ) level2.appendChild(level3); if ( !insertLevel2AfterCstr ) level1.appendChild(level2); if ( !insertLevel1AfterCstr ) doc.appendChild(level1); QVERIFY( compareDocuments( doc, docRes ) ); } bool tst_QDom::isFakeXMLDeclaration(const QDomNode &node) { return node.isProcessingInstruction() && node.nodeName() == QLatin1String("xml"); } bool tst_QDom::isDeepEqual(const QDomNode &n1, const QDomNode &n2) { const QDomNode::NodeType nt = n1.nodeType(); if(nt != n2.nodeType()) return false; if(n1.nodeName() != n2.nodeName() || n1.namespaceURI() != n2.namespaceURI() || n1.nodeValue() != n2.nodeValue()) return false; /* Check the children. */ const QDomNodeList children1(n1.childNodes()); const QDomNodeList children2(n2.childNodes()); uint len1 = children1.length(); uint len2 = children2.length(); uint i1 = 0; uint i2 = 0; if(len1 != 0 && isFakeXMLDeclaration(children1.at(0))) ++i1; if(len2 != 0 && isFakeXMLDeclaration(children2.at(0))) ++i2; if(len1 - i1 != len2 - i2) return false; // We jump over the first to skip the processing instructions that // are (incorrectly) used as XML declarations. for(; i1 < len1; ++i1) { if(!isDeepEqual(children1.at(i1), children2.at(i2))) return false; ++i2; } return true; } /* Returns true if \a doc1 and \a doc2 represent the same XML document, i.e. they have the same informational content. Otherwise, this function returns false. */ bool tst_QDom::compareDocuments( const QDomDocument &doc1, const QDomDocument &doc2 ) { return isDeepEqual(doc1, doc2); } /* Returns true if \a node1 and \a node2 represent the same XML node, i.e. they have the same informational content. Otherwise, this function returns false. If \a deep is true, children of the nodes are also tested. If \a deep is false, only \a node1 and \a node 2 are compared. */ bool tst_QDom::compareNodes( const QDomNode &node1, const QDomNode &node2, bool deep ) { if ( deep ) { QString str1; { QTextStream stream( &str1 ); stream << node1; } QString str2; { QTextStream stream( &str2 ); stream << node2; } return str1 == str2; } if ( node1.isNull() && node2.isNull() ) return true; // ### I am not sure if this test is complete bool equal = node1.nodeName() == node2.nodeName(); equal = equal && node1.nodeType() == node2.nodeType(); equal = equal && node1.localName() == node2.localName(); equal = equal && node1.nodeValue() == node2.nodeValue(); equal = equal && node1.prefix() == node2.prefix(); return equal; } /* \a pathToNode is a list of indices to wanted node in \a doc. Returns the wanted node. */ QDomNode tst_QDom::findDomNode( const QDomDocument &doc, const QList &pathToNode ) { QDomNode node = doc; QList::const_iterator it; for ( it = pathToNode.begin(); it != pathToNode.end(); ++it ) { QDomNodeList children = node.childNodes(); node = children.item( (*it).toInt() ); // QVERIFY( !node.isNull() ); } return node; } void tst_QDom::browseElements() { QDomDocument doc; QDomElement root = doc.createElement("foo"); doc.appendChild(root); root.appendChild(doc.createElement("bar")); root.appendChild(doc.createElement("bop")); root.appendChild(doc.createElement("bar")); root.appendChild(doc.createElement("bop")); QVERIFY(doc.firstChildElement("ding").isNull()); QDomElement foo = doc.firstChildElement("foo"); QVERIFY(!foo.isNull()); QVERIFY(foo.firstChildElement("ding").isNull()); QVERIFY(foo.nextSiblingElement("foo").isNull()); QVERIFY(foo.previousSiblingElement("bar").isNull()); QVERIFY(foo.nextSiblingElement().isNull()); QVERIFY(foo.previousSiblingElement().isNull()); QDomElement bar = foo.firstChildElement("bar"); QVERIFY(!bar.isNull()); QVERIFY(bar.previousSiblingElement("bar").isNull()); QVERIFY(bar.previousSiblingElement().isNull()); QVERIFY(bar.nextSiblingElement("bar").tagName() == "bar"); QVERIFY(bar.nextSiblingElement("bar").nextSiblingElement("bar").isNull()); QDomElement bop = foo.firstChildElement("bop"); QVERIFY(!bop.isNull()); QVERIFY(bar.nextSiblingElement() == bop); QVERIFY(bop.nextSiblingElement("bop") == foo.lastChildElement("bop")); QVERIFY(bop.previousSiblingElement("bar") == foo.firstChildElement("bar")); QVERIFY(bop.previousSiblingElement("bar") == foo.firstChildElement()); } void tst_QDom::domNodeMapAndList() { QString xml_str = QString::fromLatin1(""); QDomDocument doc; QVERIFY(doc.setContent(xml_str)); QDomNamedNodeMap map = doc.documentElement().attributes(); QCOMPARE(map.item(0).nodeName(), QString("ding")); QCOMPARE(map.item(1).nodeName(), QString()); // Make sure we don't assert QDomNodeList list = doc.elementsByTagName("foo"); QCOMPARE(list.item(0).nodeName(), QString("foo")); QCOMPARE(list.item(1).nodeName(), QString()); // Make sure we don't assert } // Verifies that a default-constructed QDomDocument is null, and that calling // any of the factory functions causes it to be non-null. #define TEST_NULL_DOCUMENT(func) \ { \ QDomDocument doc; \ QVERIFY(doc.isNull()); \ QVERIFY(!doc.func.isNull()); \ QVERIFY(!doc.isNull()); \ } void tst_QDom::nullDocument() { TEST_NULL_DOCUMENT(createAttribute("foo")) TEST_NULL_DOCUMENT(createAttributeNS("http://foo/", "bar")) TEST_NULL_DOCUMENT(createCDATASection("foo")) TEST_NULL_DOCUMENT(createComment("foo")) TEST_NULL_DOCUMENT(createDocumentFragment()) TEST_NULL_DOCUMENT(createElement("foo")) TEST_NULL_DOCUMENT(createElementNS("http://foo/", "foo")) TEST_NULL_DOCUMENT(createEntityReference("foo")) TEST_NULL_DOCUMENT(createProcessingInstruction("foo", "bar")) TEST_NULL_DOCUMENT(createTextNode("foo")) QDomDocument doc2; QDomElement elt = doc2.createElement("foo"); doc2.appendChild(elt); TEST_NULL_DOCUMENT(importNode(elt, true)) } #undef TEST_NULL_DOCUMENT void tst_QDom::invalidName_data() { QTest::addColumn("in_name"); QTest::addColumn("ok_AcceptInvalidChars"); QTest::addColumn("ok_DropInvalidChars"); QTest::addColumn("ok_ReturnNullNode"); QTest::addColumn("out_name"); QTest::newRow( "foo" ) << QString("foo") << true << true << true << QString("foo"); QTest::newRow( "_f.o-o:" ) << QString("_f.o-o:") << true << true << true << QString("_f.o-o:"); QTest::newRow( "...:." ) << QString("...:.") << true << true << false << QString(":."); QTest::newRow( "empty" ) << QString() << false << false << false << QString(); QTest::newRow( "~f~o~o~" ) << QString("~f~o~o~") << true << true << false << QString("foo"); QTest::newRow( "~" ) << QString("~") << true << false << false << QString(); QTest::newRow( "..." ) << QString("...") << true << false << false << QString(); } void tst_QDom::invalidName() { QFETCH( QString, in_name ); QFETCH( bool, ok_AcceptInvalidChars ); QFETCH( bool, ok_DropInvalidChars ); QFETCH( bool, ok_ReturnNullNode ); QFETCH( QString, out_name ); QDomImplementation impl; QDomDocument doc; QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars); { QDomElement elt = doc.createElement(in_name); QDomElement elt_ns = doc.createElementNS("foo", "foo:" + in_name); QDomAttr attr = doc.createAttribute(in_name); QDomAttr attr_ns = doc.createAttributeNS("foo", "foo:" + in_name); QDomEntityReference ref = doc.createEntityReference(in_name); QCOMPARE(!elt.isNull(), ok_AcceptInvalidChars); QCOMPARE(!elt_ns.isNull(), ok_AcceptInvalidChars); QCOMPARE(!attr.isNull(), ok_AcceptInvalidChars); QCOMPARE(!attr_ns.isNull(), ok_AcceptInvalidChars); QCOMPARE(!ref.isNull(), ok_AcceptInvalidChars); if (ok_AcceptInvalidChars) { QCOMPARE(elt.tagName(), in_name); QCOMPARE(elt_ns.tagName(), in_name); QCOMPARE(attr.name(), in_name); QCOMPARE(attr_ns.name(), in_name); QCOMPARE(ref.nodeName(), in_name); } } QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars); { QDomElement elt = doc.createElement(in_name); QDomElement elt_ns = doc.createElementNS("foo", "foo:" + in_name); QDomAttr attr = doc.createAttribute(in_name); QDomAttr attr_ns = doc.createAttributeNS("foo", "foo:" + in_name); QDomEntityReference ref = doc.createEntityReference(in_name); QCOMPARE(!elt.isNull(), ok_DropInvalidChars); QCOMPARE(!elt_ns.isNull(), ok_DropInvalidChars); QCOMPARE(!attr.isNull(), ok_DropInvalidChars); QCOMPARE(!attr_ns.isNull(), ok_DropInvalidChars); QCOMPARE(!ref.isNull(), ok_DropInvalidChars); if (ok_DropInvalidChars) { QCOMPARE(elt.tagName(), out_name); QCOMPARE(elt_ns.tagName(), out_name); QCOMPARE(attr.name(), out_name); QCOMPARE(attr_ns.name(), out_name); QCOMPARE(ref.nodeName(), out_name); } } QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); { QDomElement elt = doc.createElement(in_name); QDomElement elt_ns = doc.createElementNS("foo", "foo:" + in_name); QDomAttr attr = doc.createAttribute(in_name); QDomAttr attr_ns = doc.createAttributeNS("foo", "foo:" + in_name); QDomEntityReference ref = doc.createEntityReference(in_name); QCOMPARE(!elt.isNull(), ok_ReturnNullNode); QCOMPARE(!elt_ns.isNull(), ok_ReturnNullNode); QCOMPARE(!attr.isNull(), ok_ReturnNullNode); QCOMPARE(!attr_ns.isNull(), ok_ReturnNullNode); QCOMPARE(!ref.isNull(), ok_ReturnNullNode); if (ok_ReturnNullNode) { QCOMPARE(elt.tagName(), in_name); QCOMPARE(elt_ns.tagName(), in_name); QCOMPARE(attr.name(), in_name); QCOMPARE(attr_ns.name(), in_name); QCOMPARE(ref.nodeName(), in_name); } } } void tst_QDom::invalidQualifiedName_data() { QTest::addColumn("in_name"); QTest::addColumn("ok_AcceptInvalidChars"); QTest::addColumn("ok_DropInvalidChars"); QTest::addColumn("ok_ReturnNullNode"); QTest::addColumn("out_name"); QTest::newRow( "foo" ) << QString("foo") << true << true << true << QString("foo"); QTest::newRow( "foo:bar" ) << QString("foo:bar") << true << true << true << QString("foo:bar"); QTest::newRow( "bar:" ) << QString("bar:") << false << false << false << QString(); QTest::newRow( ":" ) << QString(":") << false << false << false << QString(); QTest::newRow( "empty" ) << QString() << false << false << false << QString(); QTest::newRow("foo:...:.") << QString("foo:...:.")<< true << true << false << QString("foo::."); QTest::newRow("foo:~") << QString("foo:~") << true << false << false << QString(); QTest::newRow("foo:.~") << QString("foo:.~") << true << false << false << QString(); } void tst_QDom::invalidQualifiedName() { QFETCH( QString, in_name ); QFETCH( bool, ok_AcceptInvalidChars ); QFETCH( bool, ok_DropInvalidChars ); QFETCH( bool, ok_ReturnNullNode ); QFETCH( QString, out_name ); QDomImplementation impl; QDomDocument doc; QDomImplementation::setInvalidDataPolicy(QDomImplementation::AcceptInvalidChars); { QDomElement elt_ns = doc.createElementNS("foo", in_name); QDomAttr attr_ns = doc.createAttributeNS("foo", in_name); QDomDocumentType doctype = impl.createDocumentType(in_name, "foo", "bar"); QDomDocument doc2 = impl.createDocument("foo", in_name, doctype); QCOMPARE(!elt_ns.isNull(), ok_AcceptInvalidChars); QCOMPARE(!attr_ns.isNull(), ok_AcceptInvalidChars); QCOMPARE(!doctype.isNull(), ok_AcceptInvalidChars); QCOMPARE(!doc2.isNull(), ok_AcceptInvalidChars); if (ok_AcceptInvalidChars) { QCOMPARE(elt_ns.nodeName(), in_name); QCOMPARE(attr_ns.nodeName(), in_name); QCOMPARE(doctype.name(), in_name); QCOMPARE(doc2.documentElement().nodeName(), in_name); } } QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars); { QDomElement elt_ns = doc.createElementNS("foo", in_name); QDomAttr attr_ns = doc.createAttributeNS("foo", in_name); QDomDocumentType doctype = impl.createDocumentType(in_name, "foo", "bar"); QDomDocument doc2 = impl.createDocument("foo", in_name, doctype); QCOMPARE(!elt_ns.isNull(), ok_DropInvalidChars); QCOMPARE(!attr_ns.isNull(), ok_DropInvalidChars); QCOMPARE(!doctype.isNull(), ok_DropInvalidChars); QCOMPARE(!doc2.isNull(), ok_DropInvalidChars); if (ok_DropInvalidChars) { QCOMPARE(elt_ns.nodeName(), out_name); QCOMPARE(attr_ns.nodeName(), out_name); QCOMPARE(doctype.name(), out_name); QCOMPARE(doc2.documentElement().nodeName(), out_name); } } QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); { QDomElement elt_ns = doc.createElementNS("foo", in_name); QDomAttr attr_ns = doc.createAttributeNS("foo", in_name); QDomDocumentType doctype = impl.createDocumentType(in_name, "foo", "bar"); QDomDocument doc2 = impl.createDocument("foo", in_name, doctype); QCOMPARE(!elt_ns.isNull(), ok_ReturnNullNode); QCOMPARE(!attr_ns.isNull(), ok_ReturnNullNode); QCOMPARE(!doctype.isNull(), ok_ReturnNullNode); QCOMPARE(!doc2.isNull(), ok_ReturnNullNode); if (ok_ReturnNullNode) { QCOMPARE(elt_ns.nodeName(), in_name); QCOMPARE(attr_ns.nodeName(), in_name); QCOMPARE(doctype.name(), in_name); QCOMPARE(doc2.documentElement().nodeName(), in_name); } } } void tst_QDom::invalidCharData_data() { QTest::addColumn("in_text"); QTest::addColumn("ok_AcceptInvalidChars"); QTest::addColumn("ok_DropInvalidChars"); QTest::addColumn("ok_ReturnNullNode"); QTest::addColumn("out_text"); QTest::newRow( "foo" ) << QString("foo") << true << true << true << QString("foo"); QTest::newRow( "f\n"); QCOMPARE(QString::fromLatin1(serialized.constData()), QString::fromLatin1(expected.constData())); } void tst_QDom::normalizeEndOfLine() const { QByteArray input("\r\nc\rc\ra\na"); QBuffer buffer(&input); QVERIFY(buffer.open(QIODevice::ReadOnly)); QDomDocument doc; QVERIFY(doc.setContent(&buffer, true)); const QString expected(QLatin1String("\nc\nc\na\na")); // ### Qt 6: fix this, if we keep QDom at all QEXPECT_FAIL("", "The parser doesn't perform newline normalization. Fixing that would change behavior.", Continue); QCOMPARE(doc.documentElement().text(), expected); } void tst_QDom::normalizeAttributes() const { QByteArray data(""); QBuffer buffer(&data); QVERIFY(buffer.open(QIODevice::ReadOnly)); QDomDocument doc; QVERIFY(doc.setContent(&buffer, true)); // ### Qt 5: fix this, if we keep QDom at all QEXPECT_FAIL("", "The parser doesn't perform Attribute Value Normalization. Fixing that would change behavior.", Continue); QCOMPARE(doc.documentElement().attribute(QLatin1String("attribute")), QString::fromLatin1("a a")); } void tst_QDom::serializeWeirdEOL() const { QDomImplementation impl; QDomDocument doc(impl.createDocument("", "name", QDomDocumentType())); QDomElement ele(doc.documentElement()); ele.appendChild(doc.createTextNode(QLatin1String("\r\nasd\nasd\rasd\n"))); QByteArray output; QBuffer writeBuffer(&output); QVERIFY(writeBuffer.open(QIODevice::WriteOnly)); QTextStream stream(&writeBuffer); const QByteArray expected(" \nasd\nasd asd\n\n"); doc.save(stream, 0); QCOMPARE(QString::fromLatin1(output.constData()), QString::fromLatin1(expected.constData())); } void tst_QDom::reparentAttribute() const { QDomImplementation impl; QDomDocument doc(impl.createDocument("", "localName", QDomDocumentType())); QDomElement ele(doc.documentElement()); QDomAttr attr(doc.createAttribute("localName")); ele.setAttributeNode(attr); QVERIFY(attr.ownerElement() == ele); QVERIFY(attr.parentNode() == ele); } void tst_QDom::serializeNamespaces() const { const char *const input = "" "" ""; QByteArray ba(input); QBuffer buffer(&ba); QVERIFY(buffer.open(QIODevice::ReadOnly)); QXmlInputSource source(&buffer); QXmlSimpleReader reader; reader.setFeature("http://xml.org/sax/features/namespaces", true); reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false); QDomDocument doc; QVERIFY(doc.setContent(&source, &reader)); const QByteArray serialized(doc.toByteArray()); QDomDocument doc2; QVERIFY(doc2.setContent(doc.toString(), true)); /* Here we test that it roundtrips. */ QVERIFY(isDeepEqual(doc2, doc)); QDomDocument doc3; QVERIFY(doc3.setContent(QString::fromLatin1(serialized.constData()), true)); QVERIFY(isDeepEqual(doc3, doc)); } void tst_QDom::flagInvalidNamespaces() const { const char *const input = "" "" ""; QDomDocument doc; QVERIFY(!doc.setContent(QString::fromLatin1(input, true))); QEXPECT_FAIL("", "The parser doesn't flag identical qualified attribute names. Fixing this would change behavior.", Continue); QVERIFY(!doc.setContent(QString::fromLatin1(input))); } void tst_QDom::flagUndeclaredNamespace() const { /* Note, prefix 'a' is not declared. */ const char *const input = "" "" ""; QByteArray ba(input); QBuffer buffer(&ba); QVERIFY(buffer.open(QIODevice::ReadOnly)); QXmlInputSource source(&buffer); QXmlSimpleReader reader; reader.setFeature("http://xml.org/sax/features/namespaces", true); reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false); QDomDocument doc; QEXPECT_FAIL("", "The parser doesn't flag not declared prefixes. Fixing this would change behavior.", Continue); QVERIFY(!doc.setContent(&source, &reader)); } void tst_QDom::indentComments() const { /* We test that: * * - Whitespace is not added if a text node appears after a comment. * - Whitespace is not added if a text node appears before a comment. * - Indentation depth is linear with level depth. */ const char *const input = "" "" "" "textNode" "" "" "textNode2" ""; const char *const expected = "\n" " \n" " \n" " \n" " " "textNode" "\n" " " "textNode2" "\n"; QDomDocument doc; QVERIFY(doc.setContent(QString::fromLatin1(input))); const QString serialized(doc.toString(5)); QCOMPARE(serialized, QString::fromLatin1(expected)); } void tst_QDom::checkLiveness() const { QDomImplementation impl; QDomDocument doc(impl.createDocument(QString(), "doc", QDomDocumentType())); QDomElement ele(doc.documentElement()); const QDomElement e1(doc.createElement("name")); const QDomElement e2(doc.createElement("name")); const QDomText t1(doc.createTextNode("content")); ele.appendChild(e1); ele.appendChild(t1); ele.appendChild(e2); const QDomNodeList children(ele.childNodes()); QCOMPARE(children.count(), 3); ele.removeChild(e1); QCOMPARE(children.count(), 2); QCOMPARE(children.at(0), static_cast(t1)); QCOMPARE(children.at(1), static_cast(e2)); } void tst_QDom::reportDuplicateAttributes() const { QDomDocument dd; bool isSuccess = dd.setContent(QLatin1String("")); QEXPECT_FAIL("", "The parser doesn't flag duplicate attributes. Fixing this would change behavior.", Continue); QVERIFY2(!isSuccess, "Duplicate attributes are well-formedness errors, and should be reported as such."); } void tst_QDom::namespacedAttributes() const { static const char *const xml = "\n" "\n" " >>> SIMPLE BASIC OP - SEND - DUT AS SINK\n" "\n"; QDomDocument one("document"); QString error; bool docParsed = one.setContent(QByteArray(xml), true, &error); QVERIFY2(docParsed, qPrintable(error)); QDomDocument two("document2"); docParsed = two.setContent(one.toByteArray(2), true, &error); QVERIFY2(docParsed, qPrintable(error)); QVERIFY(isDeepEqual(one, two)); } void tst_QDom::appendChildFromToDocument() const { QDomDocument doc; const QByteArray input(""); doc.setContent(input); QDomDocument doc2(doc.documentElement().toDocument()); QDomElement element = doc2.createElement("name"); element.setAttribute("name", "value"); doc.documentElement().appendChild(element); } void tst_QDom::iterateCDATA() const { const QByteArray input(""); QDomDocument doc; QVERIFY(doc.setContent(input)); QCOMPARE(doc.toString(), QString("\n")); const QDomElement element(doc.documentElement()); QVERIFY(!element.isNull()); /* The node at element.childNodes().at(0) is not an element, * it's a CDATA section. */ const QDomElement child(element.childNodes().at(0).toElement()); QVERIFY(child.isNull()); QVERIFY(element.childNodes().at(0).isCDATASection()); } /*! \internal \since 4.4 \brief This function cannot be factored into appendDocumentNode(). The invocation of constructors/destructors is part of triggering the bug. */ QDomDocument tst_QDom::generateRequest() { QDomDocument doc; QDomElement elem = doc.createElement("test_elem"); elem.setAttribute("name", "value"); doc.appendChild(elem); return doc; } void tst_QDom::appendDocumentNode() const { QDomDocument doc; QDomDocument xml = generateRequest(); QDomElement elem = doc.createElement("document"); doc.appendChild(elem); QVERIFY(!xml.isNull()); const QString expected(QLatin1String("\n\n\n")); elem.appendChild(xml); QCOMPARE(doc.childNodes().count(), 1); QCOMPARE(doc.toString(0), expected); elem.appendChild(xml.firstChild()); QCOMPARE(doc.childNodes().count(), 1); QCOMPARE(doc.toString(0), expected); } static const QChar umlautName[] = { 'a', 0xfc, 'b' }; /*! \internal Write a german umlaut to a QByteArray, via a QTextStream. */ void tst_QDom::germanUmlautToByteArray() const { QCOMPARE(ulong(sizeof(umlautName) / sizeof(QChar)), ulong(3)); const QString name(umlautName, 3); QDomDocument d; d.appendChild(d.createElement(name)); QByteArray data; QBuffer buffer(&data); QVERIFY(buffer.open(QIODevice::WriteOnly)); QTextStream ts(&buffer); ts.setCodec("UTF-8"); ts << d.toString(); buffer.close(); QByteArray baseline("\n\n"); const QByteArray in(inFile.readAll()); /* Check that it was wwritten out correctly. */ QCOMPARE(in.length(), 34); QCOMPARE(in, baseline.toUtf8()); inFile.close(); /* Check that we read it in correctly with QDomDocument::setContent(). */ QVERIFY(inFile.open(QIODevice::ReadOnly)); QDomDocument dd; QVERIFY(dd.setContent(&inFile)); QCOMPARE(dd.toString(), baseline); } void tst_QDom::setInvalidDataPolicy() const { QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); QDomDocument doc; QDomElement elem = doc.createElement("invalid name"); QVERIFY(elem.isNull()); } void tst_QDom::crashInSetContent() const { QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); QDomDocument docImport; QCOMPARE(docImport.setContent(QLatin1String("text"), true), false); QVERIFY(docImport.setContent(QLatin1String(""))); } void tst_QDom::doubleNamespaceDeclarations() const { QDomDocument doc; QString testFile = QFINDTESTDATA("doubleNamespaces.xml"); if (testFile.isEmpty()) QFAIL("Cannot find test file doubleNamespaces.xml!"); QFile file(testFile); QVERIFY(file.open(QIODevice::ReadOnly)); QXmlSimpleReader reader; QXmlInputSource source(&file); QVERIFY(doc.setContent(&source, &reader)); // tst_QDom relies on a specific QHash ordering, see QTBUG-25071 QString docAsString = doc.toString(0); QVERIFY(docAsString == QString::fromLatin1("\n\n\n") || docAsString == QString::fromLatin1("\n\n\n") || docAsString == QString::fromLatin1("\n\n\n") || docAsString == QString::fromLatin1("\n\n\n") || docAsString == QString::fromLatin1("\n\n\n") || docAsString == QString::fromLatin1("\n\n\n") ); } void tst_QDom::setContentQXmlReaderOverload() const { QDomDocument doc; QXmlSimpleReader reader; QXmlInputSource data; data.setData(QByteArray("")); doc.setContent(&data, true); QCOMPARE(doc.documentElement().nodeName(), QString::fromLatin1("e")); } void tst_QDom::cleanupTestCase() const { QFile::remove("germanUmlautToFile.xml"); } void tst_QDom::toStringWithoutNewlines() const { QDomDocument doc; doc.setContent(QLatin1String("")); QCOMPARE(doc.toString(0), QString::fromLatin1("\n\n\n")); QCOMPARE(doc.toString(-1), QString::fromLatin1("")); } void tst_QDom::checkIntOverflow() const { /* This test takes a *very* long time to run, so it is at best a manual * test. */ return; /* QDom used an internal global int which overflowed. So iterate until an * uint wrapsaround. */ const QString xmlMessage(QLatin1String("")); bool hasWrapped = false; for(uint i = 1; i != 0; ++i) { /* We want to exit the second time, not loop infinitely. */ if(i == 1 && hasWrapped) break; else hasWrapped = true; QDomDocument doc; QVERIFY(doc.setContent(xmlMessage)); const QDomNodeList nl(doc.elementsByTagName(QLatin1String("test"))); QCOMPARE(nl.length(), 1); } } void tst_QDom::setContentWhitespace() const { QFETCH(QString, doc); QFETCH(bool, expectedValidity); QDomDocument domDoc; QCOMPARE(domDoc.setContent(doc), expectedValidity); if(expectedValidity) QCOMPARE(domDoc.documentElement().nodeName(), QString::fromLatin1("e")); } void tst_QDom::setContentWhitespace_data() const { QTest::addColumn("doc"); QTest::addColumn("expectedValidity"); QTest::newRow("data1") << QString::fromLatin1(" ") << true; QTest::newRow("data2") << QString::fromLatin1(" ") << true; QTest::newRow("data3") << QString::fromLatin1(" ") << true; QTest::newRow("data4") << QString::fromLatin1(" ") << true; QTest::newRow("data5") << QString::fromLatin1("\n") << true; QTest::newRow("data6") << QString::fromLatin1("\n\n") << true; QTest::newRow("data7") << QString::fromLatin1("\n\n\n") << true; QTest::newRow("data8") << QString::fromLatin1("\n\n\n\n") << true; QTest::newRow("data9") << QString::fromLatin1("\t") << true; QTest::newRow("data10") << QString::fromLatin1("\t\t") << true; QTest::newRow("data11") << QString::fromLatin1("\t\t\t") << true; QTest::newRow("data12") << QString::fromLatin1("\t\t\t\t") << true; /* With XML prolog. */ QTest::newRow("data13") << QString::fromLatin1("") << true; QTest::newRow("data14") << QString::fromLatin1(" ") << false; QTest::newRow("data15") << QString::fromLatin1(" ") << false; QTest::newRow("data16") << QString::fromLatin1(" ") << false; QTest::newRow("data17") << QString::fromLatin1(" ") << false; QTest::newRow("data18") << QString::fromLatin1("\n") << false; QTest::newRow("data19") << QString::fromLatin1("\n\n") << false; QTest::newRow("data20") << QString::fromLatin1("\n\n\n") << false; QTest::newRow("data21") << QString::fromLatin1("\n\n\n\n") << false; QTest::newRow("data22") << QString::fromLatin1("\t") << false; QTest::newRow("data23") << QString::fromLatin1("\t\t") << false; QTest::newRow("data24") << QString::fromLatin1("\t\t\t") << false; QTest::newRow("data25") << QString::fromLatin1("\t\t\t\t") << false; } void tst_QDom::taskQTBUG4595_dontAssertWhenDocumentSpecifiesUnknownEncoding() const { QString xmlWithUnknownEncoding("" "" " How will this sentence be handled?" ""); QDomDocument d; QVERIFY(d.setContent(xmlWithUnknownEncoding)); QString dontAssert = d.toString(); // this should not assert QVERIFY(true); } void tst_QDom::cloneDTD_QTBUG8398() const { QString dtd("\n" "\n" "\n" "]>\n" "\n"); QDomDocument domDocument; QVERIFY(domDocument.setContent(dtd)); QDomDocument domDocument2 = domDocument.cloneNode(true).toDocument(); // this string is relying on a specific QHash ordering, QTBUG-25071 QString expected("\n" "\n" "\n" "]>\n" "\n"); QString output; QTextStream stream(&output); domDocument2.save(stream, 0); // check against the original string and the expected one, QTBUG-25071 QVERIFY(output == dtd || output == expected); } void tst_QDom::DTDNotationDecl() { QString dtd("\n" "\n" "\n" "]>\n" "\n"); QDomDocument domDocument; QVERIFY(domDocument.setContent(dtd)); const QDomDocumentType doctype = domDocument.doctype(); QCOMPARE(doctype.notations().size(), 2); QVERIFY(doctype.namedItem(QString("gif")).isNotation()); QCOMPARE(doctype.namedItem(QString("gif")).toNotation().systemId(), QString("image/gif")); QVERIFY(doctype.namedItem(QString("jpeg")).isNotation()); QCOMPARE(doctype.namedItem(QString("jpeg")).toNotation().systemId(), QString("image/jpeg")); } void tst_QDom::DTDEntityDecl() { QString dtd("\n" "\n" "" "]>\n" "\n"); QDomDocument domDocument; QVERIFY(domDocument.setContent(dtd)); const QDomDocumentType doctype = domDocument.doctype(); QCOMPARE(doctype.entities().count(), 2); QVERIFY(doctype.namedItem(QString("secondFile")).isEntity()); QCOMPARE(doctype.namedItem(QString("secondFile")).toEntity().systemId(), QString("second.xml")); QCOMPARE(doctype.namedItem(QString("secondFile")).toEntity().notationName(), QString()); QVERIFY(doctype.namedItem(QString("logo")).isEntity()); QCOMPARE(doctype.namedItem(QString("logo")).toEntity().systemId(), QString("http://www.w3c.org/logo.gif")); QCOMPARE(doctype.namedItem(QString("logo")).toEntity().notationName(), QString("gif")); } QTEST_MAIN(tst_QDom) #include "tst_qdom.moc"