qt5base-lts/tests/auto/qxmlstream/tst_qxmlstream.cpp
Qt by Nokia 38be0d1383 Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
2011-04-27 12:05:43 +02:00

1647 lines
54 KiB
C++
Raw Blame History

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