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>
33# include <QtCore/private/qohoslogger_p.h>
38using namespace Qt::StringLiterals;
41static const char multiplePrefixes[] =
"\0kMGTPE";
42static const char submultiplePrefixes[] =
"afpnum";
44template <
int N>
struct FixedBufString
46 static constexpr size_t MaxSize = N;
48 std::array<
char, N + 2> buf;
59 operator
const char *()
const
64 void append(
const char *text)
66 size_t len = qMin(strlen(text), MaxSize - used);
67 memcpy(buf.data() + used, text, len);
72 template <
typename... Args>
void appendf(
const char *format, Args... args)
75 used += std::snprintf(buf.data() + used, MaxSize - used + 1, format,
79 template <
int Power = 1000>
void appendScaled(qreal value,
const char *unit)
82 qreal v = qAbs(value);
84 if (v < 1 && Power == 1000) {
85 const char *prefixes = submultiplePrefixes;
86 ratio = qreal(std::atto::num) / qreal(std::atto::den);
87 while (value * ratio > 1000 && *prefixes) {
91 prefix[0] = *prefixes;
93 const char *prefixes = multiplePrefixes;
95 while (value > 1000 * ratio) {
99 prefix[0] = *prefixes;
104 appendf(
", %.3g %s%s", value, prefix, unit);
162 template <
typename T>
171 while (num / divisor >= 1) {
182 if (qIsNaN(number) || number < T(0))
189 return QByteArray::number(number,
'e', qMax(1, significantDigits - 1));
194
195
196
197
198
199
200
202void QPlainTestLogger::outputMessage(
const char *str)
206 if (stream == stdout && !QtPrivate::shouldLogToStderr()) {
207 OutputDebugStringA(str);
210#elif defined(Q_OS_ANDROID)
211 __android_log_write(ANDROID_LOG_INFO,
"QTestLib", str);
212#elif defined(Q_OS_OHOS)
213 qOhosLogMessage(LOG_INFO,
"QTestLib", str);
218void QPlainTestLogger::printMessage(MessageSource source,
const char *type,
const char *msg,
219 const char *file,
int line)
224 QTestCharBuffer messagePrefix;
226 QTestCharBuffer messageLocation;
228 constexpr const char *INCIDENT_LOCATION_STR =
"\n%s(%d) : failure location";
229 constexpr const char *OTHER_LOCATION_STR =
"\n%s(%d) : message location";
231 constexpr const char *INCIDENT_LOCATION_STR =
"\n Loc: [%s(%d)]";
232 constexpr const char *OTHER_LOCATION_STR = INCIDENT_LOCATION_STR;
237 case MessageSource::Incident:
238 QTest::qt_asprintf(&messageLocation, INCIDENT_LOCATION_STR, file, line);
240 case MessageSource::Other:
241 QTest::qt_asprintf(&messageLocation, OTHER_LOCATION_STR, file, line);
246 const char *msgFiller = msg[0] ?
" " :
"";
247 QTestCharBuffer testIdentifier;
248 QTestPrivate::generateTestIdentifier(&testIdentifier);
249 QTest::qt_asprintf(&messagePrefix,
"%s: %s%s%s%s\n",
250 type, testIdentifier.data(), msgFiller, msg, messageLocation.data());
254 memcpy(messagePrefix.data(), type, strlen(type));
256 outputMessage(messagePrefix.data());
259void QPlainTestLogger::printBenchmarkResultsHeader(
const QBenchmarkResult &result)
261 FixedBufString<1022> buf;
262 buf.appendf(
"%s: %s::%s", QTest::benchmarkResult2String(),
263 QTestResult::currentTestObjectName(), result.context.slotName.toLatin1().data());
265 QByteArray tag = QTestResult::currentDataTag();
266 QByteArray gtag = QTestResult::currentGlobalDataTag();
268 if (!gtag.isEmpty() && !tag.isEmpty())
269 buf.appendf(
":\"%s:%s\":\n", gtag.constData(), tag.constData());
270 else if (!gtag.isEmpty())
271 buf.appendf(
":\"%s\":\n", gtag.constData());
272 else if (!tag.isEmpty())
273 buf.appendf(
":\"%s\":\n", tag.constData());
279void QPlainTestLogger::printBenchmarkResults(
const QList<QBenchmarkResult> &results)
281 using namespace std::chrono;
282 FixedBufString<1022> buf;
283 auto findResultFor = [&results](QTest::QBenchmarkMetric metric) -> std::optional<qreal> {
284 for (
const QBenchmarkResult &result : results) {
285 if (result.measurement.metric == metric)
286 return result.measurement.value;
292 qreal executionTime = 0;
293 if (
auto ns = findResultFor(QTest::WalltimeNanoseconds))
294 executionTime = *ns / (1000 * 1000 * 1000);
295 else if (
auto ms = findResultFor(QTest::WalltimeMilliseconds))
296 executionTime = *ms / 1000;
298 for (
const QBenchmarkResult &result : results) {
301 const char * unitText = QTest::benchmarkMetricUnit(result.measurement.metric);
302 int significantDigits = QTest::countSignificantDigits(result.measurement.value);
303 qreal valuePerIteration = qreal(result.measurement.value) / qreal(result.iterations);
304 buf.appendf(
" %s %s%s", QTest::formatResult(valuePerIteration, significantDigits).constData(),
305 unitText, result.setByMacro ?
" per iteration" :
"");
307 switch (result.measurement.metric) {
308 case QTest::BitsPerSecond:
310 buf.appendScaled<1000>(result.measurement.value,
"bit/s");
312 case QTest::BytesPerSecond:
314 buf.appendScaled<1024>(result.measurement.value,
"B/s");
317 case QTest::CPUCycles:
318 case QTest::RefCPUCycles:
319 if (!qIsNull(executionTime))
320 buf.appendScaled(result.measurement.value / executionTime,
"Hz");
323 case QTest::Instructions:
324 if (
auto cycles = findResultFor(QTest::CPUCycles)) {
325 buf.appendf(
", %.3f instr/cycle", result.measurement.value / *cycles);
330 case QTest::InstructionReads:
332 case QTest::BytesAllocated:
333 case QTest::CPUMigrations:
334 case QTest::BusCycles:
335 case QTest::StalledCycles:
336 case QTest::BranchInstructions:
337 case QTest::BranchMisses:
338 case QTest::CacheReferences:
339 case QTest::CacheReads:
340 case QTest::CacheWrites:
341 case QTest::CachePrefetches:
342 case QTest::CacheMisses:
343 case QTest::CacheReadMisses:
344 case QTest::CacheWriteMisses:
345 case QTest::CachePrefetchMisses:
346 case QTest::ContextSwitches:
347 case QTest::PageFaults:
348 case QTest::MinorPageFaults:
349 case QTest::MajorPageFaults:
350 case QTest::AlignmentFaults:
351 case QTest::EmulationFaults:
352 if (!qIsNull(executionTime))
353 buf.appendScaled(result.measurement.value / executionTime,
"/sec");
356 case QTest::FramesPerSecond:
357 case QTest::CPUTicks:
358 case QTest::WalltimeMilliseconds:
359 case QTest::WalltimeNanoseconds:
363 Q_ASSERT(result.iterations > 0);
364 if (result.measurement.variance) {
365 buf.appendf(
" (total: %s, std.dev: %s, iterations: %d)\n",
366 QTest::formatResult(result.measurement.value,
367 significantDigits).constData(),
368 QTest::formatResult(sqrt(result.measurement.variance),
369 significantDigits).constData(),
372 buf.appendf(
" (total: %s, iterations: %d)\n",
373 QTest::formatResult(result.measurement.value,
374 significantDigits).constData(),
381QPlainTestLogger::QPlainTestLogger(
const char *filename)
382 : QAbstractTestLogger(filename)
386QPlainTestLogger::~QPlainTestLogger() =
default;
388void QPlainTestLogger::startLogging()
390 QAbstractTestLogger::startLogging();
393 if (QTestLog::verboseLevel() < 0) {
394 std::snprintf(buf,
sizeof(buf),
"Testing %s\n", QTestResult::currentTestObjectName());
396 std::snprintf(buf,
sizeof(buf),
397 "********* Start testing of %s *********\n"
398 "Config: Using QtTest library " QTEST_VERSION_STR
399 ", %s, %s %s\n", QTestResult::currentTestObjectName(), QLibraryInfo::build(),
400 qPrintable(QSysInfo::productType()), qPrintable(QSysInfo::productVersion()));
405void QPlainTestLogger::stopLogging()
408 const int timeMs = qRound(QTestLog::msecsTotalTime());
409 if (QTestLog::verboseLevel() < 0) {
410 std::snprintf(buf,
sizeof(buf),
411 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n",
412 QTestLog::passCount(), QTestLog::failCount(),
413 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs);
415 std::snprintf(buf,
sizeof(buf),
416 "Totals: %d passed, %d failed, %d skipped, %d blacklisted, %dms\n"
417 "********* Finished testing of %s *********\n",
418 QTestLog::passCount(), QTestLog::failCount(),
419 QTestLog::skipCount(), QTestLog::blacklistCount(), timeMs,
420 QTestResult::currentTestObjectName());
424 QAbstractTestLogger::stopLogging();
428void QPlainTestLogger::enterTestFunction(
const char * )
430 if (QTestLog::verboseLevel() >= 1)
431 printMessage(MessageSource::Other, QTest::ptMessageType2String(Info),
"entering");
434void QPlainTestLogger::leaveTestFunction()
438void QPlainTestLogger::addIncident(IncidentTypes type,
const char *description,
439 const char *file,
int line)
442 if ((type == Pass || type == BlacklistedPass || type == XFail || type == BlacklistedXFail)
443 && QTestLog::verboseLevel() < 0)
446 printMessage(MessageSource::Incident, QTest::ptIncidentType2String(type), description, file, line);
449void QPlainTestLogger::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
452 if (QTestLog::verboseLevel() < 0 || results.isEmpty())
455 printBenchmarkResultsHeader(results.first());
456 printBenchmarkResults(results);
459void QPlainTestLogger::addMessage(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
461 QAbstractTestLogger::addMessage(type, context, message);
464void QPlainTestLogger::addMessage(MessageTypes type,
const QString &message,
465 const char *file,
int line)
468 if (type != QFatal && QTestLog::verboseLevel() < 0)
471 printMessage(MessageSource::Other, QTest::ptMessageType2String(type), qPrintable(message), file, line);
474bool 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)