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>
20#include <QtCore/QByteArray>
21#include <QtCore/qmath.h>
22#include <QtCore/QLibraryInfo>
25# include <android/log.h>
29# include <qt_windows.h>
34using namespace Qt::StringLiterals;
37static const char multiplePrefixes[] =
"\0kMGTPE";
38static const char submultiplePrefixes[] =
"afpnum";
40template <
int N>
struct FixedBufString
42 static constexpr size_t MaxSize = N;
44 std::array<
char, N + 2> buf;
55 operator
const char *()
const
60 void append(
const char *text)
62 size_t len = qMin(strlen(text), MaxSize - used);
63 memcpy(buf.data() + used, text, len);
68 template <
typename... Args>
void appendf(
const char *format, Args... args)
71 used += std::snprintf(buf.data() + used, MaxSize - used + 1, format,
75 template <
int Power = 1000>
void appendScaled(qreal value,
const char *unit)
78 qreal v = qAbs(value);
80 if (v < 1 && Power == 1000) {
81 const char *prefixes = submultiplePrefixes;
82 ratio = qreal(std::atto::num) / qreal(std::atto::den);
83 while (value * ratio > 1000 && *prefixes) {
87 prefix[0] = *prefixes;
89 const char *prefixes = multiplePrefixes;
91 while (value > 1000 * ratio) {
95 prefix[0] = *prefixes;
100 appendf(
", %.3g %s%s", value, prefix, unit);
158 template <
typename T>
167 while (num / divisor >= 1) {
183 QByteArray beforeDecimalPoint = QByteArray::number(qint64(number),
'f', 0);
184 QByteArray afterDecimalPoint = QByteArray::number(number,
'f', 20);
185 afterDecimalPoint.remove(0, beforeDecimalPoint.size() + 1);
187 int beforeUse = qMin(beforeDecimalPoint.size(), significantDigits);
188 int beforeRemove = beforeDecimalPoint.size() - beforeUse;
191 beforeDecimalPoint.chop(beforeRemove);
192 for (
int i = 0; i < beforeRemove; ++i) {
193 beforeDecimalPoint.append(u'0');
196 int afterUse = significantDigits - beforeUse;
199 if (beforeDecimalPoint ==
"0" && !afterDecimalPoint.isEmpty()) {
203 while (i < afterDecimalPoint.size() && afterDecimalPoint.at(i) ==
'0')
209 int afterRemove = afterDecimalPoint.size() - afterUse;
210 afterDecimalPoint.chop(afterRemove);
212 char separator =
',';
213 char decimalPoint =
'.';
216 int length = beforeDecimalPoint.size();
217 for (
int i = beforeDecimalPoint.size() -1; i >= 1; --i) {
218 if ((length - i) % 3 == 0)
219 beforeDecimalPoint.insert(i, separator);
223 print = beforeDecimalPoint;
225 print.append(decimalPoint);
227 print += afterDecimalPoint;
235
236
237
238
239
240
241
243void QPlainTestLogger::outputMessage(
const char *str)
247 if (stream == stdout && !QtPrivate::shouldLogToStderr()) {
248 OutputDebugStringA(str);
251#elif defined(Q_OS_ANDROID)
252 __android_log_write(ANDROID_LOG_INFO,
"QTestLib", str);
257void QPlainTestLogger::printMessage(MessageSource source,
const char *type,
const char *msg,
258 const char *file,
int line)
263 QTestCharBuffer messagePrefix;
265 QTestCharBuffer messageLocation;
267 constexpr const char *INCIDENT_LOCATION_STR =
"\n%s(%d) : failure location";
268 constexpr const char *OTHER_LOCATION_STR =
"\n%s(%d) : message location";
270 constexpr const char *INCIDENT_LOCATION_STR =
"\n Loc: [%s(%d)]";
271 constexpr const char *OTHER_LOCATION_STR = INCIDENT_LOCATION_STR;
276 case MessageSource::Incident:
277 QTest::qt_asprintf(&messageLocation, INCIDENT_LOCATION_STR, file, line);
279 case MessageSource::Other:
280 QTest::qt_asprintf(&messageLocation, OTHER_LOCATION_STR, file, line);
285 const char *msgFiller = msg[0] ?
" " :
"";
286 QTestCharBuffer testIdentifier;
287 QTestPrivate::generateTestIdentifier(&testIdentifier);
288 QTest::qt_asprintf(&messagePrefix,
"%s: %s%s%s%s\n",
289 type, testIdentifier.data(), msgFiller, msg, messageLocation.data());
293 memcpy(messagePrefix.data(), type, strlen(type));
295 outputMessage(messagePrefix.data());
298void QPlainTestLogger::printBenchmarkResultsHeader(
const QBenchmarkResult &result)
300 FixedBufString<1022> buf;
301 buf.appendf(
"%s: %s::%s", QTest::benchmarkResult2String(),
302 QTestResult::currentTestObjectName(), result.context.slotName.toLatin1().data());
304 QByteArray tag = QTestResult::currentDataTag();
305 QByteArray gtag = QTestResult::currentGlobalDataTag();
307 if (!gtag.isEmpty() && !tag.isEmpty())
308 buf.appendf(
":\"%s:%s\":\n", gtag.constData(), tag.constData());
309 else if (!gtag.isEmpty())
310 buf.appendf(
":\"%s\":\n", gtag.constData());
311 else if (!tag.isEmpty())
312 buf.appendf(
":\"%s\":\n", tag.constData());
318void QPlainTestLogger::printBenchmarkResults(
const QList<QBenchmarkResult> &results)
320 using namespace std::chrono;
321 FixedBufString<1022> buf;
322 auto findResultFor = [&results](QTest::QBenchmarkMetric metric) -> std::optional<qreal> {
323 for (
const QBenchmarkResult &result : results) {
324 if (result.measurement.metric == metric)
325 return result.measurement.value;
331 qreal executionTime = 0;
332 if (
auto ns = findResultFor(QTest::WalltimeNanoseconds))
333 executionTime = *ns / (1000 * 1000 * 1000);
334 else if (
auto ms = findResultFor(QTest::WalltimeMilliseconds))
335 executionTime = *ms / 1000;
337 for (
const QBenchmarkResult &result : results) {
340 const char * unitText = QTest::benchmarkMetricUnit(result.measurement.metric);
341 int significantDigits = QTest::countSignificantDigits(result.measurement.value);
342 qreal valuePerIteration = qreal(result.measurement.value) / qreal(result.iterations);
343 buf.appendf(
" %s %s%s", QTest::formatResult(valuePerIteration, significantDigits).constData(),
344 unitText, result.setByMacro ?
" per iteration" :
"");
346 switch (result.measurement.metric) {
347 case QTest::BitsPerSecond:
349 buf.appendScaled<1000>(result.measurement.value,
"bit/s");
351 case QTest::BytesPerSecond:
353 buf.appendScaled<1024>(result.measurement.value,
"B/s");
356 case QTest::CPUCycles:
357 case QTest::RefCPUCycles:
358 if (!qIsNull(executionTime))
359 buf.appendScaled(result.measurement.value / executionTime,
"Hz");
362 case QTest::Instructions:
363 if (
auto cycles = findResultFor(QTest::CPUCycles)) {
364 buf.appendf(
", %.3f instr/cycle", result.measurement.value / *cycles);
369 case QTest::InstructionReads:
371 case QTest::BytesAllocated:
372 case QTest::CPUMigrations:
373 case QTest::BusCycles:
374 case QTest::StalledCycles:
375 case QTest::BranchInstructions:
376 case QTest::BranchMisses:
377 case QTest::CacheReferences:
378 case QTest::CacheReads:
379 case QTest::CacheWrites:
380 case QTest::CachePrefetches:
381 case QTest::CacheMisses:
382 case QTest::CacheReadMisses:
383 case QTest::CacheWriteMisses:
384 case QTest::CachePrefetchMisses:
385 case QTest::ContextSwitches:
386 case QTest::PageFaults:
387 case QTest::MinorPageFaults:
388 case QTest::MajorPageFaults:
389 case QTest::AlignmentFaults:
390 case QTest::EmulationFaults:
391 if (!qIsNull(executionTime))
392 buf.appendScaled(result.measurement.value / executionTime,
"/sec");
395 case QTest::FramesPerSecond:
396 case QTest::CPUTicks:
397 case QTest::WalltimeMilliseconds:
398 case QTest::WalltimeNanoseconds:
402 Q_ASSERT(result.iterations > 0);
403 buf.appendf(
" (total: %s, iterations: %d)\n",
404 QTest::formatResult(result.measurement.value, significantDigits).constData(),
411QPlainTestLogger::QPlainTestLogger(
const char *filename)
412 : QAbstractTestLogger(filename)
416QPlainTestLogger::~QPlainTestLogger() =
default;
418void QPlainTestLogger::startLogging()
420 QAbstractTestLogger::startLogging();
423 if (QTestLog::verboseLevel() < 0) {
424 std::snprintf(buf,
sizeof(buf),
"Testing %s\n", QTestResult::currentTestObjectName());
426 std::snprintf(buf,
sizeof(buf),
427 "********* Start testing of %s *********\n"
428 "Config: Using QtTest library " QTEST_VERSION_STR
429 ", %s, %s %s\n", QTestResult::currentTestObjectName(), QLibraryInfo::build(),
430 qPrintable(QSysInfo::productType()), qPrintable(QSysInfo::productVersion()));
435void QPlainTestLogger::stopLogging()
438 const int timeMs = qRound(QTestLog::msecsTotalTime());
439 if (QTestLog::verboseLevel() < 0) {
440 std::snprintf(buf,
sizeof(buf),
441 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n",
442 QTestLog::passCount(), QTestLog::failCount(),
443 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs);
445 std::snprintf(buf,
sizeof(buf),
446 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n"
447 "********* Finished testing of %s *********\n",
448 QTestLog::passCount(), QTestLog::failCount(),
449 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs,
450 QTestResult::currentTestObjectName());
454 QAbstractTestLogger::stopLogging();
458void QPlainTestLogger::enterTestFunction(
const char * )
460 if (QTestLog::verboseLevel() >= 1)
461 printMessage(MessageSource::Other, QTest::ptMessageType2String(Info),
"entering");
464void QPlainTestLogger::leaveTestFunction()
468void QPlainTestLogger::addIncident(IncidentTypes type,
const char *description,
469 const char *file,
int line)
472 if ((type == Pass || type == BlacklistedPass || type == XFail || type == BlacklistedXFail)
473 && QTestLog::verboseLevel() < 0)
476 printMessage(MessageSource::Incident, QTest::ptIncidentType2String(type), description, file, line);
479void QPlainTestLogger::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
482 if (QTestLog::verboseLevel() < 0 || results.isEmpty())
485 printBenchmarkResultsHeader(results.first());
486 printBenchmarkResults(results);
489void QPlainTestLogger::addMessage(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
491 QAbstractTestLogger::addMessage(type, context, message);
494void QPlainTestLogger::addMessage(MessageTypes type,
const QString &message,
495 const char *file,
int line)
498 if (type != QFatal && QTestLog::verboseLevel() < 0)
501 printMessage(MessageSource::Other, QTest::ptMessageType2String(type), qPrintable(message), file, line);
504bool QPlainTestLogger::isRepeatSupported()
const
static const char * ptMessageType2String(QAbstractTestLogger::MessageTypes type)
static const char * ptIncidentType2String(QAbstractTestLogger::IncidentTypes type)
static int countSignificantDigits(T num)
static const char * benchmarkResult2String()
QByteArray formatResult(T number, int significantDigits)