| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2020 Intel Corporation. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtCore module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include <QtCore/qcborvalue.h> | 
| 41 | #include <QtTest> | 
| 42 |  | 
| 43 | #include <QtCore/private/qbytearray_p.h> | 
| 44 |  | 
| 45 | Q_DECLARE_METATYPE(QCborKnownTags) | 
| 46 | Q_DECLARE_METATYPE(QCborValue) | 
| 47 | Q_DECLARE_METATYPE(QCborValue::EncodingOptions) | 
| 48 |  | 
| 49 | class tst_QCborValue : public QObject | 
| 50 | { | 
| 51 |     Q_OBJECT | 
| 52 |  | 
| 53 | private slots: | 
| 54 |     void basics_data(); | 
| 55 |     void basics(); | 
| 56 |     void tagged_data() { basics_data(); } | 
| 57 |     void tagged(); | 
| 58 |     void extendedTypes_data(); | 
| 59 |     void extendedTypes(); | 
| 60 |     void copyCompare_data() { basics_data(); } | 
| 61 |     void copyCompare(); | 
| 62 |  | 
| 63 |     void arrayDefaultInitialization(); | 
| 64 |     void arrayEmptyInitializerList(); | 
| 65 |     void arrayEmptyDetach(); | 
| 66 |     void arrayNonEmptyDetach(); | 
| 67 |     void arrayInitializerList(); | 
| 68 |     void arrayMutation(); | 
| 69 |     void arrayMutateWithCopies(); | 
| 70 |     void arrayPrepend(); | 
| 71 |     void arrayInsertRemove_data() { basics_data(); } | 
| 72 |     void arrayInsertRemove(); | 
| 73 |     void arrayInsertTagged_data() { basics_data(); } | 
| 74 |     void arrayInsertTagged(); | 
| 75 |     void arrayStringElements(); | 
| 76 |     void arraySelfAssign_data() { basics_data(); } | 
| 77 |     void arraySelfAssign(); | 
| 78 |  | 
| 79 |     void mapDefaultInitialization(); | 
| 80 |     void mapEmptyInitializerList(); | 
| 81 |     void mapEmptyDetach(); | 
| 82 |     void mapNonEmptyDetach(); | 
| 83 |     void mapSimpleInitializerList(); | 
| 84 |     void mapMutation(); | 
| 85 |     void mapMutateWithCopies(); | 
| 86 |     void mapStringValues(); | 
| 87 |     void mapStringKeys(); | 
| 88 |     void mapInsertRemove_data() { basics_data(); } | 
| 89 |     void mapInsertRemove(); | 
| 90 |     void mapInsertTagged_data() { basics_data(); } | 
| 91 |     void mapInsertTagged(); | 
| 92 |     void mapSelfAssign_data() { basics_data(); } | 
| 93 |     void mapSelfAssign(); | 
| 94 |     void mapComplexKeys_data() { basics_data(); } | 
| 95 |     void mapComplexKeys(); | 
| 96 |  | 
| 97 |     void sorting(); | 
| 98 |  | 
| 99 |     void toCbor_data(); | 
| 100 |     void toCbor(); | 
| 101 |     void toCborStreamWriter_data() { toCbor_data(); } | 
| 102 |     void toCborStreamWriter(); | 
| 103 |     void fromCbor_data(); | 
| 104 |     void fromCbor(); | 
| 105 |     void fromCborStreamReaderByteArray_data() { fromCbor_data(); } | 
| 106 |     void fromCborStreamReaderByteArray(); | 
| 107 |     void fromCborStreamReaderIODevice_data() { fromCbor_data(); } | 
| 108 |     void fromCborStreamReaderIODevice(); | 
| 109 |     void validation_data(); | 
| 110 |     void validation(); | 
| 111 |     void extendedTypeValidation_data(); | 
| 112 |     void extendedTypeValidation(); | 
| 113 |     void hugeDeviceValidation_data(); | 
| 114 |     void hugeDeviceValidation(); | 
| 115 |     void recursionLimit_data(); | 
| 116 |     void recursionLimit(); | 
| 117 |     void toDiagnosticNotation_data(); | 
| 118 |     void toDiagnosticNotation(); | 
| 119 |  | 
| 120 |     void datastreamSerialization_data(); | 
| 121 |     void datastreamSerialization(); | 
| 122 |     void streamVariantSerialization(); | 
| 123 | }; | 
| 124 |  | 
| 125 | namespace SimpleEncodeToCbor { | 
| 126 | inline size_t lengthOf(int) | 
| 127 | { | 
| 128 |     return 1;       // encode as byte | 
| 129 | } | 
| 130 |  | 
| 131 | template <unsigned N> inline size_t lengthOf(const char (&)[N]) | 
| 132 | { | 
| 133 |     return N - 1; | 
| 134 | } | 
| 135 |  | 
| 136 |  | 
| 137 | inline size_t lengthOf(const char *str) | 
| 138 | { | 
| 139 |     return strlen(s: str); | 
| 140 | } | 
| 141 |  | 
| 142 | template <typename T> inline size_t lengthOf(T) | 
| 143 | { | 
| 144 |     return sizeof(T); | 
| 145 | } | 
| 146 |  | 
| 147 | static void encodeOneAt(char *ptr, int v, size_t) | 
| 148 | { | 
| 149 |     // encode as byte | 
| 150 |     *ptr = char(v); | 
| 151 | } | 
| 152 |  | 
| 153 | static void encodeOneAt(char *ptr, const char *v, size_t size) | 
| 154 | { | 
| 155 |     memcpy(dest: ptr, src: v, n: size); | 
| 156 | } | 
| 157 |  | 
| 158 | template <typename T> | 
| 159 | static typename std::enable_if<std::is_unsigned<T>::value>::type | 
| 160 | encodeOneAt(char *ptr, T v, size_t) | 
| 161 | { | 
| 162 |     qToBigEndian(v, ptr); | 
| 163 | } | 
| 164 |  | 
| 165 | template <typename T> | 
| 166 | static typename std::enable_if<std::is_floating_point<T>::value || | 
| 167 |                                std::is_same<T, qfloat16>::value>::type | 
| 168 | encodeOneAt(char *ptr, T v, size_t) | 
| 169 | { | 
| 170 |     typename QIntegerForSizeof<T>::Unsigned u; | 
| 171 |     memcpy(&u, &v, sizeof(u)); | 
| 172 |     qToBigEndian(u, ptr); | 
| 173 | } | 
| 174 |  | 
| 175 | static char *encodeAt(char *ptr) | 
| 176 | { | 
| 177 |     return ptr; | 
| 178 | } | 
| 179 |  | 
| 180 | template <typename Arg0, typename... Args> | 
| 181 | static char *encodeAt(char *ptr, Arg0 a0, Args... a) | 
| 182 | { | 
| 183 |     encodeOneAt(ptr, a0, lengthOf(a0)); | 
| 184 |     return encodeAt(ptr + lengthOf(a0), a...); | 
| 185 | } | 
| 186 |  | 
| 187 | } // namespace SimpleEncodetoCbor | 
| 188 |  | 
| 189 | template <typename... Args> | 
| 190 | static QByteArray encode(Args... a) | 
| 191 | { | 
| 192 |     // this would be much easier with C++17 fold expressions... | 
| 193 |     using namespace SimpleEncodeToCbor; | 
| 194 |     using namespace std; | 
| 195 |     size_t lengths[] = { lengthOf(a)... }; | 
| 196 |     size_t total = accumulate(begin(lengths), end(lengths), size_t(0), plus<size_t>{}); | 
| 197 |     QByteArray result(QByteArray::size_type(total), Qt::Uninitialized); | 
| 198 |     char *ptr = result.data(); | 
| 199 |     encodeAt(ptr, a...); | 
| 200 |     return result; | 
| 201 | } | 
| 202 |  | 
| 203 | // Get the validation data from TinyCBOR (see src/3rdparty/tinycbor/tests/parser/data.cpp) | 
| 204 | #include "data.cpp" | 
| 205 |  | 
| 206 | struct SimpleTypeWrapper | 
| 207 | { | 
| 208 |     // QCborSimpleType is an enum, so QVariant knows how to convert it to | 
| 209 |     // integer and we don't want it to do that. | 
| 210 |     SimpleTypeWrapper(QCborSimpleType type = {}) : st(type) {} | 
| 211 |     QCborSimpleType st; | 
| 212 | }; | 
| 213 | Q_DECLARE_METATYPE(SimpleTypeWrapper) | 
| 214 |  | 
| 215 | void tst_QCborValue::basics_data() | 
| 216 | { | 
| 217 |     QTest::addColumn<QCborValue::Type>(name: "type" ); | 
| 218 |     QTest::addColumn<QCborValue>(name: "v" ); | 
| 219 |     QTest::addColumn<QVariant>(name: "expectedValue" ); | 
| 220 |     QDateTime dt = QDateTime::currentDateTimeUtc(); | 
| 221 |     QUuid uuid = QUuid::createUuid(); | 
| 222 |  | 
| 223 |     QMetaEnum me = QMetaEnum::fromType<QCborValue::Type>(); | 
| 224 |     auto add = [me](QCborValue::Type t, const QCborValue &v, const QVariant &exp) { | 
| 225 |         auto addRow = [=]() -> QTestData & { | 
| 226 |             const char *typeString = me.valueToKey(value: t); | 
| 227 |             if (t == QCborValue::Integer) | 
| 228 |                 return QTest::addRow(format: "Integer:%lld" , exp.toLongLong()); | 
| 229 |             if (t == QCborValue::Double) | 
| 230 |                 return QTest::addRow(format: "Double:%g" , exp.toDouble()); | 
| 231 |             if (t == QCborValue::ByteArray || t == QCborValue::String) | 
| 232 |                 return QTest::addRow(format: "%s:%d" , typeString, exp.toString().size()); | 
| 233 |             return QTest::newRow(dataTag: typeString); | 
| 234 |         }; | 
| 235 |         addRow() << t << v << exp; | 
| 236 |     }; | 
| 237 |     auto st = [](QCborSimpleType t) { return QVariant::fromValue<SimpleTypeWrapper>(value: t); }; | 
| 238 |  | 
| 239 |     add(QCborValue::Undefined, QCborValue(), st(QCborSimpleType::Undefined)); | 
| 240 |     add(QCborValue::Null, QCborValue::Null, st(QCborSimpleType::Null)); | 
| 241 |     QTest::newRow(dataTag: "nullptr" ) << QCborValue::Null << QCborValue(nullptr) | 
| 242 |                              << st(QCborSimpleType::Null); | 
| 243 |     add(QCborValue::False, false, st(QCborSimpleType::False)); | 
| 244 |     QTest::newRow(dataTag: "false" ) << QCborValue::False << QCborValue(QCborValue::False) | 
| 245 |                            << st(QCborSimpleType::False); | 
| 246 |     add(QCborValue::True, true, st(QCborSimpleType::True)); | 
| 247 |     QTest::newRow(dataTag: "true" ) << QCborValue::True << QCborValue(QCborValue::True) | 
| 248 |                           << st(QCborSimpleType::True); | 
| 249 |     QTest::newRow(dataTag: "simpletype" ) << QCborValue::Type(QCborValue::SimpleType + 255) | 
| 250 |                                 << QCborValue(QCborSimpleType(255)) | 
| 251 |                                 << st(QCborSimpleType(255)); | 
| 252 |     add(QCborValue::Integer, 0, 0); | 
| 253 |     add(QCborValue::Integer, 1, 1); | 
| 254 |     add(QCborValue::Integer, -1, -1); | 
| 255 |     add(QCborValue::Integer, std::numeric_limits<qint64>::min(), std::numeric_limits<qint64>::min()); | 
| 256 |     add(QCborValue::Integer, std::numeric_limits<qint64>::max(), std::numeric_limits<qint64>::max()); | 
| 257 |     add(QCborValue::Double, 0., 0.); | 
| 258 |     add(QCborValue::Double, 1.25, 1.25); | 
| 259 |     add(QCborValue::Double, -1.25, -1.25); | 
| 260 |     add(QCborValue::Double, qInf(), qInf()); | 
| 261 |     add(QCborValue::Double, -qInf(), -qInf()); | 
| 262 |     add(QCborValue::Double, qQNaN(), qQNaN()); | 
| 263 |     add(QCborValue::ByteArray, QByteArray("Hello" ), QByteArray("Hello" )); | 
| 264 |     add(QCborValue::ByteArray, QByteArray(), QByteArray()); | 
| 265 |     add(QCborValue::String, "Hello" , "Hello" ); | 
| 266 |     add(QCborValue::String, QLatin1String(), QString()); | 
| 267 |     add(QCborValue::DateTime, QCborValue(dt), dt); | 
| 268 |     add(QCborValue::Url, QCborValue(QUrl("http://example.com" )), QUrl("http://example.com" )); | 
| 269 |     add(QCborValue::RegularExpression, QCborValue(QRegularExpression("^.*$" )), QRegularExpression("^.*$" )); | 
| 270 |     add(QCborValue::Uuid, QCborValue(uuid), uuid); | 
| 271 |  | 
| 272 |     // empty arrays and maps | 
| 273 |     add(QCborValue::Array, QCborArray(), QVariantList()); | 
| 274 |     add(QCborValue::Map, QCborMap(), QVariantMap()); | 
| 275 | } | 
| 276 |  | 
| 277 | static void basicTypeCheck(QCborValue::Type type, const QCborValue &v, const QVariant &expectedValue) | 
| 278 | { | 
| 279 |     bool isSimpleType = (expectedValue.userType() == qMetaTypeId<SimpleTypeWrapper>()); | 
| 280 |     QCborSimpleType st = expectedValue.value<SimpleTypeWrapper>().st; | 
| 281 |  | 
| 282 |     QCOMPARE(v.type(), type); | 
| 283 |     QCOMPARE(v.isInteger(), type == QCborValue::Integer); | 
| 284 |     QCOMPARE(v.isByteArray(), type == QCborValue::ByteArray); | 
| 285 |     QCOMPARE(v.isString(), type == QCborValue::String); | 
| 286 |     QCOMPARE(v.isArray(), type == QCborValue::Array); | 
| 287 |     QCOMPARE(v.isMap(), type == QCborValue::Map); | 
| 288 |     QCOMPARE(v.isFalse(), type == QCborValue::False); | 
| 289 |     QCOMPARE(v.isTrue(), type == QCborValue::True); | 
| 290 |     QCOMPARE(v.isBool(), type == QCborValue::False || type == QCborValue::True); | 
| 291 |     QCOMPARE(v.isNull(), type == QCborValue::Null); | 
| 292 |     QCOMPARE(v.isUndefined(), type == QCborValue::Undefined); | 
| 293 |     QCOMPARE(v.isDouble(), type == QCborValue::Double); | 
| 294 |     QCOMPARE(v.isDateTime(), type == QCborValue::DateTime); | 
| 295 |     QCOMPARE(v.isUrl(), type == QCborValue::Url); | 
| 296 |     QCOMPARE(v.isUuid(), type == QCborValue::Uuid); | 
| 297 |     QCOMPARE(v.isInvalid(), type == QCborValue::Invalid); | 
| 298 |     QCOMPARE(v.isContainer(), type == QCborValue::Array || type == QCborValue::Map); | 
| 299 |     QCOMPARE(v.isSimpleType(), isSimpleType); | 
| 300 |     QCOMPARE(v.isSimpleType(QCborSimpleType::False), st == QCborSimpleType::False); | 
| 301 |     QCOMPARE(v.isSimpleType(QCborSimpleType::True), st == QCborSimpleType::True); | 
| 302 |     QCOMPARE(v.isSimpleType(QCborSimpleType::Null), st == QCborSimpleType::Null); | 
| 303 |     QCOMPARE(v.isSimpleType(QCborSimpleType::Undefined), st == QCborSimpleType::Undefined); | 
| 304 |     QCOMPARE(v.isSimpleType(QCborSimpleType(255)), st == QCborSimpleType(255)); | 
| 305 |  | 
| 306 |     if (v.isInteger()) { | 
| 307 |         QCOMPARE(v.toInteger(), expectedValue.toLongLong()); | 
| 308 |         QCOMPARE(v.toDouble(), 0. + expectedValue.toLongLong()); | 
| 309 |     } else { | 
| 310 |         QCOMPARE(v.toInteger(), qint64(expectedValue.toDouble())); | 
| 311 |         QCOMPARE(v.toDouble(), expectedValue.toDouble()); | 
| 312 |     } | 
| 313 |     QCOMPARE(v.toBool(true), st != QCborSimpleType::False); | 
| 314 |     QCOMPARE(v.toBool(), st == QCborSimpleType::True); | 
| 315 |     if (st == QCborSimpleType::Undefined) | 
| 316 |         QCOMPARE(v.toSimpleType(QCborSimpleType::Null), QCborSimpleType::Undefined); | 
| 317 |     else if (isSimpleType) | 
| 318 |         QCOMPARE(v.toSimpleType(), st); | 
| 319 |     else | 
| 320 |         QCOMPARE(v.toSimpleType(), QCborSimpleType::Undefined); | 
| 321 |  | 
| 322 | #define CMP(expr, T, validexpr)    \ | 
| 323 |     if (expectedValue.userType() == qMetaTypeId<T>()) \ | 
| 324 |         QCOMPARE(expr, expectedValue.value<T>()); \ | 
| 325 |     else \ | 
| 326 |         QVERIFY(validexpr) | 
| 327 |     CMP(v.toByteArray(), QByteArray, v.toByteArray().isNull()); | 
| 328 |     CMP(v.toString(), QString, v.toString().isNull()); | 
| 329 |     CMP(v.toDateTime(), QDateTime, !v.toDateTime().isValid()); | 
| 330 |     CMP(v.toUrl(), QUrl, !v.toUrl().isValid()); | 
| 331 |     CMP(v.toRegularExpression(), QRegularExpression, v.toRegularExpression().pattern().isNull()); | 
| 332 |     CMP(v.toUuid(), QUuid, v.toUuid().isNull()); | 
| 333 | #undef CMP | 
| 334 |  | 
| 335 |     QVERIFY(v.toArray().isEmpty()); | 
| 336 |     QVERIFY(v.toMap().isEmpty()); | 
| 337 |  | 
| 338 |     QVERIFY(v["Hello" ].isUndefined()); | 
| 339 |     QVERIFY(v[0].isUndefined()); | 
| 340 | } | 
| 341 |  | 
| 342 | void tst_QCborValue::basics() | 
| 343 | { | 
| 344 |     QFETCH(QCborValue::Type, type); | 
| 345 |     QFETCH(QCborValue, v); | 
| 346 |     QFETCH(QVariant, expectedValue); | 
| 347 |  | 
| 348 |     basicTypeCheck(type, v, expectedValue); | 
| 349 | } | 
| 350 |  | 
| 351 | void tst_QCborValue::tagged() | 
| 352 | { | 
| 353 |     QFETCH(QCborValue::Type, type); | 
| 354 |     QFETCH(QCborValue, v); | 
| 355 |     QFETCH(QVariant, expectedValue); | 
| 356 |  | 
| 357 |     // make it tagged | 
| 358 |     QCborValue tagged(QCborKnownTags::Signature, v); | 
| 359 |     QVERIFY(tagged.isTag()); | 
| 360 |     QCOMPARE(tagged.tag(), QCborTag(QCborKnownTags::Signature)); | 
| 361 |  | 
| 362 |     // shouldn't compare equal | 
| 363 |     QVERIFY(tagged != v); | 
| 364 |     QVERIFY(v != tagged); | 
| 365 |  | 
| 366 |     // ensure we can reach the original value | 
| 367 |     basicTypeCheck(type, v: tagged.taggedValue(), expectedValue); | 
| 368 |     QVERIFY(tagged.taggedValue() == v); | 
| 369 |     QVERIFY(v == tagged.taggedValue()); | 
| 370 |  | 
| 371 |     // nested tagging should work too | 
| 372 |     QCborValue tagged2(QCborKnownTags::EncodedCbor, tagged); | 
| 373 |     QVERIFY(tagged2.isTag()); | 
| 374 |     QCOMPARE(tagged2.tag(), QCborTag(QCborKnownTags::EncodedCbor)); | 
| 375 |  | 
| 376 |     QVERIFY(tagged2 != tagged); | 
| 377 |     QVERIFY(tagged != tagged2); | 
| 378 |  | 
| 379 |     QVERIFY(tagged2.taggedValue() == tagged); | 
| 380 |     QVERIFY(tagged == tagged2.taggedValue()); | 
| 381 |     QVERIFY(tagged2.taggedValue().taggedValue() == v); | 
| 382 |     QVERIFY(v == tagged2.taggedValue().taggedValue()); | 
| 383 | } | 
| 384 |  | 
| 385 | void tst_QCborValue::extendedTypes_data() | 
| 386 | { | 
| 387 |     QTest::addColumn<QCborValue>(name: "extended" ); | 
| 388 |     QTest::addColumn<QCborKnownTags>(name: "tag" ); | 
| 389 |     QTest::addColumn<QCborValue>(name: "taggedValue" ); | 
| 390 |     QTest::addColumn<QCborValue>(name: "correctedTaggedValue" ); | 
| 391 |     QCborValue v(QCborValue::Invalid); | 
| 392 |     QDateTime dt = QDateTime::currentDateTimeUtc(); | 
| 393 |     QDateTime dtTzOffset(dt.date(), dt.time(), Qt::OffsetFromUTC, dt.offsetFromUtc()); | 
| 394 |     QUuid uuid = QUuid::createUuid(); | 
| 395 |  | 
| 396 |     // non-correcting extended types (tagged value remains unchanged) | 
| 397 |     QTest::newRow(dataTag: "DateTime" ) << QCborValue(dt) | 
| 398 |                               << QCborKnownTags::DateTimeString << QCborValue(dt.toString(format: Qt::ISODateWithMs)) << v; | 
| 399 |     QTest::newRow(dataTag: "DateTime:TzOffset" ) << QCborValue(dtTzOffset) | 
| 400 |                                        << QCborKnownTags::DateTimeString << QCborValue(dtTzOffset.toString(format: Qt::ISODateWithMs)) << v; | 
| 401 |     QTest::newRow(dataTag: "Url:Empty" ) << QCborValue(QUrl()) | 
| 402 |                                << QCborKnownTags::Url << QCborValue(QString()) << v; | 
| 403 |     QTest::newRow(dataTag: "Url:Authority" ) << QCborValue(QUrl("https://example.com" )) | 
| 404 |                                    << QCborKnownTags::Url << QCborValue("https://example.com" ) << v; | 
| 405 |     QTest::newRow(dataTag: "Url:Path" ) << QCborValue(QUrl("file:///tmp/none" )) | 
| 406 |                               << QCborKnownTags::Url << QCborValue("file:///tmp/none" ) << v; | 
| 407 |     QTest::newRow(dataTag: "Url:QueryFragment" ) << QCborValue(QUrl("whatever:?a=b&c=d#e" )) | 
| 408 |                                        << QCborKnownTags::Url << QCborValue("whatever:?a=b&c=d#e" ) << v; | 
| 409 |     QTest::newRow(dataTag: "Regex:Empty" ) << QCborValue(QRegularExpression()) | 
| 410 |                                  << QCborKnownTags::RegularExpression << QCborValue(QString()) << v; | 
| 411 |     QTest::newRow(dataTag: "Regex" ) << QCborValue(QRegularExpression("^.*$" )) | 
| 412 |                            << QCborKnownTags::RegularExpression << QCborValue(QString("^.*$" )) << v; | 
| 413 |     QTest::newRow(dataTag: "Uuid" ) << QCborValue(uuid) | 
| 414 |                           << QCborKnownTags::Uuid << QCborValue(uuid.toRfc4122()) << v; | 
| 415 |  | 
| 416 |     // correcting extended types | 
| 417 |     QDateTime dtNoMsecs = dt.fromSecsSinceEpoch(secs: dt.toSecsSinceEpoch(), spe: Qt::UTC); | 
| 418 |     QUrl url("https://example.com/\xc2\xa9 " ); | 
| 419 |     QTest::newRow(dataTag: "UnixTime_t:Integer" ) << QCborValue(dtNoMsecs) << QCborKnownTags::UnixTime_t | 
| 420 |                                         << QCborValue(dtNoMsecs.toSecsSinceEpoch()) | 
| 421 |                                         << QCborValue(dtNoMsecs.toString(format: Qt::ISODateWithMs)); | 
| 422 |     QTest::newRow(dataTag: "UnixTime_t:Double" ) << QCborValue(dt) << QCborKnownTags::UnixTime_t | 
| 423 |                                        << QCborValue(dt.toMSecsSinceEpoch() / 1000.) | 
| 424 |                                        << QCborValue(dt.toString(format: Qt::ISODateWithMs)); | 
| 425 |     QTest::newRow(dataTag: "DateTime::JustDate" ) << QCborValue(QDateTime({2018, 1, 1}, {})) | 
| 426 |                                         << QCborKnownTags::DateTimeString | 
| 427 |                                         << QCborValue("2018-01-01" ) << QCborValue("2018-01-01T00:00:00.000" ); | 
| 428 |     QTest::newRow(dataTag: "DateTime::TzOffset" ) << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::UTC)) | 
| 429 |                                         << QCborKnownTags::DateTimeString | 
| 430 |                                         << QCborValue("2018-01-01T09:00:00.000+00:00" ) | 
| 431 |                                         << QCborValue("2018-01-01T09:00:00.000Z" ); | 
| 432 |     QTest::newRow(dataTag: "Url:NotNormalized" ) << QCborValue(url) << QCborKnownTags::Url | 
| 433 |                                        << QCborValue("HTTPS://EXAMPLE.COM/%c2%a9%20" ) | 
| 434 |                                        << QCborValue(url.toString()); | 
| 435 |     QTest::newRow(dataTag: "Uuid:Zero" ) << QCborValue(QUuid()) << QCborKnownTags::Uuid | 
| 436 |                                << QCborValue(QByteArray()) | 
| 437 |                                << QCborValue(QByteArray(sizeof(QUuid), 0)); | 
| 438 |     QTest::newRow(dataTag: "Uuid:TooShort" ) << QCborValue(QUuid(0x12345678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) | 
| 439 |                                    << QCborKnownTags::Uuid | 
| 440 |                                    << QCborValue(raw(data: "\x12\x34\x56\x78" )) | 
| 441 |                                    << QCborValue(raw(data: "\x12\x34\x56\x78"  "\0\0\0\0"  "\0\0\0\0"  "\0\0\0\0" )); | 
| 442 |     QTest::newRow(dataTag: "Uuid:TooLong" ) << QCborValue(uuid) << QCborKnownTags::Uuid | 
| 443 |                                   << QCborValue(uuid.toRfc4122() + "\1\2\3\4" ) << QCborValue(uuid.toRfc4122()); | 
| 444 | } | 
| 445 |  | 
| 446 | void tst_QCborValue::extendedTypes() | 
| 447 | { | 
| 448 |     QFETCH(QCborValue, extended); | 
| 449 |     QFETCH(QCborKnownTags, tag); | 
| 450 |     QFETCH(QCborValue, taggedValue); | 
| 451 |     QFETCH(QCborValue, correctedTaggedValue); | 
| 452 |     if (correctedTaggedValue.isInvalid()) | 
| 453 |         correctedTaggedValue = taggedValue; | 
| 454 |  | 
| 455 |     QCborValue tagged(tag, taggedValue); | 
| 456 |     QVERIFY(extended.isTag()); | 
| 457 |     QVERIFY(tagged.isTag()); | 
| 458 |     QCOMPARE(tagged.taggedValue(), correctedTaggedValue); | 
| 459 |     QVERIFY(extended == tagged); | 
| 460 |     QVERIFY(tagged == extended); | 
| 461 |  | 
| 462 |     QCOMPARE(extended.tag(), tagged.tag()); | 
| 463 |     QCOMPARE(extended.taggedValue(), tagged.taggedValue()); | 
| 464 | } | 
| 465 |  | 
| 466 | void tst_QCborValue::copyCompare() | 
| 467 | { | 
| 468 |     QFETCH(QCborValue, v); | 
| 469 |     QCborValue other = v; | 
| 470 |  | 
| 471 |     // self-moving | 
| 472 |     v = std::move(v); | 
| 473 |     QCOMPARE(v, other); // make sure it's still valid | 
| 474 |  | 
| 475 |     // moving | 
| 476 |     v = std::move(other); | 
| 477 |     other = std::move(v); | 
| 478 |  | 
| 479 |     // normal copying | 
| 480 |     other = v; | 
| 481 |     other = v; | 
| 482 |     v = other; | 
| 483 |  | 
| 484 |  | 
| 485 |     QCOMPARE(v.compare(other), 0); | 
| 486 |     QCOMPARE(v, other); | 
| 487 |     QVERIFY(!(v != other)); | 
| 488 |     QVERIFY(!(v < other)); | 
| 489 | #if 0 && __has_include(<compare>) | 
| 490 |     QVERIFY(v <= other); | 
| 491 |     QVERIFY(v >= other); | 
| 492 |     QVERIFY(!(v > other)); | 
| 493 | #endif | 
| 494 |  | 
| 495 |     if (v.isUndefined()) | 
| 496 |         other = nullptr; | 
| 497 |     else | 
| 498 |         other = {}; | 
| 499 |     QVERIFY(v.type() != other.type()); | 
| 500 |     QVERIFY(!(v == other)); | 
| 501 |     QVERIFY(v != other); | 
| 502 |  | 
| 503 |     // they're different types, so they can't compare equal | 
| 504 |     QVERIFY(v.compare(other) != 0); | 
| 505 |     QVERIFY((v < other) || (other < v)); | 
| 506 | } | 
| 507 |  | 
| 508 | void tst_QCborValue::arrayDefaultInitialization() | 
| 509 | { | 
| 510 |     QCborArray a; | 
| 511 |     QVERIFY(a.isEmpty()); | 
| 512 |     QCOMPARE(a.size(), 0); | 
| 513 |     QVERIFY(!a.contains(0)); | 
| 514 |     QVERIFY(!a.contains(-1)); | 
| 515 |     QVERIFY(!a.contains(false)); | 
| 516 |     QVERIFY(!a.contains(true)); | 
| 517 |     QVERIFY(!a.contains(nullptr)); | 
| 518 |     QVERIFY(!a.contains({})); | 
| 519 |     QVERIFY(!a.contains(1.0)); | 
| 520 |     QVERIFY(!a.contains(QByteArray("Hello" ))); | 
| 521 |     QVERIFY(!a.contains("Hello" )); | 
| 522 |     QVERIFY(!a.contains(QCborArray())); | 
| 523 |     QVERIFY(!a.contains(QCborMap())); | 
| 524 |     QVERIFY(!a.contains(QCborValue(QDateTime::currentDateTimeUtc()))); | 
| 525 |     QVERIFY(!a.contains(QCborValue(QUrl("http://example.com" )))); | 
| 526 |     QVERIFY(!a.contains(QCborValue(QUuid::createUuid()))); | 
| 527 |  | 
| 528 |     QVERIFY(a.at(0).isUndefined()); | 
| 529 |     QCOMPARE(a.constBegin(), a.constEnd()); | 
| 530 |  | 
| 531 |     QVERIFY(a == a); | 
| 532 |     QVERIFY(a == QCborArray()); | 
| 533 |     QVERIFY(QCborArray() == a); | 
| 534 |  | 
| 535 |     QCborValue v(a); | 
| 536 |     QVERIFY(v.isArray()); | 
| 537 |     QVERIFY(!v.isMap()); | 
| 538 |     QVERIFY(!v.isTag()); | 
| 539 |  | 
| 540 |     QCborArray a2 = v.toArray(); | 
| 541 |     QVERIFY(a2.isEmpty()); | 
| 542 |     QCOMPARE(a2, a); | 
| 543 |     auto front = v[0]; | 
| 544 |     QVERIFY(front.isUndefined()); | 
| 545 |     front = 1; | 
| 546 |     QCOMPARE(v[0], 1); | 
| 547 |     QVERIFY(a2.isEmpty()); | 
| 548 |     a2 = v.toArray(); | 
| 549 |     QCOMPARE(a2.size(), 1); | 
| 550 | } | 
| 551 |  | 
| 552 | void tst_QCborValue::mapDefaultInitialization() | 
| 553 | { | 
| 554 |     QCborMap m; | 
| 555 |     QVERIFY(m.isEmpty()); | 
| 556 |     QCOMPARE(m.size(), 0); | 
| 557 |     QVERIFY(m.keys().isEmpty()); | 
| 558 |     QVERIFY(!m.contains(0)); | 
| 559 |     QVERIFY(!m.contains(-1)); | 
| 560 |     QVERIFY(!m.contains(false)); | 
| 561 |     QVERIFY(!m.contains(true)); | 
| 562 |     QVERIFY(!m.contains(QCborValue::Null)); | 
| 563 |     QVERIFY(!m.contains({})); | 
| 564 |     QVERIFY(!m.contains(1.0)); | 
| 565 |     QVERIFY(!m.contains(QLatin1String("Hello" ))); | 
| 566 |     QVERIFY(!m.contains(QStringLiteral("Hello" ))); | 
| 567 |     QVERIFY(!m.contains(QCborValue(QByteArray("Hello" )))); | 
| 568 |     QVERIFY(!m.contains(QCborArray())); | 
| 569 |     QVERIFY(!m.contains(QCborMap())); | 
| 570 |     QVERIFY(!m.contains(QCborValue(QDateTime::currentDateTimeUtc()))); | 
| 571 |     QVERIFY(!m.contains(QCborValue(QUrl("http://example.com" )))); | 
| 572 |     QVERIFY(!m.contains(QCborValue(QUuid::createUuid()))); | 
| 573 |  | 
| 574 |     QVERIFY(m.value(0).isUndefined()); | 
| 575 |     QVERIFY(m.value(QLatin1String("Hello" )).isUndefined()); | 
| 576 |     QVERIFY(m.value(QStringLiteral("Hello" )).isUndefined()); | 
| 577 |     QVERIFY(m.value(QCborValue()).isUndefined()); | 
| 578 | #if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) | 
| 579 |     QVERIFY(m.value("Hello" ).isUndefined()); | 
| 580 | #endif | 
| 581 |  | 
| 582 |     QVERIFY(m == m); | 
| 583 |     QVERIFY(m == QCborMap{}); | 
| 584 |     QVERIFY(QCborMap{} == m); | 
| 585 |  | 
| 586 |     const QCborValue v(m); | 
| 587 |     QVERIFY(v.isMap()); | 
| 588 |     QVERIFY(!v.isArray()); | 
| 589 |     QVERIFY(!v.isTag()); | 
| 590 |     QVERIFY(v[0].isUndefined()); | 
| 591 |     QVERIFY(v[QLatin1String("Hello" )].isUndefined()); | 
| 592 |     QVERIFY(v["Hello" ].isUndefined()); | 
| 593 |  | 
| 594 |     QCborMap m2 = v.toMap(); | 
| 595 |     QVERIFY(m2.isEmpty()); | 
| 596 |     QCOMPARE(m2.size(), 0); | 
| 597 |     QCOMPARE(m2, m); | 
| 598 | } | 
| 599 |  | 
| 600 | void tst_QCborValue::arrayEmptyInitializerList() | 
| 601 | { | 
| 602 |     QCborArray a{}; | 
| 603 |     QVERIFY(a.isEmpty()); | 
| 604 |     QCOMPARE(a.size(), 0); | 
| 605 |     QVERIFY(a == a); | 
| 606 |     QVERIFY(a == QCborArray()); | 
| 607 |     QVERIFY(QCborArray() == a); | 
| 608 | } | 
| 609 |  | 
| 610 | void tst_QCborValue::mapEmptyInitializerList() | 
| 611 | { | 
| 612 |     QCborMap m{}; | 
| 613 |     QVERIFY(m.isEmpty()); | 
| 614 |     QCOMPARE(m.size(), 0); | 
| 615 |     QVERIFY(m == m); | 
| 616 |     QVERIFY(m == QCborMap{}); | 
| 617 |     QVERIFY(QCborMap{} == m); | 
| 618 | } | 
| 619 |  | 
| 620 | void tst_QCborValue::arrayEmptyDetach() | 
| 621 | { | 
| 622 |     QCborArray a; | 
| 623 |     QCOMPARE(a.begin(), a.end()); | 
| 624 |     QVERIFY(a.isEmpty()); | 
| 625 |     QCOMPARE(a.size(), 0); | 
| 626 |  | 
| 627 |     QVERIFY(a == a); | 
| 628 |     QVERIFY(a == QCborArray()); | 
| 629 |     QVERIFY(QCborArray() == a); | 
| 630 |  | 
| 631 |     QCborValue v(a); | 
| 632 |     QVERIFY(v.isArray()); | 
| 633 |     QVERIFY(!v.isMap()); | 
| 634 |     QVERIFY(!v.isTag()); | 
| 635 |  | 
| 636 |     QCborArray a2 = v.toArray(); | 
| 637 |     QVERIFY(a2.isEmpty()); | 
| 638 |     QCOMPARE(a2, a); | 
| 639 | } | 
| 640 |  | 
| 641 | void tst_QCborValue::mapEmptyDetach() | 
| 642 | { | 
| 643 |     QCborMap m; | 
| 644 |     QCOMPARE(m.begin(), m.end()); | 
| 645 |     QVERIFY(m.isEmpty()); | 
| 646 |     QCOMPARE(m.size(), 0); | 
| 647 |  | 
| 648 |     QVERIFY(m == m); | 
| 649 |     QVERIFY(m == QCborMap{}); | 
| 650 |     QVERIFY(QCborMap{} == m); | 
| 651 |  | 
| 652 |     QCborValue v(m); | 
| 653 |     QVERIFY(v.isMap()); | 
| 654 |     QVERIFY(!v.isArray()); | 
| 655 |     QVERIFY(!v.isTag()); | 
| 656 |  | 
| 657 |     QCborMap m2 = v.toMap(); | 
| 658 |     QVERIFY(m2.isEmpty()); | 
| 659 |     QCOMPARE(m2, m); | 
| 660 | } | 
| 661 |  | 
| 662 | void tst_QCborValue::arrayNonEmptyDetach() | 
| 663 | { | 
| 664 |     QCborArray a; | 
| 665 |     a.append(value: 1); | 
| 666 |     a.append(value: 2); | 
| 667 |  | 
| 668 |     QCOMPARE(a.first(), 1); | 
| 669 |     QCOMPARE(a.last(), 2); | 
| 670 |     QVERIFY(!a.contains(3)); | 
| 671 |     QVERIFY(a.constBegin() != a.constEnd()); | 
| 672 |     QVERIFY(a.begin() != a.end()); | 
| 673 |  | 
| 674 |     // now the same, with an active copy | 
| 675 |     { QCborArray copy(a); QCOMPARE(a.first(), 1); } | 
| 676 |     { QCborArray copy(a); QCOMPARE(a.last(), 2); } | 
| 677 |     { QCborArray copy(a); QVERIFY(!a.contains(3)); } | 
| 678 |     { QCborArray copy(a); QVERIFY(a.constBegin() != a.constEnd()); } | 
| 679 |     { QCborArray copy(a); QVERIFY(a.begin() != a.end()); } | 
| 680 | } | 
| 681 |  | 
| 682 | void tst_QCborValue::mapNonEmptyDetach() | 
| 683 | { | 
| 684 |     QCborMap m; | 
| 685 |     m.insert(key: 1, value_: {}); | 
| 686 |     m.insert(key: 2, value_: nullptr); | 
| 687 |     QVERIFY(!m.contains(3)); | 
| 688 |     QVERIFY(m.constBegin() != m.constEnd()); | 
| 689 |     QVERIFY(m.begin() != m.end()); | 
| 690 |     // test all 4 overloads of find() | 
| 691 |     QVERIFY(m.constFind(3) == m.constEnd()); | 
| 692 |     QVERIFY(m.constFind(QLatin1String("3" )) == m.constEnd()); | 
| 693 |     QVERIFY(m.constFind(QString("3" )) == m.constEnd()); | 
| 694 |     QVERIFY(m.constFind(QCborValue(3)) == m.constEnd()); | 
| 695 |     QVERIFY(m.find(3) == m.end()); | 
| 696 |     QVERIFY(m.find(QLatin1String("3" )) == m.end()); | 
| 697 |     QVERIFY(m.find(QString("3" )) == m.end()); | 
| 698 |     QVERIFY(m.find(QCborValue(3)) == m.end()); | 
| 699 |     { auto it = m.find(key: 3); QVERIFY(it == m.end()); } | 
| 700 |     { auto it = m.find(key: QLatin1String("3" )); QVERIFY(it == m.end()); } | 
| 701 |     { auto it = m.find(key: QString("3" )); QVERIFY(it == m.end()); } | 
| 702 |     { auto it = m.find(key: QCborValue(3)); QVERIFY(it == m.end()); } | 
| 703 |  | 
| 704 |     // now the same, with an active copy | 
| 705 |     { QCborMap copy(m); QVERIFY(!m.contains(3)); } | 
| 706 |     { QCborMap copy(m); QVERIFY(m.constBegin() != m.constEnd()); } | 
| 707 |     { QCborMap copy(m); QVERIFY(m.begin() != m.end()); } | 
| 708 |     { QCborMap copy(m); QVERIFY(m.constFind(3) == m.constEnd()); } | 
| 709 |     { QCborMap copy(m); QVERIFY(m.constFind(QLatin1String("3" )) == m.constEnd()); } | 
| 710 |     { QCborMap copy(m); QVERIFY(m.constFind(QString("3" )) == m.constEnd()); } | 
| 711 |     { QCborMap copy(m); QVERIFY(m.constFind(QCborValue(3)) == m.constEnd()); } | 
| 712 |     { QCborMap copy(m); QVERIFY(m.find(3) == m.end()); } | 
| 713 |     { QCborMap copy(m); QVERIFY(m.find(QLatin1String("3" )) == m.end()); } | 
| 714 |     { QCborMap copy(m); QVERIFY(m.find(QString("3" )) == m.end()); } | 
| 715 |     { QCborMap copy(m); QVERIFY(m.find(QCborValue(3)) == m.end()); }\ | 
| 716 |     { QCborMap copy(m); auto it = m.find(key: 3); QVERIFY(it == m.end()); } | 
| 717 |     { QCborMap copy(m); auto it = m.find(key: QLatin1String("3" )); QVERIFY(it == m.end()); } | 
| 718 |     { QCborMap copy(m); auto it = m.find(key: QString("3" )); QVERIFY(it == m.end()); } | 
| 719 |     { QCborMap copy(m); auto it = m.find(key: QCborValue(3)); QVERIFY(it == m.end()); } | 
| 720 | } | 
| 721 |  | 
| 722 | void tst_QCborValue::arrayInitializerList() | 
| 723 | { | 
| 724 |     QCborArray a{0, -1, false, true, nullptr, {}, 1.0}; | 
| 725 |     QVERIFY(!a.isEmpty()); | 
| 726 |     QCOMPARE(a.size(), 7); | 
| 727 |     QCOMPARE(a.at(0), QCborValue(0)); | 
| 728 |     QCOMPARE(a.at(1), QCborValue(-1)); | 
| 729 |     QCOMPARE(a.at(2), QCborValue(QCborValue::False)); | 
| 730 |     QCOMPARE(a.at(3), QCborValue(QCborValue::True)); | 
| 731 |     QCOMPARE(a.at(4), QCborValue(QCborValue::Null)); | 
| 732 |     QCOMPARE(a.at(5), QCborValue(QCborValue::Undefined)); | 
| 733 |     QCOMPARE(a.at(6), QCborValue(1.0)); | 
| 734 |  | 
| 735 |     QVERIFY(a == a); | 
| 736 |     QVERIFY(a != QCborArray{}); | 
| 737 |     QVERIFY(QCborArray{} != a); | 
| 738 |     QVERIFY(a == QCborArray({0, -1, false, true, nullptr, {}, 1.0})); | 
| 739 |  | 
| 740 |     QCborValue v = a; | 
| 741 |     QCOMPARE(v[0], QCborValue(0)); | 
| 742 |     QCOMPARE(v[1], QCborValue(-1)); | 
| 743 |     QCOMPARE(v[2], QCborValue(QCborValue::False)); | 
| 744 |     QCOMPARE(v[3], QCborValue(QCborValue::True)); | 
| 745 |     QCOMPARE(v[4], QCborValue(QCborValue::Null)); | 
| 746 |     QCOMPARE(v[5], QCborValue(QCborValue::Undefined)); | 
| 747 |     QCOMPARE(v[6], QCborValue(1.0)); | 
| 748 |  | 
| 749 |     QVERIFY(a.contains(0)); | 
| 750 |     QVERIFY(a.contains(-1)); | 
| 751 |     QVERIFY(a.contains(false)); | 
| 752 |     QVERIFY(a.contains(true)); | 
| 753 |     QVERIFY(a.contains(nullptr)); | 
| 754 |     QVERIFY(a.contains({})); | 
| 755 |     QVERIFY(a.contains(1.0)); | 
| 756 |     QVERIFY(!a.contains(QByteArray("Hello" ))); | 
| 757 |     QVERIFY(!a.contains("Hello" )); | 
| 758 |     QVERIFY(!a.contains(QCborArray())); | 
| 759 |     QVERIFY(!a.contains(QCborMap())); | 
| 760 |     QVERIFY(!a.contains(QCborValue(QDateTime::currentDateTimeUtc()))); | 
| 761 |     QVERIFY(!a.contains(QCborValue(QUrl("http://example.com" )))); | 
| 762 |     QVERIFY(!a.contains(QCborValue(QUuid::createUuid()))); | 
| 763 |  | 
| 764 |     // iterators | 
| 765 |     auto it = a.constBegin(); | 
| 766 |     auto end = a.constEnd(); | 
| 767 |     QCOMPARE(end - it, 7); | 
| 768 |     QCOMPARE(it + 7, end); | 
| 769 |     QVERIFY(it->isInteger()); | 
| 770 |     QCOMPARE(*it, QCborValue(0)); | 
| 771 |     QCOMPARE(it[1], QCborValue(-1)); | 
| 772 |     QCOMPARE(*(it + 2), QCborValue(false)); | 
| 773 |     it += 3; | 
| 774 |     QCOMPARE(*it, QCborValue(true)); | 
| 775 |     ++it; | 
| 776 |     QCOMPARE(*it, QCborValue(nullptr)); | 
| 777 |     it++; | 
| 778 |     QCOMPARE(*it, QCborValue()); | 
| 779 |     --end; | 
| 780 |     QCOMPARE(*end, QCborValue(1.0)); | 
| 781 |     end--; | 
| 782 |     QCOMPARE(it, end); | 
| 783 |  | 
| 784 |     // range for | 
| 785 |     int i = 0; | 
| 786 |     for (const QCborValue &v : qAsConst(t&: a)) { | 
| 787 |         QVERIFY(!v.isInvalid()); | 
| 788 |         QCOMPARE(v.isUndefined(), i == 5); // 6th element is Undefined | 
| 789 |         ++i; | 
| 790 |     } | 
| 791 |     QCOMPARE(i, a.size()); | 
| 792 | } | 
| 793 |  | 
| 794 | void tst_QCborValue::mapSimpleInitializerList() | 
| 795 | { | 
| 796 |     QCborMap m{{0, 0}, {1, 0}, {2, "Hello" }, {"Hello" , 2}, {3, QLatin1String("World" )}, {QLatin1String("World" ), 3}}; | 
| 797 |     QCOMPARE(m.size(), 6); | 
| 798 |     QVERIFY(m == m); | 
| 799 |     QVERIFY(m != QCborMap{}); | 
| 800 |     QVERIFY(QCborMap{} != m); | 
| 801 |     QVERIFY(m == QCborMap({{0, 0}, {1, 0}, {2, "Hello" }, {"Hello" , 2}, {3, QLatin1String("World" )}, {QLatin1String("World" ), 3}})); | 
| 802 |  | 
| 803 |     QCborValue vmap = m; | 
| 804 |     { | 
| 805 |         QVERIFY(m.contains(0)); | 
| 806 |         QCborValue v = m.value(key: 0); | 
| 807 |         QVERIFY(v.isInteger()); | 
| 808 |         QCOMPARE(v.toInteger(), 0); | 
| 809 |         QCOMPARE(vmap[0], v); | 
| 810 |     } | 
| 811 |     { | 
| 812 |         QVERIFY(m.contains(1)); | 
| 813 |         QCborValue v = m.value(key: 1); | 
| 814 |         QVERIFY(v.isInteger()); | 
| 815 |         QCOMPARE(v.toInteger(), 0); | 
| 816 |         QCOMPARE(vmap[1], v); | 
| 817 |     } | 
| 818 |     { | 
| 819 |         QVERIFY(m.contains(2)); | 
| 820 |         QCborValue v = m.value(key: 2); | 
| 821 |         QVERIFY(v.isString()); | 
| 822 |         QCOMPARE(v.toString(), "Hello" ); | 
| 823 |         QCOMPARE(vmap[2], v); | 
| 824 |     } | 
| 825 |     { | 
| 826 |         QVERIFY(m.contains(3)); | 
| 827 |         QCborValue v = m.value(key: 3); | 
| 828 |         QVERIFY(v.isString()); | 
| 829 |         QCOMPARE(v.toString(), "World" ); | 
| 830 |         QCOMPARE(vmap[3], v); | 
| 831 |     } | 
| 832 |     { | 
| 833 |         QVERIFY(m.contains(QStringLiteral("Hello" ))); | 
| 834 |         QCborValue v = m.value(key: QLatin1String("Hello" )); | 
| 835 |         QVERIFY(v.isInteger()); | 
| 836 |         QCOMPARE(v.toInteger(), 2); | 
| 837 |         QCOMPARE(vmap[QStringLiteral("Hello" )], v); | 
| 838 |     } | 
| 839 |     { | 
| 840 |         QVERIFY(m.contains(QLatin1String("World" ))); | 
| 841 |         QCborValue v = m.value(QStringLiteral("World" )); | 
| 842 |         QVERIFY(v.isInteger()); | 
| 843 |         QCOMPARE(v.toInteger(), 3); | 
| 844 |         QCOMPARE(vmap[QLatin1String("World" )], v); | 
| 845 |     } | 
| 846 |  | 
| 847 |     QVERIFY(!m.contains(QCborValue::Null)); | 
| 848 |     QVERIFY(!m.contains(QCborValue())); | 
| 849 |     QVERIFY(!m.contains(QCborValue(1.0)));  // Important: 1.0 does not match 1 | 
| 850 |     QVERIFY(!m.contains(QCborValue(QByteArray("Hello" )))); | 
| 851 |     QVERIFY(!m.contains(QCborArray())); | 
| 852 |     QVERIFY(!m.contains(QCborMap())); | 
| 853 |     QVERIFY(!m.contains(QCborValue(QDateTime::currentDateTimeUtc()))); | 
| 854 |     QVERIFY(!m.contains(QCborValue(QUrl("http://example.com" )))); | 
| 855 |     QVERIFY(!m.contains(QCborValue(QUuid::createUuid()))); | 
| 856 |  | 
| 857 |     // iterators (QCborMap is not sorted) | 
| 858 |     auto it = m.constBegin(); | 
| 859 |     auto end = m.constEnd(); | 
| 860 |     QCOMPARE(end - it, 6); | 
| 861 |     QCOMPARE(it + 6, end); | 
| 862 |     QCOMPARE(it.key(), QCborValue(0)); | 
| 863 |     QCOMPARE(it.value(), QCborValue(0)); | 
| 864 |     QVERIFY(it->isInteger()); | 
| 865 |     ++it; | 
| 866 |     QCOMPARE(it.key(), QCborValue(1)); | 
| 867 |     QCOMPARE(it.value(), QCborValue(0)); | 
| 868 |     QCOMPARE((it + 1).key(), QCborValue(2)); | 
| 869 |     QVERIFY((it + 1)->isString()); | 
| 870 |     QCOMPARE((it + 1)->toString(), "Hello" ); | 
| 871 |     it += 2; | 
| 872 |     QCOMPARE(it.key(), QCborValue("Hello" )); | 
| 873 |     QVERIFY(it->isInteger()); | 
| 874 |     it++; | 
| 875 |     QCOMPARE(it.key(), QCborValue(3)); | 
| 876 |     QVERIFY(it->isString()); | 
| 877 |     QCOMPARE(it.value().toString(), "World" ); | 
| 878 |     --end; | 
| 879 |     QCOMPARE(end.key(), QCborValue("World" )); | 
| 880 |     QCOMPARE(end.value(), QCborValue(3)); | 
| 881 |     end--; | 
| 882 |     QCOMPARE(it, end); | 
| 883 |  | 
| 884 |     // range for | 
| 885 |     int i = 0; | 
| 886 |     for (auto pair : qAsConst(t&: m)) { | 
| 887 |         QVERIFY(!pair.first.isUndefined()); | 
| 888 |         QVERIFY(!pair.second.isUndefined()); | 
| 889 |         ++i; | 
| 890 |     } | 
| 891 |     QCOMPARE(i, m.size()); | 
| 892 | } | 
| 893 |  | 
| 894 | void tst_QCborValue::arrayMutation() | 
| 895 | { | 
| 896 |     QCborArray a{42}; | 
| 897 |     { | 
| 898 |         QCborValueRef v = a[0]; | 
| 899 |         QVERIFY(!a.isEmpty()); | 
| 900 |         QVERIFY(v.isInteger()); | 
| 901 |         QCOMPARE(v.toInteger(), 42); | 
| 902 |  | 
| 903 |         // now mutate the list | 
| 904 |         v = true; | 
| 905 |         QVERIFY(v.isBool()); | 
| 906 |         QVERIFY(v.isTrue()); | 
| 907 |         QVERIFY(a.at(0).isTrue()); | 
| 908 |         QVERIFY(a.at(0) == v); | 
| 909 |         QVERIFY(v == a.at(0)); | 
| 910 |     } | 
| 911 |  | 
| 912 |     QVERIFY(a == a); | 
| 913 |     QVERIFY(a == QCborArray{true}); | 
| 914 |  | 
| 915 |     QCborArray a2 = a; | 
| 916 |     a.append(value: nullptr); | 
| 917 |     QCOMPARE(a.size(), 2); | 
| 918 |     QCOMPARE(a2.size(), 1); | 
| 919 |  | 
| 920 |     // self-insertion | 
| 921 |     a2.append(value: a2); | 
| 922 |     QCOMPARE(a2.size(), 2); | 
| 923 |     QCOMPARE(a2.last().toArray().size(), 1); | 
| 924 |  | 
| 925 |     QCborValueRef v = a[0]; | 
| 926 |     QVERIFY(v.isTrue()); | 
| 927 |     v = 2.5; | 
| 928 |     QVERIFY(v.isDouble()); | 
| 929 |     QVERIFY(a.first().isDouble()); | 
| 930 |     QVERIFY(a.last().isNull()); | 
| 931 |     QVERIFY(a2.first().isTrue()); | 
| 932 |  | 
| 933 |     a2 = a; | 
| 934 |     auto it = a.begin();    // detaches again | 
| 935 |     auto end = a.end(); | 
| 936 |     QCOMPARE(end - it, 2); | 
| 937 |     QCOMPARE(it + 2, end); | 
| 938 |     QCOMPARE(*it, QCborValue(2.5)); | 
| 939 |     QCOMPARE(*++it, QCborValue(nullptr)); | 
| 940 |     QVERIFY(a2 == a); | 
| 941 |     QVERIFY(a == a2); | 
| 942 |  | 
| 943 |     *it = -1; | 
| 944 |     QCOMPARE(*it, QCborValue(-1)); | 
| 945 |     QCOMPARE(a.at(1), QCborValue(-1)); | 
| 946 |     QCOMPARE(a2.at(1), QCborValue(nullptr)); | 
| 947 |     QCOMPARE(++it, end); | 
| 948 |  | 
| 949 |     // Array accessed via value: | 
| 950 |     QCborValue val(a); | 
| 951 |     val[2] = QCborArray{2, 3, 5, 7}; | 
| 952 |     QCOMPARE(a.size(), 2); // Unchanged | 
| 953 |     QVERIFY(val.isArray()); | 
| 954 |     QCOMPARE(val.toArray().size(), 3); | 
| 955 |     val[2][4] = 17; | 
| 956 |     QVERIFY(val.isArray()); | 
| 957 |     QVERIFY(val[2].isArray()); | 
| 958 |     QCOMPARE(val[2].toArray().size(), 5); | 
| 959 |     QCOMPARE(val[2][4], 17); | 
| 960 |     QCOMPARE(val.toArray().size(), 3); | 
| 961 |     val[3] = 42; | 
| 962 |     QVERIFY(val.isArray()); | 
| 963 |     QCOMPARE(val.toArray().size(), 4); | 
| 964 |     QCOMPARE(val[3], 42); | 
| 965 |  | 
| 966 |     // Coerce to map on string key: | 
| 967 |     const QLatin1String any("any" ); | 
| 968 |     val[any] = any; | 
| 969 |     QVERIFY(val.isMap()); | 
| 970 |     QCOMPARE(val.toMap().size(), 5); | 
| 971 |     QVERIFY(val[2].isArray()); | 
| 972 |     QCOMPARE(val[2].toArray().size(), 5); | 
| 973 | } | 
| 974 |  | 
| 975 | void tst_QCborValue::arrayMutateWithCopies() | 
| 976 | { | 
| 977 |     { | 
| 978 |         QCborArray array; | 
| 979 |         array.append(value: "TEST" ); | 
| 980 |         QCOMPARE(array.size(), 1); | 
| 981 |         QCOMPARE(array.at(0), "TEST" ); | 
| 982 |  | 
| 983 |         array.append(value: array.at(i: 0)); | 
| 984 |         QCOMPARE(array.size(), 2); | 
| 985 |         QCOMPARE(array.at(0), "TEST" ); | 
| 986 |         QCOMPARE(array.at(1), "TEST" ); | 
| 987 |     } | 
| 988 |     { | 
| 989 |         QCborArray array; | 
| 990 |         array.append(value: "TEST" ); | 
| 991 |         QCOMPARE(array.size(), 1); | 
| 992 |         QCOMPARE(array.at(0), "TEST" ); | 
| 993 |  | 
| 994 |         // same as previous, but with prepend() not append() | 
| 995 |         array.prepend(value: array.at(i: 0)); | 
| 996 |         QCOMPARE(array.size(), 2); | 
| 997 |         QCOMPARE(array.at(0), "TEST" ); | 
| 998 |         QCOMPARE(array.at(1), "TEST" ); | 
| 999 |     } | 
| 1000 |     { | 
| 1001 |         QCborArray array; | 
| 1002 |         array.append(value: "TEST" ); | 
| 1003 |         QCOMPARE(array.size(), 1); | 
| 1004 |         QCOMPARE(array.at(0), "TEST" ); | 
| 1005 |  | 
| 1006 |         // same as previous, but using a QCborValueRef | 
| 1007 |         QCborValueRef rv = array[0]; | 
| 1008 |         array.prepend(value: rv); | 
| 1009 |         QCOMPARE(array.size(), 2); | 
| 1010 |         QCOMPARE(array.at(0), "TEST" ); | 
| 1011 |         QCOMPARE(array.at(1), "TEST" ); | 
| 1012 |     } | 
| 1013 |     { | 
| 1014 |         QCborArray array; | 
| 1015 |         array.append(value: "TEST" ); | 
| 1016 |         QCOMPARE(array.size(), 1); | 
| 1017 |         QCOMPARE(array.at(0), "TEST" ); | 
| 1018 |  | 
| 1019 |         // same as previous, but now extending the array | 
| 1020 |         QCborValueRef rv = array[0]; | 
| 1021 |         array[2] = rv; | 
| 1022 |         QCOMPARE(array.size(), 3); | 
| 1023 |         QCOMPARE(array.at(0), "TEST" ); | 
| 1024 |         QCOMPARE(array.at(2), "TEST" ); | 
| 1025 |     } | 
| 1026 | } | 
| 1027 |  | 
| 1028 | void tst_QCborValue::mapMutation() | 
| 1029 | { | 
| 1030 |     QCborMap m; | 
| 1031 |     QVERIFY(m.isEmpty()); | 
| 1032 |  | 
| 1033 |     { | 
| 1034 |         QCborValueRef v = m[42]; | 
| 1035 |         QCOMPARE(m.size(), 1); | 
| 1036 |         QVERIFY(v.isUndefined()); | 
| 1037 |  | 
| 1038 |         // now mutate the list | 
| 1039 |         // simple -> HasByteData | 
| 1040 |         const QString strValue = QStringLiteral("value" ); | 
| 1041 |         v = strValue; | 
| 1042 |         QVERIFY(v.isString()); | 
| 1043 |         QCOMPARE(v, QCborValue(strValue)); | 
| 1044 |         QCOMPARE(m, QCborMap({{42, strValue}})); | 
| 1045 |  | 
| 1046 |         // HasByteData -> HasByteData | 
| 1047 |         const QLatin1String otherStrValue("othervalue" ); | 
| 1048 |         v = otherStrValue; | 
| 1049 |         QVERIFY(v.isString()); | 
| 1050 |         QCOMPARE(v, QCborValue(otherStrValue)); | 
| 1051 |         QCOMPARE(m, QCborMap({{42, otherStrValue}})); | 
| 1052 |  | 
| 1053 |         // HasByteData -> simple | 
| 1054 |         v = 42; | 
| 1055 |         QVERIFY(v.isInteger()); | 
| 1056 |         QCOMPARE(v, QCborValue(42)); | 
| 1057 |         QCOMPARE(m, QCborMap({{42, 42}})); | 
| 1058 |  | 
| 1059 |         // simple -> container | 
| 1060 |         v = QCborArray{1, 2, 3}; | 
| 1061 |         QVERIFY(v.isArray()); | 
| 1062 |         QCOMPARE(v, QCborArray({1, 2, 3})); | 
| 1063 |         QCOMPARE(m,  QCborMap({{42, QCborArray{1, 2, 3}}})); | 
| 1064 |  | 
| 1065 |         // container -> simple | 
| 1066 |         v = true; | 
| 1067 |         QVERIFY(v.isBool()); | 
| 1068 |         QVERIFY(v.isTrue()); | 
| 1069 |         QCOMPARE(m, QCborMap({{42, true}})); | 
| 1070 |         QVERIFY(m.begin()->isTrue()); | 
| 1071 |         QVERIFY(m.begin().value() == v); | 
| 1072 |         QVERIFY(v == m.begin().value()); | 
| 1073 |     } | 
| 1074 |  | 
| 1075 |     QVERIFY(m == QCborMap({{42, true}})); | 
| 1076 |     QVERIFY(QCborMap({{42, true}}) == m); | 
| 1077 |  | 
| 1078 |     QCborMap m2 = m; | 
| 1079 |     m.insert(v: {nullptr, nullptr}); | 
| 1080 |     QCOMPARE(m.size(), 2); | 
| 1081 |     QCOMPARE(m2.size(), 1); | 
| 1082 |  | 
| 1083 |     QCborValueRef v = m[42]; | 
| 1084 |     QVERIFY(v.isTrue()); | 
| 1085 |     v = 2.5; | 
| 1086 |     QVERIFY(v.isDouble()); | 
| 1087 |     QVERIFY(m.begin()->isDouble()); | 
| 1088 |     QVERIFY((m.end() - 1)->isNull()); | 
| 1089 |     QVERIFY(m2.begin()->isTrue()); | 
| 1090 |  | 
| 1091 |     m2 = m; | 
| 1092 |     auto it = m.begin();    // detaches again | 
| 1093 |     auto end = m.end(); | 
| 1094 |     QCOMPARE(end - it, 2); | 
| 1095 |     QCOMPARE(it + 2, end); | 
| 1096 |     QCOMPARE(it.key(), QCborValue(42)); | 
| 1097 |     QCOMPARE(it.value(), QCborValue(2.5)); | 
| 1098 |     QCOMPARE((++it).value(), QCborValue(nullptr)); | 
| 1099 |     QCOMPARE(it.key(), QCborValue(nullptr)); | 
| 1100 |     QVERIFY(m2 == m); | 
| 1101 |     QVERIFY(m == m2); | 
| 1102 |  | 
| 1103 |     it.value() = -1; | 
| 1104 |     QCOMPARE(it.key(), QCborValue(nullptr)); | 
| 1105 |     QCOMPARE(it.value(), QCborValue(-1)); | 
| 1106 |     QCOMPARE((m.end() - 1)->toInteger(), -1); | 
| 1107 |     QVERIFY((m2.end() - 1)->isNull()); | 
| 1108 |     QCOMPARE(++it, end); | 
| 1109 |  | 
| 1110 |     // Map accessed via value: | 
| 1111 |     QCborValue val(m); | 
| 1112 |     val[7] = QCborMap({{0, 2}, {1, 3}, {2, 5}}); | 
| 1113 |     QCOMPARE(m.size(), 2); // Unchanged | 
| 1114 |     QVERIFY(val.isMap()); | 
| 1115 |     QCOMPARE(val.toMap().size(), 3); | 
| 1116 |     val[7][3] = 11; | 
| 1117 |     QVERIFY(val.isMap()); | 
| 1118 |     QVERIFY(val[7].isMap()); | 
| 1119 |     QCOMPARE(val[7].toMap().size(), 4); | 
| 1120 |     val[14] = 42; | 
| 1121 |     QVERIFY(val.isMap()); | 
| 1122 |     QCOMPARE(val.toMap().size(), 4); | 
| 1123 |  | 
| 1124 |     const QLatin1String any("any" ); | 
| 1125 |     const QString hello(QStringLiteral("Hello World" )); | 
| 1126 |     val[any][3][hello] = any; | 
| 1127 |     QVERIFY(val.isMap()); | 
| 1128 |     QCOMPARE(val.toMap().size(), 5); | 
| 1129 |     QVERIFY(val[any].isMap()); | 
| 1130 |     QCOMPARE(val[any].toMap().size(), 1); | 
| 1131 |     QVERIFY(val[any][3].isMap()); | 
| 1132 |     QCOMPARE(val[any][3].toMap().size(), 1); | 
| 1133 | } | 
| 1134 |  | 
| 1135 | void tst_QCborValue::mapMutateWithCopies() | 
| 1136 | { | 
| 1137 |     { | 
| 1138 |         QCborMap map; | 
| 1139 |         map[QLatin1String("prop1" )] = "TEST" ; | 
| 1140 |         QCOMPARE(map.size(), 1); | 
| 1141 |         QCOMPARE(map.value("prop1" ), "TEST" ); | 
| 1142 |  | 
| 1143 |         map[QLatin1String("prop2" )] = map.value(key: "prop1" ); | 
| 1144 |         QCOMPARE(map.size(), 2); | 
| 1145 |         QCOMPARE(map.value("prop1" ), "TEST" ); | 
| 1146 |         QCOMPARE(map.value("prop2" ), "TEST" ); | 
| 1147 |     } | 
| 1148 |     { | 
| 1149 |         // see QTBUG-83366 | 
| 1150 |         QCborMap map; | 
| 1151 |         map[QLatin1String("value" )] = "TEST" ; | 
| 1152 |         QCOMPARE(map.size(), 1); | 
| 1153 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1154 |  | 
| 1155 |         QCborValue v = map.value(key: "value" ); | 
| 1156 |         map[QLatin1String("prop2" )] = v; | 
| 1157 |         QCOMPARE(map.size(), 2); | 
| 1158 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1159 |         QCOMPARE(map.value("prop2" ), "TEST" ); | 
| 1160 |     } | 
| 1161 |     { | 
| 1162 |         QCborMap map; | 
| 1163 |         map[QLatin1String("value" )] = "TEST" ; | 
| 1164 |         QCOMPARE(map.size(), 1); | 
| 1165 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1166 |  | 
| 1167 |         // same as previous, but this is a QJsonValueRef | 
| 1168 |         QCborValueRef rv = map[QLatin1String("prop2" )]; | 
| 1169 |         rv = map[QLatin1String("value" )]; | 
| 1170 |         QCOMPARE(map.size(), 2); | 
| 1171 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1172 |         QCOMPARE(map.value("prop2" ), "TEST" ); | 
| 1173 |     } | 
| 1174 |     { | 
| 1175 |         QCborMap map; | 
| 1176 |         map[QLatin1String("value" )] = "TEST" ; | 
| 1177 |         QCOMPARE(map.size(), 1); | 
| 1178 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1179 |  | 
| 1180 |         // same as previous, but now we call the operator[] that reallocates | 
| 1181 |         // after we create the source QCborValueRef | 
| 1182 |         QCborValueRef rv = map[QLatin1String("value" )]; | 
| 1183 |         map[QLatin1String("prop2" )] = rv; | 
| 1184 |         QCOMPARE(map.size(), 2); | 
| 1185 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1186 |         QCOMPARE(map.value("prop2" ), "TEST" ); | 
| 1187 |     } | 
| 1188 |     { | 
| 1189 |         QCborMap map; | 
| 1190 |         map[QLatin1String("value" )] = "TEST" ; | 
| 1191 |         QCOMPARE(map.size(), 1); | 
| 1192 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1193 |  | 
| 1194 |         QCborValueRef v = map[QLatin1String("value" )]; | 
| 1195 |         QCborMap map2 = map; | 
| 1196 |         map.insert(key: QLatin1String("prop2" ), value_: v); | 
| 1197 |         QCOMPARE(map.size(), 2); | 
| 1198 |         QCOMPARE(map.value("value" ), "TEST" ); | 
| 1199 |         QCOMPARE(map.value("prop2" ), "TEST" ); | 
| 1200 |         QCOMPARE(map2.size(), 1); | 
| 1201 |         QCOMPARE(map2.value("value" ), "TEST" ); | 
| 1202 |     } | 
| 1203 | } | 
| 1204 |  | 
| 1205 | void tst_QCborValue::arrayPrepend() | 
| 1206 | { | 
| 1207 |     QCborArray a; | 
| 1208 |     a.prepend(value: 0); | 
| 1209 |     a.prepend(value: nullptr); | 
| 1210 |     QCOMPARE(a.at(1), QCborValue(0)); | 
| 1211 |     QCOMPARE(a.at(0), QCborValue(nullptr)); | 
| 1212 |     QCOMPARE(a.size(), 2); | 
| 1213 | } | 
| 1214 |  | 
| 1215 | void tst_QCborValue::arrayInsertRemove() | 
| 1216 | { | 
| 1217 |     QFETCH(QCborValue, v); | 
| 1218 |     QCborArray a; | 
| 1219 |     a.append(value: 42); | 
| 1220 |     a.append(value: v); | 
| 1221 |     a.insert(i: 1, value: QCborValue(nullptr)); | 
| 1222 |     QCOMPARE(a.at(0), QCborValue(42)); | 
| 1223 |     QCOMPARE(a.at(1), QCborValue(nullptr)); | 
| 1224 |     QCOMPARE(a.at(2), v); | 
| 1225 |  | 
| 1226 |     // remove 42 | 
| 1227 |     a.removeAt(i: 0); | 
| 1228 |     QCOMPARE(a.size(), 2); | 
| 1229 |     QCOMPARE(a.at(0), QCborValue(nullptr)); | 
| 1230 |     QCOMPARE(a.at(1), v); | 
| 1231 |  | 
| 1232 |     auto it = a.begin(); | 
| 1233 |     it = a.erase(it);   // removes nullptr | 
| 1234 |     QCOMPARE(a.size(), 1); | 
| 1235 |     QCOMPARE(a.at(0), v); | 
| 1236 |  | 
| 1237 |     it = a.erase(it); | 
| 1238 |     QVERIFY(a.isEmpty()); | 
| 1239 |     QCOMPARE(it, a.end()); | 
| 1240 |  | 
| 1241 |     // reinsert the element so we can take it | 
| 1242 |     a.append(value: v); | 
| 1243 |     QCOMPARE(a.takeAt(0), v); | 
| 1244 |     QVERIFY(a.isEmpty()); | 
| 1245 | } | 
| 1246 |  | 
| 1247 | void tst_QCborValue::arrayStringElements() | 
| 1248 | { | 
| 1249 |     QCborArray a{"Hello" }; | 
| 1250 |     a.append(value: QByteArray("Hello" )); | 
| 1251 |     a.append(value: QLatin1String("World" )); | 
| 1252 |     QVERIFY(a == a); | 
| 1253 |     QVERIFY(a == QCborArray({QLatin1String("Hello" ), | 
| 1254 |                              QByteArray("Hello" ), QStringLiteral("World" )})); | 
| 1255 |  | 
| 1256 |     QCborValueRef r1 = a[0]; | 
| 1257 |     QCOMPARE(r1.toString(), "Hello" ); | 
| 1258 |     QCOMPARE(r1.operator QCborValue(), QCborValue("Hello" )); | 
| 1259 |     QVERIFY(r1 == QCborValue("Hello" )); | 
| 1260 |  | 
| 1261 |     QCborValue v2 = a.at(i: 1); | 
| 1262 |     QCOMPARE(v2.toByteArray(), QByteArray("Hello" )); | 
| 1263 |     QCOMPARE(v2, QCborValue(QByteArray("Hello" ))); | 
| 1264 |  | 
| 1265 |     // v2 must continue to be valid after the entry getting removed | 
| 1266 |     a.removeAt(i: 1); | 
| 1267 |     QCOMPARE(v2.toByteArray(), QByteArray("Hello" )); | 
| 1268 |     QCOMPARE(v2, QCborValue(QByteArray("Hello" ))); | 
| 1269 |  | 
| 1270 |     v2 = a.at(i: 1); | 
| 1271 |     QCOMPARE(v2.toString(), "World" ); | 
| 1272 |     QCOMPARE(v2, QCborValue("World" )); | 
| 1273 |  | 
| 1274 |     QCOMPARE(a.takeAt(1).toString(), "World" ); | 
| 1275 |     QCOMPARE(a.takeAt(0).toString(), "Hello" ); | 
| 1276 |     QVERIFY(a.isEmpty()); | 
| 1277 | } | 
| 1278 |  | 
| 1279 | void tst_QCborValue::mapStringValues() | 
| 1280 | { | 
| 1281 |     QCborMap m{{0, "Hello" }}; | 
| 1282 |     m.insert(v: {1, QByteArray("Hello" )}); | 
| 1283 |     m.insert(v: {2, QLatin1String("World" )}); | 
| 1284 |     QVERIFY(m == m); | 
| 1285 |  | 
| 1286 |     QCborValueRef r1 = m[0]; | 
| 1287 |     QCOMPARE(r1.toString(), "Hello" ); | 
| 1288 |     QCOMPARE(r1.operator QCborValue(), QCborValue("Hello" )); | 
| 1289 |     QVERIFY(r1 == QCborValue("Hello" )); | 
| 1290 |  | 
| 1291 |     QCborValue v2 = m.value(key: 1); | 
| 1292 |     QCOMPARE(v2.toByteArray(), QByteArray("Hello" )); | 
| 1293 |     QCOMPARE(v2, QCborValue(QByteArray("Hello" ))); | 
| 1294 |  | 
| 1295 |     // v2 must continue to be valid after the entry getting removed | 
| 1296 |     m.erase(it: m.constFind(key: 1)); | 
| 1297 |     QCOMPARE(v2.toByteArray(), QByteArray("Hello" )); | 
| 1298 |     QCOMPARE(v2, QCborValue(QByteArray("Hello" ))); | 
| 1299 |  | 
| 1300 |     v2 = (m.begin() + 1).value(); | 
| 1301 |     QCOMPARE(v2.toString(), "World" ); | 
| 1302 |     QCOMPARE(v2, QCborValue("World" )); | 
| 1303 |  | 
| 1304 |     QCOMPARE(m.extract(m.begin() + 1).toString(), "World" ); | 
| 1305 |     QCOMPARE(m.take(0).toString(), "Hello" ); | 
| 1306 |     QVERIFY(m.isEmpty()); | 
| 1307 | } | 
| 1308 |  | 
| 1309 | void tst_QCborValue::mapStringKeys() | 
| 1310 | { | 
| 1311 |     QCborMap m{{QLatin1String("Hello" ), 1}, {QStringLiteral("World" ), 2}}; | 
| 1312 |     QCOMPARE(m.value(QStringLiteral("Hello" )), QCborValue(1)); | 
| 1313 |     QCOMPARE(m.value(QLatin1String("World" )), QCborValue(2)); | 
| 1314 |  | 
| 1315 |     QCborMap m2 = m; | 
| 1316 |     QVERIFY(m2 == m); | 
| 1317 |     QVERIFY(m == m2); | 
| 1318 |  | 
| 1319 |     m.insert(v: {QByteArray("foo" ), "bar" }); | 
| 1320 |     QCOMPARE(m.size(), 3); | 
| 1321 |     QCOMPARE(m2.size(), 2); | 
| 1322 |     QVERIFY(m2 != m); | 
| 1323 |     QVERIFY(m != m2); | 
| 1324 |  | 
| 1325 |     QVERIFY(m2.value(QCborValue(QByteArray("foo" ))).isUndefined()); | 
| 1326 |     QVERIFY(m.value(QCborValue(QLatin1String("foo" ))).isUndefined()); | 
| 1327 |     QCOMPARE(m.value(QCborValue(QByteArray("foo" ))).toString(), "bar" ); | 
| 1328 | } | 
| 1329 |  | 
| 1330 | void tst_QCborValue::mapInsertRemove() | 
| 1331 | { | 
| 1332 |     QFETCH(QCborValue, v); | 
| 1333 |     QCborMap m{{1, v}}; | 
| 1334 |  | 
| 1335 |     m.remove(key: 1); | 
| 1336 |     QVERIFY(m.isEmpty()); | 
| 1337 |     QVERIFY(!m.contains(1)); | 
| 1338 |  | 
| 1339 |     m.insert(key: 2, value_: v); | 
| 1340 |     QVERIFY(m.contains(2)); | 
| 1341 |     QVERIFY(m[2] == v); | 
| 1342 |     QVERIFY(v == m[2]); | 
| 1343 |  | 
| 1344 |     auto it = m.find(key: 2); | 
| 1345 |     it = m.erase(it); | 
| 1346 |     QVERIFY(m.isEmpty()); | 
| 1347 |  | 
| 1348 |     // creates m[2] and m[42] just by referencing them | 
| 1349 |     m[2]; | 
| 1350 |     QCborValueRef r = m[42]; | 
| 1351 |     QCOMPARE(m.size(), 2); | 
| 1352 |  | 
| 1353 |     r = v; | 
| 1354 |     it = m.find(key: 42); | 
| 1355 |     QVERIFY(it.value() == v); | 
| 1356 |     QVERIFY(v == it.value()); | 
| 1357 |     QVERIFY(it.value() == r); | 
| 1358 |     QVERIFY(r == it.value()); | 
| 1359 |  | 
| 1360 |     QCOMPARE(m.extract(it), v); | 
| 1361 |     QVERIFY(!m.contains(42)); | 
| 1362 |  | 
| 1363 |     m[2] = v; | 
| 1364 |     QCOMPARE(m.take(2), v); | 
| 1365 |     QVERIFY(m.take(2).isUndefined()); | 
| 1366 |     QVERIFY(m.isEmpty()); | 
| 1367 | } | 
| 1368 |  | 
| 1369 | void tst_QCborValue::arrayInsertTagged() | 
| 1370 | { | 
| 1371 |     QFETCH(QCborValue, v); | 
| 1372 |  | 
| 1373 |     // make it tagged | 
| 1374 |     QCborValue tagged(QCborKnownTags::Signature, v); | 
| 1375 |  | 
| 1376 |     QCborArray a{tagged}; | 
| 1377 |     a.insert(i: 1, value: tagged); | 
| 1378 |     QCOMPARE(a.size(), 2); | 
| 1379 |     QCOMPARE(a.at(0), tagged); | 
| 1380 |     QCOMPARE(a.at(1), tagged); | 
| 1381 |     QCOMPARE(a.at(0).taggedValue(), v); | 
| 1382 |     QCOMPARE(a.at(1).taggedValue(), v); | 
| 1383 |     QCOMPARE(a.takeAt(0).taggedValue(), v); | 
| 1384 |     QCOMPARE(a.takeAt(0).taggedValue(), v); | 
| 1385 |     QVERIFY(a.isEmpty()); | 
| 1386 | } | 
| 1387 |  | 
| 1388 | void tst_QCborValue::mapInsertTagged() | 
| 1389 | { | 
| 1390 |     QFETCH(QCborValue, v); | 
| 1391 |  | 
| 1392 |     // make it tagged | 
| 1393 |     QCborValue tagged(QCborKnownTags::Signature, v); | 
| 1394 |  | 
| 1395 |     QCborMap m{{11, tagged}}; | 
| 1396 |     m.insert(v: {-21, tagged}); | 
| 1397 |     QCOMPARE(m.size(), 2); | 
| 1398 |     QCOMPARE(m.constBegin().value(), tagged); | 
| 1399 |     QCOMPARE(m.value(-21), tagged); | 
| 1400 |     QCOMPARE(m.value(11).taggedValue(), v); | 
| 1401 |     QCOMPARE((m.end() - 1).value().taggedValue(), v); | 
| 1402 |     QCOMPARE(m.extract(m.end() - 1).taggedValue(), v); | 
| 1403 |     QVERIFY(!m.contains(-21)); | 
| 1404 |     QCOMPARE(m.take(11).taggedValue(), v); | 
| 1405 |     QVERIFY(m.isEmpty()); | 
| 1406 | } | 
| 1407 |  | 
| 1408 | void tst_QCborValue::arraySelfAssign() | 
| 1409 | { | 
| 1410 |     QFETCH(QCborValue, v); | 
| 1411 |     QCborArray a; | 
| 1412 |  | 
| 1413 |     a = {v}; | 
| 1414 |  | 
| 1415 |     // Test 1: QCborValue created first, so | 
| 1416 |     // QCborArray::insert() detaches | 
| 1417 |     { | 
| 1418 |         a.append(value: a); | 
| 1419 |         QCOMPARE(a.size(), 2); | 
| 1420 |         QCOMPARE(a.last().toArray().size(), 1); | 
| 1421 |     } | 
| 1422 |  | 
| 1423 |     a = {v}; | 
| 1424 |  | 
| 1425 |     // Test 2: QCborValueRef created first | 
| 1426 |     { | 
| 1427 |         a.append(value: 36); | 
| 1428 |         auto it = a.end() - 1; | 
| 1429 |         *it = a; | 
| 1430 |  | 
| 1431 |         QCOMPARE(a.size(), 2); | 
| 1432 |         QCOMPARE(it->toArray().size(), 2); | 
| 1433 |         QCOMPARE(it->toArray().last(), QCborValue(36)); | 
| 1434 |     } | 
| 1435 | } | 
| 1436 |  | 
| 1437 | void tst_QCborValue::mapSelfAssign() | 
| 1438 | { | 
| 1439 |     QFETCH(QCborValue, v); | 
| 1440 |     QCborMap m; | 
| 1441 |  | 
| 1442 |     m = {{0, v}}; | 
| 1443 |     QCOMPARE(m.size(), 1); | 
| 1444 |  | 
| 1445 |     // Test 1: create a QCborValue first | 
| 1446 |     // in this case, QCborMap::operator[] detaches first | 
| 1447 |     { | 
| 1448 |         QCborValue vm = m; | 
| 1449 |         m[1] = vm;      // self-assign | 
| 1450 |         QCOMPARE(m.size(), 2); | 
| 1451 |         QCOMPARE(m.value(0), v); | 
| 1452 |  | 
| 1453 |         QCborMap m2 = m.value(key: 1).toMap(); | 
| 1454 |         // there mustn't be an element with key 1 | 
| 1455 |         QCOMPARE(m2.size(), 1); | 
| 1456 |         QCOMPARE(m2.value(0), v); | 
| 1457 |         QVERIFY(!m2.contains(1)); | 
| 1458 |     } | 
| 1459 |  | 
| 1460 |     m = {{0, v}}; | 
| 1461 |  | 
| 1462 |     // Test 2: create the QCborValueRef first | 
| 1463 |     // in this case, there's no opportunity to detach | 
| 1464 |     { | 
| 1465 |         QCborValueRef rv = m[1]; | 
| 1466 |         rv = m;     // self-assign (implicit QCborValue creation) | 
| 1467 |         QCOMPARE(m.size(), 2); | 
| 1468 |         QCOMPARE(m.value(0), v); | 
| 1469 |  | 
| 1470 |         QCborMap m2 = m.value(key: 1).toMap(); | 
| 1471 |         // there must be an element with key 1 | 
| 1472 |         QCOMPARE(m2.size(), 2); | 
| 1473 |         QCOMPARE(m2.value(0), v); | 
| 1474 |         QVERIFY(m2.contains(1)); | 
| 1475 |         QCOMPARE(m2.value(1), QCborValue()); | 
| 1476 |     } | 
| 1477 |  | 
| 1478 |     m = {{0, v}}; | 
| 1479 |  | 
| 1480 |     // Test 3: don't force creation of either before | 
| 1481 |     // in this case, it's up to the compiler to choose | 
| 1482 |     { | 
| 1483 |         m[1] = m;   // self-assign | 
| 1484 |         QCOMPARE(m.size(), 2); | 
| 1485 |  | 
| 1486 |         QCborMap m2 = m.value(key: 1).toMap(); | 
| 1487 |         QVERIFY(m2.size() == 1 || m2.size() == 2); | 
| 1488 |     } | 
| 1489 |  | 
| 1490 |     m = {{0, v}}; | 
| 1491 |  | 
| 1492 |     // Test 4: self-assign as key | 
| 1493 |     // in this scase, QCborMap::operator[] must detach | 
| 1494 |     { | 
| 1495 |         m[m] = v; | 
| 1496 |         QCOMPARE(m.size(), 2); | 
| 1497 |  | 
| 1498 |         auto it = m.constEnd() - 1; | 
| 1499 |         QCOMPARE(it.value(), v); | 
| 1500 |         QCOMPARE(it.key(), QCborMap({{0, v}})); | 
| 1501 |     } | 
| 1502 | } | 
| 1503 |  | 
| 1504 | void tst_QCborValue::mapComplexKeys() | 
| 1505 | { | 
| 1506 |     QFETCH(QCborValue, v); | 
| 1507 |     QCborValue tagged(QCborKnownTags::Signature, v); | 
| 1508 |  | 
| 1509 |     QCborMap m{{42, true}, {v, 42}, {-3, nullptr}}; | 
| 1510 |     QCOMPARE(m.size(), 3); | 
| 1511 |     QVERIFY(m.contains(42)); | 
| 1512 |     QVERIFY(m.contains(-3)); | 
| 1513 |     QVERIFY(m.contains(v)); | 
| 1514 |     QVERIFY(!m.contains(tagged)); | 
| 1515 |  | 
| 1516 |     auto it = m.constFind(key: v); | 
| 1517 |     QVERIFY(it != m.constEnd()); | 
| 1518 |     QVERIFY(it.key() == v); | 
| 1519 |     QVERIFY(v == it.key()); | 
| 1520 |     QCOMPARE(it.value().toInteger(), 42); | 
| 1521 |  | 
| 1522 |     QCborArray a{0, 1, 2, 3, v}; | 
| 1523 |     m[a] = 1; | 
| 1524 |     QCOMPARE(m.size(), 4); | 
| 1525 |     QCOMPARE((m.constEnd() - 1).value(), QCborValue(1)); | 
| 1526 |     if (v != QCborValue(QCborValue::Array)) | 
| 1527 |         QVERIFY(!m.contains(QCborArray{})); | 
| 1528 |     QVERIFY(!m.contains(QCborArray{0})); | 
| 1529 |     QVERIFY(!m.contains(QCborArray{0, 1})); | 
| 1530 |     QVERIFY(!m.contains(QCborArray{0, 1, 2})); | 
| 1531 |     QVERIFY(!m.contains(QCborArray{0, 1, 2, 4})); | 
| 1532 |     QVERIFY(!m.contains(QCborArray{0, 1, 2, 3, v, 4})); | 
| 1533 |  | 
| 1534 |     it = m.constFind(key: QCborArray{0, 1, 2, 3, v}); | 
| 1535 |     QVERIFY(it != m.constEnd()); | 
| 1536 |     QCOMPARE(it.key(), a); | 
| 1537 |     QCOMPARE(it.value(), QCborValue(1)); | 
| 1538 |  | 
| 1539 |     m[m] = 1;   // assign itself as a key -- this necessarily detaches before | 
| 1540 |     QCOMPARE(m.size(), 5); | 
| 1541 |     QCOMPARE((m.end() - 1).value(), 1); | 
| 1542 |     QCOMPARE((m.end() - 1).key().toMap().size(), 4); | 
| 1543 |  | 
| 1544 |     QCborValue mv(m); | 
| 1545 |     if (v.isInteger()) { | 
| 1546 |         // we should be able to find using the overloads too | 
| 1547 |         QCOMPARE(m[v.toInteger()].toInteger(), 42); | 
| 1548 |         QCOMPARE(mv[v.toInteger()].toInteger(), 42); | 
| 1549 |     } else if (v.isString()) { | 
| 1550 |         // ditto | 
| 1551 |         QCOMPARE(m[v.toString()].toInteger(), 42); | 
| 1552 |         QCOMPARE(mv[v.toString()].toInteger(), 42); | 
| 1553 |  | 
| 1554 |         // basics_data() strings are Latin1 | 
| 1555 |         QByteArray latin1 = v.toString().toLatin1(); | 
| 1556 |         Q_ASSERT(v.toString() == QString::fromLatin1(latin1)); | 
| 1557 |         QCOMPARE(m[QLatin1String(latin1)].toInteger(), 42); | 
| 1558 |     } | 
| 1559 |  | 
| 1560 |     m.remove(key: v); | 
| 1561 |     QVERIFY(!m.contains(v)); | 
| 1562 |     QVERIFY(!m.contains(tagged)); | 
| 1563 |  | 
| 1564 |     QCborValueRef r = m[tagged]; | 
| 1565 |     QVERIFY(!m.contains(v)); | 
| 1566 |     QVERIFY(m.contains(tagged)); | 
| 1567 |     r = 47; | 
| 1568 |     QCOMPARE(m[tagged].toInteger(), 47); | 
| 1569 |     QCOMPARE(m.take(tagged).toInteger(), 47); | 
| 1570 |     QVERIFY(!m.contains(tagged)); | 
| 1571 | } | 
| 1572 |  | 
| 1573 | void tst_QCborValue::sorting() | 
| 1574 | { | 
| 1575 |     QCborValue vundef, vnull(nullptr); | 
| 1576 |     QCborValue vtrue(true), vfalse(false); | 
| 1577 |     QCborValue vint1(1), vint2(2); | 
| 1578 |     QCborValue vneg1(-1), vneg2(-2); | 
| 1579 |     QCborValue vba2(QByteArray("Hello" )), vba3(QByteArray("World" )), vba1(QByteArray("foo" )); | 
| 1580 |     QCborValue vs2("Hello" ), vs3("World" ), vs1("foo" ); | 
| 1581 |     QCborValue va1(QCborValue::Array), va2(QCborArray{1}), va3(QCborArray{0, 0}); | 
| 1582 |     QCborValue vm1(QCborValue::Map), vm2(QCborMap{{1, 0}}), vm3(QCborMap{{0, 0}, {1, 0}}); | 
| 1583 |     QCborValue vdt1(QDateTime::fromMSecsSinceEpoch(msecs: 0, spec: Qt::UTC)), vdt2(QDateTime::currentDateTimeUtc()); | 
| 1584 |     QCborValue vtagged1(QCborKnownTags::PositiveBignum, QByteArray()), | 
| 1585 |             vtagged2(QCborKnownTags::PositiveBignum, 0.0),  // bignums are supposed to have byte arrays... | 
| 1586 |             vtagged3(QCborKnownTags::Signature, 0), | 
| 1587 |             vtagged4(QCborTag(-2), 0), | 
| 1588 |             vtagged5(QCborTag(-1), 0); | 
| 1589 |     QCborValue vurl1(QUrl("https://example.net" )), vurl2(QUrl("https://example.com/" )); | 
| 1590 |     QCborValue vuuid1{QUuid()}, vuuid2(QUuid::createUuid()); | 
| 1591 |     QCborValue vsimple1(QCborSimpleType(1)), vsimple32(QCborSimpleType(32)), vsimple255(QCborSimpleType(255)); | 
| 1592 |     QCborValue vdouble1(1.5), vdouble2(qInf()); | 
| 1593 |     QCborValue vndouble1(-1.5), vndouble2(-qInf()); | 
| 1594 |  | 
| 1595 | #define CHECK_ORDER(v1, v2) \ | 
| 1596 |     QVERIFY(v1 < v2); \ | 
| 1597 |     QVERIFY(!(v2 < v2)) | 
| 1598 |  | 
| 1599 |     // intra-type comparisons | 
| 1600 |     CHECK_ORDER(vfalse, vtrue); | 
| 1601 |     CHECK_ORDER(vsimple1, vsimple32); | 
| 1602 |     CHECK_ORDER(vsimple32, vsimple255); | 
| 1603 |     CHECK_ORDER(vint1, vint2); | 
| 1604 |     CHECK_ORDER(vdouble1, vdouble2); | 
| 1605 |     CHECK_ORDER(vndouble1, vndouble2); | 
| 1606 |     // note: shorter length sorts first | 
| 1607 |     CHECK_ORDER(vba1, vba2); | 
| 1608 |     CHECK_ORDER(vba2, vba3); | 
| 1609 |     CHECK_ORDER(vs1, vs2); | 
| 1610 |     CHECK_ORDER(vs2, vs3); | 
| 1611 |     CHECK_ORDER(va1, va2); | 
| 1612 |     CHECK_ORDER(va2, va3); | 
| 1613 |     CHECK_ORDER(vm1, vm2); | 
| 1614 |     CHECK_ORDER(vm2, vm3); | 
| 1615 |     CHECK_ORDER(vdt1, vdt2); | 
| 1616 |     CHECK_ORDER(vtagged1, vtagged2); | 
| 1617 |     CHECK_ORDER(vtagged2, vtagged3); | 
| 1618 |     CHECK_ORDER(vtagged3, vtagged4); | 
| 1619 |     CHECK_ORDER(vtagged4, vtagged5); | 
| 1620 |     CHECK_ORDER(vurl1, vurl2); | 
| 1621 |     CHECK_ORDER(vuuid1, vuuid2); | 
| 1622 |  | 
| 1623 |     // surprise 1: CBOR sorts integrals by absolute value | 
| 1624 |     CHECK_ORDER(vneg1, vneg2); | 
| 1625 |  | 
| 1626 |     // surprise 2: CBOR sorts negatives after positives (sign+magnitude) | 
| 1627 |     CHECK_ORDER(vint2, vneg1); | 
| 1628 |     QVERIFY(vint2.toInteger() > vneg1.toInteger()); | 
| 1629 |     CHECK_ORDER(vdouble2, vndouble1); | 
| 1630 |     QVERIFY(vdouble2.toDouble() > vndouble1.toDouble()); | 
| 1631 |  | 
| 1632 |     // inter-type comparisons | 
| 1633 |     CHECK_ORDER(vneg2, vba1); | 
| 1634 |     CHECK_ORDER(vba3, vs1); | 
| 1635 |     CHECK_ORDER(vs3, va1); | 
| 1636 |     CHECK_ORDER(va2, vm1); | 
| 1637 |     CHECK_ORDER(vm2, vdt1); | 
| 1638 |     CHECK_ORDER(vdt2, vtagged1); | 
| 1639 |     CHECK_ORDER(vtagged2, vurl1); | 
| 1640 |     CHECK_ORDER(vurl1, vuuid1); | 
| 1641 |     CHECK_ORDER(vuuid2, vtagged3); | 
| 1642 |     CHECK_ORDER(vtagged4, vsimple1); | 
| 1643 |     CHECK_ORDER(vsimple1, vfalse); | 
| 1644 |     CHECK_ORDER(vtrue, vnull); | 
| 1645 |     CHECK_ORDER(vnull, vundef); | 
| 1646 |     CHECK_ORDER(vundef, vsimple32); | 
| 1647 |     CHECK_ORDER(vsimple255, vdouble1); | 
| 1648 |  | 
| 1649 |     // which shows all doubles sorted after integrals | 
| 1650 |     CHECK_ORDER(vint2, vdouble1); | 
| 1651 |     QVERIFY(vint2.toInteger() > vdouble1.toDouble()); | 
| 1652 | #undef CHECK_ORDER | 
| 1653 | } | 
| 1654 |  | 
| 1655 | static void addCommonCborData() | 
| 1656 | { | 
| 1657 |     // valid for both decoding and encoding | 
| 1658 |     QTest::addColumn<QCborValue>(name: "v" ); | 
| 1659 |     QTest::addColumn<QByteArray>(name: "result" ); | 
| 1660 |     QTest::addColumn<QCborValue::EncodingOptions>(name: "options" ); | 
| 1661 |     QDateTime dt = QDateTime::currentDateTimeUtc(); | 
| 1662 |     QUuid uuid = QUuid::createUuid(); | 
| 1663 |     QCborValue::EncodingOptions noxfrm = QCborValue::NoTransformation; | 
| 1664 |  | 
| 1665 |     // integrals | 
| 1666 |     QTest::newRow(dataTag: "Integer:0" ) << QCborValue(0) << raw(data: "\x00" ) << noxfrm; | 
| 1667 |     QTest::newRow(dataTag: "Integer:1" ) << QCborValue(1) << raw(data: "\x01" ) << noxfrm; | 
| 1668 |     QTest::newRow(dataTag: "Integer:-1" ) << QCborValue(-1) << raw(data: "\x20" ) << noxfrm; | 
| 1669 |     QTest::newRow(dataTag: "Integer:INT64_MAX" ) << QCborValue(std::numeric_limits<qint64>::max()) | 
| 1670 |                                        << raw(data: "\x1b\x7f\xff\xff\xff" "\xff\xff\xff\xff" ) | 
| 1671 |                                        << noxfrm; | 
| 1672 |     QTest::newRow(dataTag: "Integer:INT64_MIN" ) << QCborValue(std::numeric_limits<qint64>::min()) | 
| 1673 |                                        << raw(data: "\x3b\x7f\xff\xff\xff" "\xff\xff\xff\xff" ) | 
| 1674 |                                        << noxfrm; | 
| 1675 |  | 
| 1676 |     QTest::newRow(dataTag: "simple0" ) << QCborValue(QCborValue::SimpleType) << raw(data: "\xe0" ) << noxfrm; | 
| 1677 |     QTest::newRow(dataTag: "simple1" ) << QCborValue(QCborSimpleType(1)) << raw(data: "\xe1" ) << noxfrm; | 
| 1678 |     QTest::newRow(dataTag: "simple255" ) << QCborValue(QCborSimpleType(255)) << raw(data: "\xf8\xff" ) << noxfrm; | 
| 1679 |     QTest::newRow(dataTag: "Undefined" ) << QCborValue() << raw(data: "\xf7" ) << noxfrm; | 
| 1680 |     QTest::newRow(dataTag: "Null" ) << QCborValue(nullptr) << raw(data: "\xf6" ) << noxfrm; | 
| 1681 |     QTest::newRow(dataTag: "True" ) << QCborValue(true) << raw(data: "\xf5" ) << noxfrm; | 
| 1682 |     QTest::newRow(dataTag: "False" ) << QCborValue(false) << raw(data: "\xf4" ) << noxfrm; | 
| 1683 |     QTest::newRow(dataTag: "simple32" ) << QCborValue(QCborSimpleType(32)) << raw(data: "\xf8\x20" ) << noxfrm; | 
| 1684 |     QTest::newRow(dataTag: "simple255" ) << QCborValue(QCborSimpleType(255)) << raw(data: "\xf8\xff" ) << noxfrm; | 
| 1685 |  | 
| 1686 |     QTest::newRow(dataTag: "Double:0" ) << QCborValue(0.) << raw(data: "\xfb\0\0\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1687 |     QTest::newRow(dataTag: "Double:1.5" ) << QCborValue(1.5) << raw(data: "\xfb\x3f\xf8\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1688 |     QTest::newRow(dataTag: "Double:-1.5" ) << QCborValue(-1.5) << raw(data: "\xfb\xbf\xf8\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1689 |     QTest::newRow(dataTag: "Double:INT64_MAX+1" ) << QCborValue(std::numeric_limits<qint64>::max() + 1.) | 
| 1690 |                                             << raw(data: "\xfb\x43\xe0\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1691 |     QTest::newRow(dataTag: "Double:maxintegralfp" ) << QCborValue(18446744073709551616.0 - 2048) | 
| 1692 |                                           << raw(data: "\xfb\x43\xef\xff\xff" "\xff\xff\xff\xff" ) | 
| 1693 |                                           << noxfrm; | 
| 1694 |     QTest::newRow(dataTag: "Double:minintegralfp" ) << QCborValue(-18446744073709551616.0 + 2048) | 
| 1695 |                                           << raw(data: "\xfb\xc3\xef\xff\xff" "\xff\xff\xff\xff" ) | 
| 1696 |                                           << noxfrm; | 
| 1697 |     QTest::newRow(dataTag: "Double:inf" ) << QCborValue(qInf()) << raw(data: "\xfb\x7f\xf0\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1698 |     QTest::newRow(dataTag: "Double:-inf" ) << QCborValue(-qInf()) << raw(data: "\xfb\xff\xf0\0" "\0\0\0\0\0" ) << noxfrm; | 
| 1699 |     QTest::newRow(dataTag: "Double:nan" ) << QCborValue(qQNaN()) << raw(data: "\xfb\x7f\xf8\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1700 |  | 
| 1701 |     QTest::newRow(dataTag: "Float:0" ) << QCborValue(0.) << raw(data: "\xfa\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1702 |     QTest::newRow(dataTag: "Float:1.5" ) << QCborValue(1.5) << raw(data: "\xfa\x3f\xc0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1703 |     QTest::newRow(dataTag: "Float:-1.5" ) << QCborValue(-1.5) << raw(data: "\xfa\xbf\xc0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1704 |     QTest::newRow(dataTag: "Float:inf" ) << QCborValue(qInf()) << raw(data: "\xfa\x7f\x80\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1705 |     QTest::newRow(dataTag: "Float:-inf" ) << QCborValue(-qInf()) << raw(data: "\xfa\xff\x80\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1706 |     QTest::newRow(dataTag: "Float:nan" ) << QCborValue(qQNaN()) << raw(data: "\xfa\x7f\xc0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1707 |  | 
| 1708 |     QTest::newRow(dataTag: "Float16:0" ) << QCborValue(0.) << raw(data: "\xf9\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1709 |     QTest::newRow(dataTag: "Float16:1.5" ) << QCborValue(1.5) << raw(data: "\xf9\x3e\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1710 |     QTest::newRow(dataTag: "Float16:-1.5" ) << QCborValue(-1.5) << raw(data: "\xf9\xbe\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1711 |     QTest::newRow(dataTag: "Float16:inf" ) << QCborValue(qInf()) << raw(data: "\xf9\x7c\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1712 |     QTest::newRow(dataTag: "Float16:-inf" ) << QCborValue(-qInf()) << raw(data: "\xf9\xfc\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1713 |     QTest::newRow(dataTag: "Float16:nan" ) << QCborValue(qQNaN()) << raw(data: "\xf9\x7e\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1714 |  | 
| 1715 |     // out of range of qint64, but in range for CBOR, so these do get converted | 
| 1716 |     // to integrals on write and back to double on read | 
| 1717 |     QTest::newRow(dataTag: "UseInteger:INT64_MAX+1" ) << QCborValue(std::numeric_limits<qint64>::max() + 1.) | 
| 1718 |                                             << raw(data: "\x1b\x80\0\0\0" "\0\0\0\0" ) | 
| 1719 |                                             << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1720 |     QTest::newRow(dataTag: "UseInteger:maxintegralfp" ) << QCborValue(18446744073709551616.0 - 2048) | 
| 1721 |                                               << raw(data: "\x1b\xff\xff\xff\xff" "\xff\xff\xf8\0" ) | 
| 1722 |                                               << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1723 |     QTest::newRow(dataTag: "UseInteger:minintegralfp" ) << QCborValue(-18446744073709551616.0 + 2048) | 
| 1724 |                                               << raw(data: "\x3b\xff\xff\xff\xff" "\xff\xff\xf7\xff" ) | 
| 1725 |                                               << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1726 |  | 
| 1727 |     QTest::newRow(dataTag: "ByteArray:Empty" ) << QCborValue(QByteArray()) << raw(data: "\x40" ) << noxfrm; | 
| 1728 |     QTest::newRow(dataTag: "ByteArray" ) << QCborValue(QByteArray("Hello" )) << raw(data: "\x45Hello" ) << noxfrm; | 
| 1729 |     QTest::newRow(dataTag: "ByteArray:WithNull" ) << QCborValue(raw(data: "\0\1\2\xff" )) << raw(data: "\x44\0\1\2\xff" ) << noxfrm; | 
| 1730 |  | 
| 1731 |     QTest::newRow(dataTag: "String:Empty" ) << QCborValue(QString()) << raw(data: "\x60" ) << noxfrm; | 
| 1732 |     QTest::newRow(dataTag: "String:UsAscii" ) << QCborValue("Hello" ) << raw(data: "\x65Hello" ) << noxfrm; | 
| 1733 |     QTest::newRow(dataTag: "String:Latin1" ) << QCborValue(QLatin1String("R\xe9sum\xe9" )) | 
| 1734 |                                      << raw(data: "\x68R\xc3\xa9sum\xc3\xa9" ) << noxfrm; | 
| 1735 |     QTest::newRow(dataTag: "String:Unicode" ) << QCborValue(QStringLiteral(u"éś α €" )) | 
| 1736 |                                      << raw(data: "\x6b\xc3\xa9\xc5\x9b \xce\xb1 \xe2\x82\xac" ) << noxfrm; | 
| 1737 |  | 
| 1738 |     QTest::newRow(dataTag: "DateTime" ) << QCborValue(dt)             // this is UTC | 
| 1739 |                               << "\xc0\x78\x18"  + dt.toString(format: Qt::ISODateWithMs).toLatin1() | 
| 1740 |                               << noxfrm; | 
| 1741 |     QTest::newRow(dataTag: "DateTime-UTC" ) << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::UTC)) | 
| 1742 |                                   << raw(data: "\xc0\x78\x18"  "2018-01-01T09:00:00.000Z" ) | 
| 1743 |                                   << noxfrm; | 
| 1744 |     QTest::newRow(dataTag: "DateTime-Local" ) << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::LocalTime)) | 
| 1745 |                                     << raw(data: "\xc0\x77"  "2018-01-01T09:00:00.000" ) | 
| 1746 |                                     << noxfrm; | 
| 1747 |     QTest::newRow(dataTag: "DateTime+01:00" ) << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::OffsetFromUTC, 3600)) | 
| 1748 |                                     << raw(data: "\xc0\x78\x1d"  "2018-01-01T09:00:00.000+01:00" ) | 
| 1749 |                                     << noxfrm; | 
| 1750 |     QTest::newRow(dataTag: "Url:Empty" ) << QCborValue(QUrl()) << raw(data: "\xd8\x20\x60" ) << noxfrm; | 
| 1751 |     QTest::newRow(dataTag: "Url" ) << QCborValue(QUrl("HTTPS://example.com/{%30%31}?q=%3Ca+b%20%C2%A9%3E&%26" )) | 
| 1752 |                          << raw(data: "\xd8\x20\x78\x27"  "https://example.com/{01}?q=<a+b \xC2\xA9>&%26" ) | 
| 1753 |                          << noxfrm; | 
| 1754 |     QTest::newRow(dataTag: "Url:NonAscii" ) << QCborValue(QUrl("https://example.com/\xc2\xa0" )) | 
| 1755 |                                   << raw(data: "\xd8\x20\x76"  "https://example.com/\xc2\xa0" ) | 
| 1756 |                                   << noxfrm; | 
| 1757 |     QTest::newRow(dataTag: "Regex:Empty" ) << QCborValue(QRegularExpression()) << raw(data: "\xd8\x23\x60" ) << noxfrm; | 
| 1758 |     QTest::newRow(dataTag: "Regex" ) << QCborValue(QRegularExpression("^.*$" )) | 
| 1759 |                            << raw(data: "\xd8\x23\x64"  "^.*$" ) << noxfrm; | 
| 1760 |     QTest::newRow(dataTag: "Uuid" ) << QCborValue(uuid) << raw(data: "\xd8\x25\x50" ) + uuid.toRfc4122() << noxfrm; | 
| 1761 |  | 
| 1762 |     // empty arrays and maps | 
| 1763 |     QTest::newRow(dataTag: "Array" ) << QCborValue(QCborArray()) << raw(data: "\x80" ) << noxfrm; | 
| 1764 |     QTest::newRow(dataTag: "Map" ) << QCborValue(QCborMap()) << raw(data: "\xa0" ) << noxfrm; | 
| 1765 |  | 
| 1766 |     QTest::newRow(dataTag: "Tagged:ByteArray" ) << QCborValue(QCborKnownTags::PositiveBignum, raw(data: "\1\0\0\0\0" "\0\0\0\0" )) | 
| 1767 |                                       << raw(data: "\xc2\x49\1\0\0\0\0" "\0\0\0\0" ) << noxfrm; | 
| 1768 |     QTest::newRow(dataTag: "Tagged:Array" ) << QCborValue(QCborKnownTags::Decimal, QCborArray{-2, 27315}) | 
| 1769 |                                   << raw(data: "\xc4\x82\x21\x19\x6a\xb3" ) << noxfrm; | 
| 1770 | } | 
| 1771 |  | 
| 1772 | void tst_QCborValue::toCbor_data() | 
| 1773 | { | 
| 1774 |     addCommonCborData(); | 
| 1775 |  | 
| 1776 |     // The rest of these tests are conversions whose decoding does not yield | 
| 1777 |     // back the same QCborValue. | 
| 1778 |  | 
| 1779 | #if QT_CONFIG(signaling_nan) | 
| 1780 |     // Signalling NaN get normalized to quiet ones | 
| 1781 |     QTest::newRow(dataTag: "Double:snan" ) << QCborValue(qSNaN()) << raw(data: "\xfb\x7f\xf8\0" "\0\0\0\0\0" ) << QCborValue::EncodingOptions(); | 
| 1782 |     QTest::newRow(dataTag: "Float:snan" ) << QCborValue(qSNaN()) << raw(data: "\xfa\x7f\xc0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat); | 
| 1783 |     QTest::newRow(dataTag: "Float16:snan" ) << QCborValue(qSNaN()) << raw(data: "\xf9\x7e\0" ) << QCborValue::EncodingOptions(QCborValue::UseFloat16); | 
| 1784 | #endif | 
| 1785 |  | 
| 1786 |     // Floating point written as integers are read back as integers | 
| 1787 |     QTest::newRow(dataTag: "UseInteger:0" ) << QCborValue(0.) << raw(data: "\x00" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1788 |     QTest::newRow(dataTag: "UseInteger:1" ) << QCborValue(1.) << raw(data: "\x01" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1789 |     QTest::newRow(dataTag: "UseInteger:-1" ) << QCborValue(-1.) << raw(data: "\x20" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1790 |     QTest::newRow(dataTag: "UseInteger:INT64_MIN" ) << QCborValue(std::numeric_limits<qint64>::min() + 0.) | 
| 1791 |                                           << raw(data: "\x3b\x7f\xff\xff\xff" "\xff\xff\xff\xff" ) | 
| 1792 |                                           << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1793 |  | 
| 1794 |     // but obviously non-integral or out of range floating point stay FP | 
| 1795 |     QTest::newRow(dataTag: "UseInteger:1.5" ) << QCborValue(1.5) << raw(data: "\xfb\x3f\xf8\0\0" "\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1796 |     QTest::newRow(dataTag: "UseInteger:-1.5" ) << QCborValue(-1.5) << raw(data: "\xfb\xbf\xf8\0\0" "\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1797 |     QTest::newRow(dataTag: "UseInteger:inf" ) << QCborValue(qInf()) << raw(data: "\xfb\x7f\xf0\0\0" "\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1798 |     QTest::newRow(dataTag: "UseInteger:-inf" ) << QCborValue(-qInf()) << raw(data: "\xfb\xff\xf0\0" "\0\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1799 |     QTest::newRow(dataTag: "UseInteger:nan" ) << QCborValue(qQNaN()) << raw(data: "\xfb\x7f\xf8\0\0" "\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1800 |     QTest::newRow(dataTag: "UseInteger:2^64" ) << QCborValue(18446744073709551616.0) << raw(data: "\xfb\x43\xf0\0\0" "\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1801 |     QTest::newRow(dataTag: "UseInteger:-2^65" ) << QCborValue(-2 * 18446744073709551616.0) << raw(data: "\xfb\xc4\0\0\0" "\0\0\0\0" ) << QCborValue::EncodingOptions(QCborValue::UseIntegers); | 
| 1802 | } | 
| 1803 |  | 
| 1804 | void tst_QCborValue::toCbor() | 
| 1805 | { | 
| 1806 |     QFETCH(QCborValue, v); | 
| 1807 |     QFETCH(QByteArray, result); | 
| 1808 |     QFETCH(QCborValue::EncodingOptions, options); | 
| 1809 |  | 
| 1810 |     QCOMPARE(v.toCbor(options), result); | 
| 1811 |  | 
| 1812 |     // in maps and arrays | 
| 1813 |     QCOMPARE(QCborArray{v}.toCborValue().toCbor(options), "\x81"  + result); | 
| 1814 |     QCOMPARE(QCborArray({v, v}).toCborValue().toCbor(options), | 
| 1815 |              "\x82"  + result + result); | 
| 1816 |     QCOMPARE(QCborMap({{1, v}}).toCborValue().toCbor(options), | 
| 1817 |              "\xa1\x01"  + result); | 
| 1818 |  | 
| 1819 |     // tagged | 
| 1820 |     QCborValue t(QCborKnownTags::Signature, v); | 
| 1821 |     QCOMPARE(t.toCbor(options), "\xd9\xd9\xf7"  + result); | 
| 1822 |     QCOMPARE(QCborArray({t, t}).toCborValue().toCbor(options), | 
| 1823 |              "\x82\xd9\xd9\xf7"  + result + "\xd9\xd9\xf7"  + result); | 
| 1824 |     QCOMPARE(QCborMap({{1, t}}).toCborValue().toCbor(options), | 
| 1825 |              "\xa1\x01\xd9\xd9\xf7"  + result); | 
| 1826 | } | 
| 1827 |  | 
| 1828 | void tst_QCborValue::toCborStreamWriter() | 
| 1829 | { | 
| 1830 |     QFETCH(QCborValue, v); | 
| 1831 |     QFETCH(QByteArray, result); | 
| 1832 |     QFETCH(QCborValue::EncodingOptions, options); | 
| 1833 |  | 
| 1834 |     QByteArray output; | 
| 1835 |     QBuffer buffer(&output); | 
| 1836 |     buffer.open(openMode: QIODevice::WriteOnly); | 
| 1837 |     QCborStreamWriter writer(&buffer); | 
| 1838 |  | 
| 1839 |     v.toCbor(writer, opt: options); | 
| 1840 |     QCOMPARE(buffer.pos(), result.size()); | 
| 1841 |     QCOMPARE(output, result); | 
| 1842 | } | 
| 1843 |  | 
| 1844 | void tst_QCborValue::fromCbor_data() | 
| 1845 | { | 
| 1846 |     addCommonCborData(); | 
| 1847 |  | 
| 1848 |     // chunked strings | 
| 1849 |     QTest::newRow(dataTag: "ByteArray:Chunked" ) << QCborValue(QByteArray("Hello" )) | 
| 1850 |                                         << raw(data: "\x5f\x43Hel\x42lo\xff" ); | 
| 1851 |     QTest::newRow(dataTag: "ByteArray:Chunked:Empty" ) << QCborValue(QByteArray()) << raw(data: "\x5f\xff" ); | 
| 1852 |     QTest::newRow(dataTag: "String:Chunked" ) << QCborValue("Hello" ) | 
| 1853 |                                     << raw(data: "\x7f\x63Hel\x62lo\xff" ); | 
| 1854 |     QTest::newRow(dataTag: "String:Chunked:Empty" ) << QCborValue(QString()) | 
| 1855 |                                     << raw(data: "\x7f\xff" ); | 
| 1856 |  | 
| 1857 |     QTest::newRow(dataTag: "DateTime:NoMilli" ) << QCborValue(QDateTime::fromSecsSinceEpoch(secs: 1515565477, spe: Qt::UTC)) | 
| 1858 |                                       << raw(data: "\xc0\x74"  "2018-01-10T06:24:37Z" ); | 
| 1859 |     // date-only is only permitted local time | 
| 1860 |     QTest::newRow(dataTag: "DateTime:NoTime:Local" ) << QCborValue(QDateTime(QDate(2020, 4, 15), QTime(0, 0), Qt::LocalTime)) | 
| 1861 |                                            << raw(data: "\xc0\x6a"  "2020-04-15" ); | 
| 1862 |     QTest::newRow(dataTag: "DateTime:24:00:00" ) << QCborValue(QDateTime(QDate(2020, 4, 16), QTime(0, 0), Qt::UTC)) | 
| 1863 |                                        << raw(data: "\xc0\x74"  "2020-04-15T24:00:00Z" ); | 
| 1864 |     QTest::newRow(dataTag: "DateTime:+00:00" ) << QCborValue(QDateTime::fromMSecsSinceEpoch(msecs: 1515565477125, spec: Qt::UTC)) | 
| 1865 |                                      << raw(data: "\xc0\x78\x1d"  "2018-01-10T06:24:37.125+00:00" ); | 
| 1866 |     QTest::newRow(dataTag: "DateTime:+01:00" ) << QCborValue(QDateTime::fromMSecsSinceEpoch(msecs: 1515565477125, spec: Qt::OffsetFromUTC, offsetFromUtc: 60*60)) | 
| 1867 |                                      << raw(data: "\xc0\x78\x1d"  "2018-01-10T07:24:37.125+01:00" ); | 
| 1868 |     QTest::newRow(dataTag: "UnixTime_t:Integer" ) << QCborValue(QDateTime::fromSecsSinceEpoch(secs: 1515565477, spe: Qt::UTC)) | 
| 1869 |                                         << raw(data: "\xc1\x1a\x5a\x55\xb1\xa5" ); | 
| 1870 |     QTest::newRow(dataTag: "UnixTime_t:Double" ) << QCborValue(QDateTime::fromMSecsSinceEpoch(msecs: 1515565477125, spec: Qt::UTC)) | 
| 1871 |                                        << raw(data: "\xc1\xfb\x41\xd6\x95\x6c" "\x69\x48\x00\x00" ); | 
| 1872 |  | 
| 1873 |     QTest::newRow(dataTag: "Url:NotNormalized" ) << QCborValue(QUrl("https://example.com/\xc2\xa9 " )) | 
| 1874 |                                        << raw(data: "\xd8\x20\x78\x1dHTTPS://EXAMPLE.COM/%c2%a9%20" ); | 
| 1875 |  | 
| 1876 |     QTest::newRow(dataTag: "Uuid:Zero" ) << QCborValue(QUuid()) << raw(data: "\xd8\x25\x40" ); | 
| 1877 |     QTest::newRow(dataTag: "Uuid:TooShort" ) << QCborValue(QUuid::fromRfc4122(raw(data: "\1\2\3\4" "\4\3\2\0" "\0\0\0\0" "\0\0\0\0" ))) | 
| 1878 |                                    << raw(data: "\xd8\x25\x47"  "\1\2\3\4\4\3\2" ); | 
| 1879 |     QTest::newRow(dataTag: "Uuid:TooLong" ) << QCborValue(QUuid::fromRfc4122(raw(data: "\1\2\3\4" "\4\3\2\0" "\0\0\0\0" "\0\0\0\1" ))) | 
| 1880 |                                    << raw(data: "\xd8\x25\x51"  "\1\2\3\4" "\4\3\2\0" "\0\0\0\0" "\0\0\0\1" "\2" ); | 
| 1881 | } | 
| 1882 |  | 
| 1883 | void fromCbor_common(void (*doCheck)(const QCborValue &, const QByteArray &)) | 
| 1884 | { | 
| 1885 |     QFETCH(QCborValue, v); | 
| 1886 |     QFETCH(QByteArray, result); | 
| 1887 |  | 
| 1888 |     doCheck(v, result); | 
| 1889 |     if (QTest::currentTestFailed()) | 
| 1890 |         return; | 
| 1891 |  | 
| 1892 |     // in an array | 
| 1893 |     doCheck(QCborArray{v}, "\x81"  + result); | 
| 1894 |     if (QTest::currentTestFailed()) | 
| 1895 |         return; | 
| 1896 |  | 
| 1897 |     doCheck(QCborArray{v, v}, "\x82"  + result + result); | 
| 1898 |     if (QTest::currentTestFailed()) | 
| 1899 |         return; | 
| 1900 |  | 
| 1901 |     // in a map | 
| 1902 |     doCheck(QCborMap{{1, v}}, "\xa1\1"  + result); | 
| 1903 |     if (QTest::currentTestFailed()) | 
| 1904 |         return; | 
| 1905 |  | 
| 1906 |     // undefined-length arrays and maps | 
| 1907 |     doCheck(QCborArray{v}, "\x9f"  + result + "\xff" ); | 
| 1908 |     if (QTest::currentTestFailed()) | 
| 1909 |         return; | 
| 1910 |     doCheck(QCborArray{v, v}, "\x9f"  + result + result + "\xff" ); | 
| 1911 |     if (QTest::currentTestFailed()) | 
| 1912 |         return; | 
| 1913 |     doCheck(QCborMap{{1, v}}, "\xbf\1"  + result + "\xff" ); | 
| 1914 |     if (QTest::currentTestFailed()) | 
| 1915 |         return; | 
| 1916 |  | 
| 1917 |     // tagged | 
| 1918 |     QCborValue t(QCborKnownTags::Signature, v); | 
| 1919 |     doCheck(t, "\xd9\xd9\xf7"  + result); | 
| 1920 |     if (QTest::currentTestFailed()) | 
| 1921 |         return; | 
| 1922 |  | 
| 1923 |     // in an array | 
| 1924 |     doCheck(QCborArray{t}, "\x81\xd9\xd9\xf7"  + result); | 
| 1925 |     if (QTest::currentTestFailed()) | 
| 1926 |         return; | 
| 1927 |  | 
| 1928 |     doCheck(QCborArray{t, t}, "\x82\xd9\xd9\xf7"  + result + "\xd9\xd9\xf7"  + result); | 
| 1929 |     if (QTest::currentTestFailed()) | 
| 1930 |         return; | 
| 1931 |  | 
| 1932 |     // in a map | 
| 1933 |     doCheck(QCborMap{{1, t}}, "\xa1\1\xd9\xd9\xf7"  + result); | 
| 1934 |     if (QTest::currentTestFailed()) | 
| 1935 |         return; | 
| 1936 | } | 
| 1937 |  | 
| 1938 | void tst_QCborValue::fromCbor() | 
| 1939 | { | 
| 1940 |     auto doCheck = [](const QCborValue &v, const QByteArray &result) { | 
| 1941 |         QCborParserError error; | 
| 1942 |         QCborValue decoded = QCborValue::fromCbor(ba: result, error: &error); | 
| 1943 |         QVERIFY2(error.error == QCborError(), qPrintable(error.errorString())); | 
| 1944 |         QCOMPARE(error.offset, result.size()); | 
| 1945 |         QVERIFY(decoded == v); | 
| 1946 |         QVERIFY(v == decoded); | 
| 1947 |     }; | 
| 1948 |  | 
| 1949 |     fromCbor_common(doCheck); | 
| 1950 | } | 
| 1951 |  | 
| 1952 | void tst_QCborValue::fromCborStreamReaderByteArray() | 
| 1953 | { | 
| 1954 |     auto doCheck = [](const QCborValue &expected, const QByteArray &data) { | 
| 1955 |         QCborStreamReader reader(data); | 
| 1956 |         QCborValue decoded = QCborValue::fromCbor(reader); | 
| 1957 |         QCOMPARE(reader.lastError(), QCborError()); | 
| 1958 |         QCOMPARE(reader.currentOffset(), data.size()); | 
| 1959 |         QVERIFY(decoded == expected); | 
| 1960 |         QVERIFY(expected == decoded); | 
| 1961 |     }; | 
| 1962 |  | 
| 1963 |     fromCbor_common(doCheck); | 
| 1964 | } | 
| 1965 |  | 
| 1966 | void tst_QCborValue::fromCborStreamReaderIODevice() | 
| 1967 | { | 
| 1968 |     auto doCheck = [](const QCborValue &expected, const QByteArray &data) { | 
| 1969 |         QBuffer buffer; | 
| 1970 |         buffer.setData(data); | 
| 1971 |         buffer.open(openMode: QIODevice::ReadOnly); | 
| 1972 |         QCborStreamReader reader(&buffer); | 
| 1973 |         QCborValue decoded = QCborValue::fromCbor(reader); | 
| 1974 |         QCOMPARE(reader.lastError(), QCborError()); | 
| 1975 |         QCOMPARE(reader.currentOffset(), data.size()); | 
| 1976 |         QVERIFY(decoded == expected); | 
| 1977 |         QVERIFY(expected == decoded); | 
| 1978 |         QCOMPARE(buffer.pos(), reader.currentOffset()); | 
| 1979 |     }; | 
| 1980 |  | 
| 1981 |     fromCbor_common(doCheck); | 
| 1982 | } | 
| 1983 |  | 
| 1984 | #include "../cborlargedatavalidation.cpp" | 
| 1985 |  | 
| 1986 | void tst_QCborValue::validation_data() | 
| 1987 | { | 
| 1988 |     // Add QCborStreamReader-specific limitations due to use of QByteArray and | 
| 1989 |     // QString, which are allocated by QArrayData::allocate(). | 
| 1990 |     const qsizetype MaxInvalid = std::numeric_limits<QByteArray::size_type>::max(); | 
| 1991 |     const qsizetype MinInvalid = MaxByteArraySize + 1 - sizeof(QByteArray::size_type); | 
| 1992 |     addValidationColumns(); | 
| 1993 |     addValidationData(minInvalid: MinInvalid); | 
| 1994 |     addValidationLargeData(minInvalid: MinInvalid, maxInvalid: MaxInvalid); | 
| 1995 |  | 
| 1996 |     // Chunked strings whose total overflows the limit, but each individual | 
| 1997 |     // chunk doesn't. 0x5a for 32-bit, 0x5b for 64-bit. | 
| 1998 |     char toolong[1 + sizeof(qsizetype)]; | 
| 1999 |     toolong[0] = sizeof(MinInvalid) > 4 ? 0x5b : 0x5a; | 
| 2000 |     qToBigEndian(src: MinInvalid - 1, dest: toolong + 1); | 
| 2001 |     QTest::addRow(format: "bytearray-2chunked+1-too-big-for-qbytearray-%llx" , MinInvalid) | 
| 2002 |             << ("\x5f\x41z"  + QByteArray(toolong, sizeof(toolong)) + '\xff') | 
| 2003 |             << 0 << CborErrorDataTooLarge; | 
| 2004 |     toolong[0] |= 0x20; | 
| 2005 |     QTest::addRow(format: "string-2chunked+1-too-big-for-qbytearray-%llx" , MinInvalid) | 
| 2006 |             << ("\x7f\x61z"  + QByteArray(toolong, sizeof(toolong)) + '\xff') | 
| 2007 |             << 0 << CborErrorDataTooLarge; | 
| 2008 |  | 
| 2009 |     // These tests say we have arrays and maps with very large item counts. | 
| 2010 |     // They are meant to ensure we don't pre-allocate a lot of memory | 
| 2011 |     // unnecessarily and possibly crash the application. The actual number of | 
| 2012 |     // elements in the stream is only 2, so we should get an unexpected EOF | 
| 2013 |     // error. QCborValue internally uses 16 bytes per element, so we get to 2 | 
| 2014 |     // GB at 2^27 elements (32-bit) or, theoretically, 2^63 bytes at 2^59 | 
| 2015 |     // elements (64-bit). | 
| 2016 |     if (sizeof(QVector<int>::size_type) == sizeof(int)) { | 
| 2017 |         // 32-bit sizes (Qt 5 and 32-bit platforms) | 
| 2018 |         QTest::addRow(format: "very-large-array-no-overflow" ) << raw(data: "\x9a\x07\xff\xff\xff"  "\0\0" ) << 0 << CborErrorUnexpectedEOF; | 
| 2019 |         QTest::addRow(format: "very-large-array-overflow1" ) << raw(data: "\x9a\x40\0\0\0"  "\0\0" ) << 0 << CborErrorUnexpectedEOF; | 
| 2020 |  | 
| 2021 |         // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements | 
| 2022 |         QTest::addRow(format: "very-large-array-overflow2" ) << raw(data: "\x9b\0\0\0\1" "\0\0\0\2"  "\0\0" ) << 0 << CborErrorDataTooLarge; | 
| 2023 |     } else { | 
| 2024 |         // 64-bit Qt 6 | 
| 2025 |         QTest::addRow(format: "very-large-array-no-overflow" ) << raw(data: "\x9b\x07\xff\xff\xff"  "\xff\xff\xff\xff"  "\0\0" ); | 
| 2026 |         QTest::addRow(format: "very-large-array-overflow" ) << raw(data: "\x9b\x40\0\0\0"  "\0\0\0\0"  "\0\0" ); | 
| 2027 |     } | 
| 2028 | } | 
| 2029 |  | 
| 2030 | void tst_QCborValue::validation() | 
| 2031 | { | 
| 2032 |     QFETCH(QByteArray, data); | 
| 2033 |     QFETCH(CborError, expectedError); | 
| 2034 |     QCborError error = { .c: QCborError::Code(expectedError) }; | 
| 2035 |  | 
| 2036 |     QCborParserError parserError; | 
| 2037 |     QCborValue decoded = QCborValue::fromCbor(ba: data, error: &parserError); | 
| 2038 |     QCOMPARE(parserError.error, error); | 
| 2039 |  | 
| 2040 |     if (data.startsWith(c: '\x81')) { | 
| 2041 |         // decode without the array prefix | 
| 2042 |         char *ptr = const_cast<char *>(data.constData()); | 
| 2043 |         QByteArray mid = QByteArray::fromRawData(ptr + 1, size: data.size() - 1); | 
| 2044 |         decoded = QCborValue::fromCbor(ba: mid, error: &parserError); | 
| 2045 |         QCOMPARE(parserError.error, error); | 
| 2046 |     } | 
| 2047 | } | 
| 2048 |  | 
| 2049 | void tst_QCborValue::extendedTypeValidation_data() | 
| 2050 | { | 
| 2051 |     QTest::addColumn<QByteArray>(name: "data" ); | 
| 2052 |     QTest::addColumn<QCborValue>(name: "expected" ); | 
| 2053 |  | 
| 2054 |     // QDateTime currently stores time in milliseconds, so make sure | 
| 2055 |     // we don't overflow | 
| 2056 |     { | 
| 2057 |         quint64 limit = std::numeric_limits<quint64>::max() / 1000; | 
| 2058 |         QTest::newRow(dataTag: "UnixTime_t:integer-overflow-positive" ) | 
| 2059 |                 << encode(a: 0xc1, a: 0x1b, a: limit + 1) | 
| 2060 |                 << QCborValue(QCborKnownTags::UnixTime_t, qint64(limit) + 1); | 
| 2061 |         QTest::newRow(dataTag: "UnixTime_t:integer-overflow-negative" ) | 
| 2062 |                 << encode(a: 0xc1, a: 0x3b, a: limit) | 
| 2063 |                 << QCborValue(QCborKnownTags::UnixTime_t, -qint64(limit) - 1); | 
| 2064 |  | 
| 2065 |         double fplimit = std::numeric_limits<qint64>::min() / (-1000.); // 2^63 ms | 
| 2066 |         QTest::newRow(dataTag: "UnixTime_t:fp-overflow-positive" ) | 
| 2067 |                 << encode(a: 0xc1, a: 0xfb, a: fplimit) | 
| 2068 |                 << QCborValue(QCborKnownTags::UnixTime_t, fplimit); | 
| 2069 |         QTest::newRow(dataTag: "UnixTime_t:fp-overflow-negative" ) | 
| 2070 |                 << encode(a: 0xc1, a: 0xfb, a: -fplimit) | 
| 2071 |                 << QCborValue(QCborKnownTags::UnixTime_t, -fplimit); | 
| 2072 |     } | 
| 2073 |  | 
| 2074 |     // But in fact, QCborValue stores date/times as their ISO textual | 
| 2075 |     // representation, which means it can't represent dates before year 1 or | 
| 2076 |     // after year 9999. | 
| 2077 |     { | 
| 2078 |         QDateTime dt(QDate(-1, 1, 1), QTime(0, 0), Qt::UTC); | 
| 2079 |         QTest::newRow(dataTag: "UnixTime_t:negative-year" ) | 
| 2080 |                 << encode(a: 0xc1, a: 0x3b, a: quint64(-dt.toSecsSinceEpoch()) - 1) | 
| 2081 |                 << QCborValue(QCborKnownTags::UnixTime_t, dt.toSecsSinceEpoch()); | 
| 2082 |  | 
| 2083 |         dt.setDate(QDate(10000, 1, 1)); | 
| 2084 |         QTest::newRow(dataTag: "UnixTime_t:year10k" ) | 
| 2085 |                 << encode(a: 0xc1, a: 0x1b, a: quint64(dt.toSecsSinceEpoch())) | 
| 2086 |                 << QCborValue(QCborKnownTags::UnixTime_t, dt.toSecsSinceEpoch()); | 
| 2087 |     } | 
| 2088 |  | 
| 2089 |     // Invalid ISO date/time strings | 
| 2090 |     { | 
| 2091 |         auto add = [](const char *tag, const char *str) { | 
| 2092 |             QByteArray raw; | 
| 2093 |             if (strlen(s: str) < 0x18) | 
| 2094 |                 raw = encode(a: 0xc0, a: 0x60 + int(strlen(s: str)), a: str); | 
| 2095 |             else | 
| 2096 |                 raw = encode(a: 0xc0, a: 0x78, a: quint8(strlen(s: str)), a: str); | 
| 2097 |             QTest::addRow(format: "DateTime:%s" , tag) | 
| 2098 |                     << raw << QCborValue(QCborKnownTags::DateTimeString, QString(str)); | 
| 2099 |         }; | 
| 2100 |         // tst_QDateTime::fromStringDateFormat has more tests | 
| 2101 |         add("junk" , "jjj" ); | 
| 2102 |         add("zoned-date-only" , "2020-04-15Z" ); | 
| 2103 |         add("month-13" , "2020-13-01T00:00:00Z" ); | 
| 2104 |         add("negative-month" , "2020--1-01T00:00:00Z" ); | 
| 2105 |         add("jan-32" , "2020-01-32T00:00:00Z" ); | 
| 2106 |         add("apr-31" , "2020-04-31T00:00:00Z" ); | 
| 2107 |         add("feb-30" , "2020-02-30T00:00:00Z" ); | 
| 2108 |         add("feb-29-nonleap" , "2021-02-29T00:00:00Z" ); | 
| 2109 |         add("negative-day" , "2020-01--1T00:00:00Z" ); | 
| 2110 |         add("bad-separator" , "2020-04-15j13:30:59Z" ); | 
| 2111 |         add("hour-25" , "2020-04-15T25:00:00Z" ); | 
| 2112 |         add("negative-hour" , "2020-04-15T-1:00:00Z" ); | 
| 2113 |         add("minute-60" , "2020-04-15T23:60:00Z" ); | 
| 2114 |         add("negative-minute" , "2020-04-15T23:-1:00Z" ); | 
| 2115 |         add("second-60" , "2020-04-15T23:59:60Z" );   // not a leap second | 
| 2116 |         add("negative-second" , "2020-04-15T23:59:-1Z" ); | 
| 2117 |         add("negative-milli" , "2020-04-15T23.59:59.-1Z" ); | 
| 2118 |  | 
| 2119 |         // walking null | 
| 2120 |         char dt[] = "2020-04-15T17:33:32.125Z" ; | 
| 2121 |         quint8 len = strlen(s: dt); | 
| 2122 |         for (int i = 0; i < int(len); ++i) { | 
| 2123 |             char c = '\0'; | 
| 2124 |             qSwap(value1&: c, value2&: dt[i]); | 
| 2125 |             QTest::addRow(format: "DateTime:Null-at-%d" , i) | 
| 2126 |                     << encode(a: 0xc0, a: 0x78, a: len) + QByteArray(dt, len) | 
| 2127 |                     << QCborValue(QCborKnownTags::DateTimeString, QLatin1String(dt, len)); | 
| 2128 |             qSwap(value1&: c, value2&: dt[i]); | 
| 2129 |         } | 
| 2130 |     } | 
| 2131 |  | 
| 2132 |     // Improperly-encoded URLs | 
| 2133 |     { | 
| 2134 |         const char badurl[] = "%zz" ; | 
| 2135 |         QTest::newRow(dataTag: "Url:Invalid" ) | 
| 2136 |                 << encode(a: 0xd8, a: int(QCborKnownTags::Url), a: 0x60 + int(strlen(s: badurl)), a: badurl) | 
| 2137 |                 << QCborValue(QCborKnownTags::Url, QLatin1String(badurl)); | 
| 2138 |     } | 
| 2139 | } | 
| 2140 |  | 
| 2141 | void tst_QCborValue::extendedTypeValidation() | 
| 2142 | { | 
| 2143 |     QFETCH(QByteArray, data); | 
| 2144 |     QFETCH(QCborValue, expected); | 
| 2145 |  | 
| 2146 |     QCborParserError error; | 
| 2147 |     QCborValue decoded = QCborValue::fromCbor(ba: data, error: &error); | 
| 2148 |     QVERIFY2(error.error == QCborError(), qPrintable(error.errorString())); | 
| 2149 |     QCOMPARE(error.offset, data.size()); | 
| 2150 |     QCOMPARE(decoded, expected); | 
| 2151 |  | 
| 2152 |     QByteArray encoded = decoded.toCbor(); | 
| 2153 | #if QT_VERSION < QT_VERSION_CHECK(6,0,0) | 
| 2154 |     // behavior change, see qdatetime.cpp:fromIsoTimeString | 
| 2155 |     QEXPECT_FAIL("DateTime:Null-at-19" , "QDateTime parsing fixed, but only in 6.0" , Abort); | 
| 2156 | #endif | 
| 2157 |     QCOMPARE(encoded, data); | 
| 2158 | } | 
| 2159 |  | 
| 2160 | void tst_QCborValue::hugeDeviceValidation_data() | 
| 2161 | { | 
| 2162 |     // because QCborValue will attempt to retain the original string in UTF-8, | 
| 2163 |     // the size which it can't store is actually the byte array size | 
| 2164 |     addValidationHugeDevice(byteArrayInvalid: MaxByteArraySize + 1, stringInvalid: MaxByteArraySize + 1); | 
| 2165 | } | 
| 2166 |  | 
| 2167 | void tst_QCborValue::hugeDeviceValidation() | 
| 2168 | { | 
| 2169 |     QFETCH(QSharedPointer<QIODevice>, device); | 
| 2170 |     QFETCH(CborError, expectedError); | 
| 2171 |     QCborError error = { .c: QCborError::Code(expectedError) }; | 
| 2172 |  | 
| 2173 |     device->open(mode: QIODevice::ReadOnly | QIODevice::Unbuffered); | 
| 2174 |     QCborStreamReader reader(device.data()); | 
| 2175 |     QCborValue decoded = QCborValue::fromCbor(reader); | 
| 2176 |     QCOMPARE(reader.lastError(), error); | 
| 2177 | } | 
| 2178 |  | 
| 2179 | void tst_QCborValue::recursionLimit_data() | 
| 2180 | { | 
| 2181 |     constexpr int RecursionAttempts = 4096; | 
| 2182 |     QTest::addColumn<QByteArray>(name: "data" ); | 
| 2183 |     QByteArray arrays(RecursionAttempts, char(0x81)); | 
| 2184 |     QByteArray _arrays(RecursionAttempts, char(0x9f)); | 
| 2185 |     QByteArray maps(RecursionAttempts, char(0xa1)); | 
| 2186 |     QByteArray _maps(RecursionAttempts, char(0xbf)); | 
| 2187 |     QByteArray tags(RecursionAttempts, char(0xc0)); | 
| 2188 |  | 
| 2189 |     QTest::newRow(dataTag: "array-nesting-too-deep" ) << arrays; | 
| 2190 |     QTest::newRow(dataTag: "_array-nesting-too-deep" ) << _arrays; | 
| 2191 |     QTest::newRow(dataTag: "map-nesting-too-deep" ) << maps; | 
| 2192 |     QTest::newRow(dataTag: "_map-nesting-too-deep" ) << _maps; | 
| 2193 |     QTest::newRow(dataTag: "tag-nesting-too-deep" ) << tags; | 
| 2194 |  | 
| 2195 |     QByteArray mixed(5 * RecursionAttempts, Qt::Uninitialized); | 
| 2196 |     char *ptr = mixed.data(); | 
| 2197 |     for (int i = 0; i < RecursionAttempts; ++i) { | 
| 2198 |         quint8 type = qBound(min: quint8(QCborStreamReader::Array), val: quint8(i & 0x80), max: quint8(QCborStreamReader::Tag)); | 
| 2199 |         quint8 additional_info = i & 0x1f; | 
| 2200 |         if (additional_info == 0x1f) | 
| 2201 |             (void)additional_info;      // leave it | 
| 2202 |         else if (additional_info > 0x1a) | 
| 2203 |             additional_info = 0x1a; | 
| 2204 |         else if (additional_info < 1) | 
| 2205 |             additional_info = 1; | 
| 2206 |  | 
| 2207 |         *ptr++ = type | additional_info; | 
| 2208 |         if (additional_info == 0x18) { | 
| 2209 |             *ptr++ = uchar(i); | 
| 2210 |         } else if (additional_info == 0x19) { | 
| 2211 |             qToBigEndian(src: ushort(i), dest: ptr); | 
| 2212 |             ptr += 2; | 
| 2213 |         } else if (additional_info == 0x1a) { | 
| 2214 |             qToBigEndian(src: uint(i), dest: ptr); | 
| 2215 |             ptr += 4; | 
| 2216 |         } | 
| 2217 |     } | 
| 2218 |  | 
| 2219 |     QTest::newRow(dataTag: "mixed-nesting-too-deep" ) << mixed; | 
| 2220 | } | 
| 2221 |  | 
| 2222 | void tst_QCborValue::recursionLimit() | 
| 2223 | { | 
| 2224 |     QFETCH(QByteArray, data); | 
| 2225 |  | 
| 2226 |     QCborParserError error; | 
| 2227 |     QCborValue decoded = QCborValue::fromCbor(ba: data, error: &error); | 
| 2228 |     QCOMPARE(error.error, QCborError::NestingTooDeep); | 
| 2229 | } | 
| 2230 |  | 
| 2231 | void tst_QCborValue::toDiagnosticNotation_data() | 
| 2232 | { | 
| 2233 |     QTest::addColumn<QCborValue>(name: "v" ); | 
| 2234 |     QTest::addColumn<int>(name: "opts" ); | 
| 2235 |     QTest::addColumn<QString>(name: "expected" ); | 
| 2236 |     QDateTime dt = QDateTime::currentDateTimeUtc(); | 
| 2237 |     QUuid uuid = QUuid::createUuid(); | 
| 2238 |  | 
| 2239 |     QMetaEnum me = QMetaEnum::fromType<QCborValue::Type>(); | 
| 2240 |     auto add = [me](const QCborValue &v, const QString &exp) { | 
| 2241 |         auto addRow = [=](const char *prefix) -> QTestData & { | 
| 2242 |             QCborValue::Type t = v.type(); | 
| 2243 |             if (t == QCborValue::Integer) | 
| 2244 |                 return QTest::addRow(format: "%sInteger:%lld" , prefix, v.toInteger()); | 
| 2245 |             if (t == QCborValue::Double) | 
| 2246 |                 return QTest::addRow(format: "%sDouble:%g" , prefix, v.toDouble()); | 
| 2247 |             if (t == QCborValue::ByteArray) | 
| 2248 |                 return QTest::addRow(format: "%sByteArray:%d" , prefix, v.toByteArray().size()); | 
| 2249 |             if (t == QCborValue::String) | 
| 2250 |                 return QTest::addRow(format: "%sString:%d" , prefix, v.toString().size()); | 
| 2251 |  | 
| 2252 |             QByteArray typeString = me.valueToKey(value: t); | 
| 2253 |             Q_ASSERT(!typeString.isEmpty()); | 
| 2254 |             return QTest::newRow(dataTag: prefix + typeString); | 
| 2255 |         }; | 
| 2256 |         addRow("" ) << v << int(QCborValue::DiagnosticNotationOptions{}) << exp; | 
| 2257 |         addRow("LW:" ) << v << int(QCborValue::LineWrapped) << exp; | 
| 2258 |         addRow("Array:" ) << QCborValue(QCborArray{v}) << int(QCborValue::DiagnosticNotationOptions{}) << '[' + exp + ']'; | 
| 2259 |         addRow("Mapped:" ) << QCborValue(QCborMap{{2, v}}) << int(QCborValue::DiagnosticNotationOptions{}) << "{2: "  + exp + '}'; | 
| 2260 |         addRow("Mapping:" ) << QCborValue(QCborMap{{v, 2}}) << int(QCborValue::DiagnosticNotationOptions{}) << '{' + exp + ": 2}" ; | 
| 2261 |     }; | 
| 2262 |  | 
| 2263 |     // empty arrays and maps | 
| 2264 |     QTest::newRow(dataTag: "EmptyArray" ) | 
| 2265 |             << QCborValue(QCborArray()) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2266 |             << "[]" ; | 
| 2267 |     QTest::newRow(dataTag: "EmptyMap" ) | 
| 2268 |             << QCborValue(QCborMap()) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2269 |             << "{}" ; | 
| 2270 |  | 
| 2271 |     add(QCborValue(), "undefined" ); | 
| 2272 |     add(QCborValue::Null, "null" ); | 
| 2273 |     add(false, "false" ); | 
| 2274 |     add(true, "true" ); | 
| 2275 |     add(QCborSimpleType(0), "simple(0)" ); | 
| 2276 |     QTest::newRow(dataTag: "SimpleType-255" ) | 
| 2277 |             << QCborValue(QCborSimpleType(255)) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2278 |             << "simple(255)" ; | 
| 2279 |     add(0, "0" ); | 
| 2280 |     add(1, "1" ); | 
| 2281 |     add(-1, "-1" ); | 
| 2282 |     add(std::numeric_limits<qint64>::min(), QString::number(std::numeric_limits<qint64>::min())); | 
| 2283 |     add(std::numeric_limits<qint64>::max(), QString::number(std::numeric_limits<qint64>::max())); | 
| 2284 |     add(0., "0.0" ); | 
| 2285 |     add(1.25, "1.25" ); | 
| 2286 |     add(-1.25, "-1.25" ); | 
| 2287 |     add(qInf(), "inf" ); | 
| 2288 |     add(-qInf(), "-inf" ); | 
| 2289 |     add(qQNaN(), "nan" ); | 
| 2290 |     add(QByteArray(), "h''" ); | 
| 2291 |     add(QByteArray("Hello" ), "h'48656c6c6f'" ); | 
| 2292 |     add(QLatin1String(), QLatin1String("\"\"" )); | 
| 2293 |     add("Hello" , "\"Hello\"" ); | 
| 2294 |     add("\"Hello\\World\"" , "\"\\\"Hello\\\\World\\\"\"" ); | 
| 2295 |     add(QCborValue(dt), "0(\""  + dt.toString(format: Qt::ISODateWithMs) + "\")" ); | 
| 2296 |     add(QCborValue(QUrl("http://example.com" )), "32(\"http://example.com\")" ); | 
| 2297 |     add(QCborValue(QRegularExpression("^.*$" )), "35(\"^.*$\")" ); | 
| 2298 |     add(QCborValue(uuid), "37(h'"  + uuid.toString(mode: QUuid::Id128) + "')" ); | 
| 2299 |  | 
| 2300 |     // arrays and maps with more than one element | 
| 2301 |     QTest::newRow(dataTag: "2Array" ) | 
| 2302 |             << QCborValue(QCborArray{0, 1}) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2303 |             << "[0, 1]" ; | 
| 2304 |     QTest::newRow(dataTag: "2Map" ) | 
| 2305 |             << QCborValue(QCborMap{{0, 1}, {"foo" , "bar" }}) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2306 |             << "{0: 1, \"foo\": \"bar\"}" ; | 
| 2307 |  | 
| 2308 |     // line wrapping in arrays and maps | 
| 2309 |     QTest::newRow(dataTag: "LW:EmptyArray" ) | 
| 2310 |             << QCborValue(QCborArray()) << int(QCborValue::LineWrapped) | 
| 2311 |             << "[\n]" ; | 
| 2312 |     QTest::newRow(dataTag: "LW:EmptyMap" ) | 
| 2313 |             << QCborValue(QCborMap()) << int(QCborValue::LineWrapped) | 
| 2314 |             << "{\n}" ; | 
| 2315 |     QTest::newRow(dataTag: "LW:Array:Integer:0" ) | 
| 2316 |             << QCborValue(QCborArray{0}) << int(QCborValue::LineWrapped) | 
| 2317 |             << "[\n    0\n]" ; | 
| 2318 |     QTest::newRow(dataTag: "LW:Array:String:5" ) | 
| 2319 |             << QCborValue(QCborArray{"Hello" }) << int(QCborValue::LineWrapped) | 
| 2320 |             << "[\n    \"Hello\"\n]" ; | 
| 2321 |     QTest::newRow(dataTag: "LW:Map:0-0" ) | 
| 2322 |             << QCborValue(QCborMap{{0, 0}}) << int(QCborValue::LineWrapped) | 
| 2323 |             << "{\n    0: 0\n}" ; | 
| 2324 |     QTest::newRow(dataTag: "LW:Map:String:5" ) | 
| 2325 |             << QCborValue(QCborMap{{0, "Hello" }}) << int(QCborValue::LineWrapped) | 
| 2326 |             << "{\n    0: \"Hello\"\n}" ; | 
| 2327 |     QTest::newRow(dataTag: "LW:2Array" ) | 
| 2328 |             << QCborValue(QCborArray{0, 1}) << int(QCborValue::LineWrapped) | 
| 2329 |             << "[\n    0,\n    1\n]" ; | 
| 2330 |     QTest::newRow(dataTag: "LW:2Map" ) | 
| 2331 |             << QCborValue(QCborMap{{0, 0}, {"foo" , "bar" }}) << int(QCborValue::LineWrapped) | 
| 2332 |             << "{\n    0: 0,\n    \"foo\": \"bar\"\n}" ; | 
| 2333 |  | 
| 2334 |     // nested arrays and maps | 
| 2335 |     QTest::newRow(dataTag: "Array:EmptyArray" ) | 
| 2336 |             << QCborValue(QCborArray() << QCborArray()) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2337 |             << "[[]]" ; | 
| 2338 |     QTest::newRow(dataTag: "Array:EmptyMap" ) | 
| 2339 |             << QCborValue(QCborArray() << QCborMap()) << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2340 |             << "[{}]" ; | 
| 2341 |     QTest::newRow(dataTag: "LW:Array:EmptyArray" ) | 
| 2342 |             << QCborValue(QCborArray() << QCborArray()) << int(QCborValue::LineWrapped) | 
| 2343 |             << "[\n    [\n    ]\n]" ; | 
| 2344 |     QTest::newRow(dataTag: "LW:Array:EmptyMap" ) | 
| 2345 |             << QCborValue(QCborArray() << QCborMap()) << int(QCborValue::LineWrapped) | 
| 2346 |             << "[\n    {\n    }\n]" ; | 
| 2347 |     QTest::newRow(dataTag: "LW:Array:2Array" ) | 
| 2348 |             << QCborValue(QCborArray() << QCborArray{0, 1}) << int(QCborValue::LineWrapped) | 
| 2349 |             << "[\n    [\n        0,\n        1\n    ]\n]" ; | 
| 2350 |     QTest::newRow(dataTag: "LW:Map:2Array" ) | 
| 2351 |             << QCborValue(QCborMap{{0, QCborArray{0, 1}}}) << int(QCborValue::LineWrapped) | 
| 2352 |             << "{\n    0: [\n        0,\n        1\n    ]\n}" ; | 
| 2353 |     QTest::newRow(dataTag: "LW:Map:2Map" ) | 
| 2354 |             << QCborValue(QCborMap{{-1, QCborMap{{0, 0}, {"foo" , "bar" }}}}) << int(QCborValue::LineWrapped) | 
| 2355 |             << "{\n    -1: {\n        0: 0,\n        \"foo\": \"bar\"\n    }\n}" ; | 
| 2356 |  | 
| 2357 |     // string escaping | 
| 2358 |     QTest::newRow(dataTag: "String:escaping" ) | 
| 2359 |             << QCborValue("\1\a\b\t\f\r\n\v\x1f\x7f \"\xc2\xa0\xe2\x82\xac\xf0\x90\x80\x80\\\"" ) | 
| 2360 |             << int(QCborValue::DiagnosticNotationOptions{}) | 
| 2361 |             << "\"\\u0001\\a\\b\\t\\f\\r\\n\\v\\u001F\\u007F \\\"\\u00A0\\u20AC\\U00010000\\\\\\\"\"" ; | 
| 2362 |  | 
| 2363 |     // extended formatting for byte arrays | 
| 2364 |     QTest::newRow(dataTag: "Extended:ByteArray:0" ) | 
| 2365 |             << QCborValue(QByteArray()) << int(QCborValue::ExtendedFormat) | 
| 2366 |             << "h''" ; | 
| 2367 |     QTest::newRow(dataTag: "Extended:ByteArray:5" ) | 
| 2368 |             << QCborValue(QByteArray("Hello" )) << int(QCborValue::ExtendedFormat) | 
| 2369 |             << "h'48 65 6c 6c 6f'" ; | 
| 2370 |     QTest::newRow(dataTag: "Extended:ByteArray:Base64url" ) | 
| 2371 |             << QCborValue(QCborKnownTags::ExpectedBase64url, QByteArray("\xff\xef" )) | 
| 2372 |             << int(QCborValue::ExtendedFormat) << "21(b64'_-8')" ; | 
| 2373 |     QTest::newRow(dataTag: "Extended:ByteArray:Base64" ) | 
| 2374 |             << QCborValue(QCborKnownTags::ExpectedBase64, QByteArray("\xff\xef" )) | 
| 2375 |             << int(QCborValue::ExtendedFormat) << "22(b64'/+8=')" ; | 
| 2376 |  | 
| 2377 |     // formatting applies through arrays too | 
| 2378 |     QTest::newRow(dataTag: "Extended:Array:ByteArray:Base64url" ) | 
| 2379 |             << QCborValue(QCborKnownTags::ExpectedBase64url, QCborArray{QByteArray("\xff\xef" )}) | 
| 2380 |             << int(QCborValue::ExtendedFormat) << "21([b64'_-8'])" ; | 
| 2381 |     // and only the innermost applies | 
| 2382 |     QTest::newRow(dataTag: "ByteArray:multiple-tags" ) | 
| 2383 |             << QCborValue(QCborKnownTags::ExpectedBase64url, | 
| 2384 |                           QCborArray{QCborValue(QCborKnownTags::ExpectedBase16, QByteArray("Hello" )), | 
| 2385 |                           QByteArray("\xff\xef" )}) | 
| 2386 |             << int(QCborValue::ExtendedFormat) << "21([23(h'48 65 6c 6c 6f'), b64'_-8'])" ; | 
| 2387 | } | 
| 2388 |  | 
| 2389 | void tst_QCborValue::toDiagnosticNotation() | 
| 2390 | { | 
| 2391 |     QFETCH(QCborValue, v); | 
| 2392 |     QFETCH(QString, expected); | 
| 2393 |     QFETCH(int, opts); | 
| 2394 |  | 
| 2395 |     QString result = v.toDiagnosticNotation(opts: QCborValue::DiagnosticNotationOptions(opts)); | 
| 2396 |     QCOMPARE(result, expected); | 
| 2397 | } | 
| 2398 |  | 
| 2399 |  | 
| 2400 | void tst_QCborValue::datastreamSerialization_data() | 
| 2401 | { | 
| 2402 |     addCommonCborData(); | 
| 2403 | } | 
| 2404 |  | 
| 2405 | void tst_QCborValue::datastreamSerialization() | 
| 2406 | { | 
| 2407 |     QFETCH(QCborValue, v); | 
| 2408 |     QByteArray buffer; | 
| 2409 |     { | 
| 2410 |         QDataStream save(&buffer, QIODevice::WriteOnly); | 
| 2411 |         save << v; | 
| 2412 |         QDataStream load(buffer); | 
| 2413 |         QCborValue output; | 
| 2414 |         load >> output; | 
| 2415 |         QCOMPARE(output, v); | 
| 2416 |     } | 
| 2417 |     if (v.isArray()) { | 
| 2418 |         QCborArray array = v.toArray(); | 
| 2419 |         QDataStream save(&buffer, QIODevice::WriteOnly); | 
| 2420 |         save << array; | 
| 2421 |         QDataStream load(buffer); | 
| 2422 |         QCborValue output; | 
| 2423 |         load >> output; | 
| 2424 |         QCOMPARE(output, array); | 
| 2425 |     } else if (v.isMap()) { | 
| 2426 |         QCborMap map = v.toMap(); | 
| 2427 |         QDataStream save(&buffer, QIODevice::WriteOnly); | 
| 2428 |         save << map; | 
| 2429 |         QDataStream load(buffer); | 
| 2430 |         QCborValue output; | 
| 2431 |         load >> output; | 
| 2432 |         QCOMPARE(output, map); | 
| 2433 |     } | 
| 2434 | } | 
| 2435 |  | 
| 2436 | void tst_QCborValue::streamVariantSerialization() | 
| 2437 | { | 
| 2438 |     // Check interface only, implementation is tested through to and from | 
| 2439 |     // cbor functions. | 
| 2440 |     QByteArray buffer; | 
| 2441 |     { | 
| 2442 |         QCborArray array{665, 666, 667}; | 
| 2443 |         QVariant output; | 
| 2444 |         QVariant variant = QVariant::fromValue(value: array); | 
| 2445 |         QDataStream save(&buffer, QIODevice::WriteOnly); | 
| 2446 |         save << variant; | 
| 2447 |         QDataStream load(buffer); | 
| 2448 |         load >> output; | 
| 2449 |         QCOMPARE(output.userType(), QMetaType::QCborArray); | 
| 2450 |         QCOMPARE(qvariant_cast<QCborArray>(output), array); | 
| 2451 |     } | 
| 2452 |     { | 
| 2453 |         QCborMap obj{{"foo" , 42}}; | 
| 2454 |         QVariant output; | 
| 2455 |         QVariant variant = QVariant::fromValue(value: obj); | 
| 2456 |         QDataStream save(&buffer, QIODevice::WriteOnly); | 
| 2457 |         save << variant; | 
| 2458 |         QDataStream load(buffer); | 
| 2459 |         load >> output; | 
| 2460 |         QCOMPARE(output.userType(), QMetaType::QCborMap); | 
| 2461 |         QCOMPARE(qvariant_cast<QCborMap>(output), obj); | 
| 2462 |     } | 
| 2463 |     { | 
| 2464 |         QCborValue value{42}; | 
| 2465 |         QVariant output; | 
| 2466 |         QVariant variant = QVariant::fromValue(value); | 
| 2467 |         QDataStream save(&buffer, QIODevice::WriteOnly); | 
| 2468 |         save << variant; | 
| 2469 |         QDataStream load(buffer); | 
| 2470 |         load >> output; | 
| 2471 |         QCOMPARE(output.userType(), QMetaType::QCborValue); | 
| 2472 |         QCOMPARE(qvariant_cast<QCborValue>(output), value); | 
| 2473 |     } | 
| 2474 | } | 
| 2475 |  | 
| 2476 | QTEST_MAIN(tst_QCborValue) | 
| 2477 |  | 
| 2478 | #include "tst_qcborvalue.moc" | 
| 2479 |  |