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 Q_DECL_UNINITIALIZED
char msg[maxMsgLen];
277 std::snprintf(msg, maxMsgLen,
"Received a warning that resulted in a failure:\n%s",
278 qPrintable(message));
279 QTestResult::addFailure(msg, context.file, context.line);
288#if QT_VERSION_MAJOR == 7
|| defined(QT_BOOTSTRAPPED)
289 return type >= QtWarningMsg;
301 Q_UNREACHABLE_RETURN(
false);
305 static void messageHandler(QtMsgType type,
const QMessageLogContext & context,
const QString &message)
309 if (loggers.isDestroyed() || loggers->allLoggers().isEmpty()) {
312 Q_ASSERT(oldMessageHandler);
317 if (handleIgnoredMessage(type, message)) {
322 if (isWarnOrWorse(type) && handleFailOnWarning(context, message)) {
323 if (type == QtFatalMsg)
328 auto loggerCapture = loggers->allLoggers();
330 if (type != QtFatalMsg) {
331 if (counter.loadRelaxed() <= 0)
334 if (!counter.deref()) {
335 for (
auto &logger : loggerCapture)
336 logger->addMessage(QAbstractTestLogger::Warn,
337 QStringLiteral(
"Maximum amount of warnings exceeded. Use "
338 "-maxwarnings to override."));
344 for (
auto &logger : loggerCapture)
345 logger->addMessage(type, context, message);
347 if (type == QtFatalMsg) {
348 QTestResult::addFailure(
"Received a fatal error.", context.file, context.line);
354void QTestLog::enterTestFunction(
const char* function)
357 QMutexLocker locker(&elapsedTimersMutex);
358 elapsedFunctionTime.start();
360 if (printAvailableTags)
363 QTEST_ASSERT(function);
365 for (
auto &logger : QTest::loggers->allLoggers())
366 logger->enterTestFunction(function);
369void QTestLog::enterTestData(QTestData *data)
373 for (
auto &logger : QTest::loggers->allLoggers())
374 logger->enterTestData(data);
377int QTestLog::unhandledIgnoreMessages()
379 const QMutexLocker mutexLocker(&QTest::mutex);
381 QTest::IgnoreResultList *list = QTest::ignoreResultList;
389void QTestLog::leaveTestFunction()
391 if (printAvailableTags)
394 for (
auto &logger : QTest::loggers->allLoggers())
395 logger->leaveTestFunction();
398void QTestLog::printUnhandledIgnoreMessages()
400 const QMutexLocker mutexLocker(&QTest::mutex);
402 QTest::IgnoreResultList *list = QTest::ignoreResultList;
404 if (
const auto *text = get_if<QString>(&list->pattern)) {
405 message =
"Did not receive message: \"%1\""_L1.arg(*text);
406#if QT_CONFIG(regularexpression)
407 }
else if (
const auto *regex = get_if<QRegularExpression>(&list->pattern)) {
408 message =
"Did not receive any message matching: \"%1\""_L1.arg(regex->pattern());
412 message =
"Missing message of unrecognized pattern type: \"%1\""_L1.arg(
413 list->pattern.metaType().name());
415 for (
auto &logger : QTest::loggers->allLoggers())
416 logger->addMessage(QAbstractTestLogger::Info, message);
422void QTestLog::clearIgnoreMessages()
424 const QMutexLocker mutexLocker(&QTest::mutex);
425 QTest::IgnoreResultList::clearList(QTest::ignoreResultList);
428void QTestLog::clearFailOnWarnings()
430 QTest::failOnWarningList.clear();
433void QTestLog::clearCurrentTestState()
435 clearIgnoreMessages();
436 clearFailOnWarnings();
437 QTest::currentTestState = QTest::Unresolved;
440void QTestLog::addPass(
const char *msg)
442 if (printAvailableTags)
446 Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
449 QTest::currentTestState = QTest::Passed;
451 for (
auto &logger : QTest::loggers->allLoggers())
452 logger->addIncident(QAbstractTestLogger::Pass, msg);
455void QTestLog::addFail(
const char *msg,
const char *file,
int line)
459 if (QTest::currentTestState == QTest::Unresolved) {
464 Q_ASSERT(QTest::currentTestState == QTest::Failed
465 || QTest::currentTestState == QTest::Skipped);
470 QTest::currentTestState = QTest::Failed;
471 for (
auto &logger : QTest::loggers->allLoggers())
472 logger->addIncident(QAbstractTestLogger::Fail, msg, file, line);
475void QTestLog::addXFail(
const char *msg,
const char *file,
int line)
481 for (
auto &logger : QTest::loggers->allLoggers())
482 logger->addIncident(QAbstractTestLogger::XFail, msg, file, line);
485void QTestLog::addXPass(
const char *msg,
const char *file,
int line)
489 if (QTest::currentTestState == QTest::Unresolved) {
494 Q_ASSERT(QTest::currentTestState == QTest::Failed
495 || QTest::currentTestState == QTest::Skipped);
498 QTest::currentTestState = QTest::Failed;
499 for (
auto &logger : QTest::loggers->allLoggers())
500 logger->addIncident(QAbstractTestLogger::XPass, msg, file, line);
503void QTestLog::addBPass(
const char *msg)
506 Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
509 QTest::currentTestState = QTest::Suppressed;
511 for (
auto &logger : QTest::loggers->allLoggers())
512 logger->addIncident(QAbstractTestLogger::BlacklistedPass, msg);
515void QTestLog::addBFail(
const char *msg,
const char *file,
int line)
519 if (QTest::currentTestState == QTest::Unresolved) {
524 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
525 || QTest::currentTestState == QTest::Skipped);
528 QTest::currentTestState = QTest::Suppressed;
529 for (
auto &logger : QTest::loggers->allLoggers())
530 logger->addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line);
533void QTestLog::addBXPass(
const char *msg,
const char *file,
int line)
537 if (QTest::currentTestState == QTest::Unresolved) {
542 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
543 || QTest::currentTestState == QTest::Skipped);
546 QTest::currentTestState = QTest::Suppressed;
547 for (
auto &logger : QTest::loggers->allLoggers())
548 logger->addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line);
551void QTestLog::addBXFail(
const char *msg,
const char *file,
int line)
557 for (
auto &logger : QTest::loggers->allLoggers())
558 logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
561void QTestLog::addSkip(
const char *msg,
const char *file,
int line)
565 if (QTest::currentTestState == QTest::Unresolved) {
567 QTest::currentTestState = QTest::Skipped;
571 Q_ASSERT(QTest::currentTestState == QTest::Suppressed
572 || QTest::currentTestState == QTest::Failed
573 || QTest::currentTestState == QTest::Skipped);
578 for (
auto &logger : QTest::loggers->allLoggers())
579 logger->addIncident(QAbstractTestLogger::Skip, msg, file, line);
582void QTestLog::addBenchmarkResults(
const QList<QBenchmarkResult> &results)
584 for (
auto &logger : QTest::loggers->allLoggers())
585 logger->addBenchmarkResults(results);
588void QTestLog::startLogging()
591 QMutexLocker locker(&elapsedTimersMutex);
592 elapsedTotalTime.start();
593 elapsedFunctionTime.start();
595 for (
auto &logger : QTest::loggers->allLoggers())
596 logger->startLogging();
597 QTest::oldMessageHandler = qInstallMessageHandler(QTest::messageHandler);
600void QTestLog::stopLogging()
602 qInstallMessageHandler(QTest::oldMessageHandler);
603 for (
auto &logger : QTest::loggers->allLoggers())
604 logger->stopLogging();
606 QTest::loggers->clear();
607 saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage());
610void QTestLog::addLogger(LogMode mode,
const char *filename)
612 if (filename && strcmp(filename,
"-") == 0)
615 QAbstractTestLogger *logger =
nullptr;
617 case QTestLog::Plain:
618 logger =
new QPlainTestLogger(filename);
621 logger =
new QCsvBenchmarkLogger(filename);
624 logger =
new QXmlTestLogger(QXmlTestLogger::Complete, filename);
626 case QTestLog::LightXML:
627 logger =
new QXmlTestLogger(QXmlTestLogger::Light, filename);
629 case QTestLog::JUnitXML:
630 logger =
new QJUnitTestLogger(filename);
632 case QTestLog::TeamCity:
633 logger =
new QTeamCityLogger(filename);
636 logger =
new QTapTestLogger(filename);
638#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
639 case QTestLog::Apple:
640 logger =
new QAppleTestLogger;
643#if defined(HAVE_XCTEST)
644 case QTestLog::XCTest:
645 logger =
new QXcodeTestLogger;
650 QTEST_ASSERT(logger);
651 addLogger(std::unique_ptr<QAbstractTestLogger>{ logger });
655
656
657
658
659
660void QTestLog::addLogger(std::unique_ptr<QAbstractTestLogger> logger)
662 QTEST_ASSERT(logger);
663 QTest::loggers()->addLogger(std::move(logger));
666bool QTestLog::hasLoggers()
668 return !QTest::loggers()->allLoggers().isEmpty();
672
673
674
675
676bool QTestLog::isRepeatSupported()
678 for (
auto &logger : QTest::loggers->allLoggers())
679 if (!logger->isRepeatSupported())
685bool QTestLog::loggerUsingStdout()
687 auto loggersCapture = QTest::loggers->allLoggers();
688 return q20::ranges::any_of(loggersCapture.begin(), loggersCapture.end(), [](
auto &logger) {
689 return logger->isLoggingToStdout();
693void QTestLog::warn(
const char *msg,
const char *file,
int line)
697 for (
auto &logger : QTest::loggers->allLoggers())
698 logger->addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line);
701void QTestLog::info(
const char *msg,
const char *file,
int line)
705 for (
auto &logger : QTest::loggers->allLoggers())
706 logger->addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line);
709void QTestLog::setVerboseLevel(
int level)
711 QTest::verbosity = level;
714int QTestLog::verboseLevel()
716 return QTest::verbosity;
719void QTestLog::ignoreMessage(QtMsgType type,
const char *msg)
723 const QMutexLocker mutexLocker(&QTest::mutex);
724 QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QString::fromUtf8(msg));
727#if QT_CONFIG(regularexpression)
728void QTestLog::ignoreMessage(QtMsgType type,
const QRegularExpression &expression)
730 QTEST_ASSERT(expression.isValid());
732 const QMutexLocker mutexLocker(&QTest::mutex);
733 QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QVariant(expression));
737void QTestLog::failOnWarning()
739 QTest::failOnWarningList.push_back({});
742void QTestLog::failOnWarning(
const char *msg)
744 QTest::failOnWarningList.push_back(QString::fromUtf8(msg));
747#if QT_CONFIG(regularexpression)
748void QTestLog::failOnWarning(
const QRegularExpression &expression)
750 QTEST_ASSERT(expression.isValid());
752 QTest::failOnWarningList.push_back(QVariant::fromValue(expression));
756void QTestLog::setMaxWarnings(
int m)
758 QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2;
761bool QTestLog::printAvailableTags =
false;
763void QTestLog::setPrintAvailableTagsMode()
765 printAvailableTags =
true;
768int QTestLog::passCount()
770 return QTest::passes;
773int QTestLog::failCount()
778int QTestLog::skipCount()
783int QTestLog::blacklistCount()
785 return QTest::blacklists;
788int QTestLog::totalCount()
790 return passCount() + failCount() + skipCount() + blacklistCount();
793void QTestLog::resetCounters()
800void QTestLog::setInstalledTestCoverage(
bool installed)
802 QTest::installedTestCoverage = installed;
805bool QTestLog::installedTestCoverage()
807 return QTest::installedTestCoverage;
810qint64 QTestLog::nsecsTotalTime()
812 QMutexLocker locker(&elapsedTimersMutex);
813 return elapsedTotalTime.nsecsElapsed();
816qint64 QTestLog::nsecsFunctionTime()
818 QMutexLocker locker(&elapsedTimersMutex);
819 return elapsedFunctionTime.nsecsElapsed();
824#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)