QXmlStreamWriter: add error-handling API
This change introduces QXmlStreamWriter::Error enum and three related functions to enable error reporting during XML writing operations: - error(): returns the current error state of the writer. - errorString(): returns the corresponding error message. - raiseError(): allows applications to raise custom write errors. This complements the existing hasError() method and aligns the writer with QXmlStreamReader's error handling. [ChangeLog][QtCore][QXmlStreamWriter] Added error handling API with QXmlStreamWriter::Error enum, error(), errorString(), and raiseError() functions. Fixes: QTBUG-82389 Change-Id: I4d57a9f611a303cf8dc05caf23b6d331a61684f9 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
9edcc46906
commit
53622aca2a
@ -3015,8 +3015,12 @@ QStringView QXmlStreamReader::documentEncoding() const
|
|||||||
|
|
||||||
QXmlStreamWriter always encodes XML in UTF-8.
|
QXmlStreamWriter always encodes XML in UTF-8.
|
||||||
|
|
||||||
If an error occurs while writing to the underlying device, hasError()
|
\note If an error occurs while writing, \l hasError() will return true.
|
||||||
starts returning true and subsequent writes are ignored.
|
However, data that was already buffered at the time the error occurred,
|
||||||
|
or data written from within the same operation, may still be written
|
||||||
|
to the underlying device. This applies to both \l EncodingError and
|
||||||
|
user-raised \l CustomError. Applications should treat the error state
|
||||||
|
as terminal and avoid further use of the writer after an error.
|
||||||
|
|
||||||
The \l{QXmlStream Bookmarks Example} illustrates how to use a
|
The \l{QXmlStream Bookmarks Example} illustrates how to use a
|
||||||
stream writer to write an XML bookmark file (XBEL) that
|
stream writer to write an XML bookmark file (XBEL) that
|
||||||
@ -3024,6 +3028,29 @@ QStringView QXmlStreamReader::documentEncoding() const
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum QXmlStreamWriter::Error
|
||||||
|
|
||||||
|
This enum specifies the different error cases that can occur
|
||||||
|
when writing XML with QXmlStreamWriter.
|
||||||
|
|
||||||
|
\value NoError No error has occurred.
|
||||||
|
|
||||||
|
\value IOError An I/O error occurred while writing to the
|
||||||
|
device.
|
||||||
|
|
||||||
|
\value EncodingError An encoding error occurred while converting
|
||||||
|
characters to the output format.
|
||||||
|
|
||||||
|
\value InvalidCharacter A character not permitted in XML 1.0
|
||||||
|
was encountered while writing.
|
||||||
|
|
||||||
|
\value CustomError A custom error has been raised with
|
||||||
|
\l raiseError().
|
||||||
|
|
||||||
|
\since 6.10
|
||||||
|
*/
|
||||||
|
|
||||||
#if QT_CONFIG(xmlstreamwriter)
|
#if QT_CONFIG(xmlstreamwriter)
|
||||||
|
|
||||||
class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
|
class QXmlStreamWriterPrivate : public QXmlStreamPrivateTagStack
|
||||||
@ -3042,6 +3069,8 @@ public:
|
|||||||
delete device;
|
delete device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void raiseError(QXmlStreamWriter::Error error);
|
||||||
|
void raiseError(QXmlStreamWriter::Error error, const QString &message);
|
||||||
void write(QAnyStringView s);
|
void write(QAnyStringView s);
|
||||||
void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
|
void writeEscaped(QAnyStringView, bool escapeWhitespace = false);
|
||||||
bool finishStartElement(bool contents = true);
|
bool finishStartElement(bool contents = true);
|
||||||
@ -3054,14 +3083,14 @@ public:
|
|||||||
uint inEmptyElement :1;
|
uint inEmptyElement :1;
|
||||||
uint lastWasStartElement :1;
|
uint lastWasStartElement :1;
|
||||||
uint wroteSomething :1;
|
uint wroteSomething :1;
|
||||||
uint hasIoError :1;
|
|
||||||
uint hasEncodingError :1;
|
|
||||||
uint autoFormatting :1;
|
uint autoFormatting :1;
|
||||||
uint didWriteStartDocument :1;
|
uint didWriteStartDocument :1;
|
||||||
uint didWriteAnyToken :1;
|
uint didWriteAnyToken :1;
|
||||||
std::string autoFormattingIndent = std::string(4, ' ');
|
std::string autoFormattingIndent = std::string(4, ' ');
|
||||||
NamespaceDeclaration emptyNamespace;
|
NamespaceDeclaration emptyNamespace;
|
||||||
qsizetype lastNamespaceDeclaration = 1;
|
qsizetype lastNamespaceDeclaration = 1;
|
||||||
|
QXmlStreamWriter::Error error = QXmlStreamWriter::Error::NoError;
|
||||||
|
QString errorString;
|
||||||
|
|
||||||
NamespaceDeclaration &addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix);
|
NamespaceDeclaration &addExtraNamespace(QAnyStringView namespaceUri, QAnyStringView prefix);
|
||||||
NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
|
NamespaceDeclaration &findNamespace(QAnyStringView namespaceUri, bool writeDeclaration = false, bool noDefault = false);
|
||||||
@ -3080,16 +3109,42 @@ private:
|
|||||||
QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
|
QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
|
||||||
: q_ptr(q), deleteDevice(false), inStartElement(false),
|
: q_ptr(q), deleteDevice(false), inStartElement(false),
|
||||||
inEmptyElement(false), lastWasStartElement(false),
|
inEmptyElement(false), lastWasStartElement(false),
|
||||||
wroteSomething(false), hasIoError(false),
|
wroteSomething(false), autoFormatting(false),
|
||||||
hasEncodingError(false), autoFormatting(false),
|
|
||||||
didWriteStartDocument(false), didWriteAnyToken(false)
|
didWriteStartDocument(false), didWriteAnyToken(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode)
|
||||||
|
{
|
||||||
|
error = errorCode;
|
||||||
|
switch (error) {
|
||||||
|
case QXmlStreamWriter::Error::IOError:
|
||||||
|
errorString = QXmlStream::tr("An I/O error occurred while writing");
|
||||||
|
break;
|
||||||
|
case QXmlStreamWriter::Error::EncodingError:
|
||||||
|
errorString = QXmlStream::tr("An encoding error occurred while writing");
|
||||||
|
break;
|
||||||
|
case QXmlStreamWriter::Error::InvalidCharacter:
|
||||||
|
errorString = QXmlStream::tr("Encountered an invalid XML 1.0 character while writing");
|
||||||
|
break;
|
||||||
|
case QXmlStreamWriter::Error::CustomError:
|
||||||
|
errorString = QXmlStream::tr("An error occurred while writing");
|
||||||
|
break;
|
||||||
|
case QXmlStreamWriter::Error::NoError:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QXmlStreamWriterPrivate::raiseError(QXmlStreamWriter::Error errorCode, const QString &message)
|
||||||
|
{
|
||||||
|
error = errorCode;
|
||||||
|
errorString = message;
|
||||||
|
}
|
||||||
|
|
||||||
void QXmlStreamWriterPrivate::write(QAnyStringView s)
|
void QXmlStreamWriterPrivate::write(QAnyStringView s)
|
||||||
{
|
{
|
||||||
if (device) {
|
if (device) {
|
||||||
if (hasIoError)
|
if (error == QXmlStreamWriter::Error::IOError)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s.visit([&] (auto s) { doWriteToDevice(s); });
|
s.visit([&] (auto s) { doWriteToDevice(s); });
|
||||||
@ -3102,27 +3157,36 @@ void QXmlStreamWriterPrivate::write(QAnyStringView s)
|
|||||||
|
|
||||||
void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
|
void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespace)
|
||||||
{
|
{
|
||||||
|
struct NextResult {
|
||||||
|
char32_t value;
|
||||||
|
bool encodingError;
|
||||||
|
};
|
||||||
struct NextLatin1 {
|
struct NextLatin1 {
|
||||||
char32_t operator()(const char *&it, const char *) const
|
NextResult operator()(const char *&it, const char *) const
|
||||||
{ return uchar(*it++); }
|
{ return {uchar(*it++), false}; }
|
||||||
};
|
};
|
||||||
struct NextUtf8 {
|
struct NextUtf8 {
|
||||||
char32_t operator()(const char *&it, const char *end) const
|
NextResult operator()(const char *&it, const char *end) const
|
||||||
{
|
{
|
||||||
uchar uc = *it++;
|
uchar uc = *it++;
|
||||||
char32_t utf32 = 0;
|
char32_t utf32 = 0;
|
||||||
char32_t *output = &utf32;
|
char32_t *output = &utf32;
|
||||||
qsizetype n = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(uc, output, it, end);
|
qsizetype n = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(uc, output, it, end);
|
||||||
return n < 0 ? 0 : utf32;
|
return n < 0 ? NextResult{0, true} : NextResult{utf32, false};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct NextUtf16 {
|
struct NextUtf16 {
|
||||||
char32_t operator()(const QChar *&it, const QChar *end) const
|
NextResult operator()(const QChar *&it, const QChar *end) const
|
||||||
{
|
{
|
||||||
QStringIterator decoder(it, end);
|
QStringIterator decoder(it, end);
|
||||||
char32_t result = decoder.next(u'\0');
|
// We can have '\0' in the text, and it should be reported as
|
||||||
|
// InvalidCharacter, not as EncodingError
|
||||||
|
constexpr char32_t invalidValue = 0xFFFFFFFF;
|
||||||
|
Q_ASSERT(invalidValue > QChar::LastValidCodePoint);
|
||||||
|
char32_t result = decoder.next(invalidValue);
|
||||||
it = decoder.position();
|
it = decoder.position();
|
||||||
return result;
|
return result == invalidValue ? NextResult{U'\0', true}
|
||||||
|
: NextResult{result, false};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3143,7 +3207,7 @@ void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespa
|
|||||||
|
|
||||||
while (it != end) {
|
while (it != end) {
|
||||||
auto next_it = it;
|
auto next_it = it;
|
||||||
char32_t uc = decoder(next_it, end);
|
auto [uc, encodingError] = decoder(next_it, end);
|
||||||
if (uc == u'<') {
|
if (uc == u'<') {
|
||||||
replacement = "<"_L1;
|
replacement = "<"_L1;
|
||||||
break;
|
break;
|
||||||
@ -3167,7 +3231,7 @@ void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespa
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (uc == u'\v' || uc == u'\f') {
|
} else if (uc == u'\v' || uc == u'\f') {
|
||||||
hasEncodingError = true;
|
raiseError(QXmlStreamWriter::Error::InvalidCharacter);
|
||||||
break;
|
break;
|
||||||
} else if (uc == u'\r') {
|
} else if (uc == u'\r') {
|
||||||
if (escapeWhitespace) {
|
if (escapeWhitespace) {
|
||||||
@ -3175,7 +3239,10 @@ void QXmlStreamWriterPrivate::writeEscaped(QAnyStringView s, bool escapeWhitespa
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (uc <= u'\x1F' || uc == u'\uFFFE' || uc == u'\uFFFF') {
|
} else if (uc <= u'\x1F' || uc == u'\uFFFE' || uc == u'\uFFFF') {
|
||||||
hasEncodingError = true;
|
if (encodingError)
|
||||||
|
raiseError(QXmlStreamWriter::Error::EncodingError);
|
||||||
|
else
|
||||||
|
raiseError(QXmlStreamWriter::Error::InvalidCharacter);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
it = next_it;
|
it = next_it;
|
||||||
@ -3307,14 +3374,14 @@ void QXmlStreamWriterPrivate::doWriteToDevice(QStringView s)
|
|||||||
s = s.sliced(chunkSize);
|
s = s.sliced(chunkSize);
|
||||||
}
|
}
|
||||||
if (state.remainingChars > 0)
|
if (state.remainingChars > 0)
|
||||||
hasEncodingError = true;
|
raiseError(QXmlStreamWriter::Error::EncodingError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
|
void QXmlStreamWriterPrivate::doWriteToDevice(QUtf8StringView s)
|
||||||
{
|
{
|
||||||
QByteArrayView bytes = s;
|
QByteArrayView bytes = s;
|
||||||
if (device->write(bytes.data(), bytes.size()) != bytes.size())
|
if (device->write(bytes.data(), bytes.size()) != bytes.size())
|
||||||
hasIoError = true;
|
raiseError(QXmlStreamWriter::Error::IOError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
|
void QXmlStreamWriterPrivate::doWriteToDevice(QLatin1StringView s)
|
||||||
@ -3481,18 +3548,66 @@ int QXmlStreamWriter::autoFormattingIndent() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns \c true if writing failed.
|
Returns \c true if an error occurred while trying to write data.
|
||||||
|
|
||||||
This can happen if the stream failed to write to the underlying
|
If the error is \l Error::IOError, subsequent writes to the underlying
|
||||||
device or if the data to be written contained invalid characters.
|
QIODevice will fail. In other cases malformed data might be written to
|
||||||
|
the document.
|
||||||
|
|
||||||
The error status is never reset. Writes happening after the error
|
The error status is never reset. Writes happening after the error
|
||||||
occurred may be ignored, even if the error condition is cleared.
|
occurred may be ignored, even if the error condition is cleared.
|
||||||
|
|
||||||
|
\sa error(), errorString(), raiseError(const QString &message),
|
||||||
*/
|
*/
|
||||||
bool QXmlStreamWriter::hasError() const
|
bool QXmlStreamWriter::hasError() const
|
||||||
|
{
|
||||||
|
return error() != QXmlStreamWriter::Error::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the current error state of the writer.
|
||||||
|
|
||||||
|
If no error has occurred, this function returns
|
||||||
|
QXmlStreamWriter::Error::NoError.
|
||||||
|
|
||||||
|
\since 6.10
|
||||||
|
\sa errorString(), raiseError(const QString &message), hasError()
|
||||||
|
*/
|
||||||
|
QXmlStreamWriter::Error QXmlStreamWriter::error() const
|
||||||
{
|
{
|
||||||
Q_D(const QXmlStreamWriter);
|
Q_D(const QXmlStreamWriter);
|
||||||
return d->hasIoError || d->hasEncodingError;
|
return d->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
If an error has occurred, returns its associated error message.
|
||||||
|
|
||||||
|
The error message is either set internally by QXmlStreamWriter or provided
|
||||||
|
by the user via raiseError(). If no error has occured, this function returns
|
||||||
|
a null string.
|
||||||
|
|
||||||
|
\since 6.10
|
||||||
|
\sa error(), raiseError(const QString &message), hasError()
|
||||||
|
*/
|
||||||
|
QString QXmlStreamWriter::errorString() const
|
||||||
|
{
|
||||||
|
Q_D(const QXmlStreamWriter);
|
||||||
|
return d->errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Raises a custom error with the given \a message.
|
||||||
|
|
||||||
|
This function is for manual indication that an error has occurred during
|
||||||
|
writing, such as an application level validation failure.
|
||||||
|
|
||||||
|
\since 6.10
|
||||||
|
\sa errorString(), error(), hasError()
|
||||||
|
*/
|
||||||
|
void QXmlStreamWriter::raiseError(const QString &message)
|
||||||
|
{
|
||||||
|
Q_D(QXmlStreamWriter);
|
||||||
|
d->raiseError(QXmlStreamWriter::Error::CustomError, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -459,6 +459,17 @@ public:
|
|||||||
void writeCurrentToken(const QXmlStreamReader &reader);
|
void writeCurrentToken(const QXmlStreamReader &reader);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum class Error {
|
||||||
|
NoError,
|
||||||
|
IOError,
|
||||||
|
EncodingError,
|
||||||
|
InvalidCharacter,
|
||||||
|
CustomError,
|
||||||
|
};
|
||||||
|
|
||||||
|
void raiseError(const QString &message);
|
||||||
|
QString errorString() const;
|
||||||
|
Error error() const;
|
||||||
bool hasError() const;
|
bool hasError() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -605,7 +605,7 @@ private slots:
|
|||||||
void crashInXmlStreamReader() const;
|
void crashInXmlStreamReader() const;
|
||||||
void invalidStringCharacters_data() const;
|
void invalidStringCharacters_data() const;
|
||||||
void invalidStringCharacters() const;
|
void invalidStringCharacters() const;
|
||||||
void hasError() const;
|
void writerErrors() const;
|
||||||
void readBack_data() const;
|
void readBack_data() const;
|
||||||
void readBack() const;
|
void readBack() const;
|
||||||
void roundTrip() const;
|
void roundTrip() const;
|
||||||
@ -2048,6 +2048,7 @@ void tst_QXmlStream::writeBadCharactersUtf8() const
|
|||||||
QXmlStreamWriter writer(&target);
|
QXmlStreamWriter writer(&target);
|
||||||
writer.writeTextElement("a", QUtf8StringView(input));
|
writer.writeTextElement("a", QUtf8StringView(input));
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::EncodingError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QXmlStream::writeBadCharactersUtf16_data() const
|
void tst_QXmlStream::writeBadCharactersUtf16_data() const
|
||||||
@ -2066,6 +2067,8 @@ void tst_QXmlStream::writeBadCharactersUtf16() const
|
|||||||
QXmlStreamWriter writer(&target);
|
QXmlStreamWriter writer(&target);
|
||||||
writer.writeTextElement("a", input);
|
writer.writeTextElement("a", input);
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::EncodingError);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QXmlStream::entitiesAndWhitespace_1() const
|
void tst_QXmlStream::entitiesAndWhitespace_1() const
|
||||||
@ -2257,10 +2260,10 @@ protected:
|
|||||||
public:
|
public:
|
||||||
void setCapacity(int capacity) { m_capacity = capacity; }
|
void setCapacity(int capacity) { m_capacity = capacity; }
|
||||||
private:
|
private:
|
||||||
qint64 m_capacity;
|
qint64 m_capacity = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QXmlStream::hasError() const
|
void tst_QXmlStream::writerErrors() const
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
FakeBuffer fb;
|
FakeBuffer fb;
|
||||||
@ -2270,6 +2273,8 @@ void tst_QXmlStream::hasError() const
|
|||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
writer.writeEndDocument();
|
writer.writeEndDocument();
|
||||||
QVERIFY(!writer.hasError());
|
QVERIFY(!writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::NoError);
|
||||||
|
QVERIFY(writer.errorString().isEmpty());
|
||||||
QCOMPARE(fb.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));
|
QCOMPARE(fb.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2282,6 +2287,8 @@ void tst_QXmlStream::hasError() const
|
|||||||
QXmlStreamWriter writer(&fb);
|
QXmlStreamWriter writer(&fb);
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::IOError);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
QCOMPARE(fb.data(), expected);
|
QCOMPARE(fb.data(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2294,6 +2301,8 @@ void tst_QXmlStream::hasError() const
|
|||||||
QXmlStreamWriter writer(&fb);
|
QXmlStreamWriter writer(&fb);
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::IOError);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
QCOMPARE(fb.data(), expected);
|
QCOMPARE(fb.data(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2301,13 +2310,16 @@ void tst_QXmlStream::hasError() const
|
|||||||
// Failure caused by write(QStringRef)
|
// Failure caused by write(QStringRef)
|
||||||
FakeBuffer fb;
|
FakeBuffer fb;
|
||||||
QVERIFY(fb.open(QBuffer::ReadWrite));
|
QVERIFY(fb.open(QBuffer::ReadWrite));
|
||||||
const QByteArray expected = QByteArrayLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?><test xmlns:");
|
const QByteArray expected =
|
||||||
|
QByteArrayLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?><test xmlns:");
|
||||||
fb.setCapacity(expected.size());
|
fb.setCapacity(expected.size());
|
||||||
QXmlStreamWriter writer(&fb);
|
QXmlStreamWriter writer(&fb);
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
writer.writeStartElement("test");
|
writer.writeStartElement("test");
|
||||||
writer.writeNamespace("http://foo.bar", "foo");
|
writer.writeNamespace("http://foo.bar", "foo");
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::IOError);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
QCOMPARE(fb.data(), expected);
|
QCOMPARE(fb.data(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2319,14 +2331,86 @@ void tst_QXmlStream::hasError() const
|
|||||||
QXmlStreamWriter writer(&fb);
|
QXmlStreamWriter writer(&fb);
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::IOError);
|
||||||
QCOMPARE(fb.data(), QByteArray("<?xml vers"));
|
QCOMPARE(fb.data(), QByteArray("<?xml vers"));
|
||||||
fb.setCapacity(1000);
|
fb.setCapacity(1000);
|
||||||
writer.writeStartElement("test"); // literal & qstring
|
writer.writeStartElement("test"); // literal & qstring
|
||||||
writer.writeNamespace("http://foo.bar", "foo"); // literal & qstringref
|
writer.writeNamespace("http://foo.bar", "foo"); // literal & qstringref
|
||||||
QVERIFY(writer.hasError());
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::IOError);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
QCOMPARE(fb.data(), QByteArray("<?xml vers"));
|
QCOMPARE(fb.data(), QByteArray("<?xml vers"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Encoding error: lone high surrogate
|
||||||
|
QByteArray buffer;
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
writer.writeStartDocument();
|
||||||
|
writer.writeStartElement("root");
|
||||||
|
writer.writeCharacters(QChar(0xD800));
|
||||||
|
writer.writeEndElement();
|
||||||
|
writer.writeEndDocument();
|
||||||
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::EncodingError);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Invalid character error: invalid character for XML 1.0 in text content
|
||||||
|
QByteArray buffer;
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
writer.writeStartDocument();
|
||||||
|
writer.writeStartElement("root"_L1);
|
||||||
|
writer.writeCharacters("Invalid \v character"_L1); // \v is invalid in XML 1.0
|
||||||
|
writer.writeEndElement();
|
||||||
|
writer.writeEndDocument();
|
||||||
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::InvalidCharacter);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Invalid character error: forbidden control character for XML 1.0 U+0001
|
||||||
|
QByteArray buffer;
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
writer.writeStartDocument();
|
||||||
|
writer.writeStartElement("root"_L1);
|
||||||
|
writer.writeCharacters("Invalid \x01 character"_L1);
|
||||||
|
writer.writeEndElement();
|
||||||
|
writer.writeEndDocument();
|
||||||
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::InvalidCharacter);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// '\0' is an InvalidCharacter, not an EncodingError
|
||||||
|
QByteArray buffer;
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
writer.writeStartDocument();
|
||||||
|
writer.writeStartElement("root"_L1);
|
||||||
|
writer.writeCharacters("Invalid \0 character"_L1);
|
||||||
|
writer.writeEndElement();
|
||||||
|
writer.writeEndDocument();
|
||||||
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::InvalidCharacter);
|
||||||
|
QVERIFY(!writer.errorString().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Custom error raised by user
|
||||||
|
QByteArray buffer;
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
writer.writeStartDocument();
|
||||||
|
writer.writeStartElement("root"_L1);
|
||||||
|
writer.raiseError("Custom error");
|
||||||
|
writer.writeEndDocument();
|
||||||
|
QVERIFY(writer.hasError());
|
||||||
|
QCOMPARE(writer.error(), QXmlStreamWriter::Error::CustomError);
|
||||||
|
QCOMPARE(writer.errorString(), "Custom error"_L1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QXmlStream::invalidStringCharacters() const
|
void tst_QXmlStream::invalidStringCharacters() const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user