From 99ce02f692bd9ad59f5e159ce65ef13f4e041a31 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 16 Apr 2020 19:09:34 -0300 Subject: [PATCH] QCborValue: add support for QCOMPARE string output Change-Id: Ibdc95e9af7bd456a94ecfffd16066c47ea9766d0 Reviewed-by: Ulf Hermann Reviewed-by: Edward Welbourne --- src/corelib/serialization/qcborvalue.cpp | 150 +++++++++--------- src/testlib/qtest.h | 145 ++++++++++++++++- .../qcborvalue/tst_qcborvalue.cpp | 140 +++++++++++++++- 3 files changed, 358 insertions(+), 77 deletions(-) diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index ea2d092a1f4..16f9b1cf054 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -2995,68 +2995,6 @@ size_t qHash(const QCborValue &value, size_t seed) return qHash(value.toSimpleType(), seed); } -#if !defined(QT_NO_DEBUG_STREAM) -static QDebug debugContents(QDebug &dbg, const QCborValue &v) -{ - switch (v.type()) { - case QCborValue::Integer: - return dbg << v.toInteger(); - case QCborValue::ByteArray: - return dbg << "QByteArray(" << v.toByteArray() << ')'; - case QCborValue::String: - return dbg << v.toString(); - case QCborValue::Array: - return dbg << v.toArray(); - case QCborValue::Map: - return dbg << v.toMap(); - case QCborValue::Tag: - dbg << v.tag() << ", "; - return debugContents(dbg, v.taggedValue()); - case QCborValue::SimpleType: - break; - case QCborValue::True: - return dbg << true; - case QCborValue::False: - return dbg << false; - case QCborValue::Null: - return dbg << "nullptr"; - case QCborValue::Undefined: - return dbg; - case QCborValue::Double: { - qint64 i = qint64(v.toDouble()); - if (i == v.toDouble()) - return dbg << i << ".0"; - else - return dbg << v.toDouble(); - } - case QCborValue::DateTime: - return dbg << v.toDateTime(); -#ifndef QT_BOOTSTRAPPED - case QCborValue::Url: - return dbg << v.toUrl(); -#endif -#if QT_CONFIG(regularexpression) - case QCborValue::RegularExpression: - return dbg << v.toRegularExpression(); -#endif - case QCborValue::Uuid: - return dbg << v.toUuid(); - case QCborValue::Invalid: - return dbg << ""; - default: - break; - } - if (v.isSimpleType()) - return dbg << v.toSimpleType(); - return dbg << "'; -} -QDebug operator<<(QDebug dbg, const QCborValue &v) -{ - QDebugStateSaver saver(dbg); - dbg.nospace() << "QCborValue("; - return debugContents(dbg, v) << ')'; -} - Q_CORE_EXPORT const char *qt_cbor_simpletype_id(QCborSimpleType st) { switch (st) { @@ -3072,16 +3010,6 @@ Q_CORE_EXPORT const char *qt_cbor_simpletype_id(QCborSimpleType st) return nullptr; } -QDebug operator<<(QDebug dbg, QCborSimpleType st) -{ - QDebugStateSaver saver(dbg); - const char *id = qt_cbor_simpletype_id(st); - if (id) - return dbg.nospace() << "QCborSimpleType::" << id; - - return dbg.nospace() << "QCborSimpleType(" << uint(st) << ')'; -} - Q_CORE_EXPORT const char *qt_cbor_tag_id(QCborTag tag) { // Casting to QCborKnownTags's underlying type will make the comparison @@ -3140,6 +3068,84 @@ Q_CORE_EXPORT const char *qt_cbor_tag_id(QCborTag tag) return nullptr; } +#if !defined(QT_NO_DEBUG_STREAM) +static QDebug debugContents(QDebug &dbg, const QCborValue &v) +{ + switch (v.type()) { + case QCborValue::Integer: + return dbg << v.toInteger(); + case QCborValue::ByteArray: + return dbg << "QByteArray(" << v.toByteArray() << ')'; + case QCborValue::String: + return dbg << v.toString(); + case QCborValue::Array: + return dbg << v.toArray(); + case QCborValue::Map: + return dbg << v.toMap(); + case QCborValue::Tag: { + QCborTag tag = v.tag(); + const char *id = qt_cbor_tag_id(tag); + if (id) + dbg.nospace() << "QCborKnownTags::" << id << ", "; + else + dbg.nospace() << "QCborTag(" << quint64(tag) << "), "; + return dbg << v.taggedValue(); + } + case QCborValue::SimpleType: + break; + case QCborValue::True: + return dbg << true; + case QCborValue::False: + return dbg << false; + case QCborValue::Null: + return dbg << "nullptr"; + case QCborValue::Undefined: + return dbg; + case QCborValue::Double: { + qint64 i; + if (convertDoubleTo(v.toDouble(), &i)) + return dbg << i << ".0"; + else + return dbg << v.toDouble(); + } + case QCborValue::DateTime: + return dbg << v.toDateTime(); +#ifndef QT_BOOTSTRAPPED + case QCborValue::Url: + return dbg << v.toUrl(); +#if QT_CONFIG(regularexpression) + case QCborValue::RegularExpression: + return dbg << v.toRegularExpression(); +#endif + case QCborValue::Uuid: + return dbg << v.toUuid(); +#endif + case QCborValue::Invalid: + return dbg << ""; + default: + break; + } + if (v.isSimpleType()) + return dbg << v.toSimpleType(); + return dbg << "'; +} +QDebug operator<<(QDebug dbg, const QCborValue &v) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QCborValue("; + return debugContents(dbg, v) << ')'; +} + +QDebug operator<<(QDebug dbg, QCborSimpleType st) +{ + QDebugStateSaver saver(dbg); + const char *id = qt_cbor_simpletype_id(st); + if (id) + return dbg.nospace() << "QCborSimpleType::" << id; + + return dbg.nospace() << "QCborSimpleType(" << uint(st) << ')'; +} + QDebug operator<<(QDebug dbg, QCborTag tag) { QDebugStateSaver saver(dbg); diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index cceefde84a5..57612de3de6 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtTest module of the Qt Toolkit. @@ -48,6 +48,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -69,7 +73,6 @@ QT_BEGIN_NAMESPACE - namespace QTest { @@ -232,6 +235,142 @@ template<> inline char *toString(const QVariant &v) return qstrdup(vstring.constData()); } +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 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()", t); + return buf; + } + + template static char *format(QCborValue::Type type, const T &t) + { + QScopedArrayPointer 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(format(v.toArray())).get()); + case QCborValue::Map: + return innerFormat(v.type(), QScopedArrayPointer(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()"); + } + + if (v.isSimpleType()) + return formatSimpleType(v.toSimpleType()); + return innerFormat(v.type(), ""); + } + + static char *format(const QCborArray &a) + { + QByteArray out(1, '['); + const char *comma = ""; + for (const QCborValueRef v : a) { + QScopedArrayPointer 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 key(format(pair.first)); + QScopedArrayPointer 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 inline char *toString(const std::pair &pair) { diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index 0ab86652d61..1fc4064955c 100644 --- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -120,6 +120,10 @@ private slots: void datastreamSerialization_data(); void datastreamSerialization(); void streamVariantSerialization(); + void debugOutput_data(); + void debugOutput(); + void testlibFormatting_data(); + void testlibFormatting(); }; namespace SimpleEncodeToCbor { @@ -456,8 +460,8 @@ void tst_QCborValue::extendedTypes() QVERIFY(extended.isTag()); QVERIFY(tagged.isTag()); QCOMPARE(tagged.taggedValue(), correctedTaggedValue); - QVERIFY(extended == tagged); - QVERIFY(tagged == extended); + QCOMPARE(tagged, extended); + QCOMPARE(extended, tagged); QCOMPARE(extended.tag(), tagged.tag()); QCOMPARE(extended.taggedValue(), tagged.taggedValue()); @@ -2474,6 +2478,138 @@ void tst_QCborValue::streamVariantSerialization() } } +void tst_QCborValue::debugOutput_data() +{ + QTest::addColumn("v"); + QTest::addColumn("expected"); + + QDateTime dt(QDate(2020, 4, 18), QTime(13, 41, 22, 123), Qt::UTC); + QBitArray bits = QBitArray::fromBits("\x79\x03", 11); + + QTest::newRow("Undefined") << QCborValue() << "QCborValue()"; + QTest::newRow("Null") << QCborValue(nullptr) << "QCborValue(nullptr)"; + QTest::newRow("False") << QCborValue(false) << "QCborValue(false)"; + QTest::newRow("True") << QCborValue(true) << "QCborValue(true)"; + QTest::newRow("simpletype") + << QCborValue(QCborSimpleType(0)) << "QCborValue(QCborSimpleType(0))"; + QTest::newRow("Integer:0") << QCborValue(0) << "QCborValue(0)"; + QTest::newRow("Double:0") << QCborValue(0.) << "QCborValue(0.0)"; + QTest::newRow("ByteArray") + << QCborValue(raw("Hello\0World")) << "QCborValue(QByteArray(\"Hello\\x00World\"))"; + QTest::newRow("String") + << QCborValue("Hello\x7fWorld") << "QCborValue(\"Hello\\u007FWorld\")"; + QTest::newRow("DateTime") + << QCborValue(dt) << "QCborValue(QDateTime(2020-04-18 13:41:22.123 UTC Qt::UTC))"; + QTest::newRow("Url") + << QCborValue(QUrl("http://example.com")) << "QCborValue(QUrl(\"http://example.com\"))"; + QTest::newRow("RegularExpression") + << QCborValue(QRegularExpression("^.*$")) + << "QCborValue(QRegularExpression(\"^.*$\", QRegularExpression::PatternOptions(\"NoPatternOption\")))"; + QTest::newRow("Uuid") + << QCborValue(QUuid()) << "QCborValue(QUuid(\"{00000000-0000-0000-0000-000000000000}\"))"; + + QTest::newRow("Tag-1387671238") + << QCborValue(QCborTag(1387671238), QCborValue()) + << "QCborValue(QCborTag(1387671238), QCborValue())"; + QTest::newRow("Tag-55799") + << QCborValue(QCborKnownTags::Signature, QCborValue("Signature")) + << "QCborValue(QCborKnownTags::Signature, QCborValue(\"Signature\"))"; + + // arrays and maps + QTest::newRow("Array:Empty") << QCborValue(QCborArray()) << "QCborValue(QCborArray{})"; + QTest::newRow("Map:Empty") << QCborValue(QCborMap()) << "QCborValue(QCborMap{})"; + QTest::newRow("Array") + << QCborValue(QCborArray{1, 2., nullptr}) + << "QCborValue(QCborArray{QCborValue(1), QCborValue(2.0), QCborValue(nullptr)})"; + QTest::newRow("Map") + << QCborValue(QCborMap{{1, 2.}, {nullptr, "Hello"}, {"World", QCborArray()}}) + << "QCborValue(QCborMap{" + "{QCborValue(1), QCborValue(2.0)}, " + "{QCborValue(nullptr), QCborValue(\"Hello\")}, " + "{QCborValue(\"World\"), QCborValue(QCborArray{})}" + "})"; + + // usually impossible types + QTest::newRow("Unknown-Basic") + << QCborValue(QCborValue::Type(0xfb)) << "QCborValue()"; + QTest::newRow("Unknown-Extended") + << QCborValue(QCborValue::Type(0x10000 + 21)) << "QCborValue()"; + QTest::newRow("Invalid") << QCborValue(QCborValue::Invalid) << "QCborValue()"; +} + +void tst_QCborValue::debugOutput() +{ + QFETCH(QCborValue, v); + QFETCH(QString, expected); + + QTest::ignoreMessage(QtDebugMsg, expected.toUtf8()); + qDebug() << v; +} + +void tst_QCborValue::testlibFormatting_data() +{ + QTest::addColumn("v"); + QTest::addColumn("expected"); + + QDateTime dt = QDateTime::currentDateTimeUtc(); + + QTest::newRow("Undefined") << QCborValue() << "QCborValue()"; + QTest::newRow("Null") << QCborValue(nullptr) << "QCborValue(nullptr)"; + QTest::newRow("False") << QCborValue(false) << "QCborValue(false)"; + QTest::newRow("True") << QCborValue(true) << "QCborValue(true)"; + QTest::newRow("simpletype") + << QCborValue(QCborSimpleType(0)) << "QCborValue(QCborSimpleType(0))"; + QTest::newRow("Integer:0") << QCborValue(0) << "QCborValue(Integer, 0)"; + QTest::newRow("Double:0") << QCborValue(0.) << "QCborValue(Double, 0)"; // must be integer! + QTest::newRow("ByteArray") + << QCborValue(raw("Hello\0World")) << "QCborValue(ByteArray, \"Hello\\x00World\")"; + QTest::newRow("String") + << QCborValue("Hej v\xc3\xa4rlden") << "QCborValue(String, \"Hej v\\u00E4rlden\")"; + QTest::newRow("DateTime") + << QCborValue(dt) << QString("QCborValue(DateTime, \"%1\")").arg(dt.toString(Qt::ISODateWithMs)); + QTest::newRow("Url") + << QCborValue(QUrl("http://example.com")) << "QCborValue(Url, \"http://example.com\")"; + QTest::newRow("RegularExpression") + << QCborValue(QRegularExpression("^.*$")) << "QCborValue(RegularExpression, \"^.*$\")"; + QTest::newRow("Uuid") + << QCborValue(QUuid()) << "QCborValue(Uuid, {00000000-0000-0000-0000-000000000000})"; + + QTest::newRow("Tag") + << QCborValue(QCborKnownTags::Signature, QCborValue()) + << "QCborValue(QCborTag(55799), QCborValue())"; + + // arrays and maps + QTest::newRow("Array:Empty") << QCborValue(QCborArray()) << "QCborValue(Array, [])"; + QTest::newRow("Map:Empty") << QCborValue(QCborMap()) << "QCborValue(Map, {})"; + QTest::newRow("Array") + << QCborValue(QCborArray{1, 2., nullptr}) + << "QCborValue(Array, [QCborValue(Integer, 1), QCborValue(Double, 2), QCborValue(nullptr)])"; + QTest::newRow("Map") + << QCborValue(QCborMap{{1, 2.}, {nullptr, "Hello"}, {"World", QCborArray()}}) + << "QCborValue(Map, {" + "QCborValue(Integer, 1): QCborValue(Double, 2), " + "QCborValue(nullptr): QCborValue(String, \"Hello\"), " + "QCborValue(String, \"World\"): QCborValue(Array, [])" + "})"; + + // usually impossible types + QTest::newRow("Unknown-Basic") + << QCborValue(QCborValue::Type(0xfb)) << "QCborValue()"; + QTest::newRow("Unknown-Extended") + << QCborValue(QCborValue::Type(0x10000 + 21)) << "QCborValue()"; + QTest::newRow("Invalid") << QCborValue(QCborValue::Invalid) << "QCborValue()"; +} + +void tst_QCborValue::testlibFormatting() +{ + QFETCH(QCborValue, v); + QFETCH(QString, expected); + + QScopedArrayPointer hold(QTest::toString(v)); + QString actual = hold.get(); + QCOMPARE(actual, expected); +} + QTEST_MAIN(tst_QCborValue) #include "tst_qcborvalue.moc"