diff options
Diffstat (limited to 'src/testlib/qxmltestlogger.cpp')
-rw-r--r-- | src/testlib/qxmltestlogger.cpp | 285 |
1 files changed, 141 insertions, 144 deletions
diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp index 89c4cbb765..27da73ba52 100644 --- a/src/testlib/qxmltestlogger.cpp +++ b/src/testlib/qxmltestlogger.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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> @@ -56,22 +20,20 @@ namespace QTest { static const char *xmlMessageType2String(QAbstractTestLogger::MessageTypes type) { switch (type) { - case QAbstractTestLogger::Warn: - return "warn"; - case QAbstractTestLogger::QCritical: - return "qcritical"; 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 "??????"; } @@ -79,6 +41,8 @@ namespace QTest { 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("edTc, 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("edTc, 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("edBuild, 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("edBuild, 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("edFunction, function); - QTest::qt_asprintf(&buf, "<TestFunction name=\"%s\">\n", quotedFunction.constData()); - outputString(buf.constData()); + if (xmlQuote("edFunction, 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=\"%.6g\" 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,41 +232,40 @@ void QXmlTestLogger::addIncident(IncidentTypes type, const char *description, QTestCharBuffer cdataTag; QTestCharBuffer cdataDescription; - xmlQuote("edFile, file); - xmlCdata(&cdataGtag, gtag); - xmlCdata(&cdataTag, tag); - xmlCdata(&cdataDescription, description); + if (xmlQuote("edFile, 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("edMetric, - benchmarkMetricName(result.metric)); - xmlQuote("edTag, result.context.tag.toUtf8().constData()); - - QTest::qt_asprintf( - &buf, - QTest::benchmarkResultFormatString(), - quotedMetric.constData(), - quotedTag.constData(), - result.value / double(result.iterations), - result.iterations); - outputString(buf.constData()); + if (xmlQuote("edMetric, benchmarkMetricName(result.measurement.metric)) + && xmlQuote("edTag, 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, @@ -290,30 +282,32 @@ void QXmlTestLogger::addMessage(MessageTypes type, const QString &message, QTestCharBuffer cdataTag; QTestCharBuffer cdataDescription; - xmlQuote("edFile, 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("edFile, 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()); + } } int QXmlTestLogger::xmlQuote(QTestCharBuffer *destBuf, char const *src, qsizetype n) { - Q_ASSERT(n > 0 && destBuf->size() >= n); - + // 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(); - if (!src || n == 1) { - *dest = '\0'; + + if (!src || !*src) { + Q_ASSERT(!dest[0]); return 0; } @@ -359,19 +353,19 @@ int QXmlTestLogger::xmlQuote(QTestCharBuffer *destBuf, char const *src, qsizetyp } } - // 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; } int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const *src, qsizetype n) { - Q_ASSERT(n > 0 && destBuf->size() >= n); - + 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; } @@ -404,10 +398,10 @@ int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const *src, qsizetyp ++dest; } - // If we get here, dest was completely filled: + // If we get here, dest was completely filled; caller shall grow and retry: Q_ASSERT(dest == end && end > begin); - dest[-1] = '\0'; - return dest - begin; + dest[-1] = '\0'; // hygiene, but it'll be ignored + return n; } typedef int (*StringFormatFunction)(QTestCharBuffer *, char const *, qsizetype); @@ -416,21 +410,24 @@ 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) { constexpr int MAXSIZE = 1024 * 1024 * 2; int size = str->size(); + Q_ASSERT(size >= 512 && !str->data()[0]); do { const int res = func(str, src, size); - if (res < size) // Success or fatal failure - return res; + if (res < size) { // Success + Q_ASSERT(res > 0 || (!res && (!src || !src[0]))); + return true; + } // Buffer wasn't big enough, try again, if not too big: size *= 2; } while (size <= MAXSIZE && str->reset(size)); - return 0; + return false; } /* @@ -442,7 +439,7 @@ int allocateStringFn(QTestCharBuffer *str, char const *src, StringFormatFunction Returns 0 on invalid or empty input, the actual length written on success. */ -int QXmlTestLogger::xmlQuote(QTestCharBuffer *str, char const *src) +bool QXmlTestLogger::xmlQuote(QTestCharBuffer *str, char const *src) { return allocateStringFn(str, src, QXmlTestLogger::xmlQuote); } @@ -455,7 +452,7 @@ int QXmlTestLogger::xmlQuote(QTestCharBuffer *str, char const *src) Returns 0 on invalid or empty input, the actual length written on success. */ -int QXmlTestLogger::xmlCdata(QTestCharBuffer *str, char const *src) +bool QXmlTestLogger::xmlCdata(QTestCharBuffer *str, char const *src) { return allocateStringFn(str, src, QXmlTestLogger::xmlCdata); } |