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