1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2024 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 QTESTTOSTRING_H
6#define QTESTTOSTRING_H
7
8#include <QtTest/qttestglobal.h>
9
10#include <QtCore/qttypetraits.h>
11
12#if QT_CONFIG(itemmodel)
13# include <QtCore/qabstractitemmodel.h>
14#endif
15#include <QtCore/qbitarray.h>
16#include <QtCore/qbytearray.h>
17#include <QtCore/qcborarray.h>
18#include <QtCore/qcborcommon.h>
19#include <QtCore/qcbormap.h>
20#include <QtCore/qcborvalue.h>
21#include <QtCore/qdatetime.h>
22#include <QtCore/qmetaobject.h>
23#include <QtCore/qmetatype.h>
24#include <QtCore/qobject.h>
25#include <QtCore/qpoint.h>
26#include <QtCore/qrect.h>
27#include <QtCore/qsize.h>
28#include <QtCore/qstring.h>
29#include <QtCore/qstringlist.h>
30#include <QtCore/qurl.h>
31#include <QtCore/quuid.h>
32#include <QtCore/qvariant.h>
33
34#include <cstdio>
35
36QT_BEGIN_NAMESPACE
37
38namespace QTest {
39namespace Internal {
40
41template<typename T> // Output registered enums
42inline typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, char*>::type toString(T e)
43{
44 QMetaEnum me = QMetaEnum::fromType<T>();
45 return qstrdup(me.valueToKey(value: int(e))); // int cast is necessary to support enum classes
46}
47
48template <typename T>
49inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && std::is_enum_v<T>, char*>::type toString(const T &e)
50{
51 return qstrdup(QByteArray::number(static_cast<std::underlying_type_t<T>>(e)).constData());
52}
53
54template <typename T> // Fallback; for built-in types debug streaming must be possible
55inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && !std::is_enum_v<T>, char *>::type toString(const T &t)
56{
57 char *result = nullptr;
58#ifndef QT_NO_DEBUG_STREAM
59 if constexpr (QTypeTraits::has_ostream_operator_v<QDebug, T>) {
60 result = qstrdup(QDebug::toString(t).toUtf8().constData());
61 } else {
62 static_assert(!QMetaTypeId2<T>::IsBuiltIn,
63 "Built-in type must implement debug streaming operator "
64 "or provide QTest::toString specialization");
65 }
66#endif
67 return result;
68}
69
70template<typename F> // Output QFlags of registered enumerations
71inline typename std::enable_if<QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f)
72{
73 const QMetaEnum me = QMetaEnum::fromType<F>();
74 return qstrdup(me.valueToKeys(value: int(f.toInt())).constData());
75}
76
77template <typename F> // Fallback: Output hex value
78inline typename std::enable_if<!QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f)
79{
80 const size_t space = 3 + 2 * sizeof(unsigned); // 2 for 0x, two hex digits per byte, 1 for '\0'
81 char *msg = new char[space];
82 std::snprintf(s: msg, maxlen: space, format: "0x%x", unsigned(f.toInt()));
83 return msg;
84}
85
86} // namespace Internal
87
88Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual,
89 const char *expected, const char *file, int line);
90Q_TESTLIB_EXPORT char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...);
91Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, qsizetype length);
92Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, qsizetype length);
93Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string);
94
95template <typename T>
96inline char *toString(const T &t)
97{
98 return Internal::toString(t);
99}
100
101template <typename T1, typename T2>
102inline char *toString(const std::pair<T1, T2> &pair);
103
104template <class... Types>
105inline char *toString(const std::tuple<Types...> &tuple);
106
107template <typename Rep, typename Period>
108inline char *toString(std::chrono::duration<Rep, Period> duration);
109
110Q_TESTLIB_EXPORT char *toString(const char *);
111Q_TESTLIB_EXPORT char *toString(const volatile void *);
112Q_TESTLIB_EXPORT char *toString(const QObject *);
113Q_TESTLIB_EXPORT char *toString(const volatile QObject *);
114
115#define QTEST_COMPARE_DECL(KLASS)\
116 template<> Q_TESTLIB_EXPORT char *toString<KLASS >(const KLASS &);
117#ifndef Q_QDOC
118QTEST_COMPARE_DECL(short)
119QTEST_COMPARE_DECL(ushort)
120QTEST_COMPARE_DECL(int)
121QTEST_COMPARE_DECL(uint)
122QTEST_COMPARE_DECL(long)
123QTEST_COMPARE_DECL(ulong)
124QTEST_COMPARE_DECL(qint64)
125QTEST_COMPARE_DECL(quint64)
126
127QTEST_COMPARE_DECL(float)
128QTEST_COMPARE_DECL(double)
129QTEST_COMPARE_DECL(qfloat16)
130QTEST_COMPARE_DECL(char)
131QTEST_COMPARE_DECL(signed char)
132QTEST_COMPARE_DECL(unsigned char)
133QTEST_COMPARE_DECL(bool)
134#endif
135#undef QTEST_COMPARE_DECL
136
137template <> inline char *toString(const QStringView &str)
138{
139 return QTest::toPrettyUnicode(string: str);
140}
141
142template<> inline char *toString(const QString &str)
143{
144 return toString(str: QStringView(str));
145}
146
147template<> inline char *toString(const QLatin1StringView &str)
148{
149 return toString(str: QString(str));
150}
151
152template<> inline char *toString(const QByteArray &ba)
153{
154 return QTest::toPrettyCString(unicode: ba.constData(), length: ba.size());
155}
156
157template<> inline char *toString(const QBitArray &ba)
158{
159 qsizetype size = ba.size();
160 char *str = new char[size + 1];
161 for (qsizetype i = 0; i < size; ++i)
162 str[i] = "01"[ba.testBit(i)];
163 str[size] = '\0';
164 return str;
165}
166
167#if QT_CONFIG(datestring)
168template<> inline char *toString(const QTime &time)
169{
170 return time.isValid()
171 ? qstrdup(qPrintable(time.toString(u"hh:mm:ss.zzz")))
172 : qstrdup("Invalid QTime");
173}
174
175template<> inline char *toString(const QDate &date)
176{
177 return date.isValid()
178 ? qstrdup(qPrintable(date.toString(u"yyyy/MM/dd")))
179 : qstrdup("Invalid QDate");
180}
181
182template<> inline char *toString(const QDateTime &dateTime)
183{
184 return dateTime.isValid()
185 ? qstrdup(qPrintable(dateTime.toString(u"yyyy/MM/dd hh:mm:ss.zzz[t]")))
186 : qstrdup("Invalid QDateTime");
187}
188#endif // datestring
189
190template<> inline char *toString(const QCborError &c)
191{
192 // use the Q_ENUM formatting
193 return toString(t: c.c);
194}
195
196template<> inline char *toString(const QChar &c)
197{
198 const ushort uc = c.unicode();
199 if (uc < 128) {
200 char msg[32];
201 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QChar: '%c' (0x%x)", char(uc), unsigned(uc));
202 return qstrdup(msg);
203 }
204 return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16))));
205}
206
207#if QT_CONFIG(itemmodel)
208template<> inline char *toString(const QModelIndex &idx)
209{
210 char msg[128];
211 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QModelIndex(%d,%d,%p,%p)",
212 idx.row(), idx.column(), idx.internalPointer(),
213 static_cast<const void*>(idx.model()));
214 return qstrdup(msg);
215}
216#endif
217
218template<> inline char *toString(const QPoint &p)
219{
220 char msg[128];
221 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QPoint(%d,%d)", p.x(), p.y());
222 return qstrdup(msg);
223}
224
225template<> inline char *toString(const QSize &s)
226{
227 char msg[128];
228 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QSize(%dx%d)", s.width(), s.height());
229 return qstrdup(msg);
230}
231
232template<> inline char *toString(const QRect &s)
233{
234 char msg[256];
235 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QRect(%d,%d %dx%d) (bottomright %d,%d)",
236 s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
237 return qstrdup(msg);
238}
239
240template<> inline char *toString(const QPointF &p)
241{
242 char msg[64];
243 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QPointF(%g,%g)", p.x(), p.y());
244 return qstrdup(msg);
245}
246
247template<> inline char *toString(const QSizeF &s)
248{
249 char msg[64];
250 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QSizeF(%gx%g)", s.width(), s.height());
251 return qstrdup(msg);
252}
253
254template<> inline char *toString(const QRectF &s)
255{
256 char msg[256];
257 std::snprintf(s: msg, maxlen: sizeof(msg), format: "QRectF(%g,%g %gx%g) (bottomright %g,%g)",
258 s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
259 return qstrdup(msg);
260}
261
262template<> inline char *toString(const QUrl &uri)
263{
264 if (!uri.isValid())
265 return qstrdup(qPrintable(QLatin1StringView("Invalid URL: ") + uri.errorString()));
266 return qstrdup(uri.toEncoded().constData());
267}
268
269template <> inline char *toString(const QUuid &uuid)
270{
271 return qstrdup(uuid.toByteArray().constData());
272}
273
274template<> inline char *toString(const QVariant &v)
275{
276 QByteArray vstring("QVariant(");
277 if (v.isValid()) {
278 QByteArray type(v.typeName());
279 if (type.isEmpty()) {
280 type = QByteArray::number(v.userType());
281 }
282 vstring.append(a: type);
283 if (!v.isNull()) {
284 vstring.append(c: ',');
285 if (v.canConvert<QString>()) {
286 vstring.append(a: v.toString().toLocal8Bit());
287 }
288 else {
289 vstring.append(s: "<value not representable as string>");
290 }
291 }
292 }
293 vstring.append(c: ')');
294
295 return qstrdup(vstring.constData());
296}
297
298template<> inline char *toString(const QPartialOrdering &o)
299{
300 if (o == QPartialOrdering::Less)
301 return qstrdup("Less");
302 if (o == QPartialOrdering::Equivalent)
303 return qstrdup("Equivalent");
304 if (o == QPartialOrdering::Greater)
305 return qstrdup("Greater");
306 if (o == QPartialOrdering::Unordered)
307 return qstrdup("Unordered");
308 return qstrdup("<invalid>");
309}
310
311namespace Internal {
312struct QCborValueFormatter
313{
314 enum { BufferLen = 256 };
315 static char *formatSimpleType(QCborSimpleType st)
316 {
317 char *buf = new char[BufferLen];
318 std::snprintf(s: buf, maxlen: BufferLen, format: "QCborValue(QCborSimpleType(%d))", int(st));
319 return buf;
320 }
321
322 static char *formatTag(QCborTag tag, const QCborValue &taggedValue)
323 {
324 QScopedArrayPointer<char> hold(format(v: taggedValue));
325 char *buf = new char[BufferLen];
326 std::snprintf(s: buf, maxlen: BufferLen, format: "QCborValue(QCborTag(%llu), %s)",
327 qToUnderlying(e: tag), hold.get());
328 return buf;
329 }
330
331 static char *innerFormat(QCborValue::Type t, const char *str)
332 {
333 static const QMetaEnum typeEnum = []() {
334 int idx = QCborValue::staticMetaObject.indexOfEnumerator(name: "Type");
335 return QCborValue::staticMetaObject.enumerator(index: idx);
336 }();
337
338 char *buf = new char[BufferLen];
339 const char *typeName = typeEnum.valueToKey(value: t);
340 if (typeName)
341 std::snprintf(s: buf, maxlen: BufferLen, format: "QCborValue(%s, %s)", typeName, str);
342 else
343 std::snprintf(s: buf, maxlen: BufferLen, format: "QCborValue(<unknown type 0x%02x>)", t);
344 return buf;
345 }
346
347 template<typename T> static char *format(QCborValue::Type type, const T &t)
348 {
349 QScopedArrayPointer<char> hold(QTest::toString(t));
350 return innerFormat(t: type, str: hold.get());
351 }
352
353 static char *format(const QCborValue &v)
354 {
355 switch (v.type()) {
356 case QCborValue::Integer:
357 return format(type: v.type(), t: v.toInteger());
358 case QCborValue::ByteArray:
359 return format(type: v.type(), t: v.toByteArray());
360 case QCborValue::String:
361 return format(type: v.type(), t: v.toString());
362 case QCborValue::Array:
363 return innerFormat(t: v.type(), str: QScopedArrayPointer<char>(format(a: v.toArray())).get());
364 case QCborValue::Map:
365 return innerFormat(t: v.type(), str: QScopedArrayPointer<char>(format(m: v.toMap())).get());
366 case QCborValue::Tag:
367 return formatTag(tag: v.tag(), taggedValue: v.taggedValue());
368 case QCborValue::SimpleType:
369 break;
370 case QCborValue::True:
371 return qstrdup("QCborValue(true)");
372 case QCborValue::False:
373 return qstrdup("QCborValue(false)");
374 case QCborValue::Null:
375 return qstrdup("QCborValue(nullptr)");
376 case QCborValue::Undefined:
377 return qstrdup("QCborValue()");
378 case QCborValue::Double:
379 return format(type: v.type(), t: v.toDouble());
380 case QCborValue::DateTime:
381 case QCborValue::Url:
382 case QCborValue::RegularExpression:
383 return format(type: v.type(), t: v.taggedValue().toString());
384 case QCborValue::Uuid:
385 return format(type: v.type(), t: v.toUuid());
386 case QCborValue::Invalid:
387 return qstrdup("QCborValue(<invalid>)");
388 }
389
390 if (v.isSimpleType())
391 return formatSimpleType(st: v.toSimpleType());
392 return innerFormat(t: v.type(), str: "");
393 }
394
395 static char *format(const QCborArray &a)
396 {
397 QByteArray out(1, '[');
398 const char *comma = "";
399 for (QCborValueConstRef v : a) {
400 QScopedArrayPointer<char> s(format(v));
401 out += comma;
402 out += s.get();
403 comma = ", ";
404 }
405 out += ']';
406 return qstrdup(out.constData());
407 }
408
409 static char *format(const QCborMap &m)
410 {
411 QByteArray out(1, '{');
412 const char *comma = "";
413 for (auto pair : m) {
414 QScopedArrayPointer<char> key(format(v: pair.first));
415 QScopedArrayPointer<char> value(format(v: pair.second));
416 out += comma;
417 out += key.get();
418 out += ": ";
419 out += value.get();
420 comma = ", ";
421 }
422 out += '}';
423 return qstrdup(out.constData());
424 }
425};
426}
427
428template<> inline char *toString(const QCborValue &v)
429{
430 return Internal::QCborValueFormatter::format(v);
431}
432
433template<> inline char *toString(const QCborValueRef &v)
434{
435 return toString(v: QCborValue(v));
436}
437
438template<> inline char *toString(const QCborArray &a)
439{
440 return Internal::QCborValueFormatter::format(a);
441}
442
443template<> inline char *toString(const QCborMap &m)
444{
445 return Internal::QCborValueFormatter::format(m);
446}
447
448template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur)
449{
450 QString r;
451 QDebug d(&r);
452 d.nospace() << qSetRealNumberPrecision(precision: 9) << dur;
453 if constexpr (Period::num != 1 || Period::den != 1) {
454 // include the equivalent value in seconds, in parentheses
455 using namespace std::chrono;
456 d << " (" << duration_cast<duration<qreal>>(dur).count() << "s)";
457 }
458 return qstrdup(std::move(r).toUtf8().constData());
459}
460
461template <typename T1, typename T2>
462inline char *toString(const std::pair<T1, T2> &pair)
463{
464 const QScopedArrayPointer<char> first(toString(pair.first));
465 const QScopedArrayPointer<char> second(toString(pair.second));
466 return formatString(prefix: "std::pair(", suffix: ")", numArguments: 2, first.data(), second.data());
467}
468
469template <typename Tuple, std::size_t... I>
470inline char *tupleToString(const Tuple &tuple, std::index_sequence<I...>) {
471 using UP = std::unique_ptr<char[]>;
472 // Generate a table of N + 1 elements where N is the number of
473 // elements in the tuple.
474 // The last element is needed to support the empty tuple use case.
475 const UP data[] = {
476 UP(toString(std::get<I>(tuple)))..., UP{}
477 };
478 return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...);
479}
480
481template <class... Types>
482inline char *toString(const std::tuple<Types...> &tuple)
483{
484 return tupleToString(tuple, std::make_index_sequence<sizeof...(Types)>{});
485}
486
487inline char *toString(std::nullptr_t)
488{
489 return toString(str: QStringView(u"nullptr"));
490}
491} // namespace QTest
492
493QT_END_NAMESPACE
494
495#endif // QTESTTOSTRING_H
496

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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