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