1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2020 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef QTEST_H
6#define QTEST_H
7
8#include <QtTest/qttestglobal.h>
9#include <QtTest/qtestcase.h>
10#include <QtTest/qtestdata.h>
11#include <QtTest/qbenchmark.h>
12
13#include <QtCore/qbitarray.h>
14#include <QtCore/qbytearray.h>
15#include <QtCore/qcborarray.h>
16#include <QtCore/qcborcommon.h>
17#include <QtCore/qcbormap.h>
18#include <QtCore/qcborvalue.h>
19#include <QtCore/qstring.h>
20#include <QtCore/qstringlist.h>
21#include <QtCore/qcborcommon.h>
22#include <QtCore/qdatetime.h>
23#if QT_CONFIG(itemmodel)
24#include <QtCore/qabstractitemmodel.h>
25#endif
26#include <QtCore/qobject.h>
27#include <QtCore/qvariant.h>
28#include <QtCore/qurl.h>
29#include <QtCore/quuid.h>
30
31#if defined(TESTCASE_LOWDPI)
32#include <QtCore/qcoreapplication.h>
33#endif
34
35#include <QtCore/qpoint.h>
36#include <QtCore/qsize.h>
37#include <QtCore/qrect.h>
38
39#include <initializer_list>
40#include <memory>
41
42QT_BEGIN_NAMESPACE
43
44namespace QTest
45{
46
47template <> inline char *toString(const QStringView &str)
48{
49 return QTest::toPrettyUnicode(string: str);
50}
51
52template<> inline char *toString(const QString &str)
53{
54 return toString(str: QStringView(str));
55}
56
57template<> inline char *toString(const QLatin1StringView &str)
58{
59 return toString(str: QString(str));
60}
61
62template<> inline char *toString(const QByteArray &ba)
63{
64 return QTest::toPrettyCString(unicode: ba.constData(), length: ba.size());
65}
66
67template<> inline char *toString(const QBitArray &ba)
68{
69 qsizetype size = ba.size();
70 char *str = new char[size + 1];
71 for (qsizetype i = 0; i < size; ++i)
72 str[i] = "01"[ba.testBit(i)];
73 str[size] = '\0';
74 return str;
75}
76
77#if QT_CONFIG(datestring)
78template<> inline char *toString(const QTime &time)
79{
80 return time.isValid()
81 ? qstrdup(qPrintable(time.toString(u"hh:mm:ss.zzz")))
82 : qstrdup("Invalid QTime");
83}
84
85template<> inline char *toString(const QDate &date)
86{
87 return date.isValid()
88 ? qstrdup(qPrintable(date.toString(u"yyyy/MM/dd")))
89 : qstrdup("Invalid QDate");
90}
91
92template<> inline char *toString(const QDateTime &dateTime)
93{
94 return dateTime.isValid()
95 ? qstrdup(qPrintable(dateTime.toString(u"yyyy/MM/dd hh:mm:ss.zzz[t]")))
96 : qstrdup("Invalid QDateTime");
97}
98#endif // datestring
99
100template<> inline char *toString(const QCborError &c)
101{
102 // use the Q_ENUM formatting
103 return toString(t: c.c);
104}
105
106template<> inline char *toString(const QChar &c)
107{
108 const ushort uc = c.unicode();
109 if (uc < 128) {
110 char msg[32] = {'\0'};
111 qsnprintf(str: msg, n: sizeof(msg), fmt: "QChar: '%c' (0x%x)", char(uc), unsigned(uc));
112 return qstrdup(msg);
113 }
114 return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16))));
115}
116
117#if QT_CONFIG(itemmodel)
118template<> inline char *toString(const QModelIndex &idx)
119{
120 char msg[128];
121 qsnprintf(str: msg, n: sizeof(msg), fmt: "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model());
122 return qstrdup(msg);
123}
124#endif
125
126template<> inline char *toString(const QPoint &p)
127{
128 char msg[128] = {'\0'};
129 qsnprintf(str: msg, n: sizeof(msg), fmt: "QPoint(%d,%d)", p.x(), p.y());
130 return qstrdup(msg);
131}
132
133template<> inline char *toString(const QSize &s)
134{
135 char msg[128] = {'\0'};
136 qsnprintf(str: msg, n: sizeof(msg), fmt: "QSize(%dx%d)", s.width(), s.height());
137 return qstrdup(msg);
138}
139
140template<> inline char *toString(const QRect &s)
141{
142 char msg[256] = {'\0'};
143 qsnprintf(str: msg, n: sizeof(msg), fmt: "QRect(%d,%d %dx%d) (bottomright %d,%d)",
144 s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
145 return qstrdup(msg);
146}
147
148template<> inline char *toString(const QPointF &p)
149{
150 char msg[64] = {'\0'};
151 qsnprintf(str: msg, n: sizeof(msg), fmt: "QPointF(%g,%g)", p.x(), p.y());
152 return qstrdup(msg);
153}
154
155template<> inline char *toString(const QSizeF &s)
156{
157 char msg[64] = {'\0'};
158 qsnprintf(str: msg, n: sizeof(msg), fmt: "QSizeF(%gx%g)", s.width(), s.height());
159 return qstrdup(msg);
160}
161
162template<> inline char *toString(const QRectF &s)
163{
164 char msg[256] = {'\0'};
165 qsnprintf(str: msg, n: sizeof(msg), fmt: "QRectF(%g,%g %gx%g) (bottomright %g,%g)",
166 s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
167 return qstrdup(msg);
168}
169
170template<> inline char *toString(const QUrl &uri)
171{
172 if (!uri.isValid())
173 return qstrdup(qPrintable(QLatin1StringView("Invalid URL: ") + uri.errorString()));
174 return qstrdup(uri.toEncoded().constData());
175}
176
177template <> inline char *toString(const QUuid &uuid)
178{
179 return qstrdup(uuid.toByteArray().constData());
180}
181
182template<> inline char *toString(const QVariant &v)
183{
184 QByteArray vstring("QVariant(");
185 if (v.isValid()) {
186 QByteArray type(v.typeName());
187 if (type.isEmpty()) {
188 type = QByteArray::number(v.userType());
189 }
190 vstring.append(a: type);
191 if (!v.isNull()) {
192 vstring.append(c: ',');
193 if (v.canConvert<QString>()) {
194 vstring.append(a: v.toString().toLocal8Bit());
195 }
196 else {
197 vstring.append(s: "<value not representable as string>");
198 }
199 }
200 }
201 vstring.append(c: ')');
202
203 return qstrdup(vstring.constData());
204}
205
206namespace Internal {
207struct QCborValueFormatter
208{
209 enum { BufferLen = 256 };
210 static char *formatSimpleType(QCborSimpleType st)
211 {
212 char *buf = new char[BufferLen];
213 qsnprintf(str: buf, n: BufferLen, fmt: "QCborValue(QCborSimpleType(%d))", int(st));
214 return buf;
215 }
216
217 static char *formatTag(QCborTag tag, const QCborValue &taggedValue)
218 {
219 QScopedArrayPointer<char> hold(format(v: taggedValue));
220 char *buf = new char[BufferLen];
221 qsnprintf(str: buf, n: BufferLen, fmt: "QCborValue(QCborTag(%llu), %s)", tag, hold.get());
222 return buf;
223 }
224
225 static char *innerFormat(QCborValue::Type t, const char *str)
226 {
227 static const QMetaEnum typeEnum = []() {
228 int idx = QCborValue::staticMetaObject.indexOfEnumerator(name: "Type");
229 return QCborValue::staticMetaObject.enumerator(index: idx);
230 }();
231
232 char *buf = new char[BufferLen];
233 const char *typeName = typeEnum.valueToKey(value: t);
234 if (typeName)
235 qsnprintf(str: buf, n: BufferLen, fmt: "QCborValue(%s, %s)", typeName, str);
236 else
237 qsnprintf(str: buf, n: BufferLen, fmt: "QCborValue(<unknown type 0x%02x>)", t);
238 return buf;
239 }
240
241 template<typename T> static char *format(QCborValue::Type type, const T &t)
242 {
243 QScopedArrayPointer<char> hold(QTest::toString(t));
244 return innerFormat(t: type, str: hold.get());
245 }
246
247 static char *format(const QCborValue &v)
248 {
249 switch (v.type()) {
250 case QCborValue::Integer:
251 return format(type: v.type(), t: v.toInteger());
252 case QCborValue::ByteArray:
253 return format(type: v.type(), t: v.toByteArray());
254 case QCborValue::String:
255 return format(type: v.type(), t: v.toString());
256 case QCborValue::Array:
257 return innerFormat(t: v.type(), str: QScopedArrayPointer<char>(format(a: v.toArray())).get());
258 case QCborValue::Map:
259 return innerFormat(t: v.type(), str: QScopedArrayPointer<char>(format(m: v.toMap())).get());
260 case QCborValue::Tag:
261 return formatTag(tag: v.tag(), taggedValue: v.taggedValue());
262 case QCborValue::SimpleType:
263 break;
264 case QCborValue::True:
265 return qstrdup("QCborValue(true)");
266 case QCborValue::False:
267 return qstrdup("QCborValue(false)");
268 case QCborValue::Null:
269 return qstrdup("QCborValue(nullptr)");
270 case QCborValue::Undefined:
271 return qstrdup("QCborValue()");
272 case QCborValue::Double:
273 return format(type: v.type(), t: v.toDouble());
274 case QCborValue::DateTime:
275 case QCborValue::Url:
276 case QCborValue::RegularExpression:
277 return format(type: v.type(), t: v.taggedValue().toString());
278 case QCborValue::Uuid:
279 return format(type: v.type(), t: v.toUuid());
280 case QCborValue::Invalid:
281 return qstrdup("QCborValue(<invalid>)");
282 }
283
284 if (v.isSimpleType())
285 return formatSimpleType(st: v.toSimpleType());
286 return innerFormat(t: v.type(), str: "");
287 }
288
289 static char *format(const QCborArray &a)
290 {
291 QByteArray out(1, '[');
292 const char *comma = "";
293 for (QCborValueConstRef v : a) {
294 QScopedArrayPointer<char> s(format(v));
295 out += comma;
296 out += s.get();
297 comma = ", ";
298 }
299 out += ']';
300 return qstrdup(out.constData());
301 }
302
303 static char *format(const QCborMap &m)
304 {
305 QByteArray out(1, '{');
306 const char *comma = "";
307 for (auto pair : m) {
308 QScopedArrayPointer<char> key(format(v: pair.first));
309 QScopedArrayPointer<char> value(format(v: pair.second));
310 out += comma;
311 out += key.get();
312 out += ": ";
313 out += value.get();
314 comma = ", ";
315 }
316 out += '}';
317 return qstrdup(out.constData());
318 }
319};
320}
321
322template<> inline char *toString(const QCborValue &v)
323{
324 return Internal::QCborValueFormatter::format(v);
325}
326
327template<> inline char *toString(const QCborValueRef &v)
328{
329 return toString(v: QCborValue(v));
330}
331
332template<> inline char *toString(const QCborArray &a)
333{
334 return Internal::QCborValueFormatter::format(a);
335}
336
337template<> inline char *toString(const QCborMap &m)
338{
339 return Internal::QCborValueFormatter::format(m);
340}
341
342template <typename T1, typename T2>
343inline char *toString(const std::pair<T1, T2> &pair)
344{
345 const QScopedArrayPointer<char> first(toString(pair.first));
346 const QScopedArrayPointer<char> second(toString(pair.second));
347 return formatString(prefix: "std::pair(", suffix: ")", numArguments: 2, first.data(), second.data());
348}
349
350template <typename Tuple, int... I>
351inline char *toString(const Tuple & tuple, QtPrivate::IndexesList<I...>) {
352 using UP = std::unique_ptr<char[]>;
353 // Generate a table of N + 1 elements where N is the number of
354 // elements in the tuple.
355 // The last element is needed to support the empty tuple use case.
356 const UP data[] = {
357 UP(toString(std::get<I>(tuple)))..., UP{}
358 };
359 return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...);
360}
361
362template <class... Types>
363inline char *toString(const std::tuple<Types...> &tuple)
364{
365 static const std::size_t params_count = sizeof...(Types);
366 return toString(tuple, typename QtPrivate::Indexes<params_count>::Value());
367}
368
369inline char *toString(std::nullptr_t)
370{
371 return toString(QStringLiteral("nullptr"));
372}
373
374template<>
375inline bool qCompare(QString const &t1, QLatin1StringView const &t2, const char *actual,
376 const char *expected, const char *file, int line)
377{
378 return qCompare(t1, t2: QString(t2), actual, expected, file, line);
379}
380template<>
381inline bool qCompare(QLatin1StringView const &t1, QString const &t2, const char *actual,
382 const char *expected, const char *file, int line)
383{
384 return qCompare(t1: QString(t1), t2, actual, expected, file, line);
385}
386
387// Compare sequences of equal size
388template <typename ActualIterator, typename ExpectedIterator>
389bool _q_compareSequence(ActualIterator actualIt, ActualIterator actualEnd,
390 ExpectedIterator expectedBegin, ExpectedIterator expectedEnd,
391 const char *actual, const char *expected,
392 const char *file, int line)
393{
394 char msg[1024];
395 msg[0] = '\0';
396
397 const qsizetype actualSize = actualEnd - actualIt;
398 const qsizetype expectedSize = expectedEnd - expectedBegin;
399 bool isOk = actualSize == expectedSize;
400
401 if (!isOk) {
402 qsnprintf(str: msg, n: sizeof(msg), fmt: "Compared lists have different sizes.\n"
403 " Actual (%s) size: %zd\n"
404 " Expected (%s) size: %zd", actual, actualSize,
405 expected, expectedSize);
406 }
407
408 for (auto expectedIt = expectedBegin; isOk && expectedIt < expectedEnd; ++actualIt, ++expectedIt) {
409 if (!(*actualIt == *expectedIt)) {
410 const qsizetype i = qsizetype(expectedIt - expectedBegin);
411 char *val1 = toString(*actualIt);
412 char *val2 = toString(*expectedIt);
413
414 qsnprintf(str: msg, n: sizeof(msg), fmt: "Compared lists differ at index %zd.\n"
415 " Actual (%s): %s\n"
416 " Expected (%s): %s", i, actual, val1 ? val1 : "<null>",
417 expected, val2 ? val2 : "<null>");
418 isOk = false;
419
420 delete [] val1;
421 delete [] val2;
422 }
423 }
424 return compare_helper(success: isOk, failureMsg: msg, actual, expected, file, line);
425}
426
427namespace Internal {
428
429#if defined(TESTCASE_LOWDPI)
430void disableHighDpi()
431{
432 qputenv("QT_ENABLE_HIGHDPI_SCALING", "0");
433}
434Q_CONSTRUCTOR_FUNCTION(disableHighDpi);
435#endif
436
437} // namespace Internal
438
439template <typename T>
440inline bool qCompare(QList<T> const &t1, QList<T> const &t2, const char *actual, const char *expected,
441 const char *file, int line)
442{
443 return _q_compareSequence(t1.cbegin(), t1.cend(), t2.cbegin(), t2.cend(),
444 actual, expected, file, line);
445}
446
447template <typename T, int N>
448bool qCompare(QList<T> const &t1, std::initializer_list<T> t2,
449 const char *actual, const char *expected,
450 const char *file, int line)
451{
452 return _q_compareSequence(t1.cbegin(), t1.cend(), t2.cbegin(), t2.cend(),
453 actual, expected, file, line);
454}
455
456// Compare QList against array
457template <typename T, int N>
458bool qCompare(QList<T> const &t1, const T (& t2)[N],
459 const char *actual, const char *expected,
460 const char *file, int line)
461{
462 return _q_compareSequence(t1.cbegin(), t1.cend(), t2, t2 + N,
463 actual, expected, file, line);
464}
465
466template <typename T>
467inline bool qCompare(QFlags<T> const &t1, T const &t2, const char *actual, const char *expected,
468 const char *file, int line)
469{
470 using Int = typename QFlags<T>::Int;
471 return qCompare(Int(t1), Int(t2), actual, expected, file, line);
472}
473
474template <typename T>
475inline bool qCompare(QFlags<T> const &t1, int const &t2, const char *actual, const char *expected,
476 const char *file, int line)
477{
478 using Int = typename QFlags<T>::Int;
479 return qCompare(Int(t1), Int(t2), actual, expected, file, line);
480}
481
482template<>
483inline bool qCompare(qint64 const &t1, qint32 const &t2, const char *actual,
484 const char *expected, const char *file, int line)
485{
486 return qCompare(t1, t2: static_cast<qint64>(t2), actual, expected, file, line);
487}
488
489template<>
490inline bool qCompare(qint64 const &t1, quint32 const &t2, const char *actual,
491 const char *expected, const char *file, int line)
492{
493 return qCompare(t1, t2: static_cast<qint64>(t2), actual, expected, file, line);
494}
495
496template<>
497inline bool qCompare(quint64 const &t1, quint32 const &t2, const char *actual,
498 const char *expected, const char *file, int line)
499{
500 return qCompare(t1, t2: static_cast<quint64>(t2), actual, expected, file, line);
501}
502
503template<>
504inline bool qCompare(qint32 const &t1, qint64 const &t2, const char *actual,
505 const char *expected, const char *file, int line)
506{
507 return qCompare(t1: static_cast<qint64>(t1), t2, actual, expected, file, line);
508}
509
510template<>
511inline bool qCompare(quint32 const &t1, qint64 const &t2, const char *actual,
512 const char *expected, const char *file, int line)
513{
514 return qCompare(t1: static_cast<qint64>(t1), t2, actual, expected, file, line);
515}
516
517template<>
518inline bool qCompare(quint32 const &t1, quint64 const &t2, const char *actual,
519 const char *expected, const char *file, int line)
520{
521 return qCompare(t1: static_cast<quint64>(t1), t2, actual, expected, file, line);
522}
523namespace Internal {
524
525template <typename T>
526class HasInitMain // SFINAE test for the presence of initMain()
527{
528private:
529 using YesType = char[1];
530 using NoType = char[2];
531
532 template <typename C> static YesType& test( decltype(&C::initMain) ) ;
533 template <typename C> static NoType& test(...);
534
535public:
536 enum { value = sizeof(test<T>(nullptr)) == sizeof(YesType) };
537};
538
539template<typename T>
540typename std::enable_if<HasInitMain<T>::value, void>::type callInitMain()
541{
542 T::initMain();
543}
544
545template<typename T>
546typename std::enable_if<!HasInitMain<T>::value, void>::type callInitMain()
547{
548}
549
550} // namespace Internal
551
552} // namespace QTest
553QT_END_NAMESPACE
554
555#ifdef QT_TESTCASE_BUILDDIR
556# define QTEST_SET_MAIN_SOURCE_PATH QTest::setMainSourcePath(__FILE__, QT_TESTCASE_BUILDDIR);
557#else
558# define QTEST_SET_MAIN_SOURCE_PATH QTest::setMainSourcePath(__FILE__);
559#endif
560
561// Hooks for coverage-testing of QTestLib itself:
562#if QT_CONFIG(testlib_selfcover) && defined(__COVERAGESCANNER__)
563struct QtCoverageScanner
564{
565 QtCoverageScanner(const char *name)
566 {
567 __coveragescanner_clear();
568 __coveragescanner_testname(name);
569 }
570 ~QtCoverageScanner()
571 {
572 __coveragescanner_save();
573 __coveragescanner_testname("");
574 }
575};
576#define TESTLIB_SELFCOVERAGE_START(name) QtCoverageScanner _qtCoverageScanner(name);
577#else
578#define TESTLIB_SELFCOVERAGE_START(name)
579#endif
580
581// Internal (but used by some testlib selftests to hack argc and argv).
582// Tests should normally implement initMain() if they have set-up to do before
583// instantiating the test class.
584#define QTEST_MAIN_WRAPPER(TestObject, ...) \
585int main(int argc, char *argv[]) \
586{ \
587 TESTLIB_SELFCOVERAGE_START(#TestObject) \
588 QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
589 __VA_ARGS__ \
590 TestObject tc; \
591 QTEST_SET_MAIN_SOURCE_PATH \
592 return QTest::qExec(&tc, argc, argv); \
593}
594
595// For when you don't even want a QApplication:
596#define QTEST_APPLESS_MAIN(TestObject) QTEST_MAIN_WRAPPER(TestObject)
597
598#include <QtTest/qtestsystem.h>
599
600#if defined(QT_NETWORK_LIB)
601# include <QtTest/qtest_network.h>
602#endif
603
604// Internal
605#define QTEST_QAPP_SETUP(klaz) \
606 klaz app(argc, argv); \
607 app.setAttribute(Qt::AA_Use96Dpi, true);
608
609#if defined(QT_WIDGETS_LIB)
610# include <QtTest/qtest_widgets.h>
611# ifdef QT_KEYPAD_NAVIGATION
612# define QTEST_DISABLE_KEYPAD_NAVIGATION QApplication::setNavigationMode(Qt::NavigationModeNone);
613# else
614# define QTEST_DISABLE_KEYPAD_NAVIGATION
615# endif
616// Internal
617# define QTEST_MAIN_SETUP() QTEST_QAPP_SETUP(QApplication) QTEST_DISABLE_KEYPAD_NAVIGATION
618#elif defined(QT_GUI_LIB)
619# include <QtTest/qtest_gui.h>
620// Internal
621# define QTEST_MAIN_SETUP() QTEST_QAPP_SETUP(QGuiApplication)
622#else
623// Internal
624# define QTEST_MAIN_SETUP() QTEST_QAPP_SETUP(QCoreApplication)
625#endif // QT_GUI_LIB
626
627// For most tests:
628#define QTEST_MAIN(TestObject) QTEST_MAIN_WRAPPER(TestObject, QTEST_MAIN_SETUP())
629
630// For command-line tests
631#define QTEST_GUILESS_MAIN(TestObject) \
632 QTEST_MAIN_WRAPPER(TestObject, QTEST_QAPP_SETUP(QCoreApplication))
633
634#endif
635

source code of qtbase/src/testlib/qtest.h