src/testlib/qxmltestlogger.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtTest module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <stdio.h>
       
    43 #include <string.h>
       
    44 #include <QtCore/qglobal.h>
       
    45 
       
    46 #include "QtTest/private/qxmltestlogger_p.h"
       
    47 #include "QtTest/private/qtestresult_p.h"
       
    48 #include "QtTest/private/qbenchmark_p.h"
       
    49 #include "QtTest/qtestcase.h"
       
    50 
       
    51 QT_BEGIN_NAMESPACE
       
    52 
       
    53 namespace QTest {
       
    54 
       
    55     static const char* xmlMessageType2String(QAbstractTestLogger::MessageTypes type)
       
    56     {
       
    57         switch (type) {
       
    58         case QAbstractTestLogger::Warn:
       
    59             return "warn";
       
    60         case QAbstractTestLogger::QSystem:
       
    61             return "system";
       
    62         case QAbstractTestLogger::QDebug:
       
    63             return "qdebug";
       
    64         case QAbstractTestLogger::QWarning:
       
    65             return "qwarn";
       
    66         case QAbstractTestLogger::QFatal:
       
    67             return "qfatal";
       
    68         case QAbstractTestLogger::Skip:
       
    69             return "skip";
       
    70         case QAbstractTestLogger::Info:
       
    71             return "info";
       
    72         }
       
    73         return "??????";
       
    74     }
       
    75 
       
    76     static const char* xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type)
       
    77     {
       
    78         switch (type) {
       
    79         case QAbstractTestLogger::Pass:
       
    80             return "pass";
       
    81         case QAbstractTestLogger::XFail:
       
    82             return "xfail";
       
    83         case QAbstractTestLogger::Fail:
       
    84             return "fail";
       
    85         case QAbstractTestLogger::XPass:
       
    86             return "xpass";
       
    87         }
       
    88         return "??????";
       
    89     }
       
    90 
       
    91 }
       
    92 
       
    93 
       
    94 QXmlTestLogger::QXmlTestLogger(XmlMode mode )
       
    95     :xmlmode(mode)
       
    96 {
       
    97 
       
    98 }
       
    99 
       
   100 QXmlTestLogger::~QXmlTestLogger()
       
   101 {
       
   102 }
       
   103 
       
   104 void QXmlTestLogger::startLogging()
       
   105 {
       
   106     QAbstractTestLogger::startLogging();
       
   107     QTestCharBuffer buf;
       
   108 
       
   109     if (xmlmode == QXmlTestLogger::Complete) {
       
   110         QTestCharBuffer quotedTc;
       
   111         xmlQuote(&quotedTc, QTestResult::currentTestObjectName());
       
   112         QTest::qt_asprintf(&buf,
       
   113                 "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
       
   114                 "<TestCase name=\"%s\">\n", quotedTc.constData());
       
   115         outputString(buf.constData());
       
   116     }
       
   117 
       
   118     QTest::qt_asprintf(&buf,
       
   119             "<Environment>\n"
       
   120             "    <QtVersion>%s</QtVersion>\n"
       
   121             "    <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n"
       
   122             "</Environment>\n", qVersion());
       
   123     outputString(buf.constData());
       
   124 }
       
   125 
       
   126 void QXmlTestLogger::stopLogging()
       
   127 {
       
   128     if (xmlmode == QXmlTestLogger::Complete) {
       
   129         outputString("</TestCase>\n");
       
   130     }
       
   131 
       
   132     QAbstractTestLogger::stopLogging();
       
   133 }
       
   134 
       
   135 void QXmlTestLogger::enterTestFunction(const char *function)
       
   136 {
       
   137     QTestCharBuffer buf;
       
   138     QTestCharBuffer quotedFunction;
       
   139     xmlQuote(&quotedFunction, function);
       
   140     QTest::qt_asprintf(&buf, "<TestFunction name=\"%s\">\n", quotedFunction.constData());
       
   141     outputString(buf.constData());
       
   142 }
       
   143 
       
   144 void QXmlTestLogger::leaveTestFunction()
       
   145 {
       
   146     outputString("</TestFunction>\n");
       
   147 }
       
   148 
       
   149 namespace QTest
       
   150 {
       
   151 
       
   152 inline static bool isEmpty(const char *str)
       
   153 {
       
   154     return !str || !str[0];
       
   155 }
       
   156 
       
   157 static const char *incidentFormatString(bool noDescription, bool noTag)
       
   158 {
       
   159     if (noDescription) {
       
   160         if (noTag)
       
   161             return "<Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n";
       
   162         else
       
   163             return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
       
   164                 "    <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
       
   165                 "</Incident>\n";
       
   166     } else {
       
   167         if (noTag)
       
   168             return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
       
   169                 "    <Description><![CDATA[%s%s%s%s]]></Description>\n"
       
   170                 "</Incident>\n";
       
   171         else
       
   172             return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
       
   173                 "    <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
       
   174                 "    <Description><![CDATA[%s]]></Description>\n"
       
   175                 "</Incident>\n";
       
   176     }
       
   177 }
       
   178 
       
   179 static const char *benchmarkResultFormatString()
       
   180 {
       
   181     return "<BenchmarkResult metric=\"%s\" tag=\"%s\" value=\"%s\" iterations=\"%d\" />\n";
       
   182 }
       
   183 
       
   184 static const char *messageFormatString(bool noDescription, bool noTag)
       
   185 {
       
   186     if (noDescription) {
       
   187         if (noTag)
       
   188             return "<Message type=\"%s\" file=\"%s\" line=\"%d\" />\n";
       
   189         else
       
   190             return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
       
   191                 "    <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
       
   192                 "</Message>\n";
       
   193     } else {
       
   194         if (noTag)
       
   195             return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
       
   196                 "    <Description><![CDATA[%s%s%s%s]]></Description>\n"
       
   197                 "</Message>\n";
       
   198         else
       
   199             return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
       
   200                 "    <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
       
   201                 "    <Description><![CDATA[%s]]></Description>\n"
       
   202                 "</Message>\n";
       
   203     }
       
   204 }
       
   205 
       
   206 } // namespace
       
   207 
       
   208 void QXmlTestLogger::addIncident(IncidentTypes type, const char *description,
       
   209                                 const char *file, int line)
       
   210 {
       
   211     QTestCharBuffer buf;
       
   212     const char *tag = QTestResult::currentDataTag();
       
   213     const char *gtag = QTestResult::currentGlobalDataTag();
       
   214     const char *filler = (tag && gtag) ? ":" : "";
       
   215     const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag);
       
   216 
       
   217     QTestCharBuffer quotedFile;
       
   218     QTestCharBuffer cdataGtag;
       
   219     QTestCharBuffer cdataTag;
       
   220     QTestCharBuffer cdataDescription;
       
   221 
       
   222     xmlQuote(&quotedFile, file);
       
   223     xmlCdata(&cdataGtag, gtag);
       
   224     xmlCdata(&cdataTag, tag);
       
   225     xmlCdata(&cdataDescription, description);
       
   226 
       
   227     QTest::qt_asprintf(&buf,
       
   228             QTest::incidentFormatString(QTest::isEmpty(description), notag),
       
   229             QTest::xmlIncidentType2String(type),
       
   230             quotedFile.constData(), line,
       
   231             cdataGtag.constData(),
       
   232             filler,
       
   233             cdataTag.constData(),
       
   234             cdataDescription.constData());
       
   235 
       
   236     outputString(buf.constData());
       
   237 }
       
   238 
       
   239 void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
       
   240 {
       
   241     QTestCharBuffer buf;
       
   242     QTestCharBuffer quotedMetric;
       
   243     QTestCharBuffer quotedTag;
       
   244 
       
   245     xmlQuote(&quotedMetric,
       
   246         QBenchmarkGlobalData::current->measurer->metricText().toAscii().constData());
       
   247     xmlQuote(&quotedTag, result.context.tag.toAscii().constData());
       
   248 
       
   249     QTest::qt_asprintf(
       
   250         &buf,
       
   251         QTest::benchmarkResultFormatString(),
       
   252         quotedMetric.constData(),
       
   253         quotedTag.constData(),
       
   254         QByteArray::number(result.value).constData(),  //no 64-bit qt_snprintf support
       
   255         result.iterations);
       
   256     outputString(buf.constData());
       
   257 }
       
   258 
       
   259 void QXmlTestLogger::addMessage(MessageTypes type, const char *message,
       
   260                                 const char *file, int line)
       
   261 {
       
   262     QTestCharBuffer buf;
       
   263     const char *tag = QTestResult::currentDataTag();
       
   264     const char *gtag = QTestResult::currentGlobalDataTag();
       
   265     const char *filler = (tag && gtag) ? ":" : "";
       
   266     const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag);
       
   267 
       
   268     QTestCharBuffer quotedFile;
       
   269     QTestCharBuffer cdataGtag;
       
   270     QTestCharBuffer cdataTag;
       
   271     QTestCharBuffer cdataDescription;
       
   272 
       
   273     xmlQuote(&quotedFile, file);
       
   274     xmlCdata(&cdataGtag, gtag);
       
   275     xmlCdata(&cdataTag, tag);
       
   276     xmlCdata(&cdataDescription, message);
       
   277 
       
   278     QTest::qt_asprintf(&buf,
       
   279             QTest::messageFormatString(QTest::isEmpty(message), notag),
       
   280             QTest::xmlMessageType2String(type),
       
   281             quotedFile.constData(), line,
       
   282             cdataGtag.constData(),
       
   283             filler,
       
   284             cdataTag.constData(),
       
   285             cdataDescription.constData());
       
   286 
       
   287     outputString(buf.constData());
       
   288 }
       
   289 
       
   290 /*
       
   291     Copy up to n characters from the src string into dest, escaping any special
       
   292     XML characters as necessary so that dest is suitable for use in an XML
       
   293     quoted attribute string.
       
   294 */
       
   295 int QXmlTestLogger::xmlQuote(QTestCharBuffer* destBuf, char const* src, size_t n)
       
   296 {
       
   297     if (n == 0) return 0;
       
   298 
       
   299     char *dest = destBuf->data();
       
   300     *dest = 0;
       
   301     if (!src) return 0;
       
   302 
       
   303     char* begin = dest;
       
   304     char* end = dest + n;
       
   305 
       
   306     while (dest < end) {
       
   307         switch (*src) {
       
   308 
       
   309 #define MAP_ENTITY(chr, ent) \
       
   310             case chr:                                   \
       
   311                 if (dest + sizeof(ent) < end) {         \
       
   312                     strcpy(dest, ent);                  \
       
   313                     dest += sizeof(ent) - 1;            \
       
   314                 }                                       \
       
   315                 else {                                  \
       
   316                     *dest = 0;                          \
       
   317                     return (dest+sizeof(ent)-begin);    \
       
   318                 }                                       \
       
   319                 ++src;                                  \
       
   320                 break;
       
   321 
       
   322             MAP_ENTITY('>', "&gt;");
       
   323             MAP_ENTITY('<', "&lt;");
       
   324             MAP_ENTITY('\'', "&apos;");
       
   325             MAP_ENTITY('"', "&quot;");
       
   326             MAP_ENTITY('&', "&amp;");
       
   327 
       
   328             // not strictly necessary, but allows handling of comments without
       
   329             // having to explicitly look for `--'
       
   330             MAP_ENTITY('-', "&#x002D;");
       
   331 
       
   332 #undef MAP_ENTITY
       
   333 
       
   334             case 0:
       
   335                 *dest = 0;
       
   336                 return (dest-begin);
       
   337 
       
   338             default:
       
   339                 *dest = *src;
       
   340                 ++dest;
       
   341                 ++src;
       
   342                 break;
       
   343         }
       
   344     }
       
   345 
       
   346     // If we get here, dest was completely filled (dest == end)
       
   347     *(dest-1) = 0;
       
   348     return (dest-begin);
       
   349 }
       
   350 
       
   351 /*
       
   352     Copy up to n characters from the src string into dest, escaping any
       
   353     special strings such that dest is suitable for use in an XML CDATA section.
       
   354 */
       
   355 int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const* src, size_t n)
       
   356 {
       
   357     if (!n) return 0;
       
   358 
       
   359     char *dest = destBuf->data();
       
   360 
       
   361     if (!src || n == 1) {
       
   362         *dest = 0;
       
   363         return 0;
       
   364     }
       
   365 
       
   366     static char const CDATA_END[] = "]]>";
       
   367     static char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>";
       
   368 
       
   369     char* begin = dest;
       
   370     char* end = dest + n;
       
   371     while (dest < end) {
       
   372         if (!*src) {
       
   373             *dest = 0;
       
   374             return (dest-begin);
       
   375         }
       
   376 
       
   377         if (!strncmp(src, CDATA_END, sizeof(CDATA_END)-1)) {
       
   378             if (dest + sizeof(CDATA_END_ESCAPED) < end) {
       
   379                 strcpy(dest, CDATA_END_ESCAPED);
       
   380                 src += sizeof(CDATA_END)-1;
       
   381                 dest += sizeof(CDATA_END_ESCAPED) - 1;
       
   382             }
       
   383             else {
       
   384                 *dest = 0;
       
   385                 return (dest+sizeof(CDATA_END_ESCAPED)-begin);
       
   386             }
       
   387             continue;
       
   388         }
       
   389 
       
   390         *dest = *src;
       
   391         ++src;
       
   392         ++dest;
       
   393     }
       
   394 
       
   395     // If we get here, dest was completely filled (dest == end)
       
   396     *(dest-1) = 0;
       
   397     return (dest-begin);
       
   398 }
       
   399 
       
   400 typedef int (*StringFormatFunction)(QTestCharBuffer*,char const*,size_t);
       
   401 
       
   402 /*
       
   403     A wrapper for string functions written to work with a fixed size buffer so they can be called
       
   404     with a dynamically allocated buffer.
       
   405 */
       
   406 int allocateStringFn(QTestCharBuffer* str, char const* src, StringFormatFunction func)
       
   407 {
       
   408     static const int MAXSIZE = 1024*1024*2;
       
   409 
       
   410     int size = str->size();
       
   411 
       
   412     int res = 0;
       
   413 
       
   414     for (;;) {
       
   415         res = func(str, src, size);
       
   416         str->data()[size - 1] = '\0';
       
   417         if (res < size) {
       
   418             // We succeeded or fatally failed
       
   419             break;
       
   420         }
       
   421         // buffer wasn't big enough, try again
       
   422         size *= 2;
       
   423         if (size > MAXSIZE) {
       
   424             break;
       
   425         }
       
   426         if (!str->reset(size))
       
   427             break; // ran out of memory - bye
       
   428     }
       
   429 
       
   430     return res;
       
   431 }
       
   432 
       
   433 int QXmlTestLogger::xmlQuote(QTestCharBuffer* str, char const* src)
       
   434 {
       
   435     return allocateStringFn(str, src, QXmlTestLogger::xmlQuote);
       
   436 }
       
   437 
       
   438 int QXmlTestLogger::xmlCdata(QTestCharBuffer* str, char const* src)
       
   439 {
       
   440     return allocateStringFn(str, src, QXmlTestLogger::xmlCdata);
       
   441 }
       
   442 
       
   443 QT_END_NAMESPACE