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 ®ion)
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