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 QBasicMutex elapsedTimersMutex;
73Q_CONSTINIT
static QElapsedTimer elapsedFunctionTime;
74Q_CONSTINIT
static QElapsedTimer elapsedTotalTime;
79 using LoggersContainer = std::vector<std::shared_ptr<QAbstractTestLogger>>;
80 using SharedLoggersContainer = std::shared_ptr<
const LoggersContainer>;
83 void addLogger(std::unique_ptr<QAbstractTestLogger> logger)
86 const SharedLoggersContainer currentLoggers = load();
87 auto newLoggers = currentLoggers
88 ? std::make_shared<LoggersContainer>(*currentLoggers)
89 : std::make_shared<LoggersContainer>();
90 newLoggers->emplace_back(
std::move(logger));
91 store(std::move(newLoggers));
94 void clear() { store(SharedLoggersContainer{}); }
96 auto allLoggers()
const
100 const SharedLoggersContainer loggers;
104 return loggers ? loggers->cbegin() : LoggersContainer::const_iterator{};
108 return loggers ? loggers->cend() : LoggersContainer::const_iterator{};
110 bool isEmpty()
const {
return loggers ? loggers->empty() :
true; }
113 return LoggersRange{ load() };
117#ifdef __cpp_lib_atomic_shared_ptr
118 SharedLoggersContainer load()
const {
return loggers.load(std::memory_order_acquire); }
119 void store(SharedLoggersContainer newLoggers)
121 loggers.store(std::move(newLoggers), std::memory_order_release);
123 std::atomic<SharedLoggersContainer> loggers =
nullptr;
125 SharedLoggersContainer load()
const
127 return std::atomic_load_explicit(&loggers, std::memory_order_acquire);
129 void store(SharedLoggersContainer newLoggers)
131 std::atomic_store_explicit(&loggers, std::move(newLoggers), std::memory_order_release);
133 SharedLoggersContainer loggers;
174 static bool stringsMatch(
const QString &expected,
const QString &actual)
176 if (expected == actual)
182 if (expected.endsWith(u' '))
183 return actual == QStringView{expected}.left(expected.size() - 1);
188 inline bool matches(QtMsgType tp,
const QString &message)
const
192#if QT_CONFIG(regularexpression)
193 if (
const auto *regex = get_if<QRegularExpression>(&pattern))
194 return regex->match(message).hasMatch();
196 Q_ASSERT(pattern.metaType() == QMetaType::fromType<QString>());
197 return stringsMatch(pattern.toString(), message);
206 Q_CONSTINIT
static QBasicMutex
mutex;
210 Q_GLOBAL_STATIC(LoggerRegistry, loggers)
220 const QMutexLocker mutexLocker(&QTest::mutex);
227 if (list->matches(type, message)) {
247
248
249
250
251
252 QTestLog::leaveTestFunction();
253 QTestLog::stopLogging();
260 for (
const auto &pattern : failOnWarningList) {
261 if (
const auto *text = get_if<QString>(&pattern)) {
262 if (message != *text)
264#if QT_CONFIG(regularexpression)
265 }
else if (
const auto *regex = get_if<QRegularExpression>(&pattern)) {
266 if (!message.contains(*regex))
271 Q_ASSERT(pattern.isNull());
274 const size_t maxMsgLen = 1024;
275 char msg[maxMsgLen] = {
'\0'};
276 std::snprintf(msg, maxMsgLen,
"Received a warning that resulted in a failure:\n%s",
277 qPrintable(message));
278 QTestResult::addFailure(msg, context.file, context.line);
287#if QT_VERSION_MAJOR == 7
|| defined(QT_BOOTSTRAPPED)
288 return type >= QtWarningMsg;
300 Q_UNREACHABLE_RETURN(
false);
304 static void messageHandler(QtMsgType type,
const QMessageLogContext & context,
const QString &message)
308 auto loggerCapture = loggers->allLoggers();
310 if (loggerCapture.isEmpty()) {
313 Q_ASSERT(oldMessageHandler);
318 if (handleIgnoredMessage(type, message)) {
323 if (isWarnOrWorse(type) && handleFailOnWarning(context, message)) {
324 if (type == QtFatalMsg)
329 if (type != QtFatalMsg) {
330 if (counter.loadRelaxed() <= 0)
333 if (!counter.deref()) {
334 for (
auto &logger : loggerCapture)
335 logger->addMessage(QAbstractTestLogger::Warn,
336 QStringLiteral(
"Maximum amount of warnings exceeded. Use "
337 "-maxwarnings to override."));
343 for (
auto &logger : loggerCapture)
344 logger->addMessage(type, context, message);
346 if (type == QtFatalMsg) {
347 QTestResult::addFailure(
"Received a fatal error.", context.file, context.line);
353void QTestLog::enterTestFunction(
const char* function)
356 QMutexLocker locker(&elapsedTimersMutex);
357 elapsedFunctionTime.start();
359 if (printAvailableTags)
362 QTEST_ASSERT(function);
364 for (
auto &logger : QTest::loggers->allLoggers())
365 logger->enterTestFunction(function);
368void QTestLog::enterTestData(QTestData *data)
372 for (
auto &logger : QTest::loggers->allLoggers())
373 logger->enterTestData(data);
376int QTestLog::unhandledIgnoreMessages()
378 const QMutexLocker mutexLocker(&QTest::mutex);
380 QTest::IgnoreResultList *list = QTest::ignoreResultList;
388void QTestLog::leaveTestFunction()
390 if (printAvailableTags)
393 for (
auto &logger : QTest::loggers->allLoggers())
394 logger->leaveTestFunction();
397void QTestLog::printUnhandledIgnoreMessages()
399 const QMutexLocker mutexLocker(&QTest::mutex);
401 QTest::IgnoreResultList *list = QTest::ignoreResultList;
403 if (
const auto *text = get_if<QString>(&list->pattern)) {
404 message =
"Did not receive message: \"%1\""_L1.arg(*text);
405#if QT_CONFIG(regularexpression)
406 }
else if (
const auto *regex = get_if<QRegularExpression>(&list->pattern)) {
407 message =
"Did not receive any message matching: \"%1\""_L1.arg(regex->pattern());
411 message =
"Missing message of unrecognized pattern type: \"%1\""_L1.arg(
412 list->pattern.metaType().name());
414 for (
auto &logger : QTest::loggers->allLoggers())
415 logger->addMessage(QAbstractTestLogger::Info, message);
421void QTestLog::clearIgnoreMessages()
423 const QMutexLocker mutexLocker(&QTest::mutex);
424 QTest::IgnoreResultList::clearList(QTest::ignoreResultList);
427void QTestLog::clearFailOnWarnings()
429 QTest::failOnWarningList.clear();
432void QTestLog::clearCurrentTestState()
434 clearIgnoreMessages();
435 clearFailOnWarnings();
436 QTest::currentTestState = QTest::Unresolved;
439void QTestLog::addPass(
const char *msg)
441 if (printAvailableTags)
445 Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
448 QTest::currentTestState = QTest::Passed;
450 for (
auto &logger : QTest::loggers->allLoggers())
451 logger->addIncident(QAbstractTestLogger::Pass, msg);
454void QTestLog::addFail(
const char *msg,
const char *file,
int line)
458 if (QTest::currentTestState == QTest::Unresolved) {
463 Q_ASSERT(QTest::currentTestState == QTest::Failed
464 || QTest::currentTestState == QTest::Skipped);
469 QTest::currentTestState = QTest::Failed;
470 for (
auto &logger : QTest::loggers->allLoggers())
471 logger->addIncident(QAbstractTestLogger::Fail, msg, file, line);
474void QTestLog::addXFail(
const char *msg,
const char *file,
int line)
480 for (
auto &logger : QTest::loggers->allLoggers())
481 logger->addIncident(QAbstractTestLogger::XFail, msg, file, line);
484void QTestLog::addXPass(
const char *msg,
const char *file,
int line)
488 if (QTest::currentTestState == QTest::Unresolved) {
493 Q_ASSERT(QTest::currentTestState == QTest::Failed
494 || QTest::currentTestState == QTest::Skipped);
497 QTest::currentTestState = QTest::Failed;
498 for (
auto &logger : QTest::loggers->allLoggers())
499 logger->addIncident(QAbstractTestLogger::XPass, msg, file, line);
502void QTestLog::addBPass(
const char *msg)
505 Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
508 QTest::currentTestState = QTest::Suppressed;
510 for (
auto &logger : QTest::loggers->allLoggers())
511 logger->addIncident(QAbstractTestLogger::BlacklistedPass, msg);
514void QTestLog::addBFail(
const char *msg,
const char *file,
int line)
518 if (QTest::currentTestState == QTest::Unresolved) {
523 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
524 || QTest::currentTestState == QTest::Skipped);
527 QTest::currentTestState = QTest::Suppressed;
528 for (
auto &logger : QTest::loggers->allLoggers())
529 logger->addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line);
532void QTestLog::addBXPass(
const char *msg,
const char *file,
int line)
536 if (QTest::currentTestState == QTest::Unresolved) {
541 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
542 || QTest::currentTestState == QTest::Skipped);
545 QTest::currentTestState = QTest::Suppressed;
546 for (
auto &logger : QTest::loggers->allLoggers())
547 logger->addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line);
550void QTestLog::addBXFail(
const char *msg,
const char *file,
int line)
556 for (
auto &logger : QTest::loggers->allLoggers())
557 logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
560void QTestLog::addSkip(
const char *msg,
const char *file,
int line)
564 if (QTest::currentTestState == QTest::Unresolved) {
566 QTest::currentTestState = QTest::Skipped;
570 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
571 || QTest::currentTestState == QTest::Failed
572 || QTest::currentTestState == QTest::Skipped);
577 for (
auto &logger : QTest::loggers->allLoggers())
578 logger->addIncident(QAbstractTestLogger::Skip, msg, file, line);
581void QTestLog::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
583 for (
auto &logger : QTest::loggers->allLoggers())
584 logger->addBenchmarkResults(results);
587void QTestLog::startLogging()
590 QMutexLocker locker(&elapsedTimersMutex);
591 elapsedTotalTime.start();
592 elapsedFunctionTime.start();
594 for (
auto &logger : QTest::loggers->allLoggers())
595 logger->startLogging();
596 QTest::oldMessageHandler = qInstallMessageHandler(QTest::messageHandler);
599void QTestLog::stopLogging()
601 qInstallMessageHandler(QTest::oldMessageHandler);
602 for (
auto &logger : QTest::loggers->allLoggers())
603 logger->stopLogging();
605 QTest::loggers->clear();
606 saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage());
609void QTestLog::addLogger(LogMode mode,
const char *filename)
611 if (filename && strcmp(filename,
"-") == 0)
614 QAbstractTestLogger *logger =
nullptr;
616 case QTestLog::Plain:
617 logger =
new QPlainTestLogger(filename);
620 logger =
new QCsvBenchmarkLogger(filename);
623 logger =
new QXmlTestLogger(QXmlTestLogger::Complete, filename);
625 case QTestLog::LightXML:
626 logger =
new QXmlTestLogger(QXmlTestLogger::Light, filename);
628 case QTestLog::JUnitXML:
629 logger =
new QJUnitTestLogger(filename);
631 case QTestLog::TeamCity:
632 logger =
new QTeamCityLogger(filename);
635 logger =
new QTapTestLogger(filename);
637#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
638 case QTestLog::Apple:
639 logger =
new QAppleTestLogger;
642#if defined(HAVE_XCTEST)
643 case QTestLog::XCTest:
644 logger =
new QXcodeTestLogger;
649 QTEST_ASSERT(logger);
650 addLogger(std::unique_ptr<QAbstractTestLogger>{ logger });
654
655
656
657
658
659void QTestLog::addLogger(std::unique_ptr<QAbstractTestLogger> logger)
661 QTEST_ASSERT(logger);
662 QTest::loggers()->addLogger(std::move(logger));
665bool QTestLog::hasLoggers()
667 return !QTest::loggers()->allLoggers().isEmpty();
671
672
673
674
675bool QTestLog::isRepeatSupported()
677 for (
auto &logger : QTest::loggers->allLoggers())
678 if (!logger->isRepeatSupported())
684bool QTestLog::loggerUsingStdout()
686 auto loggersCapture = QTest::loggers->allLoggers();
687 return q20::ranges::any_of(loggersCapture.begin(), loggersCapture.end(), [](
auto &logger) {
688 return logger->isLoggingToStdout();
692void QTestLog::warn(
const char *msg,
const char *file,
int line)
696 for (
auto &logger : QTest::loggers->allLoggers())
697 logger->addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line);
700void QTestLog::info(
const char *msg,
const char *file,
int line)
704 for (
auto &logger : QTest::loggers->allLoggers())
705 logger->addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line);
708void QTestLog::setVerboseLevel(
int level)
710 QTest::verbosity = level;
713int QTestLog::verboseLevel()
715 return QTest::verbosity;
718void QTestLog::ignoreMessage(QtMsgType type,
const char *msg)
722 const QMutexLocker mutexLocker(&QTest::mutex);
723 QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QString::fromUtf8(msg));
726#if QT_CONFIG(regularexpression)
727void QTestLog::ignoreMessage(QtMsgType type,
const QRegularExpression &expression)
729 QTEST_ASSERT(expression.isValid());
731 const QMutexLocker mutexLocker(&QTest::mutex);
732 QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QVariant(expression));
736void QTestLog::failOnWarning()
738 QTest::failOnWarningList.push_back({});
741void QTestLog::failOnWarning(
const char *msg)
743 QTest::failOnWarningList.push_back(QString::fromUtf8(msg));
746#if QT_CONFIG(regularexpression)
747void QTestLog::failOnWarning(
const QRegularExpression &expression)
749 QTEST_ASSERT(expression.isValid());
751 QTest::failOnWarningList.push_back(QVariant::fromValue(expression));
755void QTestLog::setMaxWarnings(
int m)
757 QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2;
760bool QTestLog::printAvailableTags =
false;
762void QTestLog::setPrintAvailableTagsMode()
764 printAvailableTags =
true;
767int QTestLog::passCount()
769 return QTest::passes;
772int QTestLog::failCount()
777int QTestLog::skipCount()
782int QTestLog::blacklistCount()
784 return QTest::blacklists;
787int QTestLog::totalCount()
789 return passCount() + failCount() + skipCount() + blacklistCount();
792void QTestLog::resetCounters()
799void QTestLog::setInstalledTestCoverage(
bool installed)
801 QTest::installedTestCoverage = installed;
804bool QTestLog::installedTestCoverage()
806 return QTest::installedTestCoverage;
809qint64 QTestLog::nsecsTotalTime()
811 QMutexLocker locker(&elapsedTimersMutex);
812 return elapsedTotalTime.nsecsElapsed();
815qint64 QTestLog::nsecsFunctionTime()
817 QMutexLocker locker(&elapsedTimersMutex);
818 return elapsedFunctionTime.nsecsElapsed();
823#include "moc_qtestlog_p.cpp"
static bool handleIgnoredMessage(QtMsgType type, const QString &message)
static bool handleFailOnWarning(const QMessageLogContext &context, const QString &message)
static void handleFatal()
static IgnoreResultList * ignoreResultList
static bool installedTestCoverage
static QtMessageHandler oldMessageHandler
static constexpr bool isWarnOrWorse(QtMsgType type)
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)