4#include <QtTest/qtestassert.h>
6#include <QtTest/private/qtestlog_p.h>
7#include <QtTest/private/qtestresult_p.h>
8#include <QtTest/private/qabstracttestlogger_p.h>
9#include <QtTest/private/qplaintestlogger_p.h>
10#include <QtTest/private/qcsvbenchmarklogger_p.h>
11#include <QtTest/private/qjunittestlogger_p.h>
12#include <QtTest/private/qxmltestlogger_p.h>
13#include <QtTest/private/qteamcitylogger_p.h>
14#include <QtTest/private/qtaptestlogger_p.h>
15#if defined(HAVE_XCTEST)
16#include <QtTest/private/qxctestlogger_p.h>
19#if defined(Q_OS_DARWIN)
20#include <QtTest/private/qappletestlogger_p.h>
23#include <QtCore/qatomic.h>
24#include <QtCore/qbytearray.h>
25#include <QtCore/qelapsedtimer.h>
26#include <QtCore/qlist.h>
27#include <QtCore/qmutex.h>
28#include <QtCore/qvariant.h>
29#if QT_CONFIG(regularexpression)
30#include <QtCore/QRegularExpression>
37#include <QtCore/q20algorithm.h>
45using namespace Qt::StringLiterals;
47static void saveCoverageTool(
const char * appname,
bool testfailed,
bool installedTestCoverage)
49#ifdef __COVERAGESCANNER__
50# if QT_CONFIG(testlib_selfcover)
51 __coveragescanner_teststate(QTestLog::failCount() > 0 ?
"FAILED" :
52 QTestLog::passCount() > 0 ?
"PASSED" :
"SKIPPED");
54 if (!installedTestCoverage)
58 __coveragescanner_install(appname);
59 __coveragescanner_teststate(testfailed ?
"FAILED" :
"PASSED");
60 __coveragescanner_save();
61 __coveragescanner_testname(
"");
62 __coveragescanner_clear();
63 unsetenv(
"QT_TESTCOCOON_ACTIVE");
68 Q_UNUSED(installedTestCoverage);
72Q_CONSTINIT
static QElapsedTimer elapsedFunctionTime;
73Q_CONSTINIT
static QElapsedTimer elapsedTotalTime;
78 using LoggersContainer = std::vector<std::shared_ptr<QAbstractTestLogger>>;
79 using SharedLoggersContainer = std::shared_ptr<LoggersContainer>;
82 void addLogger(std::unique_ptr<QAbstractTestLogger> logger)
85 const SharedLoggersContainer currentLoggers = load();
86 SharedLoggersContainer newLoggers = currentLoggers
87 ? std::make_shared<LoggersContainer>(*currentLoggers)
88 : std::make_shared<LoggersContainer>();
89 newLoggers->emplace_back(
std::move(logger));
90 store(std::move(newLoggers));
93 void clear() { store(SharedLoggersContainer{}); }
95 auto allLoggers()
const
99 const SharedLoggersContainer loggers;
103 return loggers ? loggers->cbegin() : LoggersContainer::const_iterator{};
107 return loggers ? loggers->cend() : LoggersContainer::const_iterator{};
109 bool isEmpty()
const {
return loggers ? loggers->empty() :
true; }
112 return LoggersRange{ load() };
116#ifdef __cpp_lib_atomic_shared_ptr
117 SharedLoggersContainer load()
const {
return loggers.load(std::memory_order_relaxed); }
118 void store(SharedLoggersContainer newLoggers)
120 loggers.store(std::move(newLoggers), std::memory_order_relaxed);
122 std::atomic<SharedLoggersContainer> loggers;
124 SharedLoggersContainer load()
const
126 return std::atomic_load_explicit(&loggers, std::memory_order_relaxed);
128 void store(SharedLoggersContainer newLoggers)
130 std::atomic_store_explicit(&loggers, std::move(newLoggers), std::memory_order_relaxed);
132 SharedLoggersContainer loggers;
173 static bool stringsMatch(
const QString &expected,
const QString &actual)
175 if (expected == actual)
181 if (expected.endsWith(u' '))
182 return actual == QStringView{expected}.left(expected.size() - 1);
187 inline bool matches(QtMsgType tp,
const QString &message)
const
190 && (pattern.userType() == QMetaType::QString ?
191 stringsMatch(pattern.toString(), message) :
192#if QT_CONFIG(regularexpression)
193 pattern.toRegularExpression().match(message).hasMatch());
205 Q_CONSTINIT
static QBasicMutex
mutex;
209 Q_GLOBAL_STATIC(LoggerRegistry, loggers)
219 const QMutexLocker mutexLocker(&QTest::mutex);
226 if (list->matches(type, message)) {
247 for (
const auto &pattern : failOnWarningList) {
248 if (pattern.metaType() == QMetaType::fromType<QString>()) {
249 if (message != pattern.toString())
252#if QT_CONFIG(regularexpression)
253 else if (pattern.metaType() == QMetaType::fromType<QRegularExpression>()) {
254 if (!message.contains(pattern.toRegularExpression()))
259 const size_t maxMsgLen = 1024;
260 char msg[maxMsgLen] = {
'\0'};
261 std::snprintf(msg, maxMsgLen,
"Received a warning that resulted in a failure:\n%s",
262 qPrintable(message));
263 QTestResult::addFailure(msg, context.file, context.line);
269 static void messageHandler(QtMsgType type,
const QMessageLogContext & context,
const QString &message)
273 auto loggerCapture = loggers->allLoggers();
275 if (loggerCapture.isEmpty()) {
278 Q_ASSERT(oldMessageHandler);
283 if (handleIgnoredMessage(type, message)) {
288 if (type == QtWarningMsg && handleFailOnWarning(context, message))
291 if (type != QtFatalMsg) {
292 if (counter.loadRelaxed() <= 0)
295 if (!counter.deref()) {
296 for (
auto &logger : loggerCapture)
297 logger->addMessage(QAbstractTestLogger::Warn,
298 QStringLiteral(
"Maximum amount of warnings exceeded. Use "
299 "-maxwarnings to override."));
305 for (
auto &logger : loggerCapture)
306 logger->addMessage(type, context, message);
308 if (type == QtFatalMsg) {
310
311
312
313
314 QTestResult::addFailure(
"Received a fatal error.", context.file, context.line);
315 QTestLog::leaveTestFunction();
316 QTestLog::stopLogging();
321void QTestLog::enterTestFunction(
const char* function)
323 elapsedFunctionTime.restart();
324 if (printAvailableTags)
327 QTEST_ASSERT(function);
329 for (
auto &logger : QTest::loggers->allLoggers())
330 logger->enterTestFunction(function);
333void QTestLog::enterTestData(QTestData *data)
337 for (
auto &logger : QTest::loggers->allLoggers())
338 logger->enterTestData(data);
341int QTestLog::unhandledIgnoreMessages()
343 const QMutexLocker mutexLocker(&QTest::mutex);
345 QTest::IgnoreResultList *list = QTest::ignoreResultList;
353void QTestLog::leaveTestFunction()
355 if (printAvailableTags)
358 for (
auto &logger : QTest::loggers->allLoggers())
359 logger->leaveTestFunction();
362void QTestLog::printUnhandledIgnoreMessages()
364 const QMutexLocker mutexLocker(&QTest::mutex);
366 QTest::IgnoreResultList *list = QTest::ignoreResultList;
368 if (list->pattern.userType() == QMetaType::QString) {
369 message =
"Did not receive message: \"%1\""_L1.arg(list->pattern.toString());
371#if QT_CONFIG(regularexpression)
372 message =
"Did not receive any message matching: \"%1\""_L1.arg(
373 list->pattern.toRegularExpression().pattern());
376 for (
auto &logger : QTest::loggers->allLoggers())
377 logger->addMessage(QAbstractTestLogger::Info, message);
383void QTestLog::clearIgnoreMessages()
385 const QMutexLocker mutexLocker(&QTest::mutex);
386 QTest::IgnoreResultList::clearList(QTest::ignoreResultList);
389void QTestLog::clearFailOnWarnings()
391 QTest::failOnWarningList.clear();
394void QTestLog::clearCurrentTestState()
396 clearIgnoreMessages();
397 clearFailOnWarnings();
398 QTest::currentTestState = QTest::Unresolved;
401void QTestLog::addPass(
const char *msg)
403 if (printAvailableTags)
407 Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
410 QTest::currentTestState = QTest::Passed;
412 for (
auto &logger : QTest::loggers->allLoggers())
413 logger->addIncident(QAbstractTestLogger::Pass, msg);
416void QTestLog::addFail(
const char *msg,
const char *file,
int line)
420 if (QTest::currentTestState == QTest::Unresolved) {
425 Q_ASSERT(QTest::currentTestState == QTest::Failed
426 || QTest::currentTestState == QTest::Skipped);
431 QTest::currentTestState = QTest::Failed;
432 for (
auto &logger : QTest::loggers->allLoggers())
433 logger->addIncident(QAbstractTestLogger::Fail, msg, file, line);
436void QTestLog::addXFail(
const char *msg,
const char *file,
int line)
442 for (
auto &logger : QTest::loggers->allLoggers())
443 logger->addIncident(QAbstractTestLogger::XFail, msg, file, line);
446void QTestLog::addXPass(
const char *msg,
const char *file,
int line)
450 if (QTest::currentTestState == QTest::Unresolved) {
455 Q_ASSERT(QTest::currentTestState == QTest::Failed
456 || QTest::currentTestState == QTest::Skipped);
459 QTest::currentTestState = QTest::Failed;
460 for (
auto &logger : QTest::loggers->allLoggers())
461 logger->addIncident(QAbstractTestLogger::XPass, msg, file, line);
464void QTestLog::addBPass(
const char *msg)
467 Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
470 QTest::currentTestState = QTest::Suppressed;
472 for (
auto &logger : QTest::loggers->allLoggers())
473 logger->addIncident(QAbstractTestLogger::BlacklistedPass, msg);
476void QTestLog::addBFail(
const char *msg,
const char *file,
int line)
480 if (QTest::currentTestState == QTest::Unresolved) {
485 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
486 || QTest::currentTestState == QTest::Skipped);
489 QTest::currentTestState = QTest::Suppressed;
490 for (
auto &logger : QTest::loggers->allLoggers())
491 logger->addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line);
494void QTestLog::addBXPass(
const char *msg,
const char *file,
int line)
498 if (QTest::currentTestState == QTest::Unresolved) {
503 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
504 || QTest::currentTestState == QTest::Skipped);
507 QTest::currentTestState = QTest::Suppressed;
508 for (
auto &logger : QTest::loggers->allLoggers())
509 logger->addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line);
512void QTestLog::addBXFail(
const char *msg,
const char *file,
int line)
518 for (
auto &logger : QTest::loggers->allLoggers())
519 logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
522void QTestLog::addSkip(
const char *msg,
const char *file,
int line)
526 if (QTest::currentTestState == QTest::Unresolved) {
528 QTest::currentTestState = QTest::Skipped;
532 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
533 || QTest::currentTestState == QTest::Failed
534 || QTest::currentTestState == QTest::Skipped);
539 for (
auto &logger : QTest::loggers->allLoggers())
540 logger->addIncident(QAbstractTestLogger::Skip, msg, file, line);
543void QTestLog::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
545 for (
auto &logger : QTest::loggers->allLoggers())
546 logger->addBenchmarkResults(results);
549void QTestLog::startLogging()
551 elapsedTotalTime.start();
552 elapsedFunctionTime.start();
553 for (
auto &logger : QTest::loggers->allLoggers())
554 logger->startLogging();
555 QTest::oldMessageHandler = qInstallMessageHandler(QTest::messageHandler);
558void QTestLog::stopLogging()
560 qInstallMessageHandler(QTest::oldMessageHandler);
561 for (
auto &logger : QTest::loggers->allLoggers())
562 logger->stopLogging();
564 QTest::loggers->clear();
565 saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage());
568void QTestLog::addLogger(LogMode mode,
const char *filename)
570 if (filename && strcmp(filename,
"-") == 0)
573 QAbstractTestLogger *logger =
nullptr;
575 case QTestLog::Plain:
576 logger =
new QPlainTestLogger(filename);
579 logger =
new QCsvBenchmarkLogger(filename);
582 logger =
new QXmlTestLogger(QXmlTestLogger::Complete, filename);
584 case QTestLog::LightXML:
585 logger =
new QXmlTestLogger(QXmlTestLogger::Light, filename);
587 case QTestLog::JUnitXML:
588 logger =
new QJUnitTestLogger(filename);
590 case QTestLog::TeamCity:
591 logger =
new QTeamCityLogger(filename);
594 logger =
new QTapTestLogger(filename);
596#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
597 case QTestLog::Apple:
598 logger =
new QAppleTestLogger;
601#if defined(HAVE_XCTEST)
602 case QTestLog::XCTest:
603 logger =
new QXcodeTestLogger;
608 QTEST_ASSERT(logger);
609 addLogger(std::unique_ptr<QAbstractTestLogger>{ logger });
613
614
615
616
617
618void QTestLog::addLogger(std::unique_ptr<QAbstractTestLogger> logger)
620 QTEST_ASSERT(logger);
621 QTest::loggers()->addLogger(std::move(logger));
624bool QTestLog::hasLoggers()
626 return !QTest::loggers()->allLoggers().isEmpty();
630
631
632
633
634bool QTestLog::isRepeatSupported()
636 for (
auto &logger : QTest::loggers->allLoggers())
637 if (!logger->isRepeatSupported())
643bool QTestLog::loggerUsingStdout()
645 auto loggersCapture = QTest::loggers->allLoggers();
646 return q20::ranges::any_of(loggersCapture.begin(), loggersCapture.end(), [](
auto &logger) {
647 return logger->isLoggingToStdout();
651void QTestLog::warn(
const char *msg,
const char *file,
int line)
655 for (
auto &logger : QTest::loggers->allLoggers())
656 logger->addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line);
659void QTestLog::info(
const char *msg,
const char *file,
int line)
663 for (
auto &logger : QTest::loggers->allLoggers())
664 logger->addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line);
667void QTestLog::setVerboseLevel(
int level)
669 QTest::verbosity = level;
672int QTestLog::verboseLevel()
674 return QTest::verbosity;
677void QTestLog::ignoreMessage(QtMsgType type,
const char *msg)
681 const QMutexLocker mutexLocker(&QTest::mutex);
682 QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QString::fromUtf8(msg));
685#if QT_CONFIG(regularexpression)
686void QTestLog::ignoreMessage(QtMsgType type,
const QRegularExpression &expression)
688 QTEST_ASSERT(expression.isValid());
690 const QMutexLocker mutexLocker(&QTest::mutex);
691 QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QVariant(expression));
695void QTestLog::failOnWarning()
697 QTest::failOnWarningList.push_back({});
700void QTestLog::failOnWarning(
const char *msg)
702 QTest::failOnWarningList.push_back(QString::fromUtf8(msg));
705#if QT_CONFIG(regularexpression)
706void QTestLog::failOnWarning(
const QRegularExpression &expression)
708 QTEST_ASSERT(expression.isValid());
710 QTest::failOnWarningList.push_back(QVariant::fromValue(expression));
714void QTestLog::setMaxWarnings(
int m)
716 QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2;
719bool QTestLog::printAvailableTags =
false;
721void QTestLog::setPrintAvailableTagsMode()
723 printAvailableTags =
true;
726int QTestLog::passCount()
728 return QTest::passes;
731int QTestLog::failCount()
736int QTestLog::skipCount()
741int QTestLog::blacklistCount()
743 return QTest::blacklists;
746int QTestLog::totalCount()
748 return passCount() + failCount() + skipCount() + blacklistCount();
751void QTestLog::resetCounters()
758void QTestLog::setInstalledTestCoverage(
bool installed)
760 QTest::installedTestCoverage = installed;
763bool QTestLog::installedTestCoverage()
765 return QTest::installedTestCoverage;
768qint64 QTestLog::nsecsTotalTime()
770 return elapsedTotalTime.nsecsElapsed();
773qint64 QTestLog::nsecsFunctionTime()
775 return elapsedFunctionTime.nsecsElapsed();
780#include "moc_qtestlog_p.cpp"
static bool handleIgnoredMessage(QtMsgType type, const QString &message)
static bool handleFailOnWarning(const QMessageLogContext &context, const QString &message)
static IgnoreResultList * ignoreResultList
static bool installedTestCoverage
static QtMessageHandler oldMessageHandler
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
static std::vector< QVariant > failOnWarningList
static void saveCoverageTool(const char *appname, bool testfailed, bool installedTestCoverage)
IgnoreResultList(QtMsgType tp, const QVariant &patternIn)
static void clearList(IgnoreResultList *&list)
bool matches(QtMsgType tp, const QString &message) const
static bool stringsMatch(const QString &expected, const QString &actual)
static void append(IgnoreResultList *&list, QtMsgType type, const QVariant &patternIn)