summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-12-12 18:32:19 -0800
committerThiago Macieira <thiago.macieira@intel.com>2018-01-26 20:59:50 +0000
commita6b697ca13945a174cff9f3e9b1af1cf61c0bea5 (patch)
tree78d5fad97ff8e89f079df29cc5a75b4e7197bfc7 /tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp
parent657894624521b580f59ff5f58b9c0e9be159dc1c (diff)
Create corelib/serialization and move existing file formats into it
This is in preparation to adding CBOR support. We don't need yet another dir for CBOR and placing it in src/corelib/json is just wrong. Change-Id: I9741f017961b410c910dfffd14ffb9d870340fa6 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp')
-rw-r--r--tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp1745
1 files changed, 0 insertions, 1745 deletions
diff --git a/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp
deleted file mode 100644
index 16a4200b5d..0000000000
--- a/tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp
+++ /dev/null
@@ -1,1745 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $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"
-
-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);
-}
-
-template <typename C>
-const C sorted_by_name(C c) { // return by const value so we can feed directly into range-for loops below
- using T = typename C::value_type;
- auto byName = [](const T &lhs, const T &rhs) {
- return lhs.name() < rhs.name();
- };
- std::sort(c.begin(), c.end(), byName);
- return c;
-}
-
-/**
- * Opens \a filename and returns content produced as per
- * xmlconf/xmltest/canonxml.html.
- *
- * \a 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()) {
- const auto notationDeclarations = reader.notationDeclarations();
- if (!notationDeclarations.isEmpty()) {
- QString dtd;
- QTextStream writeDtd(&dtd);
-
- writeDtd << "<!DOCTYPE ";
- writeDtd << docType;
- writeDtd << " [";
- writeDtd << endl;
- for (const QXmlStreamNotationDeclaration &notation : sorted_by_name(notationDeclarations)) {
- 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());
- for (const QXmlStreamAttribute &attribute : sorted_by_name(reader.attributes()))
- 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(QLatin1Char('#') + QString::number(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;
-}
-
-/**
- * \brief Returns the lexical QName of the document element in
- * \a document.
- *
- * It is assumed that \a 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();
- }
-
- qFatal("The input %s didn't contain an element", document.constData());
- return QString();
-}
-
-/**
- * \brief 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
- * Qt Test's usual error reporting and testing mechanisms are slightly bypassed.
- *
- * Part of this code is a manual, ad-hoc implementation of xml:base.
- *
- * See \l {http://www.w3.org/XML/Test/} {Extensible Markup Language (XML) Conformance Test Suites}
- */
-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
- {
- friend class QVector<MissedBaseline>;
- MissedBaseline() {} // for QVector, don't use
- public:
- MissedBaseline(const QString &aId,
- const QByteArray &aExpected,
- const QByteArray &aOutput) : id(aId),
- expected(aExpected),
- output(aOutput)
- {
- if (aId.isEmpty())
- qFatal("%s: aId must not be an empty string", Q_FUNC_INFO);
- }
-
- void swap(MissedBaseline &other) Q_DECL_NOTHROW
- {
- qSwap(id, other.id);
- qSwap(expected, other.expected);
- qSwap(output, other.output);
- }
-
- QString id;
- QByteArray expected;
- QByteArray output;
- };
-
- QVector<GeneralFailure> failures;
- QVector<MissedBaseline> missedBaselines;
-
- /**
- * The count of how many tests that were run.
- */
- int runCount;
-
- int skipCount;
-
- /**
- * \a baseURI is the URI of where the catalog file resides.
- */
- TestSuiteHandler(const QUrl &baseURI) : runCount(0),
- skipCount(0)
- {
- if (!baseURI.isValid())
- qFatal("%s: baseURI must be valid", Q_FUNC_INFO);
- 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"));
-
- 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, QLatin1String("Failed to open input file ") + inputFilePath));
- return true;
- }
-
- if(type == QLatin1String("not-wf"))
- {
- if(isWellformed(&inputFile, ParseSinglePass))
- {
- failures.append(qMakePair(id, QLatin1String("Failed to flag ") + inputFilePath
- + QLatin1String(" as not well-formed.")));
-
- /* 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, QLatin1String("Failed to flag ") + inputFilePath
- + QLatin1String(" as not well-formed with incremental parsing.")));
- }
- 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, QLatin1String("Failed to open baseline ") + 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
- {
- qFatal("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)
- {
- if (!inputFile)
- qFatal("%s: inputFile must be a valid QIODevice pointer", Q_FUNC_INFO);
- if (!inputFile->isOpen())
- qFatal("%s: inputFile must be opened by the caller", Q_FUNC_INFO);
- if (mode != ParseIncrementally && mode != ParseSinglePass)
- qFatal("%s: mode must be either ParseIncrementally or ParseSinglePass", Q_FUNC_INFO);
-
- 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;
-};
-QT_BEGIN_NAMESPACE
-Q_DECLARE_SHARED(TestSuiteHandler::MissedBaseline)
-QT_END_NAMESPACE
-
-class tst_QXmlStream: public QObject
-{
- Q_OBJECT
-public:
- tst_QXmlStream() : m_handler(QUrl::fromLocalFile(QFINDTESTDATA(catalogFile)))
- {
- }
-
-private slots:
- void initTestCase();
- void cleanupTestCase();
- 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 writerHangs() const;
- void writerAutoFormattingWithComments() const;
- void writerAutoFormattingWithTabs() const;
- void writerAutoFormattingWithProcessingInstructions() 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 writeWithUtf8Codec() const;
- void writeWithUtf16Codec() 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;
- void crashInXmlStreamReader() const;
- void write8bitCodec() const;
- void invalidStringCharacters_data() const;
- void invalidStringCharacters() const;
- void hasError() const;
- void readBack() const;
-
-private:
- static QByteArray readFile(const QString &filename);
-
- TestSuiteHandler m_handler;
-};
-
-void tst_QXmlStream::initTestCase()
-{
- QFile file(QFINDTESTDATA(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::cleanupTestCase()
-{
- QFile::remove(QLatin1String("test.xml"));
-}
-
-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 Qt Test 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 Qt Test 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) {
- const QByteArray testName = QByteArray::number(i) + ". " + m_handler.successes.at(i).toLatin1();
- QTest::newRow(testName.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() << '"';
- const auto attributes = reader.attributes();
- if (attributes.size()) {
- for (const QXmlStreamAttribute &attribute : 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;
- }
- }
- const auto namespaceDeclarations = reader.namespaceDeclarations();
- if (namespaceDeclarations.size()) {
- for (const QXmlStreamNamespaceDeclaration &namespaceDeclaration : namespaceDeclarations) {
- writer << endl << " NamespaceDeclaration(";
- if (!namespaceDeclaration.prefix().isEmpty())
- writer << " prefix=\"" << namespaceDeclaration.prefix().toString() << '"';
- if (!namespaceDeclaration.namespaceUri().isEmpty())
- writer << " namespaceUri=\"" << namespaceDeclaration.namespaceUri().toString() << '"';
- writer << " )" << endl;
- }
- }
- const auto notationDeclarations = reader.notationDeclarations();
- if (notationDeclarations.size()) {
- for (const QXmlStreamNotationDeclaration &notationDeclaration : 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;
- }
- }
- const auto entityDeclarations = reader.entityDeclarations();
- if (entityDeclarations.size()) {
- for (const QXmlStreamEntityDeclaration &entityDeclaration : 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(QFINDTESTDATA("data/"));
- const auto fileNames = dir.entryList(QStringList() << "*.xml");
- for (const QString &filename : fileNames) {
- QString reference = QFileInfo(filename).baseName() + ".ref";
- QTest::newRow(dir.filePath(filename).toLatin1().data()) << dir.filePath(filename) << dir.filePath(reference);
- }
-}
-
-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
-{
- const char *illegal_start = "illegal<sta";
- const char *legal_start = "<sta";
- const char* end = "rt/>";
- {
- QXmlStreamReader xml("");
- while (!xml.atEnd()) {
- xml.readNext();
- }
- QCOMPARE(xml.error(), QXmlStreamReader::PrematureEndOfDocumentError);
- QCOMPARE(xml.errorString(), QLatin1String("Premature end of document."));
- xml.addData(legal_start);
- while (!xml.atEnd()) {
- xml.readNext();
- }
- QCOMPARE(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."));
- QCOMPARE(xml.error(), QXmlStreamReader::NotWellFormedError);
- }
-}
-
-// Regression test for crash due to using empty QStack.
-void tst_QXmlStream::writerHangs() const
-{
- QTemporaryDir dir(QDir::tempPath() + QLatin1String("/tst_qxmlstream.XXXXXX"));
- QFile file(dir.path() + "/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();
-}
-
-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);
-}
-
-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);
-}
-
-void tst_QXmlStream::writerAutoFormattingWithProcessingInstructions() const
-{
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
-
- QXmlStreamWriter writer(&buffer);
- writer.setAutoFormatting(true);
- writer.writeStartDocument();
- writer.writeProcessingInstruction("B", "C");
- writer.writeStartElement("A");
- writer.writeEndElement();
- writer.writeEndDocument();
- const char *str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?B C?>\n<A/>\n";
- QCOMPARE(buffer.buffer().data(), str);
-}
-
-void tst_QXmlStream::writeAttributesWithSpace() const
-{
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
-
-
- QXmlStreamWriter writer(&buffer);
- writer.writeStartDocument();
- writer.writeEmptyElement("A");
- writer.writeAttribute("attribute", QStringLiteral("value") + QChar(QChar::Nbsp));
- writer.writeEndDocument();
- QString s = QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?><A attribute=\"value")
- + QChar(QChar::Nbsp) + QLatin1String("\"/>\n");
- QCOMPARE(buffer.buffer().data(), s.toUtf8().data());
-}
-
-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() == QLatin1String("A"));
-
- int amountOfB = 0;
- while (reader.readNextStartElement()) {
- QVERIFY(reader.isStartElement() && reader.name() == QLatin1String("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(QFINDTESTDATA("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 Qt Test'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\xe9 h\xe9";
- 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::writeWithUtf8Codec() const
-{
- QByteArray outarray;
- QXmlStreamWriter writer(&outarray);
-
- QTextCodec *codec = QTextCodec::codecForMib(106); // utf-8
- QVERIFY(codec);
- writer.setCodec(codec);
-
- writer.writeStartDocument("1.0");
- static const char begin[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
- QVERIFY(outarray.startsWith(begin));
-}
-
-void tst_QXmlStream::writeWithUtf16Codec() const
-{
- QByteArray outarray;
- QXmlStreamWriter writer(&outarray);
-
- QTextCodec *codec = QTextCodec::codecForMib(1014); // utf-16LE
- QVERIFY(codec);
- writer.setCodec(codec);
-
- writer.writeStartDocument("1.0");
- static const char begin[] = "<?xml version=\"1.0\" encoding=\"UTF-16"; // skip potential "LE" suffix
- const int count = sizeof(begin) - 1; // don't include 0 terminator
- QByteArray begin_UTF16;
- begin_UTF16.reserve(2*(count));
- for (int i = 0; i < count; ++i) {
- begin_UTF16.append(begin[i]);
- begin_UTF16.append((char)'\0');
- }
- QVERIFY(outarray.startsWith(begin_UTF16));
-}
-
-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
-{
- 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
-{
- 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);
-}
-
-// This is a regression test for QTBUG-9196, where the series of tags used
-// in the test caused a crash in the XML stream reader.
-void tst_QXmlStream::crashInXmlStreamReader() const
-{
- QByteArray ba("<a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a>"
- "<a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a><a></a>");
- QXmlStreamReader xml(ba);
- while (!xml.atEnd()) {
- xml.readNext();
- }
-}
-
-class FakeBuffer : public QBuffer
-{
-protected:
- qint64 writeData(const char *c, qint64 i)
- {
- qint64 ai = qMin(m_capacity, i);
- m_capacity -= ai;
- return ai ? QBuffer::writeData(c, ai) : 0;
- }
-public:
- void setCapacity(int capacity) { m_capacity = capacity; }
-private:
- qint64 m_capacity;
-};
-
-void tst_QXmlStream::hasError() const
-{
- {
- FakeBuffer fb;
- QVERIFY(fb.open(QBuffer::ReadWrite));
- fb.setCapacity(1000);
- QXmlStreamWriter writer(&fb);
- writer.writeStartDocument();
- writer.writeEndDocument();
- QVERIFY(!writer.hasError());
- QCOMPARE(fb.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));
- }
-
- {
- // Failure caused by write(QString)
- FakeBuffer fb;
- QVERIFY(fb.open(QBuffer::ReadWrite));
- const QByteArray expected = QByteArrayLiteral("<?xml version=\"");
- fb.setCapacity(expected.size());
- QXmlStreamWriter writer(&fb);
- writer.writeStartDocument();
- QVERIFY(writer.hasError());
- QCOMPARE(fb.data(), expected);
- }
-
- {
- // Failure caused by write(char *)
- FakeBuffer fb;
- QVERIFY(fb.open(QBuffer::ReadWrite));
- const QByteArray expected = QByteArrayLiteral("<?xml version=\"1.0");
- fb.setCapacity(expected.size());
- QXmlStreamWriter writer(&fb);
- writer.writeStartDocument();
- QVERIFY(writer.hasError());
- QCOMPARE(fb.data(), expected);
- }
-
- {
- // Failure caused by write(QStringRef)
- FakeBuffer fb;
- QVERIFY(fb.open(QBuffer::ReadWrite));
- const QByteArray expected = QByteArrayLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?><test xmlns:");
- fb.setCapacity(expected.size());
- QXmlStreamWriter writer(&fb);
- writer.writeStartDocument();
- writer.writeStartElement("test");
- writer.writeNamespace("http://foo.bar", "foo");
- QVERIFY(writer.hasError());
- QCOMPARE(fb.data(), expected);
- }
-
- {
- // Refusal to write after 1st failure
- FakeBuffer fb;
- QVERIFY(fb.open(QBuffer::ReadWrite));
- fb.setCapacity(10);
- QXmlStreamWriter writer(&fb);
- writer.writeStartDocument();
- QVERIFY(writer.hasError());
- QCOMPARE(fb.data(), QByteArray("<?xml vers"));
- fb.setCapacity(1000);
- writer.writeStartElement("test"); // literal & qstring
- writer.writeNamespace("http://foo.bar", "foo"); // literal & qstringref
- QVERIFY(writer.hasError());
- QCOMPARE(fb.data(), QByteArray("<?xml vers"));
- }
-
-}
-
-void tst_QXmlStream::write8bitCodec() const
-{
- QBuffer outBuffer;
- QVERIFY(outBuffer.open(QIODevice::WriteOnly));
- QXmlStreamWriter writer(&outBuffer);
- writer.setAutoFormatting(false);
-
- QTextCodec *codec = QTextCodec::codecForName("IBM500");
- if (!codec) {
- QSKIP("Encoding IBM500 not available.");
- }
- writer.setCodec(codec);
-
- writer.writeStartDocument();
- writer.writeStartElement("root");
- writer.writeAttribute("attrib", "1");
- writer.writeEndElement();
- writer.writeEndDocument();
- outBuffer.close();
-
- // test 8 bit encoding
- QByteArray values = outBuffer.data();
- QVERIFY(values.size() > 1);
- // check '<'
- QCOMPARE(values[0] & 0x00FF, 0x4c);
- // check '?'
- QCOMPARE(values[1] & 0x00FF, 0x6F);
-
- // convert the start of the XML
- const QString expected = ("<?xml version=\"1.0\" encoding=\"IBM500\"?>");
- QTextDecoder *decoder = codec->makeDecoder();
- QVERIFY(decoder);
- QString decodedText = decoder->toUnicode(values);
- delete decoder;
- QVERIFY(decodedText.startsWith(expected));
-}
-
-void tst_QXmlStream::invalidStringCharacters() const
-{
- // test scan in attributes
- QFETCH(QString, testString);
- QFETCH(bool, expectedResultNoError);
-
- QByteArray values = testString.toUtf8();
- QBuffer inBuffer;
- inBuffer.setData(values);
- QVERIFY(inBuffer.open(QIODevice::ReadOnly));
- QXmlStreamReader reader(&inBuffer);
- do {
- reader.readNext();
- } while (!reader.atEnd());
- QCOMPARE((reader.error() == QXmlStreamReader::NoError), expectedResultNoError);
-}
-
-void tst_QXmlStream::invalidStringCharacters_data() const
-{
- // test scan in attributes
- QTest::addColumn<bool>("expectedResultNoError");
- QTest::addColumn<QString>("testString");
- QChar ctrl(0x1A);
- QTest::newRow("utf8, attributes, legal") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'/>");
- QTest::newRow("utf8, attributes, only char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='")+ctrl+QString("'/>");
- QTest::newRow("utf8, attributes, 1st char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='")+ctrl+QString("abc'/>");
- QTest::newRow("utf8, attributes, middle char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='abc")+ctrl+QString("efgx'/>");
- QTest::newRow("utf8, attributes, last char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='abcde")+ctrl+QString("'/>");
- //
- QTest::newRow("utf8, text, legal") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abcx1A</root>");
- QTest::newRow("utf8, text, only, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>")+ctrl+QString("</root>");
- QTest::newRow("utf8, text, 1st char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abc")+ctrl+QString("def</root>");
- QTest::newRow("utf8, text, middle char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abc")+ctrl+QString("efg</root>");
- QTest::newRow("utf8, text, last char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'>abc")+ctrl+QString("</root>");
- //
- QTest::newRow("utf8, cdata text, legal") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcdefghi]]></root>");
- QTest::newRow("utf8, cdata text, only, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[")+ctrl+QString("]]></root>");
- QTest::newRow("utf8, cdata text, 1st char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[")+ctrl+QString("abcdefghi]]></root>");
- QTest::newRow("utf8, cdata text, middle char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcd")+ctrl+QString("efghi]]></root>");
- QTest::newRow("utf8, cdata text, last char, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcdefghi")+ctrl+QString("]]></root>");
- //
- QTest::newRow("utf8, mixed, control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a")+ctrl+QString("a'><![CDATA[abcdefghi")+ctrl+QString("]]></root>");
- QTest::newRow("utf8, tag") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><roo")+ctrl+QString("t attr='aa'><![CDATA[abcdefghi]]></roo")+ctrl+QString("t>");
- //
- QTest::newRow("utf8, attributes, 1st char, legal escaping hex") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a&#xA0;'/>");
- QTest::newRow("utf8, attributes, 1st char, control escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='&#x1A;aaa'/>");
- QTest::newRow("utf8, attributes, middle char, legal escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa&#x1A;aaa'/>");
- QTest::newRow("utf8, attributes, last char, control escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa&#x1A;'/>");
- QTest::newRow("utf8, attributes, 1st char, legal escaping dec") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a&#160;'/>");
- QTest::newRow("utf8, attributes, 1st char, control escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='&#26;aaaa'/>");
- QTest::newRow("utf8, attributes, middle char, legal escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa&#26;aaaaa'/>");
- QTest::newRow("utf8, attributes, last char, control escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaa&#26;'/>");
- QTest::newRow("utf8, tag escaping") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><roo&#x1A;t attr='aa'><![CDATA[abcdefghi]]></roo&#x1A;t>");
- //
- QTest::newRow("utf8, mix of illegal control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a&#0;&#x4;&#x1c;a'><![CDATA[abcdefghi]]></root>");
- //
-}
-
-static bool isValidSingleTextChar(const ushort c)
-{
- // Conforms to https://www.w3.org/TR/REC-xml/#NT-Char - except for the high range, which is done
- // with surrogates.
- // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
- static const QPair<ushort, ushort> validRanges[] = {
- QPair<ushort, ushort>(0x9, 0xb),
- QPair<ushort, ushort>(0xd, 0xe),
- QPair<ushort, ushort>(0x20, 0xd800),
- QPair<ushort, ushort>(0xe000, 0xfffe)
- };
-
- for (const QPair<ushort, ushort> &range : validRanges) {
- if (c >= range.first && c < range.second)
- return true;
- }
- return false;
-}
-
-void tst_QXmlStream::readBack() const
-{
- for (ushort c = 0; c < std::numeric_limits<ushort>::max(); ++c) {
- QBuffer buffer;
-
- QVERIFY(buffer.open(QIODevice::WriteOnly));
- QXmlStreamWriter writer(&buffer);
- writer.writeStartDocument();
- writer.writeTextElement("a", QString(QChar(c)));
- writer.writeEndDocument();
- buffer.close();
-
- if (writer.hasError()) {
- QVERIFY2(!isValidSingleTextChar(c), QByteArray::number(c));
- } else {
- QVERIFY2(isValidSingleTextChar(c), QByteArray::number(c));
- QVERIFY(buffer.open(QIODevice::ReadOnly));
- QXmlStreamReader reader(&buffer);
- do {
- reader.readNext();
- } while (!reader.atEnd());
- QVERIFY2(!reader.hasError(), QByteArray::number(c));
- }
- }
-}
-
-#include "tst_qxmlstream.moc"
-// vim: et:ts=4:sw=4:sts=4