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 char msg[32];
203 std::snprintf(msg, sizeof(msg), "QChar: '%c' (0x%x)", char(uc), unsigned(uc));
204 return qstrdup(msg);
205 }
206 return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16))));
207}
208
209#if QT_CONFIG(itemmodel)
210template<> inline char *toString(const QModelIndex &idx)
211{
212 char msg[128];
213 std::snprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)",
215 static_cast<const void*>(idx.model()));
216 return qstrdup(msg);
217}
218#endif
219
220template<> inline char *toString(const QPoint &p)
221{
222 char msg[128];
223 std::snprintf(msg, sizeof(msg), "QPoint(%d,%d)", p.x(), p.y());
224 return qstrdup(msg);
225}
226
227template<> inline char *toString(const QSize &s)
228{
229 char msg[128];
230 std::snprintf(msg, sizeof(msg), "QSize(%dx%d)", s.width(), s.height());
231 return qstrdup(msg);
232}
233
234template<> inline char *toString(const QRect &s)
235{
236 char msg[256];
237 std::snprintf(msg, sizeof(msg), "QRect(%d,%d %dx%d) (bottomright %d,%d)",
238 s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
239 return qstrdup(msg);
240}
241
242template<> inline char *toString(const QPointF &p)
243{
244 char msg[64];
245 std::snprintf(msg, sizeof(msg), "QPointF(%g,%g)", p.x(), p.y());
246 return qstrdup(msg);
247}
248
249template<> inline char *toString(const QSizeF &s)
250{
251 char msg[64];
252 std::snprintf(msg, sizeof(msg), "QSizeF(%gx%g)", s.width(), s.height());
253 return qstrdup(msg);
254}
255
256template<> inline char *toString(const QRectF &s)
257{
258 char msg[256];
259 std::snprintf(msg, sizeof(msg), "QRectF(%g,%g %gx%g) (bottomright %g,%g)",
260 s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
261 return qstrdup(msg);
262}
263
264template<> inline char *toString(const QUrl &uri)
265{
266 if (!uri.isValid())
267 return qstrdup(qPrintable(QLatin1StringView("Invalid URL: ") + uri.errorString()));
268 return qstrdup(uri.toEncoded().constData());
269}
270
271template <> inline char *toString(const QUuid &uuid)
272{
273 return qstrdup(uuid.toByteArray().constData());
274}
275
276template<> inline char *toString(const QVariant &v)
277{
278 QByteArray vstring("QVariant(");
279 if (v.isValid()) {
280 QByteArray type(v.typeName());
281 if (type.isEmpty()) {
282 type = QByteArray::number(v.userType());
283 }
284 vstring.append(type);
285 if (!v.isNull()) {
286 vstring.append(',');
287 if (v.canConvert<QString>()) {
288 vstring.append(v.toString().toLocal8Bit());
289 }
290 else {
291 vstring.append("<value not representable as string>");
292 }
293 }
294 }
295 vstring.append(')');
296
297 return qstrdup(vstring.constData());
298}
299
300template<> inline char *toString(const QPartialOrdering &o)
301{
302 if (o == QPartialOrdering::Less)
303 return qstrdup("Less");
304 if (o == QPartialOrdering::Equivalent)
305 return qstrdup("Equivalent");
306 if (o == QPartialOrdering::Greater)
307 return qstrdup("Greater");
308 if (o == QPartialOrdering::Unordered)
309 return qstrdup("Unordered");
310 return qstrdup("<invalid>");
311}
312
313namespace Internal {
315{
316private:
317 using UP = std::unique_ptr<char[]>;
318 enum { BufferLen = 256 };
319
320 static UP createBuffer() { return q20::make_unique_for_overwrite<char[]>(BufferLen); }
321
322 static UP formatSimpleType(QCborSimpleType st)
323 {
324 auto buf = createBuffer();
325 std::snprintf(buf.get(), BufferLen, "QCborValue(QCborSimpleType(%d))", int(st));
326 return buf;
327 }
328
329 static UP formatTag(QCborTag tag, const QCborValue &taggedValue)
330 {
331 auto buf = createBuffer();
332 const std::unique_ptr<char[]> hold(format(taggedValue));
333 std::snprintf(buf.get(), BufferLen, "QCborValue(QCborTag(%llu), %s)",
334 qToUnderlying(tag), hold.get());
335 return buf;
336 }
337
338 static UP innerFormat(QCborValue::Type t, const UP &str)
339 {
340 static const QMetaEnum typeEnum = []() {
341 int idx = QCborValue::staticMetaObject.indexOfEnumerator("Type");
342 return QCborValue::staticMetaObject.enumerator(idx);
343 }();
344
345 auto buf = createBuffer();
346 const char *typeName = typeEnum.valueToKey(t);
347 if (typeName)
348 std::snprintf(buf.get(), BufferLen, "QCborValue(%s, %s)", typeName, str ? str.get() : "");
349 else
350 std::snprintf(buf.get(), BufferLen, "QCborValue(<unknown type 0x%02x>)", t);
351 return buf;
352 }
353
354 template<typename T> static UP format(QCborValue::Type type, const T &t)
355 {
356 const std::unique_ptr<char[]> hold(QTest::toString(t));
357 return innerFormat(type, hold);
358 }
359
360public:
361
362 static UP format(const QCborValue &v)
363 {
364 switch (v.type()) {
365 case QCborValue::Integer:
366 return format(v.type(), v.toInteger());
367 case QCborValue::ByteArray:
368 return format(v.type(), v.toByteArray());
369 case QCborValue::String:
370 return format(v.type(), v.toString());
371 case QCborValue::Array:
372 return innerFormat(v.type(), format(v.toArray()));
373 case QCborValue::Map:
374 return innerFormat(v.type(), format(v.toMap()));
375 case QCborValue::Tag:
376 return formatTag(v.tag(), v.taggedValue());
377 case QCborValue::SimpleType:
378 break;
379 case QCborValue::True:
380 return UP{qstrdup("QCborValue(true)")};
381 case QCborValue::False:
382 return UP{qstrdup("QCborValue(false)")};
383 case QCborValue::Null:
384 return UP{qstrdup("QCborValue(nullptr)")};
385 case QCborValue::Undefined:
386 return UP{qstrdup("QCborValue()")};
387 case QCborValue::Double:
388 return format(v.type(), v.toDouble());
389 case QCborValue::DateTime:
390 case QCborValue::Url:
391 case QCborValue::RegularExpression:
392 return format(v.type(), v.taggedValue().toString());
393 case QCborValue::Uuid:
394 return format(v.type(), v.toUuid());
395 case QCborValue::Invalid:
396 return UP{qstrdup("QCborValue(<invalid>)")};
397 }
398
399 if (v.isSimpleType())
400 return formatSimpleType(v.toSimpleType());
401 return innerFormat(v.type(), nullptr);
402 }
403
404 static UP format(const QCborArray &a)
405 {
406 QByteArray out(1, '[');
407 const char *comma = "";
408 for (QCborValueConstRef v : a) {
409 const std::unique_ptr<char[]> s(format(v));
410 out += comma;
411 out += s.get();
412 comma = ", ";
413 }
414 out += ']';
415 return UP{qstrdup(out.constData())};
416 }
417
418 static UP format(const QCborMap &m)
419 {
420 QByteArray out(1, '{');
421 const char *comma = "";
422 for (auto pair : m) {
423 const std::unique_ptr<char[]> key(format(pair.first));
424 const std::unique_ptr<char[]> value(format(pair.second));
425 out += comma;
426 out += key.get();
427 out += ": ";
428 out += value.get();
429 comma = ", ";
430 }
431 out += '}';
432 return UP{qstrdup(out.constData())};
433 }
434};
435}
436
437template<> inline char *toString(const QCborValue &v)
438{
439 return Internal::QCborValueFormatter::format(v).release();
440}
441
442template<> inline char *toString(const QCborValueRef &v)
443{
444 return toString(QCborValue(v));
445}
446
447template<> inline char *toString(const QCborArray &a)
448{
449 return Internal::QCborValueFormatter::format(a).release();
450}
451
452template<> inline char *toString(const QCborMap &m)
453{
454 return Internal::QCborValueFormatter::format(m).release();
455}
456
457template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur)
458{
459 QString r;
460 QDebug d(&r);
462 if constexpr (Period::num != 1 || Period::den != 1) {
463 // include the equivalent value in seconds, in parentheses
464 using namespace std::chrono;
465 d << " (" << duration_cast<duration<qreal>>(dur).count() << "s)";
466 }
467 return qstrdup(std::move(r).toUtf8().constData());
468}
469
470template <typename T1, typename T2>
471inline char *toString(const std::pair<T1, T2> &pair)
472{
473 const std::unique_ptr<char[]> first(toString(pair.first));
474 const std::unique_ptr<char[]> second(toString(pair.second));
475 return formatString("std::pair(", ")", 2, first.get(), second.get());
476}
477
478template <typename Tuple, std::size_t... I>
479inline char *tupleToString(const Tuple &tuple, std::index_sequence<I...>) {
480 using UP = std::unique_ptr<char[]>;
481 // Generate a table of N + 1 elements where N is the number of
482 // elements in the tuple.
483 // The last element is needed to support the empty tuple use case.
484 const UP data[] = {
485 UP(toString(std::get<I>(tuple)))..., UP{}
486 };
487 return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...);
488}
489
490template <class... Types>
491inline char *toString(const std::tuple<Types...> &tuple)
492{
493 return tupleToString(tuple, std::make_index_sequence<sizeof...(Types)>{});
494}
495
496inline char *toString(std::nullptr_t)
497{
498 return toString(QStringView(u"nullptr"));
499}
500} // namespace QTest
501
502QT_END_NAMESPACE
503
504#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)