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 +=
981 QString::fromLatin1(argv[i], colon);
982 QTest::testTags +=
983 QString::fromLatin1(argv[i] + colon + 1);
984 }
985 }
986 }
987
988 bool installedTestCoverage = installCoverageTool(QTestResult::currentAppName(), QTestResult::currentTestObjectName());
989 QTestLog::setInstalledTestCoverage(installedTestCoverage);
990
991 // If no loggers were created by the long version of the -o command-line
992 // option, but a logger was requested via the old-style option, add it.
993 const bool explicitLoggerRequested = logFormat != -1;
994 if (!QTestLog::hasLoggers() && explicitLoggerRequested)
995 QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename);
996
997 bool addFallbackLogger = !explicitLoggerRequested;
998
999#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
1000 // Any explicitly requested loggers will be added by now, so we can check if they use stdout
1001 const bool safeToAddAppleLogger = !AppleUnifiedLogger::preventsStderrLogging() || !QTestLog::loggerUsingStdout();
1002 if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
1003 QTestLog::addLogger(QTestLog::Apple, nullptr);
1004 if (AppleUnifiedLogger::preventsStderrLogging() && !logFilename)
1005 addFallbackLogger = false; // Prevent plain test logger fallback below
1006 }
1007#endif
1008
1009 if (addFallbackLogger)
1010 QTestLog::addLogger(QTestLog::Plain, logFilename);
1011
1012 if (repetitions != 1 && !QTestLog::isRepeatSupported()) {
1013 std::fprintf(stderr, "-repeat is only supported with plain text logger\n");
1014 exit(1);
1015 }
1016}
1017
1018// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
1019Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) {
1020 qtest_qParseArgs(argc, const_cast<const char *const *>(argv), qml);
1021}
1022
1023static QList<QBenchmarkResult> qMedian(const QList<QList<QBenchmarkResult>> &container)
1024{
1025 const int count = container.size();
1026 if (count == 0)
1027 return {};
1028
1029 if (count == 1)
1030 return container.front();
1031
1032 QList<QList<QBenchmarkResult>> containerCopy = container;
1033 std::sort(containerCopy.begin(), containerCopy.end(),
1034 [](const QList<QBenchmarkResult> &a, const QList<QBenchmarkResult> &b) {
1035 return a.first() < b.first();
1036 });
1037
1038 const int middle = count / 2;
1039
1040 // ### handle even-sized containers here by doing an arithmetic mean of the two middle items.
1041 return containerCopy.at(middle);
1042}
1043
1045{
1046 QTestDataSetter(QTestData *data)
1047 {
1048 QTestResult::setCurrentTestData(data);
1049 }
1051 {
1052 QTestResult::setCurrentTestData(nullptr);
1053 }
1054};
1055
1056void TestMethods::invokeTestOnData(int index) const
1057{
1058 /* Benchmarking: for each median iteration*/
1059
1060 bool isBenchmark = false;
1061 int i = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
1062
1063 QList<QList<QBenchmarkResult>> resultsList;
1064 bool minimumTotalReached = false;
1065 do {
1066 QBenchmarkTestMethodData::current->beginDataRun();
1067 if (i < 0)
1068 QBenchmarkTestMethodData::current->iterationCount = 1;
1069
1070 /* Benchmarking: for each accumulation iteration*/
1071 bool invokeOk;
1072 do {
1073 QTest::inTestFunction = true;
1074 invokeTestMethodIfValid(m_initMethod);
1075
1076 const bool initQuit =
1077 QTestResult::skipCurrentTest() || QTestResult::currentTestFailed();
1078 if (!initQuit) {
1079 QBenchmarkTestMethodData::current->results.clear();
1080 QBenchmarkTestMethodData::current->resultAccepted = false;
1081 QBenchmarkTestMethodData::current->valid = false;
1082
1083 QBenchmarkGlobalData::current->context.tag = QLatin1StringView(
1084 QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "");
1085
1086 invokeOk = invokeTestMethodIfValid(m_methods[index]);
1087 if (!invokeOk)
1088 QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__);
1089
1090 isBenchmark = QBenchmarkTestMethodData::current->isBenchmark();
1091 } else {
1092 invokeOk = false;
1093 }
1094
1095 QTest::inTestFunction = false;
1096 QTestResult::finishedCurrentTestData();
1097
1098 if (!initQuit) {
1099 invokeTestMethodIfValid(m_cleanupMethod);
1100
1101 // Process any deleteLater(), used by event-loop-based apps.
1102 // Fixes memleak reports.
1103 if (QCoreApplication::instance())
1104 QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
1105 }
1106 // If the test isn't a benchmark, finalize the result after
1107 // cleanup() has finished (or init has lead us to skip the test).
1108 if (!isBenchmark)
1109 QTestResult::finishedCurrentTestDataCleanup();
1110
1111 // If this test method has a benchmark, repeat until all measurements are
1112 // acceptable.
1113 // The QBENCHMARK macro increases the number of iterations for each run until
1114 // this happens.
1115 } while (invokeOk && isBenchmark
1116 && QBenchmarkTestMethodData::current->resultsAccepted() == false
1117 && !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed());
1118
1119 QBenchmarkTestMethodData::current->endDataRun();
1120 if (!QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed()) {
1121 if (i > -1) // iteration -1 is the warmup iteration.
1122 resultsList.append(QBenchmarkTestMethodData::current->results);
1123
1124 if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput &&
1125 !QBenchmarkTestMethodData::current->results.isEmpty()) {
1126 // we only print the first result
1127 const QBenchmarkResult &first = QBenchmarkTestMethodData::current->results.constFirst();
1128 QString pattern = i < 0 ? "warmup stage result : %1"_L1
1129 : "accumulation stage result: %1"_L1;
1130 QTestLog::info(qPrintable(pattern.arg(first.measurement.value)), nullptr, 0);
1131 }
1132 }
1133
1134 // Verify if the minimum total measurement (for the first measurement)
1135 // was reached, if it was specified:
1136 if (QBenchmarkGlobalData::current->minimumTotal == -1) {
1137 minimumTotalReached = true;
1138 } else {
1139 auto addResult = [](qreal current, const QList<QBenchmarkResult> &r) {
1140 if (!r.isEmpty())
1141 current += r.first().measurement.value;
1142 return current;
1143 };
1144 const qreal total = std::accumulate(resultsList.begin(), resultsList.end(), 0.0, addResult);
1145 minimumTotalReached = (total >= QBenchmarkGlobalData::current->minimumTotal);
1146 }
1147 } while (isBenchmark
1148 && ((++i < QBenchmarkGlobalData::current->adjustMedianIterationCount()) || !minimumTotalReached)
1149 && !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed());
1150
1151 // If the test is a benchmark, finalize the result after all iterations have finished.
1152 if (isBenchmark) {
1153 bool testPassed = !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed();
1154 QTestResult::finishedCurrentTestDataCleanup();
1155 // Only report benchmark figures if the test passed
1156 if (testPassed && QBenchmarkTestMethodData::current->resultsAccepted())
1157 QTestLog::addBenchmarkResults(qMedian(resultsList));
1158 }
1159}
1160
1161#if QT_CONFIG(thread)
1162
1163class WatchDog : public QThread
1164{
1165 enum Expectation : std::size_t {
1166 // bits 0..1: state
1170 ThreadEnd,
1171
1172 // bits 2..: generation
1173 };
1175 static_assert(size_t(ExpectationMask) == 0x3);
1176 static constexpr size_t GenerationShift = 2;
1177
1178 static constexpr Expectation state(Expectation e) noexcept
1179 { return Expectation{e & ExpectationMask}; }
1180 static constexpr size_t generation(Expectation e) noexcept
1181 { return e >> GenerationShift; }
1182 static constexpr Expectation combine(Expectation e, size_t gen) noexcept
1183 { return Expectation{e | (gen << GenerationShift)}; }
1184
1186 {
1187 auto expectationChanged = [this, e] { return expecting.load(std::memory_order_relaxed) != e; };
1188 switch (state(e)) {
1189 case TestFunctionEnd:
1191 case ThreadStart:
1192 case ThreadEnd:
1193 case TestFunctionStart:
1195 return true;
1196 }
1197 Q_UNREACHABLE_RETURN(false);
1198 }
1199
1201 {
1202 Q_ASSERT(generation(e) == 0); // no embedded generation allowed
1203 const auto locker = qt_scoped_lock(mutex);
1205 auto gen = generation(cur);
1206 if (e == TestFunctionStart)
1207 ++gen;
1208 e = combine(e, gen);
1211 }
1212
1213public:
1214 WatchDog()
1216 {
1217 setObjectName("QtTest Watchdog"_L1);
1218 auto locker = qt_unique_lock(mutex);
1219 start();
1221 }
1222
1223 ~WatchDog()
1224 {
1226 wait();
1227 }
1228
1229 void beginTest()
1230 {
1232 }
1233
1234 void testFinished()
1235 {
1237 }
1238
1239 void run() override
1240 {
1242 auto locker = qt_unique_lock(mutex);
1245 while (true) {
1247 switch (state(e)) {
1248 case ThreadEnd:
1249 return;
1250 case ThreadStart:
1251 Q_UNREACHABLE();
1252 case TestFunctionStart:
1253 case TestFunctionEnd:
1254 if (Q_UNLIKELY(!waitFor(locker, e))) {
1255 std::fflush(stderr);
1258 qFatal("Test function timed out");
1259 }
1260 }
1261 }
1262 }
1263
1264private:
1265 std::mutex mutex;
1268};
1269
1270#else // !QT_CONFIG(thread)
1271
1272class WatchDog : public QObject
1273{
1274public:
1275 void beginTest() {};
1276 void testFinished() {};
1277};
1278
1279#endif // QT_CONFIG(thread)
1280
1281template <typename Functor>
1282void runWithWatchdog(std::optional<WatchDog> &watchDog, Functor &&f)
1283{
1284 if (watchDog)
1285 watchDog->beginTest();
1286
1287 f();
1288
1289 if (watchDog)
1290 watchDog->testFinished();
1291}
1292
1293static void printUnknownDataTagError(QLatin1StringView name, QLatin1StringView tag,
1294 const QTestTable &lTable, const QTestTable &gTable)
1295{
1296 std::fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), tag.data());
1297 const int localDataCount = lTable.dataCount();
1298 if (localDataCount) {
1299 std::fputs("Available test-specific data tags:\n", stderr);
1300 for (int i = 0; i < localDataCount; ++i)
1301 std::fprintf(stderr, "\t%s\n", lTable.testData(i)->dataTag());
1302 }
1303 const int globalDataCount = gTable.dataCount();
1304 if (globalDataCount) {
1305 std::fputs("Available global data tags:\n", stderr);
1306 for (int i = 0; i < globalDataCount; ++i)
1307 std::fprintf(stderr, "\t%s\n", gTable.testData(i)->dataTag());
1308 }
1309 if (localDataCount == 0 && globalDataCount == 0)
1310 std::fputs("Function has no data tags\n", stderr);
1311}
1312
1313/*!
1314 \internal
1315
1316 Call slot_data(), init(), slot(), cleanup(), init(), slot(), cleanup(), ...
1317 If data is set then it is the only test that is performed
1318*/
1319void TestMethods::invokeTest(int index, QLatin1StringView tag, std::optional<WatchDog> &watchDog) const
1320{
1321 QBenchmarkTestMethodData benchmarkData;
1322 QBenchmarkTestMethodData::current = &benchmarkData;
1323
1324 const QByteArray &name = m_methods[index].name();
1325 QBenchmarkGlobalData::current->context.slotName = QLatin1StringView(name) + "()"_L1;
1326
1327 char member[512];
1328 QTestTable table;
1329
1330 QTestResult::setCurrentTestFunction(name.constData());
1331
1332 const QTestTable *gTable = QTestTable::globalTestTable();
1333 const int globalDataCount = gTable->dataCount();
1334 int curGlobalDataIndex = 0;
1335 const auto globalDataTag = [gTable, globalDataCount](int index) {
1336 return globalDataCount ? gTable->testData(index)->dataTag() : nullptr;
1337 };
1338
1339 const auto dataTagMatches = [](QLatin1StringView tag, QLatin1StringView local,
1340 QLatin1StringView global) {
1341 if (tag.isEmpty()) // No tag specified => run all data sets for this function
1342 return true;
1343 if (tag == local || tag == global) // Equal to either => run it
1344 return true;
1345 // Also allow global:local as a match:
1346 return tag.startsWith(global) && tag.endsWith(local) &&
1347 tag.size() == global.size() + 1 + local.size() &&
1348 tag[global.size()] == ':';
1349 };
1350 bool foundFunction = false;
1351 bool blacklisted = false;
1352
1353 /* For each entry in the global data table, do: */
1354 do {
1355 if (!gTable->isEmpty())
1356 QTestResult::setCurrentGlobalTestData(gTable->testData(curGlobalDataIndex));
1357
1358 if (curGlobalDataIndex == 0) {
1359 std::snprintf(member, 512, "%s_data()", name.constData());
1360 runWithWatchdog(watchDog, [&member] {
1362 });
1363 if (QTestResult::skipCurrentTest())
1364 break;
1365 }
1366
1367 int curDataIndex = 0;
1368 const int dataCount = table.dataCount();
1369 const auto dataTag = [&table, dataCount](int index) {
1370 return dataCount ? table.testData(index)->dataTag() : nullptr;
1371 };
1372
1373 /* For each entry in this test's data table, do: */
1374 do {
1375 QTestResult::setSkipCurrentTest(false);
1376 QTestResult::setBlacklistCurrentTest(false);
1377 if (dataTagMatches(tag, QLatin1StringView(dataTag(curDataIndex)),
1378 QLatin1StringView(globalDataTag(curGlobalDataIndex)))) {
1379 foundFunction = true;
1380 blacklisted = QTestPrivate::checkBlackLists(name.constData(), dataTag(curDataIndex),
1381 globalDataTag(curGlobalDataIndex));
1382 if (blacklisted)
1383 QTestResult::setBlacklistCurrentTest(true);
1384
1385 if (blacklisted && skipBlacklisted) {
1386 QTest::qSkip("Skipping blacklisted test since -skipblacklisted option is set.",
1387 NULL, 0);
1388 QTestResult::finishedCurrentTestData();
1389 QTestResult::finishedCurrentTestDataCleanup();
1390 } else {
1392 curDataIndex >= dataCount ? nullptr : table.testData(curDataIndex));
1393
1394 QTestPrivate::qtestMouseButtons = Qt::NoButton;
1395
1396 // Maintain at least 500ms mouse event timestamps between each test function
1397 // call
1399
1400 runWithWatchdog(watchDog, [this, index] {
1401 invokeTestOnData(index);
1402 });
1403 }
1404
1405 if (!tag.isEmpty() && !globalDataCount)
1406 break;
1407 }
1408 ++curDataIndex;
1409 } while (curDataIndex < dataCount);
1410
1411 QTestResult::setCurrentGlobalTestData(nullptr);
1412 ++curGlobalDataIndex;
1413 } while (curGlobalDataIndex < globalDataCount);
1414
1415 if (!tag.isEmpty() && !foundFunction) {
1416 printUnknownDataTagError(QLatin1StringView(name), tag, table, *gTable);
1417 QTestResult::addFailure(qPrintable("Data tag not found: %1"_L1.arg(tag)));
1418 }
1419 QTestResult::finishedCurrentTestFunction();
1420 QTestResult::setSkipCurrentTest(false);
1421 QTestResult::setBlacklistCurrentTest(false);
1422}
1423
1424void *fetchData(QTestData *data, const char *tagName, int typeId)
1425{
1426 QTEST_ASSERT(typeId);
1427 QTEST_ASSERT_X(data, "QTest::fetchData()", "Test data requested, but no testdata available.");
1428 QTEST_ASSERT(data->parent());
1429
1430 int idx = data->parent()->indexOf(tagName);
1431
1432 if (Q_UNLIKELY(idx == -1 || idx >= data->dataCount())) {
1433 qFatal("QFETCH: Requested testdata '%s' not available, check your _data function.",
1434 tagName);
1435 }
1436
1437 if (Q_UNLIKELY(typeId != data->parent()->elementTypeId(idx))) {
1438 qFatal("Requested type '%s' does not match available type '%s'.",
1439 QMetaType(typeId).name(),
1440 QMetaType(data->parent()->elementTypeId(idx)).name());
1441 }
1442
1443 return data->data(idx);
1444}
1445
1446/*!
1447 * \internal
1448*/
1449char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...)
1450{
1451 va_list ap;
1452 va_start(ap, numArguments);
1453
1454 QByteArray arguments;
1455 arguments += prefix;
1456
1457 if (numArguments > 0) {
1458 arguments += va_arg(ap, const char *);
1459
1460 for (size_t i = 1; i < numArguments; ++i) {
1461 arguments += ", ";
1462 arguments += va_arg(ap, const char *);
1463 }
1464 }
1465
1466 va_end(ap);
1467 arguments += suffix;
1468 return qstrdup(arguments.constData());
1469}
1470
1471/*!
1472 Returns a pointer to a string that is the string \a ba represented
1473 as a space-separated sequence of hex characters. If the input is
1474 considered too long, it is truncated. A trucation is indicated in
1475 the returned string as an ellipsis at the end. The caller has
1476 ownership of the returned pointer and must ensure it is later passed
1477 to operator delete[].
1478
1479 \a length is the length of the string \a ba.
1480*/
1481char *toHexRepresentation(const char *ba, qsizetype length)
1482{
1483 if (length == 0)
1484 return qstrdup("");
1485
1486 /* We output at maximum about maxLen characters in order to avoid
1487 * running out of memory and flooding things when the byte array
1488 * is large.
1489 *
1490 * maxLen can't be for example 200 because Qt Test is sprinkled with fixed
1491 * size char arrays.
1492 * */
1493 const qsizetype maxLen = 50;
1494 const qsizetype len = qMin(maxLen, length);
1495 char *result = nullptr;
1496
1497 if (length > maxLen) {
1498 const qsizetype size = len * 3 + 4;
1499 result = new char[size];
1500
1501 char *const forElipsis = result + size - 5;
1502 forElipsis[0] = ' ';
1503 forElipsis[1] = '.';
1504 forElipsis[2] = '.';
1505 forElipsis[3] = '.';
1506 result[size - 1] = '\0';
1507 }
1508 else {
1509 const qsizetype size = len * 3;
1510 result = new char[size];
1511 result[size - 1] = '\0';
1512 }
1513
1514 qsizetype i = 0;
1515 qsizetype o = 0;
1516
1517 while (true) {
1518 const char at = ba[i];
1519
1520 result[o] = toHexUpper(at >> 4);
1521 ++o;
1522 result[o] = toHexUpper(at);
1523
1524 ++i;
1525 ++o;
1526 if (i == len)
1527 break;
1528 result[o] = ' ';
1529 ++o;
1530 }
1531
1532 return result;
1533}
1534
1535/*!
1536 \internal
1537 Returns the same QByteArray but with only the ASCII characters still shown;
1538 everything else is replaced with \c {\xHH}.
1539*/
1540char *toPrettyCString(const char *p, qsizetype length)
1541{
1542 bool trimmed = false;
1543 auto buffer = std::make_unique<char[]>(256);
1544 const char *end = p + length;
1545 char *dst = buffer.get();
1546
1547 bool lastWasHexEscape = false;
1548 *dst++ = '"';
1549 for ( ; p != end; ++p) {
1550 // we can add:
1551 // 1 byte: a single character
1552 // 2 bytes: a simple escape sequence (\n)
1553 // 3 bytes: "" and a character
1554 // 4 bytes: an hex escape sequence (\xHH)
1555 if (dst - buffer.get() > 246) {
1556 // plus the quote, the three dots and NUL, it's 255 in the worst case
1557 trimmed = true;
1558 break;
1559 }
1560
1561 // check if we need to insert "" to break an hex escape sequence
1562 if (Q_UNLIKELY(lastWasHexEscape)) {
1563 if (fromHex(*p) != -1) {
1564 // yes, insert it
1565 *dst++ = '"';
1566 *dst++ = '"';
1567 }
1568 lastWasHexEscape = false;
1569 }
1570
1571 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1572 *dst++ = *p;
1573 continue;
1574 }
1575
1576 // write as an escape sequence
1577 // this means we may advance dst to buffer.data() + 247 or 250
1578 *dst++ = '\\';
1579 switch (*p) {
1580 case 0x5c:
1581 case 0x22:
1582 *dst++ = uchar(*p);
1583 break;
1584 case 0x8:
1585 *dst++ = 'b';
1586 break;
1587 case 0xc:
1588 *dst++ = 'f';
1589 break;
1590 case 0xa:
1591 *dst++ = 'n';
1592 break;
1593 case 0xd:
1594 *dst++ = 'r';
1595 break;
1596 case 0x9:
1597 *dst++ = 't';
1598 break;
1599 default:
1600 // print as hex escape
1601 *dst++ = 'x';
1602 *dst++ = toHexUpper(uchar(*p) >> 4);
1603 *dst++ = toHexUpper(uchar(*p));
1604 lastWasHexEscape = true;
1605 break;
1606 }
1607 }
1608
1609 *dst++ = '"';
1610 if (trimmed) {
1611 *dst++ = '.';
1612 *dst++ = '.';
1613 *dst++ = '.';
1614 }
1615 *dst++ = '\0';
1616 return buffer.release();
1617}
1618
1619/*!
1620 \fn char *toPrettyUnicode(QStringView string)
1621 \internal
1622 Returns the same QString but with only the ASCII characters still shown;
1623 everything else is replaced with \c {\uXXXX}.
1624
1625 Similar to QDebug::putString().
1626*/
1627
1629// escape sequence, closing quote, the three dots and NUL
1630constexpr qsizetype PrettyUnicodeMaxIncrement = sizeof(R"(\uXXXX"...)"); // includes NUL
1631
1632static char *writePrettyUnicodeChar(char16_t ch, char * const buffer)
1633{
1634 auto dst = buffer;
1635 auto first = [&](int n) { Q_ASSERT(dst - buffer == n); return dst; };
1636 if (ch < 0x7f && ch >= 0x20 && ch != '\\' && ch != '"') {
1637 *dst++ = ch;
1638 return first(1);
1639 }
1640
1641 // write as an escape sequence
1642 *dst++ = '\\';
1643 switch (ch) {
1644 case 0x22:
1645 case 0x5c:
1646 *dst++ = uchar(ch);
1647 break;
1648 case 0x8:
1649 *dst++ = 'b';
1650 break;
1651 case 0xc:
1652 *dst++ = 'f';
1653 break;
1654 case 0xa:
1655 *dst++ = 'n';
1656 break;
1657 case 0xd:
1658 *dst++ = 'r';
1659 break;
1660 case 0x9:
1661 *dst++ = 't';
1662 break;
1663 default:
1664 *dst++ = 'u';
1665 *dst++ = toHexUpper(ch >> 12);
1666 *dst++ = toHexUpper(ch >> 8);
1667 *dst++ = toHexUpper(ch >> 4);
1668 *dst++ = toHexUpper(ch);
1669 return first(6);
1670 }
1671 return first(2);
1672}
1673
1674char *toPrettyUnicode(QStringView string)
1675{
1676 auto p = string.utf16();
1677 auto length = string.size();
1678 // keep it simple for the vast majority of cases
1679 bool trimmed = false;
1680 auto buffer = std::make_unique<char[]>(PrettyUnicodeMaxOutputSize);
1681 const auto end = p + length;
1682 char *dst = buffer.get();
1683
1684 *dst++ = '"';
1685 for ( ; p != end; ++p) {
1686 if (dst - buffer.get() > PrettyUnicodeMaxOutputSize - PrettyUnicodeMaxIncrement) {
1687 trimmed = true;
1688 break;
1689 }
1690 dst = writePrettyUnicodeChar(*p, dst);
1691 }
1692
1693 *dst++ = '"';
1694 if (trimmed) {
1695 *dst++ = '.';
1696 *dst++ = '.';
1697 *dst++ = '.';
1698 }
1699 *dst++ = '\0';
1700 return buffer.release();
1701}
1702
1703void TestMethods::invokeTests(QObject *testObject) const
1704{
1705 const QMetaObject *metaObject = testObject->metaObject();
1706 QTEST_ASSERT(metaObject);
1707
1708 std::optional<WatchDog> watchDog = std::nullopt;
1709 if (!CrashHandler::alreadyDebugging()
1710#if QT_CONFIG(valgrind)
1711 && QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindChildProcess
1712#endif
1713 ) {
1714 watchDog.emplace();
1715 }
1716
1717 QTestResult::setCurrentTestFunction("initTestCase");
1718 runWithWatchdog(watchDog, [this, testObject] {
1719 invokeTestMethodIfValid(m_initTestCaseDataMethod, testObject);
1720 });
1721
1722 QSignalDumper::startDump();
1723
1724 if (!QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed()) {
1725
1726 runWithWatchdog(watchDog, [this, testObject] {
1727 invokeTestMethodIfValid(m_initTestCaseMethod, testObject);
1728 });
1729
1730 // finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy.
1731 const bool previousFailed = QTestResult::currentTestFailed();
1732 QTestResult::finishedCurrentTestData();
1733 QTestResult::finishedCurrentTestDataCleanup();
1734 QTestResult::finishedCurrentTestFunction();
1735
1736 if (!QTestResult::skipCurrentTest() && !previousFailed) {
1737 for (int i = 0, count = int(m_methods.size()); i < count; ++i) {
1738 const char *data = nullptr;
1739 if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
1740 data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
1741 invokeTest(i, QLatin1StringView(data), watchDog);
1742 delete [] data;
1743 }
1744 }
1745
1746 const bool wasSkipped = QTestResult::skipCurrentTest();
1747 QTestResult::setSkipCurrentTest(false);
1748 QTestResult::setBlacklistCurrentTest(false);
1749 QTestResult::setCurrentTestFunction("cleanupTestCase");
1750 runWithWatchdog(watchDog, [this, testObject] {
1751 invokeTestMethodIfValid(m_cleanupTestCaseMethod, testObject);
1752 });
1753
1754 QTestResult::finishedCurrentTestData();
1755 // Restore skip state as it affects decision on whether we passed:
1756 QTestResult::setSkipCurrentTest(wasSkipped || QTestResult::skipCurrentTest());
1757 QTestResult::finishedCurrentTestDataCleanup();
1758 }
1759 QTestResult::finishedCurrentTestFunction();
1760 QTestResult::setCurrentTestFunction(nullptr);
1761
1762 QSignalDumper::endDump();
1763}
1764
1765#if QT_DEPRECATED_SINCE(6, 8)
1766static const char *functionRefFormatter(const void *f)
1767{
1768 auto formatter = static_cast<const qxp::function_ref<const char *()> *>(f);
1769 return (*formatter)();
1770};
1771
1772bool reportResult(bool success, qxp::function_ref<const char *()> lhs,
1773 qxp::function_ref<const char *()> rhs,
1774 const char *lhsExpr, const char *rhsExpr,
1775 ComparisonOperation op, const char *file, int line)
1776{
1779 lhsExpr, rhsExpr, op, file, line);
1780}
1781#endif // QT_DEPRECATED_SINCE(6, 8)
1782
1783bool reportResult(bool success, const void *lhs, const void *rhs,
1784 const char *(*lhsFormatter)(const void*),
1785 const char *(*rhsFormatter)(const void*),
1786 const char *lhsExpr, const char *rhsExpr,
1787 ComparisonOperation op, const char *file, int line)
1788{
1789 return QTestResult::reportResult(success, lhs, rhs, lhsFormatter, rhsFormatter,
1790 lhsExpr, rhsExpr, op, file, line);
1791}
1792} // namespace QTest
1793
1794static void initEnvironment()
1795{
1796 qputenv("QT_QTESTLIB_RUNNING", "1");
1797}
1798
1799#ifdef Q_OS_ANDROID
1800static QFile androidExitCodeFile()
1801{
1802 const QString testHome = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
1803 return QFile(testHome + "/qtest_last_exit_code"_L1);
1804}
1805#endif
1806
1807/*!
1808 Executes tests declared in \a testObject. In addition, the private slots
1809 \c{initTestCase()}, \c{cleanupTestCase()}, \c{init()} and \c{cleanup()}
1810 are executed if they exist. See \l{Creating a Test} for more details.
1811
1812 Optionally, the command line arguments \a argc and \a argv can be provided.
1813 For a list of recognized arguments, read \l {Qt Test Command Line Arguments}.
1814
1815 The following example will run all tests in \c MyTestObject:
1816
1817 \snippet code/src_qtestlib_qtestcase.cpp 18
1818
1819 This function returns 0 if no tests failed, or a value other than 0 if one
1820 or more tests failed or in case of unhandled exceptions. (Skipped tests do
1821 not influence the return value.)
1822
1823 For stand-alone test applications, the convenience macro \l QTEST_MAIN() can
1824 be used to declare a main() function that parses the command line arguments
1825 and executes the tests, avoiding the need to call this function explicitly.
1826
1827 The return value from this function is also the exit code of the test
1828 application when the \l QTEST_MAIN() macro is used.
1829
1830 For stand-alone test applications, this function should not be called more
1831 than once, as command-line options for logging test output to files and
1832 executing individual test functions will not behave correctly.
1833
1834 \note This function is not reentrant, only one test can run at a time. A
1835 test that was executed with qExec() can't run another test via qExec() and
1836 threads are not allowed to call qExec() simultaneously.
1837
1838 If you have programmatically created the arguments, as opposed to getting them
1839 from the arguments in \c main(), it is likely of interest to use
1840 QTest::qExec(QObject *, const QStringList &) since it is Unicode safe.
1841
1842 \sa QTEST_MAIN(), QTEST_GUILESS_MAIN(), QTEST_APPLESS_MAIN()
1843*/
1844
1845int QTest::qExec(QObject *testObject, int argc, char **argv)
1846{
1847 // NB: QtQuick's testing recombines qInit(), qRun() and qCleanup() to
1848 // provide a replacement for qExec() that calls qRun() once for each
1849 // built-in style. So think twice about moving parts between these three
1850 // functions, as doing so may mess up QtQuick's testing.
1851 qInit(testObject, argc, argv);
1852 int ret = qRun();
1853 qCleanup();
1854
1855#if defined(Q_OS_WASM)
1856 EM_ASM({
1857 if (typeof Module != "undefined" && typeof Module.notifyTestFinished != "undefined")
1858 Module.notifyTestFinished($0);
1859 }, ret);
1860#endif // Q_OS_WASM
1861
1862 return ret;
1863}
1864
1865/*! \internal
1866*/
1867void QTest::qInit(QObject *testObject, int argc, char **argv)
1868{
1870 CrashHandler::maybeDisableCoreDump();
1871 QBenchmarkGlobalData::current = new QBenchmarkGlobalData;
1872
1873#if defined(Q_OS_MACOS)
1874 // Don't restore saved window state for auto tests
1875 QTestPrivate::disableWindowRestore();
1876
1877 // Disable App Nap which may cause tests to stall
1878 if (!appNapDisabler)
1879 appNapDisabler.emplace();
1880
1881 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1882 kIOPMAssertionLevelOn, CFSTR("QtTest running tests"),
1883 &macPowerSavingDisabled);
1884#endif
1885
1886 QTestPrivate::parseBlackList();
1887 QTestResult::reset();
1888
1889 QTEST_ASSERT(testObject);
1890 QTEST_ASSERT(!currentTestObject);
1891 currentTestObject = testObject;
1892
1893 const QMetaObject *metaObject = testObject->metaObject();
1894 QTEST_ASSERT(metaObject);
1895
1896 QTestResult::setCurrentTestObject(metaObject->className());
1897 if (argc > 0)
1898 QTestResult::setCurrentAppName(argv[0]);
1899
1900 qtest_qParseArgs(argc, argv, false);
1901
1902#if QT_CONFIG(valgrind)
1903 if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess)
1904#endif
1905 QTestLog::startLogging();
1906
1907#ifdef Q_OS_ANDROID
1908 androidExitCodeFile().remove();
1909#endif
1910}
1911
1912/*! \internal
1913*/
1915{
1916 QTEST_ASSERT(currentTestObject);
1917
1918#if QT_CONFIG(valgrind)
1919 int callgrindChildExitCode = 0;
1920#endif
1921
1922#ifndef QT_NO_EXCEPTIONS
1923 try {
1924#endif
1925
1926#if QT_CONFIG(valgrind)
1927 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) {
1928 if (Q_UNLIKELY(!qApp))
1929 qFatal("QtTest: -callgrind option is not available with QTEST_APPLESS_MAIN");
1930
1931 const QStringList origAppArgs(QCoreApplication::arguments());
1932 if (!QBenchmarkValgrindUtils::rerunThroughCallgrind(origAppArgs, callgrindChildExitCode))
1933 return -1;
1934
1935 QBenchmarkValgrindUtils::cleanup();
1936
1937 } else
1938#endif
1939 {
1940 std::optional<CrashHandler::FatalSignalHandler> handler;
1941 CrashHandler::prepareStackTrace();
1943 handler.emplace();
1944
1945 bool seenBad = false;
1946 TestMethods::MetaMethods commandLineMethods;
1947 commandLineMethods.reserve(static_cast<size_t>(QTest::testFunctions.size()));
1948 for (const QString &tf : std::as_const(QTest::testFunctions)) {
1949 const QByteArray tfB = tf.toLatin1();
1950 const QByteArray signature = tfB + QByteArrayLiteral("()");
1951 QMetaMethod m = TestMethods::findMethod(currentTestObject, signature.constData());
1952 if (m.isValid() && isValidSlot(m)) {
1953 commandLineMethods.push_back(m);
1954 } else {
1955 std::fprintf(stderr, "Unknown test function: '%s'.", tfB.constData());
1956 if (!qPrintTestSlots(stderr, tfB.constData(), " Possible matches:\n"))
1957 std::fputc('\n', stderr);
1958 QTestResult::setCurrentTestFunction(tfB.constData());
1959 QTestResult::addFailure(qPrintable("Function not found: %1"_L1.arg(tf)));
1960 QTestResult::finishedCurrentTestFunction();
1961 // Ditch the tag that came with tf as test function:
1962 QTest::testTags.remove(commandLineMethods.size());
1963 seenBad = true;
1964 }
1965 }
1966 if (seenBad) {
1967 // Provide relevant help to do better next time:
1968 std::fprintf(stderr, "\n%s -functions\nlists all available test functions.\n\n",
1969 QTestResult::currentAppName());
1970 if (commandLineMethods.empty()) // All requested functions missing.
1971 return 1;
1972 }
1973 TestMethods test(currentTestObject, std::move(commandLineMethods));
1974
1975 int remainingRepetitions = repetitions;
1976 const bool repeatForever = repetitions < 0;
1977 while (QTestLog::failCount() == 0 && (repeatForever || remainingRepetitions-- > 0)) {
1978 QTestTable::globalTestTable();
1979 test.invokeTests(currentTestObject);
1980 QTestTable::clearGlobalTestTable();
1981 }
1982 }
1983
1984#ifndef QT_NO_EXCEPTIONS
1985 } catch (...) {
1986 QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__);
1987 if (QTestResult::currentTestFunction()) {
1988 QTestResult::finishedCurrentTestFunction();
1989 QTestResult::setCurrentTestFunction(nullptr);
1990 }
1991
1992 qCleanup();
1993
1994 // Re-throw exception to make debugging easier
1995 throw;
1996 return 1;
1997 }
1998#endif
1999
2000#if QT_CONFIG(valgrind)
2001 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess)
2002 return callgrindChildExitCode;
2003#endif
2004 // make sure our exit code is never going above 127
2005 // since that could wrap and indicate 0 test fails
2006 const int exitCode = qMin(QTestLog::failCount(), 127);
2007
2008#ifdef Q_OS_ANDROID
2009 QFile exitCodeFile = androidExitCodeFile();
2010 if (exitCodeFile.open(QIODevice::WriteOnly)) {
2011 exitCodeFile.write(qPrintable(QString::number(exitCode)));
2012 } else {
2013 qWarning("Failed to open %s for writing test exit code: %s",
2014 qPrintable(exitCodeFile.fileName()), qPrintable(exitCodeFile.errorString()));
2015 }
2016#endif
2017
2018 return exitCode;
2019}
2020
2021/*! \internal
2022*/
2024{
2025 currentTestObject = nullptr;
2026
2027#if QT_CONFIG(valgrind)
2028 if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess)
2029#endif
2030 QTestLog::stopLogging();
2031
2032 delete QBenchmarkGlobalData::current;
2033 QBenchmarkGlobalData::current = nullptr;
2034
2035#if defined(Q_OS_MACOS)
2036 IOPMAssertionRelease(macPowerSavingDisabled);
2037 appNapDisabler = std::nullopt;
2038#endif
2039}
2040
2041#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
2042/*!
2043 Registers the test \a name, with entry function \a entryFunction, in a
2044 central test case registry for the current binary.
2045
2046 The \a name will be listed when running the batch test binary with no
2047 parameters. Running the test binary with the argv[1] of \a name will result
2048 in \a entryFunction being called.
2049
2050 \since 6.5
2051*/
2052void QTest::qRegisterTestCase(const QString &name, TestEntryFunction entryFunction)
2053{
2054 QTest::TestRegistry::instance()->registerTest(name, entryFunction);
2055}
2056
2057QList<QString> QTest::qGetTestCaseNames()
2058{
2059 return QTest::TestRegistry::instance()->getAllTestNames();
2060}
2061
2062QTest::TestEntryFunction QTest::qGetTestCaseEntryFunction(const QString& name)
2063{
2064 return QTest::TestRegistry::instance()->getTestEntryFunction(name);
2065}
2066
2067#endif // QT_CONFIG(batch_test_support)
2068
2069/*!
2070 \overload
2071 \since 4.4
2072
2073 Behaves identically to qExec(QObject *, int, char**) but takes a
2074 QStringList of \a arguments instead of a \c char** list.
2075*/
2076int QTest::qExec(QObject *testObject, const QStringList &arguments)
2077{
2078 const int argc = arguments.size();
2079 QVarLengthArray<char *> argv(argc);
2080
2081 QList<QByteArray> args;
2082 args.reserve(argc);
2083
2084 for (int i = 0; i < argc; ++i)
2085 {
2086 args.append(arguments.at(i).toLocal8Bit().constData());
2087 argv[i] = args.last().data();
2088 }
2089
2090 return qExec(testObject, argc, argv.data());
2091}
2092
2093/*! \internal
2094*/
2095void QTest::qFail(const char *message, const char *file, int line)
2096{
2097 QTestResult::fail(message, file, line);
2098}
2099
2100/*! \internal
2101*/
2102bool QTest::qVerify(bool statement, const char *statementStr, const char *description,
2103 const char *file, int line)
2104{
2105 return QTestResult::verify(statement, statementStr, description, file, line);
2106}
2107
2108/*! \fn void QTest::qSkip(const char *message, const char *file, int line)
2109 \internal
2110*/
2111void QTest::qSkip(const char *message, const char *file, int line)
2112{
2113 QTestResult::addSkip(message, file, line);
2114 QTestResult::setSkipCurrentTest(true);
2115}
2116
2117/*! \fn bool QTest::qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
2118 \internal
2119*/
2120bool QTest::qExpectFail(const char *dataIndex, const char *comment,
2121 QTest::TestFailMode mode, const char *file, int line)
2122{
2123 return QTestResult::expectFail(dataIndex, qstrdup(comment), mode, file, line);
2124}
2125
2126/*!
2127 \internal
2128
2129 Executes qFail() following a failed QVERIFY_THROWS_EXCEPTION or
2130 QVERIFY_THROWS_NO_EXCEPTION, passing a suitable message created from \a expected,
2131 \a what, along with \a file and \a line.
2132
2133 The \a expected parameter contains the type of the exception that is expected to
2134 be thrown, or \nullptr, if no exception was expected.
2135
2136 The \a what parameter contains the result of \c{std::exception::what()}, or nullptr,
2137 if a non-\c{std::exception}-derived exception was caught.
2138
2139 The \a file and \a line parameters hold expansions of the \c{__FILE__} and \c{__LINE__}
2140 macros, respectively.
2141*/
2142void QTest::qCaught(const char *expected, const char *what, const char *file, int line)
2143{
2144 auto message = [&] {
2145 const auto exType = what ? "std::" : "unknown ";
2146 const auto ofType = expected ? " of type " : "";
2147 const auto no = expected ? "an" : "no";
2148 const auto withMsg = what ? " with message " : "";
2149 const auto protect = [](const char *s) { return s ? s : ""; };
2150
2151 return QString::asprintf("Expected %s exception%s%s to be thrown, "
2152 "but caught %sexception%s%s",
2153 no, ofType, protect(expected),
2154 exType, withMsg, protect(what));
2155 };
2156 qFail(message().toUtf8().constData(), file, line);
2157}
2158
2159/*!
2160 \internal
2161
2162 Contains the implementation of the catch(...) block of
2163 QVERIFY_THROWS_EXCEPTION.
2164
2165 The function inspects std::current_exception() by rethrowing it using
2166 std::rethrow_exception().
2167
2168 The function must be called from a catch handler.
2169
2170 If the exception inherits std::exception, its what() message is logged and
2171 this function returns normally. The caller of this function must then
2172 execute a \c{QTEST_FAIL_ACTION} to exit from the test function.
2173
2174 Otherwise, a message saying an unknown exception was caught is logged and
2175 this function rethrows the exception, skipping the \c{QTEST_FAIL_ACTION}
2176 that follows this function call in the caller.
2177*/
2178void QTest::qCaught(const char *expected, const char *file, int line)
2179{
2180 try {
2181 // let's see what the cat brought us:
2182 std::rethrow_exception(std::current_exception());
2183 } catch (const std::exception &e) {
2184 qCaught(expected, e.what(), file, line);
2185 } catch (...) {
2186 qCaught(expected, nullptr, file, line);
2187 throw;
2188 }
2189 // caller shall invoke `QTEST_FAIL_ACTION` if control reached here
2190}
2191
2192
2193#if QT_DEPRECATED_SINCE(6, 3)
2194/*!
2195 \internal
2196 \deprecated [6.3] Use qWarning() instead
2197*/
2198void QTest::qWarn(const char *message, const char *file, int line)
2199{
2200 QTestLog::warn(message, file, line);
2201}
2202#endif
2203
2204/*!
2205 Ignores messages created by qDebug(), qInfo() or qWarning(). If the \a message
2206 with the corresponding \a type is outputted, it will be removed from the
2207 test log. If the test finished and the \a message was not outputted,
2208 a test failure is appended to the test log.
2209
2210 \note Invoking this function will only ignore one message. If the message
2211 you want to ignore is output twice, you have to call ignoreMessage() twice,
2212 too.
2213
2214 Example:
2215 \snippet code/src_qtestlib_qtestcase.cpp 19
2216
2217 The example above tests that QDir::mkdir() outputs the right warning when invoked
2218 with an invalid file name.
2219
2220 \note \a message will be interpreted as UTF-8.
2221*/
2222void QTest::ignoreMessage(QtMsgType type, const char *message)
2223{
2224 QTestLog::ignoreMessage(type, message);
2225}
2226
2227#if QT_CONFIG(regularexpression)
2228/*!
2229 \overload
2230
2231 Ignores messages created by qDebug(), qInfo() or qWarning(). If the message
2232 matching \a messagePattern
2233 with the corresponding \a type is outputted, it will be removed from the
2234 test log. If the test finished and the message was not outputted,
2235 a test failure is appended to the test log.
2236
2237 \note Invoking this function will only ignore one message. If the message
2238 you want to ignore is output twice, you have to call ignoreMessage() twice,
2239 too.
2240
2241 \since 5.3
2242*/
2243void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
2244{
2245 QTestLog::ignoreMessage(type, messagePattern);
2246}
2247#endif // QT_CONFIG(regularexpression)
2248
2249/*!
2250 \since 6.8
2251 \overload failOnWarning()
2253 Appends a test failure to the test log if any warning is output.
2254
2255 \sa failOnWarning(const char *)
2256*/
2258{
2259 return QTestLog::failOnWarning();
2260}
2261
2262/*!
2263 \since 6.3
2264 \overload failOnWarning()
2266 Appends a test failure to the test log if the \a message is output.
2267
2268 \sa failOnWarning()
2269*/
2270void QTest::failOnWarning(const char *message)
2271{
2272 return QTestLog::failOnWarning(message);
2273}
2274
2275#if QT_CONFIG(regularexpression)
2276/*!
2277 \since 6.3
2278
2279 Appends a test failure to the test log for each warning that matches
2280 \a messagePattern.
2281
2282 The test function will continue execution when a failure is added. To abort
2283 the test instead, you can check \l currentTestFailed() and return early if
2284 it's \c true.
2285
2286 For each warning, the first pattern that matches will cause a failure,
2287 and the remaining patterns will be ignored.
2288
2289 All patterns are cleared at the end of each test function.
2290
2291 \code
2292 void FileTest::loadFiles()
2293 {
2294 QTest::failOnWarning(QRegularExpression("^Failed to load"));
2295
2296 // Each of these will cause a test failure:
2297 qWarning() << "Failed to load image";
2298 qWarning() << "Failed to load video";
2299 }
2300 \endcode
2301
2302 To fail every test that triggers a given warning, pass a suitable regular
2303 expression to this function in \l {Creating a Test}{init()}:
2304
2305 \code
2306 void FileTest::init()
2307 {
2308 QTest::failOnWarning(
2309 QRegularExpression("QFile::.*: File(.*) already open"));
2310 }
2311 \endcode
2312
2313 For the common case of failing on \e any warning pass no parameter:
2314
2315 \code
2316 void FileTest::init()
2317 {
2318 QTest::failOnWarning();
2319 }
2320 \endcode
2321
2322 \note \l ignoreMessage() takes precedence over this function, so any
2323 warnings that match a pattern given to both \c ignoreMessage() and
2324 \c failOnWarning() will be ignored.
2325
2326 \sa {Qt Test Environment Variables}{QTEST_FATAL_FAIL}
2327*/
2328void QTest::failOnWarning(const QRegularExpression &messagePattern)
2329{
2330 QTestLog::failOnWarning(messagePattern);
2331}
2332#endif // QT_CONFIG(regularexpression)
2333
2334/*! \internal
2335*/
2336
2337#ifdef Q_OS_WIN
2338static inline bool isWindowsBuildDirectory(const QString &dirName)
2339{
2340 return dirName.compare("Debug"_L1, Qt::CaseInsensitive) == 0
2341 || dirName.compare("Release"_L1, Qt::CaseInsensitive) == 0;
2342}
2343#endif
2344
2345#if QT_CONFIG(temporaryfile)
2346/*!
2347 Extracts a directory from resources to disk. The content is extracted
2348 recursively to a temporary folder. The extracted content is removed
2349 automatically once the last reference to the return value goes out of scope.
2350
2351 \a dirName is the name of the directory to extract from resources.
2352
2353 Returns the temporary directory where the data was extracted or null in case of
2354 errors.
2355*/
2356QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
2357{
2358 QSharedPointer<QTemporaryDir> result; // null until success, then == tempDir
2359
2360 QSharedPointer<QTemporaryDir> tempDir = QSharedPointer<QTemporaryDir>::create();
2361
2362 tempDir->setAutoRemove(true);
2363
2364 if (!tempDir->isValid())
2365 return result;
2366
2367 const QString dataPath = tempDir->path();
2368 const QString resourcePath = u':' + dirName;
2369 const QFileInfo fileInfo(resourcePath);
2370
2371 if (!fileInfo.isDir()) {
2372 qWarning("Resource path '%s' is not a directory.", qPrintable(resourcePath));
2373 return result;
2374 }
2375
2376 bool isResourceDirEmpty = true;
2377 for (const auto &dirEntry : QDirListing(resourcePath, QDirListing::IteratorFlag::Recursive)) {
2378 isResourceDirEmpty = false;
2379 if (!dirEntry.isDir()) {
2380 const QString &filePath = dirEntry.filePath();
2381 const QString destination =
2382 dataPath + u'/' + QStringView{filePath}.sliced(resourcePath.size());
2383 QFileInfo destinationFileInfo(destination);
2384 QDir().mkpath(destinationFileInfo.path());
2385 QFile file(filePath);
2386 if (!file.copy(destination)) {
2387 qWarning("Failed to copy '%ls': %ls.", qUtf16Printable(filePath),
2388 qUtf16Printable(file.errorString()));
2389 return result;
2390 }
2391
2392 file.setFileName(destination);
2393 if (!file.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup)) {
2394 qWarning("Failed to set permissions on '%ls': %ls.", qUtf16Printable(destination),
2395 qUtf16Printable(file.errorString()));
2396 return result;
2397 }
2398 }
2399 }
2400
2401 if (isResourceDirEmpty) {
2402 qWarning("Resource directory '%s' is empty.", qPrintable(resourcePath));
2403 return result;
2404 }
2405
2406 result = std::move(tempDir);
2407
2408 return result;
2409}
2410#endif // QT_CONFIG(temporaryfile)
2411
2412/*! \internal
2413*/
2414
2415QString QTest::qFindTestData(const QString& base, const char *file, int line, const char *builddir,
2416 const char *sourcedir)
2417{
2418 QString found;
2419
2420 // Testdata priorities:
2421
2422 // 1. relative to test binary.
2423 if (qApp) {
2424 QDir binDirectory(QCoreApplication::applicationDirPath());
2425 if (binDirectory.exists(base)) {
2426 found = binDirectory.absoluteFilePath(base);
2427 }
2428#ifdef Q_OS_WIN
2429 // Windows: The executable is typically located in one of the
2430 // 'Release' or 'Debug' directories.
2431 else if (isWindowsBuildDirectory(binDirectory.dirName())
2432 && binDirectory.cdUp() && binDirectory.exists(base)) {
2433 found = binDirectory.absoluteFilePath(base);
2434 }
2435#endif // Q_OS_WIN
2436 else if (QTestLog::verboseLevel() >= 2) {
2437 const QString candidate = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + u'/' + base);
2438 QTestLog::info(qPrintable("testdata %1 not found relative to test binary [%2]; "
2439 "checking next location"_L1.arg(base, candidate)),
2440 file, line);
2441 }
2442 }
2443
2444 // 2. installed path.
2445 if (found.isEmpty()) {
2446 const char *testObjectName = QTestResult::currentTestObjectName();
2447 if (testObjectName) {
2448 const QString testsPath = QLibraryInfo::path(QLibraryInfo::TestsPath);
2449 const QString candidate = "%1/%2/%3"_L1
2450 .arg(testsPath, QFile::decodeName(testObjectName).toLower(), base);
2451 if (QFileInfo::exists(candidate)) {
2452 found = candidate;
2453 } else if (QTestLog::verboseLevel() >= 2) {
2454 QTestLog::info(qPrintable("testdata %1 not found in tests install path [%2]; "
2455 "checking next location"_L1
2456 .arg(base, QDir::toNativeSeparators(candidate))),
2457 file, line);
2458 }
2459 }
2460 }
2461
2462 // 3. relative to test source.
2463 if (found.isEmpty() && qstrncmp(file, ":/", 2) != 0) {
2464 // srcdir is the directory containing the calling source file.
2465 QFileInfo srcdir(QFileInfo(QFile::decodeName(file)).path());
2466
2467 // If the srcdir is relative, that means it is relative to the current working
2468 // directory of the compiler at compile time, which should be passed in as `builddir'.
2469 if (!srcdir.isAbsolute() && builddir)
2470 srcdir.setFile(QFile::decodeName(builddir) + u'/' + srcdir.filePath());
2471
2472 const QString canonicalPath = srcdir.canonicalFilePath();
2473 const QString candidate = "%1/%2"_L1.arg(canonicalPath, base);
2474 if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) {
2475 found = candidate;
2476 } else if (QTestLog::verboseLevel() >= 2) {
2477 QTestLog::info(qPrintable(
2478 "testdata %1 not found relative to source path [%2]"_L1
2479 .arg(base, QDir::toNativeSeparators(candidate))),
2480 file, line);
2481 }
2482 }
2483
2484 // 4. Try resources
2485 if (found.isEmpty()) {
2486 const QString candidate = ":/%1"_L1.arg(base);
2487 if (QFileInfo::exists(candidate)) {
2488 found = candidate;
2489 } else if (QTestLog::verboseLevel() >= 2) {
2490 QTestLog::info(qPrintable(
2491 "testdata %1 not found in resources [%2]"_L1
2492 .arg(base, QDir::toNativeSeparators(candidate))),
2493 file, line);
2494 }
2495 }
2496
2497 // 5. Try current directory
2498 if (found.isEmpty()) {
2499 const QString candidate = QDir::currentPath() + u'/' + base;
2500 if (QFileInfo::exists(candidate)) {
2501 found = candidate;
2502 } else if (QTestLog::verboseLevel() >= 2) {
2503 QTestLog::info(qPrintable(
2504 "testdata %1 not found in current directory [%2]"_L1
2505 .arg(base, QDir::toNativeSeparators(candidate))),
2506 file, line);
2507 }
2508 }
2509
2510 // 6. Try main source directory
2511 if (found.isEmpty()) {
2512 const QString candidate = QTest::mainSourcePath % u'/' % base;
2513 if (QFileInfo::exists(candidate)) {
2514 found = candidate;
2515 } else if (QTestLog::verboseLevel() >= 2) {
2516 QTestLog::info(qPrintable(
2517 "testdata %1 not found in main source directory [%2]"_L1
2518 .arg(base, QDir::toNativeSeparators(candidate))),
2519 file, line);
2520 }
2521 }
2522
2523 // 7. Try the supplied source directory
2524 if (found.isEmpty() && sourcedir) {
2525 const QString candidate = QFile::decodeName(sourcedir) % u'/' % base;
2526 if (QFileInfo::exists(candidate)) {
2527 found = candidate;
2528 } else if (QTestLog::verboseLevel() >= 2) {
2529 QTestLog::info(qPrintable(
2530 "testdata %1 not found in supplied source directory [%2]"_L1
2531 .arg(base, QDir::toNativeSeparators(candidate))),
2532 file, line);
2533 }
2534 }
2535
2536
2537 if (found.isEmpty()) {
2538 QTestLog::warn(qPrintable(
2539 "testdata %1 could not be located!"_L1.arg(base)),
2540 file, line);
2541 } else if (QTestLog::verboseLevel() >= 1) {
2542 QTestLog::info(qPrintable(
2543 "testdata %1 was located at %2"_L1.arg(base, QDir::toNativeSeparators(found))),
2544 file, line);
2545 }
2546
2547 return found;
2548}
2549
2550/*! \internal
2551*/
2552QString QTest::qFindTestData(const char *base, const char *file, int line, const char *builddir,
2553 const char *sourcedir)
2554{
2555 return qFindTestData(QFile::decodeName(base), file, line, builddir, sourcedir);
2556}
2557
2558/*! \internal
2559*/
2560void *QTest::qData(const char *tagName, int typeId)
2561{
2562 return fetchData(QTestResult::currentTestData(), tagName, typeId);
2563}
2564
2565/*! \internal
2566*/
2567void *QTest::qGlobalData(const char *tagName, int typeId)
2568{
2569 return fetchData(QTestResult::currentGlobalTestData(), tagName, typeId);
2570}
2571
2572/*! \internal
2573*/
2574void *QTest::qElementData(const char *tagName, int metaTypeId)
2575{
2576 QTEST_ASSERT(tagName);
2577 QTestData *data = QTestResult::currentTestData();
2578 QTEST_ASSERT(data);
2579 QTEST_ASSERT(data->parent());
2580
2581 int idx = data->parent()->indexOf(tagName);
2582 QTEST_ASSERT(idx != -1);
2583 QTEST_ASSERT(data->parent()->elementTypeId(idx) == metaTypeId);
2584
2585 return data->data(data->parent()->indexOf(tagName));
2586}
2587
2588/*! \internal
2589*/
2590void QTest::addColumnInternal(int id, const char *name)
2591{
2592 QTestTable *tbl = QTestTable::currentTestTable();
2593 QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot.");
2594
2595 tbl->addColumn(id, name);
2596}
2597
2598/*!
2599 Appends a new row to the current test data.
2600
2601 The test output will identify the test run with this test data using the
2602 name \a dataTag.
2603
2604 Returns a QTestData reference that can be used to stream in data, one value
2605 for each column in the table.
2606
2607 Example:
2608 \snippet code/src_qtestlib_qtestcase.cpp 20
2609
2610 \note This function can only be called as part of a test's data function
2611 that is invoked by the test framework.
2612
2613 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2614 a more extensive example.
2615
2616 \sa addRow(), addColumn(), QFETCH()
2617*/
2618QTestData &QTest::newRow(const char *dataTag)
2619{
2620 QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null");
2621 QTestTable *tbl = QTestTable::currentTestTable();
2622 QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot.");
2623 QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()",
2624 "Must add columns before attempting to add rows.");
2625
2626 return *tbl->newData(dataTag);
2627}
2628
2629/*!
2630 \since 5.9
2631
2632 Appends a new row to the current test data.
2633
2634 The function's arguments are passed to std::snprintf() for
2635 formatting according to \a format. See the
2636 \l{https://en.cppreference.com/w/cpp/io/c/fprintf}{std::snprintf()
2637 documentation} for caveats and limitations.
2638
2639 The test output will identify the test run with this test data using the
2640 name that results from this formatting.
2641
2642 Returns a QTestData reference that can be used to stream in data, one value
2643 for each column in the table.
2644
2645 Example:
2646 \snippet code/src_qtestlib_qtestcase.cpp addRow
2647
2648 \note This function can only be called as part of a test's data function
2649 that is invoked by the test framework.
2650
2651 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2652 a more extensive example.
2653
2654 \sa newRow(), addColumn(), QFETCH()
2655*/
2656QTestData &QTest::addRow(const char *format, ...)
2657{
2658 QTEST_ASSERT_X(format, "QTest::addRow()", "Format string cannot be null");
2659 QTestTable *tbl = QTestTable::currentTestTable();
2660 QTEST_ASSERT_X(tbl, "QTest::addRow()", "Cannot add testdata outside of a _data slot.");
2661 QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()",
2662 "Must add columns before attempting to add rows.");
2663
2664 char buf[1024];
2665
2666 va_list va;
2667 va_start(va, format);
2668 // we don't care about failures, we accept truncation, as well as trailing garbage.
2669 // Names with more than 1K characters are nonsense, anyway.
2670 std::vsnprintf(buf, sizeof buf, format, va);
2671 va_end(va);
2672
2673 return *tbl->newData(buf);
2674}
2675
2676/*! \fn template <typename T> void QTest::addColumn(const char *name, T *dummy = 0)
2677
2678 Adds a column with type \c{T} to the current test data.
2679 \a name is the name of the column. \a dummy is a workaround
2680 for buggy compilers and can be ignored.
2681
2682 To populate the column with values, newRow() can be used. Use
2683 \l QFETCH() to fetch the data in the actual test.
2684
2685 Example:
2686 \snippet code/src_qtestlib_qtestcase.cpp 21
2687
2688 \note This function can only be used called as part of a test's data
2689 function that is invoked by the test framework.
2690
2691 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2692 a more extensive example.
2693
2694 \sa QTest::newRow(), QFETCH(), QMetaType
2695*/
2696
2697/*!
2698 Returns the name of the binary that is currently executed.
2699*/
2700const char *QTest::currentAppName()
2701{
2702 return QTestResult::currentAppName();
2703}
2704
2705/*!
2706 Returns the name of the test function that is currently executed.
2707
2708 Example:
2709
2710 \snippet code/src_qtestlib_qtestcase.cpp 22
2711*/
2713{
2714 return QTestResult::currentTestFunction();
2715}
2716
2717/*!
2718 Returns the name of the current test data. If the test doesn't
2719 have any assigned testdata, the function returns \nullptr.
2720*/
2721const char *QTest::currentDataTag()
2722{
2723 return QTestResult::currentDataTag();
2724}
2725
2726/*!
2727 Returns the name of the current global test data. If the test doesn't
2728 have any assigned global testdata, the function returns \nullptr.
2729
2730 \since 6.11
2731*/
2733{
2734 return QTestResult::currentGlobalDataTag();
2735}
2736
2737/*!
2738 Returns \c true if the current test function has failed, otherwise false.
2739
2740 \sa QTest::currentTestResolved()
2741*/
2743{
2744 return QTestResult::currentTestFailed();
2745}
2746
2747/*!
2748 \since 6.5
2749 Returns \c true if the current test function has failed or skipped.
2750
2751 This applies if the test has failed or exercised a skip. When it is true,
2752 the test function should return early. In particular, the \c{QTRY_*} macros
2753 and the test event loop terminate their loops early if executed during the
2754 test function (but not its cleanup()). After a test has called a helper
2755 function that uses this module's macros, it can use this function to test
2756 whether to return early.
2757
2758 \sa QTest::currentTestFailed()
2759*/
2761{
2762 return QTestResult::currentTestFailed() || QTestResult::skipCurrentTest();
2763}
2764
2765/*!
2766 \internal
2767 \since 6.4
2768 Returns \c true during the run of the test-function and its set-up.
2769
2770 Used by the \c{QTRY_*} macros and \l QTestEventLoop to check whether to
2771 return when QTest::currentTestResolved() is true.
2772*/
2774{
2775 return QTest::inTestFunction;
2776}
2777
2778/*! \internal
2779*/
2780QObject *QTest::testObject()
2781{
2782 return currentTestObject;
2783}
2784
2785/*! \internal
2786*/
2787void QTest::setMainSourcePath(const char *file, const char *builddir)
2788{
2789 QString mainSourceFile = QFile::decodeName(file);
2790 QFileInfo fi;
2791 if (builddir)
2792 fi.setFile(QDir(QFile::decodeName(builddir)), mainSourceFile);
2793 else
2794 fi.setFile(mainSourceFile);
2795 QTest::mainSourcePath = fi.absolutePath();
2796}
2797
2798#if QT_DEPRECATED_SINCE(6, 4)
2799/*! \internal
2800 \deprecated [6.4]
2801 This function is called by various specializations of QTest::qCompare
2802 to decide whether to report a failure and to produce verbose test output.
2803
2804 The failureMsg parameter can be null, in which case a default message
2805 will be output if the compare fails. If the compare succeeds, failureMsg
2806 will not be output.
2807
2808 Using this function is not optimal, because it requires the string
2809 representations of \a actualVal and \a expectedVal to be pre-calculated,
2810 even though they will be used only if the comparison fails. Prefer using the
2811 \l compare_helper() overload that takes qxp::function_ref() for such cases.
2812
2813 If the caller creates a custom failure message showing the compared values,
2814 or if those values cannot be stringified, use the overload of the function
2815 that takes no \a actualVal and \a expectedVal parameters.
2816*/
2817bool QTest::compare_helper(bool success, const char *failureMsg,
2818 char *actualVal, char *expectedVal,
2819 const char *actual, const char *expected,
2820 const char *file, int line)
2821{
2822 return QTestResult::compare(success, failureMsg, actualVal, expectedVal,
2823 actual, expected, file, line);
2824}
2825#endif // QT_DEPRECATED_SINCE(6, 4)
2826
2827#if QT_DEPRECATED_SINCE(6, 8)
2828/*! \internal
2829 \since 6.4
2830 This function is called by various specializations of QTest::qCompare
2831 to decide whether to report a failure and to produce verbose test output.
2832
2833 The \a failureMsg parameter can be \c {nullptr}, in which case a default
2834 message will be output if the compare fails. If the comparison succeeds,
2835 \a failureMsg will not be output.
2836
2837 This overload of the function uses qxp::function_ref to defer conversion of
2838 \a actualVal and \a expectedVal to strings until that is really needed
2839 (when the comparison fails). This speeds up test case execution on success.
2840*/
2841bool QTest::compare_helper(bool success, const char *failureMsg,
2842 qxp::function_ref<const char *()> actualVal,
2843 qxp::function_ref<const char *()> expectedVal,
2844 const char *actual, const char *expected,
2845 const char *file, int line)
2846{
2847 return QTestResult::reportResult(success, &actualVal, &expectedVal,
2848 QTest::functionRefFormatter,
2849 QTest::functionRefFormatter, actual, expected,
2850 QTest::ComparisonOperation::CustomCompare,
2851 file, line, failureMsg);
2852}
2853#endif // QT_DEPRECATED_SINCE(6, 8)
2854
2855/*! \internal
2856 \since 6.8
2857 This function is called by various specializations of QTest::qCompare
2858 to decide whether to report a failure and to produce verbose test output.
2859
2860 The \a failureMsg parameter can be \c {nullptr}, in which case a default
2861 message will be output if the compare fails. If the comparison succeeds,
2862 \a failureMsg will not be output.
2863*/
2864
2865bool QTest::compare_helper(bool success, const char *failureMsg,
2866 const void *actualPtr, const void *expectedPtr,
2867 const char *(*actualFormatter)(const void *),
2868 const char *(*expectedFormatter)(const void *),
2869 const char *actual, const char *expected,
2870 const char *file, int line)
2871{
2872 return QTestResult::reportResult(success, actualPtr, expectedPtr,
2873 actualFormatter, expectedFormatter,
2874 actual, expected,
2875 QTest::ComparisonOperation::CustomCompare,
2876 file, line, failureMsg);
2877}
2878
2879
2880/*! \internal
2881 \since 6.9
2882 This function reports the result of a three-way comparison, when needed.
2883
2884 Aside from logging every check if in verbose mode and reporting an
2885 unexpected pass when failure was expected, if \a success is \c true
2886 this produces no output. Otherwise, a failure is reported. The output
2887 on failure reports the expressions compared, their values, the actual
2888 result of the comparison and the expected result of comparison, along
2889 with the supplied failure message \a failureMsg and the \a file and
2890 \a line number at which the error arose.
2891
2892 The expressions compared are supplied as \a lhsExpression and
2893 \a rhsExpression.
2894 These are combined, with \c{"<=>"}, to obtain the actual comparison
2895 expression. Their actual values are pointed to by \a lhsPtr and
2896 \a rhsPtr, which are formatted by \a lhsFormatter and \a rhsFormatter
2897 as, respectively, \c lhsFormatter(lhsPtr) and \c rhsFormatter(rhsPtr).
2898 The actual comparison expression is contrasted,
2899 in the output, with the expected comparison expression
2900 \a expectedExpression. Their respective values are supplied by
2901 \a actualOrderPtr and \a expectedOrderPtr pointers, which are
2902 formatted by \a actualOrderFormatter and \a expectedOrderFormatter.
2903
2904 If \a failureMsg is \nullptr a default is used. If a formatter
2905 function returns \a nullptr, the text \c{"<null>"} is used.
2906*/
2907bool QTest::compare_3way_helper(bool success, const char *failureMsg,
2908 const void *lhsPtr, const void *rhsPtr,
2909 const char *(*lhsFormatter)(const void*),
2910 const char *(*rhsFormatter)(const void*),
2911 const char *lhsExpression, const char *rhsExpression,
2912 const char *(*actualOrderFormatter)(const void *),
2913 const char *(*expectedOrderFormatter)(const void *),
2914 const void *actualOrderPtr, const void *expectedOrderPtr,
2915 const char *expectedExpression,
2916 const char *file, int line)
2917{
2918 return QTestResult::report3WayResult(success, failureMsg,
2919 lhsPtr, rhsPtr,
2920 lhsFormatter, rhsFormatter,
2921 lhsExpression, rhsExpression,
2922 actualOrderFormatter,
2923 expectedOrderFormatter,
2924 actualOrderPtr, expectedOrderPtr,
2925 expectedExpression,
2926 file, line);
2927}
2928
2929/*! \internal
2930 \since 6.4
2931 This function is called by various specializations of QTest::qCompare
2932 to decide whether to report a failure and to produce verbose test output.
2933
2934 This overload should be used when there is no string representation of
2935 actual and expected values, so only the \a failureMsg is shown when the
2936 comparison fails. Because of that, \a failureMsg can't be \c {nullptr}.
2937 If the comparison succeeds, \a failureMsg will not be output.
2938*/
2939bool QTest::compare_helper(bool success, const char *failureMsg, const char *actual,
2940 const char *expected, const char *file, int line)
2941{
2942 return QTestResult::compare(success, failureMsg, actual, expected, file, line);
2943}
2944
2945template <typename T>
2946static bool floatingCompare(const T &actual, const T &expected)
2947{
2948 switch (qFpClassify(expected))
2949 {
2950 case FP_INFINITE:
2951 return (expected < 0) == (actual < 0) && qFpClassify(actual) == FP_INFINITE;
2952 case FP_NAN:
2953 return qFpClassify(actual) == FP_NAN;
2954 default:
2955 if (!qFuzzyIsNull(expected))
2956 return qFuzzyCompare(actual, expected);
2957 Q_FALLTHROUGH();
2958 case FP_SUBNORMAL: // subnormal is always fuzzily null
2959 case FP_ZERO:
2960 return qFuzzyIsNull(actual);
2961 }
2962}
2963
2964/*! \fn bool QTest::qCompare(const qfloat16 &t1, const qfloat16 &t2, const char *actual, const char *expected, const char *file, int line)
2965 \internal
2966*/
2967bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
2968 const char *file, int line)
2969{
2970 auto formatter = Internal::genericToString<qfloat16>;
2971 return compare_helper(floatingCompare(t1, t2),
2972 "Compared qfloat16s are not the same (fuzzy compare)",
2973 &t1, &t2, formatter, formatter,
2974 actual, expected, file, line);
2975}
2976
2977/*! \fn bool QTest::qCompare(const float &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
2978 \internal
2979*/
2980bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const char *expected,
2981 const char *file, int line)
2982{
2983 return QTestResult::compare(floatingCompare(t1, t2),
2984 "Compared floats are not the same (fuzzy compare)",
2985 t1, t2, actual, expected, file, line);
2986}
2987
2988/*! \fn bool QTest::qCompare(const double &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
2989 \internal
2990*/
2991bool QTest::qCompare(double const &t1, double const &t2, const char *actual, const char *expected,
2992 const char *file, int line)
2993{
2994 return QTestResult::compare(floatingCompare(t1, t2),
2995 "Compared doubles are not the same (fuzzy compare)",
2996 t1, t2, actual, expected, file, line);
2997}
2998
2999/*! \fn bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected, const char *file, int line)
3000 \internal
3001 \since 5.14
3002*/
3003bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
3004 const char *file, int line)
3005{
3006 return QTestResult::compare(t1 == t2,
3007 "Compared values are not the same",
3008 t1, t2, actual, expected, file, line);
3009}
3010
3011#if QT_POINTER_SIZE == 8
3012/*! \fn bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected, const char *file, int line)
3013 \internal
3014 \since 6.0
3015*/
3016
3017bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected,
3018 const char *file, int line)
3019{
3020 return QTestResult::compare(t1 == t2,
3021 "Compared values are not the same",
3022 t1, t2, actual, expected, file, line);
3023}
3024#endif // QT_POINTER_SIZE == 8
3025
3026/*! \fn bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected, const char *file, int line)
3027 \internal
3028 \since 5.14
3029*/
3030bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected,
3031 const char *file, int line)
3032{
3033 return QTestResult::compare(t1 == t2,
3034 "Compared values are not the same",
3035 t1, t2, actual, expected, file, line);
3036}
3037
3038/*! \fn bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
3039 \internal
3040 \since 5.14
3041*/
3042bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected,
3043 const char *file, int line)
3044{
3045 return QTestResult::compare(t1 == t2,
3046 "Compared values are not the same",
3047 t1, t2, actual, expected, file, line);
3048}
3049
3050/*!
3051 \internal
3052 \since 5.14
3053*/
3054bool QTest::qCompare(QStringView t1, const QLatin1StringView &t2, const char *actual, const char *expected,
3055 const char *file, int line)
3056{
3057 return QTestResult::compare(t1 == t2,
3058 "Compared values are not the same",
3059 t1, t2, actual, expected, file, line);
3060}
3061
3062/*!
3063 \internal
3064 \since 5.14
3065*/
3066bool QTest::qCompare(const QLatin1StringView &t1, QStringView t2, const char *actual, const char *expected,
3067 const char *file, int line)
3068{
3069 return QTestResult::compare(t1 == t2,
3070 "Compared values are not the same",
3071 t1, t2, actual, expected, file, line);
3072}
3073
3074/*! \fn bool QTest::qCompare(const QString &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
3075 \internal
3076 \since 5.14
3077*/
3078
3079/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1StringView &t2, const char *actual, const char *expected, const char *file, int line)
3080 \internal
3081 \since 5.14
3082*/
3083
3084/*! \fn bool QTest::qCompare(const QLatin1StringView &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
3085 \internal
3086 \since 5.14
3087*/
3088
3089/*! \fn bool QTest::qCompare(const double &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
3090 \internal
3091*/
3092
3093/*! \fn bool QTest::qCompare(const float &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
3094 \internal
3095*/
3096
3097#define TO_STRING_IMPL(TYPE, FORMAT) template
3098 <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
3099{
3100 char *msg = new char[128];
3101 std::snprintf(msg, 128, #FORMAT, t);
3102 return msg; \
3103}
3104
3105TO_STRING_IMPL(short, %hd)
3106TO_STRING_IMPL(ushort, %hu)
3107TO_STRING_IMPL(int, %d)
3108TO_STRING_IMPL(uint, %u)
3109TO_STRING_IMPL(long, %ld)
3110TO_STRING_IMPL(ulong, %lu)
3111TO_STRING_IMPL(qint64, %lld)
3112TO_STRING_IMPL(quint64, %llu)
3113TO_STRING_IMPL(bool, %d)
3114TO_STRING_IMPL(signed char, %hhd)
3115TO_STRING_IMPL(unsigned char, %hhu)
3116
3117// Merge of ISO C23 getpayload() and issignaling()
3118template <typename T> static auto decodeNanPayload(T t)
3119{
3120 constexpr int Digits = std::numeric_limits<T>::digits;
3121 constexpr quint64 MantissaMask = (Q_UINT64_C(1) << (Digits - 1)) - 1;
3122 constexpr quint64 IsQuietBit = quint64(QT_CONFIG(signaling_nan)) << (Digits - 2);
3123 constexpr quint64 PayloadMask = MantissaMask & ~IsQuietBit;
3124
3125 struct R {
3126 quint64 payload;
3127 bool isQuiet;
3128 } r;
3129 Q_ASSERT(qIsNaN(t));
3130 quint64 u = qFromUnaligned<typename QIntegerForSizeof<T>::Unsigned>(&t);
3131 r.payload = u & PayloadMask;
3132 r.isQuiet = !QT_CONFIG(signaling_nan) || (u & IsQuietBit);
3133 return r;
3134}
3135
3136static bool signbit(qfloat16 f) { return f.signBit(); }
3137
3138// Be consistent about display of infinities and NaNs (snprintf()'s varies,
3139// notably on MinGW, despite POSIX documenting "[-]inf" or "[-]infinity" for %f,
3140// %e and %g, uppercasing for their capital versions; similar for "nan"):
3141template <typename T> static char *toStringFp(T t)
3142{
3143 using std::signbit;
3144 char *msg = new char[128];
3145 bool negative = signbit(t);
3146
3147 switch (qFpClassify(t)) {
3148 case FP_INFINITE:
3149 qstrncpy(msg, (negative ? "-inf" : "inf"), 128);
3150 break;
3151 case FP_NAN:
3152 if (auto r = decodeNanPayload(t); r.payload) {
3153 std::snprintf(msg, 128, "%s%snan(%#llx)",
3154 negative ? "-" : "", r.isQuiet ? "" : "s", r.payload);
3155 } else {
3156 Q_ASSERT(r.isQuiet); // only quiet NaNs can have payload == 0
3157 qstrncpy(msg, (negative ? "-nan" : "nan"), 128);
3158 }
3159 break;
3160 case FP_ZERO:
3161 qstrncpy(msg, (negative ? "-0 (-0x0p+0)" : "0 (0x0p+0)"), 128);
3162 break;
3163 default:
3164 std::snprintf(msg, 128, "%.*g (%a)", std::numeric_limits<T>::digits10 + 1, double(t),
3165 double(t));
3166 break;
3167 }
3168 return msg;
3169}
3170
3171#define TO_STRING_FLOAT(TYPE) template
3172 <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
3173{
3174 return toStringFp(t); \
3175}
3176TO_STRING_FLOAT(qfloat16)
3177TO_STRING_FLOAT(float)
3178TO_STRING_FLOAT(double)
3179
3180template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t)
3181{
3182 unsigned char c = static_cast<unsigned char>(t);
3183 char *msg = new char[16];
3184 switch (c) {
3185 case 0x00:
3186 qstrcpy(msg, "'\\0'");
3187 break;
3188 case 0x07:
3189 qstrcpy(msg, "'\\a'");
3190 break;
3191 case 0x08:
3192 qstrcpy(msg, "'\\b'");
3193 break;
3194 case 0x09:
3195 qstrcpy(msg, "'\\t'");
3196 break;
3197 case 0x0a:
3198 qstrcpy(msg, "'\\n'");
3199 break;
3200 case 0x0b:
3201 qstrcpy(msg, "'\\v'");
3202 break;
3203 case 0x0c:
3204 qstrcpy(msg, "'\\f'");
3205 break;
3206 case 0x0d:
3207 qstrcpy(msg, "'\\r'");
3208 break;
3209 case 0x22:
3210 qstrcpy(msg, "'\\\"'");
3211 break;
3212 case 0x27:
3213 qstrcpy(msg, "'\\\''");
3214 break;
3215 case 0x5c:
3216 qstrcpy(msg, "'\\\\'");
3217 break;
3218 default:
3219 if (c < 0x20 || c >= 0x7F)
3220 std::snprintf(msg, 16, "'\\x%02x'", c);
3221 else
3222 std::snprintf(msg, 16, "'%c'" , c);
3223 }
3224 return msg;
3225}
3226
3227/*! \internal
3228*/
3229char *QTest::toString(const char *str)
3230{
3231 if (!str) {
3232 char *msg = new char[1];
3233 *msg = '\0';
3234 return msg;
3235 }
3236 char *msg = new char[strlen(str) + 1];
3237 return qstrcpy(msg, str);
3238}
3239
3240/*! \internal
3241*/
3242char *QTest::toString(const volatile void *p) // Use volatile to match compare_ptr_helper()
3243{
3244 char *msg = new char[128];
3245 std::snprintf(msg, 128, "%p", p);
3246 return msg;
3247}
3248
3249/*! \internal
3250*/
3251char *QTest::toString(const volatile QObject *vo)
3252{
3253 if (vo == nullptr)
3254 return qstrdup("<null>");
3255
3256 return QTest::toString(const_cast<const QObject*>(vo));
3257}
3258
3259/*! \internal
3260*/
3261char *QTest::toString(const QObject *o)
3262{
3263 if (o == nullptr)
3264 return qstrdup("<null>");
3265
3266 const QString &name = o->objectName();
3267 const char *className = o->metaObject()->className();
3268 char *msg = new char[256];
3269 if (name.isEmpty())
3270 std::snprintf(msg, 256, "%s/%p", className, o);
3271 else
3272 std::snprintf(msg, 256, "%s/\"%s\"", className, qPrintable(name));
3273 return msg;
3274}
3275
3276
3277/*! \fn char *QTest::toString(const QColor &color)
3278 \internal
3279*/
3280
3281/*! \fn char *QTest::toString(const QRegion &region)
3282 \internal
3283*/
3284
3285/*! \fn char *QTest::toString(const QHostAddress &addr)
3286 \internal
3287*/
3288
3289/*! \fn char *QTest::toString(QNetworkReply::NetworkError code)
3290 \internal
3291*/
3292
3293/*! \fn char *QTest::toString(const QNetworkCookie &cookie)
3294 \internal
3295*/
3296
3297/*! \fn char *QTest::toString(const QList<QNetworkCookie> &list)
3298 \internal
3299*/
3300
3301/*! \internal
3302*/
3303bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
3304 const char *expected, const char *file, int line)
3305{
3306 auto formatter = Internal::genericToString<const char *>;
3307 return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same",
3308 &t1, &t2, formatter, formatter,
3309 actual, expected, file, line);
3310}
3311
3312/*!
3313 \namespace QTest::Internal
3314 \internal
3315*/
3316
3317/*! \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)
3318 \internal
3319*/
3320
3321/*! \fn bool QTest::compare_ptr_helper(const volatile void *t1, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
3322 \internal
3323*/
3324
3325/*! \fn bool QTest::compare_ptr_helper(std::nullptr_t, const volatile void *t2, const char *actual, const char *expected, const char *file, int line)
3326 \internal
3327*/
3328
3329/*! \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)
3330 \internal
3331*/
3332
3333/*! \fn bool QTest::qCompare(const QIcon &t1, const QIcon &t2, const char *actual, const char *expected, const char *file, int line)
3334 \internal
3335*/
3336
3337/*! \fn bool QTest::qCompare(const QImage &t1, const QImage &t2, const char *actual, const char *expected, const char *file, int line)
3338 \internal
3339*/
3340
3341/*! \fn bool QTest::qCompare(const QPixmap &t1, const QPixmap &t2, const char *actual, const char *expected, const char *file, int line)
3342 \internal
3343*/
3344
3345/*! \fn template <typename T> bool QTest::qCompare(const T &t1, const T &t2, const char *actual, const char *expected, const char *file, int line)
3346 \internal
3347*/
3348
3349/*! \fn template <typename T> bool QTest::qCompare(const T *t1, const T *t2, const char *actual, const char *expected, const char *file, int line)
3350 \internal
3351*/
3352
3353/*! \fn template <typename T> bool QTest::qCompare(T *t, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
3354 \internal
3355*/
3356
3357/*! \fn template <typename T> bool QTest::qCompare(std::nullptr_t, T *t, const char *actual, const char *expected, const char *file, int line)
3358 \internal
3359*/
3360
3361/*! \fn template <typename T> bool QTest::qCompare(T *t1, T *t2, const char *actual, const char *expected, const char *file, int line)
3362 \internal
3363*/
3364
3365/*! \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)
3366 \internal
3367*/
3368
3369/*! \fn template <typename T1, typename T2> bool QTest::qCompare(T1 *t1, T2 *t2, const char *actual, const char *expected, const char *file, int line)
3370 \internal
3371*/
3372
3373/*! \fn bool QTest::qCompare(const char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
3374 \internal
3375*/
3376
3377/*! \fn bool QTest::qCompare(char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
3378 \internal
3379*/
3380
3381/*! \fn bool QTest::qCompare(char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
3382 \internal
3383*/
3384
3385/*! \fn bool QTest::qCompare(const char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
3386 \internal
3387*/
3388
3389/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1StringView &t2, const char *actual, const char *expected, const char *file, int line)
3390 \internal
3391*/
3392
3393/*! \fn bool QTest::qCompare(const QLatin1StringView &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
3394 \internal
3395*/
3396
3397/*! \fn bool QTest::qCompare(const QStringList &t1, const QStringList &t2, const char *actual, const char *expected, const char *file, int line)
3398 \internal
3399*/
3400
3401/*! \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)
3402 \internal
3403*/
3404
3405/*! \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)
3406 \internal
3407*/
3408
3409/*! \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)
3410 \internal
3411*/
3412
3413/*! \fn bool QTest::qCompare(const qint64 &t1, const qint32 &t2, const char *actual, const char *expected, const char *file, int line)
3414 \internal
3415*/
3416
3417/*! \fn bool QTest::qCompare(const qint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
3418 \internal
3419*/
3420
3421/*! \fn bool QTest::qCompare(const quint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
3422 \internal
3423*/
3424
3425/*! \fn bool QTest::qCompare(const qint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
3426 \internal
3427*/
3428
3429/*! \fn bool QTest::qCompare(const quint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
3430 \internal
3431*/
3432
3433/*! \fn bool QTest::qCompare(const quint32 &t1, const quint64 &t2, const char *actual, const char *expected, const char *file, int line)
3434 \internal
3435*/
3436
3437/*! \fn template <typename T> bool QTest::qTest(const T& actual, const char *elementName, const char *actualStr, const char *expected, const char *file, int line)
3438 \internal
3439*/
3440
3441/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
3442 \internal
3443*/
3444
3445/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
3446 \internal
3447*/
3448
3449/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
3450 \internal
3451*/
3452
3453/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
3454 \internal
3455*/
3456
3457/*! \fn void QTest::simulateEvent(QWidget *widget, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
3458 \internal
3459*/
3460
3461/*! \fn void QTest::simulateEvent(QWindow *window, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
3462 \internal
3463*/
3464
3465QT_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)
static bool signbit(qfloat16 f)
#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)