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) {
178 if (qIsNaN(number) || number < T(0))
185 return QByteArray::number(number,
'e', qMax(1, significantDigits - 1));
190
191
192
193
194
195
196
198void QPlainTestLogger::outputMessage(
const char *str)
202 if (stream == stdout && !QtPrivate::shouldLogToStderr()) {
203 OutputDebugStringA(str);
206#elif defined(Q_OS_ANDROID)
207 __android_log_write(ANDROID_LOG_INFO,
"QTestLib", str);
212void QPlainTestLogger::printMessage(MessageSource source,
const char *type,
const char *msg,
213 const char *file,
int line)
218 QTestCharBuffer messagePrefix;
220 QTestCharBuffer messageLocation;
222 constexpr const char *INCIDENT_LOCATION_STR =
"\n%s(%d) : failure location";
223 constexpr const char *OTHER_LOCATION_STR =
"\n%s(%d) : message location";
225 constexpr const char *INCIDENT_LOCATION_STR =
"\n Loc: [%s(%d)]";
226 constexpr const char *OTHER_LOCATION_STR = INCIDENT_LOCATION_STR;
231 case MessageSource::Incident:
232 QTest::qt_asprintf(&messageLocation, INCIDENT_LOCATION_STR, file, line);
234 case MessageSource::Other:
235 QTest::qt_asprintf(&messageLocation, OTHER_LOCATION_STR, file, line);
240 const char *msgFiller = msg[0] ?
" " :
"";
241 QTestCharBuffer testIdentifier;
242 QTestPrivate::generateTestIdentifier(&testIdentifier);
243 QTest::qt_asprintf(&messagePrefix,
"%s: %s%s%s%s\n",
244 type, testIdentifier.data(), msgFiller, msg, messageLocation.data());
248 memcpy(messagePrefix.data(), type, strlen(type));
250 outputMessage(messagePrefix.data());
253void QPlainTestLogger::printBenchmarkResultsHeader(
const QBenchmarkResult &result)
255 FixedBufString<1022> buf;
256 buf.appendf(
"%s: %s::%s", QTest::benchmarkResult2String(),
257 QTestResult::currentTestObjectName(), result.context.slotName.toLatin1().data());
259 QByteArray tag = QTestResult::currentDataTag();
260 QByteArray gtag = QTestResult::currentGlobalDataTag();
262 if (!gtag.isEmpty() && !tag.isEmpty())
263 buf.appendf(
":\"%s:%s\":\n", gtag.constData(), tag.constData());
264 else if (!gtag.isEmpty())
265 buf.appendf(
":\"%s\":\n", gtag.constData());
266 else if (!tag.isEmpty())
267 buf.appendf(
":\"%s\":\n", tag.constData());
273void QPlainTestLogger::printBenchmarkResults(
const QList<QBenchmarkResult> &results)
275 using namespace std::chrono;
276 FixedBufString<1022> buf;
277 auto findResultFor = [&results](QTest::QBenchmarkMetric metric) -> std::optional<qreal> {
278 for (
const QBenchmarkResult &result : results) {
279 if (result.measurement.metric == metric)
280 return result.measurement.value;
286 qreal executionTime = 0;
287 if (
auto ns = findResultFor(QTest::WalltimeNanoseconds))
288 executionTime = *ns / (1000 * 1000 * 1000);
289 else if (
auto ms = findResultFor(QTest::WalltimeMilliseconds))
290 executionTime = *ms / 1000;
292 for (
const QBenchmarkResult &result : results) {
295 const char * unitText = QTest::benchmarkMetricUnit(result.measurement.metric);
296 int significantDigits = QTest::countSignificantDigits(result.measurement.value);
297 qreal valuePerIteration = qreal(result.measurement.value) / qreal(result.iterations);
298 buf.appendf(
" %s %s%s", QTest::formatResult(valuePerIteration, significantDigits).constData(),
299 unitText, result.setByMacro ?
" per iteration" :
"");
301 switch (result.measurement.metric) {
302 case QTest::BitsPerSecond:
304 buf.appendScaled<1000>(result.measurement.value,
"bit/s");
306 case QTest::BytesPerSecond:
308 buf.appendScaled<1024>(result.measurement.value,
"B/s");
311 case QTest::CPUCycles:
312 case QTest::RefCPUCycles:
313 if (!qIsNull(executionTime))
314 buf.appendScaled(result.measurement.value / executionTime,
"Hz");
317 case QTest::Instructions:
318 if (
auto cycles = findResultFor(QTest::CPUCycles)) {
319 buf.appendf(
", %.3f instr/cycle", result.measurement.value / *cycles);
324 case QTest::InstructionReads:
326 case QTest::BytesAllocated:
327 case QTest::CPUMigrations:
328 case QTest::BusCycles:
329 case QTest::StalledCycles:
330 case QTest::BranchInstructions:
331 case QTest::BranchMisses:
332 case QTest::CacheReferences:
333 case QTest::CacheReads:
334 case QTest::CacheWrites:
335 case QTest::CachePrefetches:
336 case QTest::CacheMisses:
337 case QTest::CacheReadMisses:
338 case QTest::CacheWriteMisses:
339 case QTest::CachePrefetchMisses:
340 case QTest::ContextSwitches:
341 case QTest::PageFaults:
342 case QTest::MinorPageFaults:
343 case QTest::MajorPageFaults:
344 case QTest::AlignmentFaults:
345 case QTest::EmulationFaults:
346 if (!qIsNull(executionTime))
347 buf.appendScaled(result.measurement.value / executionTime,
"/sec");
350 case QTest::FramesPerSecond:
351 case QTest::CPUTicks:
352 case QTest::WalltimeMilliseconds:
353 case QTest::WalltimeNanoseconds:
357 Q_ASSERT(result.iterations > 0);
358 buf.appendf(
" (total: %s, iterations: %d)\n",
359 QTest::formatResult(result.measurement.value, significantDigits).constData(),
366QPlainTestLogger::QPlainTestLogger(
const char *filename)
367 : QAbstractTestLogger(filename)
371QPlainTestLogger::~QPlainTestLogger() =
default;
373void QPlainTestLogger::startLogging()
375 QAbstractTestLogger::startLogging();
378 if (QTestLog::verboseLevel() < 0) {
379 std::snprintf(buf,
sizeof(buf),
"Testing %s\n", QTestResult::currentTestObjectName());
381 std::snprintf(buf,
sizeof(buf),
382 "********* Start testing of %s *********\n"
383 "Config: Using QtTest library " QTEST_VERSION_STR
384 ", %s, %s %s\n", QTestResult::currentTestObjectName(), QLibraryInfo::build(),
385 qPrintable(QSysInfo::productType()), qPrintable(QSysInfo::productVersion()));
390void QPlainTestLogger::stopLogging()
393 const int timeMs = qRound(QTestLog::msecsTotalTime());
394 if (QTestLog::verboseLevel() < 0) {
395 std::snprintf(buf,
sizeof(buf),
396 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n",
397 QTestLog::passCount(), QTestLog::failCount(),
398 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs);
400 std::snprintf(buf,
sizeof(buf),
401 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n"
402 "********* Finished testing of %s *********\n",
403 QTestLog::passCount(), QTestLog::failCount(),
404 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs,
405 QTestResult::currentTestObjectName());
409 QAbstractTestLogger::stopLogging();
413void QPlainTestLogger::enterTestFunction(
const char * )
415 if (QTestLog::verboseLevel() >= 1)
416 printMessage(MessageSource::Other, QTest::ptMessageType2String(Info),
"entering");
419void QPlainTestLogger::leaveTestFunction()
423void QPlainTestLogger::addIncident(IncidentTypes type,
const char *description,
424 const char *file,
int line)
427 if ((type == Pass || type == BlacklistedPass || type == XFail || type == BlacklistedXFail)
428 && QTestLog::verboseLevel() < 0)
431 printMessage(MessageSource::Incident, QTest::ptIncidentType2String(type), description, file, line);
434void QPlainTestLogger::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
437 if (QTestLog::verboseLevel() < 0 || results.isEmpty())
440 printBenchmarkResultsHeader(results.first());
441 printBenchmarkResults(results);
444void QPlainTestLogger::addMessage(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
446 QAbstractTestLogger::addMessage(type, context, message);
449void QPlainTestLogger::addMessage(MessageTypes type,
const QString &message,
450 const char *file,
int line)
453 if (type != QFatal && QTestLog::verboseLevel() < 0)
456 printMessage(MessageSource::Other, QTest::ptMessageType2String(type), qPrintable(message), file, line);
459bool 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)