tests/auto/qxmlstream/tst_qxmlstream.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qxmlstream/tst_qxmlstream.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1518 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QDirIterator>
+#include <QEventLoop>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QtTest/QtTest>
+#include <QUrl>
+#include <QXmlDefaultHandler>
+#include <QXmlStreamReader>
+
+#include "qc14n.h"
+
+//TESTED_CLASS=QXmlStreamReader QXmlStreamWriter
+//TESTED_FILES=corelib/xml/stream/qxmlutils.cpp corelib/xml/stream/qxmlstream.cpp corelib/xml/stream/qxmlstream_p.h
+
+Q_DECLARE_METATYPE(QXmlStreamReader::ReadElementTextBehaviour)
+
+static const char *const catalogFile = "XML-Test-Suite/xmlconf/finalCatalog.xml";
+static const int expectedRunCount = 1646;
+static const int expectedSkipCount = 532;
+
+static inline int best(int a, int b)
+{
+    if (a < 0)
+        return b;
+    if (b < 0)
+        return a;
+    return qMin(a, b);
+}
+
+static inline int best(int a, int b, int c)
+{
+    if (a < 0)
+        return best(b, c);
+    if (b < 0)
+        return best(a, c);
+    if (c < 0)
+        return best(a, b);
+    return qMin(qMin(a, b), c);
+}
+
+/**
+ *  Opens @p filename and returns content produced as per
+ *  xmlconf/xmltest/canonxml.html.
+ *
+ *  @p docType is the DOCTYPE name that the returned output should
+ *  have, if it doesn't already have one.
+ */
+static QByteArray makeCanonical(const QString &filename,
+                                const QString &docType,
+                                bool &hasError,
+                                bool testIncremental = false)
+{
+    QFile file(filename);
+    file.open(QIODevice::ReadOnly);
+
+    QXmlStreamReader reader;
+
+    QByteArray buffer;
+    int bufferPos = 0;
+
+    if (testIncremental)
+        buffer = file.readAll();
+    else
+        reader.setDevice(&file);
+
+    QByteArray outarray;
+    QXmlStreamWriter writer(&outarray);
+
+    forever {
+        while (!reader.atEnd()) {
+            reader.readNext();
+            if (reader.isDTD()) {
+                if (!reader.notationDeclarations().isEmpty()) {
+                    QString dtd;
+                    QTextStream writeDtd(&dtd);
+
+                    writeDtd << "<!DOCTYPE ";
+                    writeDtd << docType;
+                    writeDtd << " [";
+                    writeDtd << endl;
+                    QMap<QString, QXmlStreamNotationDeclaration> sortedNotationDeclarations;
+                    foreach (QXmlStreamNotationDeclaration notation, reader.notationDeclarations())
+                        sortedNotationDeclarations.insert(notation.name().toString(), notation);
+                    foreach (QXmlStreamNotationDeclaration notation, sortedNotationDeclarations.values()) {
+                        writeDtd << "<!NOTATION ";
+                        writeDtd << notation.name().toString();
+                        if (notation.publicId().isEmpty()) {
+                            writeDtd << " SYSTEM \'";
+                            writeDtd << notation.systemId().toString();
+                            writeDtd << "\'";
+                        } else {
+                            writeDtd << " PUBLIC \'";
+                            writeDtd << notation.publicId().toString();
+                            writeDtd << "\'";
+                            if (!notation.systemId().isEmpty() ) {
+                                writeDtd << " \'";
+                                writeDtd << notation.systemId().toString();
+                                writeDtd << "\'";
+                            }
+                        }
+                        writeDtd << ">";
+                        writeDtd << endl;
+                    }
+
+                    writeDtd << "]>";
+                    writeDtd << endl;
+                    writer.writeDTD(dtd);
+                }
+            } else if (reader.isStartElement()) {
+                writer.writeStartElement(reader.namespaceUri().toString(), reader.name().toString());
+
+                QMap<QString, QXmlStreamAttribute> sortedAttributes;
+                foreach(QXmlStreamAttribute attribute, reader.attributes())
+                    sortedAttributes.insert(attribute.name().toString(), attribute);
+                foreach(QXmlStreamAttribute attribute, sortedAttributes.values())
+                    writer.writeAttribute(attribute);
+                writer.writeCharacters(QString()); // write empty string to avoid having empty xml tags
+            } else if (reader.isCharacters()) {
+                // make canonical
+
+                QString text = reader.text().toString();
+                int i = 0;
+                int p = 0;
+                while ((i = best(text.indexOf(QLatin1Char(10), p),
+                                 text.indexOf(QLatin1Char(13), p),
+                                 text.indexOf(QLatin1Char(9), p))) >= 0) {
+                    writer.writeCharacters(text.mid(p, i - p));
+                    writer.writeEntityReference(QString("#%1").arg(text.at(i).unicode()));
+                    p = i + 1;
+                }
+                writer.writeCharacters(text.mid(p));
+            } else if (reader.isStartDocument() || reader.isEndDocument() || reader.isComment()){
+                // canonical does not want any of those
+            } else if (reader.isProcessingInstruction() && reader.processingInstructionData().isEmpty()) {
+                // for some reason canonical wants a space
+                writer.writeProcessingInstruction(reader.processingInstructionTarget().toString(), QLatin1String(""));
+            } else if (!reader.hasError()){
+                writer.writeCurrentToken(reader);
+            }
+        }
+        if (testIncremental && bufferPos < buffer.size()) {
+            reader.addData(QByteArray(buffer.data() + (bufferPos++), 1));
+        } else {
+            break;
+        }
+    }
+
+    if (reader.hasError()) {
+        hasError = true;
+        outarray += "ERROR:";
+        outarray += reader.errorString().toLatin1();
+    }
+    else
+        hasError = false;
+
+    return outarray;
+}
+
+/**
+ * @short Returns the lexical QName of the document element in
+ * @p document.
+ *
+ * It is assumed that @p document is a well-formed XML document.
+ */
+static QString documentElement(const QByteArray &document)
+{
+    QXmlStreamReader reader(document);
+
+    while(!reader.atEnd())
+    {
+        if(reader.isStartElement())
+            return reader.qualifiedName().toString();
+
+        reader.readNext();
+    }
+
+    Q_ASSERT_X(false, Q_FUNC_INFO,
+               qPrintable(QString::fromLatin1("The input %1 didn't contain an element.").arg(QString::fromUtf8(document.constData()))));
+    return QString();
+}
+
+/**
+ * @short Loads W3C's XML conformance test suite and runs it on QXmlStreamReader.
+ *
+ * Since this suite is fairly large, it runs the tests sequentially in order to not
+ * have them all loaded into memory at once. In this way, the maximum memory usage stays
+ * low, which means one can run valgrind on this test. However, the drawback is that
+ * QTestLib's usual error reporting and testing mechanisms are slightly bypassed.
+ *
+ * Part of this code is a manual, ad-hoc implementation of xml:base.
+ *
+ * @see <a href="http://www.w3.org/XML/Test/">Extensible
+ * Markup Language (XML) Conformance Test Suites</a>
+ */
+class TestSuiteHandler : public QXmlDefaultHandler
+{
+public:
+    /**
+     * The first string is the test ID, the second is
+     * a description of what went wrong.
+     */
+    typedef QPair<QString, QString> GeneralFailure;
+
+    /**
+     * The string is the test ID.
+     */
+    QStringList successes;
+
+    /**
+     * The first value is the baseline, while the se
+     */
+    class MissedBaseline
+    {
+    public:
+        MissedBaseline(const QString &aId,
+                       const QByteArray &aExpected,
+                       const QByteArray &aOutput) : id(aId),
+                                                    expected(aExpected),
+                                                    output(aOutput)
+        {
+            Q_ASSERT(!aId.isEmpty());
+        }
+
+        QString     id;
+        QByteArray  expected;
+        QByteArray  output;
+    };
+
+    QList<GeneralFailure> failures;
+    QList<MissedBaseline> missedBaselines;
+
+    /**
+     * The count of how many tests that were run.
+     */
+    int runCount;
+
+    int skipCount;
+
+    /**
+     * @p baseURI is the the URI of where the catalog file resides.
+     */
+    TestSuiteHandler(const QUrl &baseURI) : runCount(0),
+                                            skipCount(0)
+    {
+        Q_ASSERT(baseURI.isValid());
+        m_baseURI.push(baseURI);
+    }
+
+    virtual bool characters(const QString &chars)
+    {
+        m_ch = chars;
+        return true;
+    }
+
+    virtual bool startElement(const QString &,
+                              const QString &,
+                              const QString &,
+                              const QXmlAttributes &atts)
+    {
+        m_atts.push(atts);
+        const int i = atts.index(QLatin1String("xml:base"));
+
+        if(i != -1)
+            m_baseURI.push(m_baseURI.top().resolved(atts.value(i)));
+
+        return true;
+    }
+
+    virtual bool endElement(const QString &,
+                            const QString &localName,
+                            const QString &)
+    {
+        if(localName == QLatin1String("TEST"))
+        {
+            /* We don't want tests for XML 1.1.0, in fact). */
+            if(m_atts.top().value(QString(), QLatin1String("VERSION")) == QLatin1String("1.1"))
+            {
+                ++skipCount;
+                m_atts.pop();
+                return true;
+            }
+
+            /* We don't want tests that conflict with the namespaces spec. Our parser is a
+             * namespace-aware parser. */
+            else if(m_atts.top().value(QString(), QLatin1String("NAMESPACE")) == QLatin1String("no"))
+            {
+                ++skipCount;
+                m_atts.pop();
+                return true;
+            }
+
+            const QString inputFilePath(m_baseURI.top().resolved(m_atts.top().value(QString(), QLatin1String("URI")))
+                                                                .toLocalFile());
+            const QString id(m_atts.top().value(QString(), QLatin1String("ID")));
+            const QString type(m_atts.top().value(QString(), QLatin1String("TYPE")));
+
+            QString expectedFilePath;
+            const int index = m_atts.top().index(QString(), QLatin1String("OUTPUT"));
+
+            //qDebug() << "Running test case:" << id;
+
+            if(index != -1)
+            {
+                expectedFilePath = m_baseURI.top().resolved(m_atts.top().value(QString(),
+                                                            QLatin1String("OUTPUT"))).toLocalFile();
+            }
+
+            /* testcases.dtd: 'No parser should accept a "not-wf" testcase
+             * unless it's a nonvalidating parser and the test contains
+             * external entities that the parser doesn't read.'
+             *
+             * We also let this apply to "valid", "invalid" and "error" tests, although
+             * I'm not fully sure this is correct. */
+            const QString ents(m_atts.top().value(QString(), QLatin1String("ENTITIES")));
+            m_atts.pop();
+
+            if(ents == QLatin1String("both") ||
+               ents == QLatin1String("general") ||
+               ents == QLatin1String("parameter"))
+            {
+                ++skipCount;
+                return true;
+            }
+
+            ++runCount;
+
+            QFile inputFile(inputFilePath);
+            if(!inputFile.open(QIODevice::ReadOnly))
+            {
+                failures.append(qMakePair(id, QString::fromLatin1("Failed to open input file %1").arg(inputFilePath)));
+                return true;
+            }
+
+            if(type == QLatin1String("not-wf"))
+            {
+                if(isWellformed(&inputFile, ParseSinglePass))
+                {
+                     failures.append(qMakePair(id, QString::fromLatin1("Failed to flag %1 as not well-formed.")
+                                                   .arg(inputFilePath)));
+
+                     /* Exit, the incremental test will fail as well, no need to flood the output. */
+                     return true;
+                }
+                else
+                    successes.append(id);
+
+                if(isWellformed(&inputFile, ParseIncrementally))
+                {
+                     failures.append(qMakePair(id, QString::fromLatin1("Failed to flag %1 as not well-formed with incremental parsing.")
+                                                   .arg(inputFilePath)));
+                }
+                else
+                    successes.append(id);
+
+                return true;
+            }
+
+            QXmlStreamReader reader(&inputFile);
+
+            /* See testcases.dtd which reads: 'Nonvalidating parsers
+             * must also accept "invalid" testcases, but validating ones must reject them.' */
+            if(type == QLatin1String("invalid") || type == QLatin1String("valid"))
+            {
+                QByteArray expected;
+                QString docType;
+
+                /* We only want to compare against a baseline when we have
+                 * one. Some "invalid"-tests, for instance, doesn't have baselines. */
+                if(!expectedFilePath.isEmpty())
+                {
+                    QFile expectedFile(expectedFilePath);
+
+                    if(!expectedFile.open(QIODevice::ReadOnly))
+                    {
+                        failures.append(qMakePair(id, QString::fromLatin1("Failed to open baseline %1").arg(expectedFilePath)));
+                        return true;
+                    }
+
+                    expected = expectedFile.readAll();
+                    docType = documentElement(expected);
+                }
+                else
+                    docType = QLatin1String("dummy");
+
+                bool hasError = true;
+                bool incremental = false;
+
+                QByteArray input(makeCanonical(inputFilePath, docType, hasError, incremental));
+
+                if (!hasError && !expectedFilePath.isEmpty() && input == expected)
+                    input = makeCanonical(inputFilePath, docType, hasError, (incremental = true));
+
+                if(hasError)
+                    failures.append(qMakePair(id, QString::fromLatin1("Failed to parse %1%2")
+                                              .arg(incremental?"(incremental run only) ":"")
+                                              .arg(inputFilePath)));
+
+                if(!expectedFilePath.isEmpty() && input != expected)
+                {
+                    missedBaselines.append(MissedBaseline(id, expected, input));
+                    return true;
+                }
+                else
+                {
+                    successes.append(id);
+                    return true;
+                }
+            }
+            else if(type == QLatin1String("error"))
+            {
+                /* Not yet sure about this one. */
+                // TODO
+                return true;
+            }
+            else
+            {
+                Q_ASSERT_X(false, Q_FUNC_INFO, "The input catalog is invalid.");
+                return false;
+            }
+        }
+        else if(localName == QLatin1String("TESTCASES") && m_atts.top().index(QLatin1String("xml:base")) != -1)
+            m_baseURI.pop();
+
+        m_atts.pop();
+
+        return true;
+    }
+
+    enum ParseMode
+    {
+        ParseIncrementally,
+        ParseSinglePass
+    };
+
+    static bool isWellformed(QIODevice *const inputFile, const ParseMode mode)
+    {
+        Q_ASSERT(inputFile);
+        Q_ASSERT_X(inputFile->isOpen(), Q_FUNC_INFO, "The caller is responsible for opening the device.");
+        Q_ASSERT(mode == ParseIncrementally || mode == ParseSinglePass);
+
+        if(mode == ParseIncrementally)
+        {
+            QXmlStreamReader reader;
+            QByteArray buffer;
+            int bufferPos = 0;
+
+            buffer = inputFile->readAll();
+
+            while(true)
+            {
+                while(!reader.atEnd())
+                    reader.readNext();
+
+                if(bufferPos < buffer.size())
+                {
+                    ++bufferPos;
+                    reader.addData(QByteArray(buffer.data() + bufferPos, 1));
+                }
+                else
+                    break;
+            }
+
+            return !reader.hasError();
+        }
+        else
+        {
+            QXmlStreamReader reader;
+            reader.setDevice(inputFile);
+
+            while(!reader.atEnd())
+                reader.readNext();
+
+            return !reader.hasError();
+        }
+    }
+
+private:
+    QStack<QXmlAttributes>  m_atts;
+    QString                 m_ch;
+    QStack<QUrl>            m_baseURI;
+};
+
+class tst_QXmlStream: public QObject
+{
+    Q_OBJECT
+public:
+    tst_QXmlStream() : m_handler(QUrl::fromLocalFile(QDir::currentPath()  + QLatin1Char('/'))
+                                .resolved(QUrl(QLatin1String(catalogFile))))
+    {
+    }
+
+private slots:
+    void initTestCase();
+    void reportFailures() const;
+    void reportFailures_data();
+    void checkBaseline() const;
+    void checkBaseline_data() const;
+    void testReader() const;
+    void testReader_data() const;
+    void reportSuccess() const;
+    void reportSuccess_data() const;
+    void parseXSLTTestSuite() const;
+    void writerHangs() const;
+    void writerAutoFormattingWithComments() const;
+    void writerAutoFormattingWithTabs() const;
+    void writerAutoEmptyTags() const;
+    void writeAttributesWithSpace() const;
+    void addExtraNamespaceDeclarations();
+    void setEntityResolver();
+    void readFromQBuffer() const;
+    void readFromQBufferInvalid() const;
+    void readNextStartElement() const;
+    void readElementText() const;
+    void readElementText_data() const;
+    void crashInUTF16Codec() const;
+    void hasAttributeSignature() const;
+    void hasAttribute() const;
+    void writeWithCodec() const;
+    void writeWithStandalone() const;
+    void entitiesAndWhitespace_1() const;
+    void entitiesAndWhitespace_2() const;
+    void testFalsePrematureError() const;
+    void garbageInXMLPrologDefaultCodec() const;
+    void garbageInXMLPrologUTF8Explicitly() const;
+    void clear() const;
+    void checkCommentIndentation() const;
+    void checkCommentIndentation_data() const;
+
+private:
+    static QByteArray readFile(const QString &filename);
+
+    TestSuiteHandler m_handler;
+};
+
+void tst_QXmlStream::initTestCase()
+{
+    QFile file(QString::fromLatin1(catalogFile));
+    QVERIFY2(file.open(QIODevice::ReadOnly),
+             qPrintable(QString::fromLatin1("Failed to open the test suite catalog; %1").arg(file.fileName())));
+
+    QXmlInputSource source(&file);
+    QXmlSimpleReader reader;
+    reader.setContentHandler(&m_handler);
+
+    QVERIFY(reader.parse(&source, false));
+}
+
+void tst_QXmlStream::reportFailures() const
+{
+    QFETCH(bool, isError);
+    QFETCH(QString, description);
+
+    QVERIFY2(!isError, qPrintable(description));
+}
+
+void tst_QXmlStream::reportFailures_data()
+{
+    const int len = m_handler.failures.count();
+
+    QTest::addColumn<bool>("isError");
+    QTest::addColumn<QString>("description");
+
+    /* We loop over all our failures(if any!), and output them such
+     * that they appear in the QTestLib log. */
+    for(int i = 0; i < len; ++i)
+        QTest::newRow(m_handler.failures.at(i).first.toLatin1().constData()) << true << m_handler.failures.at(i).second;
+
+    /* We need to add at least one column of test data, otherwise QTestLib complains. */
+    if(len == 0)
+        QTest::newRow("Whole test suite passed") << false << QString();
+
+    /* We compare the test case counts to ensure that we've actually run test cases, that
+     * the driver hasn't been broken or changed without updating the expected count, and
+     * similar reasons. */
+    QCOMPARE(m_handler.runCount, expectedRunCount);
+    QCOMPARE(m_handler.skipCount, expectedSkipCount);
+}
+
+void tst_QXmlStream::checkBaseline() const
+{
+    QFETCH(bool, isError);
+    QFETCH(QString, expected);
+    QFETCH(QString, output);
+
+    if(isError)
+        QCOMPARE(output, expected);
+}
+
+void tst_QXmlStream::checkBaseline_data() const
+{
+    QTest::addColumn<bool>("isError");
+    QTest::addColumn<QString>("expected");
+    QTest::addColumn<QString>("output");
+
+    const int len = m_handler.missedBaselines.count();
+
+    for(int i = 0; i < len; ++i)
+    {
+        const TestSuiteHandler::MissedBaseline &b = m_handler.missedBaselines.at(i);
+
+        /* We indeed don't know what encoding the content is in so in some cases fromUtf8
+         * is all wrong, but it's an acceptable guess for error reporting. */
+        QTest::newRow(b.id.toLatin1().constData())
+                << true
+                << QString::fromUtf8(b.expected.constData())
+                << QString::fromUtf8(b.output.constData());
+    }
+
+    if(len == 0)
+        QTest::newRow("dummy") << false << QString() << QString();
+}
+
+void tst_QXmlStream::reportSuccess() const
+{
+    QFETCH(bool, isError);
+
+    QVERIFY(!isError);
+}
+
+void tst_QXmlStream::reportSuccess_data() const
+{
+    QTest::addColumn<bool>("isError");
+
+    const int len = m_handler.successes.count();
+
+    for(int i = 0; i < len; ++i)
+        QTest::newRow(m_handler.successes.at(i).toLatin1().constData()) << false;
+
+    if(len == 0)
+        QTest::newRow("No test cases succeeded.") << true;
+}
+
+QByteArray tst_QXmlStream::readFile(const QString &filename)
+{
+    QFile file(filename);
+    file.open(QIODevice::ReadOnly);
+
+    QXmlStreamReader reader;
+
+    reader.setDevice(&file);
+    QByteArray outarray;
+    QTextStream writer(&outarray);
+	// We always want UTF-8, and not what the system picks up.
+	writer.setCodec("UTF-8");
+
+    while (!reader.atEnd()) {
+        reader.readNext();
+        writer << reader.tokenString() << "(";
+        if (reader.isWhitespace())
+            writer << " whitespace";
+        if (reader.isCDATA())
+            writer << " CDATA";
+        if (reader.isStartDocument() && reader.isStandaloneDocument())
+            writer << " standalone";
+        if (!reader.text().isEmpty())
+            writer << " text=\"" << reader.text().toString() << "\"";
+        if (!reader.processingInstructionTarget().isEmpty())
+            writer << " processingInstructionTarget=\"" << reader.processingInstructionTarget().toString() << "\"";
+        if (!reader.processingInstructionData().isEmpty())
+            writer << " processingInstructionData=\"" << reader.processingInstructionData().toString() << "\"";
+        if (!reader.dtdName().isEmpty())
+            writer << " dtdName=\"" << reader.dtdName().toString() << "\"";
+        if (!reader.dtdPublicId().isEmpty())
+            writer << " dtdPublicId=\"" << reader.dtdPublicId().toString() << "\"";
+        if (!reader.dtdSystemId().isEmpty())
+            writer << " dtdSystemId=\"" << reader.dtdSystemId().toString() << "\"";
+        if (!reader.documentVersion().isEmpty())
+            writer << " documentVersion=\"" << reader.documentVersion().toString() << "\"";
+        if (!reader.documentEncoding().isEmpty())
+            writer << " documentEncoding=\"" << reader.documentEncoding().toString() << "\"";
+        if (!reader.name().isEmpty())
+            writer << " name=\"" << reader.name().toString() << "\"";
+        if (!reader.namespaceUri().isEmpty())
+            writer << " namespaceUri=\"" << reader.namespaceUri().toString() << "\"";
+        if (!reader.qualifiedName().isEmpty())
+            writer << " qualifiedName=\"" << reader.qualifiedName().toString() << "\"";
+        if (!reader.prefix().isEmpty())
+            writer << " prefix=\"" << reader.prefix().toString() << "\"";
+        if (reader.attributes().size()) {
+            foreach(QXmlStreamAttribute attribute, reader.attributes()) {
+                writer << endl << "    Attribute(";
+                if (!attribute.name().isEmpty())
+                    writer << " name=\"" << attribute.name().toString() << "\"";
+                if (!attribute.namespaceUri().isEmpty())
+                    writer << " namespaceUri=\"" << attribute.namespaceUri().toString() << "\"";
+                if (!attribute.qualifiedName().isEmpty())
+                    writer << " qualifiedName=\"" << attribute.qualifiedName().toString() << "\"";
+                if (!attribute.prefix().isEmpty())
+                    writer << " prefix=\"" << attribute.prefix().toString() << "\"";
+                if (!attribute.value().isEmpty())
+                    writer << " value=\"" << attribute.value().toString() << "\"";
+                writer << " )" << endl;
+            }
+        }
+        if (reader.namespaceDeclarations().size()) {
+            foreach(QXmlStreamNamespaceDeclaration namespaceDeclaration, reader.namespaceDeclarations()) {
+                writer << endl << "    NamespaceDeclaration(";
+                if (!namespaceDeclaration.prefix().isEmpty())
+                    writer << " prefix=\"" << namespaceDeclaration.prefix().toString() << "\"";
+                if (!namespaceDeclaration.namespaceUri().isEmpty())
+                    writer << " namespaceUri=\"" << namespaceDeclaration.namespaceUri().toString() << "\"";
+                writer << " )" << endl;
+            }
+        }
+        if (reader.notationDeclarations().size()) {
+            foreach(QXmlStreamNotationDeclaration notationDeclaration, reader.notationDeclarations()) {
+                writer << endl << "    NotationDeclaration(";
+                if (!notationDeclaration.name().isEmpty())
+                    writer << " name=\"" << notationDeclaration.name().toString() << "\"";
+                if (!notationDeclaration.systemId().isEmpty())
+                    writer << " systemId=\"" << notationDeclaration.systemId().toString() << "\"";
+                if (!notationDeclaration.publicId().isEmpty())
+                    writer << " publicId=\"" << notationDeclaration.publicId().toString() << "\"";
+                writer << " )" << endl;
+            }
+        }
+        if (reader.entityDeclarations().size()) {
+            foreach(QXmlStreamEntityDeclaration entityDeclaration, reader.entityDeclarations()) {
+                writer << endl << "    EntityDeclaration(";
+                if (!entityDeclaration.name().isEmpty())
+                    writer << " name=\"" << entityDeclaration.name().toString() << "\"";
+                if (!entityDeclaration.notationName().isEmpty())
+                    writer << " notationName=\"" << entityDeclaration.notationName().toString() << "\"";
+                if (!entityDeclaration.systemId().isEmpty())
+                    writer << " systemId=\"" << entityDeclaration.systemId().toString() << "\"";
+                if (!entityDeclaration.publicId().isEmpty())
+                    writer << " publicId=\"" << entityDeclaration.publicId().toString() << "\"";
+                if (!entityDeclaration.value().isEmpty())
+                    writer << " value=\"" << entityDeclaration.value().toString() << "\"";
+                writer << " )" << endl;
+            }
+        }
+        writer << " )" << endl;
+    }
+    if (reader.hasError())
+        writer << "ERROR: " << reader.errorString() << endl;
+    return outarray;
+}
+
+void tst_QXmlStream::testReader() const
+{
+    QFETCH(QString, xml);
+    QFETCH(QString, ref);
+    QFile file(ref);
+    if (!file.exists()) {
+        QByteArray reference = readFile(xml);
+        QVERIFY(file.open(QIODevice::WriteOnly));
+        file.write(reference);
+        file.close();
+    } else {
+        QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
+        QString reference = QString::fromUtf8(file.readAll());
+        QString qxmlstream = QString::fromUtf8(readFile(xml));
+        QCOMPARE(qxmlstream, reference);
+    }
+}
+
+void tst_QXmlStream::testReader_data() const
+{
+    QTest::addColumn<QString>("xml");
+    QTest::addColumn<QString>("ref");
+    QDir dir;
+    dir.cd("data/");
+    foreach(QString filename , dir.entryList(QStringList() << "*.xml")) {
+        QString reference =  QFileInfo(filename).baseName() + ".ref";
+        QTest::newRow(dir.filePath(filename).toLatin1().data()) << dir.filePath(filename) << dir.filePath(reference);
+    }
+}
+
+void tst_QXmlStream::parseXSLTTestSuite() const
+{
+    /* We disable this test for now, so it doesn't show up as an XFAIL. */
+#if 0
+    QEXPECT_FAIL("", "Two problems needs to be solved in order to enable this test: \n"
+                     "* The XSLT suite is 69 MB large, which is quite a lot compared to the existing XML suite on 2 mb.\n"
+                     "* We need a c14n-like implementation in order to compare the outputs.", Abort);
+    QVERIFY(false);
+
+    /* We don't yet know this. TODO */
+    int xsltExpectedRunCount = -1;
+
+    QStringList nameFilters;
+    nameFilters.append("*.xsl");
+    nameFilters.append("*.xml");
+
+    QDirIterator dirIterator("XSLT-Test-Suite/", nameFilters,
+                             QDir::AllEntries, QDirIterator::Subdirectories);
+
+    int filesParsed = 0;
+
+    while(dirIterator.hasNext())
+    {
+        dirIterator.next();
+
+        const QString fp(dirIterator.filePath());
+        qDebug() << "Found" << fp;
+
+        QFile inputFile(fp);
+        QVERIFY(inputFile.open(QIODevice::ReadOnly));
+
+        /* Read in and write out to the QByteArray. */
+        QByteArray outputArray;
+        {
+            QXmlStreamReader reader(&inputFile);
+
+            QXmlStreamWriter writer(&outputArray);
+
+            while(!reader.atEnd())
+            {
+                writer.writeCurrentToken(reader);
+                reader.readNext();
+
+                QVERIFY2(!reader.hasError(), qPrintable(reader.errorString()));
+            }
+            /* Might be we got an error here, but we don't care. */
+        }
+
+        /* Read in the two files, and compare them. */
+        {
+            QBuffer outputBuffer(&outputArray);
+            outputBuffer.open(QIODevice::ReadOnly);
+            inputFile.close();
+            inputFile.open(QIODevice::ReadOnly);
+
+            QString message;
+            const bool isEqual = QC14N::isEqual(&inputFile, &outputBuffer, &message);
+
+            QVERIFY2(isEqual, message.toLatin1().constData());
+
+            ++filesParsed;
+        }
+    }
+
+    QCOMPARE(xsltExpectedRunCount, filesParsed);
+#endif
+}
+
+void tst_QXmlStream::addExtraNamespaceDeclarations()
+{
+    const char *data = "<bla><undeclared:foo/><undeclared_too:foo/></bla>";
+    {
+        QXmlStreamReader xml(data);
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY2(xml.hasError(), "namespaces undeclared");
+    }
+    {
+        QXmlStreamReader xml(data);
+        xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("undeclared", "blabla"));
+        xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("undeclared_too", "foofoo"));
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY2(!xml.hasError(), xml.errorString().toLatin1().constData());
+    }
+}
+
+
+class EntityResolver : public QXmlStreamEntityResolver {
+public:
+    QString resolveUndeclaredEntity(const QString &name) {
+        static int count = 0;
+        return name.toUpper() + QString::number(++count);
+    }
+};
+void tst_QXmlStream::setEntityResolver()
+{
+    const char *data = "<bla foo=\"&undeclared;\">&undeclared_too;</bla>";
+    {
+        QXmlStreamReader xml(data);
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY2(xml.hasError(), "undeclared entities");
+    }
+    {
+        QString foo;
+        QString bla_text;
+        QXmlStreamReader xml(data);
+        EntityResolver resolver;
+        xml.setEntityResolver(&resolver);
+        while (!xml.atEnd()) {
+            xml.readNext();
+            if (xml.isStartElement())
+                foo = xml.attributes().value("foo").toString();
+            if (xml.isCharacters())
+                bla_text += xml.text().toString();
+        }
+        QVERIFY2(!xml.hasError(), xml.errorString().toLatin1().constData());
+        QCOMPARE(foo, QLatin1String("UNDECLARED1"));
+        QCOMPARE(bla_text, QLatin1String("UNDECLARED_TOO2"));
+    }
+}
+
+void tst_QXmlStream::testFalsePrematureError() const // task 179320
+{
+    const char *illegal_start = "illegal<sta";
+    const char *legal_start = "<sta";
+    const char* end = "rt/>";
+    {
+        QXmlStreamReader xml("");
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY(xml.error() == QXmlStreamReader::PrematureEndOfDocumentError);
+        QCOMPARE(xml.errorString(), QLatin1String("Premature end of document."));
+        xml.addData(legal_start);
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY(xml.error() == QXmlStreamReader::PrematureEndOfDocumentError);
+        QCOMPARE(xml.errorString(), QLatin1String("Premature end of document."));
+        xml.addData(end);
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY(!xml.hasError());
+    }
+    {
+        QXmlStreamReader xml(illegal_start);
+        while (!xml.atEnd()) {
+            xml.readNext();
+        }
+        QVERIFY(xml.hasError());
+        QCOMPARE(xml.errorString(), QLatin1String("Start tag expected."));
+        QVERIFY(xml.error() == QXmlStreamReader::NotWellFormedError);
+    }
+}
+
+/*!
+ See task 188737. Crash due to using empty QStack.
+ */
+void tst_QXmlStream::writerHangs() const
+{
+    QFile file("test.xml");
+
+    QVERIFY(file.open(QIODevice::WriteOnly));
+
+    QXmlStreamWriter  writer(&file);
+    double radius = 4.0;
+    writer.setAutoFormatting(true);
+    writer.writeStartDocument();
+    writer.writeEmptyElement("circle");
+    writer.writeAttribute("radius", QString::number(radius));
+    writer.writeEndElement();
+    writer.writeEndDocument();
+}
+/*!
+  Task 189611
+*/
+void tst_QXmlStream::writerAutoFormattingWithComments() const
+{
+    QBuffer buffer;
+    buffer.open(QIODevice::WriteOnly);
+
+    QXmlStreamWriter writer(&buffer);
+    writer.setAutoFormatting(true);
+    writer.writeStartDocument();
+    writer.writeComment("This is a comment");
+    writer.writeEndDocument();
+    const char *str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This is a comment-->\n";
+    QCOMPARE(buffer.buffer().data(), str);
+}
+
+
+/*!
+  Task 206782
+*/
+void tst_QXmlStream::writerAutoFormattingWithTabs() const
+{
+    QBuffer buffer;
+    buffer.open(QIODevice::WriteOnly);
+
+
+    QXmlStreamWriter writer(&buffer);
+    writer.setAutoFormatting(true);
+    writer.setAutoFormattingIndent(-1);
+    QCOMPARE(writer.autoFormattingIndent(), -1);
+    writer.writeStartDocument();
+    writer.writeStartElement("A");
+    writer.writeStartElement("B");
+    writer.writeEndDocument();
+    const char *str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<A>\n\t<B/>\n</A>\n";
+    QCOMPARE(buffer.buffer().data(), str);
+}
+
+/*!
+  Task 204822
+*/
+void tst_QXmlStream::writeAttributesWithSpace() const
+{
+    QBuffer buffer;
+    buffer.open(QIODevice::WriteOnly);
+
+
+    QXmlStreamWriter writer(&buffer);
+    writer.writeStartDocument();
+    writer.writeEmptyElement("A");
+    writer.writeAttribute("attribute", QString("value")+QChar::Nbsp);
+    writer.writeEndDocument();
+    QString s = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><A attribute=\"value%1\"/>\n").arg(QChar(QChar::Nbsp));
+    QCOMPARE(buffer.buffer().data(), s.toUtf8().data());
+}
+
+/*!
+  Task 209340
+*/
+void tst_QXmlStream::writerAutoEmptyTags() const
+{
+    QBuffer buffer;
+    buffer.open(QIODevice::WriteOnly);
+
+
+    QXmlStreamWriter writer(&buffer);
+
+    writer.writeStartDocument();
+
+    writer.writeStartElement("Hans");
+    writer.writeAttribute("key", "value");
+    writer.writeEndElement();
+
+    writer.writeStartElement("Hans");
+    writer.writeAttribute("key", "value");
+    writer.writeEmptyElement("Leer");
+    writer.writeAttribute("key", "value");
+    writer.writeEndElement();
+
+    writer.writeStartElement("Hans");
+    writer.writeAttribute("key", "value");
+    writer.writeCharacters("stuff");
+    writer.writeEndElement();
+
+    writer.writeEndDocument();
+
+    QString s = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Hans key=\"value\"/><Hans key=\"value\"><Leer key=\"value\"/></Hans><Hans key=\"value\">stuff</Hans>\n");
+    QCOMPARE(buffer.buffer().data(), s.toUtf8().data());
+}
+
+void tst_QXmlStream::readFromQBuffer() const
+{
+    QByteArray in("<e/>");
+    QBuffer buffer(&in);
+    QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+    QXmlStreamReader reader(&buffer);
+
+    while(!reader.atEnd())
+    {
+        reader.readNext();
+    }
+
+    QVERIFY(!reader.hasError());
+}
+
+void tst_QXmlStream::readFromQBufferInvalid() const
+{
+    QByteArray in("<e/><e/>");
+    QBuffer buffer(&in);
+    QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+    QXmlStreamReader reader(&buffer);
+
+    while(!reader.atEnd())
+    {
+        reader.readNext();
+    }
+
+    QVERIFY(reader.hasError());
+}
+
+void tst_QXmlStream::readNextStartElement() const
+{
+    QLatin1String in("<?xml version=\"1.0\"?><A><!-- blah --><B><C/></B><B attr=\"value\"/>text</A>");
+    QXmlStreamReader reader(in);
+
+    QVERIFY(reader.readNextStartElement());
+    QVERIFY(reader.isStartElement() && reader.name() == "A");
+
+    int amountOfB = 0;
+    while (reader.readNextStartElement()) {
+        QVERIFY(reader.isStartElement() && reader.name() == "B");
+        ++amountOfB;
+        reader.skipCurrentElement();
+    }
+
+    QCOMPARE(amountOfB, 2);
+}
+
+void tst_QXmlStream::readElementText() const
+{
+    QFETCH(QXmlStreamReader::ReadElementTextBehaviour, behaviour);
+    QFETCH(QString, input);
+    QFETCH(QString, expected);
+
+    QXmlStreamReader reader(input);
+
+    QVERIFY(reader.readNextStartElement());
+    QCOMPARE(reader.readElementText(behaviour), expected);
+}
+
+void tst_QXmlStream::readElementText_data() const
+{
+    QTest::addColumn<QXmlStreamReader::ReadElementTextBehaviour>("behaviour");
+    QTest::addColumn<QString>("input");
+    QTest::addColumn<QString>("expected");
+
+    QString validInput("<p>He was <em>never</em> going to admit<!-- TODO: rephrase --> his mistake.</p>");
+    QString invalidInput("<p>invalid...<p>");
+    QString invalidOutput("invalid...");
+
+    QTest::newRow("ErrorOnUnexpectedElement")
+            << QXmlStreamReader::ErrorOnUnexpectedElement
+            << validInput << QString("He was ");
+
+    QTest::newRow("IncludeChildElements")
+            << QXmlStreamReader::IncludeChildElements
+            << validInput << QString("He was never going to admit his mistake.");
+
+    QTest::newRow("SkipChildElements")
+            << QXmlStreamReader::SkipChildElements
+            << validInput << QString("He was  going to admit his mistake.");
+
+    QTest::newRow("ErrorOnUnexpectedElement Invalid")
+            << QXmlStreamReader::ErrorOnUnexpectedElement
+            << invalidInput << invalidOutput;
+
+    QTest::newRow("IncludeChildElements Invalid")
+            << QXmlStreamReader::IncludeChildElements
+            << invalidInput << invalidOutput;
+
+    QTest::newRow("SkipChildElements Invalid")
+            << QXmlStreamReader::SkipChildElements
+            << invalidInput << invalidOutput;
+}
+
+void tst_QXmlStream::crashInUTF16Codec() const
+{
+    QEventLoop eventLoop;
+
+    QNetworkAccessManager networkManager;
+    QNetworkRequest request(QUrl::fromLocalFile(QLatin1String("data/051reduced.xml")));
+    QNetworkReply *const reply = networkManager.get(request);
+    eventLoop.connect(reply, SIGNAL(finished()), SLOT(quit()));
+
+    QCOMPARE(eventLoop.exec(), 0);
+
+    QXmlStreamReader reader(reply);
+    while(!reader.atEnd())
+    {
+        reader.readNext();
+        continue;
+    }
+
+    QVERIFY(!reader.hasError());
+}
+
+/*
+  In addition to QTestLib's flags, one can specify "-c <filename>" and have that file output in its canonical form.
+*/
+int main(int argc, char *argv[])
+{
+    QCoreApplication app(argc, argv);
+
+    if (argc == 3 && QByteArray(argv[1]).startsWith("-c")) {
+        // output canonical only
+        bool error = false;
+        QByteArray canonical = makeCanonical(argv[2], "doc", error);
+        QTextStream myStdOut(stdout);
+        myStdOut << canonical << endl;
+        exit(0);
+    }
+
+    tst_QXmlStream tc;
+    return QTest::qExec(&tc, argc, argv);
+}
+
+void tst_QXmlStream::hasAttributeSignature() const
+{
+    /* These functions should be const so invoke all
+     * of them on a const object. */
+    const QXmlStreamAttributes atts;
+    atts.hasAttribute(QLatin1String("localName"));
+    atts.hasAttribute(QString::fromLatin1("localName"));
+    atts.hasAttribute(QString::fromLatin1("http://example.com/"), QLatin1String("localName"));
+
+    /* The input arguments should be const references, not mutable references
+     * so pass const references. */
+    const QLatin1String latin1StringLocalName(QLatin1String("localName"));
+    const QString qStringLocalname(QLatin1String("localName"));
+    const QString namespaceURI(QLatin1String("http://example.com/"));
+
+    /* QLatin1String overload. */
+    atts.hasAttribute(latin1StringLocalName);
+
+    /* QString overload. */
+    atts.hasAttribute(latin1StringLocalName);
+
+    /* namespace/local name overload. */
+    atts.hasAttribute(namespaceURI, qStringLocalname);
+}
+
+void tst_QXmlStream::hasAttribute() const
+{
+    QXmlStreamReader reader(QLatin1String("<e xmlns:p='http://example.com/2' xmlns='http://example.com/' "
+                                          "attr1='value' attr2='value2' p:attr3='value3' emptyAttr=''><noAttributes/></e>"));
+
+    QCOMPARE(reader.readNext(), QXmlStreamReader::StartDocument);
+    QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement);
+    const QXmlStreamAttributes &atts = reader.attributes();
+
+    /* QLatin1String overload. */
+    QVERIFY(atts.hasAttribute(QLatin1String("attr1")));
+    QVERIFY(atts.hasAttribute(QLatin1String("attr2")));
+    QVERIFY(atts.hasAttribute(QLatin1String("p:attr3")));
+    QVERIFY(atts.hasAttribute(QLatin1String("emptyAttr")));
+    QVERIFY(!atts.hasAttribute(QLatin1String("DOESNOTEXIST")));
+
+    /* Test with an empty & null namespaces. */
+    QVERIFY(atts.hasAttribute(QString(), QLatin1String("attr2"))); /* A null string. */
+    QVERIFY(atts.hasAttribute(QLatin1String(""), QLatin1String("attr2"))); /* An empty string. */
+
+    /* QString overload. */
+    QVERIFY(atts.hasAttribute(QString::fromLatin1("attr1")));
+    QVERIFY(atts.hasAttribute(QString::fromLatin1("attr2")));
+    QVERIFY(atts.hasAttribute(QString::fromLatin1("p:attr3")));
+    QVERIFY(atts.hasAttribute(QString::fromLatin1("emptyAttr")));
+    QVERIFY(!atts.hasAttribute(QString::fromLatin1("DOESNOTEXIST")));
+
+    /* namespace/local name overload. */
+    QVERIFY(atts.hasAttribute(QString(), QString::fromLatin1("attr1")));
+    /* Attributes do not pick up the default namespace. */
+    QVERIFY(!atts.hasAttribute(QLatin1String("http://example.com/"), QString::fromLatin1("attr1")));
+    QVERIFY(atts.hasAttribute(QLatin1String("http://example.com/2"), QString::fromLatin1("attr3")));
+    QVERIFY(atts.hasAttribute(QString(), QString::fromLatin1("emptyAttr")));
+    QVERIFY(!atts.hasAttribute(QLatin1String("http://example.com/2"), QString::fromLatin1("DOESNOTEXIST")));
+    QVERIFY(!atts.hasAttribute(QLatin1String("WRONG_NAMESPACE"), QString::fromLatin1("attr3")));
+
+    /* Invoke on an QXmlStreamAttributes that has no attributes at all. */
+    QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement);
+
+    const QXmlStreamAttributes &atts2 = reader.attributes();
+    QVERIFY(atts2.isEmpty());
+
+    /* QLatin1String overload. */
+    QVERIFY(!atts.hasAttribute(QLatin1String("arbitraryName")));
+
+    /* QString overload. */
+    QVERIFY(!atts.hasAttribute(QString::fromLatin1("arbitraryName")));
+
+    /* namespace/local name overload. */
+    QVERIFY(!atts.hasAttribute(QLatin1String("http://example.com/"), QString::fromLatin1("arbitraryName")));
+
+    while(!reader.atEnd())
+        reader.readNext();
+
+    QVERIFY(!reader.hasError());
+}
+
+
+void tst_QXmlStream::writeWithCodec() const
+{
+
+    QByteArray outarray;
+    QXmlStreamWriter writer(&outarray);
+    writer.setAutoFormatting(true);
+
+    QTextCodec *codec = QTextCodec::codecForName("ISO 8859-15");
+    QVERIFY(codec);
+    writer.setCodec(codec);
+
+    const char *latin2 = "hé hé";
+    const QString string = codec->toUnicode(latin2);
+
+
+    writer.writeStartDocument("1.0");
+
+    writer.writeTextElement("foo", string);
+    writer.writeEndElement();
+    writer.writeEndDocument();
+
+    QVERIFY(outarray.contains(latin2));
+    QVERIFY(outarray.contains(codec->name()));
+}
+
+void tst_QXmlStream::writeWithStandalone() const
+{
+    {
+        QByteArray outarray;
+        QXmlStreamWriter writer(&outarray);
+        writer.setAutoFormatting(true);
+        writer.writeStartDocument("1.0", true);
+        writer.writeEndDocument();
+        const char *ref = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
+        QCOMPARE(outarray.constData(), ref);
+    }
+    {
+        QByteArray outarray;
+        QXmlStreamWriter writer(&outarray);
+        writer.setAutoFormatting(true);
+        writer.writeStartDocument("1.0", false);
+        writer.writeEndDocument();
+        const char *ref = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
+        QCOMPARE(outarray.constData(), ref);
+    }
+}
+
+void tst_QXmlStream::entitiesAndWhitespace_1() const
+{
+    QXmlStreamReader reader(QLatin1String("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"><test>&extEnt;</test>"));
+
+    int entityCount = 0;
+    int characterCount = 0;
+    while(!reader.atEnd())
+    {
+        QXmlStreamReader::TokenType token = reader.readNext();
+        switch(token)
+        {
+            case QXmlStreamReader::Characters:
+                characterCount++;
+                break;
+            case QXmlStreamReader::EntityReference:
+                entityCount++;
+                break;
+            default:
+                ;
+        }
+    }
+
+    QCOMPARE(entityCount, 1);
+    QCOMPARE(characterCount, 0);
+    QVERIFY(!reader.hasError());
+}
+
+void tst_QXmlStream::entitiesAndWhitespace_2() const
+{
+    QXmlStreamReader reader(QLatin1String("<test>&extEnt;</test>"));
+
+    int entityCount = 0;
+    int characterCount = 0;
+    while(!reader.atEnd())
+    {
+        QXmlStreamReader::TokenType token = reader.readNext();
+        switch(token)
+        {
+            case QXmlStreamReader::Characters:
+                characterCount++;
+                break;
+            case QXmlStreamReader::EntityReference:
+                entityCount++;
+                break;
+            default:
+                ;
+        }
+    }
+
+    QCOMPARE(entityCount, 0);
+    QCOMPARE(characterCount, 0);
+    QVERIFY(reader.hasError());
+}
+
+void tst_QXmlStream::garbageInXMLPrologDefaultCodec() const
+{
+    QBuffer out;
+    QVERIFY(out.open(QIODevice::ReadWrite));
+
+    QXmlStreamWriter writer (&out);
+    writer.writeStartDocument();
+    writer.writeEmptyElement("Foo");
+    writer.writeEndDocument();
+
+    QCOMPARE(out.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Foo/>\n"));
+}
+
+void tst_QXmlStream::garbageInXMLPrologUTF8Explicitly() const
+{
+    QBuffer out;
+    QVERIFY(out.open(QIODevice::ReadWrite));
+
+    QXmlStreamWriter writer (&out);
+    writer.setCodec("UTF-8");
+    writer.writeStartDocument();
+    writer.writeEmptyElement("Foo");
+    writer.writeEndDocument();
+
+    QCOMPARE(out.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Foo/>\n"));
+}
+
+void tst_QXmlStream::clear() const // task 228768
+{
+    QString xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><body></body>";
+    QXmlStreamReader reader;
+
+    reader.addData(xml);
+    while (!reader.atEnd()) {
+        reader.readNext();
+    }
+    QCOMPARE(reader.tokenType(), QXmlStreamReader::EndDocument);
+
+    reader.clear();
+    reader.addData(xml);
+    while (!reader.atEnd()) {
+        reader.readNext();
+    }
+    QCOMPARE(reader.tokenType(), QXmlStreamReader::EndDocument);
+
+
+    // now we stop in the middle to check whether clear really works
+    reader.clear();
+    reader.addData(xml);
+    reader.readNext();
+    reader.readNext();
+    QCOMPARE(reader.tokenType(), QXmlStreamReader::StartElement);
+
+    // and here the final read
+    reader.clear();
+    reader.addData(xml);
+    while (!reader.atEnd()) {
+        reader.readNext();
+    }
+    QCOMPARE(reader.tokenType(), QXmlStreamReader::EndDocument);
+}
+
+void tst_QXmlStream::checkCommentIndentation_data() const
+{
+
+    QTest::addColumn<QString>("input");
+    QTest::addColumn<QString>("expectedOutput");
+
+    QString simpleInput = "<a><!-- bla --></a>";
+    QString simpleOutput = "<?xml version=\"1.0\"?>\n"
+                           "<a>\n"
+                           "   <!-- bla -->\n"
+                           "</a>\n";
+    QTest::newRow("simple-comment") << simpleInput << simpleOutput;
+
+    QString advancedInput = "<a><!-- bla --><!-- bla --><b><!-- bla --><c><!-- bla --></c><!-- bla --></b></a>";
+    QString advancedOutput = "<?xml version=\"1.0\"?>\n"
+                           "<a>\n"
+                           "   <!-- bla -->\n"
+                           "   <!-- bla -->\n"
+                           "   <b>\n"
+                           "      <!-- bla -->\n"
+                           "      <c>\n"
+                           "         <!-- bla -->\n"
+                           "      </c>\n"
+                           "      <!-- bla -->\n"
+                           "   </b>\n"
+                           "</a>\n";
+    QTest::newRow("advanced-comment") << advancedInput << advancedOutput;
+}
+
+void tst_QXmlStream::checkCommentIndentation() const // task 256468
+{
+    QFETCH(QString, input);
+    QFETCH(QString, expectedOutput);
+    QString output;
+    QXmlStreamReader reader(input);
+    QXmlStreamWriter writer(&output);
+    writer.setAutoFormatting(true);
+    writer.setAutoFormattingIndent(3);
+
+    while (!reader.atEnd()) {
+        reader.readNext();
+        if (reader.error()) {
+            QFAIL("error reading XML input");
+        } else {
+            writer.writeCurrentToken(reader);
+        }
+    }
+    QCOMPARE(output, expectedOutput);
+}
+
+#include "tst_qxmlstream.moc"
+// vim: et:ts=4:sw=4:sts=4