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