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

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