1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 Intel Corporation
4 **
5 ** Permission is hereby granted, free of charge, to any person obtaining a copy
6 ** of this software and associated documentation files (the "Software"), to deal
7 ** in the Software without restriction, including without limitation the rights
8 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 ** copies of the Software, and to permit persons to whom the Software is
10 ** furnished to do so, subject to the following conditions:
11 **
12 ** The above copyright notice and this permission notice shall be included in
13 ** all copies or substantial portions of the Software.
14 **
15 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 ** THE SOFTWARE.
22 **
23 ****************************************************************************/
24
25 #include <QtTest>
26 #include "cbor.h"
27 #include "cborjson.h"
28 #include <locale.h>
29
30 extern "C" FILE *open_memstream(char **bufptr, size_t *sizeptr);
31
32 class tst_ToJson : public QObject
33 {
34 Q_OBJECT
35 private slots:
36 void initTestCase();
37
38 void fixed_data();
39 void fixed();
40 void textstrings_data();
textstrings()41 void textstrings() { fixed(); }
42 void nonjson_data();
nonjson()43 void nonjson() { fixed(); }
44 void bytestrings_data();
bytestrings()45 void bytestrings() { fixed(); }
46 void emptyContainers_data();
emptyContainers()47 void emptyContainers() { fixed(); }
48 void arrays_data();
49 void arrays();
nestedArrays_data()50 void nestedArrays_data() { arrays_data(); }
51 void nestedArrays();
maps_data()52 void maps_data() { arrays_data(); }
53 void maps();
nestedMaps_data()54 void nestedMaps_data() { maps_data(); }
55 void nestedMaps();
56 void nonStringKeyMaps_data();
57 void nonStringKeyMaps();
58
59 void tagsToObjects_data();
60 void tagsToObjects();
61 void taggedByteStringsToBase16_data();
62 void taggedByteStringsToBase16();
taggedByteStringsToBase64_data()63 void taggedByteStringsToBase64_data() { taggedByteStringsToBase16_data(); }
64 void taggedByteStringsToBase64();
taggedByteStringsToBigNum_data()65 void taggedByteStringsToBigNum_data() { taggedByteStringsToBase16_data(); }
66 void taggedByteStringsToBigNum();
67 void otherTags_data();
68 void otherTags();
69
70 void metaData_data();
71 void metaData();
metaDataAndTagsToObjects_data()72 void metaDataAndTagsToObjects_data() { tagsToObjects_data(); }
73 void metaDataAndTagsToObjects();
74 void metaDataForKeys_data();
75 void metaDataForKeys();
76 };
77 #include "tst_tojson.moc"
78
raw(const char (& data)[N])79 template <size_t N> QByteArray raw(const char (&data)[N])
80 {
81 return QByteArray::fromRawData(data, N - 1);
82 }
83
addColumns()84 void addColumns()
85 {
86 QTest::addColumn<QByteArray>("data");
87 QTest::addColumn<QString>("expected");
88 }
89
addFixedData()90 void addFixedData()
91 {
92 // unsigned integers
93 QTest::newRow("0") << raw("\x00") << "0";
94 QTest::newRow("1") << raw("\x01") << "1";
95 QTest::newRow("2^53-1") << raw("\x1b\0\x1f\xff\xff""\xff\xff\xff\xff") << "9007199254740991";
96 QTest::newRow("2^64-epsilon") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xf8\x00") << "18446744073709549568";
97
98 // negative integers
99 QTest::newRow("-1") << raw("\x20") << "-1";
100 QTest::newRow("-2") << raw("\x21") << "-2";
101 QTest::newRow("-2^53+1") << raw("\x3b\0\x1f\xff\xff""\xff\xff\xff\xfe") << "-9007199254740991";
102 QTest::newRow("-2^64+epsilon") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xf8\x00") << "-18446744073709549568";
103
104 QTest::newRow("false") << raw("\xf4") << "false";
105 QTest::newRow("true") << raw("\xf5") << "true";
106 QTest::newRow("null") << raw("\xf6") << "null";
107
108 QTest::newRow("0.f16") << raw("\xf9\0\0") << "0";
109 QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "0";
110 QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "0";
111 QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "-1";
112 QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "-1";
113 QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "-1";
114 QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "16777215";
115 QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "16777215";
116 QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << "-16777215";
117 QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "-16777215";
118
119 QTest::newRow("0.5f16") << raw("\xf9\x38\0") << "0.5";
120 QTest::newRow("0.5f") << raw("\xfa\x3f\0\0\0") << "0.5";
121 QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << "0.5";
122 QTest::newRow("2.f^24-1") << raw("\xfa\x4b\x7f\xff\xff") << "16777215";
123 QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "9007199254740991";
124 QTest::newRow("2.f^64-epsilon") << raw("\xfa\x5f\x7f\xff\xff") << "18446742974197923840";
125 QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "18446744073709549568";
126 QTest::newRow("2.f^64") << raw("\xfa\x5f\x80\0\0") << "1.8446744073709552e+19";
127 QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << "1.8446744073709552e+19";
128
129 // infinities and NaN are not supported in JSON, they convert to null
130 QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "null";
131 QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "null";
132 QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "null";
133 QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "null";
134 QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "null";
135 QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "null";
136 QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "null";
137 QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "null";
138 QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "null";
139 }
140
addTextStringsData()141 void addTextStringsData()
142 {
143 QTest::newRow("emptytextstring") << raw("\x60") << "\"\"";
144 QTest::newRow("textstring1") << raw("\x61 ") << "\" \"";
145 QTest::newRow("textstring5") << raw("\x65Hello") << "\"Hello\"";
146 QTest::newRow("textstring24") << raw("\x78\x18""123456789012345678901234")
147 << "\"123456789012345678901234\"";
148 QTest::newRow("textstring256") << raw("\x79\1\0") + QByteArray(256, '3')
149 << '"' + QString(256, '3') + '"';
150
151 // strings with undefined length
152 QTest::newRow("_emptytextstring") << raw("\x7f\xff") << "\"\"";
153 QTest::newRow("_emptytextstring2") << raw("\x7f\x60\xff") << "\"\"";
154 QTest::newRow("_emptytextstring3") << raw("\x7f\x60\x60\xff") << "\"\"";
155 QTest::newRow("_textstring5*2") << raw("\x7f\x63Hel\x62lo\xff") << "\"Hello\"";
156 QTest::newRow("_textstring5*5") << raw("\x7f\x61H\x61""e\x61l\x61l\x61o\xff") << "\"Hello\"";
157 QTest::newRow("_textstring5*6") << raw("\x7f\x61H\x61""e\x61l\x60\x61l\x61o\xff") << "\"Hello\"";
158 }
159
addNonJsonData()160 void addNonJsonData()
161 {
162 QTest::newRow("undefined") << raw("\xf7") << "\"undefined\"";
163 QTest::newRow("simple0") << raw("\xe0") << "\"simple(0)\"";
164 QTest::newRow("simple19") << raw("\xf3") << "\"simple(19)\"";
165 QTest::newRow("simple32") << raw("\xf8\x20") << "\"simple(32)\"";
166 QTest::newRow("simple255") << raw("\xf8\xff") << "\"simple(255)\"";
167 }
168
addByteStringsData()169 void addByteStringsData()
170 {
171 QTest::newRow("emptybytestring") << raw("\x40") << "\"\"";
172 QTest::newRow("bytestring1") << raw("\x41 ") << "\"IA\"";
173 QTest::newRow("bytestring1-nul") << raw("\x41\0") << "\"AA\"";
174 QTest::newRow("bytestring2") << raw("\x42Hi") << "\"SGk\"";
175 QTest::newRow("bytestring3") << raw("\x43Hey") << "\"SGV5\"";
176 QTest::newRow("bytestring4") << raw("\x44Hola") << "\"SG9sYQ\"";
177 QTest::newRow("bytestring5") << raw("\x45Hello") << "\"SGVsbG8\"";
178 QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234")
179 << "\"MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0\"";
180
181 // strings with undefined length
182 QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "\"\"";
183 QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "\"\"";
184 QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "\"\"";
185 QTest::newRow("_bytestring5*2") << raw("\x5f\x43Hel\x42lo\xff") << "\"SGVsbG8\"";
186 QTest::newRow("_bytestring5*5") << raw("\x5f\x41H\x41""e\x41l\x41l\x41o\xff") << "\"SGVsbG8\"";
187 QTest::newRow("_bytestring5*6") << raw("\x5f\x41H\x41""e\x40\x41l\x41l\x41o\xff") << "\"SGVsbG8\"";
188 }
189
addEmptyContainersData()190 void addEmptyContainersData()
191 {
192 QTest::newRow("emptyarray") << raw("\x80") << "[]";
193 QTest::newRow("emptymap") << raw("\xa0") << "{}";
194 QTest::newRow("_emptyarray") << raw("\x9f\xff") << "[]";
195 QTest::newRow("_emptymap") << raw("\xbf\xff") << "{}";
196 }
197
parseOne(CborValue * it,QString * parsed,int flags)198 CborError parseOne(CborValue *it, QString *parsed, int flags)
199 {
200 char *buffer;
201 size_t size;
202
203 FILE *f = open_memstream(&buffer, &size);
204 CborError err = cbor_value_to_json_advance(f, it, flags);
205 fclose(f);
206
207 *parsed = QString::fromLatin1(buffer);
208 free(buffer);
209 return err;
210 }
211
212 bool compareFailed = true;
compareOne_real(const QByteArray & data,const QString & expected,int flags,int line)213 void compareOne_real(const QByteArray &data, const QString &expected, int flags, int line)
214 {
215 compareFailed = true;
216 CborParser parser;
217 CborValue first;
218 CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
219 QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\"");
220
221 QString decoded;
222 err = parseOne(&first, &decoded, flags);
223 QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) +
224 "\"; decoded stream:\n" + decoded.toLatin1());
225 QCOMPARE(decoded, expected);
226
227 // check that we consumed everything
228 QCOMPARE((void*)first.ptr, (void*)data.constEnd());
229
230 compareFailed = false;
231 }
232 #define compareOne(data, expected, flags) \
233 compareOne_real(data, expected, flags, __LINE__); \
234 if (compareFailed) return
235
initTestCase()236 void tst_ToJson::initTestCase()
237 {
238 setlocale(LC_ALL, "C");
239 }
240
fixed_data()241 void tst_ToJson::fixed_data()
242 {
243 addColumns();
244 addFixedData();
245 }
246
fixed()247 void tst_ToJson::fixed()
248 {
249 QFETCH(QByteArray, data);
250 QFETCH(QString, expected);
251
252 compareOne(data, expected, 0);
253 }
254
textstrings_data()255 void tst_ToJson::textstrings_data()
256 {
257 addColumns();
258 addTextStringsData();
259 }
260
nonjson_data()261 void tst_ToJson::nonjson_data()
262 {
263 addColumns();
264 addNonJsonData();
265 }
266
bytestrings_data()267 void tst_ToJson::bytestrings_data()
268 {
269 addColumns();
270 addByteStringsData();
271 }
272
emptyContainers_data()273 void tst_ToJson::emptyContainers_data()
274 {
275 addColumns();
276 addEmptyContainersData();
277 }
278
arrays_data()279 void tst_ToJson::arrays_data()
280 {
281 addColumns();
282 addFixedData();
283 addTextStringsData();
284 addNonJsonData();
285 addByteStringsData();
286 }
287
arrays()288 void tst_ToJson::arrays()
289 {
290 QFETCH(QByteArray, data);
291 QFETCH(QString, expected);
292
293 compareOne("\x81" + data, '[' + expected + ']', 0);
294 compareOne("\x82" + data + data, '[' + expected + ',' + expected + ']', 0);
295 }
296
nestedArrays()297 void tst_ToJson::nestedArrays()
298 {
299 QFETCH(QByteArray, data);
300 QFETCH(QString, expected);
301
302 compareOne("\x81\x81" + data, "[[" + expected + "]]", 0);
303 compareOne("\x81\x81\x81" + data, "[[[" + expected + "]]]", 0);
304 compareOne("\x81\x82" + data + data, "[[" + expected + ',' + expected + "]]", 0);
305 compareOne("\x82\x81" + data + data, "[[" + expected + "]," + expected + "]", 0);
306 compareOne("\x82\x81" + data + '\x81' + data, "[[" + expected + "],[" + expected + "]]", 0);
307 }
308
maps()309 void tst_ToJson::maps()
310 {
311 QFETCH(QByteArray, data);
312 QFETCH(QString, expected);
313
314 compareOne("\xa1\x65" "Hello" + data, "{\"Hello\":" + expected + '}', 0);
315 }
316
nestedMaps()317 void tst_ToJson::nestedMaps()
318 {
319 QFETCH(QByteArray, data);
320 QFETCH(QString, expected);
321
322 compareOne("\xa1\x65Hello\xa1\x65World" + data, "{\"Hello\":{\"World\":" + expected + "}}", 0);
323 // compareOne("\xa1\x63""foo\xa1\63""bar" + data + "\63""baz\xa1\x64quux" + data,
324 // "{\"foo\":{\"bar\":" + expected + "},\"baz\":{\"quux\":" + expected + "}", 0);
325 }
326
nonStringKeyMaps_data()327 void tst_ToJson::nonStringKeyMaps_data()
328 {
329 addColumns();
330
331 QTest::newRow("0") << raw("\x00") << "0";
332 QTest::newRow("1") << raw("\x01") << "1";
333 QTest::newRow("UINT32_MAX") << raw("\x1a\xff\xff\xff\xff") << "4294967295";
334 QTest::newRow("UINT32_MAX+1") << raw("\x1b\0\0\0\1\0\0\0\0") << "4294967296";
335 QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
336 << QString::number(std::numeric_limits<uint64_t>::max());
337
338 QTest::newRow("-1") << raw("\x20") << "-1";
339 QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << "-4294967296";
340 QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << "-4294967297";
341 QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe")
342 << '-' + QString::number(std::numeric_limits<uint64_t>::max());
343 QTest::newRow("-UINT64_MAX-1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
344 << "-18446744073709551616";
345
346 QTest::newRow("simple0") << raw("\xe0") << "simple(0)";
347 QTest::newRow("simple19") << raw("\xf3") << "simple(19)";
348 QTest::newRow("false") << raw("\xf4") << "false";
349 QTest::newRow("true") << raw("\xf5") << "true";
350 QTest::newRow("null") << raw("\xf6") << "null";
351 QTest::newRow("undefined") << raw("\xf7") << "undefined";
352 QTest::newRow("simple32") << raw("\xf8\x20") << "simple(32)";
353 QTest::newRow("simple255") << raw("\xf8\xff") << "simple(255)";
354
355 QTest::newRow("0.f16") << raw("\xf9\0\0") << "0.f16";
356 QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "0.f";
357 QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "0.";
358 QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "-1.f16";
359 QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "-1.f";
360 QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "-1.";
361 QTest::newRow("65504.f16") << raw("\xf9\x7b\xff") << "65504.f16";
362 QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "16777215.f";
363 QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "16777215.";
364 QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << "-16777215.f";
365 QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "-16777215.";
366
367 QTest::newRow("0.5f16") << raw("\xf9\x38\0") << "0.5f16";
368 QTest::newRow("0.5f") << raw("\xfa\x3f\0\0\0") << "0.5f";
369 QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << "0.5";
370 QTest::newRow("2.f16^11-1") << raw("\xf9\x67\xff") << "2047.f16";
371 QTest::newRow("2.f^24-1") << raw("\xfa\x4b\x7f\xff\xff") << "16777215.f";
372 QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "9007199254740991.";
373 QTest::newRow("2.f^64-epsilon") << raw("\xfa\x5f\x7f\xff\xff") << "18446742974197923840.f";
374 QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "18446744073709549568.";
375 QTest::newRow("2.f^64") << raw("\xfa\x5f\x80\0\0") << "1.8446744073709552e+19f";
376 QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << "1.8446744073709552e+19";
377
378 QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "nan";
379 QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "nan";
380 QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "nan";
381 QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "-inf";
382 QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "-inf";
383 QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "-inf";
384 QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "inf";
385 QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "inf";
386 QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "inf";
387
388 QTest::newRow("emptybytestring") << raw("\x40") << "h''";
389 QTest::newRow("bytestring1") << raw("\x41 ") << "h'20'";
390 QTest::newRow("bytestring1-nul") << raw("\x41\0") << "h'00'";
391 QTest::newRow("bytestring5") << raw("\x45Hello") << "h'48656c6c6f'";
392 QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234")
393 << "h'313233343536373839303132333435363738393031323334'";
394
395 QTest::newRow("tag0") << raw("\xc0\x00") << "0(0)";
396 QTest::newRow("tag1") << raw("\xc1\x00") << "1(0)";
397 QTest::newRow("tag24") << raw("\xd8\x18\x00") << "24(0)";
398 QTest::newRow("tagUINT64_MAX") << raw("\xdb" "\xff\xff\xff\xff" "\xff\xff\xff\xff" "\x00")
399 << QString::number(std::numeric_limits<uint64_t>::max()) + "(0)";
400
401 QTest::newRow("emptyarray") << raw("\x80") << "[]";
402 QTest::newRow("emptymap") << raw("\xa0") << "{}";
403 QTest::newRow("_emptyarray") << raw("\x9f\xff") << "[_ ]";
404 QTest::newRow("_emptymap") << raw("\xbf\xff") << "{_ }";
405
406 QTest::newRow("map-0-24") << raw("\xa1\0\x18\x18") << "{0: 24}";
407 QTest::newRow("map-24-0") << raw("\xa1\x18\x18\0") << "{24: 0}";
408 QTest::newRow("_map-0-24") << raw("\xbf\0\x18\x18\xff") << "{_ 0: 24}";
409 QTest::newRow("_map-24-0") << raw("\xbf\x18\x18\0\xff") << "{_ 24: 0}";
410 }
411
nonStringKeyMaps()412 void tst_ToJson::nonStringKeyMaps()
413 {
414 QFETCH(QByteArray, data);
415 QFETCH(QString, expected);
416
417 data = "\xa1" + data + "\1";
418 compareOne(data, "{\"" + expected + "\":1}", CborConvertStringifyMapKeys);
419
420 // and verify that they fail if we use CborConvertRequireMapStringKeys
421 CborParser parser;
422 CborValue first;
423 QString decoded;
424 cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
425 CborError err = parseOne(&first, &decoded, CborConvertRequireMapStringKeys);
426 QCOMPARE(err, CborErrorJsonObjectKeyNotString);
427 }
428
tagsToObjects_data()429 void tst_ToJson::tagsToObjects_data()
430 {
431 addColumns();
432 QTest::newRow("0(0)") << raw("\xc0\0") << "{\"tag0\":0}";
433 QTest::newRow("0(-1)") << raw("\xc0\x20") << "{\"tag0\":-1}";
434 QTest::newRow("0(\"hello\")") << raw("\xc0\x65hello") << "{\"tag0\":\"hello\"}";
435 QTest::newRow("22(h'48656c6c6f')") << raw("\xd6\x45Hello") << "{\"tag22\":\"SGVsbG8\"}";
436 QTest::newRow("0([1,2,3])") << raw("\xc0\x83\1\2\3") << "{\"tag0\":[1,2,3]}";
437 QTest::newRow("0({\"z\":true,\"y\":1})") << raw("\xc0\xa2\x61z\xf5\x61y\1") << "{\"tag0\":{\"z\":true,\"y\":1}}";
438
439 // large tags
440 QTest::newRow("55799(0)") << raw("\xd9\xd9\xf7\0") << "{\"tag55799\":0}";
441 QTest::newRow("4294967295") << raw("\xda\xff\xff\xff\xff\0") << "{\"tag4294967295\":0}";
442 QTest::newRow("18446744073709551615(0)") << raw("\xdb\xff\xff\xff\xff""\xff\xff\xff\xff\0")
443 << "{\"tag18446744073709551615\":0}";
444
445 // nested tags
446 QTest::newRow("0(1(2))") << raw("\xc0\xc1\2") << "{\"tag0\":{\"tag1\":2}}";
447 QTest::newRow("0({\"z\":1(2)})") << raw("\xc0\xa1\x61z\xc1\2") << "{\"tag0\":{\"z\":{\"tag1\":2}}}";
448 }
449
tagsToObjects()450 void tst_ToJson::tagsToObjects()
451 {
452 QFETCH(QByteArray, data);
453 QFETCH(QString, expected);
454
455 compareOne(data, expected, CborConvertTagsToObjects);
456 }
457
taggedByteStringsToBase16_data()458 void tst_ToJson::taggedByteStringsToBase16_data()
459 {
460 QTest::addColumn<QByteArray>("data");
461 QTest::addColumn<QString>("base64url");
462 QTest::addColumn<QString>("base64");
463 QTest::addColumn<QString>("base16");
464
465 QTest::newRow("emptybytestring") << raw("\x40") << "" << "" << "";
466 QTest::newRow("bytestring1") << raw("\x41 ") << "IA" << "IA==" << "20";
467 QTest::newRow("bytestring1-nul") << raw("\x41\0") << "AA" << "AA==" << "00";
468 QTest::newRow("bytestring1-ff") << raw("\x41\xff") << "_w" << "/w==" << "ff";
469 QTest::newRow("bytestring2") << raw("\x42Hi") << "SGk" << "SGk=" << "4869";
470 QTest::newRow("bytestring3") << raw("\x43Hey") << "SGV5" << "SGV5" << "486579";
471 QTest::newRow("bytestring4") << raw("\x44Hola") << "SG9sYQ" << "SG9sYQ==" << "486f6c61";
472 QTest::newRow("bytestring5") << raw("\x45Hello") << "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
473 QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234")
474 << "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0"
475 << "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0"
476 << "313233343536373839303132333435363738393031323334";
477
478 // strings with undefined length
479 QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "" << "" << "";
480 QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "" << "" << "";
481 QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "" << "" << "";
482 QTest::newRow("_bytestring5*2") << raw("\x5f\x43Hel\x42lo\xff") << "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
483 QTest::newRow("_bytestring5*5") << raw("\x5f\x41H\x41""e\x41l\x41l\x41o\xff")
484 << "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
485 QTest::newRow("_bytestring5*6") << raw("\x5f\x41H\x41""e\x40\x41l\x41l\x41o\xff")
486 << "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
487 }
488
taggedByteStringsToBase16()489 void tst_ToJson::taggedByteStringsToBase16()
490 {
491 QFETCH(QByteArray, data);
492 QFETCH(QString, base16);
493
494 compareOne('\xd7' + data, '"' + base16 + '"', 0);
495 }
496
taggedByteStringsToBase64()497 void tst_ToJson::taggedByteStringsToBase64()
498 {
499 QFETCH(QByteArray, data);
500 QFETCH(QString, base64);
501
502 compareOne('\xd6' + data, '"' + base64 + '"', 0);
503 }
504
taggedByteStringsToBigNum()505 void tst_ToJson::taggedByteStringsToBigNum()
506 {
507 QFETCH(QByteArray, data);
508 QFETCH(QString, base64url);
509
510 compareOne('\xc3' + data, "\"~" + base64url + '"', 0);
511 }
512
otherTags_data()513 void tst_ToJson::otherTags_data()
514 {
515 addColumns();
516 addFixedData();
517 addTextStringsData();
518 addNonJsonData();
519 addByteStringsData();
520 addEmptyContainersData();
521 }
522
otherTags()523 void tst_ToJson::otherTags()
524 {
525 QFETCH(QByteArray, data);
526 QFETCH(QString, expected);
527
528 // other tags produce no change in output
529 compareOne("\xc0" + data, expected, 0);
530 compareOne("\xc1" + data, expected, 0);
531 compareOne("\xc2" + data, expected, 0);
532 compareOne("\xc4" + data, expected, 0);
533 compareOne("\xc5" + data, expected, 0);
534 compareOne("\xd8\x20" + data, expected, 0);
535 compareOne("\xd8\x21" + data, expected, 0);
536 compareOne("\xd8\x22" + data, expected, 0);
537 compareOne("\xd8\x23" + data, expected, 0);
538 compareOne("\xd8\x24" + data, expected, 0);
539 compareOne("\xd9\xd9\xf7" + data, expected, 0);
540 }
541
metaData_data()542 void tst_ToJson::metaData_data()
543 {
544 addColumns();
545
546 // booleans, null, strings, double precision numbers, regular maps, arrays and integers that
547 // didn't get rounded don't have metadata
548 QTest::newRow("0") << raw("\x00") << QString();
549 QTest::newRow("1") << raw("\x01") << QString();
550 QTest::newRow("2^53-1") << raw("\x1b\0\x1f\xff\xff""\xff\xff\xff\xff") << QString();
551 QTest::newRow("2^64-epsilon") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xf8\x00") << QString();
552 QTest::newRow("-1") << raw("\x20") << QString();
553 QTest::newRow("-2") << raw("\x21") << QString();
554 QTest::newRow("-2^53+1") << raw("\x3b\0\x1f\xff\xff""\xff\xff\xff\xfe") << QString();
555 QTest::newRow("-2^64+epsilon") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xf8\x00") << QString();
556 QTest::newRow("emptytextstring") << raw("\x60") << QString();
557 QTest::newRow("textstring1") << raw("\x61 ") << QString();
558 QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << QString();
559 QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << QString();
560 QTest::newRow("false") << raw("\xf4") << QString();
561 QTest::newRow("true") << raw("\xf5") << QString();
562 QTest::newRow("null") << raw("\xf6") << QString();
563 QTest::newRow("emptyarray") << raw("\x80") << QString();
564 QTest::newRow("emptymap") << raw("\xa0") << QString();
565 QTest::newRow("array*1") << raw("\x81\xf6") << QString();
566 QTest::newRow("map*1") << raw("\xa1\x61z\xf4") << QString();
567
568 // ---- everything from here on has at least the type ----
569 QTest::newRow("emptybytestring") << raw("\x40") << "\"t\":64";
570 QTest::newRow("bytestring1") << raw("\x41 ") << "\"t\":64";
571 QTest::newRow("undefined") << raw("\xf7") << "\"t\":247";
572 QTest::newRow("0.f16") << raw("\xf9\0\0") << "\"t\":249";
573 QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "\"t\":249";
574 QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "\"t\":250";
575 QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "\"t\":250";
576 QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "\"t\":250";
577 QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << "\"t\":250";
578 QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "\"t\":251";
579 QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "\"t\":251";
580 QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "\"t\":251";
581 QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "\"t\":251";
582 QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "\"t\":251";
583 QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "\"t\":251";
584
585 // integers that are too precise for double
586 QTest::newRow("2^53+1") << raw("\x1b\0\x20\0\0""\0\0\0\1")
587 << "\"t\":0,\"v\":\"+20000000000001\"";
588 QTest::newRow("INT64_MAX-1") << raw("\x1b\x7f\xff\xff\xff""\xff\xff\xff\xfe")
589 << "\"t\":0,\"v\":\"+7ffffffffffffffe\"";
590 QTest::newRow("INT64_MAX+1") << raw("\x1b\x80\0\0\0""\0\0\0\1")
591 << "\"t\":0,\"v\":\"+8000000000000001\"";
592 QTest::newRow("-2^53-1") << raw("\x3b\0\x20\0\0""\0\0\0\0")
593 << "\"t\":0,\"v\":\"-20000000000000\"";
594
595 // simple values
596 QTest::newRow("simple0") << raw("\xe0") << "\"t\":224,\"v\":0";
597 QTest::newRow("simple19") << raw("\xf3") << "\"t\":224,\"v\":19";
598 QTest::newRow("simple32") << raw("\xf8\x20") << "\"t\":224,\"v\":32";
599 QTest::newRow("simple255") << raw("\xf8\xff") << "\"t\":224,\"v\":255";
600
601 // infinities and NaN are not supported in JSON, they convert to null
602 QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "\"t\":249,\"v\":\"nan\"";
603 QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "\"t\":250,\"v\":\"nan\"";
604 QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "\"t\":251,\"v\":\"nan\"";
605 QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "\"t\":249,\"v\":\"-inf\"";
606 QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "\"t\":250,\"v\":\"-inf\"";
607 QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "\"t\":251,\"v\":\"-inf\"";
608 QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "\"t\":249,\"v\":\"inf\"";
609 QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "\"t\":250,\"v\":\"inf\"";
610 QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "\"t\":251,\"v\":\"inf\"";
611
612 // tags on native types
613 QTest::newRow("tag+0") << raw("\xc0\x00") << "\"tag\":\"0\"";
614 QTest::newRow("tag+-2") << raw("\xc0\x21") << "\"tag\":\"0\"";
615 QTest::newRow("tag+0.5") << raw("\xc0\xfb\x3f\xe0\0\0\0\0\0\0") << "\"tag\":\"0\"";
616 QTest::newRow("tag+emptytextstring") << raw("\xc0\x60") << "\"tag\":\"0\"";
617 QTest::newRow("tag+textstring1") << raw("\xc0\x61 ") << "\"tag\":\"0\"";
618 QTest::newRow("tag+false") << raw("\xc0\xf4") << "\"tag\":\"0\"";
619 QTest::newRow("tag+true") << raw("\xc0\xf5") << "\"tag\":\"0\"";
620 QTest::newRow("tag+null") << raw("\xc0\xf6") << "\"tag\":\"0\"";
621 QTest::newRow("tag+emptyarray") << raw("\xc0\x80") << "\"tag\":\"0\"";
622 QTest::newRow("tag+emptymap") << raw("\xc0\xa0") << "\"tag\":\"0\"";
623 QTest::newRow("tag+array*1") << raw("\xc0\x81\xf6") << "\"tag\":\"0\"";
624 QTest::newRow("tag+map*1") << raw("\xc0\xa1\x61z\xf4") << "\"tag\":\"0\"";
625
626 // tags on non-native types
627 QTest::newRow("tag+emptybytestring") << raw("\xc0\x40") << "\"tag\":\"0\",\"t\":64";
628 QTest::newRow("tag+bytestring1") << raw("\xc0\x41 ") << "\"tag\":\"0\",\"t\":64";
629 QTest::newRow("tag+undefined") << raw("\xc0\xf7") << "\"tag\":\"0\",\"t\":247";
630 QTest::newRow("tag+0.f") << raw("\xc0\xfa\0\0\0\0") << "\"tag\":\"0\",\"t\":250";
631 QTest::newRow("tag+-1.f") << raw("\xc0\xfa\xbf\x80\0\0") << "\"tag\":\"0\",\"t\":250";
632 QTest::newRow("tag+16777215.f") << raw("\xc0\xfa\x4b\x7f\xff\xff") << "\"tag\":\"0\",\"t\":250";
633 QTest::newRow("tag+-16777215.f") << raw("\xc0\xfa\xcb\x7f\xff\xff") << "\"tag\":\"0\",\"t\":250";
634 QTest::newRow("tag+0.") << raw("\xc0\xfb\0\0\0\0\0\0\0\0") << "\"tag\":\"0\",\"t\":251";
635 QTest::newRow("tag+-1.") << raw("\xc0\xfb\xbf\xf0\0\0\0\0\0\0") << "\"tag\":\"0\",\"t\":251";
636 QTest::newRow("tag+16777215.") << raw("\xc0\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "\"tag\":\"0\",\"t\":251";
637 QTest::newRow("tag+-16777215.") << raw("\xc0\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "\"tag\":\"0\",\"t\":251";
638
639 // big tags (don't fit in JS numbers)
640 QTest::newRow("bigtag1") << raw("\xdb\0\x20\0\0""\0\0\0\1\x60") << "\"tag\":\"9007199254740993\"";
641 QTest::newRow("bigtag2") << raw("\xdb\xff\xff\xff\xff""\xff\xff\xff\xfe\x60")
642 << "\"tag\":\"18446744073709551614\"";
643
644 // specially-handled tags
645 QTest::newRow("negativebignum") << raw("\xc3\x41 ") << "\"tag\":\"3\",\"t\":64";
646 QTest::newRow("base64") << raw("\xd6\x41 ") << "\"tag\":\"22\",\"t\":64";
647 QTest::newRow("base16") << raw("\xd7\x41 ") << "\"tag\":\"23\",\"t\":64";
648 }
649
compareMetaData(QByteArray data,const QString & expected,int otherFlags=0)650 void compareMetaData(QByteArray data, const QString &expected, int otherFlags = 0)
651 {
652 QString decoded;
653
654 // needs to be in one map, with the entry called "v"
655 data = "\xa1\x61v" + data;
656
657 {
658 CborParser parser;
659 CborValue first;
660 CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
661 QVERIFY2(!err, QByteArrayLiteral(": Got error \"") + cbor_error_string(err) + "\"");
662
663 err = parseOne(&first, &decoded, CborConvertAddMetadata | otherFlags);
664 QVERIFY2(!err, QByteArrayLiteral(": Got error \"") + cbor_error_string(err) +
665 "\"; decoded stream:\n" + decoded.toLatin1());
666
667 // check that we consumed everything
668 QCOMPARE((void*)first.ptr, (void*)data.constEnd());
669 }
670
671 QVERIFY(decoded.startsWith("{\"v\":"));
672 QVERIFY(decoded.endsWith('}'));
673 // qDebug() << "was" << decoded;
674
675 // extract just the metadata
676 static const char needle[] = "\"v$cbor\":{";
677 int pos = decoded.indexOf(needle);
678 QCOMPARE(pos == -1, expected.isEmpty());
679 if (pos != -1) {
680 decoded.chop(2);
681 decoded = std::move(decoded).mid(pos + strlen(needle));
682 QCOMPARE(decoded, expected);
683 }
684 }
685
metaData()686 void tst_ToJson::metaData()
687 {
688 QFETCH(QByteArray, data);
689 QFETCH(QString, expected);
690 compareMetaData(data, expected);
691 }
692
metaDataAndTagsToObjects()693 void tst_ToJson::metaDataAndTagsToObjects()
694 {
695 QFETCH(QByteArray, data);
696
697 // when a tag is converted to an object, the object gets metadata indicating it was a tag
698 compareMetaData(data, "\"t\":192", CborConvertTagsToObjects);
699 }
700
metaDataForKeys_data()701 void tst_ToJson::metaDataForKeys_data()
702 {
703 nonStringKeyMaps_data();
704
705 // string keys generate no metadata
706 QTest::newRow("string") << raw("\x60") << QString();
707 }
708
metaDataForKeys()709 void tst_ToJson::metaDataForKeys()
710 {
711 QFETCH(QByteArray, data);
712 QFETCH(QString, expected);
713 if (expected.isEmpty())
714 expected = "{\"\":false}";
715 else
716 expected = "{\"" + expected + "\":false,\"" + expected + "$keycbordump\":true}";
717 compareOne('\xa1' + data + '\xf4', expected,
718 CborConvertAddMetadata | CborConvertStringifyMapKeys);
719 }
720
721 QTEST_MAIN(tst_ToJson)
722