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