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