1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtTest module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <QtTest/qtestcase.h>
42#include <QtTest/qtestassert.h>
43
44#include <QtCore/qbytearray.h>
45#include <QtCore/qmetaobject.h>
46#include <QtCore/qobject.h>
47#include <QtCore/qstringlist.h>
48#include <QtCore/qvector.h>
49#include <QtCore/qvarlengtharray.h>
50#include <QtCore/qcoreapplication.h>
51#include <QtCore/qfile.h>
52#include <QtCore/qfileinfo.h>
53#include <QtCore/qdir.h>
54#include <QtCore/qdebug.h>
55#include <QtCore/qfloat16.h>
56#include <QtCore/qlibraryinfo.h>
57#include <QtCore/private/qtools_p.h>
58#include <QtCore/qdiriterator.h>
59#include <QtCore/qtemporarydir.h>
60#include <QtCore/qthread.h>
61#include <QtCore/private/qlocking_p.h>
62#include <QtCore/private/qwaitcondition_p.h>
63
64#include <QtCore/qtestsupport_core.h>
65
66#include <QtTest/private/qtestlog_p.h>
67#include <QtTest/private/qtesttable_p.h>
68#include <QtTest/qtestdata.h>
69#include <QtTest/private/qtestresult_p.h>
70#include <QtTest/private/qsignaldumper_p.h>
71#include <QtTest/private/qbenchmark_p.h>
72#include <QtTest/private/cycle_p.h>
73#include <QtTest/private/qtestblacklist_p.h>
74#if defined(HAVE_XCTEST)
75#include <QtTest/private/qxctestlogger_p.h>
76#endif
77#if defined Q_OS_MACOS
78#include <QtTest/private/qtestutil_macos_p.h>
79#endif
80
81#if defined(Q_OS_DARWIN)
82#include <QtTest/private/qappletestlogger_p.h>
83#endif
84
85#include <cmath>
86#include <numeric>
87#include <algorithm>
88#include <condition_variable>
89#include <mutex>
90#include <chrono>
91
92#include <stdarg.h>
93#include <stdio.h>
94#include <stdlib.h>
95
96#if defined(Q_OS_LINUX)
97#include <sys/types.h>
98#include <fcntl.h>
99#endif
100
101#ifdef Q_OS_WIN
102# if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
103# include <crtdbg.h>
104# endif
105#include <windows.h> // for Sleep
106#endif
107#ifdef Q_OS_UNIX
108#include <errno.h>
109#include <signal.h>
110#include <time.h>
111#include <unistd.h>
112# if !defined(Q_OS_INTEGRITY)
113# include <sys/resource.h>
114# endif
115#endif
116
117#if defined(Q_OS_MACX)
118#include <IOKit/pwr_mgt/IOPMLib.h>
119#include <mach/task.h>
120#include <mach/mach_init.h>
121#include <CoreFoundation/CFPreferences.h>
122#endif
123
124#include <vector>
125
126QT_BEGIN_NAMESPACE
127
128using QtMiscUtils::toHexUpper;
129using QtMiscUtils::fromHex;
130
131static bool debuggerPresent()
132{
133#if defined(Q_OS_LINUX)
134 int fd = open(file: "/proc/self/status", O_RDONLY);
135 if (fd == -1)
136 return false;
137 char buffer[2048];
138 ssize_t size = read(fd: fd, buf: buffer, nbytes: sizeof(buffer) - 1);
139 if (size == -1) {
140 close(fd: fd);
141 return false;
142 }
143 buffer[size] = 0;
144 const char tracerPidToken[] = "\nTracerPid:";
145 char *tracerPid = strstr(haystack: buffer, needle: tracerPidToken);
146 if (!tracerPid) {
147 close(fd: fd);
148 return false;
149 }
150 tracerPid += sizeof(tracerPidToken);
151 long int pid = strtol(nptr: tracerPid, endptr: &tracerPid, base: 10);
152 close(fd: fd);
153 return pid != 0;
154#elif defined(Q_OS_WIN)
155 return IsDebuggerPresent();
156#elif defined(Q_OS_MACOS)
157 // Check if there is an exception handler for the process:
158 mach_msg_type_number_t portCount = 0;
159 exception_mask_t masks[EXC_TYPES_COUNT];
160 mach_port_t ports[EXC_TYPES_COUNT];
161 exception_behavior_t behaviors[EXC_TYPES_COUNT];
162 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
163 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
164 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
165 ports, behaviors, flavors);
166 if (result == KERN_SUCCESS) {
167 for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
168 if (MACH_PORT_VALID(ports[portIndex])) {
169 return true;
170 }
171 }
172 }
173 return false;
174#else
175 // TODO
176 return false;
177#endif
178}
179
180static bool hasSystemCrashReporter()
181{
182#if defined(Q_OS_MACOS)
183 return QTestPrivate::macCrashReporterWillShowDialog();
184#else
185 return false;
186#endif
187}
188
189static void disableCoreDump()
190{
191 bool ok = false;
192 const int disableCoreDump = qEnvironmentVariableIntValue(varName: "QTEST_DISABLE_CORE_DUMP", ok: &ok);
193 if (ok && disableCoreDump == 1) {
194#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
195 struct rlimit limit;
196 limit.rlim_cur = 0;
197 limit.rlim_max = 0;
198 if (setrlimit(RLIMIT_CORE, rlimits: &limit) != 0)
199 qWarning(msg: "Failed to disable core dumps: %d", errno);
200#endif
201 }
202}
203Q_CONSTRUCTOR_FUNCTION(disableCoreDump);
204
205static void stackTrace()
206{
207 bool ok = false;
208 const int disableStackDump = qEnvironmentVariableIntValue(varName: "QTEST_DISABLE_STACK_DUMP", ok: &ok);
209 if (ok && disableStackDump == 1)
210 return;
211
212 if (debuggerPresent() || hasSystemCrashReporter())
213 return;
214
215#if defined(Q_OS_LINUX) || (defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM_64))
216 const int msecsFunctionTime = qRound(d: QTestLog::msecsFunctionTime());
217 const int msecsTotalTime = qRound(d: QTestLog::msecsTotalTime());
218 fprintf(stderr, format: "\n=== Received signal at function time: %dms, total time: %dms, dumping stack ===\n",
219 msecsFunctionTime, msecsTotalTime);
220
221# ifdef Q_OS_LINUX
222 char cmd[512];
223 qsnprintf(str: cmd, n: 512, fmt: "gdb --pid %d 2>/dev/null <<EOF\n"
224 "set prompt\n"
225 "set height 0\n"
226 "thread apply all where full\n"
227 "detach\n"
228 "quit\n"
229 "EOF\n",
230 (int)getpid());
231 if (system(command: cmd) == -1)
232 fprintf(stderr, format: "calling gdb failed\n");
233 fprintf(stderr, format: "=== End of stack trace ===\n");
234# elif defined(Q_OS_MACOS)
235 char cmd[512];
236 qsnprintf(cmd, 512, "lldb -p %d 2>/dev/null <<EOF\n"
237 "bt all\n"
238 "quit\n"
239 "EOF\n",
240 (int)getpid());
241 if (system(cmd) == -1)
242 fprintf(stderr, "calling lldb failed\n");
243 fprintf(stderr, "=== End of stack trace ===\n");
244# endif
245
246#endif
247}
248
249static bool installCoverageTool(const char * appname, const char * testname)
250{
251#if defined(__COVERAGESCANNER__) && !QT_CONFIG(testlib_selfcover)
252 if (!qEnvironmentVariableIsEmpty("QT_TESTCOCOON_ACTIVE"))
253 return false;
254 // Set environment variable QT_TESTCOCOON_ACTIVE to prevent an eventual subtest from
255 // being considered as a stand-alone test regarding the coverage analysis.
256 qputenv("QT_TESTCOCOON_ACTIVE", "1");
257
258 // Install Coverage Tool
259 __coveragescanner_install(appname);
260 __coveragescanner_testname(testname);
261 __coveragescanner_clear();
262 return true;
263#else
264 Q_UNUSED(appname);
265 Q_UNUSED(testname);
266 return false;
267#endif
268}
269
270static bool isValidSlot(const QMetaMethod &sl)
271{
272 if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0
273 || sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot)
274 return false;
275 const QByteArray name = sl.name();
276 return !(name.isEmpty() || name.endsWith(c: "_data")
277 || name == "initTestCase" || name == "cleanupTestCase"
278 || name == "init" || name == "cleanup");
279}
280
281namespace QTestPrivate
282{
283 Q_TESTLIB_EXPORT Qt::MouseButtons qtestMouseButtons = Qt::NoButton;
284}
285
286namespace QTest
287{
288extern Q_TESTLIB_EXPORT int lastMouseTimestamp;
289
290class WatchDog;
291
292static QObject *currentTestObject = nullptr;
293static QString mainSourcePath;
294
295#if defined(Q_OS_MACOS)
296static IOPMAssertionID macPowerSavingDisabled = 0;
297#endif
298
299class TestMethods {
300public:
301 Q_DISABLE_COPY_MOVE(TestMethods)
302
303 using MetaMethods = std::vector<QMetaMethod>;
304
305 explicit TestMethods(const QObject *o, const MetaMethods &m = MetaMethods());
306
307 void invokeTests(QObject *testObject) const;
308
309 static QMetaMethod findMethod(const QObject *obj, const char *signature);
310
311private:
312 bool invokeTest(int index, const char *data, WatchDog *watchDog) const;
313 void invokeTestOnData(int index) const;
314
315 QMetaMethod m_initTestCaseMethod; // might not exist, check isValid().
316 QMetaMethod m_initTestCaseDataMethod;
317 QMetaMethod m_cleanupTestCaseMethod;
318 QMetaMethod m_initMethod;
319 QMetaMethod m_cleanupMethod;
320
321 MetaMethods m_methods;
322};
323
324TestMethods::TestMethods(const QObject *o, const MetaMethods &m)
325 : m_initTestCaseMethod(TestMethods::findMethod(obj: o, signature: "initTestCase()"))
326 , m_initTestCaseDataMethod(TestMethods::findMethod(obj: o, signature: "initTestCase_data()"))
327 , m_cleanupTestCaseMethod(TestMethods::findMethod(obj: o, signature: "cleanupTestCase()"))
328 , m_initMethod(TestMethods::findMethod(obj: o, signature: "init()"))
329 , m_cleanupMethod(TestMethods::findMethod(obj: o, signature: "cleanup()"))
330 , m_methods(m)
331{
332 if (m.empty()) {
333 const QMetaObject *metaObject = o->metaObject();
334 const int count = metaObject->methodCount();
335 m_methods.reserve(n: count);
336 for (int i = 0; i < count; ++i) {
337 const QMetaMethod me = metaObject->method(index: i);
338 if (isValidSlot(sl: me))
339 m_methods.push_back(x: me);
340 }
341 }
342}
343
344QMetaMethod TestMethods::findMethod(const QObject *obj, const char *signature)
345{
346 const QMetaObject *metaObject = obj->metaObject();
347 const int funcIndex = metaObject->indexOfMethod(method: signature);
348 return funcIndex >= 0 ? metaObject->method(index: funcIndex) : QMetaMethod();
349}
350
351static int keyDelay = -1;
352static int mouseDelay = -1;
353static int eventDelay = -1;
354#if QT_CONFIG(thread)
355static int timeout = -1;
356#endif
357static bool noCrashHandler = false;
358
359/*! \internal
360 Invoke a method of the object without generating warning if the method does not exist
361 */
362static void invokeMethod(QObject *obj, const char *methodName)
363{
364 const QMetaObject *metaObject = obj->metaObject();
365 int funcIndex = metaObject->indexOfMethod(method: methodName);
366 if (funcIndex >= 0) {
367 QMetaMethod method = metaObject->method(index: funcIndex);
368 method.invoke(object: obj, connectionType: Qt::DirectConnection);
369 }
370}
371
372int defaultEventDelay()
373{
374 if (eventDelay == -1) {
375 const QByteArray env = qgetenv(varName: "QTEST_EVENT_DELAY");
376 if (!env.isEmpty())
377 eventDelay = atoi(nptr: env.constData());
378 else
379 eventDelay = 0;
380 }
381 return eventDelay;
382}
383
384int Q_TESTLIB_EXPORT defaultMouseDelay()
385{
386 if (mouseDelay == -1) {
387 const QByteArray env = qgetenv(varName: "QTEST_MOUSEEVENT_DELAY");
388 if (!env.isEmpty())
389 mouseDelay = atoi(nptr: env.constData());
390 else
391 mouseDelay = defaultEventDelay();
392 }
393 return mouseDelay;
394}
395
396int Q_TESTLIB_EXPORT defaultKeyDelay()
397{
398 if (keyDelay == -1) {
399 const QByteArray env = qgetenv(varName: "QTEST_KEYEVENT_DELAY");
400 if (!env.isEmpty())
401 keyDelay = atoi(nptr: env.constData());
402 else
403 keyDelay = defaultEventDelay();
404 }
405 return keyDelay;
406}
407#if QT_CONFIG(thread)
408static std::chrono::milliseconds defaultTimeout()
409{
410 if (timeout == -1) {
411 bool ok = false;
412 timeout = qEnvironmentVariableIntValue(varName: "QTEST_FUNCTION_TIMEOUT", ok: &ok);
413
414 if (!ok || timeout <= 0)
415 timeout = 5*60*1000;
416 }
417 return std::chrono::milliseconds{timeout};
418}
419#endif
420
421Q_TESTLIB_EXPORT bool printAvailableFunctions = false;
422Q_TESTLIB_EXPORT QStringList testFunctions;
423Q_TESTLIB_EXPORT QStringList testTags;
424
425static void qPrintTestSlots(FILE *stream, const char *filter = nullptr)
426{
427 for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) {
428 QMetaMethod sl = QTest::currentTestObject->metaObject()->method(index: i);
429 if (isValidSlot(sl)) {
430 const QByteArray signature = sl.methodSignature();
431 if (!filter || QString::fromLatin1(str: signature).contains(s: QLatin1String(filter), cs: Qt::CaseInsensitive))
432 fprintf(stream: stream, format: "%s\n", signature.constData());
433 }
434 }
435}
436
437static void qPrintDataTags(FILE *stream)
438{
439 // Avoid invoking the actual test functions, and also avoid printing irrelevant output:
440 QTestLog::setPrintAvailableTagsMode();
441
442 // Get global data tags:
443 QTestTable::globalTestTable();
444 invokeMethod(obj: QTest::currentTestObject, methodName: "initTestCase_data()");
445 const QTestTable *gTable = QTestTable::globalTestTable();
446
447 const QMetaObject *currTestMetaObj = QTest::currentTestObject->metaObject();
448
449 // Process test functions:
450 for (int i = 0; i < currTestMetaObj->methodCount(); ++i) {
451 QMetaMethod tf = currTestMetaObj->method(index: i);
452
453 if (isValidSlot(sl: tf)) {
454
455 // Retrieve local tags:
456 QStringList localTags;
457 QTestTable table;
458 char *slot = qstrdup(tf.methodSignature().constData());
459 slot[strlen(s: slot) - 2] = '\0';
460 QByteArray member;
461 member.resize(size: qstrlen(str: slot) + qstrlen(str: "_data()") + 1);
462 qsnprintf(str: member.data(), n: member.size(), fmt: "%s_data()", slot);
463 invokeMethod(obj: QTest::currentTestObject, methodName: member.constData());
464 const int dataCount = table.dataCount();
465 localTags.reserve(alloc: dataCount);
466 for (int j = 0; j < dataCount; ++j)
467 localTags << QLatin1String(table.testData(index: j)->dataTag());
468
469 // Print all tag combinations:
470 if (gTable->dataCount() == 0) {
471 if (localTags.count() == 0) {
472 // No tags at all, so just print the test function:
473 fprintf(stream: stream, format: "%s %s\n", currTestMetaObj->className(), slot);
474 } else {
475 // Only local tags, so print each of them:
476 for (int k = 0; k < localTags.size(); ++k)
477 fprintf(
478 stream: stream, format: "%s %s %s\n",
479 currTestMetaObj->className(), slot, localTags.at(i: k).toLatin1().data());
480 }
481 } else {
482 for (int j = 0; j < gTable->dataCount(); ++j) {
483 if (localTags.count() == 0) {
484 // Only global tags, so print the current one:
485 fprintf(
486 stream: stream, format: "%s %s __global__ %s\n",
487 currTestMetaObj->className(), slot, gTable->testData(index: j)->dataTag());
488 } else {
489 // Local and global tags, so print each of the local ones and
490 // the current global one:
491 for (int k = 0; k < localTags.size(); ++k)
492 fprintf(
493 stream: stream, format: "%s %s %s __global__ %s\n", currTestMetaObj->className(), slot,
494 localTags.at(i: k).toLatin1().data(), gTable->testData(index: j)->dataTag());
495 }
496 }
497 }
498
499 delete[] slot;
500 }
501 }
502}
503
504static int qToInt(const char *str)
505{
506 char *pEnd;
507 int l = (int)strtol(nptr: str, endptr: &pEnd, base: 10);
508 if (*pEnd != 0) {
509 fprintf(stderr, format: "Invalid numeric parameter: '%s'\n", str);
510 exit(status: 1);
511 }
512 return l;
513}
514
515Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml)
516{
517 int logFormat = -1; // Not set
518 const char *logFilename = nullptr;
519
520 QTest::testFunctions.clear();
521 QTest::testTags.clear();
522
523#if defined(Q_OS_MAC) && defined(HAVE_XCTEST)
524 if (QXcodeTestLogger::canLogTestProgress())
525 logFormat = QTestLog::XCTest;
526#endif
527
528 const char *testOptions =
529 " New-style logging options:\n"
530 " -o filename,format : Output results to file in the specified format\n"
531 " Use - to output to stdout\n"
532 " Valid formats are:\n"
533 " txt : Plain text\n"
534 " csv : CSV format (suitable for benchmarks)\n"
535 " junitxml : XML JUnit document\n"
536 " xml : XML document\n"
537 " lightxml : A stream of XML tags\n"
538 " teamcity : TeamCity format\n"
539 " tap : Test Anything Protocol\n"
540 "\n"
541 " *** Multiple loggers can be specified, but at most one can log to stdout.\n"
542 "\n"
543 " Old-style logging options:\n"
544 " -o filename : Write the output into file\n"
545 " -txt : Output results in Plain Text\n"
546 " -csv : Output results in a CSV format (suitable for benchmarks)\n"
547 " -junitxml : Output results as XML JUnit document\n"
548 " -xml : Output results as XML document\n"
549 " -lightxml : Output results as stream of XML tags\n"
550 " -teamcity : Output results in TeamCity format\n"
551 " -tap : Output results in Test Anything Protocol format\n"
552 "\n"
553 " *** If no output file is specified, stdout is assumed.\n"
554 " *** If no output format is specified, -txt is assumed.\n"
555 "\n"
556 " Test log detail options:\n"
557 " -silent : Log failures and fatal errors only\n"
558 " -v1 : Log the start of each testfunction\n"
559 " -v2 : Log each QVERIFY/QCOMPARE/QTEST (implies -v1)\n"
560 " -vs : Log every signal emission and resulting slot invocations\n"
561 "\n"
562 " *** The -silent and -v1 options only affect plain text output.\n"
563 "\n"
564 " Testing options:\n"
565 " -functions : Returns a list of current testfunctions\n"
566 " -datatags : Returns a list of current data tags.\n"
567 " A global data tag is preceded by ' __global__ '.\n"
568 " -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n"
569 " -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n"
570 " -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n"
571 " -maxwarnings n : Sets the maximum amount of messages to output.\n"
572 " 0 means unlimited, default: 2000\n"
573 " -nocrashhandler : Disables the crash handler. Useful for debugging crashes.\n"
574 "\n"
575 " Benchmarking options:\n"
576#if QT_CONFIG(valgrind)
577 " -callgrind : Use callgrind to time benchmarks\n"
578#endif
579#ifdef QTESTLIB_USE_PERF_EVENTS
580 " -perf : Use Linux perf events to time benchmarks\n"
581 " -perfcounter name : Use the counter named 'name'\n"
582 " -perfcounterlist : Lists the counters available\n"
583#endif
584#ifdef HAVE_TICK_COUNTER
585 " -tickcounter : Use CPU tick counters to time benchmarks\n"
586#endif
587 " -eventcounter : Counts events received during benchmarks\n"
588 " -minimumvalue n : Sets the minimum acceptable measurement value\n"
589 " -minimumtotal n : Sets the minimum acceptable total for repeated executions of a test function\n"
590 " -iterations n : Sets the number of accumulation iterations.\n"
591 " -median n : Sets the number of median iterations.\n"
592 " -vb : Print out verbose benchmarking information.\n";
593
594 for (int i = 1; i < argc; ++i) {
595 if (strcmp(s1: argv[i], s2: "-help") == 0 || strcmp(s1: argv[i], s2: "--help") == 0
596 || strcmp(s1: argv[i], s2: "/?") == 0) {
597 printf(format: " Usage: %s [options] [testfunction[:testdata]]...\n"
598 " By default, all testfunctions will be run.\n\n"
599 "%s", argv[0], testOptions);
600
601 if (qml) {
602 printf (format: "\n"
603 " QmlTest options:\n"
604 " -import dir : Specify an import directory.\n"
605 " -plugins dir : Specify a directory where to search for plugins.\n"
606 " -input dir/file : Specify the root directory for test cases or a single test case file.\n"
607 " -translation file : Specify the translation file.\n"
608 " -file-selector dir : Specify a file selector for the QML engine.\n"
609 );
610 }
611
612 printf(format: "\n"
613 " -help : This help\n");
614 exit(status: 0);
615 } else if (strcmp(s1: argv[i], s2: "-functions") == 0) {
616 if (qml) {
617 QTest::printAvailableFunctions = true;
618 } else {
619 qPrintTestSlots(stdout);
620 exit(status: 0);
621 }
622 } else if (strcmp(s1: argv[i], s2: "-datatags") == 0) {
623 if (!qml) {
624 qPrintDataTags(stdout);
625 exit(status: 0);
626 }
627 } else if (strcmp(s1: argv[i], s2: "-txt") == 0) {
628 logFormat = QTestLog::Plain;
629 } else if (strcmp(s1: argv[i], s2: "-csv") == 0) {
630 logFormat = QTestLog::CSV;
631 } else if (strcmp(s1: argv[i], s2: "-junitxml") == 0 || strcmp(s1: argv[i], s2: "-xunitxml") == 0) {
632 logFormat = QTestLog::JUnitXML;
633 } else if (strcmp(s1: argv[i], s2: "-xml") == 0) {
634 logFormat = QTestLog::XML;
635 } else if (strcmp(s1: argv[i], s2: "-lightxml") == 0) {
636 logFormat = QTestLog::LightXML;
637 } else if (strcmp(s1: argv[i], s2: "-teamcity") == 0) {
638 logFormat = QTestLog::TeamCity;
639 } else if (strcmp(s1: argv[i], s2: "-tap") == 0) {
640 logFormat = QTestLog::TAP;
641 } else if (strcmp(s1: argv[i], s2: "-silent") == 0) {
642 QTestLog::setVerboseLevel(-1);
643 } else if (strcmp(s1: argv[i], s2: "-v1") == 0) {
644 QTestLog::setVerboseLevel(1);
645 } else if (strcmp(s1: argv[i], s2: "-v2") == 0) {
646 QTestLog::setVerboseLevel(2);
647 } else if (strcmp(s1: argv[i], s2: "-vs") == 0) {
648 QSignalDumper::startDump();
649 } else if (strcmp(s1: argv[i], s2: "-o") == 0) {
650 if (i + 1 >= argc) {
651 fprintf(stderr, format: "-o needs an extra parameter specifying the filename and optional format\n");
652 exit(status: 1);
653 }
654 ++i;
655 // Do we have the old or new style -o option?
656 char *filename = new char[strlen(s: argv[i])+1];
657 char *format = new char[strlen(s: argv[i])+1];
658 if (sscanf(s: argv[i], format: "%[^,],%s", filename, format) == 1) {
659 // Old-style
660 logFilename = argv[i];
661 } else {
662 // New-style
663 if (strcmp(s1: format, s2: "txt") == 0)
664 logFormat = QTestLog::Plain;
665 else if (strcmp(s1: format, s2: "csv") == 0)
666 logFormat = QTestLog::CSV;
667 else if (strcmp(s1: format, s2: "lightxml") == 0)
668 logFormat = QTestLog::LightXML;
669 else if (strcmp(s1: format, s2: "xml") == 0)
670 logFormat = QTestLog::XML;
671 else if (strcmp(s1: format, s2: "junitxml") == 0 || strcmp(s1: format, s2: "xunitxml") == 0)
672 logFormat = QTestLog::JUnitXML;
673 else if (strcmp(s1: format, s2: "teamcity") == 0)
674 logFormat = QTestLog::TeamCity;
675 else if (strcmp(s1: format, s2: "tap") == 0)
676 logFormat = QTestLog::TAP;
677 else {
678 fprintf(stderr, format: "output format must be one of txt, csv, lightxml, xml, tap, teamcity or junitxml\n");
679 exit(status: 1);
680 }
681 if (strcmp(s1: filename, s2: "-") == 0 && QTestLog::loggerUsingStdout()) {
682 fprintf(stderr, format: "only one logger can log to stdout\n");
683 exit(status: 1);
684 }
685 QTestLog::addLogger(mode: QTestLog::LogMode(logFormat), filename);
686 }
687 delete [] filename;
688 delete [] format;
689 } else if (strcmp(s1: argv[i], s2: "-eventdelay") == 0) {
690 if (i + 1 >= argc) {
691 fprintf(stderr, format: "-eventdelay needs an extra parameter to indicate the delay(ms)\n");
692 exit(status: 1);
693 } else {
694 QTest::eventDelay = qToInt(str: argv[++i]);
695 }
696 } else if (strcmp(s1: argv[i], s2: "-keydelay") == 0) {
697 if (i + 1 >= argc) {
698 fprintf(stderr, format: "-keydelay needs an extra parameter to indicate the delay(ms)\n");
699 exit(status: 1);
700 } else {
701 QTest::keyDelay = qToInt(str: argv[++i]);
702 }
703 } else if (strcmp(s1: argv[i], s2: "-mousedelay") == 0) {
704 if (i + 1 >= argc) {
705 fprintf(stderr, format: "-mousedelay needs an extra parameter to indicate the delay(ms)\n");
706 exit(status: 1);
707 } else {
708 QTest::mouseDelay = qToInt(str: argv[++i]);
709 }
710 } else if (strcmp(s1: argv[i], s2: "-maxwarnings") == 0) {
711 if (i + 1 >= argc) {
712 fprintf(stderr, format: "-maxwarnings needs an extra parameter with the amount of warnings\n");
713 exit(status: 1);
714 } else {
715 QTestLog::setMaxWarnings(qToInt(str: argv[++i]));
716 }
717 } else if (strcmp(s1: argv[i], s2: "-nocrashhandler") == 0) {
718 QTest::noCrashHandler = true;
719#if QT_CONFIG(valgrind)
720 } else if (strcmp(s1: argv[i], s2: "-callgrind") == 0) {
721 if (QBenchmarkValgrindUtils::haveValgrind())
722 if (QFileInfo(QDir::currentPath()).isWritable()) {
723 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindParentProcess);
724 } else {
725 fprintf(stderr, format: "WARNING: Current directory not writable. Using the walltime measurer.\n");
726 }
727 else {
728 fprintf(stderr, format: "WARNING: Valgrind not found or too old. Make sure it is installed and in your path. "
729 "Using the walltime measurer.\n");
730 }
731 } else if (strcmp(s1: argv[i], s2: "-callgrindchild") == 0) { // "private" option
732 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindChildProcess);
733 QBenchmarkGlobalData::current->callgrindOutFileBase =
734 QBenchmarkValgrindUtils::outFileBase();
735#endif
736#ifdef QTESTLIB_USE_PERF_EVENTS
737 } else if (strcmp(s1: argv[i], s2: "-perf") == 0) {
738 if (QBenchmarkPerfEventsMeasurer::isAvailable()) {
739 // perf available
740 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::PerfCounter);
741 } else {
742 fprintf(stderr, format: "WARNING: Linux perf events not available. Using the walltime measurer.\n");
743 }
744 } else if (strcmp(s1: argv[i], s2: "-perfcounter") == 0) {
745 if (i + 1 >= argc) {
746 fprintf(stderr, format: "-perfcounter needs an extra parameter with the name of the counter\n");
747 exit(status: 1);
748 } else {
749 QBenchmarkPerfEventsMeasurer::setCounter(argv[++i]);
750 }
751 } else if (strcmp(s1: argv[i], s2: "-perfcounterlist") == 0) {
752 QBenchmarkPerfEventsMeasurer::listCounters();
753 exit(status: 0);
754#endif
755#ifdef HAVE_TICK_COUNTER
756 } else if (strcmp(s1: argv[i], s2: "-tickcounter") == 0) {
757 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::TickCounter);
758#endif
759 } else if (strcmp(s1: argv[i], s2: "-eventcounter") == 0) {
760 QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::EventCounter);
761 } else if (strcmp(s1: argv[i], s2: "-minimumvalue") == 0) {
762 if (i + 1 >= argc) {
763 fprintf(stderr, format: "-minimumvalue needs an extra parameter to indicate the minimum time(ms)\n");
764 exit(status: 1);
765 } else {
766 QBenchmarkGlobalData::current->walltimeMinimum = qToInt(str: argv[++i]);
767 }
768 } else if (strcmp(s1: argv[i], s2: "-minimumtotal") == 0) {
769 if (i + 1 >= argc) {
770 fprintf(stderr, format: "-minimumtotal needs an extra parameter to indicate the minimum total measurement\n");
771 exit(status: 1);
772 } else {
773 QBenchmarkGlobalData::current->minimumTotal = qToInt(str: argv[++i]);
774 }
775 } else if (strcmp(s1: argv[i], s2: "-iterations") == 0) {
776 if (i + 1 >= argc) {
777 fprintf(stderr, format: "-iterations needs an extra parameter to indicate the number of iterations\n");
778 exit(status: 1);
779 } else {
780 QBenchmarkGlobalData::current->iterationCount = qToInt(str: argv[++i]);
781 }
782 } else if (strcmp(s1: argv[i], s2: "-median") == 0) {
783 if (i + 1 >= argc) {
784 fprintf(stderr, format: "-median needs an extra parameter to indicate the number of median iterations\n");
785 exit(status: 1);
786 } else {
787 QBenchmarkGlobalData::current->medianIterationCount = qToInt(str: argv[++i]);
788 }
789
790 } else if (strcmp(s1: argv[i], s2: "-vb") == 0) {
791 QBenchmarkGlobalData::current->verboseOutput = true;
792#if defined(Q_OS_WINRT)
793 } else if (strncmp(argv[i], "-ServerName:", 12) == 0 ||
794 strncmp(argv[i], "-qdevel", 7) == 0) {
795 continue;
796#elif defined(Q_OS_MAC) && defined(HAVE_XCTEST)
797 } else if (int skip = QXcodeTestLogger::parseCommandLineArgument(argv[i])) {
798 i += (skip - 1); // Eating argv[i] with a continue counts towards skips
799 continue;
800#endif
801 } else if (argv[i][0] == '-') {
802 fprintf(stderr, format: "Unknown option: '%s'\n\n%s", argv[i], testOptions);
803 if (qml) {
804 fprintf(stderr, format: "\nqmltest related options:\n"
805 " -import : Specify an import directory.\n"
806 " -plugins : Specify a directory where to search for plugins.\n"
807 " -input : Specify the root directory for test cases.\n"
808 );
809 }
810
811 fprintf(stderr, format: "\n"
812 " -help : This help\n");
813 exit(status: 1);
814 } else {
815 // We can't check the availability of test functions until
816 // we load the QML files. So just store the data for now.
817 int colon = -1;
818 int offset;
819 for (offset = 0; argv[i][offset]; ++offset) {
820 if (argv[i][offset] == ':') {
821 if (argv[i][offset + 1] == ':') {
822 // "::" is used as a test name separator.
823 // e.g. "ClickTests::test_click:row1".
824 ++offset;
825 } else {
826 colon = offset;
827 break;
828 }
829 }
830 }
831 if (colon == -1) {
832 QTest::testFunctions += QString::fromLatin1(str: argv[i]);
833 QTest::testTags += QString();
834 } else {
835 QTest::testFunctions +=
836 QString::fromLatin1(str: argv[i], size: colon);
837 QTest::testTags +=
838 QString::fromLatin1(str: argv[i] + colon + 1);
839 }
840 }
841 }
842
843 bool installedTestCoverage = installCoverageTool(appname: QTestResult::currentAppName(), testname: QTestResult::currentTestObjectName());
844 QTestLog::setInstalledTestCoverage(installedTestCoverage);
845
846 // If no loggers were created by the long version of the -o command-line
847 // option, but a logger was requested via the old-style option, add it.
848 const bool explicitLoggerRequested = logFormat != -1;
849 if (QTestLog::loggerCount() == 0 && explicitLoggerRequested)
850 QTestLog::addLogger(mode: QTestLog::LogMode(logFormat), filename: logFilename);
851
852 bool addFallbackLogger = !explicitLoggerRequested;
853
854#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
855 // Any explicitly requested loggers will be added by now, so we can check if they use stdout
856 const bool safeToAddAppleLogger = !AppleUnifiedLogger::willMirrorToStderr() || !QTestLog::loggerUsingStdout();
857 if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
858 QTestLog::addLogger(QTestLog::Apple, nullptr);
859 if (AppleUnifiedLogger::willMirrorToStderr() && !logFilename)
860 addFallbackLogger = false; // Prevent plain test logger fallback below
861 }
862#endif
863
864 if (addFallbackLogger)
865 QTestLog::addLogger(mode: QTestLog::Plain, filename: logFilename);
866}
867
868// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
869Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) {
870 qtest_qParseArgs(argc, argv: const_cast<const char *const *>(argv), qml);
871}
872
873QBenchmarkResult qMedian(const QVector<QBenchmarkResult> &container)
874{
875 const int count = container.count();
876 if (count == 0)
877 return QBenchmarkResult();
878
879 if (count == 1)
880 return container.front();
881
882 QVector<QBenchmarkResult> containerCopy = container;
883 std::sort(first: containerCopy.begin(), last: containerCopy.end());
884
885 const int middle = count / 2;
886
887 // ### handle even-sized containers here by doing an aritmetic mean of the two middle items.
888 return containerCopy.at(i: middle);
889}
890
891struct QTestDataSetter
892{
893 QTestDataSetter(QTestData *data)
894 {
895 QTestResult::setCurrentTestData(data);
896 }
897 ~QTestDataSetter()
898 {
899 QTestResult::setCurrentTestData(nullptr);
900 }
901};
902
903namespace {
904
905qreal addResult(qreal current, const QBenchmarkResult& r)
906{
907 return current + r.value;
908}
909
910}
911
912void TestMethods::invokeTestOnData(int index) const
913{
914 /* Benchmarking: for each median iteration*/
915
916 bool isBenchmark = false;
917 int i = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
918
919 QVector<QBenchmarkResult> results;
920 bool minimumTotalReached = false;
921 do {
922 QBenchmarkTestMethodData::current->beginDataRun();
923
924 /* Benchmarking: for each accumulation iteration*/
925 bool invokeOk;
926 do {
927 if (m_initMethod.isValid())
928 m_initMethod.invoke(object: QTest::currentTestObject, connectionType: Qt::DirectConnection);
929 if (QTestResult::skipCurrentTest() || QTestResult::currentTestFailed())
930 break;
931
932 QBenchmarkTestMethodData::current->result = QBenchmarkResult();
933 QBenchmarkTestMethodData::current->resultAccepted = false;
934
935 QBenchmarkGlobalData::current->context.tag =
936 QLatin1String(
937 QTestResult::currentDataTag()
938 ? QTestResult::currentDataTag() : "");
939
940 invokeOk = m_methods[index].invoke(object: QTest::currentTestObject, connectionType: Qt::DirectConnection);
941 if (!invokeOk)
942 QTestResult::addFailure(message: "Unable to execute slot", __FILE__, __LINE__);
943
944 isBenchmark = QBenchmarkTestMethodData::current->isBenchmark();
945
946 QTestResult::finishedCurrentTestData();
947
948 if (m_cleanupMethod.isValid())
949 m_cleanupMethod.invoke(object: QTest::currentTestObject, connectionType: Qt::DirectConnection);
950
951 // Process any deleteLater(), like event-loop based apps would do. Fixes memleak reports.
952 if (QCoreApplication::instance())
953 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
954
955 // If the test isn't a benchmark, finalize the result after cleanup() has finished.
956 if (!isBenchmark)
957 QTestResult::finishedCurrentTestDataCleanup();
958
959 // If this test method has a benchmark, repeat until all measurements are
960 // acceptable.
961 // The QBENCHMARK macro increases the number of iterations for each run until
962 // this happens.
963 } while (invokeOk && isBenchmark
964 && QBenchmarkTestMethodData::current->resultsAccepted() == false
965 && !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed());
966
967 QBenchmarkTestMethodData::current->endDataRun();
968 if (!QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed()) {
969 if (i > -1) // iteration -1 is the warmup iteration.
970 results.append(t: QBenchmarkTestMethodData::current->result);
971
972 if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput) {
973 if (i == -1) {
974 QTestLog::info(qPrintable(
975 QString::fromLatin1("warmup stage result : %1")
976 .arg(QBenchmarkTestMethodData::current->result.value)), file: nullptr, line: 0);
977 } else {
978 QTestLog::info(qPrintable(
979 QString::fromLatin1("accumulation stage result: %1")
980 .arg(QBenchmarkTestMethodData::current->result.value)), file: nullptr, line: 0);
981 }
982 }
983 }
984
985 // Verify if the minimum total measurement is reached, if it was specified:
986 if (QBenchmarkGlobalData::current->minimumTotal == -1) {
987 minimumTotalReached = true;
988 } else {
989 const qreal total = std::accumulate(first: results.begin(), last: results.end(), init: 0.0, binary_op: addResult);
990 minimumTotalReached = (total >= QBenchmarkGlobalData::current->minimumTotal);
991 }
992 } while (isBenchmark
993 && ((++i < QBenchmarkGlobalData::current->adjustMedianIterationCount()) || !minimumTotalReached)
994 && !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed());
995
996 // If the test is a benchmark, finalize the result after all iterations have finished.
997 if (isBenchmark) {
998 bool testPassed = !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed();
999 QTestResult::finishedCurrentTestDataCleanup();
1000 // Only report benchmark figures if the test passed
1001 if (testPassed && QBenchmarkTestMethodData::current->resultsAccepted())
1002 QTestLog::addBenchmarkResult(result: qMedian(container: results));
1003 }
1004}
1005
1006#if QT_CONFIG(thread)
1007
1008class WatchDog : public QThread
1009{
1010 enum Expectation {
1011 ThreadStart,
1012 TestFunctionStart,
1013 TestFunctionEnd,
1014 ThreadEnd,
1015 };
1016
1017 bool waitFor(std::unique_lock<QtPrivate::mutex> &m, Expectation e) {
1018 auto expectationChanged = [this, e] { return expecting != e; };
1019 switch (e) {
1020 case TestFunctionEnd:
1021 return waitCondition.wait_for(lock&: m, rtime: defaultTimeout(), p: expectationChanged);
1022 case ThreadStart:
1023 case ThreadEnd:
1024 case TestFunctionStart:
1025 waitCondition.wait(lock&: m, p: expectationChanged);
1026 return true;
1027 }
1028 Q_UNREACHABLE();
1029 return false;
1030 }
1031
1032public:
1033 WatchDog()
1034 {
1035 auto locker = qt_unique_lock(mutex);
1036 expecting = ThreadStart;
1037 start();
1038 waitFor(m&: locker, e: ThreadStart);
1039 }
1040 ~WatchDog() {
1041 {
1042 const auto locker = qt_scoped_lock(mutex);
1043 expecting = ThreadEnd;
1044 waitCondition.notify_all();
1045 }
1046 wait();
1047 }
1048
1049 void beginTest() {
1050 const auto locker = qt_scoped_lock(mutex);
1051 expecting = TestFunctionEnd;
1052 waitCondition.notify_all();
1053 }
1054
1055 void testFinished() {
1056 const auto locker = qt_scoped_lock(mutex);
1057 expecting = TestFunctionStart;
1058 waitCondition.notify_all();
1059 }
1060
1061 void run() override {
1062 auto locker = qt_unique_lock(mutex);
1063 expecting = TestFunctionStart;
1064 waitCondition.notify_all();
1065 while (true) {
1066 switch (expecting) {
1067 case ThreadEnd:
1068 return;
1069 case ThreadStart:
1070 Q_UNREACHABLE();
1071 case TestFunctionStart:
1072 case TestFunctionEnd:
1073 if (Q_UNLIKELY(!waitFor(locker, expecting))) {
1074 stackTrace();
1075 qFatal(msg: "Test function timed out");
1076 }
1077 }
1078 }
1079 }
1080
1081private:
1082 QtPrivate::mutex mutex;
1083 QtPrivate::condition_variable waitCondition;
1084 Expectation expecting;
1085};
1086
1087#else // !QT_CONFIG(thread)
1088
1089class WatchDog : public QObject
1090{
1091public:
1092 void beginTest() {};
1093 void testFinished() {};
1094};
1095
1096#endif
1097
1098
1099/*!
1100 \internal
1101
1102 Call slot_data(), init(), slot(), cleanup(), init(), slot(), cleanup(), ...
1103 If data is set then it is the only test that is performed
1104
1105 If the function was successfully called, true is returned, otherwise
1106 false.
1107 */
1108bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) const
1109{
1110 QBenchmarkTestMethodData benchmarkData;
1111 QBenchmarkTestMethodData::current = &benchmarkData;
1112
1113 const QByteArray &name = m_methods[index].name();
1114 QBenchmarkGlobalData::current->context.slotName = QLatin1String(name) + QLatin1String("()");
1115
1116 char member[512];
1117 QTestTable table;
1118
1119 QTestResult::setCurrentTestFunction(name.constData());
1120
1121 const QTestTable *gTable = QTestTable::globalTestTable();
1122 const int globalDataCount = gTable->dataCount();
1123 int curGlobalDataIndex = 0;
1124
1125 /* For each entry in the global data table, do: */
1126 do {
1127 if (!gTable->isEmpty())
1128 QTestResult::setCurrentGlobalTestData(gTable->testData(index: curGlobalDataIndex));
1129
1130 if (curGlobalDataIndex == 0) {
1131 qsnprintf(str: member, n: 512, fmt: "%s_data()", name.constData());
1132 invokeMethod(obj: QTest::currentTestObject, methodName: member);
1133 if (QTestResult::skipCurrentTest())
1134 break;
1135 }
1136
1137 bool foundFunction = false;
1138 int curDataIndex = 0;
1139 const int dataCount = table.dataCount();
1140
1141 // Data tag requested but none available?
1142 if (data && !dataCount) {
1143 // Let empty data tag through.
1144 if (!*data)
1145 data = nullptr;
1146 else {
1147 fprintf(stderr, format: "Unknown testdata for function %s(): '%s'\n", name.constData(), data);
1148 fprintf(stderr, format: "Function has no testdata.\n");
1149 return false;
1150 }
1151 }
1152
1153 /* For each entry in this test's data table, do: */
1154 do {
1155 QTestResult::setSkipCurrentTest(false);
1156 QTestResult::setBlacklistCurrentTest(false);
1157 if (!data || !qstrcmp(str1: data, str2: table.testData(index: curDataIndex)->dataTag())) {
1158 foundFunction = true;
1159
1160 QTestPrivate::checkBlackLists(slot: name.constData(), data: dataCount ? table.testData(index: curDataIndex)->dataTag() : nullptr);
1161
1162 QTestDataSetter s(curDataIndex >= dataCount ? nullptr : table.testData(index: curDataIndex));
1163
1164 QTestPrivate::qtestMouseButtons = Qt::NoButton;
1165 if (watchDog)
1166 watchDog->beginTest();
1167 QTest::lastMouseTimestamp += 500; // Maintain at least 500ms mouse event timestamps between each test function call
1168 invokeTestOnData(index);
1169 if (watchDog)
1170 watchDog->testFinished();
1171
1172 if (data)
1173 break;
1174 }
1175 ++curDataIndex;
1176 } while (curDataIndex < dataCount);
1177
1178 if (data && !foundFunction) {
1179 fprintf(stderr, format: "Unknown testdata for function %s: '%s()'\n", name.constData(), data);
1180 fprintf(stderr, format: "Available testdata:\n");
1181 for (int i = 0; i < table.dataCount(); ++i)
1182 fprintf(stderr, format: "%s\n", table.testData(index: i)->dataTag());
1183 return false;
1184 }
1185
1186 QTestResult::setCurrentGlobalTestData(nullptr);
1187 ++curGlobalDataIndex;
1188 } while (curGlobalDataIndex < globalDataCount);
1189
1190 QTestResult::finishedCurrentTestFunction();
1191 QTestResult::setSkipCurrentTest(false);
1192 QTestResult::setBlacklistCurrentTest(false);
1193 QTestResult::setCurrentTestData(nullptr);
1194
1195 return true;
1196}
1197
1198void *fetchData(QTestData *data, const char *tagName, int typeId)
1199{
1200 QTEST_ASSERT(typeId);
1201 QTEST_ASSERT_X(data, "QTest::fetchData()", "Test data requested, but no testdata available.");
1202 QTEST_ASSERT(data->parent());
1203
1204 int idx = data->parent()->indexOf(elementName: tagName);
1205
1206 if (Q_UNLIKELY(idx == -1 || idx >= data->dataCount())) {
1207 qFatal(msg: "QFETCH: Requested testdata '%s' not available, check your _data function.",
1208 tagName);
1209 }
1210
1211 if (Q_UNLIKELY(typeId != data->parent()->elementTypeId(idx))) {
1212 qFatal(msg: "Requested type '%s' does not match available type '%s'.",
1213 QMetaType::typeName(type: typeId),
1214 QMetaType::typeName(type: data->parent()->elementTypeId(index: idx)));
1215 }
1216
1217 return data->data(index: idx);
1218}
1219
1220/*!
1221 * \internal
1222 */
1223char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...)
1224{
1225 va_list ap;
1226 va_start(ap, numArguments);
1227
1228 QByteArray arguments;
1229 arguments += prefix;
1230
1231 if (numArguments > 0) {
1232 arguments += va_arg(ap, const char *);
1233
1234 for (size_t i = 1; i < numArguments; ++i) {
1235 arguments += ", ";
1236 arguments += va_arg(ap, const char *);
1237 }
1238 }
1239
1240 va_end(ap);
1241 arguments += suffix;
1242 return qstrdup(arguments.constData());
1243}
1244
1245/*!
1246 \fn char* QTest::toHexRepresentation(const char *ba, int length)
1247
1248 Returns a pointer to a string that is the string \a ba represented
1249 as a space-separated sequence of hex characters. If the input is
1250 considered too long, it is truncated. A trucation is indicated in
1251 the returned string as an ellipsis at the end. The caller has
1252 ownership of the returned pointer and must ensure it is later passed
1253 to operator delete[].
1254
1255 \a length is the length of the string \a ba.
1256 */
1257char *toHexRepresentation(const char *ba, int length)
1258{
1259 if (length == 0)
1260 return qstrdup("");
1261
1262 /* We output at maximum about maxLen characters in order to avoid
1263 * running out of memory and flooding things when the byte array
1264 * is large.
1265 *
1266 * maxLen can't be for example 200 because Qt Test is sprinkled with fixed
1267 * size char arrays.
1268 * */
1269 const int maxLen = 50;
1270 const int len = qMin(a: maxLen, b: length);
1271 char *result = nullptr;
1272
1273 if (length > maxLen) {
1274 const int size = len * 3 + 4;
1275 result = new char[size];
1276
1277 char *const forElipsis = result + size - 5;
1278 forElipsis[0] = ' ';
1279 forElipsis[1] = '.';
1280 forElipsis[2] = '.';
1281 forElipsis[3] = '.';
1282 result[size - 1] = '\0';
1283 }
1284 else {
1285 const int size = len * 3;
1286 result = new char[size];
1287 result[size - 1] = '\0';
1288 }
1289
1290 int i = 0;
1291 int o = 0;
1292
1293 while (true) {
1294 const char at = ba[i];
1295
1296 result[o] = toHexUpper(value: at >> 4);
1297 ++o;
1298 result[o] = toHexUpper(value: at);
1299
1300 ++i;
1301 ++o;
1302 if (i == len)
1303 break;
1304 result[o] = ' ';
1305 ++o;
1306 }
1307
1308 return result;
1309}
1310
1311/*!
1312 \internal
1313 Returns the same QByteArray but with only the ASCII characters still shown;
1314 everything else is replaced with \c {\xHH}.
1315*/
1316char *toPrettyCString(const char *p, int length)
1317{
1318 bool trimmed = false;
1319 QScopedArrayPointer<char> buffer(new char[256]);
1320 const char *end = p + length;
1321 char *dst = buffer.data();
1322
1323 bool lastWasHexEscape = false;
1324 *dst++ = '"';
1325 for ( ; p != end; ++p) {
1326 // we can add:
1327 // 1 byte: a single character
1328 // 2 bytes: a simple escape sequence (\n)
1329 // 3 bytes: "" and a character
1330 // 4 bytes: an hex escape sequence (\xHH)
1331 if (dst - buffer.data() > 246) {
1332 // plus the quote, the three dots and NUL, it's 255 in the worst case
1333 trimmed = true;
1334 break;
1335 }
1336
1337 // check if we need to insert "" to break an hex escape sequence
1338 if (Q_UNLIKELY(lastWasHexEscape)) {
1339 if (fromHex(c: *p) != -1) {
1340 // yes, insert it
1341 *dst++ = '"';
1342 *dst++ = '"';
1343 }
1344 lastWasHexEscape = false;
1345 }
1346
1347 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1348 *dst++ = *p;
1349 continue;
1350 }
1351
1352 // write as an escape sequence
1353 // this means we may advance dst to buffer.data() + 247 or 250
1354 *dst++ = '\\';
1355 switch (*p) {
1356 case 0x5c:
1357 case 0x22:
1358 *dst++ = uchar(*p);
1359 break;
1360 case 0x8:
1361 *dst++ = 'b';
1362 break;
1363 case 0xc:
1364 *dst++ = 'f';
1365 break;
1366 case 0xa:
1367 *dst++ = 'n';
1368 break;
1369 case 0xd:
1370 *dst++ = 'r';
1371 break;
1372 case 0x9:
1373 *dst++ = 't';
1374 break;
1375 default:
1376 // print as hex escape
1377 *dst++ = 'x';
1378 *dst++ = toHexUpper(value: uchar(*p) >> 4);
1379 *dst++ = toHexUpper(value: uchar(*p));
1380 lastWasHexEscape = true;
1381 break;
1382 }
1383 }
1384
1385 *dst++ = '"';
1386 if (trimmed) {
1387 *dst++ = '.';
1388 *dst++ = '.';
1389 *dst++ = '.';
1390 }
1391 *dst++ = '\0';
1392 return buffer.take();
1393}
1394
1395#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1396// this used to be the signature up to and including Qt 5.9
1397// keep it for BC reasons:
1398Q_TESTLIB_EXPORT
1399char *toPrettyUnicode(const ushort *p, int length)
1400{
1401 return toPrettyUnicode(string: QStringView(p, length));
1402}
1403#endif
1404
1405/*!
1406 \internal
1407 Returns the same QString but with only the ASCII characters still shown;
1408 everything else is replaced with \c {\uXXXX}.
1409
1410 Similar to QDebug::putString().
1411*/
1412char *toPrettyUnicode(QStringView string)
1413{
1414 auto p = reinterpret_cast<const ushort *>(string.utf16());
1415 auto length = string.size();
1416 // keep it simple for the vast majority of cases
1417 bool trimmed = false;
1418 QScopedArrayPointer<char> buffer(new char[256]);
1419 const ushort *end = p + length;
1420 char *dst = buffer.data();
1421
1422 *dst++ = '"';
1423 for ( ; p != end; ++p) {
1424 if (dst - buffer.data() > 245) {
1425 // plus the quote, the three dots and NUL, it's 250, 251 or 255
1426 trimmed = true;
1427 break;
1428 }
1429
1430 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1431 *dst++ = *p;
1432 continue;
1433 }
1434
1435 // write as an escape sequence
1436 // this means we may advance dst to buffer.data() + 246 or 250
1437 *dst++ = '\\';
1438 switch (*p) {
1439 case 0x22:
1440 case 0x5c:
1441 *dst++ = uchar(*p);
1442 break;
1443 case 0x8:
1444 *dst++ = 'b';
1445 break;
1446 case 0xc:
1447 *dst++ = 'f';
1448 break;
1449 case 0xa:
1450 *dst++ = 'n';
1451 break;
1452 case 0xd:
1453 *dst++ = 'r';
1454 break;
1455 case 0x9:
1456 *dst++ = 't';
1457 break;
1458 default:
1459 *dst++ = 'u';
1460 *dst++ = toHexUpper(value: *p >> 12);
1461 *dst++ = toHexUpper(value: *p >> 8);
1462 *dst++ = toHexUpper(value: *p >> 4);
1463 *dst++ = toHexUpper(value: *p);
1464 }
1465 }
1466
1467 *dst++ = '"';
1468 if (trimmed) {
1469 *dst++ = '.';
1470 *dst++ = '.';
1471 *dst++ = '.';
1472 }
1473 *dst++ = '\0';
1474 return buffer.take();
1475}
1476
1477void TestMethods::invokeTests(QObject *testObject) const
1478{
1479 const QMetaObject *metaObject = testObject->metaObject();
1480 QTEST_ASSERT(metaObject);
1481 QTestResult::setCurrentTestFunction("initTestCase");
1482 if (m_initTestCaseDataMethod.isValid())
1483 m_initTestCaseDataMethod.invoke(object: testObject, connectionType: Qt::DirectConnection);
1484
1485 QScopedPointer<WatchDog> watchDog;
1486 if (!debuggerPresent()
1487#if QT_CONFIG(valgrind)
1488 && QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindChildProcess
1489#endif
1490 ) {
1491 watchDog.reset(other: new WatchDog);
1492 }
1493
1494 if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) {
1495 if (m_initTestCaseMethod.isValid())
1496 m_initTestCaseMethod.invoke(object: testObject, connectionType: Qt::DirectConnection);
1497
1498 // finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy.
1499 const bool previousFailed = QTestResult::currentTestFailed();
1500 QTestResult::finishedCurrentTestData();
1501 QTestResult::finishedCurrentTestDataCleanup();
1502 QTestResult::finishedCurrentTestFunction();
1503
1504 if (!QTestResult::skipCurrentTest() && !previousFailed) {
1505 for (int i = 0, count = int(m_methods.size()); i < count; ++i) {
1506 const char *data = nullptr;
1507 if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
1508 data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
1509 const bool ok = invokeTest(index: i, data, watchDog: watchDog.data());
1510 delete [] data;
1511 if (!ok)
1512 break;
1513 }
1514 }
1515
1516 QTestResult::setSkipCurrentTest(false);
1517 QTestResult::setBlacklistCurrentTest(false);
1518 QTestResult::setCurrentTestFunction("cleanupTestCase");
1519 if (m_cleanupTestCaseMethod.isValid())
1520 m_cleanupTestCaseMethod.invoke(object: testObject, connectionType: Qt::DirectConnection);
1521 QTestResult::finishedCurrentTestData();
1522 QTestResult::finishedCurrentTestDataCleanup();
1523 }
1524 QTestResult::finishedCurrentTestFunction();
1525 QTestResult::setCurrentTestFunction(nullptr);
1526}
1527
1528#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1529
1530// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
1531class DebugSymbolResolver
1532{
1533 Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
1534public:
1535 struct Symbol {
1536 Symbol() : name(nullptr), address(0) {}
1537
1538 const char *name; // Must be freed by caller.
1539 DWORD64 address;
1540 };
1541
1542 explicit DebugSymbolResolver(HANDLE process);
1543 ~DebugSymbolResolver() { cleanup(); }
1544
1545 bool isValid() const { return m_symFromAddr; }
1546
1547 Symbol resolveSymbol(DWORD64 address) const;
1548
1549private:
1550 // typedefs from DbgHelp.h/.dll
1551 struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
1552 ULONG SizeOfStruct;
1553 ULONG TypeIndex; // Type Index of symbol
1554 ULONG64 Reserved[2];
1555 ULONG Index;
1556 ULONG Size;
1557 ULONG64 ModBase; // Base Address of module comtaining this symbol
1558 ULONG Flags;
1559 ULONG64 Value; // Value of symbol, ValuePresent should be 1
1560 ULONG64 Address; // Address of symbol including base address of module
1561 ULONG Register; // register holding value or pointer to value
1562 ULONG Scope; // scope of the symbol
1563 ULONG Tag; // pdb classification
1564 ULONG NameLen; // Actual length of name
1565 ULONG MaxNameLen;
1566 CHAR Name[1]; // Name of symbol
1567 };
1568
1569 typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
1570 typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
1571
1572 void cleanup();
1573
1574 const HANDLE m_process;
1575 HMODULE m_dbgHelpLib;
1576 SymFromAddrType m_symFromAddr;
1577};
1578
1579void DebugSymbolResolver::cleanup()
1580{
1581 if (m_dbgHelpLib)
1582 FreeLibrary(m_dbgHelpLib);
1583 m_dbgHelpLib = 0;
1584 m_symFromAddr = nullptr;
1585}
1586
1587DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
1588 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
1589{
1590 bool success = false;
1591 m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
1592 if (m_dbgHelpLib) {
1593 SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
1594 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
1595 m_symFromAddr = reinterpret_cast<SymFromAddrType>(
1596 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
1597 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
1598 }
1599 if (!success)
1600 cleanup();
1601}
1602
1603DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
1604{
1605 // reserve additional buffer where SymFromAddr() will store the name
1606 struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
1607 enum { symbolNameLength = 255 };
1608
1609 char name[symbolNameLength + 1];
1610 };
1611
1612 Symbol result;
1613 if (!isValid())
1614 return result;
1615 NamedSymbolInfo symbolBuffer;
1616 memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
1617 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
1618 symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
1619 if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
1620 return result;
1621 result.name = qstrdup(symbolBuffer.Name);
1622 result.address = symbolBuffer.Address;
1623 return result;
1624}
1625
1626#endif // Q_OS_WIN && !Q_OS_WINRT
1627
1628class FatalSignalHandler
1629{
1630public:
1631 FatalSignalHandler()
1632 {
1633#if defined(Q_OS_WIN)
1634# if !defined(Q_CC_MINGW)
1635 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
1636# endif
1637# if !defined(Q_OS_WINRT)
1638 SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
1639 SetUnhandledExceptionFilter(windowsFaultHandler);
1640# endif
1641#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1642 sigemptyset(set: &handledSignals);
1643
1644 const int fatalSignals[] = {
1645 SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 };
1646
1647 struct sigaction act;
1648 memset(s: &act, c: 0, n: sizeof(act));
1649 act.sa_handler = FatalSignalHandler::signal;
1650
1651 // Remove the handler after it is invoked.
1652# if !defined(Q_OS_INTEGRITY)
1653 act.sa_flags = SA_RESETHAND;
1654# endif
1655
1656 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
1657 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
1658# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
1659 // Let the signal handlers use an alternate stack
1660 // This is necessary if SIGSEGV is to catch a stack overflow
1661# if defined(Q_CC_GNU) && defined(Q_OF_ELF)
1662 // Put the alternate stack in the .lbss (large BSS) section so that it doesn't
1663 // interfere with normal .bss symbols
1664 __attribute__((section(".lbss.altstack"), aligned(4096)))
1665# endif
1666 static char alternate_stack[16 * 1024];
1667 stack_t stack;
1668 stack.ss_flags = 0;
1669 stack.ss_size = sizeof alternate_stack;
1670 stack.ss_sp = alternate_stack;
1671 sigaltstack(ss: &stack, oss: nullptr);
1672 act.sa_flags |= SA_ONSTACK;
1673# endif
1674
1675 // Block all fatal signals in our signal handler so we don't try to close
1676 // the testlog twice.
1677 sigemptyset(set: &act.sa_mask);
1678 for (int i = 0; fatalSignals[i]; ++i)
1679 sigaddset(set: &act.sa_mask, signo: fatalSignals[i]);
1680
1681 struct sigaction oldact;
1682
1683 for (int i = 0; fatalSignals[i]; ++i) {
1684 sigaction(sig: fatalSignals[i], act: &act, oact: &oldact);
1685 if (
1686# ifdef SA_SIGINFO
1687 oldact.sa_flags & SA_SIGINFO ||
1688# endif
1689 oldact.sa_handler != SIG_DFL) {
1690 sigaction(sig: fatalSignals[i], act: &oldact, oact: nullptr);
1691 } else
1692 {
1693 sigaddset(set: &handledSignals, signo: fatalSignals[i]);
1694 }
1695 }
1696#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1697 }
1698
1699 ~FatalSignalHandler()
1700 {
1701#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1702 // Unregister any of our remaining signal handlers
1703 struct sigaction act;
1704 memset(s: &act, c: 0, n: sizeof(act));
1705 act.sa_handler = SIG_DFL;
1706
1707 struct sigaction oldact;
1708
1709 for (int i = 1; i < 32; ++i) {
1710 if (!sigismember(set: &handledSignals, signo: i))
1711 continue;
1712 sigaction(sig: i, act: &act, oact: &oldact);
1713
1714 // If someone overwrote it in the mean time, put it back
1715 if (oldact.sa_handler != FatalSignalHandler::signal)
1716 sigaction(sig: i, act: &oldact, oact: nullptr);
1717 }
1718#endif
1719 }
1720
1721private:
1722#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1723 static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
1724 {
1725 enum { maxStackFrames = 100 };
1726 char appName[MAX_PATH];
1727 if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
1728 appName[0] = 0;
1729 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
1730 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
1731 const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
1732 printf("A crash occurred in %s.\n"
1733 "Function time: %dms Total time: %dms\n\n"
1734 "Exception address: 0x%p\n"
1735 "Exception code : 0x%lx\n",
1736 appName, msecsFunctionTime, msecsTotalTime,
1737 exceptionAddress, exInfo->ExceptionRecord->ExceptionCode);
1738
1739 DebugSymbolResolver resolver(GetCurrentProcess());
1740 if (resolver.isValid()) {
1741 DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
1742 if (exceptionSymbol.name) {
1743 printf("Nearby symbol : %s\n", exceptionSymbol.name);
1744 delete [] exceptionSymbol.name;
1745 }
1746 void *stack[maxStackFrames];
1747 fputs("\nStack:\n", stdout);
1748 const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
1749 for (unsigned f = 0; f < frameCount; ++f) {
1750 DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
1751 if (symbol.name) {
1752 printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
1753 delete [] symbol.name;
1754 } else {
1755 printf("#%3u: Unable to obtain symbol\n", f + 1);
1756 }
1757 }
1758 }
1759
1760 fputc('\n', stdout);
1761 fflush(stdout);
1762
1763 return EXCEPTION_EXECUTE_HANDLER;
1764 }
1765#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1766
1767#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1768 static void signal(int signum)
1769 {
1770 const int msecsFunctionTime = qRound(d: QTestLog::msecsFunctionTime());
1771 const int msecsTotalTime = qRound(d: QTestLog::msecsTotalTime());
1772 if (signum != SIGINT) {
1773 stackTrace();
1774 if (qEnvironmentVariableIsSet(varName: "QTEST_PAUSE_ON_CRASH")) {
1775 fprintf(stderr, format: "Pausing process %d for debugging\n", getpid());
1776 raise(SIGSTOP);
1777 }
1778 }
1779 qFatal(msg: "Received signal %d\n"
1780 " Function time: %dms Total time: %dms",
1781 signum, msecsFunctionTime, msecsTotalTime);
1782# if defined(Q_OS_INTEGRITY)
1783 {
1784 struct sigaction act;
1785 memset(&act, 0, sizeof(struct sigaction));
1786 act.sa_handler = SIG_DFL;
1787 sigaction(signum, &act, NULL);
1788 }
1789# endif
1790 }
1791
1792 sigset_t handledSignals;
1793#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1794};
1795
1796} // namespace
1797
1798static void initEnvironment()
1799{
1800 qputenv(varName: "QT_QTESTLIB_RUNNING", value: "1");
1801}
1802
1803/*!
1804 Executes tests declared in \a testObject. In addition, the private slots
1805 \c{initTestCase()}, \c{cleanupTestCase()}, \c{init()} and \c{cleanup()}
1806 are executed if they exist. See \l{Creating a Test} for more details.
1807
1808 Optionally, the command line arguments \a argc and \a argv can be provided.
1809 For a list of recognized arguments, read \l {Qt Test Command Line Arguments}.
1810
1811 The following example will run all tests in \c MyTestObject:
1812
1813 \snippet code/src_qtestlib_qtestcase.cpp 18
1814
1815 This function returns 0 if no tests failed, or a value other than 0 if one
1816 or more tests failed or in case of unhandled exceptions. (Skipped tests do
1817 not influence the return value.)
1818
1819 For stand-alone test applications, the convenience macro \l QTEST_MAIN() can
1820 be used to declare a main() function that parses the command line arguments
1821 and executes the tests, avoiding the need to call this function explicitly.
1822
1823 The return value from this function is also the exit code of the test
1824 application when the \l QTEST_MAIN() macro is used.
1825
1826 For stand-alone test applications, this function should not be called more
1827 than once, as command-line options for logging test output to files and
1828 executing individual test functions will not behave correctly.
1829
1830 Note: This function is not reentrant, only one test can run at a time. A
1831 test that was executed with qExec() can't run another test via qExec() and
1832 threads are not allowed to call qExec() simultaneously.
1833
1834 If you have programatically created the arguments, as opposed to getting them
1835 from the arguments in \c main(), it is likely of interest to use
1836 QTest::qExec(QObject *, const QStringList &) since it is Unicode safe.
1837
1838 \sa QTEST_MAIN()
1839*/
1840
1841int QTest::qExec(QObject *testObject, int argc, char **argv)
1842{
1843 qInit(testObject, argc, argv);
1844 int ret = qRun();
1845 qCleanup();
1846 return ret;
1847}
1848
1849/*! \internal
1850 */
1851void QTest::qInit(QObject *testObject, int argc, char **argv)
1852{
1853 initEnvironment();
1854 QBenchmarkGlobalData::current = new QBenchmarkGlobalData;
1855
1856#if defined(Q_OS_MACOS)
1857 // Don't restore saved window state for auto tests
1858 QTestPrivate::disableWindowRestore();
1859
1860 // Disable App Nap which may cause tests to stall
1861 QTestPrivate::AppNapDisabler appNapDisabler;
1862
1863 if (qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0)) {
1864 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1865 kIOPMAssertionLevelOn, CFSTR("QtTest running tests"),
1866 &macPowerSavingDisabled);
1867 }
1868#endif
1869
1870 QTestPrivate::parseBlackList();
1871 QTestResult::reset();
1872
1873 QTEST_ASSERT(testObject);
1874 QTEST_ASSERT(!currentTestObject);
1875 currentTestObject = testObject;
1876
1877 const QMetaObject *metaObject = testObject->metaObject();
1878 QTEST_ASSERT(metaObject);
1879
1880 QTestResult::setCurrentTestObject(metaObject->className());
1881 if (argc > 0)
1882 QTestResult::setCurrentAppName(argv[0]);
1883
1884 qtest_qParseArgs(argc, argv, qml: false);
1885
1886 QTestTable::globalTestTable();
1887 QTestLog::startLogging();
1888}
1889
1890/*! \internal
1891 */
1892int QTest::qRun()
1893{
1894 QTEST_ASSERT(currentTestObject);
1895
1896#if QT_CONFIG(valgrind)
1897 int callgrindChildExitCode = 0;
1898#endif
1899
1900#ifndef QT_NO_EXCEPTIONS
1901 try {
1902#endif
1903
1904#if QT_CONFIG(valgrind)
1905 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) {
1906 if (Q_UNLIKELY(!qApp))
1907 qFatal(msg: "QtTest: -callgrind option is not available with QTEST_APPLESS_MAIN");
1908
1909 const QStringList origAppArgs(QCoreApplication::arguments());
1910 if (!QBenchmarkValgrindUtils::rerunThroughCallgrind(origAppArgs, exitCode&: callgrindChildExitCode))
1911 return -1;
1912
1913 QBenchmarkValgrindUtils::cleanup();
1914
1915 } else
1916#endif
1917 {
1918 QScopedPointer<FatalSignalHandler> handler;
1919 if (!noCrashHandler)
1920 handler.reset(other: new FatalSignalHandler);
1921
1922 TestMethods::MetaMethods commandLineMethods;
1923 for (const QString &tf : qAsConst(t&: QTest::testFunctions)) {
1924 const QByteArray tfB = tf.toLatin1();
1925 const QByteArray signature = tfB + QByteArrayLiteral("()");
1926 QMetaMethod m = TestMethods::findMethod(obj: currentTestObject, signature: signature.constData());
1927 if (!m.isValid() || !isValidSlot(sl: m)) {
1928 fprintf(stderr, format: "Unknown test function: '%s'. Possible matches:\n", tfB.constData());
1929 qPrintTestSlots(stderr, filter: tfB.constData());
1930 fprintf(stderr, format: "\n%s -functions\nlists all available test functions.\n", QTestResult::currentAppName());
1931 exit(status: 1);
1932 }
1933 commandLineMethods.push_back(x: m);
1934 }
1935 TestMethods test(currentTestObject, commandLineMethods);
1936 test.invokeTests(testObject: currentTestObject);
1937 }
1938
1939#ifndef QT_NO_EXCEPTIONS
1940 } catch (...) {
1941 QTestResult::addFailure(message: "Caught unhandled exception", __FILE__, __LINE__);
1942 if (QTestResult::currentTestFunction()) {
1943 QTestResult::finishedCurrentTestFunction();
1944 QTestResult::setCurrentTestFunction(nullptr);
1945 }
1946
1947 qCleanup();
1948
1949 // Re-throw exception to make debugging easier
1950 throw;
1951 return 1;
1952 }
1953#endif
1954
1955#if QT_CONFIG(valgrind)
1956 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess)
1957 return callgrindChildExitCode;
1958#endif
1959 // make sure our exit code is never going above 127
1960 // since that could wrap and indicate 0 test fails
1961 return qMin(a: QTestLog::failCount(), b: 127);
1962}
1963
1964/*! \internal
1965 */
1966void QTest::qCleanup()
1967{
1968 currentTestObject = nullptr;
1969
1970 QTestTable::clearGlobalTestTable();
1971 QTestLog::stopLogging();
1972
1973 delete QBenchmarkGlobalData::current;
1974 QBenchmarkGlobalData::current = nullptr;
1975
1976 QSignalDumper::endDump();
1977
1978#if defined(Q_OS_MACOS)
1979 IOPMAssertionRelease(macPowerSavingDisabled);
1980#endif
1981}
1982
1983/*!
1984 \overload
1985 \since 4.4
1986
1987 Behaves identically to qExec(QObject *, int, char**) but takes a
1988 QStringList of \a arguments instead of a \c char** list.
1989 */
1990int QTest::qExec(QObject *testObject, const QStringList &arguments)
1991{
1992 const int argc = arguments.count();
1993 QVarLengthArray<char *> argv(argc);
1994
1995 QVector<QByteArray> args;
1996 args.reserve(asize: argc);
1997
1998 for (int i = 0; i < argc; ++i)
1999 {
2000 args.append(t: arguments.at(i).toLocal8Bit().constData());
2001 argv[i] = args.last().data();
2002 }
2003
2004 return qExec(testObject, argc, argv: argv.data());
2005}
2006
2007/*! \internal
2008 */
2009void QTest::qFail(const char *statementStr, const char *file, int line)
2010{
2011 QTestResult::addFailure(message: statementStr, file, line);
2012}
2013
2014/*! \internal
2015 */
2016bool QTest::qVerify(bool statement, const char *statementStr, const char *description,
2017 const char *file, int line)
2018{
2019 return QTestResult::verify(statement, statementStr, extraInfo: description, file, line);
2020}
2021
2022/*! \fn void QTest::qSkip(const char *message, const char *file, int line)
2023\internal
2024 */
2025void QTest::qSkip(const char *message, const char *file, int line)
2026{
2027 QTestResult::addSkip(message, file, line);
2028 QTestResult::setSkipCurrentTest(true);
2029}
2030
2031/*! \fn bool QTest::qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
2032\internal
2033 */
2034bool QTest::qExpectFail(const char *dataIndex, const char *comment,
2035 QTest::TestFailMode mode, const char *file, int line)
2036{
2037 return QTestResult::expectFail(dataIndex, comment: qstrdup(comment), mode, file, line);
2038}
2039
2040/*! \internal
2041 */
2042void QTest::qWarn(const char *message, const char *file, int line)
2043{
2044 QTestLog::warn(msg: message, file, line);
2045}
2046
2047/*!
2048 Ignores messages created by qDebug(), qInfo() or qWarning(). If the \a message
2049 with the corresponding \a type is outputted, it will be removed from the
2050 test log. If the test finished and the \a message was not outputted,
2051 a test failure is appended to the test log.
2052
2053 \b {Note:} Invoking this function will only ignore one message.
2054 If the message you want to ignore is outputted twice, you have to
2055 call ignoreMessage() twice, too.
2056
2057 Example:
2058 \snippet code/src_qtestlib_qtestcase.cpp 19
2059
2060 The example above tests that QDir::mkdir() outputs the right warning when invoked
2061 with an invalid file name.
2062*/
2063void QTest::ignoreMessage(QtMsgType type, const char *message)
2064{
2065 QTestLog::ignoreMessage(type, msg: message);
2066}
2067
2068#if QT_CONFIG(regularexpression)
2069/*!
2070 \overload
2071
2072 Ignores messages created by qDebug(), qInfo() or qWarning(). If the message
2073 matching \a messagePattern
2074 with the corresponding \a type is outputted, it will be removed from the
2075 test log. If the test finished and the message was not outputted,
2076 a test failure is appended to the test log.
2077
2078 \b {Note:} Invoking this function will only ignore one message.
2079 If the message you want to ignore is outputted twice, you have to
2080 call ignoreMessage() twice, too.
2081
2082 \since 5.3
2083*/
2084void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
2085{
2086 QTestLog::ignoreMessage(type, expression: messagePattern);
2087}
2088#endif // QT_CONFIG(regularexpression)
2089
2090/*! \internal
2091 */
2092
2093#ifdef Q_OS_WIN
2094static inline bool isWindowsBuildDirectory(const QString &dirName)
2095{
2096 return dirName.compare(QLatin1String("Debug"), Qt::CaseInsensitive) == 0
2097 || dirName.compare(QLatin1String("Release"), Qt::CaseInsensitive) == 0;
2098}
2099#endif
2100
2101#if QT_CONFIG(temporaryfile)
2102/*!
2103 Extracts a directory from resources to disk. The content is extracted
2104 recursively to a temporary folder. The extracted content is removed
2105 automatically once the last reference to the return value goes out of scope.
2106
2107 \a dirName is the name of the directory to extract from resources.
2108
2109 Returns the temporary directory where the data was extracted or null in case of
2110 errors.
2111 */
2112QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
2113{
2114 QSharedPointer<QTemporaryDir> result; // null until success, then == tempDir
2115
2116 QSharedPointer<QTemporaryDir> tempDir = QSharedPointer<QTemporaryDir>::create();
2117
2118 tempDir->setAutoRemove(true);
2119
2120 if (!tempDir->isValid())
2121 return result;
2122
2123 const QString dataPath = tempDir->path();
2124 const QString resourcePath = QLatin1Char(':') + dirName;
2125 const QFileInfo fileInfo(resourcePath);
2126
2127 if (!fileInfo.isDir()) {
2128 qWarning(msg: "Resource path '%s' is not a directory.", qPrintable(resourcePath));
2129 return result;
2130 }
2131
2132 QDirIterator it(resourcePath, QDirIterator::Subdirectories);
2133 if (!it.hasNext()) {
2134 qWarning(msg: "Resource directory '%s' is empty.", qPrintable(resourcePath));
2135 return result;
2136 }
2137
2138 while (it.hasNext()) {
2139 it.next();
2140
2141 QFileInfo fileInfo = it.fileInfo();
2142
2143 if (!fileInfo.isDir()) {
2144 const QString destination = dataPath + QLatin1Char('/') + fileInfo.filePath().midRef(position: resourcePath.length());
2145 QFileInfo destinationFileInfo(destination);
2146 QDir().mkpath(dirPath: destinationFileInfo.path());
2147 if (!QFile::copy(fileName: fileInfo.filePath(), newName: destination)) {
2148 qWarning(msg: "Failed to copy '%s'.", qPrintable(fileInfo.filePath()));
2149 return result;
2150 }
2151 if (!QFile::setPermissions(filename: destination, permissionSpec: QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup)) {
2152 qWarning(msg: "Failed to set permissions on '%s'.", qPrintable(destination));
2153 return result;
2154 }
2155 }
2156 }
2157
2158 result = std::move(tempDir);
2159
2160 return result;
2161}
2162#endif // QT_CONFIG(temporaryfile)
2163
2164/*! \internal
2165 */
2166
2167QString QTest::qFindTestData(const QString& base, const char *file, int line, const char *builddir)
2168{
2169 QString found;
2170
2171 // Testdata priorities:
2172
2173 // 1. relative to test binary.
2174 if (qApp) {
2175 QDir binDirectory(QCoreApplication::applicationDirPath());
2176 if (binDirectory.exists(name: base)) {
2177 found = binDirectory.absoluteFilePath(fileName: base);
2178 }
2179#ifdef Q_OS_WIN
2180 // Windows: The executable is typically located in one of the
2181 // 'Release' or 'Debug' directories.
2182 else if (isWindowsBuildDirectory(binDirectory.dirName())
2183 && binDirectory.cdUp() && binDirectory.exists(base)) {
2184 found = binDirectory.absoluteFilePath(base);
2185 }
2186#endif // Q_OS_WIN
2187 else if (QTestLog::verboseLevel() >= 2) {
2188 const QString candidate = QDir::toNativeSeparators(pathName: QCoreApplication::applicationDirPath() + QLatin1Char('/') + base);
2189 QTestLog::info(qPrintable(
2190 QString::fromLatin1("testdata %1 not found relative to test binary [%2]; "
2191 "checking next location").arg(base, candidate)),
2192 file, line);
2193 }
2194 }
2195
2196 // 2. installed path.
2197 if (found.isEmpty()) {
2198 const char *testObjectName = QTestResult::currentTestObjectName();
2199 if (testObjectName) {
2200 const QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath);
2201 const QString candidate = QString::fromLatin1(str: "%1/%2/%3")
2202 .arg(args: testsPath, args: QFile::decodeName(localFileName: testObjectName).toLower(), args: base);
2203 if (QFileInfo::exists(file: candidate)) {
2204 found = candidate;
2205 } else if (QTestLog::verboseLevel() >= 2) {
2206 QTestLog::info(qPrintable(
2207 QString::fromLatin1("testdata %1 not found in tests install path [%2]; "
2208 "checking next location")
2209 .arg(base, QDir::toNativeSeparators(candidate))),
2210 file, line);
2211 }
2212 }
2213 }
2214
2215 // 3. relative to test source.
2216 if (found.isEmpty() && qstrncmp(str1: file, str2: ":/", len: 2) != 0) {
2217 // srcdir is the directory containing the calling source file.
2218 QFileInfo srcdir = QFileInfo(QFile::decodeName(localFileName: file)).path();
2219
2220 // If the srcdir is relative, that means it is relative to the current working
2221 // directory of the compiler at compile time, which should be passed in as `builddir'.
2222 if (!srcdir.isAbsolute() && builddir) {
2223 srcdir.setFile(QFile::decodeName(localFileName: builddir) + QLatin1String("/") + srcdir.filePath());
2224 }
2225
2226 const QString canonicalPath = srcdir.canonicalFilePath();
2227 const QString candidate = QString::fromLatin1(str: "%1/%2").arg(a1: canonicalPath, a2: base);
2228 if (!canonicalPath.isEmpty() && QFileInfo::exists(file: candidate)) {
2229 found = candidate;
2230 } else if (QTestLog::verboseLevel() >= 2) {
2231 QTestLog::info(qPrintable(
2232 QString::fromLatin1("testdata %1 not found relative to source path [%2]")
2233 .arg(base, QDir::toNativeSeparators(candidate))),
2234 file, line);
2235 }
2236 }
2237
2238 // 4. Try resources
2239 if (found.isEmpty()) {
2240 const QString candidate = QString::fromLatin1(str: ":/%1").arg(a: base);
2241 if (QFileInfo::exists(file: candidate)) {
2242 found = candidate;
2243 } else if (QTestLog::verboseLevel() >= 2) {
2244 QTestLog::info(qPrintable(
2245 QString::fromLatin1("testdata %1 not found in resources [%2]")
2246 .arg(base, QDir::toNativeSeparators(candidate))),
2247 file, line);
2248 }
2249 }
2250
2251 // 5. Try current directory
2252 if (found.isEmpty()) {
2253 const QString candidate = QDir::currentPath() + QLatin1Char('/') + base;
2254 if (QFileInfo::exists(file: candidate)) {
2255 found = candidate;
2256 } else if (QTestLog::verboseLevel() >= 2) {
2257 QTestLog::info(qPrintable(
2258 QString::fromLatin1("testdata %1 not found in current directory [%2]")
2259 .arg(base, QDir::toNativeSeparators(candidate))),
2260 file, line);
2261 }
2262 }
2263
2264 // 6. Try main source directory
2265 if (found.isEmpty()) {
2266 const QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base;
2267 if (QFileInfo::exists(file: candidate)) {
2268 found = candidate;
2269 } else if (QTestLog::verboseLevel() >= 2) {
2270 QTestLog::info(qPrintable(
2271 QString::fromLatin1("testdata %1 not found in main source directory [%2]")
2272 .arg(base, QDir::toNativeSeparators(candidate))),
2273 file, line);
2274 }
2275 }
2276
2277 if (found.isEmpty()) {
2278 QTest::qWarn(qPrintable(
2279 QString::fromLatin1("testdata %1 could not be located!").arg(base)),
2280 file, line);
2281 } else if (QTestLog::verboseLevel() >= 1) {
2282 QTestLog::info(qPrintable(
2283 QString::fromLatin1("testdata %1 was located at %2").arg(base, QDir::toNativeSeparators(found))),
2284 file, line);
2285 }
2286
2287 return found;
2288}
2289
2290/*! \internal
2291 */
2292QString QTest::qFindTestData(const char *base, const char *file, int line, const char *builddir)
2293{
2294 return qFindTestData(base: QFile::decodeName(localFileName: base), file, line, builddir);
2295}
2296
2297/*! \internal
2298 */
2299void *QTest::qData(const char *tagName, int typeId)
2300{
2301 return fetchData(data: QTestResult::currentTestData(), tagName, typeId);
2302}
2303
2304/*! \internal
2305 */
2306void *QTest::qGlobalData(const char *tagName, int typeId)
2307{
2308 return fetchData(data: QTestResult::currentGlobalTestData(), tagName, typeId);
2309}
2310
2311/*! \internal
2312 */
2313void *QTest::qElementData(const char *tagName, int metaTypeId)
2314{
2315 QTEST_ASSERT(tagName);
2316 QTestData *data = QTestResult::currentTestData();
2317 QTEST_ASSERT(data);
2318 QTEST_ASSERT(data->parent());
2319
2320 int idx = data->parent()->indexOf(elementName: tagName);
2321 QTEST_ASSERT(idx != -1);
2322 QTEST_ASSERT(data->parent()->elementTypeId(idx) == metaTypeId);
2323
2324 return data->data(index: data->parent()->indexOf(elementName: tagName));
2325}
2326
2327/*! \internal
2328 */
2329void QTest::addColumnInternal(int id, const char *name)
2330{
2331 QTestTable *tbl = QTestTable::currentTestTable();
2332 QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot.");
2333
2334 tbl->addColumn(elementType: id, elementName: name);
2335}
2336
2337/*!
2338 Appends a new row to the current test data. \a dataTag is the name of
2339 the testdata that will appear in the test output. Returns a QTestData reference
2340 that can be used to stream in data.
2341
2342 Example:
2343 \snippet code/src_qtestlib_qtestcase.cpp 20
2344
2345 \b {Note:} This macro can only be used in a test's data function
2346 that is invoked by the test framework.
2347
2348 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2349 a more extensive example.
2350
2351 \sa addColumn(), QFETCH()
2352*/
2353QTestData &QTest::newRow(const char *dataTag)
2354{
2355 QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null");
2356 QTestTable *tbl = QTestTable::currentTestTable();
2357 QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot.");
2358 QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()", "Must add columns before attempting to add rows.");
2359
2360 return *tbl->newData(tag: dataTag);
2361}
2362
2363/*!
2364 \since 5.9
2365
2366 Appends a new row to the current test data. The function's arguments are passed
2367 to qsnprintf() for formatting according to \a format. See the qvsnprintf()
2368 documentation for caveats and limitations.
2369
2370 The formatted string will appear as the name of this test data in the test output.
2371
2372 Returns a QTestData reference that can be used to stream in data.
2373
2374 Example:
2375 \snippet code/src_qtestlib_qtestcase.cpp addRow
2376
2377 \b {Note:} This function can only be used in a test's data function
2378 that is invoked by the test framework.
2379
2380 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2381 a more extensive example.
2382
2383 \sa addColumn(), QFETCH()
2384*/
2385QTestData &QTest::addRow(const char *format, ...)
2386{
2387 QTEST_ASSERT_X(format, "QTest::addRow()", "Format string cannot be null");
2388 QTestTable *tbl = QTestTable::currentTestTable();
2389 QTEST_ASSERT_X(tbl, "QTest::addRow()", "Cannot add testdata outside of a _data slot.");
2390 QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()", "Must add columns before attempting to add rows.");
2391
2392 char buf[1024];
2393
2394 va_list va;
2395 va_start(va, format);
2396 // we don't care about failures, we accept truncation, as well as trailing garbage.
2397 // Names with more than 1K characters are nonsense, anyway.
2398 (void)qvsnprintf(str: buf, n: sizeof buf, fmt: format, ap: va);
2399 buf[sizeof buf - 1] = '\0';
2400 va_end(va);
2401
2402 return *tbl->newData(tag: buf);
2403}
2404
2405/*! \fn template <typename T> void QTest::addColumn(const char *name, T *dummy = 0)
2406
2407 Adds a column with type \c{T} to the current test data.
2408 \a name is the name of the column. \a dummy is a workaround
2409 for buggy compilers and can be ignored.
2410
2411 To populate the column with values, newRow() can be used. Use
2412 \l QFETCH() to fetch the data in the actual test.
2413
2414 Example:
2415 \snippet code/src_qtestlib_qtestcase.cpp 21
2416
2417 To add custom types to the testdata, the type must be registered with
2418 QMetaType via \l Q_DECLARE_METATYPE().
2419
2420 \b {Note:} This macro can only be used in a test's data function
2421 that is invoked by the test framework.
2422
2423 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2424 a more extensive example.
2425
2426 \sa QTest::newRow(), QFETCH(), QMetaType
2427*/
2428
2429/*!
2430 Returns the name of the binary that is currently executed.
2431*/
2432const char *QTest::currentAppName()
2433{
2434 return QTestResult::currentAppName();
2435}
2436
2437/*!
2438 Returns the name of the test function that is currently executed.
2439
2440 Example:
2441
2442 \snippet code/src_qtestlib_qtestcase.cpp 22
2443*/
2444const char *QTest::currentTestFunction()
2445{
2446 return QTestResult::currentTestFunction();
2447}
2448
2449/*!
2450 Returns the name of the current test data. If the test doesn't
2451 have any assigned testdata, the function returns 0.
2452*/
2453const char *QTest::currentDataTag()
2454{
2455 return QTestResult::currentDataTag();
2456}
2457
2458/*!
2459 Returns \c true if the current test function failed, otherwise false.
2460*/
2461bool QTest::currentTestFailed()
2462{
2463 return QTestResult::currentTestFailed();
2464}
2465
2466/*!
2467 Sleeps for \a ms milliseconds, blocking execution of the
2468 test. qSleep() will not do any event processing and leave your test
2469 unresponsive. Network communication might time out while
2470 sleeping. Use \l {QTest::qWait()} to do non-blocking sleeping.
2471
2472 \a ms must be greater than 0.
2473
2474 \b {Note:} The qSleep() function calls either \c nanosleep() on
2475 unix or \c Sleep() on windows, so the accuracy of time spent in
2476 qSleep() depends on the operating system.
2477
2478 Example:
2479 \snippet code/src_qtestlib_qtestcase.cpp 23
2480
2481 \sa {QTest::qWait()}
2482*/
2483void QTest::qSleep(int ms)
2484{
2485 // ### Qt 6, move to QtCore or remove altogether
2486 QTEST_ASSERT(ms > 0);
2487 QTestPrivate::qSleep(ms);
2488}
2489
2490/*! \internal
2491 */
2492QObject *QTest::testObject()
2493{
2494 return currentTestObject;
2495}
2496
2497/*! \internal
2498 */
2499void QTest::setMainSourcePath(const char *file, const char *builddir)
2500{
2501 QString mainSourceFile = QFile::decodeName(localFileName: file);
2502 QFileInfo fi;
2503 if (builddir)
2504 fi.setFile(dir: QDir(QFile::decodeName(localFileName: builddir)), file: mainSourceFile);
2505 else
2506 fi.setFile(mainSourceFile);
2507 QTest::mainSourcePath = fi.absolutePath();
2508}
2509
2510/*! \internal
2511 This function is called by various specializations of QTest::qCompare
2512 to decide whether to report a failure and to produce verbose test output.
2513
2514 The failureMsg parameter can be null, in which case a default message
2515 will be output if the compare fails. If the compare succeeds, failureMsg
2516 will not be output.
2517
2518 If the caller has already passed a failure message showing the compared
2519 values, or if those values cannot be stringified, val1 and val2 can be null.
2520 */
2521bool QTest::compare_helper(bool success, const char *failureMsg,
2522 char *val1, char *val2,
2523 const char *actual, const char *expected,
2524 const char *file, int line)
2525{
2526 return QTestResult::compare(success, failureMsg, val1, val2, actual, expected, file, line);
2527}
2528
2529template <typename T>
2530static bool floatingCompare(const T &actual, const T &expected)
2531{
2532 switch (qFpClassify(expected))
2533 {
2534 case FP_INFINITE:
2535 return (expected < 0) == (actual < 0) && qFpClassify(actual) == FP_INFINITE;
2536 case FP_NAN:
2537 return qFpClassify(actual) == FP_NAN;
2538 default:
2539 if (!qFuzzyIsNull(expected))
2540 return qFuzzyCompare(actual, expected);
2541 Q_FALLTHROUGH();
2542 case FP_SUBNORMAL: // subnormal is always fuzzily null
2543 case FP_ZERO:
2544 return qFuzzyIsNull(actual);
2545 }
2546}
2547
2548/*! \fn bool QTest::qCompare(const qfloat16 &t1, const qfloat16 &t2, const char *actual, const char *expected, const char *file, int line)
2549 \internal
2550 */
2551bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
2552 const char *file, int line)
2553{
2554 return compare_helper(success: floatingCompare(actual: t1, expected: t2),
2555 failureMsg: "Compared qfloat16s are not the same (fuzzy compare)",
2556 val1: toString(t1), val2: toString(t2), actual, expected, file, line);
2557}
2558
2559/*! \fn bool QTest::qCompare(const float &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
2560 \internal
2561 */
2562bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const char *expected,
2563 const char *file, int line)
2564{
2565 return QTestResult::compare(success: floatingCompare(actual: t1, expected: t2),
2566 failureMsg: "Compared floats are not the same (fuzzy compare)",
2567 val1: t1, val2: t2, actual, expected, file, line);
2568}
2569
2570/*! \fn bool QTest::qCompare(const double &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
2571 \internal
2572 */
2573bool QTest::qCompare(double const &t1, double const &t2, const char *actual, const char *expected,
2574 const char *file, int line)
2575{
2576 return QTestResult::compare(success: floatingCompare(actual: t1, expected: t2),
2577 failureMsg: "Compared doubles are not the same (fuzzy compare)",
2578 val1: t1, val2: t2, actual, expected, file, line);
2579}
2580
2581/*! \fn bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected, const char *file, int line)
2582 \internal
2583 \since 5.14
2584 */
2585bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
2586 const char *file, int line)
2587{
2588 return QTestResult::compare(success: t1 == t2,
2589 failureMsg: "Compared values are not the same",
2590 val1: t1, val2: t2, actual, expected, file, line);
2591}
2592
2593/*! \fn bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected, const char *file, int line)
2594 \internal
2595 \since 5.14
2596 */
2597bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected,
2598 const char *file, int line)
2599{
2600 return QTestResult::compare(success: t1 == t2,
2601 failureMsg: "Compared values are not the same",
2602 val1: t1, val2: t2, actual, expected, file, line);
2603}
2604
2605/*! \fn bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
2606 \internal
2607 \since 5.14
2608 */
2609bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected,
2610 const char *file, int line)
2611{
2612 return QTestResult::compare(success: t1 == t2,
2613 failureMsg: "Compared values are not the same",
2614 val1: t1, val2: t2, actual, expected, file, line);
2615}
2616
2617/*! \fn bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
2618 \internal
2619 \since 5.14
2620 */
2621bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual, const char *expected,
2622 const char *file, int line)
2623{
2624 return QTestResult::compare(success: t1 == t2,
2625 failureMsg: "Compared values are not the same",
2626 val1: t1, val2: t2, actual, expected, file, line);
2627}
2628
2629/*! \fn bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
2630 \internal
2631 \since 5.14
2632 */
2633bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual, const char *expected,
2634 const char *file, int line)
2635{
2636 return QTestResult::compare(success: t1 == t2,
2637 failureMsg: "Compared values are not the same",
2638 val1: t1, val2: t2, actual, expected, file, line);
2639}
2640
2641/*! \fn bool QTest::qCompare(const QString &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
2642 \internal
2643 \since 5.14
2644 */
2645
2646/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
2647 \internal
2648 \since 5.14
2649 */
2650
2651/*! \fn bool QTest::qCompare(const QLatin1String &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
2652 \internal
2653 \since 5.14
2654 */
2655
2656/*! \fn bool QTest::qCompare(const double &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
2657 \internal
2658 */
2659
2660/*! \fn bool QTest::qCompare(const float &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
2661 \internal
2662 */
2663
2664#define TO_STRING_IMPL(TYPE, FORMAT) \
2665template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
2666{ \
2667 char *msg = new char[128]; \
2668 qsnprintf(msg, 128, #FORMAT, t); \
2669 return msg; \
2670}
2671
2672TO_STRING_IMPL(short, %hd)
2673TO_STRING_IMPL(ushort, %hu)
2674TO_STRING_IMPL(int, %d)
2675TO_STRING_IMPL(uint, %u)
2676TO_STRING_IMPL(long, %ld)
2677TO_STRING_IMPL(ulong, %lu)
2678#if defined(Q_OS_WIN)
2679TO_STRING_IMPL(qint64, %I64d)
2680TO_STRING_IMPL(quint64, %I64u)
2681#else
2682TO_STRING_IMPL(qint64, %lld)
2683TO_STRING_IMPL(quint64, %llu)
2684#endif
2685TO_STRING_IMPL(bool, %d)
2686TO_STRING_IMPL(signed char, %hhd)
2687TO_STRING_IMPL(unsigned char, %hhu)
2688
2689/*!
2690 \internal
2691
2692 Be consistent about leading 0 in exponent.
2693
2694 POSIX specifies that %e (hence %g when using it) uses at least two digits in
2695 the exponent, requiring a leading 0 on single-digit exponents; (at least)
2696 MinGW includes a leading zero also on an already-two-digit exponent,
2697 e.g. 9e-040, which differs from more usual platforms. So massage that away.
2698 */
2699static void massageExponent(char *text)
2700{
2701 char *p = strchr(s: text, c: 'e');
2702 if (!p)
2703 return;
2704 const char *const end = p + strlen(s: p); // *end is '\0'
2705 p += (p[1] == '-' || p[1] == '+') ? 2 : 1;
2706 if (p[0] != '0' || end - 2 <= p)
2707 return;
2708 // We have a leading 0 on an exponent of at least two more digits
2709 const char *n = p + 1;
2710 while (end - 2 > n && n[0] == '0')
2711 ++n;
2712 memmove(dest: p, src: n, n: end + 1 - n);
2713}
2714
2715// Be consistent about display of infinities and NaNs (snprintf()'s varies,
2716// notably on MinGW, despite POSIX documenting "[-]inf" or "[-]infinity" for %f,
2717// %e and %g, uppercasing for their capital versions; similar for "nan"):
2718#define TO_STRING_FLOAT(TYPE, FORMAT) \
2719template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
2720{ \
2721 char *msg = new char[128]; \
2722 switch (qFpClassify(t)) { \
2723 case FP_INFINITE: \
2724 qstrncpy(msg, (t < 0 ? "-inf" : "inf"), 128); \
2725 break; \
2726 case FP_NAN: \
2727 qstrncpy(msg, "nan", 128); \
2728 break; \
2729 default: \
2730 qsnprintf(msg, 128, #FORMAT, double(t)); \
2731 massageExponent(msg); \
2732 break; \
2733 } \
2734 return msg; \
2735}
2736
2737TO_STRING_FLOAT(qfloat16, %.3g)
2738TO_STRING_FLOAT(float, %g)
2739TO_STRING_FLOAT(double, %.12g)
2740
2741template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t)
2742{
2743 unsigned char c = static_cast<unsigned char>(t);
2744 char *msg = new char[16];
2745 switch (c) {
2746 case 0x00:
2747 qstrcpy(dst: msg, src: "'\\0'");
2748 break;
2749 case 0x07:
2750 qstrcpy(dst: msg, src: "'\\a'");
2751 break;
2752 case 0x08:
2753 qstrcpy(dst: msg, src: "'\\b'");
2754 break;
2755 case 0x09:
2756 qstrcpy(dst: msg, src: "'\\t'");
2757 break;
2758 case 0x0a:
2759 qstrcpy(dst: msg, src: "'\\n'");
2760 break;
2761 case 0x0b:
2762 qstrcpy(dst: msg, src: "'\\v'");
2763 break;
2764 case 0x0c:
2765 qstrcpy(dst: msg, src: "'\\f'");
2766 break;
2767 case 0x0d:
2768 qstrcpy(dst: msg, src: "'\\r'");
2769 break;
2770 case 0x22:
2771 qstrcpy(dst: msg, src: "'\\\"'");
2772 break;
2773 case 0x27:
2774 qstrcpy(dst: msg, src: "'\\\''");
2775 break;
2776 case 0x5c:
2777 qstrcpy(dst: msg, src: "'\\\\'");
2778 break;
2779 default:
2780 if (c < 0x20 || c >= 0x7F)
2781 qsnprintf(str: msg, n: 16, fmt: "'\\x%02x'", c);
2782 else
2783 qsnprintf(str: msg, n: 16, fmt: "'%c'" , c);
2784 }
2785 return msg;
2786}
2787
2788/*! \internal
2789 */
2790char *QTest::toString(const char *str)
2791{
2792 if (!str) {
2793 char *msg = new char[1];
2794 *msg = '\0';
2795 return msg;
2796 }
2797 char *msg = new char[strlen(s: str) + 1];
2798 return qstrcpy(dst: msg, src: str);
2799}
2800
2801/*! \internal
2802 */
2803char *QTest::toString(const volatile void *p) // Use volatile to match compare_ptr_helper()
2804{
2805 return QTest::toString(const_cast<const void *>(p));
2806}
2807
2808char *QTest::toString(const void *p)
2809{
2810 char *msg = new char[128];
2811 qsnprintf(str: msg, n: 128, fmt: "%p", p);
2812 return msg;
2813}
2814
2815/*! \fn char *QTest::toString(const QColor &color)
2816 \internal
2817 */
2818
2819/*! \fn char *QTest::toString(const QRegion &region)
2820 \internal
2821 */
2822
2823/*! \fn char *QTest::toString(const QHostAddress &addr)
2824 \internal
2825 */
2826
2827/*! \fn char *QTest::toString(QNetworkReply::NetworkError code)
2828 \internal
2829 */
2830
2831/*! \fn char *QTest::toString(const QNetworkCookie &cookie)
2832 \internal
2833 */
2834
2835/*! \fn char *QTest::toString(const QList<QNetworkCookie> &list)
2836 \internal
2837 */
2838
2839/*! \internal
2840 */
2841bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
2842 const char *expected, const char *file, int line)
2843{
2844 return compare_helper(success: qstrcmp(str1: t1, str2: t2) == 0, failureMsg: "Compared strings are not the same",
2845 val1: toString(str: t1), val2: toString(str: t2), actual, expected, file, line);
2846}
2847
2848/*!
2849 \namespace QTest::Internal
2850 \internal
2851*/
2852
2853/*! \fn bool QTest::compare_ptr_helper(const volatile void *t1, const volatile void *t2, const char *actual, const char *expected, const char *file, int line)
2854 \internal
2855*/
2856
2857/*! \fn bool QTest::compare_ptr_helper(const volatile void *t1, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
2858 \internal
2859*/
2860
2861/*! \fn bool QTest::compare_ptr_helper(std::nullptr_t, const volatile void *t2, const char *actual, const char *expected, const char *file, int line)
2862 \internal
2863*/
2864
2865/*! \fn template <typename T1, typename T2> bool QTest::qCompare(const T1 &t1, const T2 &t2, const char *actual, const char *expected, const char *file, int line)
2866 \internal
2867*/
2868
2869/*! \fn bool QTest::qCompare(const QIcon &t1, const QIcon &t2, const char *actual, const char *expected, const char *file, int line)
2870 \internal
2871*/
2872
2873/*! \fn bool QTest::qCompare(const QImage &t1, const QImage &t2, const char *actual, const char *expected, const char *file, int line)
2874 \internal
2875*/
2876
2877/*! \fn bool QTest::qCompare(const QPixmap &t1, const QPixmap &t2, const char *actual, const char *expected, const char *file, int line)
2878 \internal
2879*/
2880
2881/*! \fn template <typename T> bool QTest::qCompare(const T &t1, const T &t2, const char *actual, const char *expected, const char *file, int line)
2882 \internal
2883*/
2884
2885/*! \fn template <typename T> bool QTest::qCompare(const T *t1, const T *t2, const char *actual, const char *expected, const char *file, int line)
2886 \internal
2887*/
2888
2889/*! \fn template <typename T> bool QTest::qCompare(T *t, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
2890 \internal
2891*/
2892
2893/*! \fn template <typename T> bool QTest::qCompare(std::nullptr_t, T *t, const char *actual, const char *expected, const char *file, int line)
2894 \internal
2895*/
2896
2897/*! \fn template <typename T> bool QTest::qCompare(T *t1, T *t2, const char *actual, const char *expected, const char *file, int line)
2898 \internal
2899*/
2900
2901/*! \fn template <typename T1, typename T2> bool QTest::qCompare(const T1 *t1, const T2 *t2, const char *actual, const char *expected, const char *file, int line)
2902 \internal
2903*/
2904
2905/*! \fn template <typename T1, typename T2> bool QTest::qCompare(T1 *t1, T2 *t2, const char *actual, const char *expected, const char *file, int line)
2906 \internal
2907*/
2908
2909/*! \fn bool QTest::qCompare(const char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
2910 \internal
2911*/
2912
2913/*! \fn bool QTest::qCompare(char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
2914 \internal
2915*/
2916
2917/*! \fn bool QTest::qCompare(char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
2918 \internal
2919*/
2920
2921/*! \fn bool QTest::qCompare(const char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
2922 \internal
2923*/
2924
2925/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
2926 \internal
2927*/
2928
2929/*! \fn bool QTest::qCompare(const QLatin1String &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
2930 \internal
2931*/
2932
2933/*! \fn bool QTest::qCompare(const QStringList &t1, const QStringList &t2, const char *actual, const char *expected, const char *file, int line)
2934 \internal
2935*/
2936
2937/*! \fn template <typename T> bool QTest::qCompare(const QList<T> &t1, const QList<T> &t2, const char *actual, const char *expected, const char *file, int line)
2938 \internal
2939*/
2940
2941/*! \fn template <typename T> bool QTest::qCompare(const QFlags<T> &t1, const T &t2, const char *actual, const char *expected, const char *file, int line)
2942 \internal
2943*/
2944
2945/*! \fn template <typename T> bool QTest::qCompare(const QFlags<T> &t1, const int &t2, const char *actual, const char *expected, const char *file, int line)
2946 \internal
2947*/
2948
2949/*! \fn bool QTest::qCompare(const qint64 &t1, const qint32 &t2, const char *actual, const char *expected, const char *file, int line)
2950 \internal
2951*/
2952
2953/*! \fn bool QTest::qCompare(const qint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
2954 \internal
2955*/
2956
2957/*! \fn bool QTest::qCompare(const quint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
2958 \internal
2959*/
2960
2961/*! \fn bool QTest::qCompare(const qint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
2962 \internal
2963*/
2964
2965/*! \fn bool QTest::qCompare(const quint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
2966 \internal
2967*/
2968
2969/*! \fn bool QTest::qCompare(const quint32 &t1, const quint64 &t2, const char *actual, const char *expected, const char *file, int line)
2970 \internal
2971*/
2972
2973/*! \fn template <typename T> bool QTest::qTest(const T& actual, const char *elementName, const char *actualStr, const char *expected, const char *file, int line)
2974 \internal
2975*/
2976
2977/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
2978 \internal
2979*/
2980
2981/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
2982 \internal
2983*/
2984
2985/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
2986 \internal
2987*/
2988
2989/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
2990 \internal
2991*/
2992
2993/*! \fn void QTest::simulateEvent(QWidget *widget, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
2994 \internal
2995*/
2996
2997/*! \fn void QTest::simulateEvent(QWindow *window, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
2998 \internal
2999*/
3000
3001QT_END_NAMESPACE
3002

source code of qtbase/src/testlib/qtestcase.cpp