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
45Q_DECLARE_METATYPE(QCborKnownTags)
46Q_DECLARE_METATYPE(QCborValue)
47Q_DECLARE_METATYPE(QCborValue::EncodingOptions)
48
49class tst_QCborValue : public QObject
50{
51 Q_OBJECT
52
53private 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
125namespace SimpleEncodeToCbor {
126inline size_t lengthOf(int)
127{
128 return 1; // encode as byte
129}
130
131template <unsigned N> inline size_t lengthOf(const char (&)[N])
132{
133 return N - 1;
134}
135
136
137inline size_t lengthOf(const char *str)
138{
139 return strlen(s: str);
140}
141
142template <typename T> inline size_t lengthOf(T)
143{
144 return sizeof(T);
145}
146
147static void encodeOneAt(char *ptr, int v, size_t)
148{
149 // encode as byte
150 *ptr = char(v);
151}
152
153static void encodeOneAt(char *ptr, const char *v, size_t size)
154{
155 memcpy(dest: ptr, src: v, n: size);
156}
157
158template <typename T>
159static typename std::enable_if<std::is_unsigned<T>::value>::type
160encodeOneAt(char *ptr, T v, size_t)
161{
162 qToBigEndian(v, ptr);
163}
164
165template <typename T>
166static typename std::enable_if<std::is_floating_point<T>::value ||
167 std::is_same<T, qfloat16>::value>::type
168encodeOneAt(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
175static char *encodeAt(char *ptr)
176{
177 return ptr;
178}
179
180template <typename Arg0, typename... Args>
181static 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
189template <typename... Args>
190static 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
206struct 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};
213Q_DECLARE_METATYPE(SimpleTypeWrapper)
214
215void 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
277static 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
342void 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
351void 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
385void 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
446void 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
466void 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
508void 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
552void 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
600void 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
610void 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
620void 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
641void 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
662void 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
682void 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
722void 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
794void 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
894void 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
975void 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
1028void 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
1135void 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
1205void 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
1215void 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
1247void 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
1279void 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
1309void 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
1330void 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
1369void 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
1388void 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
1408void 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
1437void 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
1504void 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
1573void 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
1655static 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
1772void 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
1804void 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
1828void 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
1844void 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
1883void 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
1938void 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
1952void 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
1966void 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
1986void 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
2030void 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
2049void 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
2141void 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
2160void 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
2167void 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
2179void 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
2222void 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
2231void 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
2389void 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
2400void tst_QCborValue::datastreamSerialization_data()
2401{
2402 addCommonCborData();
2403}
2404
2405void 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
2436void 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
2476QTEST_MAIN(tst_QCborValue)
2477
2478#include "tst_qcborvalue.moc"
2479

source code of qtbase/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp