summaryrefslogtreecommitdiffstats
path: root/src/testlib/qxmltestlogger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/testlib/qxmltestlogger.cpp')
-rw-r--r--src/testlib/qxmltestlogger.cpp410
1 files changed, 203 insertions, 207 deletions
diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp
index 763cea327b..27da73ba52 100644
--- a/src/testlib/qxmltestlogger.cpp
+++ b/src/testlib/qxmltestlogger.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** 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-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <stdio.h>
#include <string.h>
@@ -53,32 +17,32 @@ QT_BEGIN_NAMESPACE
namespace QTest {
- static const char* xmlMessageType2String(QAbstractTestLogger::MessageTypes type)
+ static const char *xmlMessageType2String(QAbstractTestLogger::MessageTypes type)
{
switch (type) {
- case QAbstractTestLogger::Warn:
- return "warn";
- case QAbstractTestLogger::QSystem:
- return "system";
case QAbstractTestLogger::QDebug:
return "qdebug";
case QAbstractTestLogger::QInfo:
return "qinfo";
case QAbstractTestLogger::QWarning:
return "qwarn";
+ case QAbstractTestLogger::QCritical:
+ return "qcritical";
case QAbstractTestLogger::QFatal:
return "qfatal";
- case QAbstractTestLogger::Skip:
- return "skip";
case QAbstractTestLogger::Info:
return "info";
+ case QAbstractTestLogger::Warn:
+ return "warn";
}
return "??????";
}
- static const char* xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type)
+ static const char *xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type)
{
switch (type) {
+ case QAbstractTestLogger::Skip:
+ return "skip";
case QAbstractTestLogger::Pass:
return "pass";
case QAbstractTestLogger::XFail:
@@ -101,6 +65,26 @@ namespace QTest {
}
+/*! \internal
+ \class QXmlTestLogger
+ \inmodule QtTest
+
+ QXmlTestLogger implements two XML formats specific to Qt.
+
+ The two formats are distinguished by the XmlMode enum.
+*/
+/*! \internal
+ \enum QXmlTestLogger::XmlMode
+
+ This enumerated type selects the type of XML output to produce.
+
+ \value Complete A full self-contained XML document
+ \value Light XML content suitable for embedding in an XML document
+
+ The Complete form wraps the Light form in a <TestCase> element whose name
+ attribute identifies the test class whose private slots are to be run. It
+ also includes the usual <?xml ...> preamble.
+*/
QXmlTestLogger::QXmlTestLogger(XmlMode mode, const char *filename)
: QAbstractTestLogger(filename), xmlmode(mode)
@@ -116,46 +100,55 @@ void QXmlTestLogger::startLogging()
if (xmlmode == QXmlTestLogger::Complete) {
QTestCharBuffer quotedTc;
- xmlQuote(&quotedTc, QTestResult::currentTestObjectName());
- QTest::qt_asprintf(&buf,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<TestCase name=\"%s\">\n", quotedTc.constData());
+ QTest::qt_asprintf(&buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
outputString(buf.constData());
+ if (xmlQuote(&quotedTc, QTestResult::currentTestObjectName())) {
+ QTest::qt_asprintf(&buf, "<TestCase name=\"%s\">\n", quotedTc.constData());
+ outputString(buf.constData());
+ } else {
+ // Unconditional end-tag => omitting the start tag is bad.
+ Q_ASSERT_X(false, "QXmlTestLogger::startLogging",
+ "Insanely long test-case name or OOM issue");
+ }
}
QTestCharBuffer quotedBuild;
- xmlQuote(&quotedBuild, QLibraryInfo::build());
-
- QTest::qt_asprintf(&buf,
- "<Environment>\n"
- " <QtVersion>%s</QtVersion>\n"
- " <QtBuild>%s</QtBuild>\n"
- " <QTestVersion>" QTEST_VERSION_STR "</QTestVersion>\n"
- "</Environment>\n", qVersion(), quotedBuild.constData());
- outputString(buf.constData());
+ if (!QLibraryInfo::build() || xmlQuote(&quotedBuild, QLibraryInfo::build())) {
+ QTest::qt_asprintf(&buf,
+ " <Environment>\n"
+ " <QtVersion>%s</QtVersion>\n"
+ " <QtBuild>%s</QtBuild>\n"
+ " <QTestVersion>" QTEST_VERSION_STR "</QTestVersion>\n"
+ " </Environment>\n", qVersion(), quotedBuild.constData());
+ outputString(buf.constData());
+ }
}
void QXmlTestLogger::stopLogging()
{
QTestCharBuffer buf;
- QTest::qt_asprintf(&buf, "<Duration msecs=\"%s\"/>\n",
+ QTest::qt_asprintf(&buf, " <Duration msecs=\"%s\"/>\n",
QString::number(QTestLog::msecsTotalTime()).toUtf8().constData());
outputString(buf.constData());
- if (xmlmode == QXmlTestLogger::Complete) {
+ if (xmlmode == QXmlTestLogger::Complete)
outputString("</TestCase>\n");
- }
QAbstractTestLogger::stopLogging();
}
void QXmlTestLogger::enterTestFunction(const char *function)
{
- QTestCharBuffer buf;
QTestCharBuffer quotedFunction;
- xmlQuote(&quotedFunction, function);
- QTest::qt_asprintf(&buf, "<TestFunction name=\"%s\">\n", quotedFunction.constData());
- outputString(buf.constData());
+ if (xmlQuote(&quotedFunction, function)) {
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf, " <TestFunction name=\"%s\">\n", quotedFunction.constData());
+ outputString(buf.constData());
+ } else {
+ // Unconditional end-tag => omitting the start tag is bad.
+ Q_ASSERT_X(false, "QXmlTestLogger::enterTestFunction",
+ "Insanely long test-function name or OOM issue");
+ }
}
void QXmlTestLogger::leaveTestFunction()
@@ -163,7 +156,7 @@ void QXmlTestLogger::leaveTestFunction()
QTestCharBuffer buf;
QTest::qt_asprintf(&buf,
" <Duration msecs=\"%s\"/>\n"
- "</TestFunction>\n",
+ " </TestFunction>\n",
QString::number(QTestLog::msecsFunctionTime()).toUtf8().constData());
outputString(buf.constData());
@@ -181,45 +174,45 @@ static const char *incidentFormatString(bool noDescription, bool noTag)
{
if (noDescription) {
return noTag
- ? "<Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"
- : "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
- " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
- "</Incident>\n";
+ ? " <Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"
+ : " <Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
+ " </Incident>\n";
}
return noTag
- ? "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
- " <Description><![CDATA[%s%s%s%s]]></Description>\n"
- "</Incident>\n"
- : "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
- " <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
- " <Description><![CDATA[%s]]></Description>\n"
- "</Incident>\n";
+ ? " <Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ " <Description><![CDATA[%s%s%s%s]]></Description>\n"
+ " </Incident>\n"
+ : " <Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ " <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
+ " <Description><![CDATA[%s]]></Description>\n"
+ " </Incident>\n";
}
static const char *benchmarkResultFormatString()
{
- return "<BenchmarkResult metric=\"%s\" tag=\"%s\" value=\"%s\" iterations=\"%d\" />\n";
+ return " <BenchmarkResult metric=\"%s\" tag=\"%s\" value=\"%.6g\" iterations=\"%d\" />\n";
}
static const char *messageFormatString(bool noDescription, bool noTag)
{
if (noDescription) {
if (noTag)
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\" />\n";
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\" />\n";
else
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
" <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
- "</Message>\n";
+ " </Message>\n";
} else {
if (noTag)
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
" <Description><![CDATA[%s%s%s%s]]></Description>\n"
- "</Message>\n";
+ " </Message>\n";
else
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
" <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
" <Description><![CDATA[%s]]></Description>\n"
- "</Message>\n";
+ " </Message>\n";
}
}
@@ -239,42 +232,40 @@ void QXmlTestLogger::addIncident(IncidentTypes type, const char *description,
QTestCharBuffer cdataTag;
QTestCharBuffer cdataDescription;
- xmlQuote(&quotedFile, file);
- xmlCdata(&cdataGtag, gtag);
- xmlCdata(&cdataTag, tag);
- xmlCdata(&cdataDescription, description);
+ if (xmlQuote(&quotedFile, file)
+ && xmlCdata(&cdataGtag, gtag)
+ && xmlCdata(&cdataTag, tag)
+ && xmlCdata(&cdataDescription, description)) {
- QTest::qt_asprintf(&buf,
- QTest::incidentFormatString(QTest::isEmpty(description), notag),
- QTest::xmlIncidentType2String(type),
- quotedFile.constData(), line,
- cdataGtag.constData(),
- filler,
- cdataTag.constData(),
- cdataDescription.constData());
+ QTest::qt_asprintf(&buf,
+ QTest::incidentFormatString(QTest::isEmpty(description), notag),
+ QTest::xmlIncidentType2String(type),
+ quotedFile.constData(), line,
+ cdataGtag.constData(),
+ filler,
+ cdataTag.constData(),
+ cdataDescription.constData());
- outputString(buf.constData());
+ outputString(buf.constData());
+ }
}
void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
{
- QTestCharBuffer buf;
QTestCharBuffer quotedMetric;
QTestCharBuffer quotedTag;
- xmlQuote(&quotedMetric,
- benchmarkMetricName(result.metric));
- xmlQuote(&quotedTag, result.context.tag.toUtf8().constData());
-
- const qreal valuePerIteration = qreal(result.value) / qreal(result.iterations);
- QTest::qt_asprintf(
- &buf,
- QTest::benchmarkResultFormatString(),
- quotedMetric.constData(),
- quotedTag.constData(),
- QByteArray::number(valuePerIteration).constData(), //no 64-bit qsnprintf support
- result.iterations);
- outputString(buf.constData());
+ if (xmlQuote(&quotedMetric, benchmarkMetricName(result.measurement.metric))
+ && xmlQuote(&quotedTag, result.context.tag.toUtf8().constData())) {
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf,
+ QTest::benchmarkResultFormatString(),
+ quotedMetric.constData(),
+ quotedTag.constData(),
+ result.measurement.value / double(result.iterations),
+ result.iterations);
+ outputString(buf.constData());
+ }
}
void QXmlTestLogger::addMessage(MessageTypes type, const QString &message,
@@ -291,54 +282,52 @@ void QXmlTestLogger::addMessage(MessageTypes type, const QString &message,
QTestCharBuffer cdataTag;
QTestCharBuffer cdataDescription;
- xmlQuote(&quotedFile, file);
- xmlCdata(&cdataGtag, gtag);
- xmlCdata(&cdataTag, tag);
- xmlCdata(&cdataDescription, message.toUtf8().constData());
-
- QTest::qt_asprintf(&buf,
- QTest::messageFormatString(message.isEmpty(), notag),
- QTest::xmlMessageType2String(type),
- quotedFile.constData(), line,
- cdataGtag.constData(),
- filler,
- cdataTag.constData(),
- cdataDescription.constData());
+ if (xmlQuote(&quotedFile, file)
+ && xmlCdata(&cdataGtag, gtag)
+ && xmlCdata(&cdataTag, tag)
+ && xmlCdata(&cdataDescription, message.toUtf8().constData())) {
+ QTest::qt_asprintf(&buf,
+ QTest::messageFormatString(message.isEmpty(), notag),
+ QTest::xmlMessageType2String(type),
+ quotedFile.constData(), line,
+ cdataGtag.constData(),
+ filler,
+ cdataTag.constData(),
+ cdataDescription.constData());
- outputString(buf.constData());
+ outputString(buf.constData());
+ }
}
-/*
- Copy up to n characters from the src string into dest, escaping any special
- XML characters as necessary so that dest is suitable for use in an XML
- quoted attribute string.
-*/
-int QXmlTestLogger::xmlQuote(QTestCharBuffer* destBuf, char const* src, size_t n)
+int QXmlTestLogger::xmlQuote(QTestCharBuffer *destBuf, char const *src, qsizetype n)
{
- if (n == 0) return 0;
-
+ // QTestCharBuffer initially has size 512, with '\0' at the start of its
+ // data; and we only grow it.
+ Q_ASSERT(n >= 512 && destBuf->size() == n);
char *dest = destBuf->data();
- *dest = 0;
- if (!src) return 0;
- char* begin = dest;
- char* end = dest + n;
+ if (!src || !*src) {
+ Q_ASSERT(!dest[0]);
+ return 0;
+ }
+
+ char *begin = dest;
+ char *end = dest + n;
while (dest < end) {
switch (*src) {
-#define MAP_ENTITY(chr, ent) \
- case chr: \
- if (dest + sizeof(ent) < end) { \
- strcpy(dest, ent); \
- dest += sizeof(ent) - 1; \
- } \
- else { \
- *dest = 0; \
- return (dest+sizeof(ent)-begin); \
- } \
- ++src; \
- break;
+#define MAP_ENTITY(chr, ent) \
+ case chr: \
+ if (dest + sizeof(ent) < end) { \
+ strcpy(dest, ent); \
+ dest += sizeof(ent) - 1; \
+ } else { \
+ *dest = '\0'; \
+ return dest + sizeof(ent) - begin; \
+ } \
+ ++src; \
+ break;
MAP_ENTITY('>', "&gt;");
MAP_ENTITY('<', "&lt;");
@@ -346,64 +335,60 @@ int QXmlTestLogger::xmlQuote(QTestCharBuffer* destBuf, char const* src, size_t n
MAP_ENTITY('"', "&quot;");
MAP_ENTITY('&', "&amp;");
- // not strictly necessary, but allows handling of comments without
+ // Not strictly necessary, but allows handling of comments without
// having to explicitly look for `--'
MAP_ENTITY('-', "&#x002D;");
#undef MAP_ENTITY
- case 0:
- *dest = 0;
- return (dest-begin);
+ case '\0':
+ *dest = '\0';
+ return dest - begin;
- default:
- *dest = *src;
- ++dest;
- ++src;
- break;
+ default:
+ *dest = *src;
+ ++dest;
+ ++src;
+ break;
}
}
- // If we get here, dest was completely filled (dest == end)
- *(dest-1) = 0;
- return (dest-begin);
+ // If we get here, dest was completely filled:
+ Q_ASSERT(dest == end && end > begin);
+ dest[-1] = '\0'; // hygiene, but it'll be ignored
+ return n;
}
-/*
- Copy up to n characters from the src string into dest, escaping any
- special strings such that dest is suitable for use in an XML CDATA section.
-*/
-int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const* src, size_t n)
+int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const *src, qsizetype n)
{
- if (!n) return 0;
-
+ Q_ASSERT(n >= 512 && destBuf->size() == n);
char *dest = destBuf->data();
- if (!src || n == 1) {
- *dest = 0;
+ if (!src || !*src) {
+ Q_ASSERT(!dest[0]);
return 0;
}
static char const CDATA_END[] = "]]>";
static char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>";
+ const size_t CDATA_END_LEN = sizeof(CDATA_END) - 1;
- char* begin = dest;
- char* end = dest + n;
+ char *begin = dest;
+ char *end = dest + n;
while (dest < end) {
if (!*src) {
- *dest = 0;
- return (dest-begin);
+ *dest = '\0';
+ return dest - begin;
}
- if (!strncmp(src, CDATA_END, sizeof(CDATA_END)-1)) {
+ if (!strncmp(src, CDATA_END, CDATA_END_LEN)) {
if (dest + sizeof(CDATA_END_ESCAPED) < end) {
strcpy(dest, CDATA_END_ESCAPED);
- src += sizeof(CDATA_END)-1;
+ src += CDATA_END_LEN;
dest += sizeof(CDATA_END_ESCAPED) - 1;
- }
- else {
- *dest = 0;
- return (dest+sizeof(CDATA_END_ESCAPED)-begin);
+ } else {
+ *dest = '\0';
+ return dest + sizeof(CDATA_END_ESCAPED) - begin;
}
continue;
}
@@ -413,50 +398,61 @@ int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const* src, size_t n
++dest;
}
- // If we get here, dest was completely filled (dest == end)
- *(dest-1) = 0;
- return (dest-begin);
+ // If we get here, dest was completely filled; caller shall grow and retry:
+ Q_ASSERT(dest == end && end > begin);
+ dest[-1] = '\0'; // hygiene, but it'll be ignored
+ return n;
}
-typedef int (*StringFormatFunction)(QTestCharBuffer*,char const*,size_t);
+typedef int (*StringFormatFunction)(QTestCharBuffer *, char const *, qsizetype);
/*
A wrapper for string functions written to work with a fixed size buffer so they can be called
with a dynamically allocated buffer.
*/
-int allocateStringFn(QTestCharBuffer* str, char const* src, StringFormatFunction func)
+static bool allocateStringFn(QTestCharBuffer *str, char const *src, StringFormatFunction func)
{
- static const int MAXSIZE = 1024*1024*2;
-
+ constexpr int MAXSIZE = 1024 * 1024 * 2;
int size = str->size();
+ Q_ASSERT(size >= 512 && !str->data()[0]);
- int res = 0;
-
- for (;;) {
- res = func(str, src, size);
- str->data()[size - 1] = '\0';
- if (res < size) {
- // We succeeded or fatally failed
- break;
+ do {
+ const int res = func(str, src, size);
+ if (res < size) { // Success
+ Q_ASSERT(res > 0 || (!res && (!src || !src[0])));
+ return true;
}
- // buffer wasn't big enough, try again
+
+ // Buffer wasn't big enough, try again, if not too big:
size *= 2;
- if (size > MAXSIZE) {
- break;
- }
- if (!str->reset(size))
- break; // ran out of memory - bye
- }
+ } while (size <= MAXSIZE && str->reset(size));
- return res;
+ return false;
}
-int QXmlTestLogger::xmlQuote(QTestCharBuffer* str, char const* src)
+/*
+ Copy from \a src into \a destBuf, escaping any special XML characters as
+ necessary so that destBuf is suitable for use in an XML quoted attribute
+ string. Expands \a destBuf as needed to make room, up to a size of 2
+ MiB. Input requiring more than that much space for output is considered
+ invalid.
+
+ Returns 0 on invalid or empty input, the actual length written on success.
+*/
+bool QXmlTestLogger::xmlQuote(QTestCharBuffer *str, char const *src)
{
return allocateStringFn(str, src, QXmlTestLogger::xmlQuote);
}
-int QXmlTestLogger::xmlCdata(QTestCharBuffer* str, char const* src)
+/*
+ Copy from \a src into \a destBuf, escaping any special strings such that
+ destBuf is suitable for use in an XML CDATA section. Expands \a destBuf as
+ needed to make room, up to a size of 2 MiB. Input requiring more than that
+ much space for output is considered invalid.
+
+ Returns 0 on invalid or empty input, the actual length written on success.
+*/
+bool QXmlTestLogger::xmlCdata(QTestCharBuffer *str, char const *src)
{
return allocateStringFn(str, src, QXmlTestLogger::xmlCdata);
}