diff options
Diffstat (limited to 'src/testlib/qxmltestlogger.cpp')
-rw-r--r-- | src/testlib/qxmltestlogger.cpp | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp new file mode 100644 index 0000000000..827b0ad472 --- /dev/null +++ b/src/testlib/qxmltestlogger.cpp @@ -0,0 +1,459 @@ +/**************************************************************************** +** +** 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 QtTest module 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 <stdio.h> +#include <string.h> +#include <QtCore/qglobal.h> + +#include "QtTest/private/qxmltestlogger_p.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qbenchmark_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" +#include "QtTest/qtestcase.h" + +QT_BEGIN_NAMESPACE + +namespace QTest { + + 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::QWarning: + return "qwarn"; + case QAbstractTestLogger::QFatal: + return "qfatal"; + case QAbstractTestLogger::Skip: + return "skip"; + case QAbstractTestLogger::Info: + return "info"; + } + return "??????"; + } + + static const char* xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type) + { + switch (type) { + case QAbstractTestLogger::Pass: + return "pass"; + case QAbstractTestLogger::XFail: + return "xfail"; + case QAbstractTestLogger::Fail: + return "fail"; + case QAbstractTestLogger::XPass: + return "xpass"; + } + return "??????"; + } + +} + + +QXmlTestLogger::QXmlTestLogger(XmlMode mode ) + :xmlmode(mode), randomSeed(0), hasRandomSeed(false) +{ + +} + +QXmlTestLogger::~QXmlTestLogger() +{ +} + +void QXmlTestLogger::startLogging() +{ + QAbstractTestLogger::startLogging(); + QTestCharBuffer buf; + + if (xmlmode == QXmlTestLogger::Complete) { + QTestCharBuffer quotedTc; + xmlQuote("edTc, QTestResult::currentTestObjectName()); + QTest::qt_asprintf(&buf, + "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" + "<TestCase name=\"%s\">\n", quotedTc.constData()); + outputString(buf.constData()); + } + + if (hasRandomSeed) { + QTest::qt_asprintf(&buf, + "<Environment>\n" + " <QtVersion>%s</QtVersion>\n" + " <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n" + " <RandomSeed>%d</RandomSeed>\n" + "</Environment>\n", qVersion(), randomSeed); + } else { + QTest::qt_asprintf(&buf, + "<Environment>\n" + " <QtVersion>%s</QtVersion>\n" + " <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n" + "</Environment>\n", qVersion()); + } + outputString(buf.constData()); +} + +void QXmlTestLogger::stopLogging() +{ + 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()); +} + +void QXmlTestLogger::leaveTestFunction() +{ + outputString("</TestFunction>\n"); +} + +namespace QTest +{ + +inline static bool isEmpty(const char *str) +{ + return !str || !str[0]; +} + +static const char *incidentFormatString(bool noDescription, bool noTag) +{ + if (noDescription) { + if (noTag) + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"; + else + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" + "</Incident>\n"; + } else { + if (noTag) + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <Description><![CDATA[%s%s%s%s]]></Description>\n" + "</Incident>\n"; + else + return "<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"; +} + +static const char *messageFormatString(bool noDescription, bool noTag) +{ + if (noDescription) { + if (noTag) + return "<Message type=\"%s\" file=\"%s\" line=\"%d\" />\n"; + else + return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" + "</Message>\n"; + } else { + if (noTag) + return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <Description><![CDATA[%s%s%s%s]]></Description>\n" + "</Message>\n"; + else + return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Message>\n"; + } +} + +} // namespace + +void QXmlTestLogger::addIncident(IncidentTypes type, const char *description, + const char *file, int line) +{ + QTestCharBuffer buf; + const char *tag = QTestResult::currentDataTag(); + const char *gtag = QTestResult::currentGlobalDataTag(); + const char *filler = (tag && gtag) ? ":" : ""; + const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); + + QTestCharBuffer quotedFile; + QTestCharBuffer cdataGtag; + QTestCharBuffer cdataTag; + QTestCharBuffer cdataDescription; + + 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()); + + 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.toAscii().constData()); + + QTest::qt_asprintf( + &buf, + QTest::benchmarkResultFormatString(), + quotedMetric.constData(), + quotedTag.constData(), + QByteArray::number(result.value).constData(), //no 64-bit qt_snprintf support + result.iterations); + outputString(buf.constData()); +} + +void QXmlTestLogger::addMessage(MessageTypes type, const char *message, + const char *file, int line) +{ + QTestCharBuffer buf; + const char *tag = QTestResult::currentDataTag(); + const char *gtag = QTestResult::currentGlobalDataTag(); + const char *filler = (tag && gtag) ? ":" : ""; + const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); + + QTestCharBuffer quotedFile; + QTestCharBuffer cdataGtag; + QTestCharBuffer cdataTag; + QTestCharBuffer cdataDescription; + + xmlQuote("edFile, file); + xmlCdata(&cdataGtag, gtag); + xmlCdata(&cdataTag, tag); + xmlCdata(&cdataDescription, message); + + QTest::qt_asprintf(&buf, + QTest::messageFormatString(QTest::isEmpty(message), notag), + QTest::xmlMessageType2String(type), + quotedFile.constData(), line, + cdataGtag.constData(), + filler, + cdataTag.constData(), + cdataDescription.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) +{ + if (n == 0) return 0; + + char *dest = destBuf->data(); + *dest = 0; + if (!src) 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; + + MAP_ENTITY('>', ">"); + MAP_ENTITY('<', "<"); + MAP_ENTITY('\'', "'"); + MAP_ENTITY('"', """); + MAP_ENTITY('&', "&"); + + // not strictly necessary, but allows handling of comments without + // having to explicitly look for `--' + MAP_ENTITY('-', "-"); + +#undef MAP_ENTITY + + case 0: + *dest = 0; + return (dest-begin); + + default: + *dest = *src; + ++dest; + ++src; + break; + } + } + + // If we get here, dest was completely filled (dest == end) + *(dest-1) = 0; + return (dest-begin); +} + +/* + 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) +{ + if (!n) return 0; + + char *dest = destBuf->data(); + + if (!src || n == 1) { + *dest = 0; + return 0; + } + + static char const CDATA_END[] = "]]>"; + static char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>"; + + char* begin = dest; + char* end = dest + n; + while (dest < end) { + if (!*src) { + *dest = 0; + return (dest-begin); + } + + if (!strncmp(src, CDATA_END, sizeof(CDATA_END)-1)) { + if (dest + sizeof(CDATA_END_ESCAPED) < end) { + strcpy(dest, CDATA_END_ESCAPED); + src += sizeof(CDATA_END)-1; + dest += sizeof(CDATA_END_ESCAPED) - 1; + } + else { + *dest = 0; + return (dest+sizeof(CDATA_END_ESCAPED)-begin); + } + continue; + } + + *dest = *src; + ++src; + ++dest; + } + + // If we get here, dest was completely filled (dest == end) + *(dest-1) = 0; + return (dest-begin); +} + +typedef int (*StringFormatFunction)(QTestCharBuffer*,char const*,size_t); + +/* + 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 const int MAXSIZE = 1024*1024*2; + + int size = str->size(); + + int res = 0; + + for (;;) { + res = func(str, src, size); + str->data()[size - 1] = '\0'; + if (res < size) { + // We succeeded or fatally failed + break; + } + // buffer wasn't big enough, try again + size *= 2; + if (size > MAXSIZE) { + break; + } + if (!str->reset(size)) + break; // ran out of memory - bye + } + + return res; +} + +int QXmlTestLogger::xmlQuote(QTestCharBuffer* str, char const* src) +{ + return allocateStringFn(str, src, QXmlTestLogger::xmlQuote); +} + +int QXmlTestLogger::xmlCdata(QTestCharBuffer* str, char const* src) +{ + return allocateStringFn(str, src, QXmlTestLogger::xmlCdata); +} + +void QXmlTestLogger::registerRandomSeed(unsigned int seed) +{ + randomSeed = seed; + hasRandomSeed = true; +} + +QT_END_NAMESPACE |