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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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