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