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 == 1) {
194#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
195 struct rlimit limit;
196 limit.rlim_cur = 0;
197 limit.rlim_max = 0;
198 if (setrlimit(RLIMIT_CORE, &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 == 1)
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 (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 (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 = (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_MAC) && defined(HAVE_XCTEST)
791 } else if (int skip = QXcodeTestLogger::parseCommandLineArgument(argv[i])) {
792 i += (skip - 1); // Eating argv[i] with a continue counts towards skips
793 continue;
794#endif
795 } else if (argv[i][0] == '-') {
796 fprintf(stderr, "Unknown option: '%s'\n\n%s", argv[i], testOptions);
797 if (qml) {
798 fprintf(stderr, "\nqmltest related options:\n"
799 " -import : Specify an import directory.\n"
800 " -plugins : Specify a directory where to search for plugins.\n"
801 " -input : Specify the root directory for test cases.\n"
802 );
803 }
804
805 fprintf(stderr, "\n"
806 " -help : This help\n");
807 exit(1);
808 } else {
809 // We can't check the availability of test functions until
810 // we load the QML files. So just store the data for now.
811 int colon = -1;
812 int offset;
813 for (offset = 0; argv[i][offset]; ++offset) {
814 if (argv[i][offset] == ':') {
815 if (argv[i][offset + 1] == ':') {
816 // "::" is used as a test name separator.
817 // e.g. "ClickTests::test_click:row1".
818 ++offset;
819 } else {
820 colon = offset;
821 break;
822 }
823 }
824 }
825 if (colon == -1) {
826 QTest::testFunctions += QString::fromLatin1(argv[i]);
827 QTest::testTags += QString();
828 } else {
829 QTest::testFunctions +=
830 QString::fromLatin1(argv[i], colon);
831 QTest::testTags +=
832 QString::fromLatin1(argv[i] + colon + 1);
833 }
834 }
835 }
836
837 bool installedTestCoverage = installCoverageTool(QTestResult::currentAppName(), QTestResult::currentTestObjectName());
838 QTestLog::setInstalledTestCoverage(installedTestCoverage);
839
840 // If no loggers were created by the long version of the -o command-line
841 // option, but a logger was requested via the old-style option, add it.
842 const bool explicitLoggerRequested = logFormat != -1;
843 if (QTestLog::loggerCount() == 0 && explicitLoggerRequested)
844 QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename);
845
846 bool addFallbackLogger = !explicitLoggerRequested;
847
848#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
849 // Any explicitly requested loggers will be added by now, so we can check if they use stdout
850 const bool safeToAddAppleLogger = !AppleUnifiedLogger::willMirrorToStderr() || !QTestLog::loggerUsingStdout();
851 if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
852 QTestLog::addLogger(QTestLog::Apple, nullptr);
853 if (AppleUnifiedLogger::willMirrorToStderr() && !logFilename)
854 addFallbackLogger = false; // Prevent plain test logger fallback below
855 }
856#endif
857
858 if (addFallbackLogger)
859 QTestLog::addLogger(QTestLog::Plain, logFilename);
860}
861
862// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
863Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) {
864 qtest_qParseArgs(argc, const_cast<const char *const *>(argv), qml);
865}
866
867QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container)
868{
869 const int count = container.count();
870 if (count == 0)
871 return QBenchmarkResult();
872
873 if (count == 1)
874 return container.front();
875
876 QList<QBenchmarkResult> containerCopy = container;
877 std::sort(containerCopy.begin(), containerCopy.end());
878
879 const int middle = count / 2;
880
881 // ### handle even-sized containers here by doing an aritmetic mean of the two middle items.
882 return containerCopy.at(middle);
883}
884
885struct QTestDataSetter
886{
887 QTestDataSetter(QTestData *data)
888 {
889 QTestResult::setCurrentTestData(data);
890 }
891 ~QTestDataSetter()
892 {
893 QTestResult::setCurrentTestData(nullptr);
894 }
895};
896
897namespace {
898
899qreal addResult(qreal current, const QBenchmarkResult& r)
900{
901 return current + r.value;
902}
903
904}
905
906void TestMethods::invokeTestOnData(int index) const
907{
908 /* Benchmarking: for each median iteration*/
909
910 bool isBenchmark = false;
911 int i = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
912
913 QList<QBenchmarkResult> results;
914 bool minimumTotalReached = false;
915 do {
916 QBenchmarkTestMethodData::current->beginDataRun();
917
918 /* Benchmarking: for each accumulation iteration*/
919 bool invokeOk;
920 do {
921 if (m_initMethod.isValid())
922 m_initMethod.invoke(QTest::currentTestObject, Qt::DirectConnection);
923 if (QTestResult::skipCurrentTest() || QTestResult::currentTestFailed())
924 break;
925
926 QBenchmarkTestMethodData::current->result = QBenchmarkResult();
927 QBenchmarkTestMethodData::current->resultAccepted = false;
928
929 QBenchmarkGlobalData::current->context.tag =
930 QLatin1String(
931 QTestResult::currentDataTag()
932 ? QTestResult::currentDataTag() : "");
933
934 invokeOk = m_methods[index].invoke(QTest::currentTestObject, Qt::DirectConnection);
935 if (!invokeOk)
936 QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__);
937
938 isBenchmark = QBenchmarkTestMethodData::current->isBenchmark();
939
940 QTestResult::finishedCurrentTestData();
941
942 if (m_cleanupMethod.isValid())
943 m_cleanupMethod.invoke(QTest::currentTestObject, Qt::DirectConnection);
944
945 // Process any deleteLater(), like event-loop based apps would do. Fixes memleak reports.
946 if (QCoreApplication::instance())
947 QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
948
949 // If the test isn't a benchmark, finalize the result after cleanup() has finished.
950 if (!isBenchmark)
951 QTestResult::finishedCurrentTestDataCleanup();
952
953 // If this test method has a benchmark, repeat until all measurements are
954 // acceptable.
955 // The QBENCHMARK macro increases the number of iterations for each run until
956 // this happens.
957 } while (invokeOk && isBenchmark
958 && QBenchmarkTestMethodData::current->resultsAccepted() == false
959 && !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed());
960
961 QBenchmarkTestMethodData::current->endDataRun();
962 if (!QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed()) {
963 if (i > -1) // iteration -1 is the warmup iteration.
964 results.append(QBenchmarkTestMethodData::current->result);
965
966 if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput) {
967 if (i == -1) {
968 QTestLog::info(qPrintable(
969 QString::fromLatin1("warmup stage result : %1")
970 .arg(QBenchmarkTestMethodData::current->result.value)), nullptr, 0);
971 } else {
972 QTestLog::info(qPrintable(
973 QString::fromLatin1("accumulation stage result: %1")
974 .arg(QBenchmarkTestMethodData::current->result.value)), nullptr, 0);
975 }
976 }
977 }
978
979 // Verify if the minimum total measurement is reached, if it was specified:
980 if (QBenchmarkGlobalData::current->minimumTotal == -1) {
981 minimumTotalReached = true;
982 } else {
983 const qreal total = std::accumulate(results.begin(), results.end(), 0.0, addResult);
984 minimumTotalReached = (total >= QBenchmarkGlobalData::current->minimumTotal);
985 }
986 } while (isBenchmark
987 && ((++i < QBenchmarkGlobalData::current->adjustMedianIterationCount()) || !minimumTotalReached)
988 && !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed());
989
990 // If the test is a benchmark, finalize the result after all iterations have finished.
991 if (isBenchmark) {
992 bool testPassed = !QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed();
993 QTestResult::finishedCurrentTestDataCleanup();
994 // Only report benchmark figures if the test passed
995 if (testPassed && QBenchmarkTestMethodData::current->resultsAccepted())
996 QTestLog::addBenchmarkResult(qMedian(results));
997 }
998}
999
1000#if QT_CONFIG(thread)
1001
1002class WatchDog : public QThread
1003{
1004 enum Expectation {
1005 ThreadStart,
1006 TestFunctionStart,
1007 TestFunctionEnd,
1008 ThreadEnd,
1009 };
1010
1011 bool waitFor(std::unique_lock<QtPrivate::mutex> &m, Expectation e) {
1012 auto expectationChanged = [this, e] { return expecting != e; };
1013 switch (e) {
1014 case TestFunctionEnd:
1015 return waitCondition.wait_for(m, defaultTimeout(), expectationChanged);
1016 case ThreadStart:
1017 case ThreadEnd:
1018 case TestFunctionStart:
1019 waitCondition.wait(m, expectationChanged);
1020 return true;
1021 }
1022 Q_UNREACHABLE();
1023 return false;
1024 }
1025
1026public:
1027 WatchDog()
1028 {
1029 setObjectName(QLatin1String("QtTest Watchdog"));
1030 auto locker = qt_unique_lock(mutex);
1031 expecting = ThreadStart;
1032 start();
1033 waitFor(locker, ThreadStart);
1034 }
1035 ~WatchDog() {
1036 {
1037 const auto locker = qt_scoped_lock(mutex);
1038 expecting = ThreadEnd;
1039 waitCondition.notify_all();
1040 }
1041 wait();
1042 }
1043
1044 void beginTest() {
1045 const auto locker = qt_scoped_lock(mutex);
1046 expecting = TestFunctionEnd;
1047 waitCondition.notify_all();
1048 }
1049
1050 void testFinished() {
1051 const auto locker = qt_scoped_lock(mutex);
1052 expecting = TestFunctionStart;
1053 waitCondition.notify_all();
1054 }
1055
1056 void run() override {
1057 auto locker = qt_unique_lock(mutex);
1058 expecting = TestFunctionStart;
1059 waitCondition.notify_all();
1060 while (true) {
1061 switch (expecting) {
1062 case ThreadEnd:
1063 return;
1064 case ThreadStart:
1065 Q_UNREACHABLE();
1066 case TestFunctionStart:
1067 case TestFunctionEnd:
1068 if (Q_UNLIKELY(!waitFor(locker, expecting))) {
1069 stackTrace();
1070 qFatal("Test function timed out");
1071 }
1072 }
1073 }
1074 }
1075
1076private:
1077 QtPrivate::mutex mutex;
1078 QtPrivate::condition_variable waitCondition;
1079 Expectation expecting;
1080};
1081
1082#else // !QT_CONFIG(thread)
1083
1084class WatchDog : public QObject
1085{
1086public:
1087 void beginTest() {};
1088 void testFinished() {};
1089};
1090
1091#endif
1092
1093
1094/*!
1095 \internal
1096
1097 Call slot_data(), init(), slot(), cleanup(), init(), slot(), cleanup(), ...
1098 If data is set then it is the only test that is performed
1099
1100 If the function was successfully called, true is returned, otherwise
1101 false.
1102 */
1103bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) const
1104{
1105 QBenchmarkTestMethodData benchmarkData;
1106 QBenchmarkTestMethodData::current = &benchmarkData;
1107
1108 const QByteArray &name = m_methods[index].name();
1109 QBenchmarkGlobalData::current->context.slotName = QLatin1String(name) + QLatin1String("()");
1110
1111 char member[512];
1112 QTestTable table;
1113
1114 QTestResult::setCurrentTestFunction(name.constData());
1115
1116 const QTestTable *gTable = QTestTable::globalTestTable();
1117 const int globalDataCount = gTable->dataCount();
1118 int curGlobalDataIndex = 0;
1119
1120 /* For each entry in the global data table, do: */
1121 do {
1122 if (!gTable->isEmpty())
1123 QTestResult::setCurrentGlobalTestData(gTable->testData(curGlobalDataIndex));
1124
1125 if (curGlobalDataIndex == 0) {
1126 qsnprintf(member, 512, "%s_data()", name.constData());
1127 invokeMethod(QTest::currentTestObject, member);
1128 if (QTestResult::skipCurrentTest())
1129 break;
1130 }
1131
1132 bool foundFunction = false;
1133 int curDataIndex = 0;
1134 const int dataCount = table.dataCount();
1135
1136 // Data tag requested but none available?
1137 if (data && !dataCount) {
1138 // Let empty data tag through.
1139 if (!*data)
1140 data = nullptr;
1141 else {
1142 fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), data);
1143 fprintf(stderr, "Function has no testdata.\n");
1144 return false;
1145 }
1146 }
1147
1148 /* For each entry in this test's data table, do: */
1149 do {
1150 QTestResult::setSkipCurrentTest(false);
1151 QTestResult::setBlacklistCurrentTest(false);
1152 if (!data || !qstrcmp(data, table.testData(curDataIndex)->dataTag())) {
1153 foundFunction = true;
1154
1155 QTestPrivate::checkBlackLists(name.constData(), dataCount ? table.testData(curDataIndex)->dataTag() : nullptr);
1156
1157 QTestDataSetter s(curDataIndex >= dataCount ? nullptr : table.testData(curDataIndex));
1158
1159 QTestPrivate::qtestMouseButtons = Qt::NoButton;
1160 if (watchDog)
1161 watchDog->beginTest();
1162 QTest::lastMouseTimestamp += 500; // Maintain at least 500ms mouse event timestamps between each test function call
1163 invokeTestOnData(index);
1164 if (watchDog)
1165 watchDog->testFinished();
1166
1167 if (data)
1168 break;
1169 }
1170 ++curDataIndex;
1171 } while (curDataIndex < dataCount);
1172
1173 if (data && !foundFunction) {
1174 fprintf(stderr, "Unknown testdata for function %s: '%s()'\n", name.constData(), data);
1175 fprintf(stderr, "Available testdata:\n");
1176 for (int i = 0; i < table.dataCount(); ++i)
1177 fprintf(stderr, "%s\n", table.testData(i)->dataTag());
1178 return false;
1179 }
1180
1181 QTestResult::setCurrentGlobalTestData(nullptr);
1182 ++curGlobalDataIndex;
1183 } while (curGlobalDataIndex < globalDataCount);
1184
1185 QTestResult::finishedCurrentTestFunction();
1186 QTestResult::setSkipCurrentTest(false);
1187 QTestResult::setBlacklistCurrentTest(false);
1188 QTestResult::setCurrentTestData(nullptr);
1189
1190 return true;
1191}
1192
1193void *fetchData(QTestData *data, const char *tagName, int typeId)
1194{
1195 QTEST_ASSERT(typeId);
1196 QTEST_ASSERT_X(data, "QTest::fetchData()", "Test data requested, but no testdata available.");
1197 QTEST_ASSERT(data->parent());
1198
1199 int idx = data->parent()->indexOf(tagName);
1200
1201 if (Q_UNLIKELY(idx == -1 || idx >= data->dataCount())) {
1202 qFatal("QFETCH: Requested testdata '%s' not available, check your _data function.",
1203 tagName);
1204 }
1205
1206 if (Q_UNLIKELY(typeId != data->parent()->elementTypeId(idx))) {
1207 qFatal("Requested type '%s' does not match available type '%s'.",
1208 QMetaType(typeId).name(),
1209 QMetaType(data->parent()->elementTypeId(idx)).name());
1210 }
1211
1212 return data->data(idx);
1213}
1214
1215/*!
1216 * \internal
1217 */
1218char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...)
1219{
1220 va_list ap;
1221 va_start(ap, numArguments);
1222
1223 QByteArray arguments;
1224 arguments += prefix;
1225
1226 if (numArguments > 0) {
1227 arguments += va_arg(ap, const char *);
1228
1229 for (size_t i = 1; i < numArguments; ++i) {
1230 arguments += ", ";
1231 arguments += va_arg(ap, const char *);
1232 }
1233 }
1234
1235 va_end(ap);
1236 arguments += suffix;
1237 return qstrdup(arguments.constData());
1238}
1239
1240/*!
1241 \fn char* QTest::toHexRepresentation(const char *ba, int length)
1242
1243 Returns a pointer to a string that is the string \a ba represented
1244 as a space-separated sequence of hex characters. If the input is
1245 considered too long, it is truncated. A trucation is indicated in
1246 the returned string as an ellipsis at the end. The caller has
1247 ownership of the returned pointer and must ensure it is later passed
1248 to operator delete[].
1249
1250 \a length is the length of the string \a ba.
1251 */
1252char *toHexRepresentation(const char *ba, int length)
1253{
1254 if (length == 0)
1255 return qstrdup("");
1256
1257 /* We output at maximum about maxLen characters in order to avoid
1258 * running out of memory and flooding things when the byte array
1259 * is large.
1260 *
1261 * maxLen can't be for example 200 because Qt Test is sprinkled with fixed
1262 * size char arrays.
1263 * */
1264 const int maxLen = 50;
1265 const int len = qMin(maxLen, length);
1266 char *result = nullptr;
1267
1268 if (length > maxLen) {
1269 const int size = len * 3 + 4;
1270 result = new char[size];
1271
1272 char *const forElipsis = result + size - 5;
1273 forElipsis[0] = ' ';
1274 forElipsis[1] = '.';
1275 forElipsis[2] = '.';
1276 forElipsis[3] = '.';
1277 result[size - 1] = '\0';
1278 }
1279 else {
1280 const int size = len * 3;
1281 result = new char[size];
1282 result[size - 1] = '\0';
1283 }
1284
1285 int i = 0;
1286 int o = 0;
1287
1288 while (true) {
1289 const char at = ba[i];
1290
1291 result[o] = toHexUpper(at >> 4);
1292 ++o;
1293 result[o] = toHexUpper(at);
1294
1295 ++i;
1296 ++o;
1297 if (i == len)
1298 break;
1299 result[o] = ' ';
1300 ++o;
1301 }
1302
1303 return result;
1304}
1305
1306/*!
1307 \internal
1308 Returns the same QByteArray but with only the ASCII characters still shown;
1309 everything else is replaced with \c {\xHH}.
1310*/
1311char *toPrettyCString(const char *p, int length)
1312{
1313 bool trimmed = false;
1314 QScopedArrayPointer<char> buffer(new char[256]);
1315 const char *end = p + length;
1316 char *dst = buffer.data();
1317
1318 bool lastWasHexEscape = false;
1319 *dst++ = '"';
1320 for ( ; p != end; ++p) {
1321 // we can add:
1322 // 1 byte: a single character
1323 // 2 bytes: a simple escape sequence (\n)
1324 // 3 bytes: "" and a character
1325 // 4 bytes: an hex escape sequence (\xHH)
1326 if (dst - buffer.data() > 246) {
1327 // plus the quote, the three dots and NUL, it's 255 in the worst case
1328 trimmed = true;
1329 break;
1330 }
1331
1332 // check if we need to insert "" to break an hex escape sequence
1333 if (Q_UNLIKELY(lastWasHexEscape)) {
1334 if (fromHex(*p) != -1) {
1335 // yes, insert it
1336 *dst++ = '"';
1337 *dst++ = '"';
1338 }
1339 lastWasHexEscape = false;
1340 }
1341
1342 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1343 *dst++ = *p;
1344 continue;
1345 }
1346
1347 // write as an escape sequence
1348 // this means we may advance dst to buffer.data() + 247 or 250
1349 *dst++ = '\\';
1350 switch (*p) {
1351 case 0x5c:
1352 case 0x22:
1353 *dst++ = uchar(*p);
1354 break;
1355 case 0x8:
1356 *dst++ = 'b';
1357 break;
1358 case 0xc:
1359 *dst++ = 'f';
1360 break;
1361 case 0xa:
1362 *dst++ = 'n';
1363 break;
1364 case 0xd:
1365 *dst++ = 'r';
1366 break;
1367 case 0x9:
1368 *dst++ = 't';
1369 break;
1370 default:
1371 // print as hex escape
1372 *dst++ = 'x';
1373 *dst++ = toHexUpper(uchar(*p) >> 4);
1374 *dst++ = toHexUpper(uchar(*p));
1375 lastWasHexEscape = true;
1376 break;
1377 }
1378 }
1379
1380 *dst++ = '"';
1381 if (trimmed) {
1382 *dst++ = '.';
1383 *dst++ = '.';
1384 *dst++ = '.';
1385 }
1386 *dst++ = '\0';
1387 return buffer.take();
1388}
1389
1390#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1391// this used to be the signature up to and including Qt 5.9
1392// keep it for BC reasons:
1393Q_TESTLIB_EXPORT
1394char *toPrettyUnicode(const ushort *p, int length)
1395{
1396 return toPrettyUnicode(QStringView(p, length));
1397}
1398#endif
1399
1400/*!
1401 \internal
1402 Returns the same QString but with only the ASCII characters still shown;
1403 everything else is replaced with \c {\uXXXX}.
1404
1405 Similar to QDebug::putString().
1406*/
1407char *toPrettyUnicode(QStringView string)
1408{
1409 auto p = reinterpret_cast<const ushort *>(string.utf16());
1410 auto length = string.size();
1411 // keep it simple for the vast majority of cases
1412 bool trimmed = false;
1413 QScopedArrayPointer<char> buffer(new char[256]);
1414 const ushort *end = p + length;
1415 char *dst = buffer.data();
1416
1417 *dst++ = '"';
1418 for ( ; p != end; ++p) {
1419 if (dst - buffer.data() > 245) {
1420 // plus the quote, the three dots and NUL, it's 250, 251 or 255
1421 trimmed = true;
1422 break;
1423 }
1424
1425 if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
1426 *dst++ = *p;
1427 continue;
1428 }
1429
1430 // write as an escape sequence
1431 // this means we may advance dst to buffer.data() + 246 or 250
1432 *dst++ = '\\';
1433 switch (*p) {
1434 case 0x22:
1435 case 0x5c:
1436 *dst++ = uchar(*p);
1437 break;
1438 case 0x8:
1439 *dst++ = 'b';
1440 break;
1441 case 0xc:
1442 *dst++ = 'f';
1443 break;
1444 case 0xa:
1445 *dst++ = 'n';
1446 break;
1447 case 0xd:
1448 *dst++ = 'r';
1449 break;
1450 case 0x9:
1451 *dst++ = 't';
1452 break;
1453 default:
1454 *dst++ = 'u';
1455 *dst++ = toHexUpper(*p >> 12);
1456 *dst++ = toHexUpper(*p >> 8);
1457 *dst++ = toHexUpper(*p >> 4);
1458 *dst++ = toHexUpper(*p);
1459 }
1460 }
1461
1462 *dst++ = '"';
1463 if (trimmed) {
1464 *dst++ = '.';
1465 *dst++ = '.';
1466 *dst++ = '.';
1467 }
1468 *dst++ = '\0';
1469 return buffer.take();
1470}
1471
1472void TestMethods::invokeTests(QObject *testObject) const
1473{
1474 const QMetaObject *metaObject = testObject->metaObject();
1475 QTEST_ASSERT(metaObject);
1476 QTestResult::setCurrentTestFunction("initTestCase");
1477 if (m_initTestCaseDataMethod.isValid())
1478 m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection);
1479
1480 QScopedPointer<WatchDog> watchDog;
1481 if (!debuggerPresent()
1482#if QT_CONFIG(valgrind)
1483 && QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindChildProcess
1484#endif
1485 ) {
1486 watchDog.reset(new WatchDog);
1487 }
1488
1489 QSignalDumper::startDump();
1490
1491 if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) {
1492 if (m_initTestCaseMethod.isValid())
1493 m_initTestCaseMethod.invoke(testObject, Qt::DirectConnection);
1494
1495 // finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy.
1496 const bool previousFailed = QTestResult::currentTestFailed();
1497 QTestResult::finishedCurrentTestData();
1498 QTestResult::finishedCurrentTestDataCleanup();
1499 QTestResult::finishedCurrentTestFunction();
1500
1501 if (!QTestResult::skipCurrentTest() && !previousFailed) {
1502 for (int i = 0, count = int(m_methods.size()); i < count; ++i) {
1503 const char *data = nullptr;
1504 if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
1505 data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
1506 const bool ok = invokeTest(i, data, watchDog.data());
1507 delete [] data;
1508 if (!ok)
1509 break;
1510 }
1511 }
1512
1513 QTestResult::setSkipCurrentTest(false);
1514 QTestResult::setBlacklistCurrentTest(false);
1515 QTestResult::setCurrentTestFunction("cleanupTestCase");
1516 if (m_cleanupTestCaseMethod.isValid())
1517 m_cleanupTestCaseMethod.invoke(testObject, Qt::DirectConnection);
1518 QTestResult::finishedCurrentTestData();
1519 QTestResult::finishedCurrentTestDataCleanup();
1520 }
1521 QTestResult::finishedCurrentTestFunction();
1522 QTestResult::setCurrentTestFunction(nullptr);
1523
1524 QSignalDumper::endDump();
1525}
1526
1527#if defined(Q_OS_WIN)
1528
1529// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
1530class DebugSymbolResolver
1531{
1532 Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
1533public:
1534 struct Symbol {
1535 Symbol() : name(nullptr), address(0) {}
1536
1537 const char *name; // Must be freed by caller.
1538 DWORD64 address;
1539 };
1540
1541 explicit DebugSymbolResolver(HANDLE process);
1542 ~DebugSymbolResolver() { cleanup(); }
1543
1544 bool isValid() const { return m_symFromAddr; }
1545
1546 Symbol resolveSymbol(DWORD64 address) const;
1547
1548private:
1549 // typedefs from DbgHelp.h/.dll
1550 struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
1551 ULONG SizeOfStruct;
1552 ULONG TypeIndex; // Type Index of symbol
1553 ULONG64 Reserved[2];
1554 ULONG Index;
1555 ULONG Size;
1556 ULONG64 ModBase; // Base Address of module comtaining this symbol
1557 ULONG Flags;
1558 ULONG64 Value; // Value of symbol, ValuePresent should be 1
1559 ULONG64 Address; // Address of symbol including base address of module
1560 ULONG Register; // register holding value or pointer to value
1561 ULONG Scope; // scope of the symbol
1562 ULONG Tag; // pdb classification
1563 ULONG NameLen; // Actual length of name
1564 ULONG MaxNameLen;
1565 CHAR Name[1]; // Name of symbol
1566 };
1567
1568 typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
1569 typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
1570
1571 void cleanup();
1572
1573 const HANDLE m_process;
1574 HMODULE m_dbgHelpLib;
1575 SymFromAddrType m_symFromAddr;
1576};
1577
1578void DebugSymbolResolver::cleanup()
1579{
1580 if (m_dbgHelpLib)
1581 FreeLibrary(m_dbgHelpLib);
1582 m_dbgHelpLib = 0;
1583 m_symFromAddr = nullptr;
1584}
1585
1586DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
1587 : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
1588{
1589 bool success = false;
1590 m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
1591 if (m_dbgHelpLib) {
1592 SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
1593 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
1594 m_symFromAddr = reinterpret_cast<SymFromAddrType>(
1595 reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
1596 success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
1597 }
1598 if (!success)
1599 cleanup();
1600}
1601
1602DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
1603{
1604 // reserve additional buffer where SymFromAddr() will store the name
1605 struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
1606 enum { symbolNameLength = 255 };
1607
1608 char name[symbolNameLength + 1];
1609 };
1610
1611 Symbol result;
1612 if (!isValid())
1613 return result;
1614 NamedSymbolInfo symbolBuffer;
1615 memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
1616 symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
1617 symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
1618 if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
1619 return result;
1620 result.name = qstrdup(symbolBuffer.Name);
1621 result.address = symbolBuffer.Address;
1622 return result;
1623}
1624
1625#endif // Q_OS_WIN
1626
1627class FatalSignalHandler
1628{
1629public:
1630 FatalSignalHandler()
1631 {
1632#if defined(Q_OS_WIN)
1633# if !defined(Q_CC_MINGW)
1634 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
1635# endif
1636 SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
1637 SetUnhandledExceptionFilter(windowsFaultHandler);
1638#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1639 sigemptyset(&handledSignals);
1640
1641 const int fatalSignals[] = {
1642 SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 };
1643
1644 struct sigaction act;
1645 memset(&act, 0, sizeof(act));
1646 act.sa_handler = FatalSignalHandler::signal;
1647
1648 // Remove the handler after it is invoked.
1649# if !defined(Q_OS_INTEGRITY)
1650 act.sa_flags = SA_RESETHAND;
1651# endif
1652
1653 // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
1654 // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
1655# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
1656 // Let the signal handlers use an alternate stack
1657 // This is necessary if SIGSEGV is to catch a stack overflow
1658# if defined(Q_CC_GNU) && defined(Q_OF_ELF)
1659 // Put the alternate stack in the .lbss (large BSS) section so that it doesn't
1660 // interfere with normal .bss symbols
1661 __attribute__((section(".lbss.altstack"), aligned(4096)))
1662# endif
1663 static char alternate_stack[16 * 1024];
1664 stack_t stack;
1665 stack.ss_flags = 0;
1666 stack.ss_size = sizeof alternate_stack;
1667 stack.ss_sp = alternate_stack;
1668 sigaltstack(&stack, nullptr);
1669 act.sa_flags |= SA_ONSTACK;
1670# endif
1671
1672 // Block all fatal signals in our signal handler so we don't try to close
1673 // the testlog twice.
1674 sigemptyset(&act.sa_mask);
1675 for (int i = 0; fatalSignals[i]; ++i)
1676 sigaddset(&act.sa_mask, fatalSignals[i]);
1677
1678 struct sigaction oldact;
1679
1680 for (int i = 0; fatalSignals[i]; ++i) {
1681 sigaction(fatalSignals[i], &act, &oldact);
1682 if (
1683# ifdef SA_SIGINFO
1684 oldact.sa_flags & SA_SIGINFO ||
1685# endif
1686 oldact.sa_handler != SIG_DFL) {
1687 sigaction(fatalSignals[i], &oldact, nullptr);
1688 } else
1689 {
1690 sigaddset(&handledSignals, fatalSignals[i]);
1691 }
1692 }
1693#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1694 }
1695
1696 ~FatalSignalHandler()
1697 {
1698#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1699 // Unregister any of our remaining signal handlers
1700 struct sigaction act;
1701 memset(&act, 0, sizeof(act));
1702 act.sa_handler = SIG_DFL;
1703
1704 struct sigaction oldact;
1705
1706 for (int i = 1; i < 32; ++i) {
1707 if (!sigismember(&handledSignals, i))
1708 continue;
1709 sigaction(i, &act, &oldact);
1710
1711 // If someone overwrote it in the mean time, put it back
1712 if (oldact.sa_handler != FatalSignalHandler::signal)
1713 sigaction(i, &oldact, nullptr);
1714 }
1715#endif
1716 }
1717
1718private:
1719#if defined(Q_OS_WIN)
1720 static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
1721 {
1722 enum { maxStackFrames = 100 };
1723 char appName[MAX_PATH];
1724 if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
1725 appName[0] = 0;
1726 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
1727 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
1728 const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
1729 printf("A crash occurred in %s.\n"
1730 "Function time: %dms Total time: %dms\n\n"
1731 "Exception address: 0x%p\n"
1732 "Exception code : 0x%lx\n",
1733 appName, msecsFunctionTime, msecsTotalTime,
1734 exceptionAddress, exInfo->ExceptionRecord->ExceptionCode);
1735
1736 DebugSymbolResolver resolver(GetCurrentProcess());
1737 if (resolver.isValid()) {
1738 DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
1739 if (exceptionSymbol.name) {
1740 printf("Nearby symbol : %s\n", exceptionSymbol.name);
1741 delete [] exceptionSymbol.name;
1742 }
1743 void *stack[maxStackFrames];
1744 fputs("\nStack:\n", stdout);
1745 const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
1746 for (unsigned f = 0; f < frameCount; ++f) {
1747 DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
1748 if (symbol.name) {
1749 printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
1750 delete [] symbol.name;
1751 } else {
1752 printf("#%3u: Unable to obtain symbol\n", f + 1);
1753 }
1754 }
1755 }
1756
1757 fputc('\n', stdout);
1758 fflush(stdout);
1759
1760 return EXCEPTION_EXECUTE_HANDLER;
1761 }
1762#endif // defined(Q_OS_WIN)
1763
1764#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1765 static void signal(int signum)
1766 {
1767 const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
1768 const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
1769 if (signum != SIGINT) {
1770 stackTrace();
1771 if (qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH")) {
1772 fprintf(stderr, "Pausing process %d for debugging\n", getpid());
1773 raise(SIGSTOP);
1774 }
1775 }
1776 qFatal("Received signal %d\n"
1777 " Function time: %dms Total time: %dms",
1778 signum, msecsFunctionTime, msecsTotalTime);
1779# if defined(Q_OS_INTEGRITY)
1780 {
1781 struct sigaction act;
1782 memset(&act, 0, sizeof(struct sigaction));
1783 act.sa_handler = SIG_DFL;
1784 sigaction(signum, &act, NULL);
1785 }
1786# endif
1787 }
1788
1789 sigset_t handledSignals;
1790#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
1791};
1792
1793} // namespace
1794
1795static void initEnvironment()
1796{
1797 qputenv("QT_QTESTLIB_RUNNING", "1");
1798}
1799
1800/*!
1801 Executes tests declared in \a testObject. In addition, the private slots
1802 \c{initTestCase()}, \c{cleanupTestCase()}, \c{init()} and \c{cleanup()}
1803 are executed if they exist. See \l{Creating a Test} for more details.
1804
1805 Optionally, the command line arguments \a argc and \a argv can be provided.
1806 For a list of recognized arguments, read \l {Qt Test Command Line Arguments}.
1807
1808 The following example will run all tests in \c MyTestObject:
1809
1810 \snippet code/src_qtestlib_qtestcase.cpp 18
1811
1812 This function returns 0 if no tests failed, or a value other than 0 if one
1813 or more tests failed or in case of unhandled exceptions. (Skipped tests do
1814 not influence the return value.)
1815
1816 For stand-alone test applications, the convenience macro \l QTEST_MAIN() can
1817 be used to declare a main() function that parses the command line arguments
1818 and executes the tests, avoiding the need to call this function explicitly.
1819
1820 The return value from this function is also the exit code of the test
1821 application when the \l QTEST_MAIN() macro is used.
1822
1823 For stand-alone test applications, this function should not be called more
1824 than once, as command-line options for logging test output to files and
1825 executing individual test functions will not behave correctly.
1826
1827 Note: This function is not reentrant, only one test can run at a time. A
1828 test that was executed with qExec() can't run another test via qExec() and
1829 threads are not allowed to call qExec() simultaneously.
1830
1831 If you have programatically created the arguments, as opposed to getting them
1832 from the arguments in \c main(), it is likely of interest to use
1833 QTest::qExec(QObject *, const QStringList &) since it is Unicode safe.
1834
1835 \sa QTEST_MAIN()
1836*/
1837
1838int QTest::qExec(QObject *testObject, int argc, char **argv)
1839{
1840 qInit(testObject, argc, argv);
1841 int ret = qRun();
1842 qCleanup();
1843 return ret;
1844}
1845
1846/*! \internal
1847 */
1848void QTest::qInit(QObject *testObject, int argc, char **argv)
1849{
1850 initEnvironment();
1851 QBenchmarkGlobalData::current = new QBenchmarkGlobalData;
1852
1853#if defined(Q_OS_MACOS)
1854 // Don't restore saved window state for auto tests
1855 QTestPrivate::disableWindowRestore();
1856
1857 // Disable App Nap which may cause tests to stall
1858 QTestPrivate::AppNapDisabler appNapDisabler;
1859
1860 if (qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0)) {
1861 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1862 kIOPMAssertionLevelOn, CFSTR("QtTest running tests"),
1863 &macPowerSavingDisabled);
1864 }
1865#endif
1866
1867 QTestPrivate::parseBlackList();
1868 QTestResult::reset();
1869
1870 QTEST_ASSERT(testObject);
1871 QTEST_ASSERT(!currentTestObject);
1872 currentTestObject = testObject;
1873
1874 const QMetaObject *metaObject = testObject->metaObject();
1875 QTEST_ASSERT(metaObject);
1876
1877 QTestResult::setCurrentTestObject(metaObject->className());
1878 if (argc > 0)
1879 QTestResult::setCurrentAppName(argv[0]);
1880
1881 qtest_qParseArgs(argc, argv, false);
1882
1883 QTestTable::globalTestTable();
1884 QTestLog::startLogging();
1885}
1886
1887/*! \internal
1888 */
1889int QTest::qRun()
1890{
1891 QTEST_ASSERT(currentTestObject);
1892
1893#if QT_CONFIG(valgrind)
1894 int callgrindChildExitCode = 0;
1895#endif
1896
1897#ifndef QT_NO_EXCEPTIONS
1898 try {
1899#endif
1900
1901#if QT_CONFIG(valgrind)
1902 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) {
1903 if (Q_UNLIKELY(!qApp))
1904 qFatal("QtTest: -callgrind option is not available with QTEST_APPLESS_MAIN");
1905
1906 const QStringList origAppArgs(QCoreApplication::arguments());
1907 if (!QBenchmarkValgrindUtils::rerunThroughCallgrind(origAppArgs, callgrindChildExitCode))
1908 return -1;
1909
1910 QBenchmarkValgrindUtils::cleanup();
1911
1912 } else
1913#endif
1914 {
1915 QScopedPointer<FatalSignalHandler> handler;
1916 if (!noCrashHandler)
1917 handler.reset(new FatalSignalHandler);
1918
1919 TestMethods::MetaMethods commandLineMethods;
1920 for (const QString &tf : qAsConst(QTest::testFunctions)) {
1921 const QByteArray tfB = tf.toLatin1();
1922 const QByteArray signature = tfB + QByteArrayLiteral("()");
1923 QMetaMethod m = TestMethods::findMethod(currentTestObject, signature.constData());
1924 if (!m.isValid() || !isValidSlot(m)) {
1925 fprintf(stderr, "Unknown test function: '%s'. Possible matches:\n", tfB.constData());
1926 qPrintTestSlots(stderr, tfB.constData());
1927 fprintf(stderr, "\n%s -functions\nlists all available test functions.\n", QTestResult::currentAppName());
1928 exit(1);
1929 }
1930 commandLineMethods.push_back(m);
1931 }
1932 TestMethods test(currentTestObject, commandLineMethods);
1933 test.invokeTests(currentTestObject);
1934 }
1935
1936#ifndef QT_NO_EXCEPTIONS
1937 } catch (...) {
1938 QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__);
1939 if (QTestResult::currentTestFunction()) {
1940 QTestResult::finishedCurrentTestFunction();
1941 QTestResult::setCurrentTestFunction(nullptr);
1942 }
1943
1944 qCleanup();
1945
1946 // Re-throw exception to make debugging easier
1947 throw;
1948 return 1;
1949 }
1950#endif
1951
1952#if QT_CONFIG(valgrind)
1953 if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess)
1954 return callgrindChildExitCode;
1955#endif
1956 // make sure our exit code is never going above 127
1957 // since that could wrap and indicate 0 test fails
1958 return qMin(QTestLog::failCount(), 127);
1959}
1960
1961/*! \internal
1962 */
1963void QTest::qCleanup()
1964{
1965 currentTestObject = nullptr;
1966
1967 QTestTable::clearGlobalTestTable();
1968 QTestLog::stopLogging();
1969
1970 delete QBenchmarkGlobalData::current;
1971 QBenchmarkGlobalData::current = nullptr;
1972
1973#if defined(Q_OS_MACOS)
1974 IOPMAssertionRelease(macPowerSavingDisabled);
1975#endif
1976}
1977
1978/*!
1979 \overload
1980 \since 4.4
1981
1982 Behaves identically to qExec(QObject *, int, char**) but takes a
1983 QStringList of \a arguments instead of a \c char** list.
1984 */
1985int QTest::qExec(QObject *testObject, const QStringList &arguments)
1986{
1987 const int argc = arguments.count();
1988 QVarLengthArray<char *> argv(argc);
1989
1990 QList<QByteArray> args;
1991 args.reserve(argc);
1992
1993 for (int i = 0; i < argc; ++i)
1994 {
1995 args.append(arguments.at(i).toLocal8Bit().constData());
1996 argv[i] = args.last().data();
1997 }
1998
1999 return qExec(testObject, argc, argv.data());
2000}
2001
2002/*! \internal
2003 */
2004void QTest::qFail(const char *statementStr, const char *file, int line)
2005{
2006 QTestResult::addFailure(statementStr, file, line);
2007}
2008
2009/*! \internal
2010 */
2011bool QTest::qVerify(bool statement, const char *statementStr, const char *description,
2012 const char *file, int line)
2013{
2014 return QTestResult::verify(statement, statementStr, description, file, line);
2015}
2016
2017/*! \fn void QTest::qSkip(const char *message, const char *file, int line)
2018\internal
2019 */
2020void QTest::qSkip(const char *message, const char *file, int line)
2021{
2022 QTestResult::addSkip(message, file, line);
2023 QTestResult::setSkipCurrentTest(true);
2024}
2025
2026/*! \fn bool QTest::qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
2027\internal
2028 */
2029bool QTest::qExpectFail(const char *dataIndex, const char *comment,
2030 QTest::TestFailMode mode, const char *file, int line)
2031{
2032 return QTestResult::expectFail(dataIndex, qstrdup(comment), mode, file, line);
2033}
2034
2035/*! \internal
2036 */
2037void QTest::qWarn(const char *message, const char *file, int line)
2038{
2039 QTestLog::warn(message, file, line);
2040}
2041
2042/*!
2043 Ignores messages created by qDebug(), qInfo() or qWarning(). If the \a message
2044 with the corresponding \a type is outputted, it will be removed from the
2045 test log. If the test finished and the \a message was not outputted,
2046 a test failure is appended to the test log.
2047
2048 \b {Note:} Invoking this function will only ignore one message.
2049 If the message you want to ignore is outputted twice, you have to
2050 call ignoreMessage() twice, too.
2051
2052 Example:
2053 \snippet code/src_qtestlib_qtestcase.cpp 19
2054
2055 The example above tests that QDir::mkdir() outputs the right warning when invoked
2056 with an invalid file name.
2057*/
2058void QTest::ignoreMessage(QtMsgType type, const char *message)
2059{
2060 QTestLog::ignoreMessage(type, message);
2061}
2062
2063#if QT_CONFIG(regularexpression)
2064/*!
2065 \overload
2066
2067 Ignores messages created by qDebug(), qInfo() or qWarning(). If the message
2068 matching \a messagePattern
2069 with the corresponding \a type is outputted, it will be removed from the
2070 test log. If the test finished and the message was not outputted,
2071 a test failure is appended to the test log.
2072
2073 \b {Note:} Invoking this function will only ignore one message.
2074 If the message you want to ignore is outputted twice, you have to
2075 call ignoreMessage() twice, too.
2076
2077 \since 5.3
2078*/
2079void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern)
2080{
2081 QTestLog::ignoreMessage(type, messagePattern);
2082}
2083#endif // QT_CONFIG(regularexpression)
2084
2085/*! \internal
2086 */
2087
2088#ifdef Q_OS_WIN
2089static inline bool isWindowsBuildDirectory(const QString &dirName)
2090{
2091 return dirName.compare(QLatin1String("Debug"), Qt::CaseInsensitive) == 0
2092 || dirName.compare(QLatin1String("Release"), Qt::CaseInsensitive) == 0;
2093}
2094#endif
2095
2096#if QT_CONFIG(temporaryfile)
2097/*!
2098 Extracts a directory from resources to disk. The content is extracted
2099 recursively to a temporary folder. The extracted content is removed
2100 automatically once the last reference to the return value goes out of scope.
2101
2102 \a dirName is the name of the directory to extract from resources.
2103
2104 Returns the temporary directory where the data was extracted or null in case of
2105 errors.
2106 */
2107QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
2108{
2109 QSharedPointer<QTemporaryDir> result; // null until success, then == tempDir
2110
2111 QSharedPointer<QTemporaryDir> tempDir = QSharedPointer<QTemporaryDir>::create();
2112
2113 tempDir->setAutoRemove(true);
2114
2115 if (!tempDir->isValid())
2116 return result;
2117
2118 const QString dataPath = tempDir->path();
2119 const QString resourcePath = QLatin1Char(':') + dirName;
2120 const QFileInfo fileInfo(resourcePath);
2121
2122 if (!fileInfo.isDir()) {
2123 qWarning("Resource path '%s' is not a directory.", qPrintable(resourcePath));
2124 return result;
2125 }
2126
2127 QDirIterator it(resourcePath, QDirIterator::Subdirectories);
2128 if (!it.hasNext()) {
2129 qWarning("Resource directory '%s' is empty.", qPrintable(resourcePath));
2130 return result;
2131 }
2132
2133 while (it.hasNext()) {
2134 it.next();
2135
2136 QFileInfo fileInfo = it.fileInfo();
2137
2138 if (!fileInfo.isDir()) {
2139 const QString destination = dataPath + QLatin1Char('/') + QStringView{fileInfo.filePath()}.mid(resourcePath.length());
2140 QFileInfo destinationFileInfo(destination);
2141 QDir().mkpath(destinationFileInfo.path());
2142 if (!QFile::copy(fileInfo.filePath(), destination)) {
2143 qWarning("Failed to copy '%s'.", qPrintable(fileInfo.filePath()));
2144 return result;
2145 }
2146 if (!QFile::setPermissions(destination, QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup)) {
2147 qWarning("Failed to set permissions on '%s'.", qPrintable(destination));
2148 return result;
2149 }
2150 }
2151 }
2152
2153 result = std::move(tempDir);
2154
2155 return result;
2156}
2157#endif // QT_CONFIG(temporaryfile)
2158
2159/*! \internal
2160 */
2161
2162QString QTest::qFindTestData(const QString& base, const char *file, int line, const char *builddir,
2163 const char *sourcedir)
2164{
2165 QString found;
2166
2167 // Testdata priorities:
2168
2169 // 1. relative to test binary.
2170 if (qApp) {
2171 QDir binDirectory(QCoreApplication::applicationDirPath());
2172 if (binDirectory.exists(base)) {
2173 found = binDirectory.absoluteFilePath(base);
2174 }
2175#ifdef Q_OS_WIN
2176 // Windows: The executable is typically located in one of the
2177 // 'Release' or 'Debug' directories.
2178 else if (isWindowsBuildDirectory(binDirectory.dirName())
2179 && binDirectory.cdUp() && binDirectory.exists(base)) {
2180 found = binDirectory.absoluteFilePath(base);
2181 }
2182#endif // Q_OS_WIN
2183 else if (QTestLog::verboseLevel() >= 2) {
2184 const QString candidate = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QLatin1Char('/') + base);
2185 QTestLog::info(qPrintable(
2186 QString::fromLatin1("testdata %1 not found relative to test binary [%2]; "
2187 "checking next location").arg(base, candidate)),
2188 file, line);
2189 }
2190 }
2191
2192 // 2. installed path.
2193 if (found.isEmpty()) {
2194 const char *testObjectName = QTestResult::currentTestObjectName();
2195 if (testObjectName) {
2196 const QString testsPath = QLibraryInfo::path(QLibraryInfo::TestsPath);
2197 const QString candidate = QString::fromLatin1("%1/%2/%3")
2198 .arg(testsPath, QFile::decodeName(testObjectName).toLower(), base);
2199 if (QFileInfo::exists(candidate)) {
2200 found = candidate;
2201 } else if (QTestLog::verboseLevel() >= 2) {
2202 QTestLog::info(qPrintable(
2203 QString::fromLatin1("testdata %1 not found in tests install path [%2]; "
2204 "checking next location")
2205 .arg(base, QDir::toNativeSeparators(candidate))),
2206 file, line);
2207 }
2208 }
2209 }
2210
2211 // 3. relative to test source.
2212 if (found.isEmpty() && qstrncmp(file, ":/", 2) != 0) {
2213 // srcdir is the directory containing the calling source file.
2214 QFileInfo srcdir = QFileInfo(QFile::decodeName(file)).path();
2215
2216 // If the srcdir is relative, that means it is relative to the current working
2217 // directory of the compiler at compile time, which should be passed in as `builddir'.
2218 if (!srcdir.isAbsolute() && builddir) {
2219 srcdir.setFile(QFile::decodeName(builddir) + QLatin1String("/") + srcdir.filePath());
2220 }
2221
2222 const QString canonicalPath = srcdir.canonicalFilePath();
2223 const QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base);
2224 if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) {
2225 found = candidate;
2226 } else if (QTestLog::verboseLevel() >= 2) {
2227 QTestLog::info(qPrintable(
2228 QString::fromLatin1("testdata %1 not found relative to source path [%2]")
2229 .arg(base, QDir::toNativeSeparators(candidate))),
2230 file, line);
2231 }
2232 }
2233
2234 // 4. Try resources
2235 if (found.isEmpty()) {
2236 const QString candidate = QString::fromLatin1(":/%1").arg(base);
2237 if (QFileInfo::exists(candidate)) {
2238 found = candidate;
2239 } else if (QTestLog::verboseLevel() >= 2) {
2240 QTestLog::info(qPrintable(
2241 QString::fromLatin1("testdata %1 not found in resources [%2]")
2242 .arg(base, QDir::toNativeSeparators(candidate))),
2243 file, line);
2244 }
2245 }
2246
2247 // 5. Try current directory
2248 if (found.isEmpty()) {
2249 const QString candidate = QDir::currentPath() + QLatin1Char('/') + base;
2250 if (QFileInfo::exists(candidate)) {
2251 found = candidate;
2252 } else if (QTestLog::verboseLevel() >= 2) {
2253 QTestLog::info(qPrintable(
2254 QString::fromLatin1("testdata %1 not found in current directory [%2]")
2255 .arg(base, QDir::toNativeSeparators(candidate))),
2256 file, line);
2257 }
2258 }
2259
2260 // 6. Try main source directory
2261 if (found.isEmpty()) {
2262 const QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base;
2263 if (QFileInfo::exists(candidate)) {
2264 found = candidate;
2265 } else if (QTestLog::verboseLevel() >= 2) {
2266 QTestLog::info(qPrintable(
2267 QString::fromLatin1("testdata %1 not found in main source directory [%2]")
2268 .arg(base, QDir::toNativeSeparators(candidate))),
2269 file, line);
2270 }
2271 }
2272
2273 // 7. Try the supplied source directory
2274 if (found.isEmpty() && sourcedir) {
2275 const QString candidate = QFile::decodeName(sourcedir) % QLatin1Char('/') % base;
2276 if (QFileInfo::exists(candidate)) {
2277 found = candidate;
2278 } else if (QTestLog::verboseLevel() >= 2) {
2279 QTestLog::info(qPrintable(
2280 QString::fromLatin1("testdata %1 not found in supplied source directory [%2]")
2281 .arg(base, QDir::toNativeSeparators(candidate))),
2282 file, line);
2283 }
2284 }
2285
2286
2287 if (found.isEmpty()) {
2288 QTest::qWarn(qPrintable(
2289 QString::fromLatin1("testdata %1 could not be located!").arg(base)),
2290 file, line);
2291 } else if (QTestLog::verboseLevel() >= 1) {
2292 QTestLog::info(qPrintable(
2293 QString::fromLatin1("testdata %1 was located at %2").arg(base, QDir::toNativeSeparators(found))),
2294 file, line);
2295 }
2296
2297 return found;
2298}
2299
2300/*! \internal
2301 */
2302QString QTest::qFindTestData(const char *base, const char *file, int line, const char *builddir,
2303 const char *sourcedir)
2304{
2305 return qFindTestData(QFile::decodeName(base), file, line, builddir, sourcedir);
2306}
2307
2308/*! \internal
2309 */
2310void *QTest::qData(const char *tagName, int typeId)
2311{
2312 return fetchData(QTestResult::currentTestData(), tagName, typeId);
2313}
2314
2315/*! \internal
2316 */
2317void *QTest::qGlobalData(const char *tagName, int typeId)
2318{
2319 return fetchData(QTestResult::currentGlobalTestData(), tagName, typeId);
2320}
2321
2322/*! \internal
2323 */
2324void *QTest::qElementData(const char *tagName, int metaTypeId)
2325{
2326 QTEST_ASSERT(tagName);
2327 QTestData *data = QTestResult::currentTestData();
2328 QTEST_ASSERT(data);
2329 QTEST_ASSERT(data->parent());
2330
2331 int idx = data->parent()->indexOf(tagName);
2332 QTEST_ASSERT(idx != -1);
2333 QTEST_ASSERT(data->parent()->elementTypeId(idx) == metaTypeId);
2334
2335 return data->data(data->parent()->indexOf(tagName));
2336}
2337
2338/*! \internal
2339 */
2340void QTest::addColumnInternal(int id, const char *name)
2341{
2342 QTestTable *tbl = QTestTable::currentTestTable();
2343 QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot.");
2344
2345 tbl->addColumn(id, name);
2346}
2347
2348/*!
2349 Appends a new row to the current test data. \a dataTag is the name of
2350 the testdata that will appear in the test output. Returns a QTestData reference
2351 that can be used to stream in data.
2352
2353 Example:
2354 \snippet code/src_qtestlib_qtestcase.cpp 20
2355
2356 \b {Note:} This macro can only be used in a test's data function
2357 that is invoked by the test framework.
2358
2359 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2360 a more extensive example.
2361
2362 \sa addColumn(), QFETCH()
2363*/
2364QTestData &QTest::newRow(const char *dataTag)
2365{
2366 QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null");
2367 QTestTable *tbl = QTestTable::currentTestTable();
2368 QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot.");
2369 QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()", "Must add columns before attempting to add rows.");
2370
2371 return *tbl->newData(dataTag);
2372}
2373
2374/*!
2375 \since 5.9
2376
2377 Appends a new row to the current test data. The function's arguments are passed
2378 to qsnprintf() for formatting according to \a format. See the qvsnprintf()
2379 documentation for caveats and limitations.
2380
2381 The formatted string will appear as the name of this test data in the test output.
2382
2383 Returns a QTestData reference that can be used to stream in data.
2384
2385 Example:
2386 \snippet code/src_qtestlib_qtestcase.cpp addRow
2387
2388 \b {Note:} This function can only be used in a test's data function
2389 that is invoked by the test framework.
2390
2391 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2392 a more extensive example.
2393
2394 \sa addColumn(), QFETCH()
2395*/
2396QTestData &QTest::addRow(const char *format, ...)
2397{
2398 QTEST_ASSERT_X(format, "QTest::addRow()", "Format string cannot be null");
2399 QTestTable *tbl = QTestTable::currentTestTable();
2400 QTEST_ASSERT_X(tbl, "QTest::addRow()", "Cannot add testdata outside of a _data slot.");
2401 QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()", "Must add columns before attempting to add rows.");
2402
2403 char buf[1024];
2404
2405 va_list va;
2406 va_start(va, format);
2407 // we don't care about failures, we accept truncation, as well as trailing garbage.
2408 // Names with more than 1K characters are nonsense, anyway.
2409 (void)qvsnprintf(buf, sizeof buf, format, va);
2410 buf[sizeof buf - 1] = '\0';
2411 va_end(va);
2412
2413 return *tbl->newData(buf);
2414}
2415
2416/*! \fn template <typename T> void QTest::addColumn(const char *name, T *dummy = 0)
2417
2418 Adds a column with type \c{T} to the current test data.
2419 \a name is the name of the column. \a dummy is a workaround
2420 for buggy compilers and can be ignored.
2421
2422 To populate the column with values, newRow() can be used. Use
2423 \l QFETCH() to fetch the data in the actual test.
2424
2425 Example:
2426 \snippet code/src_qtestlib_qtestcase.cpp 21
2427
2428 To add custom types to the testdata, the type must be registered with
2429 QMetaType via \l Q_DECLARE_METATYPE().
2430
2431 \b {Note:} This macro can only be used in a test's data function
2432 that is invoked by the test framework.
2433
2434 See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
2435 a more extensive example.
2436
2437 \sa QTest::newRow(), QFETCH(), QMetaType
2438*/
2439
2440/*!
2441 Returns the name of the binary that is currently executed.
2442*/
2443const char *QTest::currentAppName()
2444{
2445 return QTestResult::currentAppName();
2446}
2447
2448/*!
2449 Returns the name of the test function that is currently executed.
2450
2451 Example:
2452
2453 \snippet code/src_qtestlib_qtestcase.cpp 22
2454*/
2455const char *QTest::currentTestFunction()
2456{
2457 return QTestResult::currentTestFunction();
2458}
2459
2460/*!
2461 Returns the name of the current test data. If the test doesn't
2462 have any assigned testdata, the function returns 0.
2463*/
2464const char *QTest::currentDataTag()
2465{
2466 return QTestResult::currentDataTag();
2467}
2468
2469/*!
2470 Returns \c true if the current test function failed, otherwise false.
2471*/
2472bool QTest::currentTestFailed()
2473{
2474 return QTestResult::currentTestFailed();
2475}
2476
2477/*! \internal
2478 */
2479QObject *QTest::testObject()
2480{
2481 return currentTestObject;
2482}
2483
2484/*! \internal
2485 */
2486void QTest::setMainSourcePath(const char *file, const char *builddir)
2487{
2488 QString mainSourceFile = QFile::decodeName(file);
2489 QFileInfo fi;
2490 if (builddir)
2491 fi.setFile(QDir(QFile::decodeName(builddir)), mainSourceFile);
2492 else
2493 fi.setFile(mainSourceFile);
2494 QTest::mainSourcePath = fi.absolutePath();
2495}
2496
2497/*! \internal
2498 This function is called by various specializations of QTest::qCompare
2499 to decide whether to report a failure and to produce verbose test output.
2500
2501 The failureMsg parameter can be null, in which case a default message
2502 will be output if the compare fails. If the compare succeeds, failureMsg
2503 will not be output.
2504
2505 If the caller has already passed a failure message showing the compared
2506 values, or if those values cannot be stringified, val1 and val2 can be null.
2507 */
2508bool QTest::compare_helper(bool success, const char *failureMsg,
2509 char *val1, char *val2,
2510 const char *actual, const char *expected,
2511 const char *file, int line)
2512{
2513 return QTestResult::compare(success, failureMsg, val1, val2, actual, expected, file, line);
2514}
2515
2516template <typename T>
2517static bool floatingCompare(const T &actual, const T &expected)
2518{
2519 switch (qFpClassify(expected))
2520 {
2521 case FP_INFINITE:
2522 return (expected < 0) == (actual < 0) && qFpClassify(actual) == FP_INFINITE;
2523 case FP_NAN:
2524 return qFpClassify(actual) == FP_NAN;
2525 default:
2526 if (!qFuzzyIsNull(expected))
2527 return qFuzzyCompare(actual, expected);
2528 Q_FALLTHROUGH();
2529 case FP_SUBNORMAL: // subnormal is always fuzzily null
2530 case FP_ZERO:
2531 return qFuzzyIsNull(actual);
2532 }
2533}
2534
2535/*! \fn bool QTest::qCompare(const qfloat16 &t1, const qfloat16 &t2, const char *actual, const char *expected, const char *file, int line)
2536 \internal
2537 */
2538bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
2539 const char *file, int line)
2540{
2541 return compare_helper(floatingCompare(t1, t2),
2542 "Compared qfloat16s are not the same (fuzzy compare)",
2543 toString(t1), toString(t2), actual, expected, file, line);
2544}
2545
2546/*! \fn bool QTest::qCompare(const float &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
2547 \internal
2548 */
2549bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const char *expected,
2550 const char *file, int line)
2551{
2552 return QTestResult::compare(floatingCompare(t1, t2),
2553 "Compared floats are not the same (fuzzy compare)",
2554 t1, t2, actual, expected, file, line);
2555}
2556
2557/*! \fn bool QTest::qCompare(const double &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
2558 \internal
2559 */
2560bool QTest::qCompare(double const &t1, double const &t2, const char *actual, const char *expected,
2561 const char *file, int line)
2562{
2563 return QTestResult::compare(floatingCompare(t1, t2),
2564 "Compared doubles are not the same (fuzzy compare)",
2565 t1, t2, actual, expected, file, line);
2566}
2567
2568/*! \fn bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected, const char *file, int line)
2569 \internal
2570 \since 5.14
2571 */
2572bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
2573 const char *file, int line)
2574{
2575 return QTestResult::compare(t1 == t2,
2576 "Compared values are not the same",
2577 t1, t2, actual, expected, file, line);
2578}
2579
2580/*! \fn bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected, const char *file, int line)
2581 \internal
2582 \since 5.14
2583 */
2584bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected,
2585 const char *file, int line)
2586{
2587 return QTestResult::compare(t1 == t2,
2588 "Compared values are not the same",
2589 t1, t2, actual, expected, file, line);
2590}
2591
2592/*! \fn bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
2593 \internal
2594 \since 5.14
2595 */
2596bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected,
2597 const char *file, int line)
2598{
2599 return QTestResult::compare(t1 == t2,
2600 "Compared values are not the same",
2601 t1, t2, actual, expected, file, line);
2602}
2603
2604/*! \fn bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
2605 \internal
2606 \since 5.14
2607 */
2608bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual, const char *expected,
2609 const char *file, int line)
2610{
2611 return QTestResult::compare(t1 == t2,
2612 "Compared values are not the same",
2613 t1, t2, actual, expected, file, line);
2614}
2615
2616/*! \fn bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
2617 \internal
2618 \since 5.14
2619 */
2620bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual, const char *expected,
2621 const char *file, int line)
2622{
2623 return QTestResult::compare(t1 == t2,
2624 "Compared values are not the same",
2625 t1, t2, actual, expected, file, line);
2626}
2627
2628/*! \fn bool QTest::qCompare(const QString &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
2629 \internal
2630 \since 5.14
2631 */
2632
2633/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
2634 \internal
2635 \since 5.14
2636 */
2637
2638/*! \fn bool QTest::qCompare(const QLatin1String &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
2639 \internal
2640 \since 5.14
2641 */
2642
2643/*! \fn bool QTest::qCompare(const double &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
2644 \internal
2645 */
2646
2647/*! \fn bool QTest::qCompare(const float &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
2648 \internal
2649 */
2650
2651#define TO_STRING_IMPL(TYPE, FORMAT) \
2652template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
2653{ \
2654 char *msg = new char[128]; \
2655 qsnprintf(msg, 128, #FORMAT, t); \
2656 return msg; \
2657}
2658
2659TO_STRING_IMPL(short, %hd)
2660TO_STRING_IMPL(ushort, %hu)
2661TO_STRING_IMPL(int, %d)
2662TO_STRING_IMPL(uint, %u)
2663TO_STRING_IMPL(long, %ld)
2664TO_STRING_IMPL(ulong, %lu)
2665#if defined(Q_OS_WIN)
2666TO_STRING_IMPL(qint64, %I64d)
2667TO_STRING_IMPL(quint64, %I64u)
2668#else
2669TO_STRING_IMPL(qint64, %lld)
2670TO_STRING_IMPL(quint64, %llu)
2671#endif
2672TO_STRING_IMPL(bool, %d)
2673TO_STRING_IMPL(signed char, %hhd)
2674TO_STRING_IMPL(unsigned char, %hhu)
2675
2676/*!
2677 \internal
2678
2679 Be consistent about leading 0 in exponent.
2680
2681 POSIX specifies that %e (hence %g when using it) uses at least two digits in
2682 the exponent, requiring a leading 0 on single-digit exponents; (at least)
2683 MinGW includes a leading zero also on an already-two-digit exponent,
2684 e.g. 9e-040, which differs from more usual platforms. So massage that away.
2685 */
2686static void massageExponent(char *text)
2687{
2688 char *p = strchr(text, 'e');
2689 if (!p)
2690 return;
2691 const char *const end = p + strlen(p); // *end is '\0'
2692 p += (p[1] == '-' || p[1] == '+') ? 2 : 1;
2693 if (p[0] != '0' || end - 2 <= p)
2694 return;
2695 // We have a leading 0 on an exponent of at least two more digits
2696 const char *n = p + 1;
2697 while (end - 2 > n && n[0] == '0')
2698 ++n;
2699 memmove(p, n, end + 1 - n);
2700}
2701
2702// Be consistent about display of infinities and NaNs (snprintf()'s varies,
2703// notably on MinGW, despite POSIX documenting "[-]inf" or "[-]infinity" for %f,
2704// %e and %g, uppercasing for their capital versions; similar for "nan"):
2705#define TO_STRING_FLOAT(TYPE, FORMAT) \
2706template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
2707{ \
2708 char *msg = new char[128]; \
2709 switch (qFpClassify(t)) { \
2710 case FP_INFINITE: \
2711 qstrncpy(msg, (t < 0 ? "-inf" : "inf"), 128); \
2712 break; \
2713 case FP_NAN: \
2714 qstrncpy(msg, "nan", 128); \
2715 break; \
2716 default: \
2717 qsnprintf(msg, 128, #FORMAT, double(t)); \
2718 massageExponent(msg); \
2719 break; \
2720 } \
2721 return msg; \
2722}
2723
2724TO_STRING_FLOAT(qfloat16, %.3g)
2725TO_STRING_FLOAT(float, %g)
2726TO_STRING_FLOAT(double, %.12g)
2727
2728template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t)
2729{
2730 unsigned char c = static_cast<unsigned char>(t);
2731 char *msg = new char[16];
2732 switch (c) {
2733 case 0x00:
2734 qstrcpy(msg, "'\\0'");
2735 break;
2736 case 0x07:
2737 qstrcpy(msg, "'\\a'");
2738 break;
2739 case 0x08:
2740 qstrcpy(msg, "'\\b'");
2741 break;
2742 case 0x09:
2743 qstrcpy(msg, "'\\t'");
2744 break;
2745 case 0x0a:
2746 qstrcpy(msg, "'\\n'");
2747 break;
2748 case 0x0b:
2749 qstrcpy(msg, "'\\v'");
2750 break;
2751 case 0x0c:
2752 qstrcpy(msg, "'\\f'");
2753 break;
2754 case 0x0d:
2755 qstrcpy(msg, "'\\r'");
2756 break;
2757 case 0x22:
2758 qstrcpy(msg, "'\\\"'");
2759 break;
2760 case 0x27:
2761 qstrcpy(msg, "'\\\''");
2762 break;
2763 case 0x5c:
2764 qstrcpy(msg, "'\\\\'");
2765 break;
2766 default:
2767 if (c < 0x20 || c >= 0x7F)
2768 qsnprintf(msg, 16, "'\\x%02x'", c);
2769 else
2770 qsnprintf(msg, 16, "'%c'" , c);
2771 }
2772 return msg;
2773}
2774
2775/*! \internal
2776 */
2777char *QTest::toString(const char *str)
2778{
2779 if (!str) {
2780 char *msg = new char[1];
2781 *msg = '\0';
2782 return msg;
2783 }
2784 char *msg = new char[strlen(str) + 1];
2785 return qstrcpy(msg, str);
2786}
2787
2788/*! \internal
2789 */
2790char *QTest::toString(const void *p)
2791{
2792 char *msg = new char[128];
2793 qsnprintf(msg, 128, "%p", p);
2794 return msg;
2795}
2796
2797/*! \fn char *QTest::toString(const QColor &color)
2798 \internal
2799 */
2800
2801/*! \fn char *QTest::toString(const QRegion &region)
2802 \internal
2803 */
2804
2805/*! \fn char *QTest::toString(const QHostAddress &addr)
2806 \internal
2807 */
2808
2809/*! \fn char *QTest::toString(QNetworkReply::NetworkError code)
2810 \internal
2811 */
2812
2813/*! \fn char *QTest::toString(const QNetworkCookie &cookie)
2814 \internal
2815 */
2816
2817/*! \fn char *QTest::toString(const QList<QNetworkCookie> &list)
2818 \internal
2819 */
2820
2821/*! \internal
2822 */
2823bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
2824 const char *expected, const char *file, int line)
2825{
2826 return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same",
2827 toString(t1), toString(t2), actual, expected, file, line);
2828}
2829
2830/*!
2831 \namespace QTest::Internal
2832 \internal
2833*/
2834
2835/*! \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)
2836 \internal
2837*/
2838
2839/*! \fn bool QTest::compare_ptr_helper(const volatile void *t1, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
2840 \internal
2841*/
2842
2843/*! \fn bool QTest::compare_ptr_helper(std::nullptr_t, const volatile void *t2, const char *actual, const char *expected, const char *file, int line)
2844 \internal
2845*/
2846
2847/*! \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)
2848 \internal
2849*/
2850
2851/*! \fn bool QTest::qCompare(const QIcon &t1, const QIcon &t2, const char *actual, const char *expected, const char *file, int line)
2852 \internal
2853*/
2854
2855/*! \fn bool QTest::qCompare(const QImage &t1, const QImage &t2, const char *actual, const char *expected, const char *file, int line)
2856 \internal
2857*/
2858
2859/*! \fn bool QTest::qCompare(const QPixmap &t1, const QPixmap &t2, const char *actual, const char *expected, const char *file, int line)
2860 \internal
2861*/
2862
2863/*! \fn template <typename T> bool QTest::qCompare(const T &t1, const T &t2, const char *actual, const char *expected, const char *file, int line)
2864 \internal
2865*/
2866
2867/*! \fn template <typename T> bool QTest::qCompare(const T *t1, const T *t2, const char *actual, const char *expected, const char *file, int line)
2868 \internal
2869*/
2870
2871/*! \fn template <typename T> bool QTest::qCompare(T *t, std::nullptr_t, const char *actual, const char *expected, const char *file, int line)
2872 \internal
2873*/
2874
2875/*! \fn template <typename T> bool QTest::qCompare(std::nullptr_t, T *t, const char *actual, const char *expected, const char *file, int line)
2876 \internal
2877*/
2878
2879/*! \fn template <typename T> bool QTest::qCompare(T *t1, T *t2, const char *actual, const char *expected, const char *file, int line)
2880 \internal
2881*/
2882
2883/*! \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)
2884 \internal
2885*/
2886
2887/*! \fn template <typename T1, typename T2> bool QTest::qCompare(T1 *t1, T2 *t2, const char *actual, const char *expected, const char *file, int line)
2888 \internal
2889*/
2890
2891/*! \fn bool QTest::qCompare(const char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
2892 \internal
2893*/
2894
2895/*! \fn bool QTest::qCompare(char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
2896 \internal
2897*/
2898
2899/*! \fn bool QTest::qCompare(char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line)
2900 \internal
2901*/
2902
2903/*! \fn bool QTest::qCompare(const char *t1, char *t2, const char *actual, const char *expected, const char *file, int line)
2904 \internal
2905*/
2906
2907/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
2908 \internal
2909*/
2910
2911/*! \fn bool QTest::qCompare(const QLatin1String &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
2912 \internal
2913*/
2914
2915/*! \fn bool QTest::qCompare(const QStringList &t1, const QStringList &t2, const char *actual, const char *expected, const char *file, int line)
2916 \internal
2917*/
2918
2919/*! \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)
2920 \internal
2921*/
2922
2923/*! \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)
2924 \internal
2925*/
2926
2927/*! \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)
2928 \internal
2929*/
2930
2931/*! \fn bool QTest::qCompare(const qint64 &t1, const qint32 &t2, const char *actual, const char *expected, const char *file, int line)
2932 \internal
2933*/
2934
2935/*! \fn bool QTest::qCompare(const qint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
2936 \internal
2937*/
2938
2939/*! \fn bool QTest::qCompare(const quint64 &t1, const quint32 &t2, const char *actual, const char *expected, const char *file, int line)
2940 \internal
2941*/
2942
2943/*! \fn bool QTest::qCompare(const qint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
2944 \internal
2945*/
2946
2947/*! \fn bool QTest::qCompare(const quint32 &t1, const qint64 &t2, const char *actual, const char *expected, const char *file, int line)
2948 \internal
2949*/
2950
2951/*! \fn bool QTest::qCompare(const quint32 &t1, const quint64 &t2, const char *actual, const char *expected, const char *file, int line)
2952 \internal
2953*/
2954
2955/*! \fn template <typename T> bool QTest::qTest(const T& actual, const char *elementName, const char *actualStr, const char *expected, const char *file, int line)
2956 \internal
2957*/
2958
2959/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
2960 \internal
2961*/
2962
2963/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1)
2964 \internal
2965*/
2966
2967/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
2968 \internal
2969*/
2970
2971/*! \fn void QTest::sendKeyEvent(KeyAction action, QWindow *window, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1)
2972 \internal
2973*/
2974
2975/*! \fn void QTest::simulateEvent(QWidget *widget, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
2976 \internal
2977*/
2978
2979/*! \fn void QTest::simulateEvent(QWindow *window, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
2980 \internal
2981*/
2982
2983QT_END_NAMESPACE
2984