From e5fcad302d86d316390c6b0f62759a067313e8a9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 23 Mar 2009 10:18:55 +0100 Subject: Long live Qt 4.5! --- tests/auto/qdom/tst_qdom.cpp | 1897 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1897 insertions(+) create mode 100644 tests/auto/qdom/tst_qdom.cpp (limited to 'tests/auto/qdom/tst_qdom.cpp') diff --git a/tests/auto/qdom/tst_qdom.cpp b/tests/auto/qdom/tst_qdom.cpp new file mode 100644 index 0000000000..3e34aafde1 --- /dev/null +++ b/tests/auto/qdom/tst_qdom.cpp @@ -0,0 +1,1897 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//TESTED_CLASS= +//TESTED_FILES= + +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 cleanupTestCase() const; + +private: + static QDomDocument generateRequest(); + static QDomDocument doc(const QString &title, const QByteArray &ba); + 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_excludedCodecs; +}; + +Q_DECLARE_METATYPE(QList) + +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"); + + QTest::newRow( "01" ) << QString("testdata/toString_01/doc01.xml"); + QTest::newRow( "02" ) << QString("testdata/toString_01/doc02.xml"); + QTest::newRow( "03" ) << QString("testdata/toString_01/doc03.xml"); + QTest::newRow( "04" ) << QString("testdata/toString_01/doc04.xml"); + QTest::newRow( "05" ) << QString("testdata/toString_01/doc05.xml"); + + QTest::newRow( "euc-jp" ) << QString("testdata/toString_01/doc_euc-jp.xml"); + QTest::newRow( "iso-2022-jp" ) << QString("testdata/toString_01/doc_iso-2022-jp.xml"); + QTest::newRow( "little-endian" ) << QString("testdata/toString_01/doc_little-endian.xml"); + QTest::newRow( "utf-16" ) << QString("testdata/toString_01/doc_utf-16.xml"); + QTest::newRow( "utf-8" ) << QString("testdata/toString_01/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() +{ +#ifdef Q_CC_MINGW + QSKIP("Our current test machine, arsia, is too slow for this auto test.", SkipAll); +#endif + + QFile file("testdata/excludedCodecs.txt"); + QVERIFY(file.open(QIODevice::ReadOnly|QIODevice::Text)); + + QByteArray codecName; + + m_excludedCodecs = file.readAll().split('\n'); + +} + +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)); + + const QList codecs(QTextCodec::availableCodecs()); + QByteArray codecName; + + foreach(codecName, codecs) { + + /* Avoid codecs that can't handle the files we have. */ + if(m_excludedCodecs.contains(codecName.toLower())) + continue; + + /* 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, even though it was in QTextCodec::availableCodecs()") + .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"); + + QTest::newRow("doc01.xml") << QString("testdata/toString_01/doc01.xml"); + QTest::newRow("doc01.xml") << QString("testdata/toString_01/doc01.xml"); + QTest::newRow("doc02.xml") << QString("testdata/toString_01/doc02.xml"); + QTest::newRow("doc03.xml") << QString("testdata/toString_01/doc03.xml"); + QTest::newRow("doc04.xml") << QString("testdata/toString_01/doc04.xml"); + QTest::newRow("doc05.xml") << QString("testdata/toString_01/doc05.xml"); + + QTest::newRow("doc_euc-jp.xml") << QString("testdata/toString_01/doc_euc-jp.xml"); + QTest::newRow("doc_iso-2022-jp.xml") << QString("testdata/toString_01/doc_iso-2022-jp.xml"); + QTest::newRow("doc_little-endian.xml") << QString("testdata/toString_01/doc_little-endian.xml"); + QTest::newRow("doc_utf-16.xml") << QString("testdata/toString_01/doc_utf-16.xml"); + QTest::newRow("doc_utf-8.xml") << QString("testdata/toString_01/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 << (bool)FALSE; + QTest::newRow( "noDeep_02" ) << doc01 << nodeC1 << (bool)FALSE; + QTest::newRow( "noDeep_03" ) << doc01 << nodeC2 << (bool)FALSE; + + QTest::newRow( "deep_01" ) << doc01 << nodeB1 << (bool)TRUE; + QTest::newRow( "deep_02" ) << doc01 << nodeC1 << (bool)TRUE; + QTest::newRow( "deep_03" ) << doc01 << nodeC2 << (bool)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" ) << (bool)FALSE << (bool)FALSE << (bool)FALSE; + QTest::newRow( "001" ) << (bool)FALSE << (bool)FALSE << (bool)TRUE; + QTest::newRow( "010" ) << (bool)FALSE << (bool)TRUE << (bool)FALSE; + QTest::newRow( "011" ) << (bool)FALSE << (bool)TRUE << (bool)TRUE; + QTest::newRow( "100" ) << (bool)TRUE << (bool)FALSE << (bool)FALSE; + QTest::newRow( "101" ) << (bool)TRUE << (bool)FALSE << (bool)TRUE; + QTest::newRow( "110" ) << (bool)TRUE << (bool)TRUE << (bool)FALSE; + QTest::newRow( "111" ) << (bool)TRUE << (bool)TRUE << (bool)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" ) << (bool)FALSE << (bool)FALSE << (bool)FALSE; + QTest::newRow( "001" ) << (bool)FALSE << (bool)FALSE << (bool)TRUE; + QTest::newRow( "010" ) << (bool)FALSE << (bool)TRUE << (bool)FALSE; + QTest::newRow( "011" ) << (bool)FALSE << (bool)TRUE << (bool)TRUE; + QTest::newRow( "100" ) << (bool)TRUE << (bool)FALSE << (bool)FALSE; + QTest::newRow( "101" ) << (bool)TRUE << (bool)FALSE << (bool)TRUE; + QTest::newRow( "110" ) << (bool)TRUE << (bool)TRUE << (bool)FALSE; + QTest::newRow( "111" ) << (bool)TRUE << (bool)TRUE << (bool)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 5: 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."); +} + +QDomDocument tst_QDom::doc(const QString &title, const QByteArray &ba) +{ + QDomDocument doc(title); + const bool ret = doc.setContent(ba, true); + Q_ASSERT(ret); + return doc; +} + +void tst_QDom::namespacedAttributes() const +{ + static const char *const xml = + "\n" + "\n" + " >>> SIMPLE BASIC OP - SEND - DUT AS SINK\n" + "\n"; + + QDomDocument one = doc("document", xml); + QDomDocument two = doc("document2", one.toByteArray(2)); + + 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); + + Q_ASSERT(!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; + + QVERIFY(docImport.setContent(QLatin1String(""))); +} + +void tst_QDom::doubleNamespaceDeclarations() const +{ + QDomDocument doc; + + QFile file("doubleNamespaces.xml" ); + QVERIFY(file.open(QIODevice::ReadOnly)); + + QXmlSimpleReader reader; + + QXmlInputSource source(&file); + QVERIFY(doc.setContent(&source, &reader)); + + QVERIFY(doc.toString(0) == QString::fromLatin1("\n\n\n") || + doc.toString(0) == 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(), uint(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("") << QString::fromLatin1(" ") << true; + QTest::newRow("") << QString::fromLatin1(" ") << true; + QTest::newRow("") << QString::fromLatin1(" ") << true; + QTest::newRow("") << QString::fromLatin1(" ") << true; + QTest::newRow("") << QString::fromLatin1("\n") << true; + QTest::newRow("") << QString::fromLatin1("\n\n") << true; + QTest::newRow("") << QString::fromLatin1("\n\n\n") << true; + QTest::newRow("") << QString::fromLatin1("\n\n\n\n") << true; + QTest::newRow("") << QString::fromLatin1("\t") << true; + QTest::newRow("") << QString::fromLatin1("\t\t") << true; + QTest::newRow("") << QString::fromLatin1("\t\t\t") << true; + QTest::newRow("") << QString::fromLatin1("\t\t\t\t") << true; + + /* With XML prolog. */ + QTest::newRow("") << QString::fromLatin1("") << true; + + QTest::newRow("") << QString::fromLatin1(" ") << false; + QTest::newRow("") << QString::fromLatin1(" ") << false; + QTest::newRow("") << QString::fromLatin1(" ") << false; + QTest::newRow("") << QString::fromLatin1(" ") << false; + QTest::newRow("") << QString::fromLatin1("\n") << false; + QTest::newRow("") << QString::fromLatin1("\n\n") << false; + QTest::newRow("") << QString::fromLatin1("\n\n\n") << false; + QTest::newRow("") << QString::fromLatin1("\n\n\n\n") << false; + QTest::newRow("") << QString::fromLatin1("\t") << false; + QTest::newRow("") << QString::fromLatin1("\t\t") << false; + QTest::newRow("") << QString::fromLatin1("\t\t\t") << false; + QTest::newRow("") << QString::fromLatin1("\t\t\t\t") << false; +} + +QTEST_MAIN(tst_QDom) +#include "tst_qdom.moc" -- cgit v1.2.3