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