38be0d1383
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
498 lines
16 KiB
C++
498 lines
16 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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 "QtTest/private/qtestresult_p.h"
|
|
#include "QtTest/qtestassert.h"
|
|
#include "QtTest/private/qtestlog_p.h"
|
|
#include "QtTest/private/qplaintestlogger_p.h"
|
|
#include "QtTest/private/qbenchmark_p.h"
|
|
#include "QtTest/private/qbenchmarkmetric_p.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include "windows.h"
|
|
#endif
|
|
|
|
#if defined(Q_OS_SYMBIAN)
|
|
#include <e32debug.h>
|
|
#endif
|
|
|
|
#ifdef Q_OS_WINCE
|
|
#include <QtCore/QString>
|
|
#endif
|
|
|
|
#include <QtCore/QByteArray>
|
|
#include <QtCore/qmath.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
namespace QTest {
|
|
|
|
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
|
|
|
|
static CRITICAL_SECTION outputCriticalSection;
|
|
static HANDLE hConsole = INVALID_HANDLE_VALUE;
|
|
static WORD consoleAttributes = 0;
|
|
|
|
static const char *qWinColoredMsg(int prefix, int color, const char *msg)
|
|
{
|
|
if (!hConsole)
|
|
return msg;
|
|
|
|
WORD attr = consoleAttributes & ~(FOREGROUND_GREEN | FOREGROUND_BLUE
|
|
| FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
if (prefix)
|
|
attr |= FOREGROUND_INTENSITY;
|
|
if (color == 32)
|
|
attr |= FOREGROUND_GREEN;
|
|
if (color == 36)
|
|
attr |= FOREGROUND_BLUE | FOREGROUND_GREEN;
|
|
if (color == 31)
|
|
attr |= FOREGROUND_RED | FOREGROUND_INTENSITY;
|
|
if (color == 37)
|
|
attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
if (color == 33)
|
|
attr |= FOREGROUND_RED | FOREGROUND_GREEN;
|
|
SetConsoleTextAttribute(hConsole, attr);
|
|
printf(msg);
|
|
SetConsoleTextAttribute(hConsole, consoleAttributes);
|
|
return "";
|
|
}
|
|
|
|
# define COLORED_MSG(prefix, color, msg) colored ? qWinColoredMsg(prefix, color, msg) : msg
|
|
#else
|
|
# define COLORED_MSG(prefix, color, msg) colored && QAbstractTestLogger::isTtyOutput() ? "\033["#prefix";"#color"m" msg "\033[0m" : msg
|
|
#endif
|
|
|
|
static const char *incidentType2String(QAbstractTestLogger::IncidentTypes type)
|
|
{
|
|
static bool colored = (!qgetenv("QTEST_COLORED").isEmpty());
|
|
switch (type) {
|
|
case QAbstractTestLogger::Pass:
|
|
return COLORED_MSG(0, 32, "PASS "); //green
|
|
case QAbstractTestLogger::XFail:
|
|
return COLORED_MSG(1, 32, "XFAIL "); //light green
|
|
case QAbstractTestLogger::Fail:
|
|
return COLORED_MSG(0, 31, "FAIL! "); //red
|
|
case QAbstractTestLogger::XPass:
|
|
return COLORED_MSG(0, 31, "XPASS "); //red, too
|
|
}
|
|
return "??????";
|
|
}
|
|
|
|
static const char *benchmarkResult2String()
|
|
{
|
|
static bool colored = (!qgetenv("QTEST_COLORED").isEmpty());
|
|
return COLORED_MSG(0, 36, "RESULT "); // cyan
|
|
}
|
|
|
|
static const char *messageType2String(QAbstractTestLogger::MessageTypes type)
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
static bool colored = (!qgetenv("QTEST_COLORED").isEmpty());
|
|
#else
|
|
static bool colored = ::getenv("QTEST_COLORED");
|
|
#endif
|
|
switch (type) {
|
|
case QAbstractTestLogger::Skip:
|
|
return COLORED_MSG(0, 37, "SKIP "); //white
|
|
case QAbstractTestLogger::Warn:
|
|
return COLORED_MSG(0, 33, "WARNING"); // yellow
|
|
case QAbstractTestLogger::QWarning:
|
|
return COLORED_MSG(1, 33, "QWARN ");
|
|
case QAbstractTestLogger::QDebug:
|
|
return COLORED_MSG(1, 33, "QDEBUG ");
|
|
case QAbstractTestLogger::QSystem:
|
|
return COLORED_MSG(1, 33, "QSYSTEM");
|
|
case QAbstractTestLogger::QFatal:
|
|
return COLORED_MSG(0, 31, "QFATAL "); // red
|
|
case QAbstractTestLogger::Info:
|
|
return "INFO "; // no coloring
|
|
}
|
|
return "??????";
|
|
}
|
|
|
|
static void outputMessage(const char *str)
|
|
{
|
|
#if defined(Q_OS_WINCE)
|
|
QString strUtf16 = QString::fromLatin1(str);
|
|
const int maxOutputLength = 255;
|
|
do {
|
|
QString tmp = strUtf16.left(maxOutputLength);
|
|
OutputDebugString((wchar_t*)tmp.utf16());
|
|
strUtf16.remove(0, maxOutputLength);
|
|
} while (!strUtf16.isEmpty());
|
|
if (QTestLog::outputFileName())
|
|
#elif defined(Q_OS_WIN)
|
|
EnterCriticalSection(&outputCriticalSection);
|
|
// OutputDebugString is not threadsafe
|
|
OutputDebugStringA(str);
|
|
LeaveCriticalSection(&outputCriticalSection);
|
|
#elif defined(Q_OS_SYMBIAN)
|
|
// RDebug::Print has a cap of 256 characters so break it up
|
|
TPtrC8 ptr(reinterpret_cast<const TUint8*>(str));
|
|
_LIT(format, "[QTestLib] %S");
|
|
const int maxBlockSize = 256 - ((const TDesC &)format).Length();
|
|
HBufC* hbuffer = HBufC::New(maxBlockSize);
|
|
if(hbuffer) {
|
|
for (int i = 0; i < ptr.Length(); i += maxBlockSize) {
|
|
int size = Min(maxBlockSize, ptr.Length() - i);
|
|
hbuffer->Des().Copy(ptr.Mid(i, size));
|
|
RDebug::Print(format, hbuffer);
|
|
}
|
|
delete hbuffer;
|
|
}
|
|
else {
|
|
// fast, no allocations, but truncates silently
|
|
RDebug::RawPrint(format);
|
|
TPtrC8 ptr(reinterpret_cast<const TUint8*>(str));
|
|
RDebug::RawPrint(ptr);
|
|
RDebug::RawPrint(_L8("\n"));
|
|
}
|
|
#endif
|
|
QAbstractTestLogger::outputString(str);
|
|
}
|
|
|
|
static void printMessage(const char *type, const char *msg, const char *file = 0, int line = 0)
|
|
{
|
|
QTEST_ASSERT(type);
|
|
QTEST_ASSERT(msg);
|
|
|
|
QTestCharBuffer buf;
|
|
|
|
const char *fn = QTestResult::currentTestFunction() ? QTestResult::currentTestFunction()
|
|
: "UnknownTestFunc";
|
|
const char *tag = QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "";
|
|
const char *gtag = QTestResult::currentGlobalDataTag()
|
|
? QTestResult::currentGlobalDataTag()
|
|
: "";
|
|
const char *filler = (tag[0] && gtag[0]) ? ":" : "";
|
|
if (file) {
|
|
QTest::qt_asprintf(&buf, "%s: %s::%s(%s%s%s)%s%s\n"
|
|
#ifdef Q_OS_WIN
|
|
"%s(%d) : failure location\n"
|
|
#else
|
|
" Loc: [%s(%d)]\n"
|
|
#endif
|
|
, type, QTestResult::currentTestObjectName(), fn, gtag, filler, tag,
|
|
msg[0] ? " " : "", msg, file, line);
|
|
} else {
|
|
QTest::qt_asprintf(&buf, "%s: %s::%s(%s%s%s)%s%s\n",
|
|
type, QTestResult::currentTestObjectName(), fn, gtag, filler, tag,
|
|
msg[0] ? " " : "", msg);
|
|
}
|
|
// In colored mode, printf above stripped our nonprintable control characters.
|
|
// Put them back.
|
|
memcpy(buf.data(), type, strlen(type));
|
|
outputMessage(buf.data());
|
|
}
|
|
|
|
template <typename T>
|
|
static int countSignificantDigits(T num)
|
|
{
|
|
if (num <= 0)
|
|
return 0;
|
|
|
|
int digits = 0;
|
|
qreal divisor = 1;
|
|
|
|
while (num / divisor >= 1) {
|
|
divisor *= 10;
|
|
++digits;
|
|
}
|
|
|
|
return digits;
|
|
}
|
|
|
|
// Pretty-prints a benchmark result using the given number of digits.
|
|
template <typename T> QString formatResult(T number, int significantDigits)
|
|
{
|
|
if (number < T(0))
|
|
return QLatin1String("NAN");
|
|
if (number == T(0))
|
|
return QLatin1String("0");
|
|
|
|
QString beforeDecimalPoint = QString::number(qint64(number), 'f', 0);
|
|
QString afterDecimalPoint = QString::number(number, 'f', 20);
|
|
afterDecimalPoint.remove(0, beforeDecimalPoint.count() + 1);
|
|
|
|
int beforeUse = qMin(beforeDecimalPoint.count(), significantDigits);
|
|
int beforeRemove = beforeDecimalPoint.count() - beforeUse;
|
|
|
|
// Replace insignificant digits before the decimal point with zeros.
|
|
beforeDecimalPoint.chop(beforeRemove);
|
|
for (int i = 0; i < beforeRemove; ++i) {
|
|
beforeDecimalPoint.append(QLatin1Char('0'));
|
|
}
|
|
|
|
int afterUse = significantDigits - beforeUse;
|
|
|
|
// leading zeroes after the decimal point does not count towards the digit use.
|
|
if (beforeDecimalPoint == QLatin1String("0") && afterDecimalPoint.isEmpty() == false) {
|
|
++afterUse;
|
|
|
|
int i = 0;
|
|
while (i < afterDecimalPoint.count() && afterDecimalPoint.at(i) == QLatin1Char('0')) {
|
|
++i;
|
|
}
|
|
|
|
afterUse += i;
|
|
}
|
|
|
|
int afterRemove = afterDecimalPoint.count() - afterUse;
|
|
afterDecimalPoint.chop(afterRemove);
|
|
|
|
QChar separator = QLatin1Char(',');
|
|
QChar decimalPoint = QLatin1Char('.');
|
|
|
|
// insert thousands separators
|
|
int length = beforeDecimalPoint.length();
|
|
for (int i = beforeDecimalPoint.length() -1; i >= 1; --i) {
|
|
if ((length - i) % 3 == 0)
|
|
beforeDecimalPoint.insert(i, separator);
|
|
}
|
|
|
|
QString print;
|
|
print = beforeDecimalPoint;
|
|
if (afterUse > 0)
|
|
print.append(decimalPoint);
|
|
|
|
print += afterDecimalPoint;
|
|
|
|
|
|
return print;
|
|
}
|
|
|
|
template <typename T>
|
|
int formatResult(char * buffer, int bufferSize, T number, int significantDigits)
|
|
{
|
|
QString result = formatResult(number, significantDigits);
|
|
qstrncpy(buffer, result.toAscii().constData(), bufferSize);
|
|
int size = result.count();
|
|
return size;
|
|
}
|
|
|
|
// static void printBenchmarkResult(const char *bmtag, int value, int iterations)
|
|
static void printBenchmarkResult(const QBenchmarkResult &result)
|
|
{
|
|
const char *bmtag = QTest::benchmarkResult2String();
|
|
|
|
char buf1[1024];
|
|
QTest::qt_snprintf(
|
|
buf1, sizeof(buf1), "%s: %s::%s",
|
|
bmtag,
|
|
QTestResult::currentTestObjectName(),
|
|
result.context.slotName.toAscii().data());
|
|
|
|
char bufTag[1024];
|
|
bufTag[0] = 0;
|
|
QByteArray tag = result.context.tag.toAscii();
|
|
if (tag.isEmpty() == false) {
|
|
QTest::qt_snprintf(bufTag, sizeof(bufTag), ":\"%s\"", tag.data());
|
|
}
|
|
|
|
|
|
char fillFormat[8];
|
|
int fillLength = 5;
|
|
QTest::qt_snprintf(
|
|
fillFormat, sizeof(fillFormat), ":\n%%%ds", fillLength);
|
|
char fill[1024];
|
|
QTest::qt_snprintf(fill, sizeof(fill), fillFormat, "");
|
|
|
|
const char * unitText = QTest::benchmarkMetricUnit(result.metric);
|
|
|
|
qreal valuePerIteration = qreal(result.value) / qreal(result.iterations);
|
|
char resultBuffer[100] = "";
|
|
formatResult(resultBuffer, 100, valuePerIteration, countSignificantDigits(result.value));
|
|
|
|
char buf2[1024];
|
|
QTest::qt_snprintf(
|
|
buf2, sizeof(buf2), "%s %s",
|
|
resultBuffer,
|
|
unitText);
|
|
|
|
char buf2_[1024];
|
|
QByteArray iterationText = " per iteration";
|
|
Q_ASSERT(result.iterations > 0);
|
|
QTest::qt_snprintf(
|
|
buf2_,
|
|
sizeof(buf2_), "%s",
|
|
iterationText.data());
|
|
|
|
char buf3[1024];
|
|
Q_ASSERT(result.iterations > 0);
|
|
formatResult(resultBuffer, 100, result.value, countSignificantDigits(result.value));
|
|
QTest::qt_snprintf(
|
|
buf3, sizeof(buf3), " (total: %s, iterations: %d)",
|
|
resultBuffer,
|
|
result.iterations);
|
|
|
|
char buf[1024];
|
|
|
|
if (result.setByMacro) {
|
|
QTest::qt_snprintf(
|
|
buf, sizeof(buf), "%s%s%s%s%s%s\n", buf1, bufTag, fill, buf2, buf2_, buf3);
|
|
} else {
|
|
QTest::qt_snprintf(buf, sizeof(buf), "%s%s%s%s\n", buf1, bufTag, fill, buf2);
|
|
}
|
|
|
|
memcpy(buf, bmtag, strlen(bmtag));
|
|
outputMessage(buf);
|
|
}
|
|
}
|
|
|
|
QPlainTestLogger::QPlainTestLogger()
|
|
: randomSeed(9), hasRandomSeed(false)
|
|
{
|
|
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
|
|
InitializeCriticalSection(&QTest::outputCriticalSection);
|
|
QTest::hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if (QTest::hConsole != INVALID_HANDLE_VALUE) {
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
if (GetConsoleScreenBufferInfo(QTest::hConsole, &info)) {
|
|
QTest::consoleAttributes = info.wAttributes;
|
|
} else {
|
|
QTest::hConsole = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
QPlainTestLogger::~QPlainTestLogger()
|
|
{
|
|
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
|
|
DeleteCriticalSection(&QTest::outputCriticalSection);
|
|
#endif
|
|
}
|
|
|
|
void QPlainTestLogger::startLogging()
|
|
{
|
|
QAbstractTestLogger::startLogging();
|
|
|
|
char buf[1024];
|
|
if (QTestLog::verboseLevel() < 0) {
|
|
QTest::qt_snprintf(buf, sizeof(buf), "Testing %s\n",
|
|
QTestResult::currentTestObjectName());
|
|
} else {
|
|
if (hasRandomSeed) {
|
|
QTest::qt_snprintf(buf, sizeof(buf),
|
|
"********* Start testing of %s *********\n"
|
|
"Config: Using QTest library " QTEST_VERSION_STR
|
|
", Qt %s, Random seed %d\n", QTestResult::currentTestObjectName(), qVersion(), randomSeed);
|
|
} else {
|
|
QTest::qt_snprintf(buf, sizeof(buf),
|
|
"********* Start testing of %s *********\n"
|
|
"Config: Using QTest library " QTEST_VERSION_STR
|
|
", Qt %s\n", QTestResult::currentTestObjectName(), qVersion());
|
|
}
|
|
}
|
|
QTest::outputMessage(buf);
|
|
}
|
|
|
|
void QPlainTestLogger::stopLogging()
|
|
{
|
|
char buf[1024];
|
|
if (QTestLog::verboseLevel() < 0) {
|
|
QTest::qt_snprintf(buf, sizeof(buf), "Totals: %d passed, %d failed, %d skipped\n",
|
|
QTestResult::passCount(), QTestResult::failCount(),
|
|
QTestResult::skipCount());
|
|
} else {
|
|
QTest::qt_snprintf(buf, sizeof(buf),
|
|
"Totals: %d passed, %d failed, %d skipped\n"
|
|
"********* Finished testing of %s *********\n",
|
|
QTestResult::passCount(), QTestResult::failCount(),
|
|
QTestResult::skipCount(), QTestResult::currentTestObjectName());
|
|
}
|
|
QTest::outputMessage(buf);
|
|
|
|
QAbstractTestLogger::stopLogging();
|
|
}
|
|
|
|
|
|
void QPlainTestLogger::enterTestFunction(const char * /*function*/)
|
|
{
|
|
if (QTestLog::verboseLevel() >= 1)
|
|
QTest::printMessage(QTest::messageType2String(Info), "entering");
|
|
}
|
|
|
|
void QPlainTestLogger::leaveTestFunction()
|
|
{
|
|
}
|
|
|
|
void QPlainTestLogger::addIncident(IncidentTypes type, const char *description,
|
|
const char *file, int line)
|
|
{
|
|
// suppress PASS in silent mode
|
|
if (type == QAbstractTestLogger::Pass && QTestLog::verboseLevel() < 0)
|
|
return;
|
|
|
|
QTest::printMessage(QTest::incidentType2String(type), description, file, line);
|
|
}
|
|
|
|
void QPlainTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
|
|
{
|
|
// QTest::printBenchmarkResult(QTest::benchmarkResult2String(), value, iterations);
|
|
QTest::printBenchmarkResult(result);
|
|
}
|
|
|
|
void QPlainTestLogger::addMessage(MessageTypes type, const char *message,
|
|
const char *file, int line)
|
|
{
|
|
// suppress PASS in silent mode
|
|
if ((type == QAbstractTestLogger::Skip || type == QAbstractTestLogger::Info)
|
|
&& QTestLog::verboseLevel() < 0)
|
|
return;
|
|
|
|
QTest::printMessage(QTest::messageType2String(type), message, file, line);
|
|
}
|
|
|
|
void QPlainTestLogger::registerRandomSeed(unsigned int seed)
|
|
{
|
|
randomSeed = seed;
|
|
hasRandomSeed = true;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|