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