Qt 6.x
The Qt SDK
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/qdiriterator.h>
14#include <QtCore/qfile.h>
15#include <QtCore/qfileinfo.h>
16#include <QtCore/qfloat16.h>
17#include <QtCore/qlibraryinfo.h>
18#include <QtCore/qlist.h>
19#include <QtCore/qmetaobject.h>
20#include <QtCore/qobject.h>
21#include <QtCore/qstringlist.h>
22#include <QtCore/qtemporarydir.h>
23#include <QtCore/qthread.h>
24#include <QtCore/qvarlengtharray.h>
25#include <QtCore/private/qlocking_p.h>
26#include <QtCore/private/qtools_p.h>
27#include <QtCore/private/qwaitcondition_p.h>
28
29#include <QtCore/qtestsupport_core.h>
30
31#include <QtTest/private/qtestlog_p.h>
32#include <QtTest/private/qtesttable_p.h>
33#include <QtTest/qtestdata.h>
34#include <QtTest/private/qtestresult_p.h>
35#include <QtTest/private/qsignaldumper_p.h>
36#include <QtTest/private/qbenchmark_p.h>
37#if QT_CONFIG(batch_test_support)
38#include <QtTest/private/qtestregistry_p.h>
39#endif // QT_CONFIG(batch_test_support)
40#include <QtTest/private/cycle_p.h>
41#include <QtTest/private/qtestblacklist_p.h>
42#if defined(HAVE_XCTEST)
43#include <QtTest/private/qxctestlogger_p.h>
44#endif
45#if defined Q_OS_MACOS
46#include <QtTest/private/qtestutil_macos_p.h>
47#endif
48
49#if defined(Q_OS_DARWIN)
50#include <QtTest/private/qappletestlogger_p.h>
51#endif
52
53#include <algorithm>
54#include <array>
55#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
56# include <charconv>
57#else
58// Broken implementation, causes link failures just by #include'ing!
59# undef __cpp_lib_to_chars // in case <version> was included
60#endif
61#include <chrono>
62#include <cmath>
63#include <limits>
64#include <memory>
65#include <mutex>
66#include <numeric>
67
68#include <stdarg.h>
69#include <stdio.h>
70#include <stdlib.h>
71
72#if defined(Q_OS_LINUX)
73#include <sys/types.h>
74#include <fcntl.h>
75#endif
76
77#ifdef Q_OS_WIN
78# include <iostream>
79# if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
80# include <crtdbg.h>
81# endif
82#include <qt_windows.h> // for Sleep
83#endif
84#ifdef Q_OS_UNIX
85#include <QtCore/private/qcore_unix_p.h>
86
87#include <errno.h>
88#if __has_include(<paths.h>)
89# include <paths.h>
90#endif
91#include <signal.h>
92#include <time.h>
93#include <sys/mman.h>
94#include <sys/uio.h>
95#include <sys/wait.h>
96#include <unistd.h>
97# if !defined(Q_OS_INTEGRITY)
98# include <sys/resource.h>
99# endif
100# ifndef _PATH_DEFPATH
101# define _PATH_DEFPATH "/usr/bin:/bin"
102# endif
103# ifndef SIGSTKSZ
104# define SIGSTKSZ 0 /* we have code to set the minimum */
105# endif
106# ifndef SA_RESETHAND
107# define SA_RESETHAND 0
108# endif
109#endif
110
111#if defined(Q_OS_MACOS)
112#include <IOKit/pwr_mgt/IOPMLib.h>
113#include <mach/task.h>
114#include <mach/mach_init.h>
115#include <CoreFoundation/CFPreferences.h>
116#endif
117
118#include <vector>
119
121
122using namespace Qt::StringLiterals;
123
126
127namespace {
128enum DebuggerProgram { None, Gdb, Lldb };
129
130#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
131static struct iovec IoVec(struct iovec vec)
132{
133 return vec;
134}
135static struct iovec IoVec(const char *str)
136{
137 struct iovec r = {};
138 r.iov_base = const_cast<char *>(str);
139 r.iov_len = strlen(str);
140 return r;
141}
142
143template <typename... Args> static ssize_t writeToStderr(Args &&... args)
144{
145 struct iovec vec[] = { IoVec(std::forward<Args>(args))... };
146 return ::writev(STDERR_FILENO, vec, std::size(vec));
147}
148
149// async-signal-safe conversion from int to string
150struct AsyncSafeIntBuffer
151{
152 // digits10 + 1 for all possible digits
153 // +1 for the sign
154 // +1 for the terminating null
155 static constexpr int Digits10 = std::numeric_limits<int>::digits10 + 3;
156 std::array<char, Digits10> array;
157 constexpr AsyncSafeIntBuffer() : array{} {} // initializes array
158 AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized
159};
160
161static struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized)
162{
163 char *ptr = result.array.data();
164 if (false) {
165#ifdef __cpp_lib_to_chars
166 } else if (auto r = std::to_chars(ptr, ptr + result.array.size(), n, 10); r.ec == std::errc{}) {
167 ptr = r.ptr;
168#endif
169 } else {
170 // handle the sign
171 if (n < 0) {
172 *ptr++ = '-';
173 n = -n;
174 }
175
176 // find the highest power of the base that is less than this number
177 static constexpr int StartingDivider = ([]() {
178 int divider = 1;
179 for (int i = 0; i < std::numeric_limits<int>::digits10; ++i)
180 divider *= 10;
181 return divider;
182 }());
183 int divider = StartingDivider;
184 while (divider && n < divider)
185 divider /= 10;
186
187 // now convert to string
188 while (divider > 1) {
189 int quot = n / divider;
190 n = n % divider;
191 divider /= 10;
192 *ptr++ = quot + '0';
193 }
194 *ptr++ = n + '0';
195 }
196
197#ifndef QT_NO_DEBUG
198 // this isn't necessary, it just helps in the debugger
199 *ptr = '\0';
200#endif
201 struct iovec r;
202 r.iov_base = result.array.data();
203 r.iov_len = ptr - result.array.data();
204 return r;
205};
206#elif defined(Q_OS_WIN)
207// Windows doesn't need to be async-safe
208template <typename... Args> static void writeToStderr(Args &&... args)
209{
210 (std::cerr << ... << args);
211}
212
213static std::string asyncSafeToString(int n)
214{
215 return std::to_string(n);
216}
217#endif // defined(Q_OS_UNIX)
218} // unnamed namespace
219
220static bool alreadyDebugging()
221{
222#if defined(Q_OS_LINUX)
223 int fd = open("/proc/self/status", O_RDONLY);
224 if (fd == -1)
225 return false;
226 char buffer[2048];
227 ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
228 if (size == -1) {
229 close(fd);
230 return false;
231 }
232 buffer[size] = 0;
233 const char tracerPidToken[] = "\nTracerPid:";
234 char *tracerPid = strstr(buffer, tracerPidToken);
235 if (!tracerPid) {
236 close(fd);
237 return false;
238 }
239 tracerPid += sizeof(tracerPidToken);
240 long int pid = strtol(tracerPid, &tracerPid, 10);
241 close(fd);
242 return pid != 0;
243#elif defined(Q_OS_WIN)
244 return IsDebuggerPresent();
245#elif defined(Q_OS_MACOS)
246 // Check if there is an exception handler for the process:
247 mach_msg_type_number_t portCount = 0;
248 exception_mask_t masks[EXC_TYPES_COUNT];
249 mach_port_t ports[EXC_TYPES_COUNT];
250 exception_behavior_t behaviors[EXC_TYPES_COUNT];
251 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
252 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
253 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
254 ports, behaviors, flavors);
255 if (result == KERN_SUCCESS) {
256 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
257 if (MACH_PORT_VALID(ports[portIndex])) {
258 return true;
259 }
260 }
261 }
262 return false;
263#else
264 // TODO
265 return false;
266#endif
267}
268
270{
271#if defined(Q_OS_MACOS)
273#else
274 return false;
275#endif
276}
277
279{
280#ifdef RLIMIT_CORE
281 bool ok = false;
282 const int disableCoreDump = qEnvironmentVariableIntValue("QTEST_DISABLE_CORE_DUMP", &ok);
283 if (ok && disableCoreDump) {
284 struct rlimit limit;
285 limit.rlim_cur = 0;
286 limit.rlim_max = 0;
287 if (setrlimit(RLIMIT_CORE, &limit) != 0)
288 qWarning("Failed to disable core dumps: %d", errno);
289 }
290#endif
291}
292
293static DebuggerProgram debugger = None;
294static void prepareStackTrace()
295{
296
297 bool ok = false;
298 const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
299 if (ok && disableStackDump)
300 return;
301
303 return;
304
305#if defined(Q_OS_MACOS)
306 #define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
307 std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
308 if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
309 return; // LLDB will fail to provide a valid stack trace
310#endif
311
312#ifdef Q_OS_UNIX
313 // like QStandardPaths::findExecutable(), but simpler
314 auto hasExecutable = [](const char *execname) {
315 std::string candidate;
316 std::string path;
317 if (const char *p = getenv("PATH"); p && *p)
318 path = p;
319 else
320 path = _PATH_DEFPATH;
321 for (const char *p = std::strtok(&path[0], ":'"); p; p = std::strtok(nullptr, ":")) {
322 candidate = p;
323 candidate += '/';
324 candidate += execname;
325 if (QT_ACCESS(candidate.data(), X_OK) == 0)
326 return true;
327 }
328 return false;
329 };
330
331 static constexpr DebuggerProgram debuggerSearchOrder[] = {
332# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
333 Gdb, Lldb
334# else
335 Lldb, Gdb
336# endif
337 };
338 for (DebuggerProgram candidate : debuggerSearchOrder) {
339 switch (candidate) {
340 case None:
341 Q_UNREACHABLE();
342 break;
343 case Gdb:
344 if (hasExecutable("gdb")) {
345 debugger = Gdb;
346 return;
347 }
348 break;
349 case Lldb:
350 if (hasExecutable("lldb")) {
351 debugger = Lldb;
352 return;
353 }
354 break;
355 }
356 }
357#endif // Q_OS_UNIX
358}
359
360#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
361static void printTestRunTime()
362{
363 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
364 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
365 const char *const name = QTest::currentTestFunction();
366 writeToStderr("\n ", name ? name : "[Non-test]",
367 " function time: ", asyncSafeToString(msecsFunctionTime),
368 "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n");
369}
370
372{
373 if (debugger == None || alreadyDebugging())
374 return;
375
376# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY)
377 writeToStderr("\n=== Stack trace ===\n");
378
379 // execlp() requires null-termination, so call the default constructor
380 AsyncSafeIntBuffer pidbuffer;
381 asyncSafeToString(getpid(), std::move(pidbuffer));
382
383 // Note: POSIX.1-2001 still has fork() in the list of async-safe functions,
384 // but in a future edition, it might be removed. It would be safer to wake
385 // up a babysitter thread to launch the debugger.
386 pid_t pid = fork();
387 if (pid == 0) {
388 // child process
389 (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
390
391 switch (debugger) {
392 case None:
393 Q_UNREACHABLE();
394 break;
395 case Gdb:
396 execlp("gdb", "gdb", "--nx", "--batch", "-ex", "thread apply all bt",
397 "--pid", pidbuffer.array.data(), nullptr);
398 break;
399 case Lldb:
400 execlp("lldb", "lldb", "--no-lldbinit", "--batch", "-o", "bt all",
401 "--attach-pid", pidbuffer.array.data(), nullptr);
402 break;
403 }
404 _exit(1);
405 } else if (pid < 0) {
406 writeToStderr("Failed to start debugger.\n");
407 } else {
408 int ret;
409 EINTR_LOOP(ret, waitpid(pid, nullptr, 0));
410 }
411
412 writeToStderr("=== End of stack trace ===\n");
413# endif // Q_OS_UNIX && !Q_OS_WASM
414}
415#endif // !defined(Q_OS_WASM) || QT_CONFIG(thread)
416
417static bool installCoverageTool(const char * appname, const char * testname)
418{
419#if defined(__COVERAGESCANNER__) && !QT_CONFIG(testlib_selfcover)
420 if (!qEnvironmentVariableIsEmpty("QT_TESTCOCOON_ACTIVE"))
421 return false;
422 // Set environment variable QT_TESTCOCOON_ACTIVE to prevent an eventual subtest from
423 // being considered as a stand-alone test regarding the coverage analysis.
424 qputenv("QT_TESTCOCOON_ACTIVE", "1");
425
426 // Install Coverage Tool
427 __coveragescanner_install(appname);
428 __coveragescanner_testname(testname);
429 __coveragescanner_clear();
430 return true;
431#else
432 Q_UNUSED(appname);
433 Q_UNUSED(testname);
434 return false;
435#endif
436}
437
438static bool isValidSlot(const QMetaMethod &sl)
439{
440 if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0
441 || sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot)
442 return false;
443 const QByteArray name = sl.name();
444 return !(name.isEmpty() || name.endsWith("_data")
445 || name == "initTestCase" || name == "cleanupTestCase"
446 || name == "init" || name == "cleanup");
447}
448
449namespace QTestPrivate
450{
451 Q_TESTLIB_EXPORT Qt::MouseButtons qtestMouseButtons = Qt::NoButton;
452}
453
454namespace QTest
455{
456
457QString Internal::formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual)
458{
459 return "QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) "
460 "was too short, %3 ms would have been sufficient this time."_L1
461 // ### Qt 7: remove the toString() (or earlier, when arg() can handle QUtf8StringView), passing the view directly
462 .arg(expr.toString(), QString::number(timeout), QString::number(actual));
463}
464
465extern Q_TESTLIB_EXPORT int lastMouseTimestamp;
466
467class WatchDog;
468
469static QObject *currentTestObject = nullptr;
471static bool inTestFunction = false;
472
473#if defined(Q_OS_MACOS)
474static IOPMAssertionID macPowerSavingDisabled = 0;
475#endif
476
478public:
479 Q_DISABLE_COPY_MOVE(TestMethods)
480
482
484
485 void invokeTests(QObject *testObject) const;
486
487 static QMetaMethod findMethod(const QObject *obj, const char *signature);
488
489private:
490 bool invokeTest(int index, QLatin1StringView tag, WatchDog *watchDog) const;
491 void invokeTestOnData(int index) const;
492
493 QMetaMethod m_initTestCaseMethod; // might not exist, check isValid().
494 QMetaMethod m_initTestCaseDataMethod;
495 QMetaMethod m_cleanupTestCaseMethod;
496 QMetaMethod m_initMethod;
497 QMetaMethod m_cleanupMethod;
498
499 MetaMethods m_methods;
500};
501
503 : m_initTestCaseMethod(TestMethods::findMethod(o, "initTestCase()"))
504 , m_initTestCaseDataMethod(TestMethods::findMethod(o, "initTestCase_data()"))
505 , m_cleanupTestCaseMethod(TestMethods::findMethod(o, "cleanupTestCase()"))
506 , m_initMethod(TestMethods::findMethod(o, "init()"))
507 , m_cleanupMethod(TestMethods::findMethod(o, "cleanup()"))
508 , m_methods(std::move(m))
509{
510 if (m_methods.empty()) {
511 const QMetaObject *metaObject = o->metaObject();
512 const int count = metaObject->methodCount();
513 m_methods.reserve(count);
514 for (int i = 0; i < count; ++i) {
515 const QMetaMethod me = metaObject->method(i);
516 if (isValidSlot(me))
517 m_methods.push_back(me);
518 }
519 }
520}
521
522QMetaMethod TestMethods::findMethod(const QObject *obj, const char *signature)
523{
524 const QMetaObject *metaObject = obj->metaObject();
525 const int funcIndex = metaObject->indexOfMethod(signature);
526 return funcIndex >= 0 ? metaObject->method(funcIndex) : QMetaMethod();
527}
528
529static int keyDelay = -1;
530static int mouseDelay = -1;
531static int eventDelay = -1;
532#if QT_CONFIG(thread)
533static int timeout = -1;
534#endif
535static bool noCrashHandler = false;
536
540static void invokeMethod(QObject *obj, const char *methodName)
541{
542 const QMetaObject *metaObject = obj->metaObject();
543 int funcIndex = metaObject->indexOfMethod(methodName);
544 if (funcIndex >= 0) {
545 QMetaMethod method = metaObject->method(funcIndex);
547 }
548}
549
551{
552 if (eventDelay == -1) {
553 const QByteArray env = qgetenv("QTEST_EVENT_DELAY");
554 if (!env.isEmpty())
555 eventDelay = atoi(env.constData());
556 else
557 eventDelay = 0;
558 }
559 return eventDelay;
560}
561
562int Q_TESTLIB_EXPORT defaultMouseDelay()
563{
564 if (mouseDelay == -1) {
565 const QByteArray env = qgetenv("QTEST_MOUSEEVENT_DELAY");
566 if (!env.isEmpty())
567 mouseDelay = atoi(env.constData());
568 else
570 }
571 return mouseDelay;
572}
573
574int Q_TESTLIB_EXPORT defaultKeyDelay()
575{
576 if (keyDelay == -1) {
577 const QByteArray env = qgetenv("QTEST_KEYEVENT_DELAY");
578 if (!env.isEmpty())
579 keyDelay = atoi(env.constData());
580 else
582 }
583 return keyDelay;
584}
585#if QT_CONFIG(thread)
586static std::chrono::milliseconds defaultTimeout()
587{
588 if (timeout == -1) {
589 bool ok = false;
590 timeout = qEnvironmentVariableIntValue("QTEST_FUNCTION_TIMEOUT", &ok);
591
592 if (!ok || timeout <= 0)
593 timeout = 5*60*1000;
594 }
595 return std::chrono::milliseconds{timeout};
596}
597#endif
598
599Q_TESTLIB_EXPORT bool printAvailableFunctions = false;
600Q_TESTLIB_EXPORT QStringList testFunctions;
601Q_TESTLIB_EXPORT QStringList testTags;
602
603static bool qPrintTestSlots(FILE *stream, const char *filter = nullptr, const char *preamble = "")
604{
605 const auto matches = [filter](const QByteArray &s) {
608 };
609 bool matched = false;
610 for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) {
611 QMetaMethod sl = QTest::currentTestObject->metaObject()->method(i);
612 if (isValidSlot(sl)) {
613 const QByteArray signature = sl.methodSignature();
614 if (matches(signature)) {
615 fprintf(stream, "%s%s\n", preamble, signature.constData());
616 preamble = "";
617 matched = true;
618 }
619 }
620 }
621 return matched;
622}
623
624static void qPrintDataTags(FILE *stream)
625{
626 // Avoid invoking the actual test functions, and also avoid printing irrelevant output:
628
629 // Get global data tags:
631 invokeMethod(QTest::currentTestObject, "initTestCase_data()");
632 const QTestTable *gTable = QTestTable::globalTestTable();
633
634 const QMetaObject *currTestMetaObj = QTest::currentTestObject->metaObject();
635
636 // Process test functions:
637 for (int i = 0; i < currTestMetaObj->methodCount(); ++i) {
638 QMetaMethod tf = currTestMetaObj->method(i);
639
640 if (isValidSlot(tf)) {
641
642 // Retrieve local tags:
643 QStringList localTags;
645 char *slot = qstrdup(tf.methodSignature().constData());
646 slot[strlen(slot) - 2] = '\0';
647 QByteArray member;
648 member.resize(qstrlen(slot) + qstrlen("_data()") + 1);
649 qsnprintf(member.data(), member.size(), "%s_data()", slot);
651 const int dataCount = table.dataCount();
652 localTags.reserve(dataCount);
653 for (int j = 0; j < dataCount; ++j)
654 localTags << QLatin1StringView(table.testData(j)->dataTag());
655
656 // Print all tag combinations:
657 if (gTable->dataCount() == 0) {
658 if (localTags.size() == 0) {
659 // No tags at all, so just print the test function:
660 fprintf(stream, "%s %s\n", currTestMetaObj->className(), slot);
661 } else {
662 // Only local tags, so print each of them:
663 for (int k = 0; k < localTags.size(); ++k)
664 fprintf(
665 stream, "%s %s %s\n",
666 currTestMetaObj->className(), slot, localTags.at(k).toLatin1().data());
667 }
668 } else {
669 for (int j = 0; j < gTable->dataCount(); ++j) {
670 if (localTags.size() == 0) {
671 // Only global tags, so print the current one:
672 fprintf(
673 stream, "%s %s __global__ %s\n",
674 currTestMetaObj->className(), slot, gTable->testData(j)->dataTag());
675 } else {
676 // Local and global tags, so print each of the local ones and
677 // the current global one:
678 for (int k = 0; k < localTags.size(); ++k)
679 fprintf(
680 stream, "%s %s %s __global__ %s\n", currTestMetaObj->className(), slot,
681 localTags.at(k).toLatin1().data(), gTable->testData(j)->dataTag());
682 }
683 }
684 }
685
686 delete[] slot;
687 }
688 }
689}
690
691static int qToInt(const char *str)
692{
693 char *pEnd;
694 int l = static_cast<int>(strtol(str, &pEnd, 10));
695 if (*pEnd != 0) {
696 fprintf(stderr, "Invalid numeric parameter: '%s'\n", str);
697 exit(1);
698 }
699 return l;
700}
701
702Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml)
703{
704 int logFormat = -1; // Not set
705 const char *logFilename = nullptr;
706
707 QTest::testFunctions.clear();
708 QTest::testTags.clear();
709
710#if defined(Q_OS_DARWIN) && defined(HAVE_XCTEST)
712 logFormat = QTestLog::XCTest;
713#endif
714
715 const char *testOptions =
716 " New-style logging options:\n"
717 " -o filename,format : Output results to file in the specified format\n"
718 " Use - to output to stdout\n"
719 " Valid formats are:\n"
720 " txt : Plain text\n"
721 " csv : CSV format (suitable for benchmarks)\n"
722 " junitxml : XML JUnit document\n"
723 " xml : XML document\n"
724 " lightxml : A stream of XML tags\n"
725 " teamcity : TeamCity format\n"
726 " tap : Test Anything Protocol\n"
727 "\n"
728 " *** Multiple loggers can be specified, but at most one can log to stdout.\n"
729 "\n"
730 " Old-style logging options:\n"
731 " -o filename : Write the output into file\n"
732 " -txt : Output results in Plain Text\n"
733 " -csv : Output results in a CSV format (suitable for benchmarks)\n"
734 " -junitxml : Output results as XML JUnit document\n"
735 " -xml : Output results as XML document\n"
736 " -lightxml : Output results as stream of XML tags\n"
737 " -teamcity : Output results in TeamCity format\n"
738 " -tap : Output results in Test Anything Protocol format\n"
739 "\n"
740 " *** If no output file is specified, stdout is assumed.\n"
741 " *** If no output format is specified, -txt is assumed.\n"
742 "\n"
743 " Test log detail options:\n"
744 " -silent : Log failures and fatal errors only\n"
745 " -v1 : Log the start of each testfunction\n"
746 " -v2 : Log each QVERIFY/QCOMPARE/QTEST (implies -v1)\n"
747 " -vs : Log every signal emission and resulting slot invocations\n"
748 "\n"
749 " *** The -silent and -v1 options only affect plain text output.\n"
750 "\n"
751 " Testing options:\n"
752 " -functions : Returns a list of current testfunctions\n"
753 " -datatags : Returns a list of current data tags.\n"
754 " A global data tag is preceded by ' __global__ '.\n"
755 " -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n"
756 " -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n"
757 " -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n"
758 " -maxwarnings n : Sets the maximum amount of messages to output.\n"
759 " 0 means unlimited, default: 2000\n"
760 " -nocrashhandler : Disables the crash handler. Useful for debugging crashes.\n"
761 "\n"
762 " Benchmarking options:\n"
763#if QT_CONFIG(valgrind)
764 " -callgrind : Use callgrind to time benchmarks\n"
765#endif
766#ifdef QTESTLIB_USE_PERF_EVENTS
767 " -perf : Use Linux perf events to time benchmarks\n"
768 " -perfcounter name : Use the counter named 'name'\n"
769 " -perfcounterlist : Lists the counters available\n"
770#endif
771#ifdef HAVE_TICK_COUNTER
772 " -tickcounter : Use CPU tick counters to time benchmarks\n"
773#endif
774 " -eventcounter : Counts events received during benchmarks\n"
775 " -minimumvalue n : Sets the minimum acceptable measurement value\n"
776 " -minimumtotal n : Sets the minimum acceptable total for repeated executions of a test function\n"
777 " -iterations n : Sets the number of accumulation iterations.\n"
778 " -median n : Sets the number of median iterations.\n"
779 " -vb : Print out verbose benchmarking information.\n";
780
781 for (int i = 1; i < argc; ++i) {
782 if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0
783 || strcmp(argv[i], "/?") == 0) {
784 printf(" Usage: %s [options] [testfunction[:testdata]]...\n"
785 " By default, all testfunctions will be run.\n\n"
786 "%s", argv[0], testOptions);
787
788 if (qml) {
789 printf("\n"
790 " QmlTest options:\n"
791 " -import dir : Specify an import directory.\n"
792 " -plugins dir : Specify a directory where to search for plugins.\n"
793 " -input dir/file : Specify the root directory for test cases or a single test case file.\n"
794 " -translation file : Specify the translation file.\n"
795 " -file-selector dir : Specify a file selector for the QML engine.\n"
796 );
797 }
798
799 printf("\n"
800 " -help : This help\n");
801 exit(0);
802 } else if (strcmp(argv[i], "-functions") == 0) {
803 if (qml) {
805 } else {
806 qPrintTestSlots(stdout);
807 exit(0);
808 }
809 } else if (strcmp(argv[i], "-datatags") == 0) {
810 if (!qml) {
811 qPrintDataTags(stdout);
812 exit(0);
813 }
814 } else if (strcmp(argv[i], "-txt") == 0) {
815 logFormat = QTestLog::Plain;
816 } else if (strcmp(argv[i], "-csv") == 0) {
817 logFormat = QTestLog::CSV;
818 } else if (strcmp(argv[i], "-junitxml") == 0) {
819 logFormat = QTestLog::JUnitXML;
820 } else if (strcmp(argv[i], "-xunitxml") == 0) {
821 fprintf(stderr, "WARNING: xunitxml is deprecated. Please use junitxml.\n");
822 logFormat = QTestLog::JUnitXML;
823 } else if (strcmp(argv[i], "-xml") == 0) {
824 logFormat = QTestLog::XML;
825 } else if (strcmp(argv[i], "-lightxml") == 0) {
826 logFormat = QTestLog::LightXML;
827 } else if (strcmp(argv[i], "-teamcity") == 0) {
828 logFormat = QTestLog::TeamCity;
829 } else if (strcmp(argv[i], "-tap") == 0) {
830 logFormat = QTestLog::TAP;
831 } else if (strcmp(argv[i], "-silent") == 0) {
833 } else if (strcmp(argv[i], "-v1") == 0) {
835 } else if (strcmp(argv[i], "-v2") == 0) {
837 } else if (strcmp(argv[i], "-vs") == 0) {
839 } else if (strcmp(argv[i], "-o") == 0) {
840 if (i + 1 >= argc) {
841 fprintf(stderr, "-o needs an extra parameter specifying the filename and optional format\n");
842 exit(1);
843 }
844 ++i;
845 // Do we have the old or new style -o option?
846 char *filename = new char[strlen(argv[i])+1];
847 char *format = new char[strlen(argv[i])+1];
848 if (sscanf(argv[i], "%[^,],%s", filename, format) == 1) {
849 // Old-style
850 logFilename = argv[i];
851 } else {
852 // New-style
853 if (strcmp(format, "txt") == 0)
854 logFormat = QTestLog::Plain;
855 else if (strcmp(format, "csv") == 0)
856 logFormat = QTestLog::CSV;
857 else if (strcmp(format, "lightxml") == 0)
858 logFormat = QTestLog::LightXML;
859 else if (strcmp(format, "xml") == 0)
860 logFormat = QTestLog::XML;
861 else if (strcmp(format, "junitxml") == 0)
862 logFormat = QTestLog::JUnitXML;
863 else if (strcmp(format, "xunitxml") == 0) {
864 fprintf(stderr, "WARNING: xunitxml is deprecated. Please use junitxml.\n");
865 logFormat = QTestLog::JUnitXML;
866 } else if (strcmp(format, "teamcity") == 0)
867 logFormat = QTestLog::TeamCity;
868 else if (strcmp(format, "tap") == 0)
869 logFormat = QTestLog::TAP;
870 else {
871 fprintf(stderr, "output format must be one of txt, csv, lightxml, xml, tap, teamcity or junitxml\n");
872 exit(1);
873 }
874 if (strcmp(filename, "-") == 0 && QTestLog::loggerUsingStdout()) {
875 fprintf(stderr, "only one logger can log to stdout\n");
876 exit(1);
877 }
878 QTestLog::addLogger(QTestLog::LogMode(logFormat), filename);
879 }
880 delete [] filename;
881 delete [] format;
882 } else if (strcmp(argv[i], "-eventdelay") == 0) {
883 if (i + 1 >= argc) {
884 fprintf(stderr, "-eventdelay needs an extra parameter to indicate the delay(ms)\n");
885 exit(1);
886 } else {
887 QTest::eventDelay = qToInt(argv[++i]);
888 }
889 } else if (strcmp(argv[i], "-keydelay") == 0) {
890 if (i + 1 >= argc) {
891 fprintf(stderr, "-keydelay needs an extra parameter to indicate the delay(ms)\n");
892 exit(1);
893 } else {
894 QTest::keyDelay = qToInt(argv[++i]);
895 }
896 } else if (strcmp(argv[i], "-mousedelay") == 0) {
897 if (i + 1 >= argc) {
898 fprintf(stderr, "-mousedelay needs an extra parameter to indicate the delay(ms)\n");
899 exit(1);
900 } else {
901 QTest::mouseDelay = qToInt(argv[++i]);
902 }
903 } else if (strcmp(argv[i], "-maxwarnings") == 0) {
904 if (i + 1 >= argc) {
905 fprintf(stderr, "-maxwarnings needs an extra parameter with the amount of warnings\n");
906 exit(1);
907 } else {
909 }
910 } else if (strcmp(argv[i], "-nocrashhandler") == 0) {
912#if QT_CONFIG(valgrind)
913 } else if (strcmp(argv[i], "-callgrind") == 0) {
915 fprintf(stderr,
916 "WARNING: Valgrind not found or too old. "
917 "Make sure it is installed and in your path. "
918 "Using the walltime measurer.\n");
919 } else if (QFileInfo(QDir::currentPath()).isWritable()) {
922 } else {
923 fprintf(stderr,
924 "WARNING: Current directory not writable. "
925 "Using the walltime measurer.\n");
926 }
927 } else if (strcmp(argv[i], "-callgrindchild") == 0) { // "private" option
931#endif
932#ifdef QTESTLIB_USE_PERF_EVENTS
933 } else if (strcmp(argv[i], "-perf") == 0) {
935 // perf available
937 } else {
938 fprintf(stderr, "WARNING: Linux perf events not available. Using the walltime measurer.\n");
939 }
940 } else if (strcmp(argv[i], "-perfcounter") == 0) {
941 if (i + 1 >= argc) {
942 fprintf(stderr, "-perfcounter needs an extra parameter with the name of the counter\n");
943 exit(1);
944 } else {
946 }
947 } else if (strcmp(argv[i], "-perfcounterlist") == 0) {
949 exit(0);
950#endif
951#ifdef HAVE_TICK_COUNTER
952 } else if (strcmp(argv[i], "-tickcounter") == 0) {
954#endif
955 } else if (strcmp(argv[i], "-eventcounter") == 0) {
957 } else if (strcmp(argv[i], "-minimumvalue") == 0) {
958 if (i + 1 >= argc) {
959 fprintf(stderr, "-minimumvalue needs an extra parameter to indicate the minimum time(ms)\n");
960 exit(1);
961 } else {
963 }
964 } else if (strcmp(argv[i], "-minimumtotal") == 0) {
965 if (i + 1 >= argc) {
966 fprintf(stderr, "-minimumtotal needs an extra parameter to indicate the minimum total measurement\n");
967 exit(1);
968 } else {
970 }
971 } else if (strcmp(argv[i], "-iterations") == 0) {
972 if (i + 1 >= argc) {
973 fprintf(stderr, "-iterations needs an extra parameter to indicate the number of iterations\n");
974 exit(1);
975 } else {
977 }
978 } else if (strcmp(argv[i], "-median") == 0) {
979 if (i + 1 >= argc) {
980 fprintf(stderr, "-median needs an extra parameter to indicate the number of median iterations\n");
981 exit(1);
982 } else {
984 }
985
986 } else if (strcmp(argv[i], "-vb") == 0) {
988#if defined(Q_OS_DARWIN)
989 } else if (strncmp(argv[i], "-Apple", 6) == 0) {
990 i += 1; // Skip Apple-specific user preferences
991 continue;
992# if defined(HAVE_XCTEST)
993 } else if (int skip = QXcodeTestLogger::parseCommandLineArgument(argv[i])) {
994 i += (skip - 1); // Eating argv[i] with a continue counts towards skips
995 continue;
996# endif
997#endif
998 } else if (argv[i][0] == '-') {
999 fprintf(stderr, "Unknown option: '%s'\n\n%s", argv[i], testOptions);
1000 if (qml) {
1001 fprintf(stderr, "\nqmltest related options:\n"
1002 " -import : Specify an import directory.\n"
1003 " -plugins : Specify a directory where to search for plugins.\n"
1004 " -input : Specify the root directory for test cases.\n"
1005 );
1006 }
1007
1008 fprintf(stderr, "\n"
1009 " -help : This help\n");
1010 exit(1);
1011 } else {
1012 // We can't check the availability of test functions until
1013 // we load the QML files. So just store the data for now.
1014 int colon = -1;
1015 int offset;
1016 for (offset = 0; argv[i][offset]; ++offset) {
1017 if (argv[i][offset] == ':') {
1018 if (argv[i][offset + 1] == ':') {
1019 // "::" is used as a test name separator.
1020 // e.g. "ClickTests::test_click:row1".
1021 ++offset;
1022 } else {
1023 colon = offset;
1024 break;
1025 }
1026 }
1027 }
1028 if (colon == -1) {
1031 } else {
1033 QString::fromLatin1(argv[i], colon);
1035 QString::fromLatin1(argv[i] + colon + 1);
1036 }
1037 }
1038 }
1039
1042
1043 // If no loggers were created by the long version of the -o command-line
1044 // option, but a logger was requested via the old-style option, add it.
1045 const bool explicitLoggerRequested = logFormat != -1;
1046 if (!QTestLog::hasLoggers() && explicitLoggerRequested)
1047 QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename);
1048
1049 bool addFallbackLogger = !explicitLoggerRequested;
1050
1051#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
1052 // Any explicitly requested loggers will be added by now, so we can check if they use stdout
1053 const bool safeToAddAppleLogger = !AppleUnifiedLogger::preventsStderrLogging() || !QTestLog::loggerUsingStdout();
1054 if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
1055 QTestLog::addLogger(QTestLog::Apple, nullptr);
1056 if (AppleUnifiedLogger::preventsStderrLogging() && !logFilename)
1057 addFallbackLogger = false; // Prevent plain test logger fallback below
1058 }
1059#endif
1060
1061 if (addFallbackLogger)
1063}
1064
1065// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
1066Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) {
1067 qtest_qParseArgs(argc, const_cast<const char *const *>(argv), qml);
1068}
1069
1071{
1072 const int count = container.size();
1073 if (count == 0)
1074 return {};
1075
1076 if (count == 1)
1077 return container.front();
1078
1079 QList<QList<QBenchmarkResult>> containerCopy = container;
1080 std::sort(containerCopy.begin(), containerCopy.end(),
1082 return a.first() < b.first();
1083 });
1084
1085 const int middle = count / 2;
1086
1087 // ### handle even-sized containers here by doing an arithmetic mean of the two middle items.
1088 return containerCopy.at(middle);
1089}
1090
1092{
1094 {
1096 }
1098 {
1100 }
1101};
1102
1103void TestMethods::invokeTestOnData(int index) const
1104{
1105 /* Benchmarking: for each median iteration*/
1106
1107 bool isBenchmark = false;
1109
1110 QList<QList<QBenchmarkResult>> resultsList;
1111 bool minimumTotalReached = false;
1112 do {
1114 if (i < 0)
1116
1117 /* Benchmarking: for each accumulation iteration*/
1118 bool invokeOk;
1119 do {
1120 QTest::inTestFunction = true;
1121 if (m_initMethod.isValid())
1123
1124 const bool initQuit =
1126 if (!initQuit) {
1130
1133
1134 invokeOk = m_methods[index].invoke(QTest::currentTestObject, Qt::DirectConnection);
1135 if (!invokeOk)
1136 QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__);
1137
1139 } else {
1140 invokeOk = false;
1141 }
1142
1143 QTest::inTestFunction = false;
1145
1146 if (!initQuit) {
1147 if (m_cleanupMethod.isValid())
1149
1150 // Process any deleteLater(), used by event-loop-based apps.
1151 // Fixes memleak reports.
1154 }
1155 // If the test isn't a benchmark, finalize the result after
1156 // cleanup() has finished (or init has lead us to skip the test).
1157 if (!isBenchmark)
1159
1160 // If this test method has a benchmark, repeat until all measurements are
1161 // acceptable.
1162 // The QBENCHMARK macro increases the number of iterations for each run until
1163 // this happens.
1164 } while (invokeOk && isBenchmark
1165 && QBenchmarkTestMethodData::current->resultsAccepted() == false
1167
1170 if (i > -1) // iteration -1 is the warmup iteration.
1172
1173 if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput &&
1175 // we only print the first result
1177 QString pattern = i < 0 ? "warmup stage result : %1"_L1
1178 : "accumulation stage result: %1"_L1;
1179 QTestLog::info(qPrintable(pattern.arg(first.measurement.value)), nullptr, 0);
1180 }
1181 }
1182
1183 // Verify if the minimum total measurement (for the first measurement)
1184 // was reached, if it was specified:
1185 if (QBenchmarkGlobalData::current->minimumTotal == -1) {
1186 minimumTotalReached = true;
1187 } else {
1188 auto addResult = [](qreal current, const QList<QBenchmarkResult> &r) {
1189 if (!r.isEmpty())
1190 current += r.first().measurement.value;
1191 return current;
1192 };
1193 const qreal total = std::accumulate(resultsList.begin(), resultsList.end(), 0.0, addResult);
1194 minimumTotalReached = (total >= QBenchmarkGlobalData::current->minimumTotal);
1195 }
1196 } while (isBenchmark
1197 && ((++i < QBenchmarkGlobalData::current->adjustMedianIterationCount()) || !minimumTotalReached)
1199
1200 // If the test is a benchmark, finalize the result after all iterations have finished.
1201 if (isBenchmark) {
1204 // Only report benchmark figures if the test passed
1205 if (testPassed && QBenchmarkTestMethodData::current->resultsAccepted())
1207 }
1208}
1209
1210#if QT_CONFIG(thread)
1211
1212class WatchDog : public QThread
1213{
1214 enum Expectation : std::size_t {
1215 // bits 0..1: state
1216 ThreadStart,
1217 TestFunctionStart,
1218 TestFunctionEnd,
1219 ThreadEnd,
1220
1221 // bits 2..: generation
1222 };
1223 static constexpr auto ExpectationMask = Expectation{ThreadStart | TestFunctionStart | TestFunctionEnd | ThreadEnd};
1224 static_assert(size_t(ExpectationMask) == 0x3);
1225 static constexpr size_t GenerationShift = 2;
1226
1227 static constexpr Expectation state(Expectation e) noexcept
1228 { return Expectation{e & ExpectationMask}; }
1229 static constexpr size_t generation(Expectation e) noexcept
1230 { return e >> GenerationShift; }
1231 static constexpr Expectation combine(Expectation e, size_t gen) noexcept
1232 { return Expectation{e | (gen << GenerationShift)}; }
1233
1234 bool waitFor(std::unique_lock<std::mutex> &m, Expectation e)
1235 {
1236 auto expectationChanged = [this, e] { return expecting.load(std::memory_order_relaxed) != e; };
1237 switch (state(e)) {
1238 case TestFunctionEnd:
1239 return waitCondition.wait_for(m, defaultTimeout(), expectationChanged);
1240 case ThreadStart:
1241 case ThreadEnd:
1242 case TestFunctionStart:
1243 waitCondition.wait(m, expectationChanged);
1244 return true;
1245 }
1246 Q_UNREACHABLE_RETURN(false);
1247 }
1248
1249 void setExpectation(Expectation e)
1250 {
1251 Q_ASSERT(generation(e) == 0); // no embedded generation allowed
1252 const auto locker = qt_scoped_lock(mutex);
1253 auto cur = expecting.load(std::memory_order_relaxed);
1254 auto gen = generation(cur);
1255 if (e == TestFunctionStart)
1256 ++gen;
1257 e = combine(e, gen);
1258 expecting.store(e, std::memory_order_relaxed);
1259 waitCondition.notify_all();
1260 }
1261
1262public:
1263 WatchDog()
1264 {
1265 setObjectName("QtTest Watchdog"_L1);
1266 auto locker = qt_unique_lock(mutex);
1267 expecting.store(ThreadStart, std::memory_order_relaxed);
1268 start();
1269 waitFor(locker, ThreadStart);
1270 }
1271
1272 ~WatchDog()
1273 {
1274 setExpectation(ThreadEnd);
1275 wait();
1276 }
1277
1278 void beginTest()
1279 {
1280 setExpectation(TestFunctionEnd);
1281 }
1282
1283 void testFinished()
1284 {
1285 setExpectation(TestFunctionStart);
1286 }
1287
1288 void run() override
1289 {
1290 auto locker = qt_unique_lock(mutex);
1291 expecting.store(TestFunctionStart, std::memory_order_release);
1292 waitCondition.notify_all();
1293 while (true) {
1294 Expectation e = expecting.load(std::memory_order_acquire);
1295 switch (state(e)) {
1296 case ThreadEnd:
1297 return;
1298 case ThreadStart:
1299 Q_UNREACHABLE();
1300 case TestFunctionStart:
1301 case TestFunctionEnd:
1302 if (Q_UNLIKELY(!waitFor(locker, e))) {
1303 fflush(stderr);
1306 qFatal("Test function timed out");
1307 }
1308 }
1309 }
1310 }
1311
1312private:
1313 std::mutex mutex;
1314 std::condition_variable waitCondition;
1315 std::atomic<Expectation> expecting;
1316};
1317
1318#else // !QT_CONFIG(thread)
1319
1320class WatchDog : public QObject
1321{
1322public:
1323 void beginTest() {};
1324 void testFinished() {};
1325};
1326
1327#endif // QT_CONFIG(thread)
1328
1329
1331 const QTestTable &lTable, const QTestTable &gTable)
1332{
1333 fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), tag.data());
1334 const int localDataCount = lTable.dataCount();
1335 if (localDataCount) {
1336 fputs("Available test-specific data tags:\n", stderr);
1337 for (int i = 0; i < localDataCount; ++i)
1338 fprintf(stderr, "\t%s\n", lTable.testData(i)->dataTag());
1339 }
1340 const int globalDataCount = gTable.dataCount();
1341 if (globalDataCount) {
1342 fputs("Available global data tags:\n", stderr);
1343 for (int i = 0; i < globalDataCount; ++i)
1344 fprintf(stderr, "\t%s\n", gTable.testData(i)->dataTag());
1345 }
1346 if (localDataCount == 0 && globalDataCount == 0)
1347 fputs("Function has no data tags\n", stderr);
1348}
1349
1359bool TestMethods::invokeTest(int index, QLatin1StringView tag, WatchDog *watchDog) const
1360{
1361 QBenchmarkTestMethodData benchmarkData;
1362 QBenchmarkTestMethodData::current = &benchmarkData;
1363
1364 const QByteArray &name = m_methods[index].name();
1366
1367 char member[512];
1369
1371
1372 const QTestTable *gTable = QTestTable::globalTestTable();
1373 const int globalDataCount = gTable->dataCount();
1374 int curGlobalDataIndex = 0;
1375 const auto globalDataTag = [gTable, globalDataCount](int index) {
1376 return globalDataCount ? gTable->testData(index)->dataTag() : nullptr;
1377 };
1378
1379 const auto dataTagMatches = [](QLatin1StringView tag, QLatin1StringView local,
1381 if (tag.isEmpty()) // No tag specified => run all data sets for this function
1382 return true;
1383 if (tag == local || tag == global) // Equal to either => run it
1384 return true;
1385 // Also allow global:local as a match:
1386 return tag.startsWith(global) && tag.endsWith(local) &&
1387 tag.size() == global.size() + 1 + local.size() &&
1388 tag[global.size()] == ':';
1389 };
1390 bool foundFunction = false;
1391
1392 /* For each entry in the global data table, do: */
1393 do {
1394 if (!gTable->isEmpty())
1395 QTestResult::setCurrentGlobalTestData(gTable->testData(curGlobalDataIndex));
1396
1397 if (curGlobalDataIndex == 0) {
1398 qsnprintf(member, 512, "%s_data()", name.constData());
1401 break;
1402 }
1403
1404 int curDataIndex = 0;
1405 const int dataCount = table.dataCount();
1406 const auto dataTag = [&table, dataCount](int index) {
1407 return dataCount ? table.testData(index)->dataTag() : nullptr;
1408 };
1409
1410 /* For each entry in this test's data table, do: */
1411 do {
1414 if (dataTagMatches(tag, QLatin1StringView(dataTag(curDataIndex)),
1415 QLatin1StringView(globalDataTag(curGlobalDataIndex)))) {
1416 foundFunction = true;
1417 QTestPrivate::checkBlackLists(name.constData(), dataTag(curDataIndex),
1418 globalDataTag(curGlobalDataIndex));
1419
1420 QTestDataSetter s(curDataIndex >= dataCount ? nullptr : table.testData(curDataIndex));
1421
1423 if (watchDog)
1424 watchDog->beginTest();
1425 QTest::lastMouseTimestamp += 500; // Maintain at least 500ms mouse event timestamps between each test function call
1426 invokeTestOnData(index);
1427 if (watchDog)
1428 watchDog->testFinished();
1429
1430 if (!tag.isEmpty() && !globalDataCount)
1431 break;
1432 }
1433 ++curDataIndex;
1434 } while (curDataIndex < dataCount);
1435
1437 ++curGlobalDataIndex;
1438 } while (curGlobalDataIndex < globalDataCount);
1439
1440 if (!tag.isEmpty() && !foundFunction) {
1442 QTestResult::addFailure(qPrintable("Data tag not found: %1"_L1.arg(tag)));
1443 }
1447
1448 return true;
1449}
1450
1451void *fetchData(QTestData *data, const char *tagName, int typeId)
1452{
1453 QTEST_ASSERT(typeId);
1454 QTEST_ASSERT_X(data, "QTest::fetchData()", "Test data requested, but no testdata available.");
1455 QTEST_ASSERT(data->parent());
1456
1457 int idx = data->parent()->indexOf(tagName);
1458
1459 if (Q_UNLIKELY(idx == -1 || idx >= data->dataCount())) {
1460 qFatal("QFETCH: Requested testdata '%s' not available, check your _data function.",
1461 tagName);
1462 }
1463
1464 if (Q_UNLIKELY(typeId != data->parent()->elementTypeId(idx))) {
1465 qFatal("Requested type '%s' does not match available type '%s'.",
1466 QMetaType(typeId).name(),
1467 QMetaType(data->parent()->elementTypeId(idx)).name());
1468 }
1469
1470 return data->data(idx);
1471}
1472
1476char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...)
1477{
1478 va_list ap;
1479 va_start(ap, numArguments);
1480
1482 arguments += prefix;
1483
1484 if (numArguments > 0) {
1485 arguments += va_arg(ap, const char *);
1486
1487 for (size_t i = 1; i < numArguments; ++i) {
1488 arguments += ", ";
1489 arguments += va_arg(ap, const char *);
1490 }
1491 }
1492
1493 va_end(ap);
1494 arguments += suffix;
1495 return qstrdup(arguments.constData());
1496}
1497
1509{
1510 if (length == 0)
1511 return qstrdup("");
1512
1513 /* We output at maximum about maxLen characters in order to avoid
1514 * running out of memory and flooding things when the byte array
1515 * is large.
1516 *
1517 * maxLen can't be for example 200 because Qt Test is sprinkled with fixed
1518 * size char arrays.
1519 * */
1520 const qsizetype maxLen = 50;
1521 const qsizetype len = qMin(maxLen, length);
1522 char *result = nullptr;
1523
1524 if (length > maxLen) {
1525 const qsizetype size = len * 3 + 4;
1526 result = new char[size];
1527
1528 char *const forElipsis = result + size - 5;
1529 forElipsis[0] = ' ';
1530 forElipsis[1] = '.';
1531 forElipsis[2] = '.';
1532 forElipsis[3] = '.';
1533 result[size - 1] = '\0';
1534 }
1535 else {
1536 const qsizetype size = len * 3;
1537 result = new char[size];
1538 result[size - 1] = '\0';
1539 }
1540
1541 qsizetype i = 0;
1542 qsizetype o = 0;
1543
1544 while (true) {
1545 const char at = ba[i];
1546
1547 result[o] = toHexUpper(at >> 4);
1548 ++o;
1549 result[o] = toHexUpper(at);
1550
1551 ++i;
1552 ++o;
1553 if (i == len)
1554 break;
1555 result[o] = ' ';
1556 ++o;
1557 }
1558
1559 return result;
1560}
1561
1568{
1569 bool trimmed = false;
1570 auto buffer = std::make_unique<char[]>(256);
1571 const char *end = p + length;
1572 char *dst = buffer.get();
1573
1574 bool lastWasHexEscape = false;
1575 *dst++ = '"';
1576 for ( ; p != end; ++p) {
1577 // we can add:
1578 // 1 byte: a single character
1579 // 2 bytes: a simple escape sequence (\n)
1580 // 3 bytes: "" and a character
1581 // 4 bytes: an hex escape sequence (\xHH)
1582 if (dst - buffer.get() > 246) {
1583 // plus the quote, the three dots and NUL, it's 255 in the worst case
1584 trimmed = true;
1585 break;
1586 }
1587
1588 // check if we need to insert "" to break an hex escape sequence
1589 if (Q_UNLIKELY(lastWasHexEscape)) {
1590 if (fromHex(*p) != -1) {
1591 // yes, insert it
1592 *dst++ = '"';
1593 *dst++ = '"';
1594 }
1595 lastWasHexEscape = false;
1596 }
1597
1598 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1599 *dst++ = *p;
1600 continue;
1601 }
1602
1603 // write as an escape sequence
1604 // this means we may advance dst to buffer.data() + 247 or 250
1605 *dst++ = '\\';
1606 switch (*p) {
1607 case 0x5c:
1608 case 0x22:
1609 *dst++ = uchar(*p);
1610 break;
1611 case 0x8:
1612 *dst++ = 'b';
1613 break;
1614 case 0xc:
1615 *dst++ = 'f';
1616 break;
1617 case 0xa:
1618 *dst++ = 'n';
1619 break;
1620 case 0xd:
1621 *dst++ = 'r';
1622 break;
1623 case 0x9:
1624 *dst++ = 't';
1625 break;
1626 default:
1627 // print as hex escape
1628 *dst++ = 'x';
1629 *dst++ = toHexUpper(uchar(*p) >> 4);
1630 *dst++ = toHexUpper(uchar(*p));
1631 lastWasHexEscape = true;
1632 break;
1633 }
1634 }
1635
1636 *dst++ = '"';
1637 if (trimmed) {
1638 *dst++ = '.';
1639 *dst++ = '.';
1640 *dst++ = '.';
1641 }
1642 *dst++ = '\0';
1643 return buffer.release();
1644}
1645
1654{
1655 auto p = string.utf16();
1656 auto length = string.size();
1657 // keep it simple for the vast majority of cases
1658 bool trimmed = false;
1659 auto buffer = std::make_unique<char[]>(256);
1660 const auto end = p + length;
1661 char *dst = buffer.get();
1662
1663 *dst++ = '"';
1664 for ( ; p != end; ++p) {
1665 if (dst - buffer.get() > 245) {
1666 // plus the quote, the three dots and NUL, it's 250, 251 or 255
1667 trimmed = true;
1668 break;
1669 }
1670
1671 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1672 *dst++ = *p;
1673 continue;
1674 }
1675
1676 // write as an escape sequence
1677 // this means we may advance dst to buffer.data() + 246 or 250
1678 *dst++ = '\\';
1679 switch (*p) {
1680 case 0x22:
1681 case 0x5c:
1682 *dst++ = uchar(*p);
1683 break;
1684 case 0x8:
1685 *dst++ = 'b';
1686 break;
1687 case 0xc:
1688 *dst++ = 'f';
1689 break;
1690 case 0xa:
1691 *dst++ = 'n';
1692 break;
1693 case 0xd:
1694 *dst++ = 'r';
1695 break;
1696 case 0x9:
1697 *dst++ = 't';
1698 break;
1699 default:
1700 *dst++ = 'u';
1701 *dst++ = toHexUpper(*p >> 12);
1702 *dst++ = toHexUpper(*p >> 8);
1703 *dst++ = toHexUpper(*p >> 4);
1704 *dst++ = toHexUpper(*p);
1705 }
1706 }
1707
1708 *dst++ = '"';
1709 if (trimmed) {
1710 *dst++ = '.';
1711 *dst++ = '.';
1712 *dst++ = '.';
1713 }
1714 *dst++ = '\0';
1715 return buffer.release();
1716}
1717
1719{
1720 const QMetaObject *metaObject = testObject->metaObject();
1723 if (m_initTestCaseDataMethod.isValid())
1724 m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection);
1725
1727 if (!alreadyDebugging()
1728#if QT_CONFIG(valgrind)
1730#endif
1731 ) {
1732 watchDog.reset(new WatchDog);
1733 }
1734
1736
1738 if (m_initTestCaseMethod.isValid())
1739 m_initTestCaseMethod.invoke(testObject, Qt::DirectConnection);
1740
1741 // finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy.
1742 const bool previousFailed = QTestResult::currentTestFailed();
1746
1747 if (!QTestResult::skipCurrentTest() && !previousFailed) {
1748 for (int i = 0, count = int(m_methods.size()); i < count; ++i) {
1749 const char *data = nullptr;
1750 if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
1751 data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
1752 const bool ok = invokeTest(i, QLatin1StringView(data), watchDog.data());
1753 delete [] data;
1754 if (!ok)
1755 break;
1756 }
1757 }
1758
1759 const bool wasSkipped = QTestResult::skipCurrentTest();
1762 QTestResult::setCurrentTestFunction("cleanupTestCase");
1763 if (m_cleanupTestCaseMethod.isValid())
1764 m_cleanupTestCaseMethod.invoke(testObject, Qt::DirectConnection);
1766 // Restore skip state as it affects decision on whether we passed:
1769 }
1772
1774}
1775
1776bool reportResult(bool success, qxp::function_ref<const char *()> lhs,
1777 qxp::function_ref<const char *()> rhs,
1778 const char *lhsExpr, const char *rhsExpr,
1779 ComparisonOperation op, const char *file, int line)
1780{
1781 return QTestResult::reportResult(success, lhs, rhs, lhsExpr, rhsExpr, op, file, line);
1782}
1783
1784} // namespace QTest
1785
1786namespace {
1787#if defined(Q_OS_WIN)
1788
1789// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
1790class DebugSymbolResolver
1791{
1792 Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
1793public:
1794 struct Symbol {
1795 Symbol() : name(nullptr), address(0) {}
1796
1797 const char *name; // Must be freed by caller.
1798 DWORD64 address;
1799 };
1800
1801 explicit DebugSymbolResolver(HANDLE process);
1802 ~DebugSymbolResolver() { cleanup(); }
1803
1804 bool isValid() const { return m_symFromAddr; }
1805
1806 Symbol resolveSymbol(DWORD64 address) const;
1807
1808private:
1809 // typedefs from DbgHelp.h/.dll
1810 struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
1811 ULONG SizeOfStruct;
1812 ULONG TypeIndex; // Type Index of symbol
1813 ULONG64 Reserved[2];
1814 ULONG Index;
1815 ULONG Size;
1816 ULONG64 ModBase; // Base Address of module comtaining this symbol
1817 ULONG Flags;
1818 ULONG64 Value; // Value of symbol, ValuePresent should be 1
1819 ULONG64 Address; // Address of symbol including base address of module
1820 ULONG Register; // register holding value or pointer to value
1821 ULONG Scope; // scope of the symbol
1822 ULONG Tag; // pdb classification
1823 ULONG NameLen; // Actual length of name
1824 ULONG MaxNameLen;
1825 CHAR Name[1]; // Name of symbol
1826 };
1827
1828 typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
1829 typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
1830
1831 void cleanup();
1832
1833 const HANDLE m_process;
1834 HMODULE m_dbgHelpLib;
1835 SymFromAddrType m_symFromAddr;
1836};
1837
1838void DebugSymbolResolver::cleanup()
1839{
1840 if (m_dbgHelpLib)
1841 FreeLibrary(m_dbgHelpLib);
1842 m_dbgHelpLib = 0;
1843 m_symFromAddr = nullptr;
1844}
1845
1846DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
1847 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
1848{
1849 bool success = false;
1850 m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
1851 if (m_dbgHelpLib) {
1852 SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
1853 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
1854 m_symFromAddr = reinterpret_cast<SymFromAddrType>(
1855 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
1856 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
1857 }
1858 if (!success)
1859 cleanup();
1860}
1861
1862DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
1863{
1864 // reserve additional buffer where SymFromAddr() will store the name
1865 struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
1866 enum { symbolNameLength = 255 };
1867
1868 char name[symbolNameLength + 1];
1869 };
1870
1871 Symbol result;
1872 if (!isValid())
1873 return result;
1874 NamedSymbolInfo symbolBuffer;
1875 memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
1876 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
1877 symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
1878 if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
1879 return result;
1880 result.name = qstrdup(symbolBuffer.Name);
1881 result.address = symbolBuffer.Address;
1882 return result;
1883}
1884
1885class WindowsFaultHandler
1886{
1887public:
1888 WindowsFaultHandler()
1889 {
1890# if !defined(Q_CC_MINGW)
1891 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
1892# endif
1893 SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
1894 SetUnhandledExceptionFilter(windowsFaultHandler);
1895 }
1896
1897private:
1898 static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
1899 {
1900 enum { maxStackFrames = 100 };
1901 char appName[MAX_PATH];
1902 if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
1903 appName[0] = 0;
1904 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
1905 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
1906 const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
1907 fprintf(stderr, "A crash occurred in %s.\n", appName);
1908 if (const char *name = QTest::currentTestFunction())
1909 fprintf(stderr, "While testing %s\n", name);
1910 fprintf(stderr, "Function time: %dms Total time: %dms\n\n"
1911 "Exception address: 0x%p\n"
1912 "Exception code : 0x%lx\n",
1913 msecsFunctionTime, msecsTotalTime, exceptionAddress,
1914 exInfo->ExceptionRecord->ExceptionCode);
1915
1916 DebugSymbolResolver resolver(GetCurrentProcess());
1917 if (resolver.isValid()) {
1918 DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
1919 if (exceptionSymbol.name) {
1920 fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name);
1921 delete [] exceptionSymbol.name;
1922 }
1923 void *stack[maxStackFrames];
1924 fputs("\nStack:\n", stderr);
1925 const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
1926 for (unsigned f = 0; f < frameCount; ++f) {
1927 DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
1928 if (symbol.name) {
1929 fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
1930 delete [] symbol.name;
1931 } else {
1932 fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1);
1933 }
1934 }
1935 }
1936
1937 fputc('\n', stderr);
1938
1939 return EXCEPTION_EXECUTE_HANDLER;
1940 }
1941};
1942using FatalSignalHandler = WindowsFaultHandler;
1943
1944#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1945class FatalSignalHandler
1946{
1947public:
1948# define OUR_SIGNALS(F) \
1949 F(HUP) \
1950 F(INT) \
1951 F(QUIT) \
1952 F(ABRT) \
1953 F(ILL) \
1954 F(BUS) \
1955 F(FPE) \
1956 F(SEGV) \
1957 F(PIPE) \
1958 F(TERM) \
1959
1960# define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
1961# define ENUMERATE_SIGNALS(S) SIG ## S,
1962 static const char *signalName(int signum) noexcept
1963 {
1964 switch (signum) {
1965 OUR_SIGNALS(CASE_LABEL)
1966 }
1967
1968# if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
1969 // get the other signal names from glibc 2.32
1970 // (accessing the sys_sigabbrev variable causes linker warnings)
1971 if (const char *p = sigabbrev_np(signum))
1972 return p;
1973# endif
1974 return "???";
1975 }
1976 static constexpr std::array fatalSignals = {
1977 OUR_SIGNALS(ENUMERATE_SIGNALS)
1978 };
1979# undef CASE_LABEL
1980# undef ENUMERATE_SIGNALS
1981
1982 static constexpr std::array crashingSignals = {
1983 // Crash signals are special, because if we return from the handler
1984 // without adjusting the machine state, the same instruction that
1985 // originally caused the crash will get re-executed and will thus cause
1986 // the same crash again. This is useful if our parent process logs the
1987 // exit result or if core dumps are enabled: the core file will point
1988 // to the actual instruction that crashed.
1989 SIGILL, SIGBUS, SIGFPE, SIGSEGV
1990 };
1991 using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
1992
1993 FatalSignalHandler()
1994 {
1995 pauseOnCrash = qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH");
1996 struct sigaction act;
1997 memset(&act, 0, sizeof(act));
1998 act.sa_handler = SIG_DFL;
1999 oldActions().fill(act);
2000
2001 // Remove the handler after it is invoked.
2002 act.sa_flags = SA_RESETHAND | setupAlternateStack();
2003
2004# ifdef SA_SIGINFO
2005 act.sa_flags |= SA_SIGINFO;
2006 act.sa_sigaction = FatalSignalHandler::actionHandler;
2007# else
2008 act.sa_handler = FatalSignalHandler::regularHandler;
2009# endif
2010
2011 // Block all fatal signals in our signal handler so we don't try to close
2012 // the testlog twice.
2013 sigemptyset(&act.sa_mask);
2014 for (int signal : fatalSignals)
2015 sigaddset(&act.sa_mask, signal);
2016
2017 for (size_t i = 0; i < fatalSignals.size(); ++i)
2018 sigaction(fatalSignals[i], &act, &oldActions()[i]);
2019 }
2020
2021 ~FatalSignalHandler()
2022 {
2023 // Restore the default signal handlers in place of ours.
2024 // If ours has been replaced, leave the replacement alone.
2025 auto isOurs = [](const struct sigaction &old) {
2026# ifdef SA_SIGINFO
2027 return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler;
2028# else
2029 return old.sa_handler == FatalSignalHandler::regularHandler;
2030# endif
2031 };
2032 struct sigaction action;
2033
2034 for (size_t i = 0; i < fatalSignals.size(); ++i) {
2035 struct sigaction &act = oldActions()[i];
2036 if (act.sa_flags == 0 && act.sa_handler == SIG_DFL)
2037 continue; // Already the default
2038 if (sigaction(fatalSignals[i], nullptr, &action))
2039 continue; // Failed to query present handler
2040 if (isOurs(action))
2041 sigaction(fatalSignals[i], &act, nullptr);
2042 }
2043
2044 freeAlternateStack();
2045 }
2046
2047private:
2048 Q_DISABLE_COPY_MOVE(FatalSignalHandler)
2049
2050 static OldActionsArray &oldActions()
2051 {
2052 Q_CONSTINIT static OldActionsArray oldActions {};
2053 return oldActions;
2054 }
2055
2056 auto alternateStackSize()
2057 {
2058 struct R { size_t size, pageSize; };
2059 static constexpr size_t MinStackSize = 32 * 1024;
2060 size_t pageSize = sysconf(_SC_PAGESIZE);
2061 size_t size = SIGSTKSZ;
2062 if (size < MinStackSize) {
2063 size = MinStackSize;
2064 } else {
2065 // round up to a page
2066 size = (size + pageSize - 1) & -pageSize;
2067 }
2068
2069 return R{ size + pageSize, pageSize };
2070 }
2071
2072 int setupAlternateStack()
2073 {
2074 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
2075 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
2076# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
2077 // Let the signal handlers use an alternate stack
2078 // This is necessary if SIGSEGV is to catch a stack overflow
2079 auto r = alternateStackSize();
2080 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
2081# ifdef MAP_STACK
2082 flags |= MAP_STACK;
2083# endif
2084 alternateStackBase = mmap(nullptr, r.size, PROT_READ | PROT_WRITE, flags, -1, 0);
2085 if (alternateStackBase == MAP_FAILED)
2086 return 0;
2087
2088 // mark the bottom page inaccessible, to catch a handler stack overflow
2089 (void) mprotect(alternateStackBase, r.pageSize, PROT_NONE);
2090
2091 stack_t stack;
2092 stack.ss_flags = 0;
2093 stack.ss_size = r.size - r.pageSize;
2094 stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
2095 sigaltstack(&stack, nullptr);
2096 return SA_ONSTACK;
2097# else
2098 return 0;
2099# endif
2100 }
2101
2102 void freeAlternateStack()
2103 {
2104# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
2105 if (alternateStackBase != MAP_FAILED) {
2106 stack_t stack = {};
2107 stack.ss_flags = SS_DISABLE;
2108 sigaltstack(&stack, nullptr);
2109 munmap(alternateStackBase, alternateStackSize().size);
2110 }
2111# endif
2112 }
2113
2114 template <typename T> static
2115 std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().si_uid) >= 1>
2116 printSentSignalInfo(T *info)
2117 {
2118 writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid),
2119 " UID ", asyncSafeToString(info->si_uid));
2120 }
2121 static void printSentSignalInfo(...) {}
2122
2123 template <typename T> static
2124 std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1> printCrashingSignalInfo(T *info)
2125 {
2126 using HexString = std::array<char, sizeof(quintptr) * 2>;
2127 auto toHexString = [](quintptr u, HexString &&r = {}) {
2128 int shift = sizeof(quintptr) * 8 - 4;
2129 for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4)
2131 struct iovec vec;
2132 vec.iov_base = r.data();
2133 vec.iov_len = r.size();
2134 return vec;
2135 };
2136 writeToStderr(", code ", asyncSafeToString(info->si_code),
2137 ", for address 0x", toHexString(quintptr(info->si_addr)));
2138 }
2139 static void printCrashingSignalInfo(...) {}
2140
2141 static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */)
2142 {
2143 writeToStderr("Received signal ", asyncSafeToString(signum),
2144 " (SIG", signalName(signum), ")");
2145
2146 bool isCrashingSignal =
2147 std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
2148 if (isCrashingSignal && (!info || info->si_code <= 0))
2149 isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
2150 if (isCrashingSignal)
2151 printCrashingSignalInfo(info);
2152 else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
2153 printSentSignalInfo(info);
2154
2156 if (signum != SIGINT) {
2158 if (pauseOnCrash) {
2159 writeToStderr("Pausing process ", asyncSafeToString(getpid()),
2160 " for debugging\n");
2161 raise(SIGSTOP);
2162 }
2163 }
2164
2165 // chain back to the previous handler, if any
2166 for (size_t i = 0; i < fatalSignals.size(); ++i) {
2167 struct sigaction &act = oldActions()[i];
2168 if (signum != fatalSignals[i])
2169 continue;
2170
2171 // restore the handler (if SA_RESETHAND hasn't done the job for us)
2172 if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
2173 (void) sigaction(signum, &act, nullptr);
2174
2175 if (!isCrashingSignal)
2176 raise(signum);
2177
2178 // signal is blocked, so it'll be delivered when we return
2179 return;
2180 }
2181
2182 // we shouldn't reach here!
2183 std::abort();
2184 }
2185
2186 [[maybe_unused]] static void regularHandler(int signum)
2187 {
2188 actionHandler(signum, nullptr, nullptr);
2189 }
2190
2191 void *alternateStackBase = MAP_FAILED;
2192 static bool pauseOnCrash;
2193};
2194bool FatalSignalHandler::pauseOnCrash = false;
2195#else // Q_OS_WASM or weird systems
2196class FatalSignalHandler {};
2197#endif // Q_OS_* choice
2198
2199} // unnamed namespace
2200
2201static void initEnvironment()
2202{
2203 qputenv("QT_QTESTLIB_RUNNING", "1");
2204}
2205
2244int QTest::qExec(QObject *testObject, int argc, char **argv)
2245{
2246 // NB: QtQuick's testing recombines qInit(), qRun() and qCleanup() to
2247 // provide a replacement for qExec() that calls qRun() once for each
2248 // built-in style. So think twice about moving parts between these three
2249 // functions, as doing so may mess up QtQuick's testing.
2250 qInit(testObject, argc, argv);
2251 int ret = qRun();
2252 qCleanup();
2253 return ret;
2254}
2255
2258void QTest::qInit(QObject *testObject, int argc, char **argv)
2259{
2263
2264#if defined(Q_OS_MACOS)
2265 // Don't restore saved window state for auto tests
2267
2268 // Disable App Nap which may cause tests to stall
2269 QTestPrivate::AppNapDisabler appNapDisabler;
2270
2271 if (qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0)) {
2272 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
2273 kIOPMAssertionLevelOn, CFSTR("QtTest running tests"),
2274 &macPowerSavingDisabled);
2275 }
2276#endif
2277
2280
2284
2285 const QMetaObject *metaObject = testObject->metaObject();
2287
2289 if (argc > 0)
2291
2292 qtest_qParseArgs(argc, argv, false);
2293
2294#if QT_CONFIG(valgrind)
2296#endif
2297 {
2300 }
2301}
2302
2306{
2308
2309#if QT_CONFIG(valgrind)
2310 int callgrindChildExitCode = 0;
2311#endif
2312
2313#ifndef QT_NO_EXCEPTIONS
2314 try {
2315#endif
2316
2317#if QT_CONFIG(valgrind)
2319 if (Q_UNLIKELY(!qApp))
2320 qFatal("QtTest: -callgrind option is not available with QTEST_APPLESS_MAIN");
2321
2322 const QStringList origAppArgs(QCoreApplication::arguments());
2323 if (!QBenchmarkValgrindUtils::rerunThroughCallgrind(origAppArgs, callgrindChildExitCode))
2324 return -1;
2325
2327
2328 } else
2329#endif
2330 {
2331 std::optional<FatalSignalHandler> handler;
2333 if (!noCrashHandler)
2334 handler.emplace();
2335
2336 bool seenBad = false;
2337 TestMethods::MetaMethods commandLineMethods;
2338 commandLineMethods.reserve(static_cast<size_t>(QTest::testFunctions.size()));
2339 for (const QString &tf : std::as_const(QTest::testFunctions)) {
2340 const QByteArray tfB = tf.toLatin1();
2341 const QByteArray signature = tfB + QByteArrayLiteral("()");
2342 QMetaMethod m = TestMethods::findMethod(currentTestObject, signature.constData());
2343 if (m.isValid() && isValidSlot(m)) {
2344 commandLineMethods.push_back(m);
2345 } else {
2346 fprintf(stderr, "Unknown test function: '%s'.", tfB.constData());
2347 if (!qPrintTestSlots(stderr, tfB.constData(), " Possible matches:\n"))
2348 fputc('\n', stderr);
2350 QTestResult::addFailure(qPrintable("Function not found: %1"_L1.arg(tf)));
2352 // Ditch the tag that came with tf as test function:
2353 QTest::testTags.remove(commandLineMethods.size());
2354 seenBad = true;
2355 }
2356 }
2357 if (seenBad) {
2358 // Provide relevant help to do better next time:
2359 fprintf(stderr, "\n%s -functions\nlists all available test functions.\n\n",
2361 if (commandLineMethods.empty()) // All requested functions missing.
2362 return 1;
2363 }
2364 TestMethods test(currentTestObject, std::move(commandLineMethods));
2366 }
2367
2368#ifndef QT_NO_EXCEPTIONS
2369 } catch (...) {
2370 QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__);
2374 }
2375
2376 qCleanup();
2377
2378 // Re-throw exception to make debugging easier
2379 throw;
2380 return 1;
2381 }
2382#endif
2383
2384#if QT_CONFIG(valgrind)
2386 return callgrindChildExitCode;
2387#endif
2388 // make sure our exit code is never going above 127
2389 // since that could wrap and indicate 0 test fails
2390 return qMin(QTestLog::failCount(), 127);
2391}
2392
2396{
2397 currentTestObject = nullptr;
2398
2399#if QT_CONFIG(valgrind)
2401#endif
2402 {
2405 }
2406
2409
2410#if defined(Q_OS_MACOS)
2411 IOPMAssertionRelease(macPowerSavingDisabled);
2412#endif
2413}
2414
2415#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
2426void QTest::qRegisterTestCase(const QString &name, TestEntryFunction entryFunction)
2427{
2429}
2430
2431QList<QString> QTest::qGetTestCaseNames()
2432{
2434}
2435
2436QTest::TestEntryFunction QTest::qGetTestCaseEntryFunction(const QString& name)
2437{
2439}
2440
2441#endif // QT_CONFIG(batch_test_support)
2442
2451{
2452 const int argc = arguments.size();
2453 QVarLengthArray<char *> argv(argc);
2454
2456 args.reserve(argc);
2457
2458 for (int i = 0; i < argc; ++i)
2459 {
2460 args.append(arguments.at(i).toLocal8Bit().constData());
2461 argv[i] = args.last().data();
2462 }
2463
2464 return qExec(testObject, argc, argv.data());
2465}
2466
2469void QTest::qFail(const char *message, const char *file, int line)
2470{
2472}
2473
2476bool QTest::qVerify(bool statement, const char *statementStr, const char *description,
2477 const char *file, int line)
2478{
2479 return QTestResult::verify(statement, statementStr, description, file, line);
2480}
2481
2485void QTest::qSkip(const char *message, const char *file, int line)
2486{
2489}
2490
2494bool QTest::qExpectFail(const char *dataIndex, const char *comment,
2495 QTest::TestFailMode mode, const char *file, int line)
2496{
2497 return QTestResult::expectFail(dataIndex, qstrdup(comment), mode, file, line);
2498}
2499
2516void QTest::qCaught(const char *expected, const char *what, const char *file, int line)
2517{
2518 auto message = [&] {
2519 const auto exType = what ? "std::" : "unknown ";
2520 const auto ofType = expected ? " of type " : "";
2521 const auto no = expected ? "an" : "no";
2522 const auto withMsg = what ? " with message " : "";
2523 const auto protect = [](const char *s) { return s ? s : ""; };
2524
2525 return QString::asprintf("Expected %s exception%s%s to be thrown, "
2526 "but caught %sexception%s%s",
2527 no, ofType, protect(expected),
2528 exType, withMsg, protect(what));
2529 };
2530 qFail(message().toUtf8().constData(), file, line);
2531}
2532
2533
2534#if QT_DEPRECATED_SINCE(6, 3)
2539void QTest::qWarn(const char *message, const char *file, int line)
2540{
2542}
2543#endif
2544
2562{
2564}
2565
2566#if QT_CONFIG(regularexpression)
2582void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
2583{
2584 QTestLog::ignoreMessage(type, messagePattern);
2585}
2586#endif // QT_CONFIG(regularexpression)
2587
2597{
2599}
2600
2601#if QT_CONFIG(regularexpression)
2644void QTest::failOnWarning(const QRegularExpression &messagePattern)
2645{
2646 QTestLog::failOnWarning(messagePattern);
2647}
2648#endif // QT_CONFIG(regularexpression)
2649
2653#ifdef Q_OS_WIN
2654static inline bool isWindowsBuildDirectory(const QString &dirName)
2655{
2656 return dirName.compare("Debug"_L1, Qt::CaseInsensitive) == 0
2657 || dirName.compare("Release"_L1, Qt::CaseInsensitive) == 0;
2658}
2659#endif
2660
2661#if QT_CONFIG(temporaryfile)
2672QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
2673{
2674 QSharedPointer<QTemporaryDir> result; // null until success, then == tempDir
2675
2677
2678 tempDir->setAutoRemove(true);
2679
2680 if (!tempDir->isValid())
2681 return result;
2682
2683 const QString dataPath = tempDir->path();
2684 const QString resourcePath = u':' + dirName;
2685 const QFileInfo fileInfo(resourcePath);
2686
2687 if (!fileInfo.isDir()) {
2688 qWarning("Resource path '%s' is not a directory.", qPrintable(resourcePath));
2689 return result;
2690 }
2691
2693 if (!it.hasNext()) {
2694 qWarning("Resource directory '%s' is empty.", qPrintable(resourcePath));
2695 return result;
2696 }
2697
2698 while (it.hasNext()) {
2699 QFileInfo fileInfo = it.nextFileInfo();
2700
2701 if (!fileInfo.isDir()) {
2702 const QString destination = dataPath + u'/' + QStringView{fileInfo.filePath()}.mid(resourcePath.size());
2703 QFileInfo destinationFileInfo(destination);
2704 QDir().mkpath(destinationFileInfo.path());
2705 if (!QFile::copy(fileInfo.filePath(), destination)) {
2706 qWarning("Failed to copy '%s'.", qPrintable(fileInfo.filePath()));
2707 return result;
2708 }
2710 qWarning("Failed to set permissions on '%s'.", qPrintable(destination));
2711 return result;
2712 }
2713 }
2714 }
2715
2716 result = std::move(tempDir);
2717
2718 return result;
2719}
2720#endif // QT_CONFIG(temporaryfile)
2721
2725QString QTest::qFindTestData(const QString& base, const char *file, int line, const char *builddir,
2726 const char *sourcedir)
2727{
2728 QString found;
2729
2730 // Testdata priorities:
2731
2732 // 1. relative to test binary.
2733 if (qApp) {
2735 if (binDirectory.exists(base)) {
2736 found = binDirectory.absoluteFilePath(base);
2737 }
2738#ifdef Q_OS_WIN
2739 // Windows: The executable is typically located in one of the
2740 // 'Release' or 'Debug' directories.
2741 else if (isWindowsBuildDirectory(binDirectory.dirName())
2742 && binDirectory.cdUp() && binDirectory.exists(base)) {
2743 found = binDirectory.absoluteFilePath(base);
2744 }
2745#endif // Q_OS_WIN
2746 else if (QTestLog::verboseLevel() >= 2) {
2748 QTestLog::info(qPrintable("testdata %1 not found relative to test binary [%2]; "
2749 "checking next location"_L1.arg(base, candidate)),
2750 file, line);
2751 }
2752 }
2753
2754 // 2. installed path.
2755 if (found.isEmpty()) {
2756 const char *testObjectName = QTestResult::currentTestObjectName();
2757 if (testObjectName) {
2759 const QString candidate = "%1/%2/%3"_L1
2760 .arg(testsPath, QFile::decodeName(testObjectName).toLower(), base);
2761 if (QFileInfo::exists(candidate)) {
2762 found = candidate;
2763 } else if (QTestLog::verboseLevel() >= 2) {
2764 QTestLog::info(qPrintable("testdata %1 not found in tests install path [%2]; "
2765 "checking next location"_L1
2766 .arg(base, QDir::toNativeSeparators(candidate))),
2767 file, line);
2768 }
2769 }
2770 }
2771
2772 // 3. relative to test source.
2773 if (found.isEmpty() && qstrncmp(file, ":/", 2) != 0) {
2774 // srcdir is the directory containing the calling source file.
2776
2777 // If the srcdir is relative, that means it is relative to the current working
2778 // directory of the compiler at compile time, which should be passed in as `builddir'.
2779 if (!srcdir.isAbsolute() && builddir)
2780 srcdir.setFile(QFile::decodeName(builddir) + u'/' + srcdir.filePath());
2781
2782 const QString canonicalPath = srcdir.canonicalFilePath();
2783 const QString candidate = "%1/%2"_L1.arg(canonicalPath, base);
2784 if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) {
2785 found = candidate;
2786 } else if (QTestLog::verboseLevel() >= 2) {
2788 "testdata %1 not found relative to source path [%2]"_L1
2789 .arg(base, QDir::toNativeSeparators(candidate))),
2790 file, line);
2791 }
2792 }
2793
2794 // 4. Try resources
2795 if (found.isEmpty()) {
2796 const QString candidate = ":/%1"_L1.arg(base);
2797 if (QFileInfo::exists(candidate)) {
2798 found = candidate;
2799 } else if (QTestLog::verboseLevel() >= 2) {
2801 "testdata %1 not found in resources [%2]"_L1
2802 .arg(base, QDir::toNativeSeparators(candidate))),
2803 file, line);
2804 }
2805 }
2806
2807 // 5. Try current directory
2808 if (found.isEmpty()) {
2809 const QString candidate = QDir::currentPath() + u'/' + base;
2810 if (QFileInfo::exists(candidate)) {
2811 found = candidate;
2812 } else if (QTestLog::verboseLevel() >= 2) {
2814 "testdata %1 not found in current directory [%2]"_L1
2815 .arg(base, QDir::toNativeSeparators(candidate))),
2816 file, line);
2817 }
2818 }
2819
2820 // 6. Try main source directory
2821 if (found.isEmpty()) {
2822 const QString candidate = QTest::mainSourcePath % u'/' % base;
2823 if (QFileInfo::exists(candidate)) {
2824 found = candidate;
2825 } else if (QTestLog::verboseLevel() >= 2) {
2827 "testdata %1 not found in main source directory [%2]"_L1
2828 .arg(base, QDir::toNativeSeparators(candidate))),
2829 file, line);
2830 }
2831 }
2832
2833 // 7. Try the supplied source directory
2834 if (found.isEmpty() && sourcedir) {
2835 const QString candidate = QFile::decodeName(sourcedir) % u'/' % base;
2836 if (QFileInfo::exists(candidate)) {
2837 found = candidate;
2838 } else if (QTestLog::verboseLevel() >= 2) {
2840 "testdata %1 not found in supplied source directory [%2]"_L1
2841 .arg(base, QDir::toNativeSeparators(candidate))),
2842 file, line);
2843 }
2844 }
2845
2846
2847 if (found.isEmpty()) {
2849 "testdata %1 could not be located!"_L1.arg(base)),
2850 file, line);
2851 } else if (QTestLog::verboseLevel() >= 1) {
2853 "testdata %1 was located at %2"_L1.arg(base, QDir::toNativeSeparators(found))),
2854 file, line);
2855 }
2856
2857 return found;
2858}
2859
2862QString QTest::qFindTestData(const char *base, const char *file, int line, const char *builddir,
2863 const char *sourcedir)
2864{
2865 return qFindTestData(QFile::decodeName(base), file, line, builddir, sourcedir);
2866}
2867
2870void *QTest::qData(const char *tagName, int typeId)
2871{
2872 return fetchData(QTestResult::currentTestData(), tagName, typeId);
2873}
2874
2877void *QTest::qGlobalData(const char *tagName, int typeId)
2878{
2879 return fetchData(QTestResult::currentGlobalTestData(), tagName, typeId);
2880}
2881
2884void *QTest::qElementData(const char *tagName, int metaTypeId)
2885{
2886 QTEST_ASSERT(tagName);
2889 QTEST_ASSERT(data->parent());
2890
2891 int idx = data->parent()->indexOf(tagName);
2892 QTEST_ASSERT(idx != -1);
2893 QTEST_ASSERT(data->parent()->elementTypeId(idx) == metaTypeId);
2894
2895 return data->data(data->parent()->indexOf(tagName));
2896}
2897
2900void QTest::addColumnInternal(int id, const char *name)
2901{
2903 QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot.");
2904
2905 tbl->addColumn(id, name);
2906}
2907
2928QTestData &QTest::newRow(const char *dataTag)
2929{
2930 QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null");
2932 QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot.");
2933 QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()",
2934 "Must add columns before attempting to add rows.");
2935
2936 return *tbl->newData(dataTag);
2937}
2938
2966{
2967 QTEST_ASSERT_X(format, "QTest::addRow()", "Format string cannot be null");
2969 QTEST_ASSERT_X(tbl, "QTest::addRow()", "Cannot add testdata outside of a _data slot.");
2970 QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()",
2971 "Must add columns before attempting to add rows.");
2972
2973 char buf[1024];
2974
2975 va_list va;
2976 va_start(va, format);
2977 // we don't care about failures, we accept truncation, as well as trailing garbage.
2978 // Names with more than 1K characters are nonsense, anyway.
2979 (void)qvsnprintf(buf, sizeof buf, format, va);
2980 buf[sizeof buf - 1] = '\0';
2981 va_end(va);
2982
2983 return *tbl->newData(buf);
2984}
2985
3014{
3016}
3017
3026{
3028}
3029
3035{
3037}
3038
3045{
3047}
3048
3063{
3065}
3066
3076{
3077 return QTest::inTestFunction;
3078}
3079
3083{
3084 return currentTestObject;
3085}
3086
3089void QTest::setMainSourcePath(const char *file, const char *builddir)
3090{
3091 QString mainSourceFile = QFile::decodeName(file);
3092 QFileInfo fi;
3093 if (builddir)
3094 fi.setFile(QDir(QFile::decodeName(builddir)), mainSourceFile);
3095 else
3096 fi.setFile(mainSourceFile);
3098}
3099
3100#if QT_DEPRECATED_SINCE(6, 4)
3119bool QTest::compare_helper(bool success, const char *failureMsg,
3120 char *actualVal, char *expectedVal,
3121 const char *actual, const char *expected,
3122 const char *file, int line)
3123{
3124 return QTestResult::compare(success, failureMsg, actualVal, expectedVal,
3125 actual, expected, file, line);
3126}
3127#endif // QT_DEPRECATED_SINCE(6, 4)
3128
3142bool QTest::compare_helper(bool success, const char *failureMsg,
3143 qxp::function_ref<const char *()> actualVal,
3144 qxp::function_ref<const char *()> expectedVal,
3145 const char *actual, const char *expected,
3146 const char *file, int line)
3147{
3148 return QTestResult::reportResult(success, actualVal, expectedVal, actual, expected,
3150 file, line, failureMsg);
3151}
3152
3163bool QTest::compare_helper(bool success, const char *failureMsg, const char *actual,
3164 const char *expected, const char *file, int line)
3165{
3166 return QTestResult::compare(success, failureMsg, actual, expected, file, line);
3167}
3168
3169template <typename T>
3170static bool floatingCompare(const T &actual, const T &expected)
3171{
3172 switch (qFpClassify(expected))
3173 {
3174 case FP_INFINITE:
3175 return (expected < 0) == (actual < 0) && qFpClassify(actual) == FP_INFINITE;
3176 case FP_NAN:
3177 return qFpClassify(actual) == FP_NAN;
3178 default:
3179 if (!qFuzzyIsNull(expected))
3180 return qFuzzyCompare(actual, expected);
3181 Q_FALLTHROUGH();
3182 case FP_SUBNORMAL: // subnormal is always fuzzily null
3183 case FP_ZERO:
3184 return qFuzzyIsNull(actual);
3185 }
3186}
3187
3191bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
3192 const char *file, int line)
3193{
3195 "Compared qfloat16s are not the same (fuzzy compare)",
3196 [&t1] { return toString(t1); }, [&t2] { return toString(t2); },
3197 actual, expected, file, line);
3198}
3199
3203bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const char *expected,
3204 const char *file, int line)
3205{
3207 "Compared floats are not the same (fuzzy compare)",
3208 t1, t2, actual, expected, file, line);
3209}
3210
3214bool QTest::qCompare(double const &t1, double const &t2, const char *actual, const char *expected,
3215 const char *file, int line)
3216{
3218 "Compared doubles are not the same (fuzzy compare)",
3219 t1, t2, actual, expected, file, line);
3220}
3221
3226bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
3227 const char *file, int line)
3228{
3229 return QTestResult::compare(t1 == t2,
3230 "Compared values are not the same",
3231 t1, t2, actual, expected, file, line);
3232}
3233
3234#if QT_POINTER_SIZE == 8
3240bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected,
3241 const char *file, int line)
3242{
3243 return QTestResult::compare(t1 == t2,
3244 "Compared values are not the same",
3245 t1, t2, actual, expected, file, line);
3246}
3247#endif // QT_POINTER_SIZE == 8
3248
3253bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected,
3254 const char *file, int line)
3255{
3256 return QTestResult::compare(t1 == t2,
3257 "Compared values are not the same",
3258 t1, t2, actual, expected, file, line);
3259}
3260
3265bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected,
3266 const char *file, int line)
3267{
3268 return QTestResult::compare(t1 == t2,
3269 "Compared values are not the same",
3270 t1, t2, actual, expected, file, line);
3271}
3272
3277bool QTest::qCompare(QStringView t1, const QLatin1StringView &t2, const char *actual, const char *expected,
3278 const char *file, int line)
3279{
3280 return QTestResult::compare(t1 == t2,
3281 "Compared values are not the same",
3282 t1, t2, actual, expected, file, line);
3283}
3284
3289bool QTest::qCompare(const QLatin1StringView &t1, QStringView t2, const char *actual, const char *expected,
3290 const char *file, int line)
3291{
3292 return QTestResult::compare(t1 == t2,
3293 "Compared values are not the same",
3294 t1, t2, actual, expected, file, line);
3295}
3296
3320#define TO_STRING_IMPL(TYPE, FORMAT) \
3321template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
3322{ \
3323 char *msg = new char[128]; \
3324 qsnprintf(msg, 128, #FORMAT, t); \
3325 return msg; \
3326}
3327
3328TO_STRING_IMPL(short, %hd)
3330TO_STRING_IMPL(int, %d)
3332TO_STRING_IMPL(long, %ld)
3334#if defined(Q_OS_WIN)
3335TO_STRING_IMPL(qint64, %I64d)
3336TO_STRING_IMPL(quint64, %I64u)
3337#else
3340#endif
3341TO_STRING_IMPL(bool, %d)
3342TO_STRING_IMPL(signed char, %hhd)
3343TO_STRING_IMPL(unsigned char, %hhu)
3344
3345
3355static void massageExponent(char *text)
3356{
3357 char *p = strchr(text, 'e');
3358 if (!p)
3359 return;
3360 const char *const end = p + strlen(p); // *end is '\0'
3361 p += (p[1] == '-' || p[1] == '+') ? 2 : 1;
3362 if (p[0] != '0' || end - 2 <= p)
3363 return;
3364 // We have a leading 0 on an exponent of at least two more digits
3365 const char *n = p + 1;
3366 while (end - 2 > n && n[0] == '0')
3367 ++n;
3368 memmove(p, n, end + 1 - n);
3369}
3370
3371// Be consistent about display of infinities and NaNs (snprintf()'s varies,
3372// notably on MinGW, despite POSIX documenting "[-]inf" or "[-]infinity" for %f,
3373// %e and %g, uppercasing for their capital versions; similar for "nan"):
3374#define TO_STRING_FLOAT(TYPE, FORMAT) \
3375template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
3376{ \
3377 char *msg = new char[128]; \
3378 switch (qFpClassify(t)) { \
3379 case FP_INFINITE: \
3380 qstrncpy(msg, (t < 0 ? "-inf" : "inf"), 128); \
3381 break; \
3382 case FP_NAN: \
3383 qstrncpy(msg, "nan", 128); \
3384 break; \
3385 default: \
3386 qsnprintf(msg, 128, #FORMAT, double(t)); \
3387 massageExponent(msg); \
3388 break; \
3389 } \
3390 return msg; \
3391}
3392
3394TO_STRING_FLOAT(float, %g)
3395TO_STRING_FLOAT(double, %.12g)
3396
3397template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t)
3398{
3399 unsigned char c = static_cast<unsigned char>(t);
3400 char *msg = new char[16];
3401 switch (c) {
3402 case 0x00:
3403 qstrcpy(msg, "'\\0'");
3404 break;
3405 case 0x07:
3406 qstrcpy(msg, "'\\a'");
3407 break;
3408 case 0x08:
3409 qstrcpy(msg, "'\\b'");
3410 break;
3411 case 0x09:
3412 qstrcpy(msg, "'\\t'");
3413 break;
3414 case 0x0a:
3415 qstrcpy(msg, "'\\n'");
3416 break;
3417 case 0x0b:
3418 qstrcpy(msg, "'\\v'");
3419 break;
3420 case 0x0c:
3421 qstrcpy(msg, "'\\f'");
3422 break;
3423 case 0x0d:
3424 qstrcpy(msg, "'\\r'");
3425 break;
3426 case 0x22:
3427 qstrcpy(msg, "'\\\"'");
3428 break;
3429 case 0x27:
3430 qstrcpy(msg, "'\\\''");
3431 break;
3432 case 0x5c:
3433 qstrcpy(msg, "'\\\\'");
3434 break;
3435 default:
3436 if (c < 0x20 || c >= 0x7F)
3437 qsnprintf(msg, 16, "'\\x%02x'", c);
3438 else
3439 qsnprintf(msg, 16, "'%c'" , c);
3440 }
3441 return msg;
3442}
3443
3446char *QTest::toString(const char *str)
3447{
3448 if (!str) {
3449 char *msg = new char[1];
3450 *msg = '\0';
3451 return msg;
3452 }
3453 char *msg = new char[strlen(str) + 1];
3454 return qstrcpy(msg, str);
3455}
3456
3459char *QTest::toString(const volatile void *p) // Use volatile to match compare_ptr_helper()
3460{
3461 return QTest::toString(const_cast<const void *>(p));
3462}
3463
3464char *QTest::toString(const void *p)
3465{
3466 char *msg = new char[128];
3467 qsnprintf(msg, 128, "%p", p);
3468 return msg;
3469}
3470
3473char *QTest::toString(const volatile QObject *vo)
3474{
3475 if (vo == nullptr)
3476 return qstrdup("<null>");
3477
3478 auto *o = const_cast<const QObject*>(vo);
3479 const QString &name = o->objectName();
3480 const char *className = o->metaObject()->className();
3481 char *msg = new char[256];
3482 if (name.isEmpty())
3483 qsnprintf(msg, 256, "%s/%p", className, o);
3484 else
3485 qsnprintf(msg, 256, "%s/\"%s\"", className, qPrintable(name));
3486 return msg;
3487}
3488
3515bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
3516 const char *expected, const char *file, int line)
3517{
3518 return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same",
3519 [t1] { return toString(t1); }, [t2] { return toString(t2); },
3520 actual, expected, file, line);
3521}
3522
std::unique_ptr< QTimer > watchDog
static bool preventsStderrLogging()
void setMode(Mode mode)
QBenchmarkContext context
QBenchmarkMeasurerBase * measurer
static QBenchmarkGlobalData * current
static void setCounter(const char *name)
static QBenchmarkTestMethodData * current
QList< QBenchmarkResult > results
static QString outFileBase(qint64 pid=-1)
static bool rerunThroughCallgrind(const QStringList &origAppArgs, int &exitCode)
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
static QString applicationDirPath()
Returns the directory that contains the application executable.
static QStringList arguments()
static void sendPostedEvents(QObject *receiver=nullptr, int event_type=0)
Immediately dispatches all events which have been previously queued with QCoreApplication::postEvent(...
The QDirIterator class provides an iterator for directory entrylists.
\inmodule QtCore
Definition qdir.h:19
bool cdUp()
Changes directory by moving one directory up from the QDir's current directory.
Definition qdir.cpp:1042
QString dirName() const
Returns the name of the directory; this is not the same as the path, e.g.
Definition qdir.cpp:715
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qdir.cpp:1715
bool mkpath(const QString &dirPath) const
Creates the directory path dirPath.
Definition qdir.cpp:1579
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
QString absoluteFilePath(const QString &fileName) const
Returns the absolute path name of a file in the directory.
Definition qdir.cpp:809
static QString currentPath()
Returns the absolute path of the application's current directory.
Definition qdir.cpp:2051
@ DeferredDelete
Definition qcoreevent.h:100
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
void setFile(const QString &file)
Sets the file that the QFileInfo provides information about to file.
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory.
QString absolutePath() const
Returns a file's path absolute path.
QString canonicalFilePath() const
Returns the canonical path including the file name, i.e.
bool isAbsolute() const
Returns true if the file path is absolute, otherwise returns false (i.e.
Definition qfileinfo.h:116
QString filePath() const
Returns the file name, including the path (which may be absolute or relative).
bool exists() const
Returns true if the file exists; otherwise returns false.
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
Definition qfile.cpp:1136
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:744
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
bool contains(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
static QString path(LibraryPath p)
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
bool isEmpty() const noexcept
Definition qlist.h:390
T & last()
Definition qlist.h:631
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
iterator begin()
Definition qlist.h:608
const T & constFirst() const noexcept
Definition qlist.h:630
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
\inmodule QtCore
Definition qmetaobject.h:18
bool invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument()) const
\obsolete [6.5] Please use the variadic overload of this function
Access access() const
Returns the access specification of this method (private, protected, or public).
int parameterCount() const
bool isValid() const
int returnType() const
QByteArray methodSignature() const
MethodType methodType() const
Returns the type of this method (signal, slot, or method).
QByteArray name() const
\inmodule QtCore
Definition qmetatype.h:320
constexpr const char * name() const
Definition qmetatype.h:2650
\inmodule QtCore
Definition qobject.h:90
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:114
\inmodule QtCore \reentrant
\inmodule QtCore
\inmodule QtCore
static QSharedPointer create(Args &&...arguments)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static void endDump()
static void startDump()
static void setEnabled(bool)
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
const char * dataTag() const
Definition qtestdata.cpp:87
static int verboseLevel()
Definition qtestlog.cpp:593
static int failCount()
Definition qtestlog.cpp:647
static void startLogging()
Definition qtestlog.cpp:480
static qreal msecsFunctionTime()
Definition qtestlog_p.h:117
static void setInstalledTestCoverage(bool installed)
Definition qtestlog.cpp:674
static void setVerboseLevel(int level)
Definition qtestlog.cpp:588
static bool hasLoggers()
Definition qtestlog.cpp:557
static bool loggerUsingStdout()
Definition qtestlog.cpp:562
static void setMaxWarnings(int max)
Definition qtestlog.cpp:630
static void info(const char *msg, const char *file, int line)
Definition qtestlog.cpp:580
static void setPrintAvailableTagsMode()
Definition qtestlog.cpp:637
static void addBenchmarkResults(const QList< QBenchmarkResult > &result)
Definition qtestlog.cpp:474
static void stopLogging()
Definition qtestlog.cpp:489
static void warn(const char *msg, const char *file, int line)
Definition qtestlog.cpp:572
static qreal msecsTotalTime()
Definition qtestlog_p.h:115
static void addLogger(LogMode mode, const char *filename)
Definition qtestlog.cpp:499
static void ignoreMessage(QtMsgType type, const char *msg)
Definition qtestlog.cpp:598
Disables App Nap by registering a background activity.
static const char * currentTestObjectName()
static void fail(const char *message, const char *file, int line)
static const char * currentAppName()
static void finishedCurrentTestDataCleanup()
This function is called after completing each test function, including test functions that are not da...
static void finishedCurrentTestData()
This function is called after completing each test function, including test functions that are not da...
static bool expectFail(const char *dataIndex, const char *comment, QTest::TestFailMode mode, const char *file, int line)
static bool verify(bool statement, const char *statementStr, const char *extraInfo, const char *file, int line)
static bool currentTestFailed()
static void setSkipCurrentTest(bool value)
static void setCurrentAppName(const char *appName)
static void reset()
static void setCurrentTestFunction(const char *func)
static void setBlacklistCurrentTest(bool b)
static void setCurrentTestObject(const char *name)
static const char * currentDataTag()
static bool compare(bool success, const char *failureMsg, char *val1, char *val2, const char *actual, const char *expected, const char *file, int line)
static void addFailure(const char *message, const char *file=nullptr, int line=0)
static bool skipCurrentTest()
static bool reportResult(bool success, qxp::function_ref< const char *()> lhs, qxp::function_ref< const char *()> rhs, const char *lhsExpr, const char *rhsExpr, QTest::ComparisonOperation op, const char *file, int line, const char *failureMessage=nullptr)
static void setCurrentGlobalTestData(QTestData *data)
static void finishedCurrentTestFunction()
This function is called after completing each test function, including test functions that are data-d...
static void setCurrentTestData(QTestData *data)
static void addSkip(const char *message, const char *file, int line)
static QTestData * currentGlobalTestData()
static QTestData * currentTestData()
static const char * currentTestFunction()
int dataCount() const
int elementCount() const
bool isEmpty() const
static QTestTable * currentTestTable()
QTestData * testData(int index) const
QTestData * newData(const char *tag)
static void clearGlobalTestTable()
void addColumn(int elementType, const char *elementName)
static QTestTable * globalTestTable()
static QMetaMethod findMethod(const QObject *obj, const char *signature)
TestMethods(const QObject *o, MetaMethods m={})
void invokeTests(QObject *testObject) const
std::vector< QMetaMethod > MetaMethods
TestEntryFunction getTestEntryFunction(const QString &name) const
static TestRegistry * instance()
void registerTest(const QString &name, TestEntryFunction data)
QStringList getAllTestNames() const
T * data() noexcept
const void * constData() const
Definition qvariant.h:446
static int parseCommandLineArgument(const char *argument)
static bool canLogTestProgress()
\keyword 16-bit Floating Point Support\inmodule QtCore \inheaderfile QFloat16
Definition qfloat16.h:46
QJSValue expected
Definition qjsengine.cpp:12
QString str
[2]
QString text
double e
QSet< QString >::iterator it
auto signal
QList< QVariant > arguments
else opt state
[0]
std::list< QString >::iterator Name
Definition lalr.h:29
Combined button and popup list for selecting options.
Lock qt_scoped_lock(Mutex &mutex)
Definition qlocking_p.h:58
Lock qt_unique_lock(Mutex &mutex)
Definition qlocking_p.h:64
Q_TESTLIB_EXPORT Qt::MouseButtons qtestMouseButtons
void disableWindowRestore()
bool macCrashReporterWillShowDialog()
void checkBlackLists(const char *slot, const char *data, const char *global)
Q_TESTLIB_EXPORT QString formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual)
Q_TESTLIB_EXPORT int lastMouseTimestamp
static bool noCrashHandler
Q_TESTLIB_EXPORT int qRun()
Q_TESTLIB_EXPORT bool currentTestResolved()
Q_TESTLIB_EXPORT QTestData & newRow(const char *dataTag)
Appends a new row to the current test data.
Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml)
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 QObject * testObject()
int Q_TESTLIB_EXPORT defaultMouseDelay()
Q_TESTLIB_EXPORT bool runningTest()
static Q_CONSTINIT QBasicMutex mutex
Definition qtestlog.cpp:142
Q_TESTLIB_EXPORT void setMainSourcePath(const char *file, const char *builddir=nullptr)
static void qPrintDataTags(FILE *stream)
Q_TESTLIB_EXPORT void * qElementData(const char *elementName, int metaTypeId)
static void invokeMethod(QObject *obj, const char *methodName)
int Q_TESTLIB_EXPORT defaultKeyDelay()
Q_TESTLIB_EXPORT bool qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
char * toString(const MyPoint &point)
static int keyDelay
Q_TESTLIB_EXPORT QStringList testFunctions
static bool qPrintTestSlots(FILE *stream, const char *filter=nullptr, const char *preamble="")
Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg, qxp::function_ref< const char *()> actualVal, qxp::function_ref< const char *()> expectedVal, const char *actual, const char *expected, const char *file, int line)
Q_TESTLIB_EXPORT void * qData(const char *tagName, int typeId)
int defaultEventDelay()
Q_TESTLIB_EXPORT bool currentTestFailed()
Returns true if the current test function has failed, otherwise false.
static bool installedTestCoverage
Definition qtestlog.cpp:150
Q_TESTLIB_EXPORT void qInit(QObject *testObject, int argc=0, char **argv=nullptr)
Q_TESTLIB_EXPORT QStringList testTags
static int mouseDelay
char * formatString(const char *prefix, const char *suffix, size_t numArguments,...)
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 void printUnknownDataTagError(QLatin1StringView name, QLatin1StringView tag, const QTestTable &lTable, const QTestTable &gTable)
Q_DECL_COLD_FUNCTION Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *what, const char *file, int line)
static int eventDelay
Q_TESTLIB_EXPORT bool printAvailableFunctions
Q_TESTLIB_EXPORT QString qFindTestData(const char *basepath, const char *file=nullptr, int line=0, const char *builddir=nullptr, const char *sourcedir=nullptr)
ComparisonOperation
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const char *message)
Ignores messages created by qDebug(), qInfo() or qWarning().
bool reportResult(bool success, qxp::function_ref< const char *()> lhs, qxp::function_ref< const char *()> rhs, const char *lhsExpr, const char *rhsExpr, ComparisonOperation op, const char *file, int line)
Q_TESTLIB_EXPORT void failOnWarning(const char *message)
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:405
static int qToInt(const char *str)
char * toPrettyUnicode(QStringView string)
Q_TESTLIB_EXPORT const char * currentDataTag()
Returns the name of the current test data.
Q_TESTLIB_EXPORT int qExec(QObject *testObject, int argc=0, char **argv=nullptr)
Executes tests declared in testObject.
Q_TESTLIB_EXPORT QTestData & addRow(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Q_TESTLIB_EXPORT void qCleanup()
Q_TESTLIB_EXPORT void addColumnInternal(int id, const char *name)
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)
static QObject * currentTestObject
Q_TESTLIB_EXPORT const char * currentAppName()
Returns the name of the binary that is currently executed.
static bool inTestFunction
static QString mainSourcePath
auto run(QThreadPool *pool, Function &&f, Args &&...args)
constexpr char toHexUpper(char32_t value) noexcept
Definition qtools_p.h:27
constexpr char toHexLower(char32_t value) noexcept
Definition qtools_p.h:32
constexpr int fromHex(char32_t c) noexcept
Definition qtools_p.h:44
@ NoButton
Definition qnamespace.h:56
void * HANDLE
@ CaseInsensitive
@ DirectConnection
constexpr Initialization Uninitialized
Initialization
static QT_WARNING_DISABLE_FLOAT_COMPARE ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qreal threshold)
Definition qbezier.cpp:309
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
size_t qstrlen(const char *str)
int qstrncmp(const char *str1, const char *str2, size_t len)
Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap)
Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt,...)
Q_CORE_EXPORT char * qstrdup(const char *)
Q_CORE_EXPORT char * qstrcpy(char *dst, const char *src)
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
#define EINTR_LOOP(var, cmd)
#define qApp
AudioChannelLayoutTag tag
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
static QString methodName(const QDBusIntrospection::Method &method)
EGLStreamKHR stream
const EGLAttrib EGLOutputPortEXT * ports
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
int qFpClassify(qfloat16 f) noexcept
Definition qfloat16.h:240
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
Flags
static void installCoverageTool(QLibraryPrivate *libPrivate)
Definition qlibrary.cpp:279
#define Size(name)
QtMsgType
Definition qlogging.h:29
#define qWarning
Definition qlogging.h:162
#define qFatal
Definition qlogging.h:164
return ret
static ControlElement< T > * ptr(QWidget *widget)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
static bool matches(const QJsonObject &object, const QString &osName, const QVersionNumber &kernelVersion, const QString &osRelease, const QOpenGLConfig::Gpu &gpu)
Definition qopengl.cpp:270
GLboolean GLboolean GLboolean b
GLenum mode
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLbitfield GLuint64 timeout
[4]
GLfloat GLfloat f
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum GLuint buffer
GLenum type
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLuint64 GLenum GLint fd
GLuint name
GLint first
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat n
GLint GLsizei GLsizei GLenum format
GLhandleARB obj
[2]
const GLubyte * c
GLenum array
GLenum GLsizei len
GLint limit
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint GLuint64EXT address
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLubyte * pattern
GLenum GLenum GLsizei void * table
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define MAP_FAILED
#define MAX_PATH
static void qInit(QSqlQuery *q, const QString &query, const QSqlDatabase &db)
SSL_CTX int(*) void arg)
static QString canonicalPath(const QString &rootPath)
#define qPrintable(string)
Definition qstring.h:1391
#define t2
#define QT_CONFIG(feature)
bool qputenv(const char *varName, QByteArrayView raw)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define QTEST_ASSERT(cond)
Definition qtestassert.h:12
#define QTEST_ASSERT_X(cond, where, what)
Definition qtestassert.h:14
static bool alreadyDebugging()
static DebuggerProgram debugger
static bool floatingCompare(const T &actual, const T &expected)
#define TO_STRING_FLOAT(TYPE, FORMAT)
static void maybeDisableCoreDump()
static void prepareStackTrace()
static bool hasSystemCrashReporter()
#define TO_STRING_IMPL(TYPE, FORMAT)
static bool installCoverageTool(const char *appname, const char *testname)
static bool isValidSlot(const QMetaMethod &sl)
static void initEnvironment()
static void generateStackTrace()
static void massageExponent(char *text)
static void printTestRunTime()
#define Q_UNUSED(x)
Tag
unsigned char uchar
Definition qtypes.h:27
size_t quintptr
Definition qtypes.h:72
unsigned long ulong
Definition qtypes.h:30
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
unsigned short ushort
Definition qtypes.h:28
double qreal
Definition qtypes.h:92
ReturnedValue read(const char *data)
class Preamble preamble
HINSTANCE HMODULE
const char className[16]
[1]
Definition qwizard.cpp:100
#define explicit
QList< int > vector
[14]
QByteArray ba
[0]
QFile file
[0]
file open(QIODevice::ReadOnly)
QFileInfo info(fileName)
[8]
QFileInfo fi("c:/temp/foo")
[newstuff]
obj metaObject() -> className()
QObject::connect nullptr
QAction * at
void testObject()
[11]
char * toString(const MyType &t)
[31]
QJSValueList args
QJSValue global
\inmodule QtCore
const char * className() const
Returns the class name.
int methodCount() const
Returns the number of methods in this class, including the number of methods provided by each base cl...
QMetaMethod method(int index) const
Returns the meta-data for the method with the given index.
QTestDataSetter(QTestData *data)