diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-12-12 18:32:19 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2018-01-26 20:59:50 +0000 |
commit | a6b697ca13945a174cff9f3e9b1af1cf61c0bea5 (patch) | |
tree | 78d5fad97ff8e89f079df29cc5a75b4e7197bfc7 /tests/auto/corelib/xml/qxmlstream/tst_qxmlstream.cpp | |
parent | 657894624521b580f59ff5f58b9c0e9be159dc1c (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.cpp | 1745 |
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 ¬ation : 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 ¬ationDeclaration : 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 '/>"); - QTest::newRow("utf8, attributes, 1st char, control escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa'/>"); - QTest::newRow("utf8, attributes, middle char, legal escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaa'/>"); - QTest::newRow("utf8, attributes, last char, control escaping hex") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaa'/>"); - QTest::newRow("utf8, attributes, 1st char, legal escaping dec") << true << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a '/>"); - QTest::newRow("utf8, attributes, 1st char, control escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaa'/>"); - QTest::newRow("utf8, attributes, middle char, legal escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaaaa'/>"); - QTest::newRow("utf8, attributes, last char, control escaping dec") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aaaaaa'/>"); - QTest::newRow("utf8, tag escaping") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='aa'><![CDATA[abcdefghi]]></root>"); - // - QTest::newRow("utf8, mix of illegal control") << false << QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root attr='a�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 |