Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtestcase.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <QtTest/qtestcase.h>
6#include <QtTest/private/qtestcase_p.h>
7#include <QtTest/qtestassert.h>
8
9#include <QtCore/qbytearray.h>
10#include <QtCore/qcoreapplication.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qdir.h>
13#include <QtCore/qdirlisting.h>
14#if QT_CONFIG(future)
15#include <QtCore/qexception.h>
16#endif
17#include <QtCore/qfile.h>
18#include <QtCore/qfileinfo.h>
19#include <QtCore/qfloat16.h>
20#include <QtCore/qlibraryinfo.h>
21#include <QtCore/qlist.h>
22#include <QtCore/qmetaobject.h>
23#include <QtCore/qobject.h>
24#include <QtCore/qstringlist.h>
25#include <QtCore/qtemporarydir.h>
26#include <QtCore/qthread.h>
27#include <QtCore/qvarlengtharray.h>
28#include <QtCore/private/qlocking_p.h>
29#include <QtCore/private/qtools_p.h>
30#include <QtCore/private/qwaitcondition_p.h>
31
32#include <QtCore/qtestsupport_core.h>
33
34#include <QtTest/private/qtestlog_p.h>
35#include <QtTest/private/qtesttable_p.h>
36#include <QtTest/qtestdata.h>
37#include <QtTest/private/qtestresult_p.h>
38#include <QtTest/private/qsignaldumper_p.h>
39#include <QtTest/private/qbenchmark_p.h>
40#if QT_CONFIG(batch_test_support)
41#include <QtTest/private/qtestregistry_p.h>
42#endif // QT_CONFIG(batch_test_support)
43#include <QtTest/private/cycle_include_p.h>
44#include <QtTest/private/qtestblacklist_p.h>
45#include <QtTest/private/qtestcrashhandler_p.h>
46#if defined(HAVE_XCTEST)
47#include <QtTest/private/qxctestlogger_p.h>
48#endif
49#if defined Q_OS_MACOS
50#include <QtTest/private/qtestutil_macos_p.h>
51#endif
52
53#if defined(Q_OS_DARWIN)
54#include <QtTest/private/qappletestlogger_p.h>
55#endif
56
57#include <algorithm>
58#include <array>
59#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
60# include <charconv>
61#else
62// Broken implementation, causes link failures just by #include'ing!
63# undef __cpp_lib_to_chars // in case <version> was included
64#endif
65#include <chrono>
66#include <cmath>
67#include <cstdio>
68#include <limits>
69#include <memory>
70#include <mutex>
71#include <numeric>
72#include <optional>
73
74#include <stdarg.h>
75#include <stdlib.h>
76
77#if defined(Q_OS_LINUX)
78#include <sys/types.h>
79#include <fcntl.h>
80#endif
81
82#ifdef Q_OS_WIN
83# include <iostream>
84# if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
85# include <crtdbg.h>
86# endif
87#include <qt_windows.h> // for Sleep
88#endif
89#ifdef Q_OS_UNIX
90#include <QtCore/private/qcore_unix_p.h>
91
92#include <errno.h>
93#if __has_include(<paths.h>)
94# include <paths.h>
95#endif
96#include <time.h>
97#include <sys/mman.h>
98#include <sys/uio.h>
99#include <sys/wait.h>
100#include <unistd.h>
101# if !defined(Q_OS_INTEGRITY)
102# include <sys/resource.h>
103# endif
104#endif
105
106#if defined(Q_OS_MACOS)
107#include <IOKit/pwr_mgt/IOPMLib.h>
108#include <mach/task.h>
109#include <mach/mach_init.h>
110#include <CoreFoundation/CFPreferences.h>
111#endif
112
113#if defined(Q_OS_WASM)
114#include <emscripten.h>
115#endif
116
117#ifdef Q_OS_ANDROID
118#include <QtCore/QStandardPaths>
119#endif
120
121#include <vector>
122
123QT_BEGIN_NAMESPACE
124
125using namespace Qt::StringLiterals;
126
127using QtMiscUtils::toHexUpper;
128using QtMiscUtils::fromHex;
129
130static bool installCoverageTool(const char * appname, const char * testname)
131{
132#if defined(__COVERAGESCANNER__) && !QT_CONFIG(testlib_selfcover)
133 if (!qEnvironmentVariableIsEmpty("QT_TESTCOCOON_ACTIVE"))
134 return false;
135 // Set environment variable QT_TESTCOCOON_ACTIVE to prevent an eventual subtest from
136 // being considered as a stand-alone test regarding the coverage analysis.
137 qputenv("QT_TESTCOCOON_ACTIVE", "1");
138
139 // Install Coverage Tool
140 __coveragescanner_install(appname);
141 __coveragescanner_testname(testname);
142 __coveragescanner_clear();
143 return true;
144#else
145 Q_UNUSED(appname);
146 Q_UNUSED(testname);
147 return false;
148#endif
149}
150
151static bool isValidSlot(const QMetaMethod &sl)
152{
153 if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0
154 || sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot)
155 return false;
156 const QByteArray name = sl.name();
157 return !(name.isEmpty() || name.endsWith("_data")
158 || name == "initTestCase" || name == "cleanupTestCase"
159 || name == "init" || name == "cleanup");
160}
161
162namespace QTestPrivate
163{
165}
166
167namespace {
168
169#if !QT_CONFIG(future)
170using QException = std::exception;
171#endif
172
173class TestFailedException : public QException // clazy:exclude=copyable-polymorphic
174{
175public:
176 TestFailedException() = default;
177 ~TestFailedException() override = default;
178
179 const char *what() const noexcept override { return "QtTest: test failed"; }
180
181#if QT_CONFIG(future)
182 TestFailedException *clone() const override { return new TestFailedException(); }
183 void raise() const override { throw TestFailedException(); }
184#endif
185};
186
187class TestSkippedException : public QException // clazy:exclude=copyable-polymorphic
188{
189public:
190 TestSkippedException() = default;
191 ~TestSkippedException() override = default;
192
193 const char *what() const noexcept override { return "QtTest: test was skipped"; }
194
195#if QT_CONFIG(future)
196 TestSkippedException *clone() const override { return new TestSkippedException(); }
197 void raise() const override { throw TestSkippedException(); }
198#endif
199};
200
201} // unnamed namespace
202
203namespace QTest
204{
205
208
209Q_CONSTINIT static QBasicAtomicInt g_throwOnFail = Q_BASIC_ATOMIC_INITIALIZER(0);
210Q_CONSTINIT static QBasicAtomicInt g_throwOnSkip = Q_BASIC_ATOMIC_INITIALIZER(0);
211
217
223
224/*
225//! [return-void]
226 Defining this macro is useful if you wish to use \1 in functions that have
227 a non-\c{void} return type. Without this macro defined, \2, for example,
228 expands to a statement including \c{return;}, so it cannot be used in
229 functions (or lambdas) that return something else than \c{void}, e.g.
230 \l{QString}. This includes the case where throwing exceptions is enabled
231 only at runtime (using \3). With this macro defined, \2 instead expands to
232 a statement without a \c{return;}, which is usable from any function.
233//! [return-void]
234*/
235
236/*!
237 \since 6.8
238 \macro QTEST_THROW_ON_FAIL
239 \relates QTest
240
241 When defined, QCOMPARE()/QVERIFY() etc always throw on failure.
242 QTest::setThrowOnFail() then no longer has any effect.
243
244 \include qtestcase.cpp {return-void} {QCOMPARE() or QVERIFY()} {QCOMPARE()} {QTest::setThrowOnFail(true)}
245*/
246
247/*!
248 \since 6.8
249 \macro QTEST_THROW_ON_SKIP
250 \relates QTest
251
252 When defined, QSKIP() always throws. QTest::setThrowOnSkip() then no longer
253 has any effect.
254
255 \include qtestcase.cpp {return-void} {QSKIP()} {QSKIP()} {QTest::setThrowOnSkip(true)}
256*/
257
258/*!
259 \since 6.8
260 \class QTest::ThrowOnFailEnabler
261 \inmodule QtTestLib
262
263 RAII class around setThrowOnFail().
264*/
265/*!
266 \fn QTest::ThrowOnFailEnabler::ThrowOnFailEnabler()
267
268 Constructor. Calls \c{setThrowOnFail(true)}.
269*/
270/*!
271 \fn QTest::ThrowOnFailEnabler::~ThrowOnFailEnabler()
272
273 Destructor. Calls \c{setThrowOnFail(false)}.
274*/
275
276/*!
277 \since 6.8
278 \class QTest::ThrowOnFailDisabler
279 \inmodule QtTestLib
280
281 RAII class around setThrowOnFail().
282*/
283/*!
284 \fn QTest::ThrowOnFailDisabler::ThrowOnFailDisabler()
285
286 Constructor. Calls \c{setThrowOnFail(false)}.
287*/
288/*!
289 \fn QTest::ThrowOnFailDisabler::~ThrowOnFailDisabler()
290
291 Destructor. Calls \c{setThrowOnFail(true)}.
292*/
293
294/*!
295 \since 6.8
296 \class QTest::ThrowOnSkipEnabler
297 \inmodule QtTestLib
298
299 RAII class around setThrowOnSkip().
300*/
301/*!
302 \fn QTest::ThrowOnSkipEnabler::ThrowOnSkipEnabler()
303
304 Constructor. Calls \c{setThrowOnSkip(true)}.
305*/
306/*!
307 \fn QTest::ThrowOnSkipEnabler::~ThrowOnSkipEnabler()
308
309 Destructor. Calls \c{setThrowOnSkip(false)}.
310*/
311
312/*!
313 \since 6.8
314 \class QTest::ThrowOnSkipDisabler
315 \inmodule QtTestLib
316
317 RAII class around setThrowOnSkip().
318*/
319/*!
320 \fn QTest::ThrowOnSkipDisabler::ThrowOnSkipDisabler()
321
322 Constructor. Calls \c{setThrowOnSkip(false)}.
323*/
324/*!
325 \fn QTest::ThrowOnSkipDisabler::~ThrowOnSkipDisabler()
326
327 Destructor. Calls \c{setThrowOnSkip(true)}.
328*/
329
330/*!
331 \since 6.8
332
333 Enables (\a enable = \c true) or disables (\ enable = \c false) throwing on
334 QCOMPARE()/QVERIFY() failures (as opposed to just returning from the
335 immediately-surrounding function context).
336
337 The feature is reference-counted: If you call this function \e{N} times
338 with \c{true}, you need to call it \e{N} times with \c{false} to get back
339 to where you started.
340
341 This call has no effect when the \l{QTEST_THROW_ON_FAIL} C++ macro is
342 defined.
343
344 \note You must compile your tests with exceptions enabled to use this
345 feature.
346
347 \sa setThrowOnSkip(), ThrowOnFailEnabler, ThrowOnFailDisabler, QTEST_THROW_ON_FAIL
348*/
349void setThrowOnFail(bool enable) noexcept
350{
351 g_throwOnFail.fetchAndAddRelaxed(enable ? 1 : -1);
352}
353
354/*!
355 \since 6.8
356
357 Enables (\a enable = \c true) or disables (\ enable = \c false) throwing on
358 QSKIP() (as opposed to just returning from the immediately-surrounding
359 function context).
360
361 The feature is reference-counted: If you call this function \e{N} times
362 with \c{true}, you need to call it \e{N} times with \c{false} to get back
363 to where you started.
364
365 This call has no effect when the \l{QTEST_THROW_ON_SKIP} C++ macro is
366 defined.
367
368 \note You must compile your tests with exceptions enabled to use this
369 feature.
370
371 \sa setThrowOnFail(), ThrowOnSkipEnabler, ThrowOnSkipDisabler, QTEST_THROW_ON_SKIP
372*/
373void setThrowOnSkip(bool enable) noexcept
374{
375 g_throwOnSkip.fetchAndAddRelaxed(enable ? 1 : -1);
376}
377
381{
382 return "QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) "
383 "was too short, %3 ms would have been sufficient this time."_L1
385}
386
388
389class WatchDog;
390
391static QObject *currentTestObject = nullptr;
393static bool inTestFunction = false;
394
395#if defined(Q_OS_MACOS)
398#endif
399
401public:
403
405
406 explicit TestMethods(const QObject *o, MetaMethods m = {});
407
408 void invokeTests(QObject *testObject) const;
409
410 static QMetaMethod findMethod(const QObject *obj, const char *signature);
411
412private:
413 void invokeTest(int index, QLatin1StringView tag, std::optional<WatchDog> &watchDog) const;
414 void invokeTestOnData(int index) const;
415
416 QMetaMethod m_initTestCaseMethod; // might not exist, check isValid().
417 QMetaMethod m_initTestCaseDataMethod;
418 QMetaMethod m_cleanupTestCaseMethod;
419 QMetaMethod m_initMethod;
420 QMetaMethod m_cleanupMethod;
421
422 MetaMethods m_methods;
423};
424
425TestMethods::TestMethods(const QObject *o, MetaMethods m)
426 : m_initTestCaseMethod(TestMethods::findMethod(o, "initTestCase()"))
427 , m_initTestCaseDataMethod(TestMethods::findMethod(o, "initTestCase_data()"))
428 , m_cleanupTestCaseMethod(TestMethods::findMethod(o, "cleanupTestCase()"))
429 , m_initMethod(TestMethods::findMethod(o, "init()"))
430 , m_cleanupMethod(TestMethods::findMethod(o, "cleanup()"))
431 , m_methods(std::move(m))
432{
433 if (m_methods.empty()) {
434 const QMetaObject *metaObject = o->metaObject();
435 const int count = metaObject->methodCount();
436 m_methods.reserve(count);
437 for (int i = 0; i < count; ++i) {
438 const QMetaMethod me = metaObject->method(i);
439 if (isValidSlot(me))
440 m_methods.push_back(me);
441 }
442 }
443}
444
445QMetaMethod TestMethods::findMethod(const QObject *obj, const char *signature)
446{
447 const QMetaObject *metaObject = obj->metaObject();
448 const int funcIndex = metaObject->indexOfMethod(signature);
449 return funcIndex >= 0 ? metaObject->method(funcIndex) : QMetaMethod();
450}
451
452static int keyDelay = -1;
453static int mouseDelay = -1;
454static int eventDelay = -1;
455#if QT_CONFIG(thread)
456static int timeout = -1;
457#endif
458static int repetitions = 1;
459static bool skipBlacklisted = false;
460
461namespace Internal {
462bool noCrashHandler = false;
463}
464
465static bool invokeTestMethodIfValid(QMetaMethod m, QObject *obj = QTest::currentTestObject)
466{
467 if (!m.isValid())
468 return false;
469 bool ok = true;
470 try { ok = m.invoke(obj, Qt ::DirectConnection); }
471 catch (const TestFailedException &) {} // ignore (used for control flow)
472 catch (const TestSkippedException &) {} // ditto
473 // every other exception is someone else's problem
474 return ok;
475}
476
477static void invokeTestMethodIfExists(const char *methodName, QObject *obj = QTest::currentTestObject)
478{
479 const QMetaObject *metaObject = obj->metaObject();
480 int funcIndex = metaObject->indexOfMethod(methodName);
481 // doesn't generate a warning if it doesn't exist:
482 invokeTestMethodIfValid(metaObject->method(funcIndex), obj);
483}
484
486{
487 if (eventDelay == -1) {
488 const QByteArray env = qgetenv("QTEST_EVENT_DELAY");
489 if (!env.isEmpty())
490 eventDelay = atoi(env.constData());
491 else
492 eventDelay = 0;
493 }
494 return eventDelay;
495}
496
497int Q_TESTLIB_EXPORT defaultMouseDelay()
498{
499 if (mouseDelay == -1) {
500 const QByteArray env = qgetenv("QTEST_MOUSEEVENT_DELAY");
501 if (!env.isEmpty())
503 else
505 }
506 return mouseDelay;
507}
508
509int Q_TESTLIB_EXPORT defaultKeyDelay()
510{
511 if (keyDelay == -1) {
512 const QByteArray env = qgetenv("QTEST_KEYEVENT_DELAY");
513 if (!env.isEmpty())
515 else
517 }
518 return keyDelay;
519}
520#if QT_CONFIG(thread)
522{
523 if (timeout == -1) {
524 bool ok = false;
525 timeout = qEnvironmentVariableIntValue("QTEST_FUNCTION_TIMEOUT", &ok);
526
527 if (!ok || timeout <= 0)
528 timeout = 5*60*1000;
529 }
530 return std::chrono::milliseconds{timeout};
531}
532#endif
533
535Q_TESTLIB_EXPORT QStringList testFunctions;
536Q_TESTLIB_EXPORT QStringList testTags;
537
538static bool qPrintTestSlots(FILE *stream, const char *filter = nullptr, const char *preamble = "")
539{
540 const auto matches = [filter](const QByteArray &s) {
541 return !filter || QLatin1StringView(s).contains(QLatin1StringView(filter),
542 Qt::CaseInsensitive);
543 };
544 bool matched = false;
545 for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) {
546 QMetaMethod sl = QTest::currentTestObject->metaObject()->method(i);
547 if (isValidSlot(sl)) {
548 const QByteArray signature = sl.methodSignature();
549 if (matches(signature)) {
550 std::fprintf(stream, "%s%s\n", preamble, signature.constData());
551 preamble = "";
552 matched = true;
553 }
554 }
555 }
556 return matched;
557}
558
559static void qPrintDataTags(FILE *stream)
560{
561 // Avoid invoking the actual test functions, and also avoid printing irrelevant output:
562 QTestLog::setPrintAvailableTagsMode();
563
564 // Get global data tags:
565 QTestTable::globalTestTable();
566 invokeTestMethodIfExists("initTestCase_data()");
567 const QTestTable *gTable = QTestTable::globalTestTable();
568
569 const QMetaObject *currTestMetaObj = QTest::currentTestObject->metaObject();
570
571 // Process test functions:
572 for (int i = 0; i < currTestMetaObj->methodCount(); ++i) {
573 QMetaMethod tf = currTestMetaObj->method(i);
574
575 if (isValidSlot(tf)) {
576
577 // Retrieve local tags:
578 QStringList localTags;
579 QTestTable table;
580 const QByteArray slot = tf.methodSignature().chopped(2);
581 const QByteArray member = slot + "_data()";
582 invokeTestMethodIfExists(member.constData());
583 const int dataCount = table.dataCount();
584 localTags.reserve(dataCount);
585 for (int j = 0; j < dataCount; ++j)
586 localTags << QLatin1StringView(table.testData(j)->dataTag());
587
588 // Print all tag combinations:
589 if (gTable->dataCount() == 0) {
590 if (localTags.size() == 0) {
591 // No tags at all, so just print the test function:
592 std::fprintf(stream, "%s %s\n", currTestMetaObj->className(), slot.data());
593 } else {
594 // Only local tags, so print each of them:
595 for (int k = 0; k < localTags.size(); ++k)
596 std::fprintf(stream, "%s %s %s\n",
597 currTestMetaObj->className(),
598 slot.data(),
599 localTags.at(k).toLatin1().data());
600 }
601 } else {
602 for (int j = 0; j < gTable->dataCount(); ++j) {
603 if (localTags.size() == 0) {
604 // Only global tags, so print the current one:
605 std::fprintf(stream, "%s %s __global__ %s\n",
606 currTestMetaObj->className(),
607 slot.data(),
608 gTable->testData(j)->dataTag());
609 } else {
610 // Local and global tags, so print each of the local ones and
611 // the current global one:
612 for (int k = 0; k < localTags.size(); ++k)
613 std::fprintf(stream, "%s %s %s __global__ %s\n",
614 currTestMetaObj->className(),
615 slot.data(),
616 localTags.at(k).toLatin1().data(),
617 gTable->testData(j)->dataTag());
618 }
619 }
620 }
621 }
622 }
623}
624
625static int qToInt(const char *str)
626{
627 char *pEnd;
628 int l = static_cast<int>(strtol(str, &pEnd, 10));
629 if (*pEnd != 0) {
630 std::fprintf(stderr, "Invalid numeric parameter: '%s'\n", str);
631 exit(1);
632 }
633 return l;
634}
635
636Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml)
637{
638 int logFormat = -1; // Not set
639 const char *logFilename = nullptr;
640
641 repetitions = 1;
642
643 QTest::testFunctions.clear();
644 QTest::testTags.clear();
645
646#if defined(Q_OS_DARWIN) && defined(HAVE_XCTEST)
647 if (QXcodeTestLogger::canLogTestProgress())
648 logFormat = QTestLog::XCTest;
649#endif
650
651 const char *testOptions =
652 " New-style logging options:\n"
653 " -o filename,format : Output results to file in the specified format\n"
654 " Use - to output to stdout\n"
655 " Valid formats are:\n"
656 " txt : Plain text\n"
657 " csv : CSV format (suitable for benchmarks)\n"
658 " junitxml : XML JUnit document\n"
659 " xml : XML document\n"
660 " lightxml : A stream of XML tags\n"
661 " teamcity : TeamCity format\n"
662 " tap : Test Anything Protocol\n"
663 "\n"
664 " *** Multiple loggers can be specified, but at most one can log to stdout.\n"
665 "\n"
666 " Old-style logging options:\n"
667 " -o filename : Write the output into file\n"
668 " -txt : Output results in Plain Text\n"
669 " -csv : Output results in a CSV format (suitable for benchmarks)\n"
670 " -junitxml : Output results as XML JUnit document\n"
671 " -xml : Output results as XML document\n"
672 " -lightxml : Output results as stream of XML tags\n"
673 " -teamcity : Output results in TeamCity format\n"
674 " -tap : Output results in Test Anything Protocol format\n"
675 "\n"
676 " *** If no output file is specified, stdout is assumed.\n"
677 " *** If no output format is specified, -txt is assumed.\n"
678 "\n"
679 " Test log detail options:\n"
680 " -silent : Log failures and fatal errors only\n"
681 " -v1 : Log the start of each testfunction\n"
682 " -v2 : Log each QVERIFY/QCOMPARE/QTEST (implies -v1)\n"
683 " -vs : Log every signal emission and resulting slot invocations\n"
684 "\n"
685 " *** The -silent and -v1 options only affect plain text output.\n"
686 "\n"
687 " Testing options:\n"
688 " -functions : Returns a list of current testfunctions\n"
689 " -datatags : Returns a list of current data tags.\n"
690 " A global data tag is preceded by ' __global__ '.\n"
691 " -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n"
692 " -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n"
693 " -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n"
694 " -maxwarnings n : Sets the maximum amount of messages to output.\n"
695 " 0 means unlimited, default: 2000\n"
696 " -nocrashhandler : Disables the crash handler. Useful for debugging crashes.\n"
697 " -repeat n : Run the testsuite n times or until the test fails.\n"
698 " Useful for finding flaky tests. If negative, the tests are\n"
699 " repeated forever. This is intended as a developer tool, and\n"
700 " is only supported with the plain text logger.\n"
701 " -skipblacklisted : Skip blacklisted tests. Useful for measuring test coverage.\n"
702 " -trytimeout ms : Set default timeout for the QTRY_* macros and the qWaitFor()\n"
703 " functions to ms milliseconds.\n"
704 "\n"
705 " Benchmarking options:\n"
706#if QT_CONFIG(valgrind)
707 " -callgrind : Use callgrind to time benchmarks\n"
708#endif
709#ifdef QTESTLIB_USE_PERF_EVENTS
710 " -perf : Use Linux perf events to time benchmarks\n"
711 " -perfcounter name : Use the counter named 'name'\n"
712 " -perfcounterlist : Lists the counters available\n"
713#endif
714#ifdef HAVE_TICK_COUNTER
715 " -tickcounter : Use CPU tick counters to time benchmarks\n"
716#endif
717 " -eventcounter : Counts events received during benchmarks\n"
718 " -minimumvalue n : Sets the minimum acceptable measurement value\n"
719 " -minimumtotal n : Sets the minimum acceptable total for repeated executions of a test function\n"
720 " -iterations n : Sets the number of accumulation iterations.\n"
721 " -median n : Sets the number of median iterations.\n"
722 " -vb : Print out verbose benchmarking information.\n";
723
724 for (int i = 1; i < argc; ++i) {
725 if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0
726 || strcmp(argv[i], "/?") == 0) {
727 std::printf(" Usage: %s [options] [testfunction[:testdata]]...\n"
728 " By default, all testfunctions will be run.\n\n"
729 "%s", argv[0], testOptions);
730
731 if (qml) {
732 std::printf("\n"
733 " QmlTest options:\n"
734 " -import dir : Specify an import directory.\n"
735 " -plugins dir : Specify a directory where to search for plugins.\n"
736 " -input dir/file : Specify the root directory for test cases or a single test case file.\n"
737 " -translation file : Specify the translation file.\n"
738 " -file-selector dir : Specify a file selector for the QML engine.\n");
739 }
740
741 std::printf("\n"
742 " -help : This help\n");
743 exit(0);
744 } else if (strcmp(argv[i], "-functions") == 0) {
745 if (qml) {
747 } else {
748 qPrintTestSlots(stdout);
749 exit(0);
750 }
751 } else if (strcmp(argv[i], "-datatags") == 0) {
752 if (!qml) {
753 qPrintDataTags(stdout);
754 exit(0);
755 }
756 } else if (strcmp(argv[i], "-txt") == 0) {
757 logFormat = QTestLog::Plain;
758 } else if (strcmp(argv[i], "-csv") == 0) {
759 logFormat = QTestLog::CSV;
760 } else if (strcmp(argv[i], "-junitxml") == 0) {
761 logFormat = QTestLog::JUnitXML;
762 } else if (strcmp(argv[i], "-xunitxml") == 0) {
763 std::fprintf(stderr, "WARNING: xunitxml is deprecated. Please use junitxml.\n");
764 logFormat = QTestLog::JUnitXML;
765 } else if (strcmp(argv[i], "-xml") == 0) {
766 logFormat = QTestLog::XML;
767 } else if (strcmp(argv[i], "-lightxml") == 0) {
768 logFormat = QTestLog::LightXML;
769 } else if (strcmp(argv[i], "-teamcity") == 0) {
770 logFormat = QTestLog::TeamCity;
771 } else if (strcmp(argv[i], "-tap") == 0) {
772 logFormat = QTestLog::TAP;
773 } else if (strcmp(argv[i], "-silent") == 0) {
774 QTestLog::setVerboseLevel(-1);
775 } else if (strcmp(argv[i], "-v1") == 0) {
776 QTestLog::setVerboseLevel(1);
777 } else if (strcmp(argv[i], "-v2") == 0) {
778 QTestLog::setVerboseLevel(2);
779 } else if (strcmp(argv[i], "-vs") == 0) {
780 QSignalDumper::setEnabled(true);
781 } else if (strcmp(argv[i], "-o") == 0) {
782 if (i + 1 >= argc) {
783 std::fprintf(stderr, "-o needs an extra parameter specifying the filename and optional format\n");
784 exit(1);
785 }
786 ++i;
787 // Do we have the old or new style -o option?
788 char *filename = new char[strlen(argv[i])+1];
789 char *format = new char[strlen(argv[i])+1];
790 if (std::sscanf(argv[i], "%[^,],%s", filename, format) == 1) {
791 // Old-style
792 logFilename = argv[i];
793 } else {
794 // New-style
795 if (strcmp(format, "txt") == 0)
796 logFormat = QTestLog::Plain;
797 else if (strcmp(format, "csv") == 0)
798 logFormat = QTestLog::CSV;
799 else if (strcmp(format, "lightxml") == 0)
800 logFormat = QTestLog::LightXML;
801 else if (strcmp(format, "xml") == 0)
802 logFormat = QTestLog::XML;
803 else if (strcmp(format, "junitxml") == 0)
804 logFormat = QTestLog::JUnitXML;
805 else if (strcmp(format, "xunitxml") == 0) {
806 std::fprintf(stderr, "WARNING: xunitxml is deprecated. Please use junitxml.\n");
807 logFormat = QTestLog::JUnitXML;
808 } else if (strcmp(format, "teamcity") == 0)
809 logFormat = QTestLog::TeamCity;
810 else if (strcmp(format, "tap") == 0)
811 logFormat = QTestLog::TAP;
812 else {
813 std::fprintf(stderr, "output format must be one of txt, csv, lightxml, xml, tap, teamcity or junitxml\n");
814 exit(1);
815 }
816 if (strcmp(filename, "-") == 0 && QTestLog::loggerUsingStdout()) {
817 std::fprintf(stderr, "only one logger can log to stdout\n");
818 exit(1);
819 }
820 QTestLog::addLogger(QTestLog::LogMode(logFormat), filename);
821 }
822 delete [] filename;
823 delete [] format;
824 } else if (strcmp(argv[i], "-eventdelay") == 0) {
825 if (i + 1 >= argc) {
826 std::fprintf(stderr, "-eventdelay needs an extra parameter to indicate the delay(ms)\n");
827 exit(1);
828 } else {
829 QTest::eventDelay = qToInt(argv[++i]);
830 }
831 } else if (strcmp(argv[i], "-keydelay") == 0) {
832 if (i + 1 >= argc) {
833 std::fprintf(stderr, "-keydelay needs an extra parameter to indicate the delay(ms)\n");
834 exit(1);
835 } else {
836 QTest::keyDelay = qToInt(argv[++i]);
837 }
838 } else if (strcmp(argv[i], "-mousedelay") == 0) {
839 if (i + 1 >= argc) {
840 std::fprintf(stderr, "-mousedelay needs an extra parameter to indicate the delay(ms)\n");
841 exit(1);
842 } else {
843 QTest::mouseDelay = qToInt(argv[++i]);
844 }
845 } else if (strcmp(argv[i], "-maxwarnings") == 0) {
846 if (i + 1 >= argc) {
847 std::fprintf(stderr, "-maxwarnings needs an extra parameter with the amount of warnings\n");
848 exit(1);
849 } else {
850 QTestLog::setMaxWarnings(qToInt(argv[++i]));
851 }
852 } else if (strcmp(argv[i], "-repeat") == 0) {
853 if (i + 1 >= argc) {
854 std::fprintf(stderr, "-repeat needs an extra parameter for the number of repetitions\n");
855 exit(1);
856 } else {
857 repetitions = qToInt(argv[++i]);
858 }
859 } else if (strcmp(argv[i], "-nocrashhandler") == 0) {
861 } else if (strcmp(argv[i], "-skipblacklisted") == 0) {
863 } else if (strcmp(argv[i], "-trytimeout") == 0) {
864 if (i + 1 >= argc) {
865 std::fprintf(stderr, "-trytimeout needs an extra parameter to indicate the timeout(ms)\n");
866 exit(1);
867 } else {
868 QTest::defaultTryTimeout.store(std::chrono::milliseconds{qToInt(argv[++i])},
869 std::memory_order_relaxed);
870 }
871#if QT_CONFIG(valgrind)
872 } else if (strcmp(argv[i], "-callgrind") == 0) {
873 if (!QBenchmarkValgrindUtils::haveValgrind()) {
874 std::fprintf(stderr,
875 "WARNING: Valgrind not found or too old. "
876 "Make sure it is installed and in your path. "
877 "Using the walltime measurer.\n");
878 } else if (QFileInfo(QDir::currentPath()).isWritable()) {
879 QBenchmarkGlobalData::current->setMode(
880 QBenchmarkGlobalData::CallgrindParentProcess);
881 } else {
882 std::fprintf(stderr,
883 "WARNING: Current directory not writable. "
884 "Using the walltime measurer.\n");
885 }
886 } else if (strcmp(argv[i], "-callgrindchild") == 0) { // "private" option
887 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindChildProcess);
888 QBenchmarkGlobalData::current->callgrindOutFileBase =
889 QBenchmarkValgrindUtils::outFileBase();
890#endif
891#ifdef QTESTLIB_USE_PERF_EVENTS
892 } else if (strcmp(argv[i], "-perf") == 0) {
893 if (QBenchmarkPerfEventsMeasurer::isAvailable()) {
894 // perf available
895 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::PerfCounter);
896 } else {
897 std::fprintf(stderr, "WARNING: Linux perf events not available. Using the walltime measurer.\n");
898 }
899 } else if (strcmp(argv[i], "-perfcounter") == 0) {
900 if (i + 1 >= argc) {
901 std::fprintf(stderr, "-perfcounter needs an extra parameter with the name of the counter\n");
902 exit(1);
903 } else {
904 QBenchmarkPerfEventsMeasurer::setCounter(argv[++i]);
905 }
906 } else if (strcmp(argv[i], "-perfcounterlist") == 0) {
907 QBenchmarkPerfEventsMeasurer::listCounters();
908 exit(0);
909#endif
910#ifdef HAVE_TICK_COUNTER
911 } else if (strcmp(argv[i], "-tickcounter") == 0) {
912 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::TickCounter);
913#endif
914 } else if (strcmp(argv[i], "-eventcounter") == 0) {
915 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::EventCounter);
916 } else if (strcmp(argv[i], "-minimumvalue") == 0) {
917 if (i + 1 >= argc) {
918 std::fprintf(stderr, "-minimumvalue needs an extra parameter to indicate the minimum time(ms)\n");
919 exit(1);
920 } else {
921 QBenchmarkGlobalData::current->walltimeMinimum = qToInt(argv[++i]);
922 }
923 } else if (strcmp(argv[i], "-minimumtotal") == 0) {
924 if (i + 1 >= argc) {
925 std::fprintf(stderr, "-minimumtotal needs an extra parameter to indicate the minimum total measurement\n");
926 exit(1);
927 } else {
928 QBenchmarkGlobalData::current->minimumTotal = qToInt(argv[++i]);
929 }
930 } else if (strcmp(argv[i], "-iterations") == 0) {
931 if (i + 1 >= argc) {
932 std::fprintf(stderr, "-iterations needs an extra parameter to indicate the number of iterations\n");
933 exit(1);
934 } else {
935 QBenchmarkGlobalData::current->iterationCount = qToInt(argv[++i]);
936 }
937 } else if (strcmp(argv[i], "-median") == 0) {
938 if (i + 1 >= argc) {
939 std::fprintf(stderr, "-median needs an extra parameter to indicate the number of median iterations\n");
940 exit(1);
941 } else {
942 QBenchmarkGlobalData::current->medianIterationCount = qToInt(argv[++i]);
943 }
944
945 } else if (strcmp(argv[i], "-vb") == 0) {
946 QBenchmarkGlobalData::current->verboseOutput = true;
947#if defined(Q_OS_DARWIN)
948 } else if (strncmp(argv[i], "-Apple", 6) == 0) {
949 i += 1; // Skip Apple-specific user preferences
950 continue;
951# if defined(HAVE_XCTEST)
952 } else if (int skip = QXcodeTestLogger::parseCommandLineArgument(argv[i])) {
953 i += (skip - 1); // Eating argv[i] with a continue counts towards skips
954 continue;
955# endif
956#endif
957 } else if (argv[i][0] == '-') {
958 std::fprintf(stderr, "Unknown option: '%s'\n\n%s", argv[i], testOptions);
959 if (qml) {
960 std::fprintf(stderr, "\nqmltest related options:\n"
961 " -import : Specify an import directory.\n"
962 " -plugins : Specify a directory where to search for plugins.\n"
963 " -input : Specify the root directory for test cases.\n");
964 }
965
966 std::fprintf(stderr, "\n"
967 " -help : This help\n");
968 exit(1);
969 } else {
970 // We can't check the availability of test functions until
971 // we load the QML files. So just store the data for now.
972 int colon = -1;
973 int offset;
974 for (offset = 0; argv[i][offset]; ++offset) {
975 if (argv[i][offset] == ':') {
976 if (argv[i][offset + 1] == ':') {
977 // "::" is used as a test name separator.
978 // e.g. "ClickTests::test_click:row1".
979 ++offset;
980 } else {
981 colon = offset;
982 break;
983 }
984 }
985 }
986 if (colon == -1) {
987 QTest::testFunctions += QString::fromLatin1(argv[i]);
988 QTest::testTags += QString();
989 } else {
990 QTest::testFunctions += QString::fromLatin1(argv[i], colon);
991 QTest::testTags += QString::fromLatin1(argv[i] + colon + 1);
992 }
993 }
994 }
995
996 bool installedTestCoverage = installCoverageTool(QTestResult::currentAppName(), QTestResult::currentTestObjectName());
997 QTestLog::setInstalledTestCoverage(installedTestCoverage);
998
999 // If no loggers were created by the long version of the -o command-line
1000 // option, but a logger was requested via the old-style option, add it.
1001 const bool explicitLoggerRequested = logFormat != -1;
1002 if (!QTestLog::hasLoggers() && explicitLoggerRequested)
1003 QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename);
1004
1005 bool addFallbackLogger = !explicitLoggerRequested;
1006
1007#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
1008 // Any explicitly requested loggers will be added by now, so we can check if they use stdout
1009 const bool safeToAddAppleLogger = !AppleUnifiedLogger::preventsStderrLogging() || !QTestLog::loggerUsingStdout();
1010 if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
1011 QTestLog::addLogger(QTestLog::Apple, nullptr);
1012 if (AppleUnifiedLogger::preventsStderrLogging() && !logFilename)
1013 addFallbackLogger = false; // Prevent plain test logger fallback below
1014 }
1015#endif
1016
1017 if (addFallbackLogger)
1018 QTestLog::addLogger(QTestLog::Plain, logFilename);
1019
1020 if (repetitions != 1 && !QTestLog::isRepeatSupported()) {
1021 std::fprintf(stderr, "-repeat is only supported with plain text logger\n");
1022 exit(1);
1023 }
1024}
1025
1026// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
1027Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) {
1028 qtest_qParseArgs(argc, const_cast<const char *const *>(argv), qml);
1029}
1030
1031static QList<QBenchmarkResult> qMedian(const QList<QList<QBenchmarkResult>> &container)
1032{
1033 const int count = container.size();
1034 if (count == 0)
1035 return {};
1036
1037 if (count == 1)
1038 return container.front();
1039
1040 QList<QList<QBenchmarkResult>> containerCopy = container;
1041 std::sort(containerCopy.begin(), containerCopy.end(),
1042 [](const QList<QBenchmarkResult> &a, const QList<QBenchmarkResult> &b) {
1043 return a.first() < b.first();
1044 });
1045
1046 const int middle = count / 2;
1047
1048 // ### handle even-sized containers here by doing an arithmetic mean of the two middle items.
1049 return containerCopy.at(middle);
1050}
1051
1053{
1054 QTestDataSetter(QTestData *data)
1055 {
1056 QTestResult::setCurrentTestData(data);
1057 }
1059 {
1060 QTestResult::setCurrentTestData(nullptr);
1061 }
1062};
1063
1064void TestMethods::invokeTestOnData(int index) const
1065{
1066 /* Benchmarking: for each median iteration*/
1067
1068 bool isBenchmark = false;
1069 int i = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
1070
1071 QList<QList<QBenchmarkResult>> resultsList;
1072 bool minimumTotalReached = false;
1073 do {
1074 QBenchmarkTestMethodData::current->beginDataRun();
1075 if (i < 0)
1076 QBenchmarkTestMethodData::current->iterationCount = 1;
1077
1078 /* Benchmarking: for each accumulation iteration*/
1079 bool invokeOk;
1080 do {
1081 QTest::inTestFunction = true;
1082 invokeTestMethodIfValid(m_initMethod);
1083
1084 const bool initQuit = QTest::currentTestResolved();
1085 if (!initQuit) {
1086 QBenchmarkTestMethodData::current->results.clear();
1087 QBenchmarkTestMethodData::current->resultAccepted = false;
1088 QBenchmarkTestMethodData::current->valid = false;
1089
1090 QBenchmarkGlobalData::current->context.tag = QLatin1StringView(
1091 QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "");
1092
1093 invokeOk = invokeTestMethodIfValid(m_methods[index]);
1094 if (!invokeOk)
1095 QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__);
1096
1097 isBenchmark = QBenchmarkTestMethodData::current->isBenchmark();
1098 } else {
1099 invokeOk = false;
1100 }
1101
1102 QTest::inTestFunction = false;
1103 QTestResult::finishedCurrentTestData();
1104
1105 if (!initQuit) {
1106 invokeTestMethodIfValid(m_cleanupMethod);
1107
1108 // Process any deleteLater(), used by event-loop-based apps.
1109 // Fixes memleak reports.
1110 if (QCoreApplication::instance())
1111 QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
1112 }
1113 // If the test isn't a benchmark, finalize the result after
1114 // cleanup() has finished (or init has lead us to skip the test).
1115 if (!isBenchmark)
1116 QTestResult::finishedCurrentTestDataCleanup();
1117
1118 // If this test method has a benchmark, repeat until all measurements are
1119 // acceptable.
1120 // The QBENCHMARK macro increases the number of iterations for each run until
1121 // this happens.
1122 } while (invokeOk && !QTest::currentTestResolved() && isBenchmark
1123 && !QBenchmarkTestMethodData::current->resultsAccepted());
1124
1125 QBenchmarkTestMethodData::current->endDataRun();
1126 if (!QTest::currentTestResolved()) {
1127 if (i > -1) // iteration -1 is the warmup iteration.
1128 resultsList.append(QBenchmarkTestMethodData::current->results);
1129
1130 if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput &&
1131 !QBenchmarkTestMethodData::current->results.isEmpty()) {
1132 // we only print the first result
1133 const QBenchmarkResult &first = QBenchmarkTestMethodData::current->results.constFirst();
1134 QString pattern = i < 0 ? "warmup stage result : %1 std.dev: %2"_L1
1135 : "accumulation stage result: %1 std.dev: %2"_L1;
1136 QTestLog::info(qPrintable(pattern.arg(first.measurement.value, 0,'f', 3)
1137 .arg(sqrt(first.measurement.variance), 0, 'e', 2)),
1138 nullptr, 0);
1139 }
1140 }
1141
1142 // Verify if the minimum total measurement (for the first measurement)
1143 // was reached, if it was specified:
1144 if (QBenchmarkGlobalData::current->minimumTotal == -1) {
1145 minimumTotalReached = true;
1146 } else {
1147 auto addResult = [](qreal current, const QList<QBenchmarkResult> &r) {
1148 if (!r.isEmpty())
1149 current += r.first().measurement.value;
1150 return current;
1151 };
1152 const qreal total = std::accumulate(resultsList.begin(), resultsList.end(), 0.0, addResult);
1153 minimumTotalReached = (total >= QBenchmarkGlobalData::current->minimumTotal);
1154 }
1155 } while (isBenchmark && !QTest::currentTestResolved()
1156 && (++i < QBenchmarkGlobalData::current->adjustMedianIterationCount()
1157 // Test that before this so i *does* get its increment:
1158 || !minimumTotalReached));
1159
1160 // If the test is a benchmark, finalize the result after all iterations have finished.
1161 if (isBenchmark) {
1162 bool testPassed = !QTest::currentTestResolved();
1163 QTestResult::finishedCurrentTestDataCleanup();
1164 // Only report benchmark figures if the test passed
1165 if (testPassed && QBenchmarkTestMethodData::current->resultsAccepted())
1166 QTestLog::addBenchmarkResults(qMedian(resultsList));
1167 }
1168}
1169
1170#if QT_CONFIG(thread)
1171
1172class WatchDog : public QThread
1173{
1174 enum Expectation : std::size_t {
1175 // bits 0..1: state
1179 ThreadEnd,
1180
1181 // bits 2..: generation
1182 };
1184 static_assert(size_t(ExpectationMask) == 0x3);
1185 static constexpr size_t GenerationShift = 2;
1186
1187 static constexpr Expectation state(Expectation e) noexcept
1188 { return Expectation{e & ExpectationMask}; }
1189 static constexpr size_t generation(Expectation e) noexcept
1190 { return e >> GenerationShift; }
1191 static constexpr Expectation combine(Expectation e, size_t gen) noexcept
1192 { return Expectation{e | (gen << GenerationShift)}; }
1193
1195 {
1196 auto expectationChanged = [this, e] { return expecting.load(std::memory_order_relaxed) != e; };
1197 switch (state(e)) {
1198 case TestFunctionEnd:
1200 case ThreadStart:
1201 case ThreadEnd:
1202 case TestFunctionStart:
1204 return true;
1205 }
1206 Q_UNREACHABLE_RETURN(false);
1207 }
1208
1210 {
1211 Q_ASSERT(generation(e) == 0); // no embedded generation allowed
1212 const auto locker = qt_scoped_lock(mutex);
1214 auto gen = generation(cur);
1215 if (e == TestFunctionStart)
1216 ++gen;
1217 e = combine(e, gen);
1220 }
1221
1222public:
1223 WatchDog()
1225 {
1226 setObjectName("QtTest Watchdog"_L1);
1227 auto locker = qt_unique_lock(mutex);
1228 start();
1230 }
1231
1232 ~WatchDog()
1233 {
1235 wait();
1236 }
1237
1238 void beginTest()
1239 {
1241 }
1242
1243 void testFinished()
1244 {
1246 }
1247
1248 void run() override
1249 {
1251 auto locker = qt_unique_lock(mutex);
1254 while (true) {
1256 switch (state(e)) {
1257 case ThreadEnd:
1258 return;
1259 case ThreadStart:
1260 Q_UNREACHABLE();
1261 case TestFunctionStart:
1262 case TestFunctionEnd:
1263 if (Q_UNLIKELY(!waitFor(locker, e))) {
1264 std::fflush(stderr);
1267 qFatal("Test function timed out");
1268 }
1269 }
1270 }
1271 }
1272
1273private:
1274 std::mutex mutex;
1277};
1278
1279#else // !QT_CONFIG(thread)
1280
1281class WatchDog : public QObject
1282{
1283public:
1284 void beginTest() {};
1285 void testFinished() {};
1286};
1287
1288#endif // QT_CONFIG(thread)
1289
1290template <typename Functor>
1291void runWithWatchdog(std::optional<WatchDog> &watchDog, Functor &&f)
1292{
1293 if (watchDog)
1294 watchDog->beginTest();
1295
1296 f();
1297
1298 if (watchDog)
1299 watchDog->testFinished();
1300}
1301
1302static void printUnknownDataTagError(QLatin1StringView name, QLatin1StringView tag,
1303 const QTestTable &lTable, const QTestTable &gTable)
1304{
1305 std::fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), tag.data());
1306 const int localDataCount = lTable.dataCount();
1307 if (localDataCount) {
1308 std::fputs("Available test-specific data tags:\n", stderr);
1309 for (int i = 0; i < localDataCount; ++i)
1310 std::fprintf(stderr, "\t%s\n", lTable.testData(i)->dataTag());
1311 }
1312 const int globalDataCount = gTable.dataCount();
1313 if (globalDataCount) {
1314 std::fputs("Available global data tags:\n", stderr);
1315 for (int i = 0; i < globalDataCount; ++i)
1316 std::fprintf(stderr, "\t%s\n", gTable.testData(i)->dataTag());
1317 }
1318 if (localDataCount == 0 && globalDataCount == 0)
1319 std::fputs("Function has no data tags\n", stderr);
1320}
1321
1322/*!
1323 \internal
1324
1325 Call slot_data(), init(), slot(), cleanup(), init(), slot(), cleanup(), ...
1326 If data is set then it is the only test that is performed
1327*/
1328void TestMethods::invokeTest(int index, QLatin1StringView tag, std::optional<WatchDog> &watchDog) const
1329{
1330 QBenchmarkTestMethodData benchmarkData;
1331 QBenchmarkTestMethodData::current = &benchmarkData;
1332
1333 const QByteArray &name = m_methods[index].name();
1334 QBenchmarkGlobalData::current->context.slotName = QLatin1StringView(name) + "()"_L1;
1335
1336 char member[512];
1337 QTestTable table;
1338
1339 QTestResult::setCurrentTestFunction(name.constData());
1340
1341 const QTestTable *gTable = QTestTable::globalTestTable();
1342 const int globalDataCount = gTable->dataCount();
1343 int curGlobalDataIndex = 0;
1344 const auto globalDataTag = [gTable, globalDataCount](int index) {
1345 return globalDataCount ? gTable->testData(index)->dataTag() : nullptr;
1346 };
1347
1348 const auto dataTagMatches = [](QLatin1StringView tag, QLatin1StringView local,
1349 QLatin1StringView global) {
1350 if (tag.isEmpty()) // No tag specified => run all data sets for this function
1351 return true;
1352 if (tag == local || tag == global) // Equal to either => run it
1353 return true;
1354 // Also allow global:local as a match:
1355 return tag.startsWith(global) && tag.endsWith(local) &&
1356 tag.size() == global.size() + 1 + local.size() &&
1357 tag[global.size()] == ':';
1358 };
1359 bool foundFunction = false;
1360 bool blacklisted = false;
1361
1362 /* For each entry in the global data table, do: */
1363 do {
1364 if (!gTable->isEmpty())
1365 QTestResult::setCurrentGlobalTestData(gTable->testData(curGlobalDataIndex));
1366
1367 if (curGlobalDataIndex == 0) {
1368 std::snprintf(member, 512, "%s_data()", name.constData());
1369 runWithWatchdog(watchDog, [&member] {
1371 });
1372 if (QTestResult::skipCurrentTest())
1373 break;
1374 }
1375
1376 int curDataIndex = 0;
1377 const int dataCount = table.dataCount();
1378 const auto dataTag = [&table, dataCount](int index) {
1379 return dataCount ? table.testData(index)->dataTag() : nullptr;
1380 };
1381
1382 /* For each entry in this test's data table, do: */
1383 do {
1384 QTestResult::setSkipCurrentTest(false);
1385 QTestResult::setBlacklistCurrentTest(false);
1386 if (dataTagMatches(tag, QLatin1StringView(dataTag(curDataIndex)),
1387 QLatin1StringView(globalDataTag(curGlobalDataIndex)))) {
1388 foundFunction = true;
1389 blacklisted = QTestPrivate::checkBlackLists(name.constData(), dataTag(curDataIndex),
1390 globalDataTag(curGlobalDataIndex));
1391 if (blacklisted)
1392 QTestResult::setBlacklistCurrentTest(true);
1393
1394 if (blacklisted && skipBlacklisted) {
1395 QTest::qSkip("Skipping blacklisted test since -skipblacklisted option is set.",
1396 NULL, 0);
1397 QTestResult::finishedCurrentTestData();
1398 QTestResult::finishedCurrentTestDataCleanup();
1399 } else {
1401 curDataIndex >= dataCount ? nullptr : table.testData(curDataIndex));
1402
1403 QTestPrivate::qtestMouseButtons = Qt::NoButton;
1404
1405 // Maintain at least 500ms mouse event timestamps between each test function
1406 // call
1408
1409 runWithWatchdog(watchDog, [this, index] {
1410 invokeTestOnData(index);
1411 });
1412 }
1413
1414 if (!tag.isEmpty() && !globalDataCount)
1415 break;
1416 }
1417 ++curDataIndex;
1418 } while (curDataIndex < dataCount);
1419
1420 QTestResult::setCurrentGlobalTestData(nullptr);
1421 ++curGlobalDataIndex;
1422 } while (curGlobalDataIndex < globalDataCount);
1423
1424 if (!tag.isEmpty() && !foundFunction) {
1425 printUnknownDataTagError(QLatin1StringView(name), tag, table, *gTable);
1426 QTestResult::addFailure(qPrintable("Data tag not found: %1"_L1.arg(tag)));
1427 }
1428 QTestResult::finishedCurrentTestFunction();
1429 QTestResult::setSkipCurrentTest(false);
1430 QTestResult::setBlacklistCurrentTest(false);
1431}
1432
1433void *fetchData(QTestData *data, const char *tagName, int typeId)
1434{
1435 QTEST_ASSERT(typeId);
1436 QTEST_ASSERT_X(data, "QTest::fetchData()", "Test data requested, but no testdata available.");
1437 QTEST_ASSERT(data->parent());
1438
1439 int idx = data->parent()->indexOf(tagName);
1440
1441 if (Q_UNLIKELY(idx == -1 || idx >= data->dataCount())) {
1442 qFatal("QFETCH: Requested testdata '%s' not available, check your _data function.",
1443 tagName);
1444 }
1445
1446 if (Q_UNLIKELY(typeId != data->parent()->elementTypeId(idx))) {
1447 qFatal("Requested type '%s' does not match available type '%s'.",
1448 QMetaType(typeId).name(),
1449 QMetaType(data->parent()->elementTypeId(idx)).name());
1450 }
1451
1452 return data->data(idx);
1453}
1454
1455/*!
1456 * \internal
1457*/
1458char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...)
1459{
1460 va_list ap;
1461 va_start(ap, numArguments);
1462
1463 QByteArray arguments;
1464 arguments += prefix;
1465
1466 if (numArguments > 0) {
1467 arguments += va_arg(ap, const char *);
1468
1469 for (size_t i = 1; i < numArguments; ++i) {
1470 arguments += ", ";
1471 arguments += va_arg(ap, const char *);
1472 }
1473 }
1474
1475 va_end(ap);
1476 arguments += suffix;
1477 return qstrdup(arguments.constData());
1478}
1479
1480/*!
1481 Returns a pointer to a string that is the string \a ba represented
1482 as a space-separated sequence of hex characters. If the input is
1483 considered too long, it is truncated. A trucation is indicated in
1484 the returned string as an ellipsis at the end. The caller has
1485 ownership of the returned pointer and must ensure it is later passed
1486 to operator delete[].
1487
1488 \a length is the length of the string \a ba.
1489*/
1490char *toHexRepresentation(const char *ba, qsizetype length)
1491{
1492 if (length == 0)
1493 return qstrdup("");
1494
1495 /* We output at maximum about maxLen characters in order to avoid
1496 * running out of memory and flooding things when the byte array
1497 * is large.
1498 *
1499 * maxLen can't be for example 200 because Qt Test is sprinkled with fixed
1500 * size char arrays.
1501 * */
1502 const qsizetype maxLen = 50;
1503 const qsizetype len = qMin(maxLen, length);
1504 char *result = nullptr;
1505
1506 if (length > maxLen) {
1507 const qsizetype size = len * 3 + 4;
1508 result = new char[size];
1509
1510 char *const forElipsis = result + size - 5;
1511 forElipsis[0] = ' ';
1512 forElipsis[1] = '.';
1513 forElipsis[2] = '.';
1514 forElipsis[3] = '.';
1515 result[size - 1] = '\0';
1516 }
1517 else {
1518 const qsizetype size = len * 3;
1519 result = new char[size];
1520 result[size - 1] = '\0';
1521 }
1522
1523 qsizetype i = 0;
1524 qsizetype o = 0;
1525
1526 while (true) {
1527 const char at = ba[i];
1528
1529 result[o] = toHexUpper(at >> 4);
1530 ++o;
1531 result[o] = toHexUpper(at);
1532
1533 ++i;
1534 ++o;
1535 if (i == len)
1536 break;
1537 result[o] = ' ';
1538 ++o;
1539 }
1540
1541 return result;
1542}
1543
1544/*!
1545 \internal
1546 Returns the same QByteArray but with only the ASCII characters still shown;
1547 everything else is replaced with \c {\xHH}.
1548*/
1549char *toPrettyCString(const char *p, qsizetype length)
1550{
1551 bool trimmed = false;
1552 auto buffer = std::make_unique<char[]>(256);
1553 const char *end = p + length;
1554 char *dst = buffer.get();
1555
1556 bool lastWasHexEscape = false;
1557 *dst++ = '"';
1558 for ( ; p != end; ++p) {
1559 // we can add:
1560 // 1 byte: a single character
1561 // 2 bytes: a simple escape sequence (\n)
1562 // 3 bytes: "" and a character
1563 // 4 bytes: an hex escape sequence (\xHH)
1564 if (dst - buffer.get() > 246) {
1565 // plus the quote, the three dots and NUL, it's 255 in the worst case
1566 trimmed = true;
1567 break;
1568 }
1569
1570 // check if we need to insert "" to break an hex escape sequence
1571 if (Q_UNLIKELY(lastWasHexEscape)) {
1572 if (fromHex(*p) != -1) {
1573 // yes, insert it
1574 *dst++ = '"';
1575 *dst++ = '"';
1576 }
1577 lastWasHexEscape = false;
1578 }
1579
1580 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1581 *dst++ = *p;
1582 continue;
1583 }
1584
1585 // write as an escape sequence
1586 // this means we may advance dst to buffer.data() + 247 or 250
1587 *dst++ = '\\';
1588 switch (*p) {
1589 case 0x5c:
1590 case 0x22:
1591 *dst++ = uchar(*p);
1592 break;
1593 case 0x8:
1594 *dst++ = 'b';
1595 break;
1596 case 0xc:
1597 *dst++ = 'f';
1598 break;
1599 case 0xa:
1600 *dst++ = 'n';
1601 break;
1602 case 0xd:
1603 *dst++ = 'r';
1604 break;
1605 case 0x9:
1606 *dst++ = 't';
1607 break;
1608 default:
1609 // print as hex escape
1610 *dst++ = 'x';
1611 *dst++ = toHexUpper(uchar(*p) >> 4);
1612 *dst++ = toHexUpper(uchar(*p));
1613 lastWasHexEscape = true;
1614 break;
1615 }
1616 }
1617
1618 *dst++ = '"';
1619 if (trimmed) {
1620 *dst++ = '.';
1621 *dst++ = '.';
1622 *dst++ = '.';
1623 }
1624 *dst++ = '\0';
1625 return buffer.release();
1626}
1627
1628/*!
1629 \fn char *toPrettyUnicode(QStringView string)
1630 \internal
1631 Returns the same QString but with only the ASCII characters still shown;
1632 everything else is replaced with \c {\uXXXX}.
1633
1634 Similar to QDebug::putString().
1635*/
1636
1638// escape sequence, closing quote, the three dots and NUL
1639constexpr qsizetype PrettyUnicodeMaxIncrement = sizeof(R"(\uXXXX"...)"); // includes NUL
1640
1641static char *writePrettyUnicodeChar(char16_t ch, char * const buffer)
1642{
1643 auto dst = buffer;
1644 auto first = [&](int n) { Q_ASSERT(dst - buffer == n); return dst; };
1645 if (ch < 0x7f && ch >= 0x20 && ch != '\\' && ch != '"') {
1646 *dst++ = ch;
1647 return first(1);
1648 }
1649
1650 // write as an escape sequence
1651 *dst++ = '\\';
1652 switch (ch) {
1653 case 0x22:
1654 case 0x5c:
1655 *dst++ = uchar(ch);
1656 break;
1657 case 0x8:
1658 *dst++ = 'b';
1659 break;
1660 case 0xc:
1661 *dst++ = 'f';
1662 break;
1663 case 0xa:
1664 *dst++ = 'n';
1665 break;
1666 case 0xd:
1667 *dst++ = 'r';
1668 break;
1669 case 0x9:
1670 *dst++ = 't';
1671 break;
1672 default:
1673 *dst++ = 'u';
1674 *dst++ = toHexUpper(ch >> 12);
1675 *dst++ = toHexUpper(ch >> 8);
1676 *dst++ = toHexUpper(ch >> 4);
1677 *dst++ = toHexUpper(ch);
1678 return first(6);
1679 }
1680 return first(2);
1681}
1682
1683char *toPrettyUnicode(QStringView string)
1684{
1685 auto p = string.utf16();
1686 auto length = string.size();
1687 // keep it simple for the vast majority of cases
1688 bool trimmed = false;
1689 auto buffer = std::make_unique<char[]>(PrettyUnicodeMaxOutputSize);
1690 const auto end = p + length;
1691 char *dst = buffer.get();
1692
1693 *dst++ = '"';
1694 for ( ; p != end; ++p) {
1695 if (dst - buffer.get() > PrettyUnicodeMaxOutputSize - PrettyUnicodeMaxIncrement) {
1696 trimmed = true;
1697 break;
1698 }
1699 dst = writePrettyUnicodeChar(*p, dst);
1700 }
1701
1702 *dst++ = '"';
1703 if (trimmed) {
1704 *dst++ = '.';
1705 *dst++ = '.';
1706 *dst++ = '.';
1707 }
1708 *dst++ = '\0';
1709 return buffer.release();
1710}
1711
1712void TestMethods::invokeTests(QObject *testObject) const
1713{
1714 const QMetaObject *metaObject = testObject->metaObject();
1715 QTEST_ASSERT(metaObject);
1716
1717 std::optional<WatchDog> watchDog = std::nullopt;
1718 if (!CrashHandler::alreadyDebugging()
1719#if QT_CONFIG(valgrind)
1720 && QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindChildProcess
1721#endif
1722 ) {
1723 watchDog.emplace();
1724 }
1725
1726 QTestResult::setCurrentTestFunction("initTestCase");
1727 runWithWatchdog(watchDog, [this, testObject] {
1728 invokeTestMethodIfValid(m_initTestCaseDataMethod, testObject);
1729 });
1730
1731 QSignalDumper::startDump();
1732
1733 if (!QTest::currentTestResolved()) {
1734
1735 runWithWatchdog(watchDog, [this, testObject] {
1736 invokeTestMethodIfValid(m_initTestCaseMethod, testObject);
1737 });
1738
1739 // finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy.
1740 const bool previousFailed = QTestResult::currentTestFailed();
1741 QTestResult::finishedCurrentTestData();
1742 QTestResult::finishedCurrentTestDataCleanup();
1743 QTestResult::finishedCurrentTestFunction();
1744
1745 if (!QTestResult::skipCurrentTest() && !previousFailed) {
1746 for (int i = 0, count = int(m_methods.size()); i < count; ++i) {
1747 const char *data = nullptr;
1748 if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
1749 data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
1750 invokeTest(i, QLatin1StringView(data), watchDog);
1751 delete [] data;
1752 }
1753 }
1754
1755 const bool wasSkipped = QTestResult::skipCurrentTest();
1756 QTestResult::setSkipCurrentTest(false);
1757 QTestResult::setBlacklistCurrentTest(false);
1758 QTestResult::setCurrentTestFunction("cleanupTestCase");
1759 runWithWatchdog(watchDog, [this, testObject] {
1760 invokeTestMethodIfValid(m_cleanupTestCaseMethod, testObject);
1761 });
1762
1763 QTestResult::finishedCurrentTestData();
1764 // Restore skip state as it affects decision on whether we passed:
1765 QTestResult::setSkipCurrentTest(wasSkipped || QTestResult::skipCurrentTest());
1766 QTestResult::finishedCurrentTestDataCleanup();
1767 }
1768 QTestResult::finishedCurrentTestFunction();
1769 QTestResult::setCurrentTestFunction(nullptr);
1770
1771 QSignalDumper::endDump();
1772}
1773
1774#if QT_DEPRECATED_SINCE(6, 8)
1775static const char *functionRefFormatter(const void *f)
1776{
1777 auto formatter = static_cast<const qxp::function_ref<const char *()> *>(f);
1778 return (*formatter)();
1779};
1780
1781bool reportResult(bool success, qxp::function_ref<const char *()> lhs,
1782 qxp::function_ref<const char *()> rhs,
1783 const char *lhsExpr, const char *rhsExpr,
1784 ComparisonOperation op, const char *file, int line)
1785{
1788 lhsExpr, rhsExpr, op, file, line);
1789}
1790#endif // QT_DEPRECATED_SINCE(6, 8)
1791
1792bool reportResult(bool success, const void *lhs, const void *rhs,
1793 const char *(*lhsFormatter)(const void*),
1794 const char *(*rhsFormatter)(const void*),
1795 const char *lhsExpr, const char *rhsExpr,
1796 ComparisonOperation op, const char *file, int line)
1797{
1798 return QTestResult::reportResult(success, lhs, rhs, lhsFormatter, rhsFormatter,
1799 lhsExpr, rhsExpr, op, file, line);
1800}
1801} // namespace QTest
1802
1803static void initEnvironment()
1804{
1805 qputenv("QT_QTESTLIB_RUNNING", "1");
1806}
1807
1808#ifdef Q_OS_ANDROID
1809static QFile androidExitCodeFile()
1810{
1811 const QString testHome = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1812 return QFile(testHome + "/qtest_last_exit_code"_L1);
1813}
1814#endif
1815
1816/*!
1817 Executes tests declared in \a testObject. In addition, the private slots
1818 \c{initTestCase()}, \c{cleanupTestCase()}, \c{init()} and \c{cleanup()}
1819 are executed if they exist. See \l{Creating a Test} for more details.
1820
1821 Optionally, the command line arguments \a argc and \a argv can be provided.
1822 For a list of recognized arguments, read \l {Qt Test Command Line Arguments}.
1823
1824 The following example will run all tests in \c MyTestObject:
1825
1826 \snippet code/src_qtestlib_qtestcase.cpp 18
1827
1828 This function returns 0 if no tests failed, or a value other than 0 if one
1829 or more tests failed or in case of unhandled exceptions. (Skipped tests do
1830 not influence the return value.)
1831
1832 For stand-alone test applications, the convenience macro \l QTEST_MAIN() can
1833 be used to declare a main() function that parses the command line arguments
1834 and executes the tests, avoiding the need to call this function explicitly.
1835
1836 The return value from this function is also the exit code of the test
1837 application when the \l QTEST_MAIN() macro is used.
1838
1839 For stand-alone test applications, this function should not be called more
1840 than once, as command-line options for logging test output to files and
1841 executing individual test functions will not behave correctly.
1842
1843 \note This function is not reentrant, only one test can run at a time. A
1844 test that was executed with qExec() can't run another test via qExec() and
1845 threads are not allowed to call qExec() simultaneously.
1846
1847 If you have programmatically created the arguments, as opposed to getting them
1848 from the arguments in \c main(), it is likely of interest to use
1849 QTest::qExec(QObject *, const QStringList &) since it is Unicode safe.
1850
1851 \sa QTEST_MAIN(), QTEST_GUILESS_MAIN(), QTEST_APPLESS_MAIN()
1852*/
1853
1854int QTest::qExec(QObject *testObject, int argc, char **argv)
1855{
1856 // NB: QtQuick's testing recombines qInit(), qRun() and qCleanup() to
1857 // provide a replacement for qExec() that calls qRun() once for each
1858 // built-in style. So think twice about moving parts between these three
1859 // functions, as doing so may mess up QtQuick's testing.
1860 qInit(testObject, argc, argv);
1861 int ret = qRun();
1862 qCleanup();
1863
1864#if defined(Q_OS_WASM)
1865 EM_ASM({
1866 if (typeof Module != "undefined" && typeof Module.notifyTestFinished != "undefined")
1867 Module.notifyTestFinished($0);
1868 }, ret);
1869#endif // Q_OS_WASM
1870
1871 return ret;
1872}
1873
1874/*! \internal
1875*/
1876void QTest::qInit(QObject *testObject, int argc, char **argv)
1877{
1879 CrashHandler::maybeDisableCoreDump();
1880 QBenchmarkGlobalData::current = new QBenchmarkGlobalData;
1881
1882#if defined(Q_OS_MACOS)
1883 // Don't restore saved window state for auto tests
1884 QTestPrivate::disableWindowRestore();
1885
1886 // Disable App Nap which may cause tests to stall
1887 if (!appNapDisabler)
1888 appNapDisabler.emplace();
1889
1890 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1891 kIOPMAssertionLevelOn, CFSTR("QtTest running tests"),
1892 &macPowerSavingDisabled);
1893#endif
1894
1895 QTestPrivate::parseBlackList();
1896 QTestResult::reset();
1897
1898 QTEST_ASSERT(testObject);
1899 QTEST_ASSERT(!currentTestObject);
1900 currentTestObject = testObject;
1901
1902 const QMetaObject *metaObject = testObject->metaObject();
1903 QTEST_ASSERT(metaObject);
1904
1905 QTestResult::setCurrentTestObject(metaObject->className());
1906 if (argc > 0)
1907 QTestResult::setCurrentAppName(argv[0]);
1908
1909 qtest_qParseArgs(argc, argv, false);
1910
1911#if QT_CONFIG(valgrind)
1912 if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess)
1913#endif
1914 QTestLog::startLogging();
1915
1916#ifdef Q_OS_ANDROID
1917 androidExitCodeFile().remove();
1918#endif
1919}
1920
1921/*! \internal
1922*/
1924{
1925 QTEST_ASSERT(currentTestObject);
1926
1927#if QT_CONFIG(valgrind)
1928 int callgrindChildExitCode = 0;
1929#endif
1930
1931#ifndef QT_NO_EXCEPTIONS
1932 try {
1933#endif
1934
1935#if QT_CONFIG(valgrind)
1936 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) {
1937 if (Q_UNLIKELY(!qApp))
1938 qFatal("QtTest: -callgrind option is not available with QTEST_APPLESS_MAIN");
1939
1940 const QStringList origAppArgs(QCoreApplication::arguments());
1941 if (!QBenchmarkValgrindUtils::rerunThroughCallgrind(origAppArgs, callgrindChildExitCode))
1942 return -1;
1943
1944 QBenchmarkValgrindUtils::cleanup();
1945
1946 } else
1947#endif
1948 {
1949 std::optional<CrashHandler::FatalSignalHandler> handler;
1950 CrashHandler::prepareStackTrace();
1952 handler.emplace();
1953
1954 TestMethods::MetaMethods commandLineMethods;
1955 commandLineMethods.reserve(static_cast<size_t>(QTest::testFunctions.size()));
1956 std::vector<size_t> badFunctionIndices;
1957 size_t index = 0;
1958 for (const QString &tf : std::as_const(QTest::testFunctions)) {
1959 const QByteArray tfB = tf.toLatin1();
1960 const QByteArray signature = tfB + QByteArrayLiteral("()");
1961 QMetaMethod m = TestMethods::findMethod(currentTestObject, signature.constData());
1962 if (m.isValid() && isValidSlot(m)) {
1963 commandLineMethods.push_back(m);
1964 } else {
1965 std::fprintf(stderr, "Unknown test function: '%s'.", tfB.constData());
1966 if (!qPrintTestSlots(stderr, tfB.constData(), " Possible matches:\n"))
1967 std::fputc('\n', stderr);
1968 QTestResult::setCurrentTestFunction(tfB.constData());
1969 QTestResult::addFailure(qPrintable("Function not found: %1"_L1.arg(tf)));
1970 QTestResult::finishedCurrentTestFunction();
1971 // Record bad indices in reverse order to make removal easier:
1972 badFunctionIndices.insert(badFunctionIndices.begin(), index);
1973 }
1974 ++index;
1975 }
1976 if (badFunctionIndices.size() > 0) {
1977 // Provide relevant help to do better next time:
1978 std::fprintf(stderr, "\n%s -functions\nlists all available test functions.\n\n",
1979 QTestResult::currentAppName());
1980 if (commandLineMethods.empty()) // All requested functions missing.
1981 return 1;
1982
1983 // List is in decreasing order, so we delete later entries before
1984 // earlier, avoiding problems with entries after each deletion
1985 // changing index:
1986 for (size_t i : std::as_const(badFunctionIndices)) {
1987 // Purge the bogus entries from testFunctions and testTags. We
1988 // need to do this from testTags so that its indexing matches
1989 // commandLineMethods. Quick Test will be calling qRun() again
1990 // later, once for each style, so we need testFunctions to stay
1991 // in sync with testTags, so we don't attempt to remove the same
1992 // tag again on each repeat (QTBUG-143440).
1993 QTest::testFunctions.removeAt(i);
1994 QTest::testTags.removeAt(i);
1995 }
1996 }
1997 // If commandLineMethods is empty, constructor uses all available instead:
1998 TestMethods test(currentTestObject, std::move(commandLineMethods));
1999
2000 int remainingRepetitions = repetitions;
2001 const bool repeatForever = repetitions < 0;
2002 const int badArgCount = QTestLog::failCount(); // Stop if anything else fails.
2003 while (!(QTestLog::failCount() > badArgCount)
2004 && (repeatForever || remainingRepetitions-- > 0)) {
2005 QTestTable::globalTestTable();
2006 test.invokeTests(currentTestObject);
2007 QTestTable::clearGlobalTestTable();
2008 }
2009 }
2010
2011#ifndef QT_NO_EXCEPTIONS
2012 } catch (...) {
2013 QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__);
2014 if (QTestResult::currentTestFunction()) {
2015 QTestResult::finishedCurrentTestFunction();
2016 QTestResult::setCurrentTestFunction(nullptr);
2017 }
2018
2019 qCleanup();
2020
2021 // Re-throw exception to make debugging easier
2022 throw;
2023 return 1;
2024 }
2025#endif
2026
2027#if QT_CONFIG(valgrind)
2028 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess)
2029 return callgrindChildExitCode;
2030#endif
2031 // make sure our exit code is never going above 127
2032 // since that could wrap and indicate 0 test fails
2033 const int exitCode = qMin(QTestLog::failCount(), 127);
2034
2035#ifdef Q_OS_ANDROID
2036 QFile exitCodeFile = androidExitCodeFile();
2037 if (exitCodeFile.open(QIODevice::WriteOnly)) {
2038 exitCodeFile.write(qPrintable(QString::number(exitCode)));
2039 } else {
2040 qWarning("Failed to open %s for writing test exit code: %s",
2041 qPrintable(exitCodeFile.fileName()), qPrintable(exitCodeFile.errorString()));
2042 }
2043#endif
2044
2045 return exitCode;
2046}
2047
2048/*! \internal
2049*/
2051{
2052 currentTestObject = nullptr;
2053
2054#if QT_CONFIG(valgrind)
2055 if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess)
2056#endif
2057 QTestLog::stopLogging();
2058
2059 delete QBenchmarkGlobalData::current;
2060 QBenchmarkGlobalData::current = nullptr;
2061
2062#if defined(Q_OS_MACOS)
2063 IOPMAssertionRelease(macPowerSavingDisabled);
2064 appNapDisabler = std::nullopt;
2065#endif
2066}
2067
2068#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
2069/*!
2070 Registers the test \a name, with entry function \a entryFunction, in a
2071 central test case registry for the current binary.
2072
2073 The \a name will be listed when running the batch test binary with no
2074 parameters. Running the test binary with the argv[1] of \a name will result
2075 in \a entryFunction being called.
2076
2077 \since 6.5
2078*/
2079void QTest::qRegisterTestCase(const QString &name, TestEntryFunction entryFunction)
2080{
2081 QTest::TestRegistry::instance()->registerTest(name, entryFunction);
2082}
2083
2084QList<QString> QTest::qGetTestCaseNames()
2085{
2086 return QTest::TestRegistry::instance()->getAllTestNames();
2087}
2088
2089QTest::TestEntryFunction QTest::qGetTestCaseEntryFunction(const QString& name)
2090{
2091 return QTest::TestRegistry::instance()->getTestEntryFunction(name);
2092}
2093
2094#endif // QT_CONFIG(batch_test_support)
2095
2096/*!
2097 \overload
2098 \since 4.4
2099
2100 Behaves identically to qExec(QObject *, int, char**) but takes a
2101 QStringList of \a arguments instead of a \c char** list.
2102*/
2103int QTest::qExec(QObject *testObject, const QStringList &arguments)
2104{
2105 const int argc = arguments.size();
2106 QVarLengthArray<char *> argv(argc);
2107
2108 QList<QByteArray> args;
2109 args.reserve(argc);
2110
2111 for (int i = 0; i < argc; ++i)
2112 {
2113 args.append(arguments.at(i).toLocal8Bit().constData());
2114 argv[i] = args.last().data();
2115 }
2116
2117 return qExec(testObject, argc, argv.data());
2118}
2119
2120/*! \internal
2121*/
2122void QTest::qFail(const char *message, const char *file, int line)
2123{
2124 QTestResult::fail(message, file, line);
2125}
2126
2127/*! \internal
2128*/
2129bool QTest::qVerify(bool statement, const char *statementStr, const char *description,
2130 const char *file, int line)
2131{
2132 return QTestResult::verify(statement, statementStr, description, file, line);
2133}
2134
2135/*! \fn void QTest::qSkip(const char *message, const char *file, int line)
2136 \internal
2137*/
2138void QTest::qSkip(const char *message, const char *file, int line)
2139{
2140 QTestResult::addSkip(message, file, line);
2141 QTestResult::setSkipCurrentTest(true);
2142}
2143
2144/*! \fn bool QTest::qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
2145 \internal
2146*/
2147bool QTest::qExpectFail(const char *dataIndex, const char *comment,
2148 QTest::TestFailMode mode, const char *file, int line)
2149{
2150 return QTestResult::expectFail(dataIndex, qstrdup(comment), mode, file, line);
2151}
2152
2153/*!
2154 \internal
2155
2156 Executes qFail() following a failed QVERIFY_THROWS_EXCEPTION or
2157 QVERIFY_THROWS_NO_EXCEPTION, passing a suitable message created from \a expected,
2158 \a what, along with \a file and \a line.
2159
2160 The \a expected parameter contains the type of the exception that is expected to
2161 be thrown, or \nullptr, if no exception was expected.
2162
2163 The \a what parameter contains the result of \c{std::exception::what()}, or nullptr,
2164 if a non-\c{std::exception}-derived exception was caught.
2165
2166 The \a file and \a line parameters hold expansions of the \c{__FILE__} and \c{__LINE__}
2167 macros, respectively.
2168*/
2169void QTest::qCaught(const char *expected, const char *what, const char *file, int line)
2170{
2171 auto message = [&] {
2172 const auto exType = what ? "std::" : "unknown ";
2173 const auto ofType = expected ? " of type " : "";
2174 const auto no = expected ? "an" : "no";
2175 const auto withMsg = what ? " with message " : "";
2176 const auto protect = [](const char *s) { return s ? s : ""; };
2177
2178 return QString::asprintf("Expected %s exception%s%s to be thrown, "
2179 "but caught %sexception%s%s",
2180 no, ofType, protect(expected),
2181 exType, withMsg, protect(what));
2182 };
2183 qFail(message().toUtf8().constData(), file, line);
2184}
2185
2186/*!
2187 \internal
2188
2189 Contains the implementation of the catch(...) block of
2190 QVERIFY_THROWS_EXCEPTION.
2191
2192 The function inspects std::current_exception() by rethrowing it using
2193 std::rethrow_exception().
2194
2195 The function must be called from a catch handler.
2196
2197 If the exception inherits std::exception, its what() message is logged and
2198 this function returns normally. The caller of this function must then
2199 execute a \c{QTEST_FAIL_ACTION} to exit from the test function.
2200
2201 Otherwise, a message saying an unknown exception was caught is logged and
2202 this function rethrows the exception, skipping the \c{QTEST_FAIL_ACTION}
2203 that follows this function call in the caller.
2204*/
2205void QTest::qCaught(const char *expected, const char *file, int line)
2206{
2207 try {
2208 // let's see what the cat brought us:
2209 std::rethrow_exception(std::current_exception());
2210 } catch (const std::exception &e) {
2211 qCaught(expected, e.what(), file, line);
2212 } catch (...) {
2213 qCaught(expected, nullptr, file, line);
2214 throw;
2215 }
2216 // caller shall invoke `QTEST_FAIL_ACTION` if control reached here
2217}
2218
2219
2220#if QT_DEPRECATED_SINCE(6, 3)
2221/*!
2222 \internal
2223 \deprecated [6.3] Use qWarning() instead.
2224*/
2225void QTest::qWarn(const char *message, const char *file, int line)
2226{
2227 QTestLog::warn(message, file, line);
2228}
2229#endif
2230
2231/*!
2232 Ignores messages created by qDebug(), qInfo() or qWarning(). If the \a message
2233 with the corresponding \a type is outputted, it will be removed from the
2234 test log. If the test finished and the \a message was not outputted,
2235 a test failure is appended to the test log.
2236
2237 \note Invoking this function will only ignore one message. If the message
2238 you want to ignore is output twice, you have to call ignoreMessage() twice,
2239 too.
2240
2241 Example:
2242 \snippet code/src_qtestlib_qtestcase.cpp 19
2243
2244 The example above tests that QDir::mkdir() outputs the right warning when invoked
2245 with an invalid file name.
2246
2247 \note \a message will be interpreted as UTF-8.
2248*/
2249void QTest::ignoreMessage(QtMsgType type, const char *message)
2250{
2251 QTestLog::ignoreMessage(type, message);
2252}
2253
2254#if QT_CONFIG(regularexpression)
2255/*!
2256 \overload
2257
2258 Ignores messages created by qDebug(), qInfo() or qWarning(). If the message
2259 matching \a messagePattern
2260 with the corresponding \a type is outputted, it will be removed from the
2261 test log. If the test finished and the message was not outputted,
2262 a test failure is appended to the test log.
2263
2264 \note Invoking this function will only ignore one message. If the message
2265 you want to ignore is output twice, you have to call ignoreMessage() twice,
2266 too.
2267
2268 \since 5.3
2269*/
2270void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
2271{
2272 QTestLog::ignoreMessage(type, messagePattern);
2273}
2274#endif // QT_CONFIG(regularexpression)
2275
2276/*!
2277 \since 6.8
2278 \overload failOnWarning()
2280 Appends a test failure to the test log if any warning is output.
2281
2282 \sa failOnWarning(const char *)
2283*/
2285{
2286 return QTestLog::failOnWarning();
2287}
2288
2289/*!
2290 \since 6.3
2291 \overload failOnWarning()
2293 Appends a test failure to the test log if the \a message is output.
2294
2295 \sa failOnWarning()
2296*/
2297void QTest::failOnWarning(const char *message)
2298{
2299 return QTestLog::failOnWarning(message);
2300}
2301
2302#if QT_CONFIG(regularexpression)
2303/*!
2304 \since 6.3
2305
2306 Appends a test failure to the test log for each warning that matches
2307 \a messagePattern.
2308
2309 The test function will continue execution when a failure is added. To abort
2310 the test instead, you can check \l currentTestFailed() and return early if
2311 it's \c true.
2312
2313 For each warning, the first pattern that matches will cause a failure,
2314 and the remaining patterns will be ignored.
2315
2316 All patterns are cleared at the end of each test function.
2317
2318 \code
2319 void FileTest::loadFiles()
2320 {
2321 QTest::failOnWarning(QRegularExpression("^Failed to load"));
2322
2323 // Each of these will cause a test failure:
2324 qWarning() << "Failed to load image";
2325 qWarning() << "Failed to load video";
2326 }
2327 \endcode
2328
2329 To fail every test that triggers a given warning, pass a suitable regular
2330 expression to this function in \l {Creating a Test}{init()}:
2331
2332 \code
2333 void FileTest::init()
2334 {
2335 QTest::failOnWarning(
2336 QRegularExpression("QFile::.*: File(.*) already open"));
2337 }
2338 \endcode
2339
2340 For the common case of failing on \e any warning pass no parameter:
2341
2342 \code
2343 void FileTest::init()
2344 {
2345 QTest::failOnWarning();
2346 }
2347 \endcode
2348
2349 \note \l ignoreMessage() takes precedence over this function, so any
2350 warnings that match a pattern given to both \c ignoreMessage() and
2351 \c failOnWarning() will be ignored.
2352
2353 \sa {Qt Test Environment Variables}{QTEST_FATAL_FAIL}
2354*/
2355void QTest::failOnWarning(const QRegularExpression &messagePattern)
2356{
2357 QTestLog::failOnWarning(messagePattern);
2358}
2359#endif // QT_CONFIG(regularexpression)
2360
2361/*! \internal
2362*/
2363
2364#ifdef Q_OS_WIN
2365static inline bool isWindowsBuildDirectory(const QString &dirName)
2366{
2367 return dirName.compare("Debug"_L1, Qt::CaseInsensitive) == 0
2368 || dirName.compare("Release"_L1, Qt::CaseInsensitive) == 0;
2369}
2370#endif
2371
2372#if QT_CONFIG(temporaryfile)
2373/*!
2374 Extracts a directory from resources to disk. The content is extracted
2375 recursively to a temporary folder. The extracted content is removed
2376 automatically once the last reference to the return value goes out of scope.
2377
2378 \a dirName is the name of the directory to extract from resources.
2379
2380 Returns the temporary directory where the data was extracted or null in case of
2381 errors.
2382*/
2383QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
2384{
2385 QSharedPointer<QTemporaryDir> result; // null until success, then == tempDir
2386
2387 QSharedPointer<QTemporaryDir> tempDir = QSharedPointer<QTemporaryDir>::create();
2388
2389 tempDir->setAutoRemove(true);
2390
2391 if (!tempDir->isValid())
2392 return result;
2393
2394 const QString dataPath = tempDir->path();
2395 const QString resourcePath = u':' + dirName;
2396 const QFileInfo fileInfo(resourcePath);
2397
2398 if (!fileInfo.isDir()) {
2399 qWarning("Resource path '%s' is not a directory.", qPrintable(resourcePath));
2400 return result;
2401 }
2402
2403 bool isResourceDirEmpty = true;
2404 for (const auto &dirEntry : QDirListing(resourcePath, QDirListing::IteratorFlag::Recursive)) {
2405 isResourceDirEmpty = false;
2406 if (!dirEntry.isDir()) {
2407 const QString &filePath = dirEntry.filePath();
2408 const QString destination =
2409 dataPath + u'/' + QStringView{filePath}.sliced(resourcePath.size());
2410 QFileInfo destinationFileInfo(destination);
2411 QDir().mkpath(destinationFileInfo.path());
2412 QFile file(filePath);
2413 if (!file.copy(destination, QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup)) {
2414 qWarning("Failed to copy '%ls': %ls.", qUtf16Printable(filePath),
2415 qUtf16Printable(file.errorString()));
2416 return result;
2417 }
2418 }
2419 }
2420
2421 if (isResourceDirEmpty) {
2422 qWarning("Resource directory '%s' is empty.", qPrintable(resourcePath));
2423 return result;
2424 }
2425
2426 result = std::move(tempDir);
2427
2428 return result;
2429}
2430#endif // QT_CONFIG(temporaryfile)
2431
2432/*! \internal
2433*/
2434
2435QString QTest::qFindTestData(const QString& base, const char *file, int line, const char *builddir,
2436 const char *sourcedir)
2437{
2438 QString found;
2439
2440 // Testdata priorities:
2441
2442 // 1. relative to test binary.
2443 if (qApp) {
2444 QDir binDirectory(QCoreApplication::applicationDirPath());
2445 if (binDirectory.exists(base)) {
2446 found = binDirectory.absoluteFilePath(base);
2447 }
2448#ifdef Q_OS_WIN
2449 // Windows: The executable is typically located in one of the
2450 // 'Release' or 'Debug' directories.
2451 else if (isWindowsBuildDirectory(binDirectory.dirName())
2452 && binDirectory.cdUp() && binDirectory.exists(base)) {
2453 found = binDirectory.absoluteFilePath(base);
2454 }
2455#endif // Q_OS_WIN
2456 else if (QTestLog::verboseLevel() >= 2) {
2457 const QString candidate = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + u'/' + base);
2458 QTestLog::info(qPrintable("testdata %1 not found relative to test binary [%2]; "
2459 "checking next location"_L1.arg(base, candidate)),
2460 file, line);
2461 }
2462 }
2463
2464 // 2. installed path.
2465 if (found.isEmpty()) {
2466 const char *testObjectName = QTestResult::currentTestObjectName();
2467 if (testObjectName) {
2468 const QStringList testsPaths = QLibraryInfo::paths(QLibraryInfo::TestsPath);
2469 for (const QString &testsPath : testsPaths) {
2470 const QString candidate = "%1/%2/%3"_L1
2471 .arg(testsPath, QFile::decodeName(testObjectName).toLower(), base);
2472 if (QFileInfo::exists(candidate)) {
2473 found = candidate;
2474 } else if (QTestLog::verboseLevel() >= 2) {
2475 QTestLog::info(qPrintable("testdata %1 not found in tests install path [%2]; "
2476 "checking next location"_L1
2477 .arg(base, QDir::toNativeSeparators(candidate))),
2478 file, line);
2479 }
2480 }
2481 }
2482 }
2483
2484 // 3. relative to test source.
2485 if (found.isEmpty() && qstrncmp(file, ":/", 2) != 0) {
2486 // srcdir is the directory containing the calling source file.
2487 QFileInfo srcdir(QFileInfo(QFile::decodeName(file)).path());
2488
2489 // If the srcdir is relative, that means it is relative to the current working
2490 // directory of the compiler at compile time, which should be passed in as `builddir'.
2491 if (!srcdir.isAbsolute() && builddir)
2492 srcdir.setFile(QFile::decodeName(builddir) + u'/' + srcdir.filePath());
2493
2494 const QString canonicalPath = srcdir.canonicalFilePath();
2495 const QString candidate = "%1/%2"_L1.arg(canonicalPath, base);
2496 if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) {
2497 found = candidate;
2498 } else if (QTestLog::verboseLevel() >= 2) {
2499 QTestLog::info(qPrintable(
2500 "testdata %1 not found relative to source path [%2]"_L1
2501 .arg(base, QDir::toNativeSeparators(candidate))),
2502 file, line);
2503 }
2504 }
2505
2506 // 4. Try resources
2507 if (found.isEmpty()) {
2508 const QString candidate = ":/%1"_L1.arg(base);
2509 if (QFileInfo::exists(candidate)) {
2510 found = candidate;
2511 } else if (QTestLog::verboseLevel() >= 2) {
2512 QTestLog::info(qPrintable(
2513 "testdata %1 not found in resources [%2]"_L1
2514 .arg(base, QDir::toNativeSeparators(candidate))),
2515 file, line);
2516 }
2517 }
2518
2519 // 5. Try current directory
2520 if (found.isEmpty()) {
2521 const QString candidate = QDir::currentPath() + u'/' + base;
2522 if (QFileInfo::exists(candidate)) {
2523 found = candidate;
2524 } else if (QTestLog::verboseLevel() >= 2) {
2525 QTestLog::info(qPrintable(
2526 "testdata %1 not found in current directory [%2]"_L1
2527 .arg(base, QDir::toNativeSeparators(candidate))),
2528 file, line);
2529 }
2530 }
2531
2532 // 6. Try main source directory
2533 if (found.isEmpty()) {
2534 const QString candidate = QTest::mainSourcePath % u'/' % base;
2535 if (QFileInfo::exists(candidate)) {
2536 found = candidate;
2537 } else if (QTestLog::verboseLevel() >= 2) {
2538 QTestLog::info(qPrintable(
2539 "testdata %1 not found in main source directory [%2]"_L1
2540 .arg(base, QDir::toNativeSeparators(candidate))),
2541 file, line);
2542 }
2543 }
2544
2545 // 7. Try the supplied source directory
2546 if (found.isEmpty() && sourcedir) {
2547 const QString candidate = QFile::decodeName(sourcedir) % u'/' % base;
2548 if (QFileInfo::exists(candidate)) {
2549 found = candidate;
2550 } else if (QTestLog::verboseLevel() >= 2) {
2551 QTestLog::info(qPrintable(
2552 "testdata %1 not found in supplied source directory [%2]"_L1
2553 .arg(base, QDir::toNativeSeparators(candidate))),
2554 file, line);
2555 }
2556 }
2557
2558
2559 if (found.isEmpty()) {
2560 QTestLog::warn(qPrintable(
2561 "testdata %1 could not be located!"_L1.arg(base)),
2562 file, line);
2563 } else if (QTestLog::verboseLevel() >= 1) {
2564 QTestLog::info(qPrintable(
2565 "testdata %1 was located at %2"_L1.arg(base, QDir::toNativeSeparators(found))),
2566 file, line);
2567 }
2568
2569 return found;
2570}
2571
2572/*! \internal
2573*/
2574QString QTest::qFindTestData(const char *base, const char *file, int line, const char *builddir,
2575 const char *sourcedir)
2576{
2577 return qFindTestData(QFile::decodeName(base), file, line, builddir, sourcedir);
2578}
2579
2580/*! \internal
2581*/
2582void *QTest::qData(const char *tagName, int typeId)
2583{
2584 return fetchData(QTestResult::currentTestData(), tagName, typeId);
2585}
2586
2587/*! \internal
2588*/
2589void *QTest::qGlobalData(const char *tagName, int typeId)
2590{
2591 return fetchData(QTestResult::currentGlobalTestData(), tagName, typeId);
2592}
2593
2594/*! \internal
2595*/
2596void *QTest::qElementData(const char *tagName, int metaTypeId)
2597{
2598 QTEST_ASSERT(tagName);
2599 QTestData *data = QTestResult::currentTestData();
2600 QTEST_ASSERT(data);
2601 QTEST_ASSERT(data->parent());
2602
2603 int idx = data->parent()->indexOf(tagName);
2604 QTEST_ASSERT(idx != -1);
2605 QTEST_ASSERT(data->parent()->elementTypeId(idx) == metaTypeId);
2606
2607 return data->data(data->parent()->indexOf(tagName));
2608}
2609
2610/*! \internal
2611*/
2612void QTest::addColumnInternal(int id, const char *name)
2613{
2614 QTestTable *tbl = QTestTable::currentTestTable();
2615 QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot.");
2616
2617 tbl->addColumn(id, name);
2618}
2619
2620/*!
2621 Appends a new row to the current test data.
2622
2623 The test output will identify the test run with this test data using the
2624 name \a dataTag.
2625
2626 Returns a QTestData reference that can be used to stream in data, one value
2627 for each column in the table.
2628
2629 Example:
2630 \snippet code/src_qtestlib_qtestcase.cpp 20
2631
2632 \note This function can only be called as part of a test's data function
2633 that is invoked by the test framework.
2634
2635 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2636 a more extensive example.
2637
2638 \sa addRow(), addColumn(), QFETCH()
2639*/
2640QTestData &QTest::newRow(const char *dataTag)
2641{
2642 QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null");
2643 QTestTable *tbl = QTestTable::currentTestTable();
2644 QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot.");
2645 QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()",
2646 "Must add columns before attempting to add rows.");
2647
2648 return *tbl->newData(dataTag);
2649}
2650
2651/*!
2652 \since 5.9
2653
2654 Appends a new row to the current test data.
2655
2656 The function's arguments are passed to std::snprintf() for
2657 formatting according to \a format. See the
2658 \l{https://en.cppreference.com/w/cpp/io/c/fprintf}{std::snprintf()
2659 documentation} for caveats and limitations.
2660
2661 The test output will identify the test run with this test data using the
2662 name that results from this formatting.
2663
2664 Returns a QTestData reference that can be used to stream in data, one value
2665 for each column in the table.
2666
2667 Example:
2668 \snippet code/src_qtestlib_qtestcase.cpp addRow
2669
2670 \note This function can only be called as part of a test's data function
2671 that is invoked by the test framework.
2672
2673 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2674 a more extensive example.
2675
2676 \sa newRow(), addColumn(), QFETCH()
2677*/
2678QTestData &QTest::addRow(const char *format, ...)
2679{
2680 QTEST_ASSERT_X(format, "QTest::addRow()", "Format string cannot be null");
2681 QTestTable *tbl = QTestTable::currentTestTable();
2682 QTEST_ASSERT_X(tbl, "QTest::addRow()", "Cannot add testdata outside of a _data slot.");
2683 QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()",
2684 "Must add columns before attempting to add rows.");
2685
2686 char buf[1024];
2687
2688 va_list va;
2689 va_start(va, format);
2690 // we don't care about failures, we accept truncation, as well as trailing garbage.
2691 // Names with more than 1K characters are nonsense, anyway.
2692 std::vsnprintf(buf, sizeof buf, format, va);
2693 va_end(va);
2694
2695 return *tbl->newData(buf);
2696}
2697
2698/*! \fn template <typename T> void QTest::addColumn(const char *name, T *dummy = 0)
2699
2700 Adds a column with type \c{T} to the current test data.
2701 \a name is the name of the column. \a dummy is a workaround
2702 for buggy compilers and can be ignored.
2703
2704 To populate the column with values, newRow() can be used. Use
2705 \l QFETCH() to fetch the data in the actual test.
2706
2707 Example:
2708 \snippet code/src_qtestlib_qtestcase.cpp 21
2709
2710 \note This function can only be used called as part of a test's data
2711 function that is invoked by the test framework.
2712
2713 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2714 a more extensive example.
2715
2716 \sa QTest::newRow(), QFETCH(), QMetaType
2717*/
2718
2719/*!
2720 Returns the name of the binary that is currently executed.
2721*/
2722const char *QTest::currentAppName()
2723{
2724 return QTestResult::currentAppName();
2725}
2726
2727/*!
2728 Returns the name of the test function that is currently executed.
2729
2730 Example:
2731
2732 \snippet code/src_qtestlib_qtestcase.cpp 22
2733*/
2735{
2736 return QTestResult::currentTestFunction();
2737}
2738
2739/*!
2740 Returns the name of the current test data. If the test doesn't
2741 have any assigned testdata, the function returns \nullptr.
2742*/
2743const char *QTest::currentDataTag()
2744{
2745 return QTestResult::currentDataTag();
2746}
2747
2748/*!
2749 Returns the name of the current global test data. If the test doesn't
2750 have any assigned global testdata, the function returns \nullptr.
2751
2752 \since 6.11
2753*/
2755{
2756 return QTestResult::currentGlobalDataTag();
2757}
2758
2759/*!
2760 Returns \c true if the current test function has failed, otherwise false.
2761
2762 \sa QTest::currentTestResolved()
2763*/
2765{
2766 return QTestResult::currentTestFailed();
2767}
2768
2769/*!
2770 \since 6.5
2771 Returns \c true if the current test function has failed or skipped.
2772
2773 This applies if the test has failed or exercised a skip. When it is true,
2774 the test function should return early. In particular, the \c{QTRY_*} macros
2775 and the test event loop terminate their loops early if executed during the
2776 test function (but not its cleanup()). After a test has called a helper
2777 function that uses this module's macros, it can use this function to test
2778 whether to return early.
2779
2780 \sa QTest::currentTestFailed()
2781*/
2783{
2784 return QTestResult::currentTestFailed() || QTestResult::skipCurrentTest();
2785}
2786
2787/*!
2788 \internal
2789 \since 6.4
2790 Returns \c true during the run of the test-function and its set-up.
2791
2792 Used by the \c{QTRY_*} macros and \l QTestEventLoop to check whether to
2793 return when QTest::currentTestResolved() is true.
2794*/
2796{
2797 return QTest::inTestFunction;
2798}
2799
2800/*! \internal
2801*/
2802QObject *QTest::testObject()
2803{
2804 return currentTestObject;
2805}
2806
2807/*! \internal
2808*/
2809void QTest::setMainSourcePath(const char *file, const char *builddir)
2810{
2811 QString mainSourceFile = QFile::decodeName(file);
2812 QFileInfo fi;
2813 if (builddir)
2814 fi.setFile(QDir(QFile::decodeName(builddir)), mainSourceFile);
2815 else
2816 fi.setFile(mainSourceFile);
2817 QTest::mainSourcePath = fi.absolutePath();
2818}
2819
2820#if QT_DEPRECATED_SINCE(6, 4)
2821/*! \internal
2822 \deprecated [6.4]
2823 This function is called by various specializations of QTest::qCompare
2824 to decide whether to report a failure and to produce verbose test output.
2825
2826 The failureMsg parameter can be null, in which case a default message
2827 will be output if the compare fails. If the compare succeeds, failureMsg
2828 will not be output.
2829
2830 Using this function is not optimal, because it requires the string
2831 representations of \a actualVal and \a expectedVal to be pre-calculated,
2832 even though they will be used only if the comparison fails. Prefer using the
2833 \l compare_helper() overload that takes qxp::function_ref() for such cases.
2834
2835 If the caller creates a custom failure message showing the compared values,
2836 or if those values cannot be stringified, use the overload of the function
2837 that takes no \a actualVal and \a expectedVal parameters.
2838*/
2839bool QTest::compare_helper(bool success, const char *failureMsg,
2840 char *actualVal, char *expectedVal,
2841 const char *actual, const char *expected,
2842 const char *file, int line)
2843{
2844 return QTestResult::compare(success, failureMsg, actualVal, expectedVal,
2845 actual, expected, file, line);
2846}
2847#endif // QT_DEPRECATED_SINCE(6, 4)
2848
2849#if QT_DEPRECATED_SINCE(6, 8)
2850/*! \internal
2851 \since 6.4
2852 This function is called by various specializations of QTest::qCompare
2853 to decide whether to report a failure and to produce verbose test output.
2854
2855 The \a failureMsg parameter can be \c {nullptr}, in which case a default
2856 message will be output if the compare fails. If the comparison succeeds,
2857 \a failureMsg will not be output.
2858
2859 This overload of the function uses qxp::function_ref to defer conversion of
2860 \a actualVal and \a expectedVal to strings until that is really needed
2861 (when the comparison fails). This speeds up test case execution on success.
2862*/
2863bool QTest::compare_helper(bool success, const char *failureMsg,
2864 qxp::function_ref<const char *()> actualVal,
2865 qxp::function_ref<const char *()> expectedVal,
2866 const char *actual, const char *expected,
2867 const char *file, int line)
2868{
2869 return QTestResult::reportResult(success, &actualVal, &expectedVal,
2870 QTest::functionRefFormatter,
2871 QTest::functionRefFormatter, actual, expected,
2872 QTest::ComparisonOperation::CustomCompare,
2873 file, line, failureMsg);
2874}
2875#endif // QT_DEPRECATED_SINCE(6, 8)
2876
2877/*! \internal
2878 \since 6.8
2879 This function is called by various specializations of QTest::qCompare
2880 to decide whether to report a failure and to produce verbose test output.
2881
2882 The \a failureMsg parameter can be \c {nullptr}, in which case a default
2883 message will be output if the compare fails. If the comparison succeeds,
2884 \a failureMsg will not be output.
2885*/
2886
2887bool QTest::compare_helper(bool success, const char *failureMsg,
2888 const void *actualPtr, const void *expectedPtr,
2889 const char *(*actualFormatter)(const void *),
2890 const char *(*expectedFormatter)(const void *),
2891 const char *actual, const char *expected,
2892 const char *file, int line)
2893{
2894 return QTestResult::reportResult(success, actualPtr, expectedPtr,
2895 actualFormatter, expectedFormatter,
2896 actual, expected,
2897 QTest::ComparisonOperation::CustomCompare,
2898 file, line, failureMsg);
2899}
2900
2901
2902/*! \internal
2903 \since 6.9
2904 This function reports the result of a three-way comparison, when needed.
2905
2906 Aside from logging every check if in verbose mode and reporting an
2907 unexpected pass when failure was expected, if \a success is \c true
2908 this produces no output. Otherwise, a failure is reported. The output
2909 on failure reports the expressions compared, their values, the actual
2910 result of the comparison and the expected result of comparison, along
2911 with the supplied failure message \a failureMsg and the \a file and
2912 \a line number at which the error arose.
2913
2914 The expressions compared are supplied as \a lhsExpression and
2915 \a rhsExpression.
2916 These are combined, with \c{"<=>"}, to obtain the actual comparison
2917 expression. Their actual values are pointed to by \a lhsPtr and
2918 \a rhsPtr, which are formatted by \a lhsFormatter and \a rhsFormatter
2919 as, respectively, \c lhsFormatter(lhsPtr) and \c rhsFormatter(rhsPtr).
2920 The actual comparison expression is contrasted,
2921 in the output, with the expected comparison expression
2922 \a expectedExpression. Their respective values are supplied by
2923 \a actualOrderPtr and \a expectedOrderPtr pointers, which are
2924 formatted by \a actualOrderFormatter and \a expectedOrderFormatter.
2925
2926 If \a failureMsg is \nullptr a default is used. If a formatter
2927 function returns \a nullptr, the text \c{"<null>"} is used.
2928*/
2929bool QTest::compare_3way_helper(bool success, const char *failureMsg,
2930 const void *lhsPtr, const void *rhsPtr,
2931 const char *(*lhsFormatter)(const void*),
2932 const char *(*rhsFormatter)(const void*),
2933 const char *lhsExpression, const char *rhsExpression,
2934 const char *(*actualOrderFormatter)(const void *),
2935 const char *(*expectedOrderFormatter)(const void *),
2936 const void *actualOrderPtr, const void *expectedOrderPtr,
2937 const char *expectedExpression,
2938 const char *file, int line)
2939{
2940 return QTestResult::report3WayResult(success, failureMsg,
2941 lhsPtr, rhsPtr,
2942 lhsFormatter, rhsFormatter,
2943 lhsExpression, rhsExpression,
2944 actualOrderFormatter,
2945 expectedOrderFormatter,
2946 actualOrderPtr, expectedOrderPtr,
2947 expectedExpression,
2948 file, line);
2949}
2950
2951/*! \internal
2952 \since 6.4
2953 This function is called by various specializations of QTest::qCompare
2954 to decide whether to report a failure and to produce verbose test output.
2955
2956 This overload should be used when there is no string representation of
2957 actual and expected values, so only the \a failureMsg is shown when the
2958 comparison fails. Because of that, \a failureMsg can't be \c {nullptr}.
2959 If the comparison succeeds, \a failureMsg will not be output.
2960*/
2961bool QTest::compare_helper(bool success, const char *failureMsg, const char *actual,
2962 const char *expected, const char *file, int line)
2963{
2964 return QTestResult::compare(success, failureMsg, actual, expected, file, line);
2965}
2966
2967template <typename T>
2968static bool floatingCompare(const T &actual, const T &expected)
2969{
2970 switch (qFpClassify(expected))
2971 {
2972 case FP_INFINITE:
2973 return (expected < 0) == (actual < 0) && qFpClassify(actual) == FP_INFINITE;
2974 case FP_NAN:
2975 return qFpClassify(actual) == FP_NAN;
2976 default:
2977 if (!qFuzzyIsNull(expected))
2978 return qFuzzyCompare(actual, expected);
2979 Q_FALLTHROUGH();
2980 case FP_SUBNORMAL: // subnormal is always fuzzily null
2981 case FP_ZERO:
2982 return qFuzzyIsNull(actual);
2983 }
2984}
2985
2986/*! \fn bool QTest::qCompare(const qfloat16 &t1, const qfloat16 &t2, const char *actual, const char *expected, const char *file, int line)
2987 \internal
2988*/
2989bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
2990 const char *file, int line)
2991{
2992 auto formatter = Internal::genericToString<qfloat16>;
2993 return compare_helper(floatingCompare(t1, t2),
2994 "Compared qfloat16s are not the same (fuzzy compare)",
2995 &t1, &t2, formatter, formatter,
2996 actual, expected, file, line);
2997}
2998
2999/*! \fn bool QTest::qCompare(const float &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
3000 \internal
3001*/
3002bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const char *expected,
3003 const char *file, int line)
3004{
3005 return QTestResult::compare(floatingCompare(t1, t2),
3006 "Compared floats are not the same (fuzzy compare)",
3007 t1, t2, actual, expected, file, line);
3008}
3009
3010/*! \fn bool QTest::qCompare(const double &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
3011 \internal
3012*/
3013bool QTest::qCompare(double const &t1, double const &t2, const char *actual, const char *expected,
3014 const char *file, int line)
3015{
3016 return QTestResult::compare(floatingCompare(t1, t2),
3017 "Compared doubles are not the same (fuzzy compare)",
3018 t1, t2, actual, expected, file, line);
3019}
3020
3021/*! \fn bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected, const char *file, int line)
3022 \internal
3023 \since 5.14
3024*/
3025bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
3026 const char *file, int line)
3027{
3028 return QTestResult::compare(t1 == t2,
3029 "Compared values are not the same",
3030 t1, t2, actual, expected, file, line);
3031}
3032
3033#if QT_POINTER_SIZE == 8
3034/*! \fn bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected, const char *file, int line)
3035 \internal
3036 \since 6.0
3037*/
3038
3039bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected,
3040 const char *file, int line)
3041{
3042 return QTestResult::compare(t1 == t2,
3043 "Compared values are not the same",
3044 t1, t2, actual, expected, file, line);
3045}
3046#endif // QT_POINTER_SIZE == 8
3047
3048/*! \fn bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected, const char *file, int line)
3049 \internal
3050 \since 5.14
3051*/
3052bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected,
3053 const char *file, int line)
3054{
3055 return QTestResult::compare(t1 == t2,
3056 "Compared values are not the same",
3057 t1, t2, actual, expected, file, line);
3058}
3059
3060/*! \fn bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
3061 \internal
3062 \since 5.14
3063*/
3064bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected,
3065 const char *file, int line)
3066{
3067 return QTestResult::compare(t1 == t2,
3068 "Compared values are not the same",
3069 t1, t2, actual, expected, file, line);
3070}
3071
3072/*!
3073 \internal
3074 \since 5.14
3075*/
3076bool QTest::qCompare(QStringView t1, const QLatin1StringView &t2, const char *actual, const char *expected,
3077 const char *file, int line)
3078{
3079 return QTestResult::compare(t1 == t2,
3080 "Compared values are not the same",
3081 t1, t2, actual, expected, file, line);
3082}
3083
3084/*!
3085 \internal
3086 \since 5.14
3087*/
3088bool QTest::qCompare(const QLatin1StringView &t1, QStringView t2, const char *actual, const char *expected,
3089 const char *file, int line)
3090{
3091 return QTestResult::compare(t1 == t2,
3092 "Compared values are not the same",
3093 t1, t2, actual, expected, file, line);
3094}
3095
3096/*! \fn bool QTest::qCompare(const QString &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
3097 \internal
3098 \since 5.14
3099*/
3100
3101/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1StringView &t2, const char *actual, const char *expected, const char *file, int line)
3102 \internal
3103 \since 5.14
3104*/
3105
3106/*! \fn bool QTest::qCompare(const QLatin1StringView &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
3107 \internal
3108 \since 5.14
3109*/
3110
3111/*! \fn bool QTest::qCompare(const double &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
3112 \internal
3113*/
3114
3115/*! \fn bool QTest::qCompare(const float &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
3116 \internal
3117*/
3118
3119#define TO_STRING_IMPL(TYPE, FORMAT) template
3120 <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
3121{
3122 char *msg = new char[128];
3123 std::snprintf(msg, 128, #FORMAT, t);
3124 return msg; \
3125}
3126
3127TO_STRING_IMPL(short, %hd)
3128TO_STRING_IMPL(ushort, %hu)
3129TO_STRING_IMPL(int, %d)
3130TO_STRING_IMPL(uint, %u)
3131TO_STRING_IMPL(long, %ld)
3132TO_STRING_IMPL(ulong, %lu)
3133TO_STRING_IMPL(qint64, %lld)
3134TO_STRING_IMPL(quint64, %llu)
3135TO_STRING_IMPL(bool, %d)
3136TO_STRING_IMPL(signed char, %hhd)
3137TO_STRING_IMPL(unsigned char, %hhu)
3138
3139// Merge of ISO C23 getpayload() and issignaling()
3140template <typename T> static auto decodeNanPayload(T t)
3141{
3142 constexpr int Digits = std::numeric_limits<T>::digits;
3143 constexpr quint64 MantissaMask = (Q_UINT64_C(1) << (Digits - 1)) - 1;
3144 constexpr quint64 IsQuietBit = quint64(QT_CONFIG(signaling_nan)) << (Digits - 2);
3145 constexpr quint64 PayloadMask = MantissaMask & ~IsQuietBit;
3146
3147 struct R {
3148 quint64 payload;
3149 bool isQuiet;
3150 } r;
3151 Q_ASSERT(qIsNaN(t));
3152 quint64 u = qFromUnaligned<typename QIntegerForSizeof<T>::Unsigned>(&t);
3153 r.payload = u & PayloadMask;
3154 r.isQuiet = !QT_CONFIG(signaling_nan) || (u & IsQuietBit);
3155 return r;
3156}
3157
3158// Be consistent about display of infinities and NaNs (snprintf()'s varies,
3159// notably on MinGW, despite POSIX documenting "[-]inf" or "[-]infinity" for %f,
3160// %e and %g, uppercasing for their capital versions; similar for "nan"):
3161template <typename T> static char *toStringFp(T t)
3162{
3163 using std::signbit;
3164 char *msg = new char[128];
3165 bool negative = signbit(t);
3166
3167 switch (qFpClassify(t)) {
3168 case FP_INFINITE:
3169 qstrncpy(msg, (negative ? "-inf" : "inf"), 128);
3170 break;
3171 case FP_NAN:
3172 if (auto r = decodeNanPayload(t); r.payload) {
3173 std::snprintf(msg, 128, "%s%snan(%#llx)",
3174 negative ? "-" : "", r.isQuiet ? "" : "s", r.payload);
3175 } else {
3176 Q_ASSERT(r.isQuiet); // only quiet NaNs can have payload == 0
3177 qstrncpy(msg, (negative ? "-nan" : "nan"), 128);
3178 }
3179 break;
3180 case FP_ZERO:
3181 qstrncpy(msg, (negative ? "-0 (-0x0p+0)" : "0 (0x0p+0)"), 128);
3182 break;
3183 default:
3184 std::snprintf(msg, 128, "%.*g (%a)", std::numeric_limits<T>::digits10 + 1, double(t),
3185 double(t));
3186 break;
3187 }
3188 return msg;
3189}
3190
3191#define TO_STRING_FLOAT(TYPE) template
3192 <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
3193{
3194 return toStringFp(t); \
3195}
3196TO_STRING_FLOAT(qfloat16)
3197TO_STRING_FLOAT(float)
3198TO_STRING_FLOAT(double)
3199
3200template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t)
3201{
3202 unsigned char c = static_cast<unsigned char>(t);
3203 char *msg = new char[16];
3204 switch (c) {
3205 case 0x00:
3206 qstrcpy(msg, "'\\0'");
3207 break;
3208 case 0x07:
3209 qstrcpy(msg, "'\\a'");
3210 break;
3211 case 0x08:
3212 qstrcpy(msg, "'\\b'");
3213 break;
3214 case 0x09:
3215 qstrcpy(msg, "'\\t'");
3216 break;
3217 case 0x0a:
3218 qstrcpy(msg, "'\\n'");
3219 break;
3220 case 0x0b:
3221 qstrcpy(msg, "'\\v'");
3222 break;
3223 case 0x0c:
3224 qstrcpy(msg, "'\\f'");
3225 break;
3226 case 0x0d:
3227 qstrcpy(msg, "'\\r'");
3228 break;
3229 case 0x22:
3230 qstrcpy(msg, "'\\\"'");
3231 break;
3232 case 0x27:
3233 qstrcpy(msg, "'\\\''");
3234 break;
3235 case 0x5c:
3236 qstrcpy(msg, "'\\\\'");
3237 break;
3238 default:
3239 if (c < 0x20 || c >= 0x7F)
3240 std::snprintf(msg, 16, "'\\x%02x'", c);
3241 else
3242 std::snprintf(msg, 16, "'%c'" , c);
3243 }
3244 return msg;
3245}
3246
3247/*! \internal
3248*/
3249char *QTest::toString(const char *str)
3250{
3251 if (!str) {
3252 char *msg = new char[1];
3253 *msg = '\0';
3254 return msg;
3255 }
3256 char *msg = new char[strlen(str) + 1];
3257 return qstrcpy(msg, str);
3258}
3259
3260/*! \internal
3261*/
3262char *QTest::toString(const volatile void *p) // Use volatile to match compare_ptr_helper()
3263{
3264 char *msg = new char[128];
3265 std::snprintf(msg, 128, "%p", p);
3266 return msg;
3267}
3268
3269/*! \internal
3270*/
3271char *QTest::toString(const volatile QObject *vo)
3272{
3273 if (vo == nullptr)
3274 return qstrdup("<null>");
3275
3276 return QTest::toString(const_cast<const QObject*>(vo));
3277}
3278
3279/*! \internal
3280*/
3281char *QTest::toString(const QObject *o)
3282{
3283 if (o == nullptr)
3284 return qstrdup("<null>");
3285
3286 const QString &name = o->objectName();
3287 const char *className = o->metaObject()->className();
3288 char *msg = new char[256];
3289 if (name.isEmpty())
3290 std::snprintf(msg, 256, "%s/%p", className, o);
3291 else
3292 std::snprintf(msg, 256, "%s/\"%s\"", className, qPrintable(name));
3293 return msg;
3294}
3295
3296
3297/*! \fn char *QTest::toString(const QColor &color)
3298 \internal
3299*/
3300
3301/*! \fn char *QTest::toString(const QRegion &region)
3302 \internal
3303*/
3304
3305/*! \fn char *QTest::toString(const QHostAddress &addr)
3306 \internal
3307*/
3308
3309/*! \fn char *QTest::toString(QNetworkReply::NetworkError code)
3310 \internal
3311*/
3312
3313/*! \fn char *QTest::toString(const QNetworkCookie &cookie)
3314 \internal
3315*/
3316
3317/*! \fn char *QTest::toString(const QList<QNetworkCookie> &list)
3318 \internal
3319*/
3320
3321/*! \internal
3322*/
3323bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
3324 const char *expected, const char *file, int line)
3325{
3326 auto formatter = Internal::genericToString<const char *>;
3327 return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same",
3328 &t1, &t2, formatter, formatter,
3329 actual, expected, file, line);
3330}
3331
3332/*!
3333 \namespace QTest::Internal
3334 \internal
3335*/
3336
3337/*! \fn bool QTest::compare_ptr_helper(const volatile void *t1, const volatile void *t2, const char *actual, const char *expected, const char *file, int line)
3338 \internal
3339*/
3340
3341/*! \fn bool QTest::compare_ptr_helper(const volatile void *t1, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
3342 \internal
3343*/
3344
3345/*! \fn bool QTest::compare_ptr_helper(std::nullptr_t, const volatile void *t2, const char *actual, const char *expected, const char *file, int line)
3346 \internal
3347*/
3348
3349/*! \fn template <typename T1, typename T2> bool QTest::qCompare(const T1 &t1, const T2 &t2, const char *actual, const char *expected, const char *file, int line)
3350 \internal
3351*/
3352
3353/*! \fn bool QTest::qCompare(const QIcon &t1, const QIcon &t2, const char *actual, const char *expected, const char *file, int line)
3354 \internal
3355*/
3356
3357/*! \fn bool QTest::qCompare(const QImage &t1, const QImage &t2, const char *actual, const char *expected, const char *file, int line)
3358 \internal
3359*/
3360
3361/*! \fn bool QTest::qCompare(const QPixmap &t1, const QPixmap &t2, const char *actual, const char *expected, const char *file, int line)
3362 \internal
3363*/
3364
3365/*! \fn template <typename T> bool QTest::qCompare(const T &t1, const T &t2, const char *actual, const char *expected, const char *file, int line)
3366 \internal
3367*/
3368
3369/*! \fn template <typename T> bool QTest::qCompare(const T *t1, const T *t2, const char *actual, const char *expected, const char *file, int line)
3370 \internal
3371*/
3372
3373/*! \fn template <typename T> bool QTest::qCompare(T *t, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
3374 \internal
3375*/
3376
3377/*! \fn template <typename T> bool QTest::qCompare(std::nullptr_t, T *t, const char *actual, const char *expected, const char *file, int line)
3378 \internal
3379*/
3380
3381/*! \fn template <typename T> bool QTest::qCompare(T *t1, T *t2, const char *actual, const char *expected, const char *file, int line)
3382 \internal
3383*/
3384
3385/*! \fn template <typename T1, typename T2> bool QTest::qCompare(const T1 *t1, const T2 *t2, const char *actual, const char *expected, const char *file, int line)
3386 \internal
3387*/
3388
3389/*! \fn template <typename T1, typename T2> bool QTest::qCompare(T1 *t1, T2 *t2, const char *actual, const char *expected, const char *file, int line)
3390 \internal
3391*/
3392
3393/*! \fn bool QTest::qCompare(const char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
3394 \internal
3395*/
3396
3397/*! \fn bool QTest::qCompare(char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
3398 \internal
3399*/
3400
3401/*! \fn bool QTest::qCompare(char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
3402 \internal
3403*/
3404
3405/*! \fn bool QTest::qCompare(const char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
3406 \internal
3407*/
3408
3409/*! \fn bool QTest::qCompare(const QStringList &t1, const QStringList &t2, const char *actual, const char *expected, const char *file, int line)
3410 \internal
3411*/
3412
3413/*! \fn template <typename T> bool QTest::qCompare(const QList<T> &t1, const QList<T> &t2, const char *actual, const char *expected, const char *file, int line)
3414 \internal
3415*/
3416
3417/*! \fn template <typename T> bool QTest::qCompare(const QFlags<T> &t1, const T &t2, const char *actual, const char *expected, const char *file, int line)
3418 \internal
3419*/
3420
3421/*! \fn template <typename T> bool QTest::qCompare(const QFlags<T> &t1, const int &t2, const char *actual, const char *expected, const char *file, int line)
3422 \internal
3423*/
3424
3425/*! \fn bool QTest::qCompare(const qint64 &t1, const qint32 &t2, const char *actual, const char *expected, const char *file, int line)
3426 \internal
3427*/
3428
3429/*! \fn bool QTest::qCompare(const qint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
3430 \internal
3431*/
3432
3433/*! \fn bool QTest::qCompare(const quint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
3434 \internal
3435*/
3436
3437/*! \fn bool QTest::qCompare(const qint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
3438 \internal
3439*/
3440
3441/*! \fn bool QTest::qCompare(const quint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
3442 \internal
3443*/
3444
3445/*! \fn bool QTest::qCompare(const quint32 &t1, const quint64 &t2, const char *actual, const char *expected, const char *file, int line)
3446 \internal
3447*/
3448
3449/*! \fn template <typename T> bool QTest::qTest(const T& actual, const char *elementName, const char *actualStr, const char *expected, const char *file, int line)
3450 \internal
3451*/
3452
3453/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
3454 \internal
3455*/
3456
3457/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
3458 \internal
3459*/
3460
3461/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
3462 \internal
3463*/
3464
3465/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
3466 \internal
3467*/
3468
3469/*! \fn void QTest::simulateEvent(QWidget *widget, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
3470 \internal
3471*/
3472
3473/*! \fn void QTest::simulateEvent(QWindow *window, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
3474 \internal
3475*/
3476
3477QT_END_NAMESPACE
static QMetaMethod findMethod(const QObject *obj, const char *signature)
TestMethods(const QObject *o, MetaMethods m={})
void invokeTests(QObject *testObject) const
Q_TESTLIB_EXPORT int lastMouseTimestamp
Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml)
constexpr qsizetype PrettyUnicodeMaxIncrement
void runWithWatchdog(std::optional< WatchDog > &watchDog, Functor &&f)
Q_TESTLIB_EXPORT int qRun()
Q_TESTLIB_EXPORT bool currentTestResolved()
bool reportResult(bool success, const void *lhs, const void *rhs, const char *(*lhsFormatter)(const void *), const char *(*rhsFormatter)(const void *), const char *lhsExpr, const char *rhsExpr, ComparisonOperation op, const char *file, int line)
Q_TESTLIB_EXPORT bool qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected, const char *file, int line)
constexpr qsizetype PrettyUnicodeMaxOutputSize
static void invokeTestMethodIfExists(const char *methodName, QObject *obj=QTest::currentTestObject)
Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml)
Q_TESTLIB_EXPORT bool compare_3way_helper(bool success, const char *failureMsg, const void *lhsPtr, const void *rhsPtr, const char *(*lhsFormatter)(const void *), const char *(*rhsFormatter)(const void *), const char *lhsStr, const char *rhsStr, const char *(*actualOrderFormatter)(const void *), const char *(*expectedOrderFormatter)(const void *), const void *actualOrderPtr, const void *expectedOrderPtr, const char *expectedExpression, const char *file, int line)
Q_TESTLIB_EXPORT bool qVerify(bool statement, const char *statementStr, const char *description, const char *file, int line)
void * fetchData(QTestData *data, const char *tagName, int typeId)
static QList< QBenchmarkResult > qMedian(const QList< QList< QBenchmarkResult > > &container)
Q_TESTLIB_EXPORT const char * currentTestFunction()
Returns the name of the test function that is currently executed.
Q_TESTLIB_EXPORT bool runningTest()
Q_TESTLIB_EXPORT void setMainSourcePath(const char *file, const char *builddir=nullptr)
static char * writePrettyUnicodeChar(char16_t ch, char *const buffer)
Q_TESTLIB_EXPORT char * toString(const char *)
Q_TESTLIB_EXPORT char * toString(const volatile QObject *)
static int repetitions
static void qPrintDataTags(FILE *stream)
Q_TESTLIB_EXPORT void * qElementData(const char *elementName, int metaTypeId)
Q_TESTLIB_EXPORT bool qCompare(int t1, int t2, const char *actual, const char *expected, const char *file, int line)
static QObject * currentTestObject
void setThrowOnFail(bool enable) noexcept
Q_TESTLIB_EXPORT char * toString(const volatile void *)
Q_TESTLIB_EXPORT bool qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
static int keyDelay
static bool invokeTestMethodIfValid(QMetaMethod m, QObject *obj=QTest::currentTestObject)
static bool qPrintTestSlots(FILE *stream, const char *filter=nullptr, const char *preamble="")
Q_TESTLIB_EXPORT void * qData(const char *tagName, int typeId)
int defaultEventDelay()
Q_TESTLIB_EXPORT bool qCompare(double const &t1, double const &t2, const char *actual, const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT bool currentTestFailed()
Returns true if the current test function has failed, otherwise false.
static bool skipBlacklisted
Q_TESTLIB_EXPORT bool qCompare(float const &t1, float const &t2, const char *actual, const char *expected, const char *file, int line)
void setThrowOnSkip(bool enable) noexcept
char * formatString(const char *prefix, const char *suffix, size_t numArguments,...)
static bool inTestFunction
char * toPrettyCString(const char *p, qsizetype length)
char * toHexRepresentation(const char *ba, qsizetype length)
Returns a pointer to a string that is the string ba represented as a space-separated sequence of hex ...
Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
static int mouseDelay
static void printUnknownDataTagError(QLatin1StringView name, QLatin1StringView tag, const QTestTable &lTable, const QTestTable &gTable)
Q_TESTLIB_EXPORT bool qCompare(const QLatin1StringView &t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT const char * currentGlobalDataTag()
Returns the name of the current global test data.
Q_TESTLIB_EXPORT void failOnWarning()
Q_DECL_COLD_FUNCTION Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *what, const char *file, int line)
Q_TESTLIB_EXPORT bool printAvailableFunctions
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const char *message)
Ignores messages created by qDebug(), qInfo() or qWarning().
Q_TESTLIB_EXPORT void failOnWarning(const char *message)
char * toString(std::chrono::duration< Rep, Period > duration)
Q_TESTLIB_EXPORT void qSkip(const char *message, const char *file, int line)
bool qCompare(QString const &t1, QLatin1StringView const &t2, const char *actual, const char *expected, const char *file, int line)
Definition qtest.h:32
static int qToInt(const char *str)
Q_TESTLIB_EXPORT char * toString(const QObject *)
char * toPrettyUnicode(QStringView string)
Q_DECL_COLD_FUNCTION Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT const char * currentDataTag()
Returns the name of the current test data.
Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg, const void *actualPtr, const void *expectedPtr, const char *(*actualFormatter)(const void *), const char *(*expectedFormatter)(const void *), const char *actual, const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg, const char *actual, const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT void qCleanup()
Q_TESTLIB_EXPORT void addColumnInternal(int id, const char *name)
static int eventDelay
static QString mainSourcePath
Q_DECL_COLD_FUNCTION Q_TESTLIB_EXPORT void qFail(const char *message, const char *file, int line)
Q_TESTLIB_EXPORT void * qGlobalData(const char *tagName, int typeId)
Q_TESTLIB_EXPORT bool qCompare(QStringView t1, const QLatin1StringView &t2, const char *actual, const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT const char * currentAppName()
Returns the name of the binary that is currently executed.
static char * toStringFp(T t)
static bool floatingCompare(const T &actual, const T &expected)
#define TO_STRING_IMPL(TYPE, FORMAT)
static bool installCoverageTool(const char *appname, const char *testname)
#define TO_STRING_FLOAT(TYPE)
static bool isValidSlot(const QMetaMethod &sl)
static void initEnvironment()
static auto decodeNanPayload(T t)
QTestDataSetter(QTestData *data)