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

source code of qtbase/src/testlib/qtestcase.cpp