4#include <QtTest/private/qtestresult_p.h>
5#include <QtTest/qtestassert.h>
6#include <QtTest/private/qtestlog_p.h>
7#include <QtTest/private/qplaintestlogger_p.h>
8#include <QtTest/private/qbenchmark_p.h>
9#include <QtTest/private/qbenchmarkmetric_p.h>
11#include <QtCore/private/qlogging_p.h>
21#include <QtCore/QByteArray>
22#include <QtCore/qmath.h>
23#include <QtCore/QLibraryInfo>
26# include <android/log.h>
30# include <qt_windows.h>
34# include <QtCore/private/qohoslogger_p.h>
39using namespace Qt::StringLiterals;
42static const char multiplePrefixes[] =
"\0kMGTPE";
43static const char submultiplePrefixes[] =
"afpnum";
45template <
int N>
struct FixedBufString
47 static constexpr size_t MaxSize = N;
49 std::array<
char, N + 2> buf;
60 operator
const char *()
const
65 void append(
const char *text)
67 size_t len = qMin(strlen(text), MaxSize - used);
68 memcpy(buf.data() + used, text, len);
73 template <
typename... Args>
void appendf(
const char *format, Args... args)
76 used += std::snprintf(buf.data() + used, MaxSize - used + 1, format,
80 template <
int Power = 1000>
void appendScaled(qreal value,
const char *unit)
83 qreal v = qAbs(value);
85 if (v < 1 && Power == 1000) {
86 const char *prefixes = submultiplePrefixes;
87 ratio = qreal(std::atto::num) / qreal(std::atto::den);
88 while (value * ratio > 1000 && *prefixes) {
92 prefix[0] = *prefixes;
94 const char *prefixes = multiplePrefixes;
96 while (value > 1000 * ratio) {
100 prefix[0] = *prefixes;
105 appendf(
", %.3g %s%s", value, prefix, unit);
163 template <
typename T>
172 while (num / divisor >= 1) {
182 if (qIsNaN(number) || number < T(0))
197 std::optional<
int> scale;
200 if (QByteArray special = specialValue(number); !special.isEmpty()) {
205 int decimals = significantDigits;
206 qreal mantissa = number;
212 Q_ASSERT(*scale % 3 == 0);
221 for (qreal extra = 1.0; mantissa * extra < 0.1; extra *= 10)
234 while (mantissa >= 1000) {
238 while (mantissa < 0.1) {
243 res.scale = 3 * thous;
250 else if (mantissa >= 10)
252 else if (mantissa >= 1)
256 res.text = QByteArray::number(mantissa,
'f', qMax(decimals, 0));
260 QByteArray tail = QByteArray::number(*res.scale);
261 if (tail.startsWith(
'-')) {
278
279
280
281
282
283
284
286void QPlainTestLogger::outputMessage(
const char *str)
290 if (stream == stdout && !QtPrivate::shouldLogToStderr()) {
291 OutputDebugStringA(str);
294#elif defined(Q_OS_ANDROID)
295 __android_log_write(ANDROID_LOG_INFO,
"QTestLib", str);
296#elif defined(Q_OS_HARMONY)
297 qOhosLogMessage(LOG_INFO,
"QTestLib", str);
302void QPlainTestLogger::printMessage(MessageSource source,
const char *type,
const char *msg,
303 const char *file,
int line)
308 QTestCharBuffer messagePrefix;
310 QTestCharBuffer messageLocation;
312 constexpr const char *INCIDENT_LOCATION_STR =
"\n%s(%d) : failure location";
313 constexpr const char *OTHER_LOCATION_STR =
"\n%s(%d) : message location";
315 constexpr const char *INCIDENT_LOCATION_STR =
"\n Loc: [%s(%d)]";
316 constexpr const char *OTHER_LOCATION_STR = INCIDENT_LOCATION_STR;
321 case MessageSource::Incident:
322 QTest::qt_asprintf(&messageLocation, INCIDENT_LOCATION_STR, file, line);
324 case MessageSource::Other:
325 QTest::qt_asprintf(&messageLocation, OTHER_LOCATION_STR, file, line);
330 const char *msgFiller = msg[0] ?
" " :
"";
331 QTestCharBuffer testIdentifier;
332 QTestPrivate::generateTestIdentifier(&testIdentifier);
333 QTest::qt_asprintf(&messagePrefix,
"%s: %s%s%s%s\n",
334 type, testIdentifier.data(), msgFiller, msg, messageLocation.data());
338 memcpy(messagePrefix.data(), type, strlen(type));
340 outputMessage(messagePrefix.data());
343void QPlainTestLogger::printBenchmarkResultsHeader(
const QBenchmarkResult &result)
345 FixedBufString<1022> buf;
346 buf.appendf(
"%s: %s::%s", QTest::benchmarkResult2String(),
347 QTestResult::currentTestObjectName(), result.context.slotName.toLatin1().data());
349 QByteArray tag = QTestResult::currentDataTag();
350 QByteArray gtag = QTestResult::currentGlobalDataTag();
352 if (!gtag.isEmpty() && !tag.isEmpty())
353 buf.appendf(
":\"%s:%s\":\n", gtag.constData(), tag.constData());
354 else if (!gtag.isEmpty())
355 buf.appendf(
":\"%s\":\n", gtag.constData());
356 else if (!tag.isEmpty())
357 buf.appendf(
":\"%s\":\n", tag.constData());
363void QPlainTestLogger::printBenchmarkResults(
const QList<QBenchmarkResult> &results)
365 using namespace std::chrono;
366 FixedBufString<1022> buf;
367 auto findResultFor = [&results](QTest::QBenchmarkMetric metric) -> std::optional<qreal> {
368 for (
const QBenchmarkResult &result : results) {
369 if (result.measurement.metric == metric)
370 return result.measurement.value;
376 qreal executionTime = 0;
377 if (
auto ns = findResultFor(QTest::WalltimeNanoseconds))
378 executionTime = *ns / (1000 * 1000 * 1000);
379 else if (
auto ms = findResultFor(QTest::WalltimeMilliseconds))
380 executionTime = *ms / 1000;
382 for (
const QBenchmarkResult &result : results) {
385 const char * unitText = QTest::benchmarkMetricUnit(result.measurement.metric);
386 int significantDigits = QTest::countSignificantDigits(result.measurement.value);
387 qreal valuePerIteration = qreal(result.measurement.value) / qreal(result.iterations);
388 auto midForm = QTest::formatResult(valuePerIteration, significantDigits);
389 buf.appendf(
" %s %s%s", midForm.text.constData(), unitText,
390 result.setByMacro ?
" per iteration" :
"");
392 switch (result.measurement.metric) {
393 case QTest::BitsPerSecond:
395 buf.appendScaled<1000>(result.measurement.value,
"bit/s");
397 case QTest::BytesPerSecond:
399 buf.appendScaled<1024>(result.measurement.value,
"B/s");
402 case QTest::CPUCycles:
403 case QTest::RefCPUCycles:
404 if (!qIsNull(executionTime))
405 buf.appendScaled(result.measurement.value / executionTime,
"Hz");
408 case QTest::Instructions:
409 if (
auto cycles = findResultFor(QTest::CPUCycles)) {
410 buf.appendf(
", %.3f instr/cycle", result.measurement.value / *cycles);
415 case QTest::InstructionReads:
417 case QTest::BytesAllocated:
418 case QTest::CPUMigrations:
419 case QTest::BusCycles:
420 case QTest::StalledCycles:
421 case QTest::BranchInstructions:
422 case QTest::BranchMisses:
423 case QTest::CacheReferences:
424 case QTest::CacheReads:
425 case QTest::CacheWrites:
426 case QTest::CachePrefetches:
427 case QTest::CacheMisses:
428 case QTest::CacheReadMisses:
429 case QTest::CacheWriteMisses:
430 case QTest::CachePrefetchMisses:
431 case QTest::ContextSwitches:
432 case QTest::PageFaults:
433 case QTest::MinorPageFaults:
434 case QTest::MajorPageFaults:
435 case QTest::AlignmentFaults:
436 case QTest::EmulationFaults:
437 if (!qIsNull(executionTime))
438 buf.appendScaled(result.measurement.value / executionTime,
"/sec");
441 case QTest::FramesPerSecond:
442 case QTest::CPUTicks:
443 case QTest::WalltimeMilliseconds:
444 case QTest::WalltimeNanoseconds:
448 Q_ASSERT(result.iterations > 0);
449 auto totForm = QTest::formatResult(result.measurement.value, significantDigits);
450 if (result.measurement.variance) {
451 auto stdForm = QTest::formatResult(sqrt(result.measurement.variance),
452 significantDigits, midForm.scale);
453 buf.appendf(
" (total: %s %s, std.dev: %s %s, iterations: %d)\n",
454 totForm.text.constData(), unitText,
455 stdForm.text.constData(), unitText,
458 buf.appendf(
" (total: %s %s, iterations: %d)\n",
459 totForm.text.constData(), unitText,
466QPlainTestLogger::QPlainTestLogger(
const char *filename)
467 : QAbstractTestLogger(filename)
471QPlainTestLogger::~QPlainTestLogger() =
default;
473void QPlainTestLogger::startLogging()
475 QAbstractTestLogger::startLogging();
478 if (QTestLog::verboseLevel() < 0) {
479 std::snprintf(buf,
sizeof(buf),
"Testing %s\n", QTestResult::currentTestObjectName());
481 std::snprintf(buf,
sizeof(buf),
482 "********* Start testing of %s *********\n"
483 "Config: Using QtTest library " QTEST_VERSION_STR
484 ", %s, %s %s\n", QTestResult::currentTestObjectName(), QLibraryInfo::build(),
485 qPrintable(QSysInfo::productType()), qPrintable(QSysInfo::productVersion()));
490void QPlainTestLogger::stopLogging()
493 const int timeMs = qRound(QTestLog::msecsTotalTime());
494 if (QTestLog::verboseLevel() < 0) {
495 std::snprintf(buf,
sizeof(buf),
496 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n",
497 QTestLog::passCount(), QTestLog::failCount(),
498 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs);
500 std::snprintf(buf,
sizeof(buf),
501 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n"
502 "********* Finished testing of %s *********\n",
503 QTestLog::passCount(), QTestLog::failCount(),
504 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs,
505 QTestResult::currentTestObjectName());
509 QAbstractTestLogger::stopLogging();
513void QPlainTestLogger::enterTestFunction(
const char * )
515 if (QTestLog::verboseLevel() >= 1)
516 printMessage(MessageSource::Other, QTest::ptMessageType2String(Info),
"entering");
519void QPlainTestLogger::leaveTestFunction()
523void QPlainTestLogger::addIncident(IncidentTypes type,
const char *description,
524 const char *file,
int line)
527 if ((type == Pass || type == BlacklistedPass || type == XFail || type == BlacklistedXFail)
528 && QTestLog::verboseLevel() < 0)
531 printMessage(MessageSource::Incident, QTest::ptIncidentType2String(type), description, file, line);
534void QPlainTestLogger::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
537 if (QTestLog::verboseLevel() < 0 || results.isEmpty())
540 printBenchmarkResultsHeader(results.first());
541 printBenchmarkResults(results);
544void QPlainTestLogger::addMessage(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
546 QAbstractTestLogger::addMessage(type, context, message);
549void QPlainTestLogger::addMessage(MessageTypes type,
const QString &message,
550 const char *file,
int line)
553 if (type != QFatal && QTestLog::verboseLevel() < 0)
556 printMessage(MessageSource::Other, QTest::ptMessageType2String(type), qPrintable(message), file, line);
559bool QPlainTestLogger::isRepeatSupported()
const
static const char * ptMessageType2String(QAbstractTestLogger::MessageTypes type)
static const char * ptIncidentType2String(QAbstractTestLogger::IncidentTypes type)
static int countSignificantDigits(T num)
QByteArray specialValue(T number)
static const char * benchmarkResult2String()
auto formatResult(qreal number, int significantDigits, std::optional< int > scale={})