src/testlib/qxmltestlogger.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/testlib/qxmltestlogger.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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/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)
+{
+
+}
+
+QXmlTestLogger::~QXmlTestLogger()
+{
+}
+
+void QXmlTestLogger::startLogging()
+{
+    QAbstractTestLogger::startLogging();
+    QTestCharBuffer buf;
+
+    if (xmlmode == QXmlTestLogger::Complete) {
+        QTestCharBuffer quotedTc;
+        xmlQuote(&quotedTc, QTestResult::currentTestObjectName());
+        QTest::qt_asprintf(&buf,
+                "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
+                "<TestCase name=\"%s\">\n", quotedTc.constData());
+        outputString(buf.constData());
+    }
+
+    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(&quotedFunction, 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(&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());
+
+    outputString(buf.constData());
+}
+
+void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
+{
+    QTestCharBuffer buf;
+    QTestCharBuffer quotedMetric;
+    QTestCharBuffer quotedTag;
+
+    xmlQuote(&quotedMetric,
+        QBenchmarkGlobalData::current->measurer->metricText().toAscii().constData());
+    xmlQuote(&quotedTag, 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(&quotedFile, 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('>', "&gt;");
+            MAP_ENTITY('<', "&lt;");
+            MAP_ENTITY('\'', "&apos;");
+            MAP_ENTITY('"', "&quot;");
+            MAP_ENTITY('&', "&amp;");
+
+            // 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);
+
+            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);
+}
+
+QT_END_NAMESPACE