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