diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2024-04-11 12:14:09 -0700 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2024-04-26 18:19:36 +0200 |
commit | 4ae6f40b3a2e2fe4f617a1c911c8d066c18cdab9 (patch) | |
tree | 8e25b674ba7f8474c6654027a5adeee629619ceb | |
parent | b7fb9d8150ac84cb3f02e808ed0a0b186a6a57d9 (diff) |
QTest: group all the toString() functionality in a single header
It was split between qtest.h and qtestcase.h, so the QTest::toString()
specializations were not available in qtestcase.h. And it was confusing.
Change-Id: Ie28eadac333c4bcd8c08fffd17c54fafc8014cc7
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/testlib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/testlib/qtest.h | 375 | ||||
-rw-r--r-- | src/testlib/qtestcase.h | 96 | ||||
-rw-r--r-- | src/testlib/qtesttostring.h | 486 |
4 files changed, 489 insertions, 469 deletions
diff --git a/src/testlib/CMakeLists.txt b/src/testlib/CMakeLists.txt index 0758a46f70..e956a47cf1 100644 --- a/src/testlib/CMakeLists.txt +++ b/src/testlib/CMakeLists.txt @@ -57,6 +57,7 @@ qt_internal_add_module(Test qtestsystem.h qtesttable.cpp qtesttable_p.h qtesttouch.h + qtesttostring.h qtestwheel.h qttestglobal.h qxmltestlogger.cpp qxmltestlogger_p.h diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index 99eae8553f..ff3744a48e 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -12,34 +12,13 @@ #include <QtTest/qttestglobal.h> #include <QtTest/qtestcase.h> #include <QtTest/qtestdata.h> +#include <QtTest/qtesttostring.h> #include <QtTest/qbenchmark.h> -#include <QtCore/qbitarray.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qcborarray.h> -#include <QtCore/qcborcommon.h> -#include <QtCore/qcbormap.h> -#include <QtCore/qcborvalue.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qcborcommon.h> -#include <QtCore/qdatetime.h> -#if QT_CONFIG(itemmodel) -#include <QtCore/qabstractitemmodel.h> -#endif -#include <QtCore/qobject.h> -#include <QtCore/qvariant.h> -#include <QtCore/qurl.h> -#include <QtCore/quuid.h> - #if defined(TESTCASE_LOWDPI) #include <QtCore/qcoreapplication.h> #endif -#include <QtCore/qpoint.h> -#include <QtCore/qsize.h> -#include <QtCore/qrect.h> - #include <initializer_list> #include <memory> @@ -48,358 +27,6 @@ QT_BEGIN_NAMESPACE namespace QTest { -template <> inline char *toString(const QStringView &str) -{ - return QTest::toPrettyUnicode(str); -} - -template<> inline char *toString(const QString &str) -{ - return toString(QStringView(str)); -} - -template<> inline char *toString(const QLatin1StringView &str) -{ - return toString(QString(str)); -} - -template<> inline char *toString(const QByteArray &ba) -{ - return QTest::toPrettyCString(ba.constData(), ba.size()); -} - -template<> inline char *toString(const QBitArray &ba) -{ - qsizetype size = ba.size(); - char *str = new char[size + 1]; - for (qsizetype i = 0; i < size; ++i) - str[i] = "01"[ba.testBit(i)]; - str[size] = '\0'; - return str; -} - -#if QT_CONFIG(datestring) -template<> inline char *toString(const QTime &time) -{ - return time.isValid() - ? qstrdup(qPrintable(time.toString(u"hh:mm:ss.zzz"))) - : qstrdup("Invalid QTime"); -} - -template<> inline char *toString(const QDate &date) -{ - return date.isValid() - ? qstrdup(qPrintable(date.toString(u"yyyy/MM/dd"))) - : qstrdup("Invalid QDate"); -} - -template<> inline char *toString(const QDateTime &dateTime) -{ - return dateTime.isValid() - ? qstrdup(qPrintable(dateTime.toString(u"yyyy/MM/dd hh:mm:ss.zzz[t]"))) - : qstrdup("Invalid QDateTime"); -} -#endif // datestring - -template<> inline char *toString(const QCborError &c) -{ - // use the Q_ENUM formatting - return toString(c.c); -} - -template<> inline char *toString(const QChar &c) -{ - const ushort uc = c.unicode(); - if (uc < 128) { - char msg[32] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QChar: '%c' (0x%x)", char(uc), unsigned(uc)); - return qstrdup(msg); - } - return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16)))); -} - -#if QT_CONFIG(itemmodel) -template<> inline char *toString(const QModelIndex &idx) -{ - char msg[128]; - qsnprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model()); - return qstrdup(msg); -} -#endif - -template<> inline char *toString(const QPoint &p) -{ - char msg[128] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QPoint(%d,%d)", p.x(), p.y()); - return qstrdup(msg); -} - -template<> inline char *toString(const QSize &s) -{ - char msg[128] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QSize(%dx%d)", s.width(), s.height()); - return qstrdup(msg); -} - -template<> inline char *toString(const QRect &s) -{ - char msg[256] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QRect(%d,%d %dx%d) (bottomright %d,%d)", - s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom()); - return qstrdup(msg); -} - -template<> inline char *toString(const QPointF &p) -{ - char msg[64] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QPointF(%g,%g)", p.x(), p.y()); - return qstrdup(msg); -} - -template<> inline char *toString(const QSizeF &s) -{ - char msg[64] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QSizeF(%gx%g)", s.width(), s.height()); - return qstrdup(msg); -} - -template<> inline char *toString(const QRectF &s) -{ - char msg[256] = {'\0'}; - qsnprintf(msg, sizeof(msg), "QRectF(%g,%g %gx%g) (bottomright %g,%g)", - s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom()); - return qstrdup(msg); -} - -template<> inline char *toString(const QUrl &uri) -{ - if (!uri.isValid()) - return qstrdup(qPrintable(QLatin1StringView("Invalid URL: ") + uri.errorString())); - return qstrdup(uri.toEncoded().constData()); -} - -template <> inline char *toString(const QUuid &uuid) -{ - return qstrdup(uuid.toByteArray().constData()); -} - -template<> inline char *toString(const QVariant &v) -{ - QByteArray vstring("QVariant("); - if (v.isValid()) { - QByteArray type(v.typeName()); - if (type.isEmpty()) { - type = QByteArray::number(v.userType()); - } - vstring.append(type); - if (!v.isNull()) { - vstring.append(','); - if (v.canConvert<QString>()) { - vstring.append(v.toString().toLocal8Bit()); - } - else { - vstring.append("<value not representable as string>"); - } - } - } - vstring.append(')'); - - return qstrdup(vstring.constData()); -} - -template<> inline char *toString(const QPartialOrdering &o) -{ - if (o == QPartialOrdering::Less) - return qstrdup("Less"); - if (o == QPartialOrdering::Equivalent) - return qstrdup("Equivalent"); - if (o == QPartialOrdering::Greater) - return qstrdup("Greater"); - if (o == QPartialOrdering::Unordered) - return qstrdup("Unordered"); - return qstrdup("<invalid>"); -} - -namespace Internal { -struct QCborValueFormatter -{ - enum { BufferLen = 256 }; - static char *formatSimpleType(QCborSimpleType st) - { - char *buf = new char[BufferLen]; - qsnprintf(buf, BufferLen, "QCborValue(QCborSimpleType(%d))", int(st)); - return buf; - } - - static char *formatTag(QCborTag tag, const QCborValue &taggedValue) - { - QScopedArrayPointer<char> hold(format(taggedValue)); - char *buf = new char[BufferLen]; - qsnprintf(buf, BufferLen, "QCborValue(QCborTag(%llu), %s)", tag, hold.get()); - return buf; - } - - static char *innerFormat(QCborValue::Type t, const char *str) - { - static const QMetaEnum typeEnum = []() { - int idx = QCborValue::staticMetaObject.indexOfEnumerator("Type"); - return QCborValue::staticMetaObject.enumerator(idx); - }(); - - char *buf = new char[BufferLen]; - const char *typeName = typeEnum.valueToKey(t); - if (typeName) - qsnprintf(buf, BufferLen, "QCborValue(%s, %s)", typeName, str); - else - qsnprintf(buf, BufferLen, "QCborValue(<unknown type 0x%02x>)", t); - return buf; - } - - template<typename T> static char *format(QCborValue::Type type, const T &t) - { - QScopedArrayPointer<char> hold(QTest::toString(t)); - return innerFormat(type, hold.get()); - } - - static char *format(const QCborValue &v) - { - switch (v.type()) { - case QCborValue::Integer: - return format(v.type(), v.toInteger()); - case QCborValue::ByteArray: - return format(v.type(), v.toByteArray()); - case QCborValue::String: - return format(v.type(), v.toString()); - case QCborValue::Array: - return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toArray())).get()); - case QCborValue::Map: - return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toMap())).get()); - case QCborValue::Tag: - return formatTag(v.tag(), v.taggedValue()); - case QCborValue::SimpleType: - break; - case QCborValue::True: - return qstrdup("QCborValue(true)"); - case QCborValue::False: - return qstrdup("QCborValue(false)"); - case QCborValue::Null: - return qstrdup("QCborValue(nullptr)"); - case QCborValue::Undefined: - return qstrdup("QCborValue()"); - case QCborValue::Double: - return format(v.type(), v.toDouble()); - case QCborValue::DateTime: - case QCborValue::Url: - case QCborValue::RegularExpression: - return format(v.type(), v.taggedValue().toString()); - case QCborValue::Uuid: - return format(v.type(), v.toUuid()); - case QCborValue::Invalid: - return qstrdup("QCborValue(<invalid>)"); - } - - if (v.isSimpleType()) - return formatSimpleType(v.toSimpleType()); - return innerFormat(v.type(), ""); - } - - static char *format(const QCborArray &a) - { - QByteArray out(1, '['); - const char *comma = ""; - for (QCborValueConstRef v : a) { - QScopedArrayPointer<char> s(format(v)); - out += comma; - out += s.get(); - comma = ", "; - } - out += ']'; - return qstrdup(out.constData()); - } - - static char *format(const QCborMap &m) - { - QByteArray out(1, '{'); - const char *comma = ""; - for (auto pair : m) { - QScopedArrayPointer<char> key(format(pair.first)); - QScopedArrayPointer<char> value(format(pair.second)); - out += comma; - out += key.get(); - out += ": "; - out += value.get(); - comma = ", "; - } - out += '}'; - return qstrdup(out.constData()); - } -}; -} - -template<> inline char *toString(const QCborValue &v) -{ - return Internal::QCborValueFormatter::format(v); -} - -template<> inline char *toString(const QCborValueRef &v) -{ - return toString(QCborValue(v)); -} - -template<> inline char *toString(const QCborArray &a) -{ - return Internal::QCborValueFormatter::format(a); -} - -template<> inline char *toString(const QCborMap &m) -{ - return Internal::QCborValueFormatter::format(m); -} - -template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur) -{ - QString r; - QDebug d(&r); - d.nospace() << qSetRealNumberPrecision(9) << dur; - if constexpr (Period::num != 1 || Period::den != 1) { - // include the equivalent value in seconds, in parentheses - using namespace std::chrono; - d << " (" << duration_cast<duration<qreal>>(dur).count() << "s)"; - } - return qstrdup(std::move(r).toUtf8().constData()); -} - -template <typename T1, typename T2> -inline char *toString(const std::pair<T1, T2> &pair) -{ - const QScopedArrayPointer<char> first(toString(pair.first)); - const QScopedArrayPointer<char> second(toString(pair.second)); - return formatString("std::pair(", ")", 2, first.data(), second.data()); -} - -template <typename Tuple, std::size_t... I> -inline char *tupleToString(const Tuple &tuple, std::index_sequence<I...>) { - using UP = std::unique_ptr<char[]>; - // Generate a table of N + 1 elements where N is the number of - // elements in the tuple. - // The last element is needed to support the empty tuple use case. - const UP data[] = { - UP(toString(std::get<I>(tuple)))..., UP{} - }; - return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...); -} - -template <class... Types> -inline char *toString(const std::tuple<Types...> &tuple) -{ - return tupleToString(tuple, std::make_index_sequence<sizeof...(Types)>{}); -} - -inline char *toString(std::nullptr_t) -{ - return toString(QStringView(u"nullptr")); -} - template<> inline bool qCompare(QString const &t1, QLatin1StringView const &t2, const char *actual, const char *expected, const char *file, int line) diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index 632b06fed3..06fbd25c9c 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -5,6 +5,7 @@ #define QTESTCASE_H #include <QtTest/qttestglobal.h> +#include <QtTest/qtesttostring.h> #include <QtCore/qstring.h> #include <QtCore/qnamespace.h> @@ -318,9 +319,6 @@ do {\ class QObject; class QTestData; -#define QTEST_COMPARE_DECL(KLASS)\ - template<> Q_TESTLIB_EXPORT char *toString<KLASS >(const KLASS &); - namespace QTest { namespace Internal { @@ -332,78 +330,11 @@ namespace QTest Q_TESTLIB_EXPORT QString formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual); - template<typename T> // Output registered enums - inline typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, char*>::type toString(T e) - { - QMetaEnum me = QMetaEnum::fromType<T>(); - return qstrdup(me.valueToKey(int(e))); // int cast is necessary to support enum classes - } - - template <typename T> - inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && std::is_enum_v<T>, char*>::type toString(const T &e) - { - return qstrdup(QByteArray::number(static_cast<std::underlying_type_t<T>>(e)).constData()); - } - - template <typename T> // Fallback; for built-in types debug streaming must be possible - inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && !std::is_enum_v<T>, char *>::type toString(const T &t) - { - char *result = nullptr; -#ifndef QT_NO_DEBUG_STREAM - if constexpr (QTypeTraits::has_ostream_operator_v<QDebug, T>) { - result = qstrdup(QDebug::toString(t).toUtf8().constData()); - } else { - static_assert(!QMetaTypeId2<T>::IsBuiltIn, - "Built-in type must implement debug streaming operator " - "or provide QTest::toString specialization"); - } -#endif - return result; - } - - template<typename F> // Output QFlags of registered enumerations - inline typename std::enable_if<QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f) - { - const QMetaEnum me = QMetaEnum::fromType<F>(); - return qstrdup(me.valueToKeys(int(f.toInt())).constData()); - } - - template <typename F> // Fallback: Output hex value - inline typename std::enable_if<!QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f) - { - const size_t space = 3 + 2 * sizeof(unsigned); // 2 for 0x, two hex digits per byte, 1 for '\0' - char *msg = new char[space]; - qsnprintf(msg, space, "0x%x", unsigned(f.toInt())); - return msg; - } - // Exported so Qt Quick Test can also use it for generating backtraces upon crashes. Q_TESTLIB_EXPORT extern bool noCrashHandler; } // namespace Internal - template<typename T> - inline char *toString(const T &t) - { - return Internal::toString(t); - } - - template <typename T1, typename T2> - inline char *toString(const std::pair<T1, T2> &pair); - - template <class... Types> - inline char *toString(const std::tuple<Types...> &tuple); - - template <typename Rep, typename Period> - inline char *toString(std::chrono::duration<Rep, Period> duration); - - Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, qsizetype length); - Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, qsizetype length); - Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string); - Q_TESTLIB_EXPORT char *toString(const char *); - Q_TESTLIB_EXPORT char *toString(const volatile void *); - Q_TESTLIB_EXPORT char *toString(const volatile QObject *); - Q_TESTLIB_EXPORT void qInit(QObject *testObject, int argc = 0, char **argv = nullptr); Q_TESTLIB_EXPORT int qRun(); Q_TESTLIB_EXPORT void qCleanup(); @@ -620,30 +551,6 @@ namespace QTest actual, expected, file, line); } - Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual, - const char *expected, const char *file, int line); - - Q_TESTLIB_EXPORT char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...); - -#ifndef Q_QDOC - QTEST_COMPARE_DECL(short) - QTEST_COMPARE_DECL(ushort) - QTEST_COMPARE_DECL(int) - QTEST_COMPARE_DECL(uint) - QTEST_COMPARE_DECL(long) - QTEST_COMPARE_DECL(ulong) - QTEST_COMPARE_DECL(qint64) - QTEST_COMPARE_DECL(quint64) - - QTEST_COMPARE_DECL(float) - QTEST_COMPARE_DECL(double) - QTEST_COMPARE_DECL(qfloat16) - QTEST_COMPARE_DECL(char) - QTEST_COMPARE_DECL(signed char) - QTEST_COMPARE_DECL(unsigned char) - QTEST_COMPARE_DECL(bool) -#endif - template <typename T1, typename T2 = T1> inline bool qCompare(const T1 &t1, const T2 &t2, const char *actual, const char *expected, const char *file, int line) @@ -743,7 +650,6 @@ namespace QTest ComparisonOperation op, const char *file, int line); } -#undef QTEST_COMPARE_DECL #define QWARN(msg) QTest::qWarn(static_cast<const char *>(msg), __FILE__, __LINE__) diff --git a/src/testlib/qtesttostring.h b/src/testlib/qtesttostring.h new file mode 100644 index 0000000000..3e2a59c217 --- /dev/null +++ b/src/testlib/qtesttostring.h @@ -0,0 +1,486 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QTESTTOSTRING_H +#define QTESTTOSTRING_H + +#include <QtTest/qttestglobal.h> + +#if QT_CONFIG(itemmodel) +# include <QtCore/qabstractitemmodel.h> +#endif +#include <QtCore/qbitarray.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qcborarray.h> +#include <QtCore/qcborcommon.h> +#include <QtCore/qcbormap.h> +#include <QtCore/qcborvalue.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtCore/qsize.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qurl.h> +#include <QtCore/quuid.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_NAMESPACE + +namespace QTest { +namespace Internal { + +template<typename T> // Output registered enums +inline typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, char*>::type toString(T e) +{ + QMetaEnum me = QMetaEnum::fromType<T>(); + return qstrdup(me.valueToKey(int(e))); // int cast is necessary to support enum classes +} + +template <typename T> +inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && std::is_enum_v<T>, char*>::type toString(const T &e) +{ + return qstrdup(QByteArray::number(static_cast<std::underlying_type_t<T>>(e)).constData()); +} + +template <typename T> // Fallback; for built-in types debug streaming must be possible +inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && !std::is_enum_v<T>, char *>::type toString(const T &t) +{ + char *result = nullptr; +#ifndef QT_NO_DEBUG_STREAM + if constexpr (QTypeTraits::has_ostream_operator_v<QDebug, T>) { + result = qstrdup(QDebug::toString(t).toUtf8().constData()); + } else { + static_assert(!QMetaTypeId2<T>::IsBuiltIn, + "Built-in type must implement debug streaming operator " + "or provide QTest::toString specialization"); + } +#endif + return result; +} + +template<typename F> // Output QFlags of registered enumerations +inline typename std::enable_if<QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f) +{ + const QMetaEnum me = QMetaEnum::fromType<F>(); + return qstrdup(me.valueToKeys(int(f.toInt())).constData()); +} + +template <typename F> // Fallback: Output hex value +inline typename std::enable_if<!QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f) +{ + const size_t space = 3 + 2 * sizeof(unsigned); // 2 for 0x, two hex digits per byte, 1 for '\0' + char *msg = new char[space]; + qsnprintf(msg, space, "0x%x", unsigned(f.toInt())); + return msg; +} + +} // namespace Internal + +Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual, + const char *expected, const char *file, int line); +Q_TESTLIB_EXPORT char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...); +Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, qsizetype length); +Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, qsizetype length); +Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string); +Q_TESTLIB_EXPORT char *toString(const char *); +Q_TESTLIB_EXPORT char *toString(const volatile void *); +Q_TESTLIB_EXPORT char *toString(const volatile QObject *); + +template<typename T> +inline char *toString(const T &t) +{ + return Internal::toString(t); +} + +template <typename T1, typename T2> +inline char *toString(const std::pair<T1, T2> &pair); + +template <class... Types> +inline char *toString(const std::tuple<Types...> &tuple); + +template <typename Rep, typename Period> +inline char *toString(std::chrono::duration<Rep, Period> duration); + +#define QTEST_COMPARE_DECL(KLASS)\ + template<> Q_TESTLIB_EXPORT char *toString<KLASS >(const KLASS &); +#ifndef Q_QDOC +QTEST_COMPARE_DECL(short) +QTEST_COMPARE_DECL(ushort) +QTEST_COMPARE_DECL(int) +QTEST_COMPARE_DECL(uint) +QTEST_COMPARE_DECL(long) +QTEST_COMPARE_DECL(ulong) +QTEST_COMPARE_DECL(qint64) +QTEST_COMPARE_DECL(quint64) + +QTEST_COMPARE_DECL(float) +QTEST_COMPARE_DECL(double) +QTEST_COMPARE_DECL(qfloat16) +QTEST_COMPARE_DECL(char) +QTEST_COMPARE_DECL(signed char) +QTEST_COMPARE_DECL(unsigned char) +QTEST_COMPARE_DECL(bool) +#endif +#undef QTEST_COMPARE_DECL + +template <> inline char *toString(const QStringView &str) +{ + return QTest::toPrettyUnicode(str); +} + +template<> inline char *toString(const QString &str) +{ + return toString(QStringView(str)); +} + +template<> inline char *toString(const QLatin1StringView &str) +{ + return toString(QString(str)); +} + +template<> inline char *toString(const QByteArray &ba) +{ + return QTest::toPrettyCString(ba.constData(), ba.size()); +} + +template<> inline char *toString(const QBitArray &ba) +{ + qsizetype size = ba.size(); + char *str = new char[size + 1]; + for (qsizetype i = 0; i < size; ++i) + str[i] = "01"[ba.testBit(i)]; + str[size] = '\0'; + return str; +} + +#if QT_CONFIG(datestring) +template<> inline char *toString(const QTime &time) +{ + return time.isValid() + ? qstrdup(qPrintable(time.toString(u"hh:mm:ss.zzz"))) + : qstrdup("Invalid QTime"); +} + +template<> inline char *toString(const QDate &date) +{ + return date.isValid() + ? qstrdup(qPrintable(date.toString(u"yyyy/MM/dd"))) + : qstrdup("Invalid QDate"); +} + +template<> inline char *toString(const QDateTime &dateTime) +{ + return dateTime.isValid() + ? qstrdup(qPrintable(dateTime.toString(u"yyyy/MM/dd hh:mm:ss.zzz[t]"))) + : qstrdup("Invalid QDateTime"); +} +#endif // datestring + +template<> inline char *toString(const QCborError &c) +{ + // use the Q_ENUM formatting + return toString(c.c); +} + +template<> inline char *toString(const QChar &c) +{ + const ushort uc = c.unicode(); + if (uc < 128) { + char msg[32] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QChar: '%c' (0x%x)", char(uc), unsigned(uc)); + return qstrdup(msg); + } + return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16)))); +} + +#if QT_CONFIG(itemmodel) +template<> inline char *toString(const QModelIndex &idx) +{ + char msg[128]; + qsnprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model()); + return qstrdup(msg); +} +#endif + +template<> inline char *toString(const QPoint &p) +{ + char msg[128] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QPoint(%d,%d)", p.x(), p.y()); + return qstrdup(msg); +} + +template<> inline char *toString(const QSize &s) +{ + char msg[128] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QSize(%dx%d)", s.width(), s.height()); + return qstrdup(msg); +} + +template<> inline char *toString(const QRect &s) +{ + char msg[256] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QRect(%d,%d %dx%d) (bottomright %d,%d)", + s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom()); + return qstrdup(msg); +} + +template<> inline char *toString(const QPointF &p) +{ + char msg[64] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QPointF(%g,%g)", p.x(), p.y()); + return qstrdup(msg); +} + +template<> inline char *toString(const QSizeF &s) +{ + char msg[64] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QSizeF(%gx%g)", s.width(), s.height()); + return qstrdup(msg); +} + +template<> inline char *toString(const QRectF &s) +{ + char msg[256] = {'\0'}; + qsnprintf(msg, sizeof(msg), "QRectF(%g,%g %gx%g) (bottomright %g,%g)", + s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom()); + return qstrdup(msg); +} + +template<> inline char *toString(const QUrl &uri) +{ + if (!uri.isValid()) + return qstrdup(qPrintable(QLatin1StringView("Invalid URL: ") + uri.errorString())); + return qstrdup(uri.toEncoded().constData()); +} + +template <> inline char *toString(const QUuid &uuid) +{ + return qstrdup(uuid.toByteArray().constData()); +} + +template<> inline char *toString(const QVariant &v) +{ + QByteArray vstring("QVariant("); + if (v.isValid()) { + QByteArray type(v.typeName()); + if (type.isEmpty()) { + type = QByteArray::number(v.userType()); + } + vstring.append(type); + if (!v.isNull()) { + vstring.append(','); + if (v.canConvert<QString>()) { + vstring.append(v.toString().toLocal8Bit()); + } + else { + vstring.append("<value not representable as string>"); + } + } + } + vstring.append(')'); + + return qstrdup(vstring.constData()); +} + +template<> inline char *toString(const QPartialOrdering &o) +{ + if (o == QPartialOrdering::Less) + return qstrdup("Less"); + if (o == QPartialOrdering::Equivalent) + return qstrdup("Equivalent"); + if (o == QPartialOrdering::Greater) + return qstrdup("Greater"); + if (o == QPartialOrdering::Unordered) + return qstrdup("Unordered"); + return qstrdup("<invalid>"); +} + +namespace Internal { +struct QCborValueFormatter +{ + enum { BufferLen = 256 }; + static char *formatSimpleType(QCborSimpleType st) + { + char *buf = new char[BufferLen]; + qsnprintf(buf, BufferLen, "QCborValue(QCborSimpleType(%d))", int(st)); + return buf; + } + + static char *formatTag(QCborTag tag, const QCborValue &taggedValue) + { + QScopedArrayPointer<char> hold(format(taggedValue)); + char *buf = new char[BufferLen]; + qsnprintf(buf, BufferLen, "QCborValue(QCborTag(%llu), %s)", tag, hold.get()); + return buf; + } + + static char *innerFormat(QCborValue::Type t, const char *str) + { + static const QMetaEnum typeEnum = []() { + int idx = QCborValue::staticMetaObject.indexOfEnumerator("Type"); + return QCborValue::staticMetaObject.enumerator(idx); + }(); + + char *buf = new char[BufferLen]; + const char *typeName = typeEnum.valueToKey(t); + if (typeName) + qsnprintf(buf, BufferLen, "QCborValue(%s, %s)", typeName, str); + else + qsnprintf(buf, BufferLen, "QCborValue(<unknown type 0x%02x>)", t); + return buf; + } + + template<typename T> static char *format(QCborValue::Type type, const T &t) + { + QScopedArrayPointer<char> hold(QTest::toString(t)); + return innerFormat(type, hold.get()); + } + + static char *format(const QCborValue &v) + { + switch (v.type()) { + case QCborValue::Integer: + return format(v.type(), v.toInteger()); + case QCborValue::ByteArray: + return format(v.type(), v.toByteArray()); + case QCborValue::String: + return format(v.type(), v.toString()); + case QCborValue::Array: + return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toArray())).get()); + case QCborValue::Map: + return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toMap())).get()); + case QCborValue::Tag: + return formatTag(v.tag(), v.taggedValue()); + case QCborValue::SimpleType: + break; + case QCborValue::True: + return qstrdup("QCborValue(true)"); + case QCborValue::False: + return qstrdup("QCborValue(false)"); + case QCborValue::Null: + return qstrdup("QCborValue(nullptr)"); + case QCborValue::Undefined: + return qstrdup("QCborValue()"); + case QCborValue::Double: + return format(v.type(), v.toDouble()); + case QCborValue::DateTime: + case QCborValue::Url: + case QCborValue::RegularExpression: + return format(v.type(), v.taggedValue().toString()); + case QCborValue::Uuid: + return format(v.type(), v.toUuid()); + case QCborValue::Invalid: + return qstrdup("QCborValue(<invalid>)"); + } + + if (v.isSimpleType()) + return formatSimpleType(v.toSimpleType()); + return innerFormat(v.type(), ""); + } + + static char *format(const QCborArray &a) + { + QByteArray out(1, '['); + const char *comma = ""; + for (QCborValueConstRef v : a) { + QScopedArrayPointer<char> s(format(v)); + out += comma; + out += s.get(); + comma = ", "; + } + out += ']'; + return qstrdup(out.constData()); + } + + static char *format(const QCborMap &m) + { + QByteArray out(1, '{'); + const char *comma = ""; + for (auto pair : m) { + QScopedArrayPointer<char> key(format(pair.first)); + QScopedArrayPointer<char> value(format(pair.second)); + out += comma; + out += key.get(); + out += ": "; + out += value.get(); + comma = ", "; + } + out += '}'; + return qstrdup(out.constData()); + } +}; +} + +template<> inline char *toString(const QCborValue &v) +{ + return Internal::QCborValueFormatter::format(v); +} + +template<> inline char *toString(const QCborValueRef &v) +{ + return toString(QCborValue(v)); +} + +template<> inline char *toString(const QCborArray &a) +{ + return Internal::QCborValueFormatter::format(a); +} + +template<> inline char *toString(const QCborMap &m) +{ + return Internal::QCborValueFormatter::format(m); +} + +template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur) +{ + QString r; + QDebug d(&r); + d.nospace() << qSetRealNumberPrecision(9) << dur; + if constexpr (Period::num != 1 || Period::den != 1) { + // include the equivalent value in seconds, in parentheses + using namespace std::chrono; + d << " (" << duration_cast<duration<qreal>>(dur).count() << "s)"; + } + return qstrdup(std::move(r).toUtf8().constData()); +} + +template <typename T1, typename T2> +inline char *toString(const std::pair<T1, T2> &pair) +{ + const QScopedArrayPointer<char> first(toString(pair.first)); + const QScopedArrayPointer<char> second(toString(pair.second)); + return formatString("std::pair(", ")", 2, first.data(), second.data()); +} + +template <typename Tuple, std::size_t... I> +inline char *tupleToString(const Tuple &tuple, std::index_sequence<I...>) { + using UP = std::unique_ptr<char[]>; + // Generate a table of N + 1 elements where N is the number of + // elements in the tuple. + // The last element is needed to support the empty tuple use case. + const UP data[] = { + UP(toString(std::get<I>(tuple)))..., UP{} + }; + return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...); +} + +template <class... Types> +inline char *toString(const std::tuple<Types...> &tuple) +{ + return tupleToString(tuple, std::make_index_sequence<sizeof...(Types)>{}); +} + +inline char *toString(std::nullptr_t) +{ + return toString(QStringView(u"nullptr")); +} +} // namespace QTest + +QT_END_NAMESPACE + +#endif // QTESTTOSTRING_H |