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