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>
This commit is contained in:
parent
b7fb9d8150
commit
4ae6f40b3a
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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__)
|
||||
|
||||
|
486
src/testlib/qtesttostring.h
Normal file
486
src/testlib/qtesttostring.h
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user