1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest> |
30 | |
31 | #include "qjsonarray.h" |
32 | #include "qjsonobject.h" |
33 | #include "qjsonvalue.h" |
34 | #include "qjsondocument.h" |
35 | #include "qregularexpression.h" |
36 | #include "private/qnumeric_p.h" |
37 | #include <limits> |
38 | |
39 | #define INVALID_UNICODE "\xCE\xBA\xE1" |
40 | #define UNICODE_NON_CHARACTER "\xEF\xBF\xBF" |
41 | #define UNICODE_DJE "\320\202" // Character from the Serbian Cyrillic alphabet |
42 | |
43 | class tst_QtJson: public QObject |
44 | { |
45 | Q_OBJECT |
46 | |
47 | private Q_SLOTS: |
48 | void initTestCase(); |
49 | |
50 | void testValueSimple(); |
51 | void testNumbers(); |
52 | void testNumbers_2(); |
53 | void testNumbers_3(); |
54 | void testNumbers_4(); |
55 | |
56 | void testObjectSimple(); |
57 | void testObjectTakeDetach(); |
58 | void testObjectSmallKeys(); |
59 | void testObjectInsertCopies(); |
60 | void testArraySimple(); |
61 | void testArrayInsertCopies(); |
62 | void testValueObject(); |
63 | void testValueArray(); |
64 | void testObjectNested(); |
65 | void testArrayNested(); |
66 | void testArrayNestedEmpty(); |
67 | void testArrayComfortOperators(); |
68 | void testObjectNestedEmpty(); |
69 | |
70 | void testValueRef(); |
71 | void testValueRefComparison(); |
72 | void testObjectIteration(); |
73 | void testArrayIteration(); |
74 | |
75 | void testObjectFind(); |
76 | |
77 | void testDocument(); |
78 | |
79 | void nullValues(); |
80 | void nullArrays(); |
81 | void nullObject(); |
82 | void constNullObject(); |
83 | |
84 | void keySorting_data(); |
85 | void keySorting(); |
86 | |
87 | void undefinedValues(); |
88 | |
89 | void fromVariant_data(); |
90 | void fromVariant(); |
91 | void fromVariantSpecial_data(); |
92 | void fromVariantSpecial(); |
93 | void toVariant_data(); |
94 | void toVariant(); |
95 | void fromVariantMap(); |
96 | void fromVariantHash(); |
97 | void toVariantMap(); |
98 | void toVariantHash(); |
99 | void toVariantList(); |
100 | |
101 | void toJson(); |
102 | void toJsonSillyNumericValues(); |
103 | void toJsonLargeNumericValues(); |
104 | void fromJson(); |
105 | void fromJsonErrors(); |
106 | void fromBinary(); |
107 | void toAndFromBinary_data(); |
108 | void toAndFromBinary(); |
109 | void invalidBinaryData(); |
110 | void parseNumbers(); |
111 | void parseStrings(); |
112 | void parseDuplicateKeys(); |
113 | void testParser(); |
114 | |
115 | void compactArray(); |
116 | void compactObject(); |
117 | |
118 | void validation(); |
119 | |
120 | void assignToDocument(); |
121 | |
122 | void testDuplicateKeys(); |
123 | void testCompaction(); |
124 | void testDebugStream(); |
125 | void testCompactionError(); |
126 | |
127 | void parseUnicodeEscapes(); |
128 | |
129 | void assignObjects(); |
130 | void assignArrays(); |
131 | |
132 | void testTrailingComma(); |
133 | void testDetachBug(); |
134 | void testJsonValueRefDefault(); |
135 | |
136 | void valueEquals(); |
137 | void objectEquals_data(); |
138 | void objectEquals(); |
139 | void arrayEquals_data(); |
140 | void arrayEquals(); |
141 | void documentEquals_data(); |
142 | void documentEquals(); |
143 | |
144 | void bom(); |
145 | void nesting(); |
146 | |
147 | void longStrings(); |
148 | |
149 | void arrayInitializerList(); |
150 | void objectInitializerList(); |
151 | |
152 | void unicodeKeys(); |
153 | void garbageAtEnd(); |
154 | |
155 | void removeNonLatinKey(); |
156 | void documentFromVariant(); |
157 | |
158 | void parseErrorOffset_data(); |
159 | void parseErrorOffset(); |
160 | |
161 | void implicitValueType(); |
162 | void implicitDocumentType(); |
163 | |
164 | void streamSerializationQJsonDocument_data(); |
165 | void streamSerializationQJsonDocument(); |
166 | void streamSerializationQJsonArray_data(); |
167 | void streamSerializationQJsonArray(); |
168 | void streamSerializationQJsonObject_data(); |
169 | void streamSerializationQJsonObject(); |
170 | void streamSerializationQJsonValue_data(); |
171 | void streamSerializationQJsonValue(); |
172 | void streamSerializationQJsonValueEmpty(); |
173 | void streamVariantSerialization(); |
174 | void escapeSurrogateCodePoints_data(); |
175 | void escapeSurrogateCodePoints(); |
176 | |
177 | void fromToVariantConversions_data(); |
178 | void fromToVariantConversions(); |
179 | |
180 | void noLeakOnNameClash(); |
181 | |
182 | private: |
183 | QString testDataDir; |
184 | }; |
185 | |
186 | void tst_QtJson::initTestCase() |
187 | { |
188 | testDataDir = QFileInfo(QFINDTESTDATA("test.json" )).absolutePath(); |
189 | if (testDataDir.isEmpty()) |
190 | testDataDir = QCoreApplication::applicationDirPath(); |
191 | } |
192 | |
193 | void tst_QtJson::testValueSimple() |
194 | { |
195 | QJsonObject object; |
196 | object.insert(key: "number" , value: 999.); |
197 | QJsonArray array; |
198 | for (int i = 0; i < 10; ++i) |
199 | array.append(value: (double)i); |
200 | |
201 | QJsonValue value(true); |
202 | QCOMPARE(value.type(), QJsonValue::Bool); |
203 | QCOMPARE(value.toDouble(), 0.); |
204 | QCOMPARE(value.toString(), QString()); |
205 | QCOMPARE(value.toBool(), true); |
206 | QCOMPARE(value.toObject(), QJsonObject()); |
207 | QCOMPARE(value.toArray(), QJsonArray()); |
208 | QCOMPARE(value.toDouble(99.), 99.); |
209 | QCOMPARE(value.toString(QString("test" )), QString("test" )); |
210 | QCOMPARE(value.toObject(object), object); |
211 | QCOMPARE(value.toArray(array), array); |
212 | |
213 | value = 999.; |
214 | QCOMPARE(value.type(), QJsonValue::Double); |
215 | QCOMPARE(value.toDouble(), 999.); |
216 | QCOMPARE(value.toString(), QString()); |
217 | QCOMPARE(value.toBool(), false); |
218 | QCOMPARE(value.toBool(true), true); |
219 | QCOMPARE(value.toObject(), QJsonObject()); |
220 | QCOMPARE(value.toArray(), QJsonArray()); |
221 | |
222 | value = QLatin1String("test" ); |
223 | QCOMPARE(value.toDouble(), 0.); |
224 | QCOMPARE(value.toString(), QLatin1String("test" )); |
225 | QCOMPARE(value.toBool(), false); |
226 | QCOMPARE(value.toObject(), QJsonObject()); |
227 | QCOMPARE(value.toArray(), QJsonArray()); |
228 | } |
229 | |
230 | void tst_QtJson::testNumbers() |
231 | { |
232 | { |
233 | int numbers[] = { |
234 | 0, |
235 | -1, |
236 | 1, |
237 | (1<<26), |
238 | (1<<27), |
239 | (1<<28), |
240 | -(1<<26), |
241 | -(1<<27), |
242 | -(1<<28), |
243 | (1<<26) - 1, |
244 | (1<<27) - 1, |
245 | (1<<28) - 1, |
246 | -((1<<26) - 1), |
247 | -((1<<27) - 1), |
248 | -((1<<28) - 1) |
249 | }; |
250 | int n = sizeof(numbers)/sizeof(int); |
251 | |
252 | QJsonArray array; |
253 | for (int i = 0; i < n; ++i) |
254 | array.append(value: (double)numbers[i]); |
255 | |
256 | QByteArray serialized = QJsonDocument(array).toJson(); |
257 | QJsonDocument json = QJsonDocument::fromJson(json: serialized); |
258 | QJsonArray array2 = json.array(); |
259 | |
260 | QCOMPARE(array.size(), array2.size()); |
261 | for (int i = 0; i < array.size(); ++i) { |
262 | QCOMPARE(array.at(i).type(), QJsonValue::Double); |
263 | QCOMPARE(array.at(i).toDouble(), (double)numbers[i]); |
264 | QCOMPARE(array2.at(i).type(), QJsonValue::Double); |
265 | QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]); |
266 | } |
267 | } |
268 | |
269 | { |
270 | qint64 numbers[] = { |
271 | 0, |
272 | -1, |
273 | 1, |
274 | (1ll<<54), |
275 | (1ll<<55), |
276 | (1ll<<56), |
277 | -(1ll<<54), |
278 | -(1ll<<55), |
279 | -(1ll<<56), |
280 | (1ll<<54) - 1, |
281 | (1ll<<55) - 1, |
282 | (1ll<<56) - 1, |
283 | -((1ll<<54) - 1), |
284 | -((1ll<<55) - 1), |
285 | -((1ll<<56) - 1) |
286 | }; |
287 | int n = sizeof(numbers)/sizeof(qint64); |
288 | |
289 | QJsonArray array; |
290 | for (int i = 0; i < n; ++i) |
291 | array.append(value: (double)numbers[i]); |
292 | |
293 | QByteArray serialized = QJsonDocument(array).toJson(); |
294 | QJsonDocument json = QJsonDocument::fromJson(json: serialized); |
295 | QJsonArray array2 = json.array(); |
296 | |
297 | QCOMPARE(array.size(), array2.size()); |
298 | for (int i = 0; i < array.size(); ++i) { |
299 | QCOMPARE(array.at(i).type(), QJsonValue::Double); |
300 | QCOMPARE(array.at(i).toDouble(), (double)numbers[i]); |
301 | QCOMPARE(array2.at(i).type(), QJsonValue::Double); |
302 | QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]); |
303 | } |
304 | } |
305 | |
306 | { |
307 | double numbers[] = { |
308 | 0, |
309 | -1, |
310 | 1, |
311 | double(1ll<<54), |
312 | double(1ll<<55), |
313 | double(1ll<<56), |
314 | double(-(1ll<<54)), |
315 | double(-(1ll<<55)), |
316 | double(-(1ll<<56)), |
317 | double((1ll<<54) - 1), |
318 | double((1ll<<55) - 1), |
319 | double((1ll<<56) - 1), |
320 | double(-((1ll<<54) - 1)), |
321 | double(-((1ll<<55) - 1)), |
322 | double(-((1ll<<56) - 1)), |
323 | 1.1, |
324 | 0.1, |
325 | -0.1, |
326 | -1.1, |
327 | 1e200, |
328 | -1e200 |
329 | }; |
330 | int n = sizeof(numbers)/sizeof(double); |
331 | |
332 | QJsonArray array; |
333 | for (int i = 0; i < n; ++i) |
334 | array.append(value: numbers[i]); |
335 | |
336 | QByteArray serialized = QJsonDocument(array).toJson(); |
337 | QJsonDocument json = QJsonDocument::fromJson(json: serialized); |
338 | QJsonArray array2 = json.array(); |
339 | |
340 | QCOMPARE(array.size(), array2.size()); |
341 | for (int i = 0; i < array.size(); ++i) { |
342 | QCOMPARE(array.at(i).type(), QJsonValue::Double); |
343 | QCOMPARE(array.at(i).toDouble(), numbers[i]); |
344 | QCOMPARE(array2.at(i).type(), QJsonValue::Double); |
345 | QCOMPARE(array2.at(i).toDouble(), numbers[i]); |
346 | } |
347 | } |
348 | |
349 | } |
350 | |
351 | void tst_QtJson::testNumbers_2() |
352 | { |
353 | // test cases from TC39 test suite for ECMAScript |
354 | // http://hg.ecmascript.org/tests/test262/file/d067d2f0ca30/test/suite/ch08/8.5/8.5.1.js |
355 | |
356 | // Fill an array with 2 to the power of (0 ... -1075) |
357 | double value = 1; |
358 | double floatValues[1076], floatValues_1[1076]; |
359 | QJsonObject jObject; |
360 | for (int power = 0; power <= 1075; power++) { |
361 | floatValues[power] = value; |
362 | jObject.insert(key: QString::number(power), value: QJsonValue(floatValues[power])); |
363 | // Use basic math operations for testing, which are required to support 'gradual underflow' rather |
364 | // than Math.pow etc..., which are defined as 'implementation dependent'. |
365 | value = value * 0.5; |
366 | } |
367 | |
368 | QJsonDocument jDocument1(jObject); |
369 | QByteArray ba(jDocument1.toJson()); |
370 | |
371 | QJsonDocument jDocument2(QJsonDocument::fromJson(json: ba)); |
372 | for (int power = 0; power <= 1075; power++) { |
373 | floatValues_1[power] = jDocument2.object().value(key: QString::number(power)).toDouble(); |
374 | #ifdef Q_OS_QNX |
375 | if (power >= 970) |
376 | QEXPECT_FAIL("" , "See QTBUG-37066" , Abort); |
377 | #endif |
378 | QVERIFY2(floatValues[power] == floatValues_1[power], QString("floatValues[%1] != floatValues_1[%1]" ).arg(power).toLatin1()); |
379 | } |
380 | |
381 | // The last value is below min denorm and should round to 0, everything else should contain a value |
382 | QVERIFY2(floatValues_1[1075] == 0, "Value after min denorm should round to 0" ); |
383 | |
384 | // Validate the last actual value is min denorm |
385 | QVERIFY2(floatValues_1[1074] == 4.9406564584124654417656879286822e-324, QString("Min denorm value is incorrect: %1" ).arg(floatValues_1[1074]).toLatin1()); |
386 | |
387 | // Validate that every value is half the value before it up to 1 |
388 | for (int index = 1074; index > 0; index--) { |
389 | QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0" ).arg(index).toLatin1()); |
390 | |
391 | QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1" ).arg(index).toLatin1()); |
392 | } |
393 | } |
394 | |
395 | void tst_QtJson::testNumbers_3() |
396 | { |
397 | // test case from QTBUG-31926 |
398 | double d1 = 1.123451234512345; |
399 | double d2 = 1.123451234512346; |
400 | |
401 | QJsonObject jObject; |
402 | jObject.insert(key: "d1" , value: QJsonValue(d1)); |
403 | jObject.insert(key: "d2" , value: QJsonValue(d2)); |
404 | QJsonDocument jDocument1(jObject); |
405 | QByteArray ba(jDocument1.toJson()); |
406 | |
407 | QJsonDocument jDocument2(QJsonDocument::fromJson(json: ba)); |
408 | |
409 | double d1_1(jDocument2.object().value(key: "d1" ).toDouble()); |
410 | double d2_1(jDocument2.object().value(key: "d2" ).toDouble()); |
411 | QVERIFY(d1_1 != d2_1); |
412 | } |
413 | |
414 | void tst_QtJson::testNumbers_4() |
415 | { |
416 | // no exponent notation used to print numbers between -2^64 and 2^64 |
417 | QJsonArray array; |
418 | array << QJsonValue(+1000000000000000.0); |
419 | array << QJsonValue(-1000000000000000.0); |
420 | array << QJsonValue(+9007199254740992.0); |
421 | array << QJsonValue(-9007199254740992.0); |
422 | array << QJsonValue(+9223372036854775808.0); |
423 | array << QJsonValue(-9223372036854775808.0); |
424 | array << QJsonValue(+18446744073709551616.0); |
425 | array << QJsonValue(-18446744073709551616.0); |
426 | const QByteArray json(QJsonDocument(array).toJson()); |
427 | const QByteArray expected = |
428 | "[\n" |
429 | " 1000000000000000,\n" |
430 | " -1000000000000000,\n" |
431 | " 9007199254740992,\n" |
432 | " -9007199254740992,\n" |
433 | " 9223372036854776000,\n" |
434 | " -9223372036854776000,\n" |
435 | " 18446744073709552000,\n" |
436 | " -18446744073709552000\n" |
437 | "]\n" ; |
438 | QCOMPARE(json, expected); |
439 | } |
440 | |
441 | void tst_QtJson::testObjectSimple() |
442 | { |
443 | QJsonObject object; |
444 | object.insert(key: "number" , value: 999.); |
445 | QCOMPARE(object.value("number" ).type(), QJsonValue::Double); |
446 | QCOMPARE(object.value(QLatin1String("number" )).toDouble(), 999.); |
447 | object.insert(key: "string" , value: QString::fromLatin1(str: "test" )); |
448 | QCOMPARE(object.value("string" ).type(), QJsonValue::String); |
449 | QCOMPARE(object.value(QLatin1String("string" )).toString(), QString("test" )); |
450 | object.insert(key: "boolean" , value: true); |
451 | QCOMPARE(object.value("boolean" ).toBool(), true); |
452 | QCOMPARE(object.value(QLatin1String("boolean" )).toBool(), true); |
453 | QJsonObject object2 = object; |
454 | QJsonObject object3 = object; |
455 | |
456 | QStringList keys = object.keys(); |
457 | QVERIFY2(keys.contains("number" ), "key number not found" ); |
458 | QVERIFY2(keys.contains("string" ), "key string not found" ); |
459 | QVERIFY2(keys.contains("boolean" ), "key boolean not found" ); |
460 | |
461 | // if we put a JsonValue into the JsonObject and retrieve |
462 | // it, it should be identical. |
463 | QJsonValue value(QLatin1String("foo" )); |
464 | object.insert(key: "value" , value); |
465 | QCOMPARE(object.value("value" ), value); |
466 | |
467 | int size = object.size(); |
468 | object.remove(key: "boolean" ); |
469 | QCOMPARE(object.size(), size - 1); |
470 | QVERIFY2(!object.contains("boolean" ), "key boolean should have been removed" ); |
471 | |
472 | QJsonValue taken = object.take(key: "value" ); |
473 | QCOMPARE(taken, value); |
474 | QVERIFY2(!object.contains("value" ), "key value should have been removed" ); |
475 | |
476 | QString before = object.value(key: "string" ).toString(); |
477 | object.insert(key: "string" , value: QString::fromLatin1(str: "foo" )); |
478 | QVERIFY2(object.value(QLatin1String("string" )).toString() != before, "value should have been updated" ); |
479 | |
480 | // same tests again but with QStringView keys |
481 | object2.insert(key: QStringView(u"value" ), value); |
482 | QCOMPARE(object2.value("value" ), value); |
483 | |
484 | size = object2.size(); |
485 | object2.remove(key: QStringView(u"boolean" )); |
486 | QCOMPARE(object2.size(), size - 1); |
487 | QVERIFY2(!object2.contains(QStringView(u"boolean" )), "key boolean should have been removed" ); |
488 | |
489 | taken = object2.take(key: QStringView(u"value" )); |
490 | QCOMPARE(taken, value); |
491 | QVERIFY2(!object2.contains("value" ), "key value should have been removed" ); |
492 | |
493 | before = object2.value(key: "string" ).toString(); |
494 | object2.insert(key: QStringView(u"string" ), value: QString::fromLatin1(str: "foo" )); |
495 | QVERIFY2(object2.value(QStringView(u"string" )).toString() != before, "value should have been updated" ); |
496 | |
497 | // same tests again but with QLatin1String keys |
498 | object3.insert(key: QLatin1String("value" ), value); |
499 | QCOMPARE(object3.value("value" ), value); |
500 | |
501 | size = object3.size(); |
502 | object3.remove(key: QLatin1String("boolean" )); |
503 | QCOMPARE(object3.size(), size - 1); |
504 | QVERIFY2(!object3.contains("boolean" ), "key boolean should have been removed" ); |
505 | |
506 | taken = object3.take(key: QLatin1String("value" )); |
507 | QCOMPARE(taken, value); |
508 | QVERIFY2(!object3.contains("value" ), "key value should have been removed" ); |
509 | |
510 | before = object3.value(key: "string" ).toString(); |
511 | object3.insert(key: QLatin1String("string" ), value: QString::fromLatin1(str: "foo" )); |
512 | QVERIFY2(object3.value(QLatin1String("string" )).toString() != before, "value should have been updated" ); |
513 | |
514 | size = object.size(); |
515 | QJsonObject subobject; |
516 | subobject.insert(key: "number" , value: 42); |
517 | subobject.insert(key: QLatin1String("string" ), value: QLatin1String("foobar" )); |
518 | object.insert(key: "subobject" , value: subobject); |
519 | QCOMPARE(object.size(), size+1); |
520 | QJsonValue subvalue = object.take(key: QLatin1String("subobject" )); |
521 | QCOMPARE(object.size(), size); |
522 | QCOMPARE(subvalue.toObject(), subobject); |
523 | // make object detach by modifying it many times |
524 | for (int i = 0; i < 64; ++i) |
525 | object.insert(key: QLatin1String("string" ), value: QLatin1String("bar" )); |
526 | QCOMPARE(object.size(), size); |
527 | QCOMPARE(subvalue.toObject(), subobject); |
528 | } |
529 | |
530 | void tst_QtJson::testObjectTakeDetach() |
531 | { |
532 | QJsonObject object1, object2; |
533 | object1["key1" ] = 1; |
534 | object1["key2" ] = 2; |
535 | object2 = object1; |
536 | |
537 | object1.take(key: "key2" ); |
538 | object1.remove(key: "key1" ); |
539 | QVERIFY(!object1.contains("key1" )); |
540 | QVERIFY(object2.contains("key1" )); |
541 | QVERIFY(object2.value("key1" ).isDouble()); |
542 | |
543 | QVERIFY(!object1.contains("key2" )); |
544 | QVERIFY(object2.contains("key2" )); |
545 | QVERIFY(object2.value("key2" ).isDouble()); |
546 | } |
547 | |
548 | void tst_QtJson::testObjectSmallKeys() |
549 | { |
550 | QJsonObject data1; |
551 | data1.insert(QStringLiteral("1" ), value: 123.); |
552 | QVERIFY(data1.contains(QStringLiteral("1" ))); |
553 | QCOMPARE(data1.value(QStringLiteral("1" )).toDouble(), (double)123); |
554 | data1.insert(QStringLiteral("12" ), value: 133.); |
555 | QCOMPARE(data1.value(QStringLiteral("12" )).toDouble(), (double)133); |
556 | QVERIFY(data1.contains(QStringLiteral("12" ))); |
557 | data1.insert(QStringLiteral("123" ), value: 323.); |
558 | QCOMPARE(data1.value(QStringLiteral("12" )).toDouble(), (double)133); |
559 | QVERIFY(data1.contains(QStringLiteral("123" ))); |
560 | QCOMPARE(data1.value(QStringLiteral("123" )).type(), QJsonValue::Double); |
561 | QCOMPARE(data1.value(QStringLiteral("123" )).toDouble(), (double)323); |
562 | QCOMPARE(data1.constEnd() - data1.constBegin(), 3); |
563 | QCOMPARE(data1.end() - data1.begin(), 3); |
564 | } |
565 | |
566 | void tst_QtJson::testObjectInsertCopies() |
567 | { |
568 | { |
569 | QJsonObject obj; |
570 | obj["prop1" ] = "TEST" ; |
571 | QCOMPARE(obj.size(), 1); |
572 | QCOMPARE(obj.value("prop1" ), "TEST" ); |
573 | |
574 | obj["prop2" ] = obj.value(key: "prop1" ); |
575 | QCOMPARE(obj.size(), 2); |
576 | QCOMPARE(obj.value("prop1" ), "TEST" ); |
577 | QCOMPARE(obj.value("prop2" ), "TEST" ); |
578 | } |
579 | { |
580 | // see QTBUG-83366 |
581 | QJsonObject obj; |
582 | obj["value" ] = "TEST" ; |
583 | QCOMPARE(obj.size(), 1); |
584 | QCOMPARE(obj.value("value" ), "TEST" ); |
585 | |
586 | obj["prop2" ] = obj.value(key: "value" ); |
587 | QCOMPARE(obj.size(), 2); |
588 | QCOMPARE(obj.value("value" ), "TEST" ); |
589 | QCOMPARE(obj.value("prop2" ), "TEST" ); |
590 | } |
591 | { |
592 | QJsonObject obj; |
593 | obj["value" ] = "TEST" ; |
594 | QCOMPARE(obj.size(), 1); |
595 | QCOMPARE(obj.value("value" ), "TEST" ); |
596 | |
597 | // same as previous, but this is a QJsonValueRef |
598 | QJsonValueRef rv = obj["prop2" ]; |
599 | rv = obj["value" ]; |
600 | QCOMPARE(obj.size(), 2); |
601 | QCOMPARE(obj.value("value" ), "TEST" ); |
602 | QCOMPARE(obj.value("prop2" ), "TEST" ); |
603 | } |
604 | { |
605 | QJsonObject obj; |
606 | obj["value" ] = "TEST" ; |
607 | QCOMPARE(obj.size(), 1); |
608 | QCOMPARE(obj.value("value" ), "TEST" ); |
609 | |
610 | // same as previous, but this is a QJsonValueRef |
611 | QJsonValueRef rv = obj["value" ]; |
612 | obj["prop2" ] = rv; |
613 | QCOMPARE(obj.size(), 2); |
614 | QCOMPARE(obj.value("value" ), "TEST" ); |
615 | QEXPECT_FAIL("" , "QTBUG-83398: design flaw: the obj[] call invalidates the QJsonValueRef" , Continue); |
616 | QCOMPARE(obj.value("prop2" ), "TEST" ); |
617 | } |
618 | { |
619 | QJsonObject obj; |
620 | obj["value" ] = "TEST" ; |
621 | QCOMPARE(obj.size(), 1); |
622 | QCOMPARE(obj.value("value" ), "TEST" ); |
623 | |
624 | QJsonValueRef v = obj["value" ]; |
625 | QJsonObject obj2 = obj; |
626 | obj.insert(key: "prop2" , value: v); |
627 | QCOMPARE(obj.size(), 2); |
628 | QCOMPARE(obj.value("value" ), "TEST" ); |
629 | QCOMPARE(obj.value("prop2" ), "TEST" ); |
630 | QCOMPARE(obj2.size(), 1); |
631 | QCOMPARE(obj2.value("value" ), "TEST" ); |
632 | } |
633 | } |
634 | |
635 | void tst_QtJson::testArraySimple() |
636 | { |
637 | QJsonArray array; |
638 | array.append(value: 999.); |
639 | array.append(value: QString::fromLatin1(str: "test" )); |
640 | array.append(value: true); |
641 | |
642 | QJsonValue val = array.at(i: 0); |
643 | QCOMPARE(array.at(0).toDouble(), 999.); |
644 | QCOMPARE(array.at(1).toString(), QString("test" )); |
645 | QCOMPARE(array.at(2).toBool(), true); |
646 | QCOMPARE(array.size(), 3); |
647 | |
648 | // if we put a JsonValue into the JsonArray and retrieve |
649 | // it, it should be identical. |
650 | QJsonValue value(QLatin1String("foo" )); |
651 | array.append(value); |
652 | QCOMPARE(array.at(3), value); |
653 | |
654 | int size = array.size(); |
655 | array.removeAt(i: 2); |
656 | --size; |
657 | QCOMPARE(array.size(), size); |
658 | |
659 | QJsonValue taken = array.takeAt(i: 0); |
660 | --size; |
661 | QCOMPARE(taken.toDouble(), 999.); |
662 | QCOMPARE(array.size(), size); |
663 | |
664 | // check whether null values work |
665 | array.append(value: QJsonValue()); |
666 | ++size; |
667 | QCOMPARE(array.size(), size); |
668 | QCOMPARE(array.last().type(), QJsonValue::Null); |
669 | QCOMPARE(array.last(), QJsonValue()); |
670 | |
671 | QCOMPARE(array.first().type(), QJsonValue::String); |
672 | QCOMPARE(array.first(), QJsonValue(QLatin1String("test" ))); |
673 | |
674 | array.prepend(value: false); |
675 | QCOMPARE(array.first().type(), QJsonValue::Bool); |
676 | QCOMPARE(array.first(), QJsonValue(false)); |
677 | |
678 | QCOMPARE(array.at(-1), QJsonValue(QJsonValue::Undefined)); |
679 | QCOMPARE(array.at(array.size()), QJsonValue(QJsonValue::Undefined)); |
680 | |
681 | array.replace(i: 0, value: -555.); |
682 | QCOMPARE(array.first().type(), QJsonValue::Double); |
683 | QCOMPARE(array.first(), QJsonValue(-555.)); |
684 | QCOMPARE(array.at(1).type(), QJsonValue::String); |
685 | QCOMPARE(array.at(1), QJsonValue(QLatin1String("test" ))); |
686 | } |
687 | |
688 | void tst_QtJson::testArrayInsertCopies() |
689 | { |
690 | { |
691 | QJsonArray array; |
692 | array.append(value: "TEST" ); |
693 | QCOMPARE(array.size(), 1); |
694 | QCOMPARE(array.at(0), "TEST" ); |
695 | |
696 | array.append(value: array.at(i: 0)); |
697 | QCOMPARE(array.size(), 2); |
698 | QCOMPARE(array.at(0), "TEST" ); |
699 | QCOMPARE(array.at(1), "TEST" ); |
700 | } |
701 | { |
702 | QJsonArray array; |
703 | array.append(value: "TEST" ); |
704 | QCOMPARE(array.size(), 1); |
705 | QCOMPARE(array.at(0), "TEST" ); |
706 | |
707 | array.prepend(value: array.at(i: 0)); |
708 | QCOMPARE(array.size(), 2); |
709 | QCOMPARE(array.at(0), "TEST" ); |
710 | QCOMPARE(array.at(1), "TEST" ); |
711 | } |
712 | } |
713 | |
714 | void tst_QtJson::testValueObject() |
715 | { |
716 | QJsonObject object; |
717 | object.insert(key: "number" , value: 999.); |
718 | object.insert(key: "string" , value: QLatin1String("test" )); |
719 | object.insert(key: "boolean" , value: true); |
720 | |
721 | QJsonValue value(object); |
722 | |
723 | // if we don't modify the original JsonObject, toObject() |
724 | // on the JsonValue should return the same object (non-detached). |
725 | QCOMPARE(value.toObject(), object); |
726 | |
727 | // if we modify the original object, it should detach |
728 | object.insert(key: "test" , value: QJsonValue(QLatin1String("test" ))); |
729 | QVERIFY2(value.toObject() != object, "object should have detached" ); |
730 | } |
731 | |
732 | void tst_QtJson::testValueArray() |
733 | { |
734 | QJsonArray array; |
735 | array.append(value: 999.); |
736 | array.append(value: QLatin1String("test" )); |
737 | array.append(value: true); |
738 | |
739 | QJsonValue value(array); |
740 | |
741 | // if we don't modify the original JsonArray, toArray() |
742 | // on the JsonValue should return the same object (non-detached). |
743 | QCOMPARE(value.toArray(), array); |
744 | |
745 | // if we modify the original array, it should detach |
746 | array.append(value: QLatin1String("test" )); |
747 | QVERIFY2(value.toArray() != array, "array should have detached" ); |
748 | } |
749 | |
750 | void tst_QtJson::testObjectNested() |
751 | { |
752 | QJsonObject inner, outer; |
753 | inner.insert(key: "number" , value: 999.); |
754 | outer.insert(key: "nested" , value: inner); |
755 | |
756 | // if we don't modify the original JsonObject, value() |
757 | // should return the same object (non-detached). |
758 | QJsonObject value = outer.value(key: "nested" ).toObject(); |
759 | QCOMPARE(value, inner); |
760 | QCOMPARE(value.value("number" ).toDouble(), 999.); |
761 | |
762 | // if we modify the original object, it should detach and not |
763 | // affect the nested object |
764 | inner.insert(key: "number" , value: 555.); |
765 | value = outer.value(key: "nested" ).toObject(); |
766 | QVERIFY2(inner.value("number" ).toDouble() != value.value("number" ).toDouble(), |
767 | "object should have detached" ); |
768 | |
769 | // array in object |
770 | QJsonArray array; |
771 | array.append(value: 123.); |
772 | array.append(value: 456.); |
773 | outer.insert(key: "array" , value: array); |
774 | QCOMPARE(outer.value("array" ).toArray(), array); |
775 | QCOMPARE(outer.value("array" ).toArray().at(1).toDouble(), 456.); |
776 | |
777 | // two deep objects |
778 | QJsonObject twoDeep; |
779 | twoDeep.insert(key: "boolean" , value: true); |
780 | inner.insert(key: "nested" , value: twoDeep); |
781 | outer.insert(key: "nested" , value: inner); |
782 | QCOMPARE(outer.value("nested" ).toObject().value("nested" ).toObject(), twoDeep); |
783 | QCOMPARE(outer.value("nested" ).toObject().value("nested" ).toObject().value("boolean" ).toBool(), |
784 | true); |
785 | } |
786 | |
787 | void tst_QtJson::testArrayNested() |
788 | { |
789 | QJsonArray inner, outer; |
790 | inner.append(value: 999.); |
791 | outer.append(value: inner); |
792 | |
793 | // if we don't modify the original JsonArray, value() |
794 | // should return the same array (non-detached). |
795 | QJsonArray value = outer.at(i: 0).toArray(); |
796 | QCOMPARE(value, inner); |
797 | QCOMPARE(value.at(0).toDouble(), 999.); |
798 | |
799 | // if we modify the original array, it should detach and not |
800 | // affect the nested array |
801 | inner.append(value: 555.); |
802 | value = outer.at(i: 0).toArray(); |
803 | QVERIFY2(inner.size() != value.size(), "array should have detached" ); |
804 | |
805 | // objects in arrays |
806 | QJsonObject object; |
807 | object.insert(key: "boolean" , value: true); |
808 | outer.append(value: object); |
809 | QCOMPARE(outer.last().toObject(), object); |
810 | QCOMPARE(outer.last().toObject().value("boolean" ).toBool(), true); |
811 | |
812 | // two deep arrays |
813 | QJsonArray twoDeep; |
814 | twoDeep.append(value: QJsonValue(QString::fromLatin1(str: "nested" ))); |
815 | inner.append(value: twoDeep); |
816 | outer.append(value: inner); |
817 | QCOMPARE(outer.last().toArray().last().toArray(), twoDeep); |
818 | QCOMPARE(outer.last().toArray().last().toArray().at(0).toString(), QString("nested" )); |
819 | } |
820 | |
821 | void tst_QtJson::testArrayNestedEmpty() |
822 | { |
823 | QJsonObject object; |
824 | QJsonArray inner; |
825 | object.insert(key: "inner" , value: inner); |
826 | QJsonValue val = object.value(key: "inner" ); |
827 | QJsonArray value = object.value(key: "inner" ).toArray(); |
828 | QVERIFY(QJsonDocument(value).isArray()); |
829 | QCOMPARE(value.size(), 0); |
830 | QCOMPARE(value, inner); |
831 | QCOMPARE(value.size(), 0); |
832 | object.insert(key: "count" , value: 0.); |
833 | QCOMPARE(object.value("inner" ).toArray().size(), 0); |
834 | QVERIFY(object.value("inner" ).toArray().isEmpty()); |
835 | QJsonDocument(object).toBinaryData(); |
836 | QCOMPARE(object.value("inner" ).toArray().size(), 0); |
837 | } |
838 | |
839 | void tst_QtJson::testObjectNestedEmpty() |
840 | { |
841 | QJsonObject object; |
842 | QJsonObject inner; |
843 | QJsonObject inner2; |
844 | object.insert(key: "inner" , value: inner); |
845 | object.insert(key: "inner2" , value: inner2); |
846 | QJsonObject value = object.value(key: "inner" ).toObject(); |
847 | QVERIFY(QJsonDocument(value).isObject()); |
848 | QCOMPARE(value.size(), 0); |
849 | QCOMPARE(value, inner); |
850 | QCOMPARE(value.size(), 0); |
851 | object.insert(key: "count" , value: 0.); |
852 | QCOMPARE(object.value("inner" ).toObject().size(), 0); |
853 | QCOMPARE(object.value("inner" ).type(), QJsonValue::Object); |
854 | QJsonDocument(object).toBinaryData(); |
855 | QVERIFY(object.value("inner" ).toObject().isEmpty()); |
856 | QVERIFY(object.value("inner2" ).toObject().isEmpty()); |
857 | QJsonDocument doc = QJsonDocument::fromBinaryData(data: QJsonDocument(object).toBinaryData()); |
858 | QVERIFY(!doc.isNull()); |
859 | QJsonObject reconstituted(doc.object()); |
860 | QCOMPARE(reconstituted.value("inner" ).toObject().size(), 0); |
861 | QCOMPARE(reconstituted.value("inner" ).type(), QJsonValue::Object); |
862 | QCOMPARE(reconstituted.value("inner2" ).type(), QJsonValue::Object); |
863 | } |
864 | |
865 | void tst_QtJson::testArrayComfortOperators() |
866 | { |
867 | QJsonArray first; |
868 | first.append(value: 123.); |
869 | first.append(value: QLatin1String("foo" )); |
870 | |
871 | QJsonArray second = QJsonArray() << 123. << QLatin1String("foo" ); |
872 | QCOMPARE(first, second); |
873 | |
874 | first = first + QLatin1String("bar" ); |
875 | second += QLatin1String("bar" ); |
876 | QCOMPARE(first, second); |
877 | } |
878 | |
879 | void tst_QtJson::testValueRef() |
880 | { |
881 | QJsonArray array; |
882 | array.append(value: 1.); |
883 | array.append(value: 2.); |
884 | array.append(value: 3.); |
885 | array.append(value: 4); |
886 | array.append(value: 4.1); |
887 | array[1] = false; |
888 | |
889 | QCOMPARE(array.size(), 5); |
890 | QCOMPARE(array.at(0).toDouble(), 1.); |
891 | QCOMPARE(array.at(2).toDouble(), 3.); |
892 | QCOMPARE(array.at(3).toInt(), 4); |
893 | QCOMPARE(array.at(4).toInt(), 0); |
894 | QCOMPARE(array.at(1).type(), QJsonValue::Bool); |
895 | QCOMPARE(array.at(1).toBool(), false); |
896 | |
897 | QJsonObject object; |
898 | object[QLatin1String("key" )] = true; |
899 | QCOMPARE(object.size(), 1); |
900 | object.insert(key: QLatin1String("null" ), value: QJsonValue()); |
901 | QCOMPARE(object.value(QLatin1String("null" )), QJsonValue()); |
902 | object[QLatin1String("null" )] = 100.; |
903 | QCOMPARE(object.value(QLatin1String("null" )).type(), QJsonValue::Double); |
904 | QJsonValue val = qAsConst(t&: object)[QLatin1String("null" )]; |
905 | QCOMPARE(val.toDouble(), 100.); |
906 | QCOMPARE(object.size(), 2); |
907 | |
908 | array[1] = array[2] = object[QLatin1String("key" )] = 42; |
909 | QCOMPARE(array[1], array[2]); |
910 | QCOMPARE(array[2], object[QLatin1String("key" )]); |
911 | QCOMPARE(object.value(QLatin1String("key" )), QJsonValue(42)); |
912 | } |
913 | |
914 | void tst_QtJson::testValueRefComparison() |
915 | { |
916 | QJsonValue a0 = 42.; |
917 | QJsonValue a1 = QStringLiteral("142" ); |
918 | |
919 | #define CHECK_IMPL(lhs, rhs, ineq) \ |
920 | QCOMPARE(lhs, rhs); \ |
921 | QVERIFY(!(lhs != rhs)); \ |
922 | QVERIFY(lhs != ineq); \ |
923 | QVERIFY(!(lhs == ineq)); \ |
924 | QVERIFY(ineq != rhs); \ |
925 | QVERIFY(!(ineq == rhs)); \ |
926 | /* end */ |
927 | |
928 | #define CHECK(lhs, rhs, ineq) \ |
929 | do { \ |
930 | CHECK_IMPL(lhs, rhs, ineq) \ |
931 | CHECK_IMPL(qAsConst(lhs), rhs, ineq) \ |
932 | CHECK_IMPL(lhs, qAsConst(rhs), ineq) \ |
933 | CHECK_IMPL(qAsConst(lhs), qAsConst(rhs), ineq) \ |
934 | } while (0) |
935 | |
936 | // check that the (in)equality operators aren't ambiguous in C++20: |
937 | QJsonArray a = {a0, a1}; |
938 | |
939 | Q_STATIC_ASSERT((std::is_same<decltype(a[0]), QJsonValueRef>::value)); |
940 | |
941 | auto r0 = a.begin()[0]; |
942 | auto r1 = a.begin()[1]; |
943 | auto c0 = qAsConst(t&: a).begin()[0]; |
944 | // ref <> ref |
945 | CHECK(r0, r0, r1); |
946 | // cref <> ref |
947 | CHECK(c0, r0, r1); |
948 | // ref <> cref |
949 | CHECK(r0, c0, r1); |
950 | // ref <> val |
951 | CHECK(r0, a0, r1); |
952 | // cref <> val |
953 | CHECK(c0, a0, r1); |
954 | // val <> ref |
955 | CHECK(a0, r0, a1); |
956 | // val <> cref |
957 | CHECK(a0, c0, a1); |
958 | // val <> val |
959 | CHECK(a0, a0, a1); |
960 | |
961 | #undef CHECK |
962 | #undef CHECK_IMPL |
963 | } |
964 | |
965 | void tst_QtJson::testObjectIteration() |
966 | { |
967 | QJsonObject object; |
968 | |
969 | for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) |
970 | QVERIFY(false); |
971 | |
972 | const QString property = "kkk" ; |
973 | object.insert(key: property, value: 11); |
974 | object.take(key: property); |
975 | for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) |
976 | QVERIFY(false); |
977 | |
978 | for (int i = 0; i < 10; ++i) |
979 | object[QString::number(i)] = (double)i; |
980 | |
981 | QCOMPARE(object.size(), 10); |
982 | |
983 | QCOMPARE(object.begin()->toDouble(), object.constBegin()->toDouble()); |
984 | |
985 | for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) { |
986 | QJsonValue value = it.value(); |
987 | QCOMPARE((double)it.key().toInt(), value.toDouble()); |
988 | } |
989 | |
990 | { |
991 | QJsonObject object2 = object; |
992 | QCOMPARE(object, object2); |
993 | |
994 | QJsonValue val = *object2.begin(); |
995 | object2.erase(it: object2.begin()); |
996 | QCOMPARE(object.size(), 10); |
997 | QCOMPARE(object2.size(), 9); |
998 | |
999 | for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) { |
1000 | QJsonValue value = it.value(); |
1001 | QVERIFY(it.value() != val); |
1002 | QCOMPARE((double)it.key().toInt(), value.toDouble()); |
1003 | } |
1004 | } |
1005 | |
1006 | { |
1007 | QJsonObject object2 = object; |
1008 | QCOMPARE(object, object2); |
1009 | |
1010 | QJsonObject::iterator it = object2.find(key: QString::number(5)); |
1011 | QJsonValue val = *it; |
1012 | object2.erase(it); |
1013 | QCOMPARE(object.size(), 10); |
1014 | QCOMPARE(object2.size(), 9); |
1015 | |
1016 | for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) { |
1017 | QJsonValue value = it.value(); |
1018 | QVERIFY(it.value() != val); |
1019 | QCOMPARE((double)it.key().toInt(), value.toDouble()); |
1020 | } |
1021 | } |
1022 | |
1023 | { |
1024 | QJsonObject::Iterator it = object.begin(); |
1025 | it += 5; |
1026 | QCOMPARE(QJsonValue(it.value()).toDouble(), 5.); |
1027 | it -= 3; |
1028 | QCOMPARE(QJsonValue(it.value()).toDouble(), 2.); |
1029 | QJsonObject::Iterator it2 = it + 5; |
1030 | QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.); |
1031 | it2 = it - 1; |
1032 | QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.); |
1033 | } |
1034 | |
1035 | { |
1036 | QJsonObject::ConstIterator it = object.constBegin(); |
1037 | it += 5; |
1038 | QCOMPARE(QJsonValue(it.value()).toDouble(), 5.); |
1039 | it -= 3; |
1040 | QCOMPARE(QJsonValue(it.value()).toDouble(), 2.); |
1041 | QJsonObject::ConstIterator it2 = it + 5; |
1042 | QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.); |
1043 | it2 = it - 1; |
1044 | QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.); |
1045 | } |
1046 | |
1047 | QJsonObject::Iterator it = object.begin(); |
1048 | while (!object.isEmpty()) |
1049 | it = object.erase(it); |
1050 | QCOMPARE(object.size() , 0); |
1051 | QCOMPARE(it, object.end()); |
1052 | } |
1053 | |
1054 | void tst_QtJson::testArrayIteration() |
1055 | { |
1056 | QJsonArray array; |
1057 | for (int i = 0; i < 10; ++i) |
1058 | array.append(value: i); |
1059 | |
1060 | QCOMPARE(array.size(), 10); |
1061 | |
1062 | int i = 0; |
1063 | for (QJsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) { |
1064 | QJsonValue value = (*it); |
1065 | QCOMPARE((double)i, value.toDouble()); |
1066 | } |
1067 | |
1068 | QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble()); |
1069 | |
1070 | { |
1071 | QJsonArray array2 = array; |
1072 | QCOMPARE(array, array2); |
1073 | |
1074 | QJsonValue val = *array2.begin(); |
1075 | array2.erase(it: array2.begin()); |
1076 | QCOMPARE(array.size(), 10); |
1077 | QCOMPARE(array2.size(), 9); |
1078 | |
1079 | i = 1; |
1080 | for (QJsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { |
1081 | QJsonValue value = (*it); |
1082 | QCOMPARE((double)i, value.toDouble()); |
1083 | } |
1084 | } |
1085 | |
1086 | { |
1087 | QJsonArray::Iterator it = array.begin(); |
1088 | it += 5; |
1089 | QCOMPARE(QJsonValue((*it)).toDouble(), 5.); |
1090 | it -= 3; |
1091 | QCOMPARE(QJsonValue((*it)).toDouble(), 2.); |
1092 | QJsonArray::Iterator it2 = it + 5; |
1093 | QCOMPARE(QJsonValue(*it2).toDouble(), 7.); |
1094 | it2 = it - 1; |
1095 | QCOMPARE(QJsonValue(*it2).toDouble(), 1.); |
1096 | } |
1097 | |
1098 | { |
1099 | QJsonArray::ConstIterator it = array.constBegin(); |
1100 | it += 5; |
1101 | QCOMPARE(QJsonValue((*it)).toDouble(), 5.); |
1102 | it -= 3; |
1103 | QCOMPARE(QJsonValue((*it)).toDouble(), 2.); |
1104 | QJsonArray::ConstIterator it2 = it + 5; |
1105 | QCOMPARE(QJsonValue(*it2).toDouble(), 7.); |
1106 | it2 = it - 1; |
1107 | QCOMPARE(QJsonValue(*it2).toDouble(), 1.); |
1108 | } |
1109 | |
1110 | QJsonArray::Iterator it = array.begin(); |
1111 | while (!array.isEmpty()) |
1112 | it = array.erase(it); |
1113 | QCOMPARE(array.size() , 0); |
1114 | QCOMPARE(it, array.end()); |
1115 | } |
1116 | |
1117 | void tst_QtJson::testObjectFind() |
1118 | { |
1119 | QJsonObject object; |
1120 | for (int i = 0; i < 10; ++i) |
1121 | object[QString::number(i)] = i; |
1122 | |
1123 | QCOMPARE(object.size(), 10); |
1124 | |
1125 | QJsonObject::iterator it = object.find(key: QLatin1String("1" )); |
1126 | QCOMPARE((*it).toDouble(), 1.); |
1127 | it = object.find(key: QString("11" )); |
1128 | QCOMPARE((*it).type(), QJsonValue::Undefined); |
1129 | QCOMPARE(it, object.end()); |
1130 | |
1131 | QJsonObject::const_iterator cit = object.constFind(key: QLatin1String("1" )); |
1132 | QCOMPARE((*cit).toDouble(), 1.); |
1133 | cit = object.constFind(key: QString("11" )); |
1134 | QCOMPARE((*it).type(), QJsonValue::Undefined); |
1135 | QCOMPARE(it, object.end()); |
1136 | } |
1137 | |
1138 | void tst_QtJson::testDocument() |
1139 | { |
1140 | QJsonDocument doc; |
1141 | QCOMPARE(doc.isEmpty(), true); |
1142 | QCOMPARE(doc.isArray(), false); |
1143 | QCOMPARE(doc.isObject(), false); |
1144 | |
1145 | QJsonObject object; |
1146 | doc.setObject(object); |
1147 | QCOMPARE(doc.isEmpty(), false); |
1148 | QCOMPARE(doc.isArray(), false); |
1149 | QCOMPARE(doc.isObject(), true); |
1150 | |
1151 | object.insert(key: QLatin1String("Key" ), value: QLatin1String("Value" )); |
1152 | doc.setObject(object); |
1153 | QCOMPARE(doc.isEmpty(), false); |
1154 | QCOMPARE(doc.isArray(), false); |
1155 | QCOMPARE(doc.isObject(), true); |
1156 | QCOMPARE(doc.object(), object); |
1157 | QCOMPARE(doc.array(), QJsonArray()); |
1158 | |
1159 | doc = QJsonDocument(); |
1160 | QCOMPARE(doc.isEmpty(), true); |
1161 | QCOMPARE(doc.isArray(), false); |
1162 | QCOMPARE(doc.isObject(), false); |
1163 | |
1164 | QJsonArray array; |
1165 | doc.setArray(array); |
1166 | QCOMPARE(doc.isEmpty(), false); |
1167 | QCOMPARE(doc.isArray(), true); |
1168 | QCOMPARE(doc.isObject(), false); |
1169 | |
1170 | array.append(value: QLatin1String("Value" )); |
1171 | doc.setArray(array); |
1172 | QCOMPARE(doc.isEmpty(), false); |
1173 | QCOMPARE(doc.isArray(), true); |
1174 | QCOMPARE(doc.isObject(), false); |
1175 | QCOMPARE(doc.array(), array); |
1176 | QCOMPARE(doc.object(), QJsonObject()); |
1177 | |
1178 | QJsonObject outer; |
1179 | outer.insert(key: QLatin1String("outerKey" ), value: 22); |
1180 | QJsonObject inner; |
1181 | inner.insert(key: QLatin1String("innerKey" ), value: 42); |
1182 | outer.insert(key: QLatin1String("innter" ), value: inner); |
1183 | QJsonArray innerArray; |
1184 | innerArray.append(value: 23); |
1185 | outer.insert(key: QLatin1String("innterArray" ), value: innerArray); |
1186 | |
1187 | QJsonDocument doc2(outer.value(key: QLatin1String("innter" )).toObject()); |
1188 | QVERIFY(doc2.object().contains(QLatin1String("innerKey" ))); |
1189 | QCOMPARE(doc2.object().value(QLatin1String("innerKey" )), QJsonValue(42)); |
1190 | |
1191 | QJsonDocument doc3; |
1192 | doc3.setObject(outer.value(key: QLatin1String("innter" )).toObject()); |
1193 | QCOMPARE(doc3.isArray(), false); |
1194 | QCOMPARE(doc3.isObject(), true); |
1195 | QVERIFY(doc3.object().contains(QString("innerKey" ))); |
1196 | QCOMPARE(doc3.object().value(QLatin1String("innerKey" )), QJsonValue(42)); |
1197 | |
1198 | QJsonDocument doc4(outer.value(key: QLatin1String("innterArray" )).toArray()); |
1199 | QCOMPARE(doc4.isArray(), true); |
1200 | QCOMPARE(doc4.isObject(), false); |
1201 | QCOMPARE(doc4.array().size(), 1); |
1202 | QCOMPARE(doc4.array().at(0), QJsonValue(23)); |
1203 | |
1204 | QJsonDocument doc5; |
1205 | doc5.setArray(outer.value(key: QLatin1String("innterArray" )).toArray()); |
1206 | QCOMPARE(doc5.isArray(), true); |
1207 | QCOMPARE(doc5.isObject(), false); |
1208 | QCOMPARE(doc5.array().size(), 1); |
1209 | QCOMPARE(doc5.array().at(0), QJsonValue(23)); |
1210 | } |
1211 | |
1212 | void tst_QtJson::nullValues() |
1213 | { |
1214 | QJsonArray array; |
1215 | array.append(value: QJsonValue()); |
1216 | |
1217 | QCOMPARE(array.size(), 1); |
1218 | QCOMPARE(array.at(0), QJsonValue()); |
1219 | |
1220 | QJsonObject object; |
1221 | object.insert(key: QString("key" ), value: QJsonValue()); |
1222 | QCOMPARE(object.contains(QLatin1String("key" )), true); |
1223 | QCOMPARE(object.size(), 1); |
1224 | QCOMPARE(object.value(QString("key" )), QJsonValue()); |
1225 | } |
1226 | |
1227 | void tst_QtJson::nullArrays() |
1228 | { |
1229 | QJsonArray nullArray; |
1230 | QJsonArray nonNull; |
1231 | nonNull.append(value: QLatin1String("bar" )); |
1232 | |
1233 | QCOMPARE(nullArray, QJsonArray()); |
1234 | QVERIFY(nullArray != nonNull); |
1235 | QVERIFY(nonNull != nullArray); |
1236 | |
1237 | QCOMPARE(nullArray.size(), 0); |
1238 | QCOMPARE(nullArray.takeAt(0), QJsonValue(QJsonValue::Undefined)); |
1239 | QCOMPARE(nullArray.first(), QJsonValue(QJsonValue::Undefined)); |
1240 | QCOMPARE(nullArray.last(), QJsonValue(QJsonValue::Undefined)); |
1241 | nullArray.removeAt(i: 0); |
1242 | nullArray.removeAt(i: -1); |
1243 | |
1244 | nullArray.append(value: QString("bar" )); |
1245 | nullArray.removeAt(i: 0); |
1246 | |
1247 | QCOMPARE(nullArray.size(), 0); |
1248 | QCOMPARE(nullArray.takeAt(0), QJsonValue(QJsonValue::Undefined)); |
1249 | QCOMPARE(nullArray.first(), QJsonValue(QJsonValue::Undefined)); |
1250 | QCOMPARE(nullArray.last(), QJsonValue(QJsonValue::Undefined)); |
1251 | nullArray.removeAt(i: 0); |
1252 | nullArray.removeAt(i: -1); |
1253 | } |
1254 | |
1255 | void tst_QtJson::nullObject() |
1256 | { |
1257 | QJsonObject nullObject; |
1258 | QJsonObject nonNull; |
1259 | nonNull.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
1260 | |
1261 | QCOMPARE(nullObject, QJsonObject()); |
1262 | QVERIFY(nullObject != nonNull); |
1263 | QVERIFY(nonNull != nullObject); |
1264 | |
1265 | QCOMPARE(nullObject.size(), 0); |
1266 | QCOMPARE(nullObject.keys(), QStringList()); |
1267 | nullObject.remove(key: "foo" ); |
1268 | QCOMPARE(nullObject, QJsonObject()); |
1269 | QCOMPARE(nullObject.take("foo" ), QJsonValue(QJsonValue::Undefined)); |
1270 | QCOMPARE(nullObject.contains("foo" ), false); |
1271 | |
1272 | nullObject.insert(key: "foo" , value: QString("bar" )); |
1273 | nullObject.remove(key: "foo" ); |
1274 | |
1275 | QCOMPARE(nullObject.size(), 0); |
1276 | QCOMPARE(nullObject.keys(), QStringList()); |
1277 | nullObject.remove(key: "foo" ); |
1278 | QCOMPARE(nullObject, QJsonObject()); |
1279 | QCOMPARE(nullObject.take("foo" ), QJsonValue(QJsonValue::Undefined)); |
1280 | QCOMPARE(nullObject.contains("foo" ), false); |
1281 | } |
1282 | |
1283 | void tst_QtJson::constNullObject() |
1284 | { |
1285 | const QJsonObject nullObject; |
1286 | QJsonObject nonNull; |
1287 | nonNull.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
1288 | |
1289 | QCOMPARE(nullObject, QJsonObject()); |
1290 | QVERIFY(nullObject != nonNull); |
1291 | QVERIFY(nonNull != nullObject); |
1292 | |
1293 | QCOMPARE(nullObject.size(), 0); |
1294 | QCOMPARE(nullObject.keys(), QStringList()); |
1295 | QCOMPARE(nullObject, QJsonObject()); |
1296 | QCOMPARE(nullObject.contains("foo" ), false); |
1297 | QCOMPARE(nullObject["foo" ], QJsonValue(QJsonValue::Undefined)); |
1298 | } |
1299 | |
1300 | void tst_QtJson::keySorting_data() |
1301 | { |
1302 | QTest::addColumn<QString>(name: "json" ); |
1303 | QTest::addColumn<QStringList>(name: "sortedKeys" ); |
1304 | |
1305 | QStringList list = {"A" , "B" }; |
1306 | QTest::newRow(dataTag: "sorted-ascii-2" ) << R"({ "A": false, "B": true })" << list; |
1307 | const char *json = "{ \"B\": true, \"A\": false }" ; |
1308 | QTest::newRow(dataTag: "unsorted-ascii-2" ) << json << list; |
1309 | |
1310 | list = QStringList{"A" , "B" , "C" , "D" , "E" }; |
1311 | QTest::newRow(dataTag: "sorted-ascii-5" ) << R"({"A": 1, "B": 2, "C": 3, "D": 4, "E": 5})" << list; |
1312 | QTest::newRow(dataTag: "unsorted-ascii-5" ) << R"({"A": 1, "C": 3, "D": 4, "B": 2, "E": 5})" << list; |
1313 | QTest::newRow(dataTag: "inverse-sorted-ascii-5" ) << R"({"E": 5, "D": 4, "C": 3, "B": 2, "A": 1})" << list; |
1314 | |
1315 | list = QStringList{"á" , "é" , "í" , "ó" , "ú" }; |
1316 | QTest::newRow(dataTag: "sorted-latin1" ) << R"({"á": 1, "é": 2, "í": 3, "ó": 4, "ú": 5})" << list; |
1317 | QTest::newRow(dataTag: "unsorted-latin1" ) << R"({"á": 1, "í": 3, "ó": 4, "é": 2, "ú": 5})" << list; |
1318 | QTest::newRow(dataTag: "inverse-sorted-latin1" ) << R"({"ú": 5, "ó": 4, "í": 3, "é": 2, "á": 1})" << list; |
1319 | |
1320 | QTest::newRow(dataTag: "sorted-escaped-latin1" ) << R"({"\u00e1": 1, "\u00e9": 2, "\u00ed": 3, "\u00f3": 4, "\u00fa": 5})" << list; |
1321 | QTest::newRow(dataTag: "unsorted-escaped-latin1" ) << R"({"\u00e1": 1, "\u00ed": 3, "\u00f3": 4, "\u00e9": 2, "\u00fa": 5})" << list; |
1322 | QTest::newRow(dataTag: "inverse-sorted-escaped-latin1" ) << R"({"\u00fa": 5, "\u00f3": 4, "\u00ed": 3, "\u00e9": 2, "\u00e1": 1})" << list; |
1323 | |
1324 | list = QStringList{"A" , "α" , "Я" , "€" , "测" }; |
1325 | QTest::newRow(dataTag: "sorted" ) << R"({"A": 1, "α": 2, "Я": 3, "€": 4, "测": 5})" << list; |
1326 | QTest::newRow(dataTag: "unsorted" ) << R"({"A": 1, "Я": 3, "€": 4, "α": 2, "测": 5})" << list; |
1327 | QTest::newRow(dataTag: "inverse-sorted" ) << R"({"测": 5, "€": 4, "Я": 3, "α": 2, "A": 1})" << list; |
1328 | |
1329 | QTest::newRow(dataTag: "sorted-escaped" ) << R"({"A": 1, "\u03b1": 2, "\u042f": 3, "\u20ac": 4, "\u6d4b": 5})" << list; |
1330 | QTest::newRow(dataTag: "unsorted-escaped" ) << R"({"A": 1, "\u042f": 3, "\u20ac": 4, "\u03b1": 2, "\u6d4b": 5})" << list; |
1331 | QTest::newRow(dataTag: "inverse-sorted-escaped" ) << R"({"\u6d4b": 5, "\u20ac": 4, "\u042f": 3, "\u03b1": 2, "A": 1})" << list; |
1332 | } |
1333 | |
1334 | void tst_QtJson::keySorting() |
1335 | { |
1336 | QFETCH(QString, json); |
1337 | QFETCH(QStringList, sortedKeys); |
1338 | QJsonDocument doc = QJsonDocument::fromJson(json: json.toUtf8()); |
1339 | |
1340 | QCOMPARE(doc.isObject(), true); |
1341 | |
1342 | QJsonObject o = doc.object(); |
1343 | QCOMPARE(o.size(), sortedKeys.size()); |
1344 | QCOMPARE(o.keys(), sortedKeys); |
1345 | QJsonObject::const_iterator it = o.constBegin(); |
1346 | QStringList::const_iterator it2 = sortedKeys.constBegin(); |
1347 | for ( ; it != o.constEnd(); ++it, ++it2) |
1348 | QCOMPARE(it.key(), *it2); |
1349 | } |
1350 | |
1351 | void tst_QtJson::undefinedValues() |
1352 | { |
1353 | QJsonObject object; |
1354 | object.insert(key: "Key" , value: QJsonValue(QJsonValue::Undefined)); |
1355 | QCOMPARE(object.size(), 0); |
1356 | object["Key" ] = QJsonValue(QJsonValue::Undefined); |
1357 | QCOMPARE(object.size(), 0); |
1358 | |
1359 | object.insert(key: "Key" , value: QLatin1String("Value" )); |
1360 | QCOMPARE(object.size(), 1); |
1361 | QCOMPARE(object.value("Key" ).type(), QJsonValue::String); |
1362 | QCOMPARE(object.value("foo" ).type(), QJsonValue::Undefined); |
1363 | object.insert(key: "Key" , value: QJsonValue(QJsonValue::Undefined)); |
1364 | QCOMPARE(object.size(), 0); |
1365 | QCOMPARE(object.value("Key" ).type(), QJsonValue::Undefined); |
1366 | |
1367 | QJsonArray array; |
1368 | array.append(value: QJsonValue(QJsonValue::Undefined)); |
1369 | QCOMPARE(array.size(), 1); |
1370 | QCOMPARE(array.at(0).type(), QJsonValue::Null); |
1371 | |
1372 | QCOMPARE(array.at(1).type(), QJsonValue::Undefined); |
1373 | QCOMPARE(array.at(-1).type(), QJsonValue::Undefined); |
1374 | } |
1375 | |
1376 | void tst_QtJson::fromVariant_data() |
1377 | { |
1378 | QTest::addColumn<QVariant>(name: "variant" ); |
1379 | QTest::addColumn<QJsonValue>(name: "jsonvalue" ); |
1380 | |
1381 | bool boolValue = true; |
1382 | int intValue = -1; |
1383 | uint uintValue = 1; |
1384 | long long longlongValue = -2; |
1385 | unsigned long long ulonglongValue = 2; |
1386 | float floatValue = 3.3f; |
1387 | double doubleValue = 4.4; |
1388 | QString stringValue("str" ); |
1389 | |
1390 | QStringList stringList; |
1391 | stringList.append(t: stringValue); |
1392 | stringList.append(t: "str2" ); |
1393 | QJsonArray jsonArray_string; |
1394 | jsonArray_string.append(value: stringValue); |
1395 | jsonArray_string.append(value: "str2" ); |
1396 | |
1397 | QVariantList variantList; |
1398 | variantList.append(t: boolValue); |
1399 | variantList.append(t: floatValue); |
1400 | variantList.append(t: doubleValue); |
1401 | variantList.append(t: stringValue); |
1402 | variantList.append(t: stringList); |
1403 | variantList.append(t: QVariant::fromValue(value: nullptr)); |
1404 | variantList.append(t: QVariant()); |
1405 | QJsonArray jsonArray_variant; |
1406 | jsonArray_variant.append(value: boolValue); |
1407 | jsonArray_variant.append(value: floatValue); |
1408 | jsonArray_variant.append(value: doubleValue); |
1409 | jsonArray_variant.append(value: stringValue); |
1410 | jsonArray_variant.append(value: jsonArray_string); |
1411 | jsonArray_variant.append(value: QJsonValue(QJsonValue::Null)); |
1412 | jsonArray_variant.append(value: QJsonValue()); |
1413 | |
1414 | QVariantMap variantMap; |
1415 | variantMap["bool" ] = boolValue; |
1416 | variantMap["float" ] = floatValue; |
1417 | variantMap["string" ] = stringValue; |
1418 | variantMap["array" ] = variantList; |
1419 | variantMap["null" ] = QVariant::fromValue(value: nullptr); |
1420 | variantMap["default" ] = QVariant(); |
1421 | QVariantHash variantHash; |
1422 | variantHash["bool" ] = boolValue; |
1423 | variantHash["float" ] = floatValue; |
1424 | variantHash["string" ] = stringValue; |
1425 | variantHash["array" ] = variantList; |
1426 | variantHash["null" ] = QVariant::fromValue(value: nullptr); |
1427 | variantHash["default" ] = QVariant(); |
1428 | QJsonObject jsonObject; |
1429 | jsonObject["bool" ] = boolValue; |
1430 | jsonObject["float" ] = floatValue; |
1431 | jsonObject["string" ] = stringValue; |
1432 | jsonObject["array" ] = jsonArray_variant; |
1433 | jsonObject["null" ] = QJsonValue::Null; |
1434 | jsonObject["default" ] = QJsonValue(); |
1435 | |
1436 | QTest::newRow(dataTag: "default" ) << QVariant() << QJsonValue(QJsonValue::Null); |
1437 | QTest::newRow(dataTag: "nullptr" ) << QVariant::fromValue(value: nullptr) << QJsonValue(QJsonValue::Null); |
1438 | QTest::newRow(dataTag: "bool" ) << QVariant(boolValue) << QJsonValue(boolValue); |
1439 | QTest::newRow(dataTag: "int" ) << QVariant(intValue) << QJsonValue(intValue); |
1440 | QTest::newRow(dataTag: "uint" ) << QVariant(uintValue) << QJsonValue(static_cast<double>(uintValue)); |
1441 | QTest::newRow(dataTag: "longlong" ) << QVariant(longlongValue) << QJsonValue(longlongValue); |
1442 | QTest::newRow(dataTag: "ulonglong" ) << QVariant(ulonglongValue) << QJsonValue(static_cast<double>(ulonglongValue)); |
1443 | QTest::newRow(dataTag: "float" ) << QVariant(floatValue) << QJsonValue(floatValue); |
1444 | QTest::newRow(dataTag: "double" ) << QVariant(doubleValue) << QJsonValue(doubleValue); |
1445 | QTest::newRow(dataTag: "string" ) << QVariant(stringValue) << QJsonValue(stringValue); |
1446 | QTest::newRow(dataTag: "stringList" ) << QVariant(stringList) << QJsonValue(jsonArray_string); |
1447 | QTest::newRow(dataTag: "variantList" ) << QVariant(variantList) << QJsonValue(jsonArray_variant); |
1448 | QTest::newRow(dataTag: "variantMap" ) << QVariant(variantMap) << QJsonValue(jsonObject); |
1449 | QTest::newRow(dataTag: "variantHash" ) << QVariant(variantHash) << QJsonValue(jsonObject); |
1450 | } |
1451 | |
1452 | // replaces QVariant() with QVariant(nullptr) |
1453 | static QVariant normalizedVariant(const QVariant &v) |
1454 | { |
1455 | switch (v.userType()) { |
1456 | case QMetaType::UnknownType: |
1457 | return QVariant::fromValue(value: nullptr); |
1458 | case QMetaType::QVariantList: { |
1459 | const QVariantList in = v.toList(); |
1460 | QVariantList out; |
1461 | out.reserve(alloc: in.size()); |
1462 | for (const QVariant &v : in) |
1463 | out << normalizedVariant(v); |
1464 | return out; |
1465 | } |
1466 | case QMetaType::QVariantMap: { |
1467 | const QVariantMap in = v.toMap(); |
1468 | QVariantMap out; |
1469 | for (auto it = in.begin(); it != in.end(); ++it) |
1470 | out.insert(akey: it.key(), avalue: normalizedVariant(v: it.value())); |
1471 | return out; |
1472 | } |
1473 | case QMetaType::QVariantHash: { |
1474 | const QVariantHash in = v.toHash(); |
1475 | QVariantHash out; |
1476 | for (auto it = in.begin(); it != in.end(); ++it) |
1477 | out.insert(akey: it.key(), avalue: normalizedVariant(v: it.value())); |
1478 | return out; |
1479 | } |
1480 | |
1481 | default: |
1482 | return v; |
1483 | } |
1484 | } |
1485 | |
1486 | void tst_QtJson::fromVariant() |
1487 | { |
1488 | QFETCH( QVariant, variant ); |
1489 | QFETCH( QJsonValue, jsonvalue ); |
1490 | |
1491 | QCOMPARE(QJsonValue::fromVariant(variant), jsonvalue); |
1492 | QCOMPARE(normalizedVariant(variant).toJsonValue(), jsonvalue); |
1493 | } |
1494 | |
1495 | void tst_QtJson::fromVariantSpecial_data() |
1496 | { |
1497 | QTest::addColumn<QVariant>(name: "variant" ); |
1498 | QTest::addColumn<QJsonValue>(name: "jsonvalue" ); |
1499 | |
1500 | // Qt types with special encoding |
1501 | QTest::newRow(dataTag: "url" ) << QVariant(QUrl("https://example.com/\xc2\xa9 " )) |
1502 | << QJsonValue("https://example.com/%C2%A9%20" ); |
1503 | QTest::newRow(dataTag: "uuid" ) << QVariant(QUuid(0x40c01df6, 0x1ad5, 0x4762, 0x9c, 0xfe, 0xfd, 0xba, 0xfa, 0xb5, 0xde, 0xf8)) |
1504 | << QJsonValue("40c01df6-1ad5-4762-9cfe-fdbafab5def8" ); |
1505 | } |
1506 | |
1507 | void tst_QtJson::fromVariantSpecial() |
1508 | { |
1509 | QFETCH( QVariant, variant ); |
1510 | QFETCH( QJsonValue, jsonvalue ); |
1511 | |
1512 | QCOMPARE(QJsonValue::fromVariant(variant), jsonvalue); |
1513 | } |
1514 | |
1515 | void tst_QtJson::toVariant_data() |
1516 | { |
1517 | fromVariant_data(); |
1518 | } |
1519 | |
1520 | void tst_QtJson::toVariant() |
1521 | { |
1522 | QFETCH( QVariant, variant ); |
1523 | QFETCH( QJsonValue, jsonvalue ); |
1524 | |
1525 | QCOMPARE(jsonvalue.toVariant(), normalizedVariant(variant)); |
1526 | } |
1527 | |
1528 | void tst_QtJson::fromVariantMap() |
1529 | { |
1530 | QVariantMap map; |
1531 | map.insert(akey: QLatin1String("key1" ), avalue: QLatin1String("value1" )); |
1532 | map.insert(akey: QLatin1String("key2" ), avalue: QLatin1String("value2" )); |
1533 | QJsonObject object = QJsonObject::fromVariantMap(map); |
1534 | QCOMPARE(object.size(), 2); |
1535 | QCOMPARE(object.value(QLatin1String("key1" )), QJsonValue(QLatin1String("value1" ))); |
1536 | QCOMPARE(object.value(QLatin1String("key2" )), QJsonValue(QLatin1String("value2" ))); |
1537 | |
1538 | QVariantList list; |
1539 | list.append(t: true); |
1540 | list.append(t: QVariant()); |
1541 | list.append(t: 999.); |
1542 | list.append(t: QLatin1String("foo" )); |
1543 | map.insert(akey: "list" , avalue: list); |
1544 | object = QJsonObject::fromVariantMap(map); |
1545 | QCOMPARE(object.size(), 3); |
1546 | QCOMPARE(object.value(QLatin1String("key1" )), QJsonValue(QLatin1String("value1" ))); |
1547 | QCOMPARE(object.value(QLatin1String("key2" )), QJsonValue(QLatin1String("value2" ))); |
1548 | QCOMPARE(object.value(QLatin1String("list" )).type(), QJsonValue::Array); |
1549 | QJsonArray array = object.value(key: QLatin1String("list" )).toArray(); |
1550 | QCOMPARE(array.size(), 4); |
1551 | QCOMPARE(array.at(0).type(), QJsonValue::Bool); |
1552 | QCOMPARE(array.at(0).toBool(), true); |
1553 | QCOMPARE(array.at(1).type(), QJsonValue::Null); |
1554 | QCOMPARE(array.at(2).type(), QJsonValue::Double); |
1555 | QCOMPARE(array.at(2).toDouble(), 999.); |
1556 | QCOMPARE(array.at(3).type(), QJsonValue::String); |
1557 | QCOMPARE(array.at(3).toString(), QLatin1String("foo" )); |
1558 | } |
1559 | |
1560 | void tst_QtJson::fromVariantHash() |
1561 | { |
1562 | QVariantHash map; |
1563 | map.insert(akey: QLatin1String("key1" ), avalue: QLatin1String("value1" )); |
1564 | map.insert(akey: QLatin1String("key2" ), avalue: QLatin1String("value2" )); |
1565 | QJsonObject object = QJsonObject::fromVariantHash(map); |
1566 | QCOMPARE(object.size(), 2); |
1567 | QCOMPARE(object.value(QLatin1String("key1" )), QJsonValue(QLatin1String("value1" ))); |
1568 | QCOMPARE(object.value(QLatin1String("key2" )), QJsonValue(QLatin1String("value2" ))); |
1569 | } |
1570 | |
1571 | void tst_QtJson::toVariantMap() |
1572 | { |
1573 | QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().type()), QMetaType::QVariantMap); // QTBUG-32524 |
1574 | |
1575 | QJsonObject object; |
1576 | QVariantMap map = object.toVariantMap(); |
1577 | QVERIFY(map.isEmpty()); |
1578 | |
1579 | object.insert(key: "Key" , value: QString("Value" )); |
1580 | object.insert(key: "null" , value: QJsonValue()); |
1581 | QJsonArray array; |
1582 | array.append(value: true); |
1583 | array.append(value: 999.); |
1584 | array.append(value: QLatin1String("string" )); |
1585 | array.append(value: QJsonValue::Null); |
1586 | object.insert(key: "Array" , value: array); |
1587 | |
1588 | map = object.toVariantMap(); |
1589 | |
1590 | QCOMPARE(map.size(), 3); |
1591 | QCOMPARE(map.value("Key" ), QVariant(QString("Value" ))); |
1592 | QCOMPARE(map.value("null" ), QVariant::fromValue(nullptr)); |
1593 | QCOMPARE(map.value("Array" ).type(), QVariant::List); |
1594 | QVariantList list = map.value(akey: "Array" ).toList(); |
1595 | QCOMPARE(list.size(), 4); |
1596 | QCOMPARE(list.at(0), QVariant(true)); |
1597 | QCOMPARE(list.at(1), QVariant(999.)); |
1598 | QCOMPARE(list.at(2), QVariant(QLatin1String("string" ))); |
1599 | QCOMPARE(list.at(3), QVariant::fromValue(nullptr)); |
1600 | } |
1601 | |
1602 | void tst_QtJson::toVariantHash() |
1603 | { |
1604 | QJsonObject object; |
1605 | QVariantHash hash = object.toVariantHash(); |
1606 | QVERIFY(hash.isEmpty()); |
1607 | |
1608 | object.insert(key: "Key" , value: QString("Value" )); |
1609 | object.insert(key: "null" , value: QJsonValue::Null); |
1610 | QJsonArray array; |
1611 | array.append(value: true); |
1612 | array.append(value: 999.); |
1613 | array.append(value: QLatin1String("string" )); |
1614 | array.append(value: QJsonValue::Null); |
1615 | object.insert(key: "Array" , value: array); |
1616 | |
1617 | hash = object.toVariantHash(); |
1618 | |
1619 | QCOMPARE(hash.size(), 3); |
1620 | QCOMPARE(hash.value("Key" ), QVariant(QString("Value" ))); |
1621 | QCOMPARE(hash.value("null" ), QVariant::fromValue(nullptr)); |
1622 | QCOMPARE(hash.value("Array" ).type(), QVariant::List); |
1623 | QVariantList list = hash.value(akey: "Array" ).toList(); |
1624 | QCOMPARE(list.size(), 4); |
1625 | QCOMPARE(list.at(0), QVariant(true)); |
1626 | QCOMPARE(list.at(1), QVariant(999.)); |
1627 | QCOMPARE(list.at(2), QVariant(QLatin1String("string" ))); |
1628 | QCOMPARE(list.at(3), QVariant::fromValue(nullptr)); |
1629 | } |
1630 | |
1631 | void tst_QtJson::toVariantList() |
1632 | { |
1633 | QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().type()), QMetaType::QVariantList); // QTBUG-32524 |
1634 | |
1635 | QJsonArray array; |
1636 | QVariantList list = array.toVariantList(); |
1637 | QVERIFY(list.isEmpty()); |
1638 | |
1639 | array.append(value: QString("Value" )); |
1640 | array.append(value: QJsonValue()); |
1641 | QJsonArray inner; |
1642 | inner.append(value: true); |
1643 | inner.append(value: 999.); |
1644 | inner.append(value: QLatin1String("string" )); |
1645 | inner.append(value: QJsonValue()); |
1646 | array.append(value: inner); |
1647 | |
1648 | list = array.toVariantList(); |
1649 | |
1650 | QCOMPARE(list.size(), 3); |
1651 | QCOMPARE(list[0], QVariant(QString("Value" ))); |
1652 | QCOMPARE(list[1], QVariant::fromValue(nullptr)); |
1653 | QCOMPARE(list[2].type(), QVariant::List); |
1654 | QVariantList vlist = list[2].toList(); |
1655 | QCOMPARE(vlist.size(), 4); |
1656 | QCOMPARE(vlist.at(0), QVariant(true)); |
1657 | QCOMPARE(vlist.at(1), QVariant(999.)); |
1658 | QCOMPARE(vlist.at(2), QVariant(QLatin1String("string" ))); |
1659 | QCOMPARE(vlist.at(3), QVariant::fromValue(nullptr)); |
1660 | } |
1661 | |
1662 | void tst_QtJson::toJson() |
1663 | { |
1664 | // Test QJsonDocument::Indented format |
1665 | { |
1666 | QJsonObject object; |
1667 | object.insert(key: "\\Key\n" , value: QString("Value" )); |
1668 | object.insert(key: "null" , value: QJsonValue()); |
1669 | QJsonArray array; |
1670 | array.append(value: true); |
1671 | array.append(value: 999.); |
1672 | array.append(value: QLatin1String("string" )); |
1673 | array.append(value: QJsonValue()); |
1674 | array.append(value: QLatin1String("\\\a\n\r\b\tabcABC\"" )); |
1675 | object.insert(key: "Array" , value: array); |
1676 | |
1677 | QByteArray json = QJsonDocument(object).toJson(); |
1678 | |
1679 | QByteArray expected = |
1680 | "{\n" |
1681 | " \"Array\": [\n" |
1682 | " true,\n" |
1683 | " 999,\n" |
1684 | " \"string\",\n" |
1685 | " null,\n" |
1686 | " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n" |
1687 | " ],\n" |
1688 | " \"\\\\Key\\n\": \"Value\",\n" |
1689 | " \"null\": null\n" |
1690 | "}\n" ; |
1691 | QCOMPARE(json, expected); |
1692 | |
1693 | QJsonDocument doc; |
1694 | doc.setObject(object); |
1695 | json = doc.toJson(); |
1696 | QCOMPARE(json, expected); |
1697 | |
1698 | doc.setArray(array); |
1699 | json = doc.toJson(); |
1700 | expected = |
1701 | "[\n" |
1702 | " true,\n" |
1703 | " 999,\n" |
1704 | " \"string\",\n" |
1705 | " null,\n" |
1706 | " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n" |
1707 | "]\n" ; |
1708 | QCOMPARE(json, expected); |
1709 | } |
1710 | |
1711 | // Test QJsonDocument::Compact format |
1712 | { |
1713 | QJsonObject object; |
1714 | object.insert(key: "\\Key\n" , value: QString("Value" )); |
1715 | object.insert(key: "null" , value: QJsonValue()); |
1716 | QJsonArray array; |
1717 | array.append(value: true); |
1718 | array.append(value: 999.); |
1719 | array.append(value: QLatin1String("string" )); |
1720 | array.append(value: QJsonValue()); |
1721 | array.append(value: QLatin1String("\\\a\n\r\b\tabcABC\"" )); |
1722 | object.insert(key: "Array" , value: array); |
1723 | |
1724 | QByteArray json = QJsonDocument(object).toJson(format: QJsonDocument::Compact); |
1725 | QByteArray expected = |
1726 | "{\"Array\":[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\":\"Value\",\"null\":null}" ; |
1727 | QCOMPARE(json, expected); |
1728 | |
1729 | QJsonDocument doc; |
1730 | doc.setObject(object); |
1731 | json = doc.toJson(format: QJsonDocument::Compact); |
1732 | QCOMPARE(json, expected); |
1733 | |
1734 | doc.setArray(array); |
1735 | json = doc.toJson(format: QJsonDocument::Compact); |
1736 | expected = "[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"]" ; |
1737 | QCOMPARE(json, expected); |
1738 | } |
1739 | } |
1740 | |
1741 | void tst_QtJson::toJsonSillyNumericValues() |
1742 | { |
1743 | QJsonObject object; |
1744 | QJsonArray array; |
1745 | array.append(value: QJsonValue(std::numeric_limits<double>::infinity())); // encode to: null |
1746 | array.append(value: QJsonValue(-std::numeric_limits<double>::infinity())); // encode to: null |
1747 | array.append(value: QJsonValue(std::numeric_limits<double>::quiet_NaN())); // encode to: null |
1748 | object.insert(key: "Array" , value: array); |
1749 | |
1750 | QByteArray json = QJsonDocument(object).toJson(); |
1751 | |
1752 | QByteArray expected = |
1753 | "{\n" |
1754 | " \"Array\": [\n" |
1755 | " null,\n" |
1756 | " null,\n" |
1757 | " null\n" |
1758 | " ]\n" |
1759 | "}\n" ; |
1760 | |
1761 | QCOMPARE(json, expected); |
1762 | |
1763 | QJsonDocument doc; |
1764 | doc.setObject(object); |
1765 | json = doc.toJson(); |
1766 | QCOMPARE(json, expected); |
1767 | } |
1768 | |
1769 | void tst_QtJson::toJsonLargeNumericValues() |
1770 | { |
1771 | QJsonObject object; |
1772 | QJsonArray array; |
1773 | array.append(value: QJsonValue(1.234567)); // actual precision bug in Qt 5.0.0 |
1774 | array.append(value: QJsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE |
1775 | array.append(value: QJsonValue(5e-324)); // JS Number.MIN_VALUE |
1776 | array.append(value: QJsonValue(std::numeric_limits<double>::min())); |
1777 | array.append(value: QJsonValue(std::numeric_limits<double>::max())); |
1778 | array.append(value: QJsonValue(std::numeric_limits<double>::epsilon())); |
1779 | array.append(value: QJsonValue(std::numeric_limits<double>::denorm_min())); |
1780 | array.append(value: QJsonValue(0.0)); |
1781 | array.append(value: QJsonValue(-std::numeric_limits<double>::min())); |
1782 | array.append(value: QJsonValue(-std::numeric_limits<double>::max())); |
1783 | array.append(value: QJsonValue(-std::numeric_limits<double>::epsilon())); |
1784 | array.append(value: QJsonValue(-std::numeric_limits<double>::denorm_min())); |
1785 | array.append(value: QJsonValue(-0.0)); |
1786 | array.append(value: QJsonValue(9007199254740992LL)); // JS Number max integer |
1787 | array.append(value: QJsonValue(-9007199254740992LL)); // JS Number min integer |
1788 | object.insert(key: "Array" , value: array); |
1789 | |
1790 | QByteArray json = QJsonDocument(object).toJson(); |
1791 | |
1792 | QByteArray expected = |
1793 | "{\n" |
1794 | " \"Array\": [\n" |
1795 | " 1.234567,\n" |
1796 | " 1.7976931348623157e+308,\n" |
1797 | #ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then |
1798 | " 4.9406564584124654e-324,\n" |
1799 | " 2.2250738585072014e-308,\n" |
1800 | " 1.7976931348623157e+308,\n" |
1801 | " 2.2204460492503131e-16,\n" |
1802 | " 4.9406564584124654e-324,\n" |
1803 | " 0,\n" |
1804 | " -2.2250738585072014e-308,\n" |
1805 | " -1.7976931348623157e+308,\n" |
1806 | " -2.2204460492503131e-16,\n" |
1807 | " -4.9406564584124654e-324,\n" |
1808 | #else |
1809 | " 5e-324,\n" |
1810 | " 2.2250738585072014e-308,\n" |
1811 | " 1.7976931348623157e+308,\n" |
1812 | " 2.220446049250313e-16,\n" |
1813 | " 5e-324,\n" |
1814 | " 0,\n" |
1815 | " -2.2250738585072014e-308,\n" |
1816 | " -1.7976931348623157e+308,\n" |
1817 | " -2.220446049250313e-16,\n" |
1818 | " -5e-324,\n" |
1819 | #endif |
1820 | " 0,\n" |
1821 | " 9007199254740992,\n" |
1822 | " -9007199254740992\n" |
1823 | " ]\n" |
1824 | "}\n" ; |
1825 | |
1826 | #ifdef Q_OS_QNX |
1827 | QEXPECT_FAIL("" , "See QTBUG-37066" , Continue); |
1828 | #endif |
1829 | QCOMPARE(json, expected); |
1830 | |
1831 | QJsonDocument doc; |
1832 | doc.setObject(object); |
1833 | json = doc.toJson(); |
1834 | #ifdef Q_OS_QNX |
1835 | QEXPECT_FAIL("" , "See QTBUG-37066" , Continue); |
1836 | #endif |
1837 | QCOMPARE(json, expected); |
1838 | } |
1839 | |
1840 | void tst_QtJson::fromJson() |
1841 | { |
1842 | { |
1843 | QByteArray json = "[\n true\n]\n" ; |
1844 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1845 | QVERIFY(!doc.isEmpty()); |
1846 | QCOMPARE(doc.isArray(), true); |
1847 | QCOMPARE(doc.isObject(), false); |
1848 | QJsonArray array = doc.array(); |
1849 | QCOMPARE(array.size(), 1); |
1850 | QCOMPARE(array.at(0).type(), QJsonValue::Bool); |
1851 | QCOMPARE(array.at(0).toBool(), true); |
1852 | QCOMPARE(doc.toJson(), json); |
1853 | } |
1854 | { |
1855 | //regression test: test if unicode_control_characters are correctly decoded |
1856 | QByteArray json = "[\n \"" UNICODE_NON_CHARACTER "\"\n]\n" ; |
1857 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1858 | QVERIFY(!doc.isEmpty()); |
1859 | QCOMPARE(doc.isArray(), true); |
1860 | QCOMPARE(doc.isObject(), false); |
1861 | QJsonArray array = doc.array(); |
1862 | QCOMPARE(array.size(), 1); |
1863 | QCOMPARE(array.at(0).type(), QJsonValue::String); |
1864 | QCOMPARE(array.at(0).toString(), QString::fromUtf8(UNICODE_NON_CHARACTER)); |
1865 | QCOMPARE(doc.toJson(), json); |
1866 | } |
1867 | { |
1868 | QByteArray json = "[]" ; |
1869 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1870 | QVERIFY(!doc.isEmpty()); |
1871 | QCOMPARE(doc.isArray(), true); |
1872 | QCOMPARE(doc.isObject(), false); |
1873 | QJsonArray array = doc.array(); |
1874 | QCOMPARE(array.size(), 0); |
1875 | } |
1876 | { |
1877 | QByteArray json = "{}" ; |
1878 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1879 | QVERIFY(!doc.isEmpty()); |
1880 | QCOMPARE(doc.isArray(), false); |
1881 | QCOMPARE(doc.isObject(), true); |
1882 | QJsonObject object = doc.object(); |
1883 | QCOMPARE(object.size(), 0); |
1884 | } |
1885 | { |
1886 | QByteArray json = "{\n \"Key\": true\n}\n" ; |
1887 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1888 | QVERIFY(!doc.isEmpty()); |
1889 | QCOMPARE(doc.isArray(), false); |
1890 | QCOMPARE(doc.isObject(), true); |
1891 | QJsonObject object = doc.object(); |
1892 | QCOMPARE(object.size(), 1); |
1893 | QCOMPARE(object.value("Key" ), QJsonValue(true)); |
1894 | QCOMPARE(doc.toJson(), json); |
1895 | } |
1896 | { |
1897 | QByteArray json = "[ null, true, false, \"Foo\", 1, [], {} ]" ; |
1898 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1899 | QVERIFY(!doc.isEmpty()); |
1900 | QCOMPARE(doc.isArray(), true); |
1901 | QCOMPARE(doc.isObject(), false); |
1902 | QJsonArray array = doc.array(); |
1903 | QCOMPARE(array.size(), 7); |
1904 | QCOMPARE(array.at(0).type(), QJsonValue::Null); |
1905 | QCOMPARE(array.at(1).type(), QJsonValue::Bool); |
1906 | QCOMPARE(array.at(1).toBool(), true); |
1907 | QCOMPARE(array.at(2).type(), QJsonValue::Bool); |
1908 | QCOMPARE(array.at(2).toBool(), false); |
1909 | QCOMPARE(array.at(3).type(), QJsonValue::String); |
1910 | QCOMPARE(array.at(3).toString(), QLatin1String("Foo" )); |
1911 | QCOMPARE(array.at(4).type(), QJsonValue::Double); |
1912 | QCOMPARE(array.at(4).toDouble(), 1.); |
1913 | QCOMPARE(array.at(5).type(), QJsonValue::Array); |
1914 | QCOMPARE(array.at(5).toArray().size(), 0); |
1915 | QCOMPARE(array.at(6).type(), QJsonValue::Object); |
1916 | QCOMPARE(array.at(6).toObject().size(), 0); |
1917 | } |
1918 | { |
1919 | QByteArray json = "{ \"0\": null, \"1\": true, \"2\": false, \"3\": \"Foo\", \"4\": 1, \"5\": [], \"6\": {} }" ; |
1920 | QJsonDocument doc = QJsonDocument::fromJson(json); |
1921 | QVERIFY(!doc.isEmpty()); |
1922 | QCOMPARE(doc.isArray(), false); |
1923 | QCOMPARE(doc.isObject(), true); |
1924 | QJsonObject object = doc.object(); |
1925 | QCOMPARE(object.size(), 7); |
1926 | QCOMPARE(object.value("0" ).type(), QJsonValue::Null); |
1927 | QCOMPARE(object.value("1" ).type(), QJsonValue::Bool); |
1928 | QCOMPARE(object.value("1" ).toBool(), true); |
1929 | QCOMPARE(object.value("2" ).type(), QJsonValue::Bool); |
1930 | QCOMPARE(object.value("2" ).toBool(), false); |
1931 | QCOMPARE(object.value("3" ).type(), QJsonValue::String); |
1932 | QCOMPARE(object.value("3" ).toString(), QLatin1String("Foo" )); |
1933 | QCOMPARE(object.value("4" ).type(), QJsonValue::Double); |
1934 | QCOMPARE(object.value("4" ).toDouble(), 1.); |
1935 | QCOMPARE(object.value("5" ).type(), QJsonValue::Array); |
1936 | QCOMPARE(object.value("5" ).toArray().size(), 0); |
1937 | QCOMPARE(object.value("6" ).type(), QJsonValue::Object); |
1938 | QCOMPARE(object.value("6" ).toObject().size(), 0); |
1939 | } |
1940 | { |
1941 | QByteArray compactJson = "{\"Array\": [true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\": \"Value\",\"null\": null}" ; |
1942 | QJsonDocument doc = QJsonDocument::fromJson(json: compactJson); |
1943 | QVERIFY(!doc.isEmpty()); |
1944 | QCOMPARE(doc.isArray(), false); |
1945 | QCOMPARE(doc.isObject(), true); |
1946 | QJsonObject object = doc.object(); |
1947 | QCOMPARE(object.size(), 3); |
1948 | QCOMPARE(object.value("\\Key\n" ).isString(), true); |
1949 | QCOMPARE(object.value("\\Key\n" ).toString(), QString("Value" )); |
1950 | QCOMPARE(object.value("null" ).isNull(), true); |
1951 | QCOMPARE(object.value("Array" ).isArray(), true); |
1952 | QJsonArray array = object.value(key: "Array" ).toArray(); |
1953 | QCOMPARE(array.size(), 5); |
1954 | QCOMPARE(array.at(0).isBool(), true); |
1955 | QCOMPARE(array.at(0).toBool(), true); |
1956 | QCOMPARE(array.at(1).isDouble(), true); |
1957 | QCOMPARE(array.at(1).toDouble(), 999.); |
1958 | QCOMPARE(array.at(2).isString(), true); |
1959 | QCOMPARE(array.at(2).toString(), QLatin1String("string" )); |
1960 | QCOMPARE(array.at(3).isNull(), true); |
1961 | QCOMPARE(array.at(4).isString(), true); |
1962 | QCOMPARE(array.at(4).toString(), QLatin1String("\\\a\n\r\b\tabcABC\"" )); |
1963 | } |
1964 | } |
1965 | |
1966 | void tst_QtJson::fromJsonErrors() |
1967 | { |
1968 | { |
1969 | QJsonParseError error; |
1970 | QByteArray json = "{\n \n\n" ; |
1971 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
1972 | QVERIFY(doc.isEmpty()); |
1973 | QCOMPARE(error.error, QJsonParseError::UnterminatedObject); |
1974 | QCOMPARE(error.offset, 8); |
1975 | } |
1976 | { |
1977 | QJsonParseError error; |
1978 | QByteArray json = "{\n \"key\" 10\n" ; |
1979 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
1980 | QVERIFY(doc.isEmpty()); |
1981 | QCOMPARE(error.error, QJsonParseError::MissingNameSeparator); |
1982 | QCOMPARE(error.offset, 13); |
1983 | } |
1984 | { |
1985 | QJsonParseError error; |
1986 | QByteArray json = "[\n \n\n" ; |
1987 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
1988 | QVERIFY(doc.isEmpty()); |
1989 | QCOMPARE(error.error, QJsonParseError::UnterminatedArray); |
1990 | QCOMPARE(error.offset, 8); |
1991 | } |
1992 | { |
1993 | QJsonParseError error; |
1994 | QByteArray json = "[\n 1, true\n\n" ; |
1995 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
1996 | QVERIFY(doc.isEmpty()); |
1997 | QCOMPARE(error.error, QJsonParseError::UnterminatedArray); |
1998 | QCOMPARE(error.offset, 14); |
1999 | } |
2000 | { |
2001 | QJsonParseError error; |
2002 | QByteArray json = "[\n 1 true\n\n" ; |
2003 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2004 | QVERIFY(doc.isEmpty()); |
2005 | QCOMPARE(error.error, QJsonParseError::MissingValueSeparator); |
2006 | QCOMPARE(error.offset, 7); |
2007 | } |
2008 | { |
2009 | QJsonParseError error; |
2010 | QByteArray json = "[\n nul" ; |
2011 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2012 | QVERIFY(doc.isEmpty()); |
2013 | QCOMPARE(error.error, QJsonParseError::IllegalValue); |
2014 | QCOMPARE(error.offset, 7); |
2015 | } |
2016 | { |
2017 | QJsonParseError error; |
2018 | QByteArray json = "[\n nulzz" ; |
2019 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2020 | QVERIFY(doc.isEmpty()); |
2021 | QCOMPARE(error.error, QJsonParseError::IllegalValue); |
2022 | QCOMPARE(error.offset, 10); |
2023 | } |
2024 | { |
2025 | QJsonParseError error; |
2026 | QByteArray json = "[\n tru" ; |
2027 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2028 | QVERIFY(doc.isEmpty()); |
2029 | QCOMPARE(error.error, QJsonParseError::IllegalValue); |
2030 | QCOMPARE(error.offset, 7); |
2031 | } |
2032 | { |
2033 | QJsonParseError error; |
2034 | QByteArray json = "[\n trud]" ; |
2035 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2036 | QVERIFY(doc.isEmpty()); |
2037 | QCOMPARE(error.error, QJsonParseError::IllegalValue); |
2038 | QCOMPARE(error.offset, 10); |
2039 | } |
2040 | { |
2041 | QJsonParseError error; |
2042 | QByteArray json = "[\n fal" ; |
2043 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2044 | QVERIFY(doc.isEmpty()); |
2045 | QCOMPARE(error.error, QJsonParseError::IllegalValue); |
2046 | QCOMPARE(error.offset, 7); |
2047 | } |
2048 | { |
2049 | QJsonParseError error; |
2050 | QByteArray json = "[\n falsd]" ; |
2051 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2052 | QVERIFY(doc.isEmpty()); |
2053 | QCOMPARE(error.error, QJsonParseError::IllegalValue); |
2054 | QCOMPARE(error.offset, 11); |
2055 | } |
2056 | { |
2057 | QJsonParseError error; |
2058 | QByteArray json = "[\n 11111" ; |
2059 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2060 | QVERIFY(doc.isEmpty()); |
2061 | QCOMPARE(error.error, QJsonParseError::TerminationByNumber); |
2062 | QCOMPARE(error.offset, 11); |
2063 | } |
2064 | { |
2065 | QJsonParseError error; |
2066 | QByteArray json = "[\n -1E10000]" ; |
2067 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2068 | QVERIFY(doc.isEmpty()); |
2069 | QCOMPARE(error.error, QJsonParseError::IllegalNumber); |
2070 | QCOMPARE(error.offset, 14); |
2071 | } |
2072 | { |
2073 | QJsonParseError error; |
2074 | QByteArray json = "[\n -1e-10000]" ; |
2075 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2076 | QVERIFY(doc.isEmpty()); |
2077 | QCOMPARE(error.error, QJsonParseError::IllegalNumber); |
2078 | QCOMPARE(error.offset, 15); |
2079 | } |
2080 | { |
2081 | QJsonParseError error; |
2082 | QByteArray json = "[\n \"\\u12\"]" ; |
2083 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2084 | QVERIFY(doc.isEmpty()); |
2085 | QCOMPARE(error.error, QJsonParseError::IllegalEscapeSequence); |
2086 | QCOMPARE(error.offset, 11); |
2087 | } |
2088 | { |
2089 | QJsonParseError error; |
2090 | QByteArray json = "[\n \"foo" INVALID_UNICODE "bar\"]" ; |
2091 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2092 | QVERIFY(doc.isEmpty()); |
2093 | QCOMPARE(error.error, QJsonParseError::IllegalUTF8String); |
2094 | QCOMPARE(error.offset, 12); |
2095 | } |
2096 | { |
2097 | QJsonParseError error; |
2098 | QByteArray json = "[\n \"" ; |
2099 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2100 | QVERIFY(doc.isEmpty()); |
2101 | QCOMPARE(error.error, QJsonParseError::UnterminatedString); |
2102 | QCOMPARE(error.offset, 8); |
2103 | } |
2104 | { |
2105 | QJsonParseError error; |
2106 | QByteArray json = "[\n \"c" UNICODE_DJE "a\\u12\"]" ; |
2107 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2108 | QVERIFY(doc.isEmpty()); |
2109 | QCOMPARE(error.error, QJsonParseError::IllegalEscapeSequence); |
2110 | QCOMPARE(error.offset, 15); |
2111 | } |
2112 | { |
2113 | QJsonParseError error; |
2114 | QByteArray json = "[\n \"c" UNICODE_DJE "a" INVALID_UNICODE "bar\"]" ; |
2115 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2116 | QVERIFY(doc.isEmpty()); |
2117 | QCOMPARE(error.error, QJsonParseError::IllegalUTF8String); |
2118 | QCOMPARE(error.offset, 13); |
2119 | } |
2120 | { |
2121 | QJsonParseError error; |
2122 | QByteArray json = "[\n \"c" UNICODE_DJE "a ]" ; |
2123 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2124 | QVERIFY(doc.isEmpty()); |
2125 | QCOMPARE(error.error, QJsonParseError::UnterminatedString); |
2126 | QCOMPARE(error.offset, 14); |
2127 | } |
2128 | } |
2129 | |
2130 | void tst_QtJson::fromBinary() |
2131 | { |
2132 | QFile file(testDataDir + "/test.json" ); |
2133 | file.open(flags: QFile::ReadOnly); |
2134 | QByteArray testJson = file.readAll(); |
2135 | |
2136 | QJsonDocument doc = QJsonDocument::fromJson(json: testJson); |
2137 | QJsonDocument outdoc = QJsonDocument::fromBinaryData(data: doc.toBinaryData()); |
2138 | QVERIFY(!outdoc.isNull()); |
2139 | QCOMPARE(doc, outdoc); |
2140 | |
2141 | QFile bfile(testDataDir + "/test.bjson" ); |
2142 | bfile.open(flags: QFile::ReadOnly); |
2143 | QByteArray binary = bfile.readAll(); |
2144 | |
2145 | QJsonDocument bdoc = QJsonDocument::fromBinaryData(data: binary); |
2146 | QVERIFY(!bdoc.isNull()); |
2147 | QCOMPARE(doc.toVariant(), bdoc.toVariant()); |
2148 | QCOMPARE(doc, bdoc); |
2149 | } |
2150 | |
2151 | void tst_QtJson::toAndFromBinary_data() |
2152 | { |
2153 | QTest::addColumn<QString>(name: "filename" ); |
2154 | QTest::newRow(dataTag: "test.json" ) << (testDataDir + "/test.json" ); |
2155 | QTest::newRow(dataTag: "test2.json" ) << (testDataDir + "/test2.json" ); |
2156 | } |
2157 | |
2158 | void tst_QtJson::toAndFromBinary() |
2159 | { |
2160 | QFETCH(QString, filename); |
2161 | QFile file(filename); |
2162 | QVERIFY(file.open(QFile::ReadOnly)); |
2163 | QByteArray data = file.readAll(); |
2164 | |
2165 | QJsonDocument doc = QJsonDocument::fromJson(json: data); |
2166 | QVERIFY(!doc.isNull()); |
2167 | QJsonDocument outdoc = QJsonDocument::fromBinaryData(data: doc.toBinaryData()); |
2168 | QVERIFY(!outdoc.isNull()); |
2169 | QCOMPARE(doc, outdoc); |
2170 | } |
2171 | |
2172 | void tst_QtJson::invalidBinaryData() |
2173 | { |
2174 | QDir dir(testDataDir + "/invalidBinaryData" ); |
2175 | QFileInfoList files = dir.entryInfoList(); |
2176 | for (int i = 0; i < files.size(); ++i) { |
2177 | if (!files.at(i).isFile()) |
2178 | continue; |
2179 | QFile file(files.at(i).filePath()); |
2180 | file.open(flags: QIODevice::ReadOnly); |
2181 | QByteArray bytes = file.readAll(); |
2182 | bytes.squeeze(); |
2183 | QJsonDocument document = QJsonDocument::fromRawData(data: bytes.constData(), size: bytes.size()); |
2184 | QVERIFY(document.isNull()); |
2185 | } |
2186 | } |
2187 | |
2188 | void tst_QtJson::parseNumbers() |
2189 | { |
2190 | { |
2191 | // test number parsing |
2192 | struct Numbers { |
2193 | const char *str; |
2194 | int n; |
2195 | }; |
2196 | Numbers numbers [] = { |
2197 | { .str: "0" , .n: 0 }, |
2198 | { .str: "1" , .n: 1 }, |
2199 | { .str: "10" , .n: 10 }, |
2200 | { .str: "-1" , .n: -1 }, |
2201 | { .str: "100000" , .n: 100000 }, |
2202 | { .str: "-999" , .n: -999 } |
2203 | }; |
2204 | int size = sizeof(numbers)/sizeof(Numbers); |
2205 | for (int i = 0; i < size; ++i) { |
2206 | QByteArray json = "[ " ; |
2207 | json += numbers[i].str; |
2208 | json += " ]" ; |
2209 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2210 | QVERIFY(!doc.isEmpty()); |
2211 | QCOMPARE(doc.isArray(), true); |
2212 | QCOMPARE(doc.isObject(), false); |
2213 | QJsonArray array = doc.array(); |
2214 | QCOMPARE(array.size(), 1); |
2215 | QJsonValue val = array.at(i: 0); |
2216 | QCOMPARE(val.type(), QJsonValue::Double); |
2217 | QCOMPARE(val.toDouble(), (double)numbers[i].n); |
2218 | } |
2219 | } |
2220 | { |
2221 | // test number parsing |
2222 | struct Numbers { |
2223 | const char *str; |
2224 | double n; |
2225 | }; |
2226 | Numbers numbers [] = { |
2227 | { .str: "0" , .n: 0 }, |
2228 | { .str: "1" , .n: 1 }, |
2229 | { .str: "10" , .n: 10 }, |
2230 | { .str: "-1" , .n: -1 }, |
2231 | { .str: "100000" , .n: 100000 }, |
2232 | { .str: "-999" , .n: -999 }, |
2233 | { .str: "1.1" , .n: 1.1 }, |
2234 | { .str: "1e10" , .n: 1e10 }, |
2235 | { .str: "-1.1" , .n: -1.1 }, |
2236 | { .str: "-1e10" , .n: -1e10 }, |
2237 | { .str: "-1E10" , .n: -1e10 }, |
2238 | { .str: "1.1e10" , .n: 1.1e10 }, |
2239 | { .str: "1.1e308" , .n: 1.1e308 }, |
2240 | { .str: "-1.1e308" , .n: -1.1e308 }, |
2241 | { .str: "1.1e-308" , .n: 1.1e-308 }, |
2242 | { .str: "-1.1e-308" , .n: -1.1e-308 }, |
2243 | { .str: "1.1e+308" , .n: 1.1e+308 }, |
2244 | { .str: "-1.1e+308" , .n: -1.1e+308 }, |
2245 | { .str: "1.e+308" , .n: 1.e+308 }, |
2246 | { .str: "-1.e+308" , .n: -1.e+308 } |
2247 | }; |
2248 | int size = sizeof(numbers)/sizeof(Numbers); |
2249 | for (int i = 0; i < size; ++i) { |
2250 | QByteArray json = "[ " ; |
2251 | json += numbers[i].str; |
2252 | json += " ]" ; |
2253 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2254 | #ifdef Q_OS_QNX |
2255 | if (0 == QString::compare(numbers[i].str, "1.1e-308" )) |
2256 | QEXPECT_FAIL("" , "See QTBUG-37066" , Abort); |
2257 | #endif |
2258 | QVERIFY(!doc.isEmpty()); |
2259 | QCOMPARE(doc.isArray(), true); |
2260 | QCOMPARE(doc.isObject(), false); |
2261 | QJsonArray array = doc.array(); |
2262 | QCOMPARE(array.size(), 1); |
2263 | QJsonValue val = array.at(i: 0); |
2264 | QCOMPARE(val.type(), QJsonValue::Double); |
2265 | QCOMPARE(val.toDouble(), numbers[i].n); |
2266 | } |
2267 | } |
2268 | } |
2269 | |
2270 | void tst_QtJson::parseStrings() |
2271 | { |
2272 | const char *strings [] = |
2273 | { |
2274 | "Foo" , |
2275 | "abc\\\"abc" , |
2276 | "abc\\\\abc" , |
2277 | "abc\\babc" , |
2278 | "abc\\fabc" , |
2279 | "abc\\nabc" , |
2280 | "abc\\rabc" , |
2281 | "abc\\tabc" , |
2282 | "abc\\u0019abc" , |
2283 | "abc" UNICODE_DJE "abc" , |
2284 | UNICODE_NON_CHARACTER |
2285 | }; |
2286 | int size = sizeof(strings)/sizeof(const char *); |
2287 | |
2288 | for (int i = 0; i < size; ++i) { |
2289 | QByteArray json = "[\n \"" ; |
2290 | json += strings[i]; |
2291 | json += "\"\n]\n" ; |
2292 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2293 | QVERIFY(!doc.isEmpty()); |
2294 | QCOMPARE(doc.isArray(), true); |
2295 | QCOMPARE(doc.isObject(), false); |
2296 | QJsonArray array = doc.array(); |
2297 | QCOMPARE(array.size(), 1); |
2298 | QJsonValue val = array.at(i: 0); |
2299 | QCOMPARE(val.type(), QJsonValue::String); |
2300 | |
2301 | QCOMPARE(doc.toJson(), json); |
2302 | } |
2303 | |
2304 | struct Pairs { |
2305 | const char *in; |
2306 | const char *out; |
2307 | }; |
2308 | Pairs pairs [] = { |
2309 | { .in: "abc\\/abc" , .out: "abc/abc" }, |
2310 | { .in: "abc\\u0402abc" , .out: "abc" UNICODE_DJE "abc" }, |
2311 | { .in: "abc\\u0065abc" , .out: "abceabc" }, |
2312 | { .in: "abc\\uFFFFabc" , .out: "abc" UNICODE_NON_CHARACTER "abc" } |
2313 | }; |
2314 | size = sizeof(pairs)/sizeof(Pairs); |
2315 | |
2316 | for (int i = 0; i < size; ++i) { |
2317 | QByteArray json = "[\n \"" ; |
2318 | json += pairs[i].in; |
2319 | json += "\"\n]\n" ; |
2320 | QByteArray out = "[\n \"" ; |
2321 | out += pairs[i].out; |
2322 | out += "\"\n]\n" ; |
2323 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2324 | QVERIFY(!doc.isEmpty()); |
2325 | QCOMPARE(doc.isArray(), true); |
2326 | QCOMPARE(doc.isObject(), false); |
2327 | QJsonArray array = doc.array(); |
2328 | QCOMPARE(array.size(), 1); |
2329 | QJsonValue val = array.at(i: 0); |
2330 | QCOMPARE(val.type(), QJsonValue::String); |
2331 | |
2332 | QCOMPARE(doc.toJson(), out); |
2333 | } |
2334 | |
2335 | } |
2336 | |
2337 | void tst_QtJson::parseDuplicateKeys() |
2338 | { |
2339 | const char *json = "{ \"B\": true, \"A\": null, \"B\": false }" ; |
2340 | |
2341 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2342 | QCOMPARE(doc.isObject(), true); |
2343 | |
2344 | QJsonObject o = doc.object(); |
2345 | QCOMPARE(o.size(), 2); |
2346 | QJsonObject::const_iterator it = o.constBegin(); |
2347 | QCOMPARE(it.key(), QLatin1String("A" )); |
2348 | QCOMPARE(it.value(), QJsonValue()); |
2349 | ++it; |
2350 | QCOMPARE(it.key(), QLatin1String("B" )); |
2351 | QCOMPARE(it.value(), QJsonValue(false)); |
2352 | } |
2353 | |
2354 | void tst_QtJson::testParser() |
2355 | { |
2356 | QFile file(testDataDir + "/test.json" ); |
2357 | file.open(flags: QFile::ReadOnly); |
2358 | QByteArray testJson = file.readAll(); |
2359 | |
2360 | QJsonDocument doc = QJsonDocument::fromJson(json: testJson); |
2361 | QVERIFY(!doc.isEmpty()); |
2362 | } |
2363 | |
2364 | void tst_QtJson::compactArray() |
2365 | { |
2366 | QJsonArray array; |
2367 | array.append(value: QLatin1String("First Entry" )); |
2368 | array.append(value: QLatin1String("Second Entry" )); |
2369 | array.append(value: QLatin1String("Third Entry" )); |
2370 | QJsonDocument doc(array); |
2371 | int s = doc.toBinaryData().size(); |
2372 | array.removeAt(i: 1); |
2373 | doc.setArray(array); |
2374 | QVERIFY(s > doc.toBinaryData().size()); |
2375 | s = doc.toBinaryData().size(); |
2376 | QCOMPARE(doc.toJson(), |
2377 | QByteArray("[\n" |
2378 | " \"First Entry\",\n" |
2379 | " \"Third Entry\"\n" |
2380 | "]\n" )); |
2381 | |
2382 | array.removeAt(i: 0); |
2383 | doc.setArray(array); |
2384 | QVERIFY(s > doc.toBinaryData().size()); |
2385 | s = doc.toBinaryData().size(); |
2386 | QCOMPARE(doc.toJson(), |
2387 | QByteArray("[\n" |
2388 | " \"Third Entry\"\n" |
2389 | "]\n" )); |
2390 | |
2391 | array.removeAt(i: 0); |
2392 | doc.setArray(array); |
2393 | QVERIFY(s > doc.toBinaryData().size()); |
2394 | s = doc.toBinaryData().size(); |
2395 | QCOMPARE(doc.toJson(), |
2396 | QByteArray("[\n" |
2397 | "]\n" )); |
2398 | |
2399 | } |
2400 | |
2401 | void tst_QtJson::compactObject() |
2402 | { |
2403 | QJsonObject object; |
2404 | object.insert(key: QLatin1String("Key1" ), value: QLatin1String("First Entry" )); |
2405 | object.insert(key: QLatin1String("Key2" ), value: QLatin1String("Second Entry" )); |
2406 | object.insert(key: QLatin1String("Key3" ), value: QLatin1String("Third Entry" )); |
2407 | QJsonDocument doc(object); |
2408 | int s = doc.toBinaryData().size(); |
2409 | object.remove(key: QLatin1String("Key2" )); |
2410 | doc.setObject(object); |
2411 | QVERIFY(s > doc.toBinaryData().size()); |
2412 | s = doc.toBinaryData().size(); |
2413 | QCOMPARE(doc.toJson(), |
2414 | QByteArray("{\n" |
2415 | " \"Key1\": \"First Entry\",\n" |
2416 | " \"Key3\": \"Third Entry\"\n" |
2417 | "}\n" )); |
2418 | |
2419 | object.remove(key: QLatin1String("Key1" )); |
2420 | doc.setObject(object); |
2421 | QVERIFY(s > doc.toBinaryData().size()); |
2422 | s = doc.toBinaryData().size(); |
2423 | QCOMPARE(doc.toJson(), |
2424 | QByteArray("{\n" |
2425 | " \"Key3\": \"Third Entry\"\n" |
2426 | "}\n" )); |
2427 | |
2428 | object.remove(key: QLatin1String("Key3" )); |
2429 | doc.setObject(object); |
2430 | QVERIFY(s > doc.toBinaryData().size()); |
2431 | s = doc.toBinaryData().size(); |
2432 | QCOMPARE(doc.toJson(), |
2433 | QByteArray("{\n" |
2434 | "}\n" )); |
2435 | |
2436 | } |
2437 | |
2438 | void tst_QtJson::validation() |
2439 | { |
2440 | // this basically tests that we don't crash on corrupt data |
2441 | QFile file(testDataDir + "/test.json" ); |
2442 | QVERIFY(file.open(QFile::ReadOnly)); |
2443 | QByteArray testJson = file.readAll(); |
2444 | QVERIFY(!testJson.isEmpty()); |
2445 | |
2446 | QJsonDocument doc = QJsonDocument::fromJson(json: testJson); |
2447 | QVERIFY(!doc.isNull()); |
2448 | |
2449 | QByteArray binary = doc.toBinaryData(); |
2450 | |
2451 | // only test the first 1000 bytes. Testing the full file takes too long |
2452 | for (int i = 0; i < 1000; ++i) { |
2453 | QByteArray corrupted = binary; |
2454 | corrupted[i] = char(0xff); |
2455 | QJsonDocument doc = QJsonDocument::fromBinaryData(data: corrupted); |
2456 | if (doc.isNull()) |
2457 | continue; |
2458 | QByteArray json = doc.toJson(); |
2459 | } |
2460 | |
2461 | |
2462 | QFile file2(testDataDir + "/test3.json" ); |
2463 | file2.open(flags: QFile::ReadOnly); |
2464 | testJson = file2.readAll(); |
2465 | QVERIFY(!testJson.isEmpty()); |
2466 | |
2467 | doc = QJsonDocument::fromJson(json: testJson); |
2468 | QVERIFY(!doc.isNull()); |
2469 | |
2470 | binary = doc.toBinaryData(); |
2471 | |
2472 | for (int i = 0; i < binary.size(); ++i) { |
2473 | QByteArray corrupted = binary; |
2474 | corrupted[i] = char(0xff); |
2475 | QJsonDocument doc = QJsonDocument::fromBinaryData(data: corrupted); |
2476 | if (doc.isNull()) |
2477 | continue; |
2478 | QByteArray json = doc.toJson(); |
2479 | |
2480 | corrupted = binary; |
2481 | corrupted[i] = 0x00; |
2482 | doc = QJsonDocument::fromBinaryData(data: corrupted); |
2483 | if (doc.isNull()) |
2484 | continue; |
2485 | json = doc.toJson(); |
2486 | } |
2487 | } |
2488 | |
2489 | void tst_QtJson::assignToDocument() |
2490 | { |
2491 | { |
2492 | const char *json = "{ \"inner\": { \"key\": true } }" ; |
2493 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2494 | |
2495 | QJsonObject o = doc.object(); |
2496 | QJsonValue inner = o.value(key: "inner" ); |
2497 | |
2498 | QJsonDocument innerDoc(inner.toObject()); |
2499 | |
2500 | QVERIFY(innerDoc != doc); |
2501 | QCOMPARE(innerDoc.object(), inner.toObject()); |
2502 | } |
2503 | { |
2504 | const char *json = "[ [ true ] ]" ; |
2505 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2506 | |
2507 | QJsonArray a = doc.array(); |
2508 | QJsonValue inner = a.at(i: 0); |
2509 | |
2510 | QJsonDocument innerDoc(inner.toArray()); |
2511 | |
2512 | QVERIFY(innerDoc != doc); |
2513 | QCOMPARE(innerDoc.array(), inner.toArray()); |
2514 | } |
2515 | } |
2516 | |
2517 | |
2518 | void tst_QtJson::testDuplicateKeys() |
2519 | { |
2520 | QJsonObject obj; |
2521 | obj.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
2522 | obj.insert(key: QLatin1String("foo" ), value: QLatin1String("zap" )); |
2523 | QCOMPARE(obj.size(), 1); |
2524 | QCOMPARE(obj.value(QLatin1String("foo" )).toString(), QLatin1String("zap" )); |
2525 | } |
2526 | |
2527 | void tst_QtJson::testCompaction() |
2528 | { |
2529 | // modify object enough times to trigger compactionCounter |
2530 | // and make sure the data is still valid |
2531 | QJsonObject obj; |
2532 | for (int i = 0; i < 33; ++i) { |
2533 | obj.remove(key: QLatin1String("foo" )); |
2534 | obj.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
2535 | } |
2536 | QCOMPARE(obj.size(), 1); |
2537 | QCOMPARE(obj.value(QLatin1String("foo" )).toString(), QLatin1String("bar" )); |
2538 | |
2539 | QJsonDocument doc = QJsonDocument::fromBinaryData(data: QJsonDocument(obj).toBinaryData()); |
2540 | QVERIFY(!doc.isNull()); |
2541 | QVERIFY(!doc.isEmpty()); |
2542 | QCOMPARE(doc.isArray(), false); |
2543 | QCOMPARE(doc.isObject(), true); |
2544 | QCOMPARE(doc.object(), obj); |
2545 | } |
2546 | |
2547 | void tst_QtJson::testDebugStream() |
2548 | { |
2549 | { |
2550 | // QJsonObject |
2551 | |
2552 | QJsonObject object; |
2553 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonObject()" ); |
2554 | qDebug() << object; |
2555 | |
2556 | object.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
2557 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonObject({\"foo\":\"bar\"})" ); |
2558 | qDebug() << object; |
2559 | } |
2560 | |
2561 | { |
2562 | // QJsonArray |
2563 | |
2564 | QJsonArray array; |
2565 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonArray()" ); |
2566 | qDebug() << array; |
2567 | |
2568 | array.append(value: 1); |
2569 | array.append(value: QLatin1String("foo" )); |
2570 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonArray([1,\"foo\"])" ); |
2571 | qDebug() << array; |
2572 | } |
2573 | |
2574 | { |
2575 | // QJsonDocument |
2576 | |
2577 | QJsonDocument doc; |
2578 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonDocument()" ); |
2579 | qDebug() << doc; |
2580 | |
2581 | QJsonObject object; |
2582 | object.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
2583 | doc.setObject(object); |
2584 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonDocument({\"foo\":\"bar\"})" ); |
2585 | qDebug() << doc; |
2586 | |
2587 | QJsonArray array; |
2588 | array.append(value: 1); |
2589 | array.append(value: QLatin1String("foo" )); |
2590 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonDocument([1,\"foo\"])" ); |
2591 | doc.setArray(array); |
2592 | qDebug() << doc; |
2593 | } |
2594 | |
2595 | { |
2596 | // QJsonValue |
2597 | |
2598 | QJsonValue value; |
2599 | |
2600 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(null)" ); |
2601 | qDebug() << value; |
2602 | |
2603 | value = QJsonValue(true); // bool |
2604 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(bool, true)" ); |
2605 | qDebug() << value; |
2606 | |
2607 | value = QJsonValue((double)4.2); // double |
2608 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(double, 4.2)" ); |
2609 | qDebug() << value; |
2610 | |
2611 | value = QJsonValue((int)42); // int |
2612 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(double, 42)" ); |
2613 | qDebug() << value; |
2614 | |
2615 | value = QJsonValue(QLatin1String("foo" )); // string |
2616 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(string, \"foo\")" ); |
2617 | qDebug() << value; |
2618 | |
2619 | QJsonArray array; |
2620 | array.append(value: 1); |
2621 | array.append(value: QLatin1String("foo" )); |
2622 | value = QJsonValue(array); // array |
2623 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(array, QJsonArray([1,\"foo\"]))" ); |
2624 | qDebug() << value; |
2625 | |
2626 | QJsonObject object; |
2627 | object.insert(key: QLatin1String("foo" ), value: QLatin1String("bar" )); |
2628 | value = QJsonValue(object); // object |
2629 | QTest::ignoreMessage(type: QtDebugMsg, message: "QJsonValue(object, QJsonObject({\"foo\":\"bar\"}))" ); |
2630 | qDebug() << value; |
2631 | } |
2632 | } |
2633 | |
2634 | void tst_QtJson::testCompactionError() |
2635 | { |
2636 | QJsonObject schemaObject; |
2637 | schemaObject.insert(key: "_Type" , value: QLatin1String("_SchemaType" )); |
2638 | schemaObject.insert(key: "name" , value: QLatin1String("Address" )); |
2639 | schemaObject.insert(key: "schema" , value: QJsonObject()); |
2640 | { |
2641 | QJsonObject content(schemaObject); |
2642 | QJsonDocument doc(content); |
2643 | QVERIFY(!doc.isNull()); |
2644 | QByteArray hash = QCryptographicHash::hash(data: doc.toBinaryData(), method: QCryptographicHash::Md5).toHex(); |
2645 | schemaObject.insert(key: "_Version" , value: QString::fromLatin1(str: hash.constData(), size: hash.size())); |
2646 | } |
2647 | |
2648 | QJsonObject schema; |
2649 | schema.insert(key: "streetNumber" , value: schema.value(key: "number" ).toObject()); |
2650 | schemaObject.insert(key: "schema" , value: schema); |
2651 | { |
2652 | QJsonObject content(schemaObject); |
2653 | content.remove(key: "_Uuid" ); |
2654 | content.remove(key: "_Version" ); |
2655 | QJsonDocument doc(content); |
2656 | QVERIFY(!doc.isNull()); |
2657 | QByteArray hash = QCryptographicHash::hash(data: doc.toBinaryData(), method: QCryptographicHash::Md5).toHex(); |
2658 | schemaObject.insert(key: "_Version" , value: QString::fromLatin1(str: hash.constData(), size: hash.size())); |
2659 | } |
2660 | } |
2661 | |
2662 | void tst_QtJson::parseUnicodeEscapes() |
2663 | { |
2664 | const QByteArray json = "[ \"A\\u00e4\\u00C4\" ]" ; |
2665 | |
2666 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2667 | QJsonArray array = doc.array(); |
2668 | |
2669 | QString result = QLatin1String("A" ); |
2670 | result += QChar(0xe4); |
2671 | result += QChar(0xc4); |
2672 | |
2673 | QCOMPARE(array.first().toString(), result); |
2674 | } |
2675 | |
2676 | void tst_QtJson::assignObjects() |
2677 | { |
2678 | const char *json = |
2679 | "[ { \"Key\": 1 }, { \"Key\": 2 } ]" ; |
2680 | |
2681 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2682 | QJsonArray array = doc.array(); |
2683 | |
2684 | QJsonObject object = array.at(i: 0).toObject(); |
2685 | QCOMPARE(object.value("Key" ).toDouble(), 1.); |
2686 | |
2687 | object = array.at(i: 1).toObject(); |
2688 | QCOMPARE(object.value("Key" ).toDouble(), 2.); |
2689 | } |
2690 | |
2691 | void tst_QtJson::assignArrays() |
2692 | { |
2693 | const char *json = |
2694 | "[ [ 1 ], [ 2 ] ]" ; |
2695 | |
2696 | QJsonDocument doc = QJsonDocument::fromJson(json); |
2697 | QJsonArray array = doc.array(); |
2698 | |
2699 | QJsonArray inner = array.at(i: 0).toArray() ; |
2700 | QCOMPARE(inner.at(0).toDouble(), 1.); |
2701 | |
2702 | inner= array.at(i: 1).toArray(); |
2703 | QCOMPARE(inner.at(0).toDouble(), 2.); |
2704 | } |
2705 | |
2706 | void tst_QtJson::testTrailingComma() |
2707 | { |
2708 | const char *jsons[] = { "{ \"Key\": 1, }" , "[ { \"Key\": 1 }, ]" }; |
2709 | |
2710 | for (unsigned i = 0; i < sizeof(jsons)/sizeof(jsons[0]); ++i) { |
2711 | QJsonParseError error; |
2712 | QJsonDocument doc = QJsonDocument::fromJson(json: jsons[i], error: &error); |
2713 | QCOMPARE(error.error, QJsonParseError::MissingObject); |
2714 | } |
2715 | } |
2716 | |
2717 | void tst_QtJson::testDetachBug() |
2718 | { |
2719 | QJsonObject dynamic; |
2720 | QJsonObject embedded; |
2721 | |
2722 | QJsonObject local; |
2723 | |
2724 | embedded.insert(key: "Key1" , value: QString("Value1" )); |
2725 | embedded.insert(key: "Key2" , value: QString("Value2" )); |
2726 | dynamic.insert(QStringLiteral("Bogus" ), value: QString("bogusValue" )); |
2727 | dynamic.insert(key: "embedded" , value: embedded); |
2728 | local = dynamic.value(key: "embedded" ).toObject(); |
2729 | |
2730 | dynamic.remove(key: "embedded" ); |
2731 | |
2732 | QCOMPARE(local.keys().size(),2); |
2733 | local.remove(key: "Key1" ); |
2734 | local.remove(key: "Key2" ); |
2735 | QCOMPARE(local.keys().size(), 0); |
2736 | |
2737 | local.insert(key: "Key1" , value: QString("anotherValue" )); |
2738 | QCOMPARE(local.keys().size(), 1); |
2739 | } |
2740 | |
2741 | void tst_QtJson::valueEquals() |
2742 | { |
2743 | QCOMPARE(QJsonValue(), QJsonValue()); |
2744 | QVERIFY(QJsonValue() != QJsonValue(QJsonValue::Undefined)); |
2745 | QVERIFY(QJsonValue() != QJsonValue(true)); |
2746 | QVERIFY(QJsonValue() != QJsonValue(1.)); |
2747 | QVERIFY(QJsonValue() != QJsonValue(QJsonArray())); |
2748 | QVERIFY(QJsonValue() != QJsonValue(QJsonObject())); |
2749 | |
2750 | QCOMPARE(QJsonValue(true), QJsonValue(true)); |
2751 | QVERIFY(QJsonValue(true) != QJsonValue(false)); |
2752 | QVERIFY(QJsonValue(true) != QJsonValue(QJsonValue::Undefined)); |
2753 | QVERIFY(QJsonValue(true) != QJsonValue()); |
2754 | QVERIFY(QJsonValue(true) != QJsonValue(1.)); |
2755 | QVERIFY(QJsonValue(true) != QJsonValue(QJsonArray())); |
2756 | QVERIFY(QJsonValue(true) != QJsonValue(QJsonObject())); |
2757 | |
2758 | QCOMPARE(QJsonValue(1), QJsonValue(1)); |
2759 | QVERIFY(QJsonValue(1) != QJsonValue(2)); |
2760 | QCOMPARE(QJsonValue(1), QJsonValue(1.)); |
2761 | QVERIFY(QJsonValue(1) != QJsonValue(1.1)); |
2762 | QVERIFY(QJsonValue(1) != QJsonValue(QJsonValue::Undefined)); |
2763 | QVERIFY(QJsonValue(1) != QJsonValue()); |
2764 | QVERIFY(QJsonValue(1) != QJsonValue(true)); |
2765 | QVERIFY(QJsonValue(1) != QJsonValue(QJsonArray())); |
2766 | QVERIFY(QJsonValue(1) != QJsonValue(QJsonObject())); |
2767 | |
2768 | QCOMPARE(QJsonValue(1.), QJsonValue(1.)); |
2769 | QVERIFY(QJsonValue(1.) != QJsonValue(2.)); |
2770 | QVERIFY(QJsonValue(1.) != QJsonValue(QJsonValue::Undefined)); |
2771 | QVERIFY(QJsonValue(1.) != QJsonValue()); |
2772 | QVERIFY(QJsonValue(1.) != QJsonValue(true)); |
2773 | QVERIFY(QJsonValue(1.) != QJsonValue(QJsonArray())); |
2774 | QVERIFY(QJsonValue(1.) != QJsonValue(QJsonObject())); |
2775 | |
2776 | QCOMPARE(QJsonValue(QJsonArray()), QJsonValue(QJsonArray())); |
2777 | QJsonArray nonEmptyArray; |
2778 | nonEmptyArray.append(value: true); |
2779 | QVERIFY(QJsonValue(QJsonArray()) != nonEmptyArray); |
2780 | QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonValue::Undefined)); |
2781 | QVERIFY(QJsonValue(QJsonArray()) != QJsonValue()); |
2782 | QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(true)); |
2783 | QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(1.)); |
2784 | QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonObject())); |
2785 | |
2786 | QCOMPARE(QJsonValue(QJsonObject()), QJsonValue(QJsonObject())); |
2787 | QJsonObject nonEmptyObject; |
2788 | nonEmptyObject.insert(key: "Key" , value: true); |
2789 | QVERIFY(QJsonValue(QJsonObject()) != nonEmptyObject); |
2790 | QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonValue::Undefined)); |
2791 | QVERIFY(QJsonValue(QJsonObject()) != QJsonValue()); |
2792 | QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(true)); |
2793 | QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(1.)); |
2794 | QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonArray())); |
2795 | |
2796 | QCOMPARE(QJsonValue("foo" ), QJsonValue(QLatin1String("foo" ))); |
2797 | QCOMPARE(QJsonValue("foo" ), QJsonValue(QString("foo" ))); |
2798 | QCOMPARE(QJsonValue("\x66\x6f\x6f" ), QJsonValue(QString("foo" ))); |
2799 | QCOMPARE(QJsonValue("\x62\x61\x72" ), QJsonValue("bar" )); |
2800 | QCOMPARE(QJsonValue(UNICODE_NON_CHARACTER), QJsonValue(QString(UNICODE_NON_CHARACTER))); |
2801 | QCOMPARE(QJsonValue(UNICODE_DJE), QJsonValue(QString(UNICODE_DJE))); |
2802 | QCOMPARE(QJsonValue("\xc3\xa9" ), QJsonValue(QString("\xc3\xa9" ))); |
2803 | } |
2804 | |
2805 | void tst_QtJson::objectEquals_data() |
2806 | { |
2807 | QTest::addColumn<QJsonObject>(name: "left" ); |
2808 | QTest::addColumn<QJsonObject>(name: "right" ); |
2809 | QTest::addColumn<bool>(name: "result" ); |
2810 | |
2811 | QTest::newRow(dataTag: "two defaults" ) << QJsonObject() << QJsonObject() << true; |
2812 | |
2813 | QJsonObject object1; |
2814 | object1.insert(key: "property" , value: 1); |
2815 | QJsonObject object2; |
2816 | object2["property" ] = 1; |
2817 | QJsonObject object3; |
2818 | object3.insert(key: "property1" , value: 1); |
2819 | object3.insert(key: "property2" , value: 2); |
2820 | |
2821 | QTest::newRow(dataTag: "the same object (1 vs 2)" ) << object1 << object2 << true; |
2822 | QTest::newRow(dataTag: "the same object (3 vs 3)" ) << object3 << object3 << true; |
2823 | QTest::newRow(dataTag: "different objects (2 vs 3)" ) << object2 << object3 << false; |
2824 | QTest::newRow(dataTag: "object vs default" ) << object1 << QJsonObject() << false; |
2825 | |
2826 | QJsonObject empty; |
2827 | empty.insert(key: "property" , value: 1); |
2828 | empty.take(key: "property" ); |
2829 | QTest::newRow(dataTag: "default vs empty" ) << QJsonObject() << empty << true; |
2830 | QTest::newRow(dataTag: "empty vs empty" ) << empty << empty << true; |
2831 | QTest::newRow(dataTag: "object vs empty" ) << object1 << empty << false; |
2832 | |
2833 | QJsonObject referencedEmpty; |
2834 | referencedEmpty["undefined" ]; |
2835 | QTest::newRow(dataTag: "referenced empty vs referenced empty" ) << referencedEmpty << referencedEmpty << true; |
2836 | QTest::newRow(dataTag: "referenced empty vs object" ) << referencedEmpty << object1 << false; |
2837 | |
2838 | QJsonObject referencedObject1; |
2839 | referencedObject1.insert(key: "property" , value: 1); |
2840 | referencedObject1["undefined" ]; |
2841 | QJsonObject referencedObject2; |
2842 | referencedObject2.insert(key: "property" , value: 1); |
2843 | referencedObject2["aaaaaaaaa" ]; // earlier then "property" |
2844 | referencedObject2["zzzzzzzzz" ]; // after "property" |
2845 | QTest::newRow(dataTag: "referenced object vs default" ) << referencedObject1 << QJsonObject() << false; |
2846 | QTest::newRow(dataTag: "referenced object vs referenced object" ) << referencedObject1 << referencedObject1 << true; |
2847 | QTest::newRow(dataTag: "referenced object vs object (different)" ) << referencedObject1 << object3 << false; |
2848 | } |
2849 | |
2850 | void tst_QtJson::objectEquals() |
2851 | { |
2852 | QFETCH(QJsonObject, left); |
2853 | QFETCH(QJsonObject, right); |
2854 | QFETCH(bool, result); |
2855 | |
2856 | QCOMPARE(left == right, result); |
2857 | QCOMPARE(right == left, result); |
2858 | |
2859 | // invariants checks |
2860 | QCOMPARE(left, left); |
2861 | QCOMPARE(right, right); |
2862 | QCOMPARE(left != right, !result); |
2863 | QCOMPARE(right != left, !result); |
2864 | |
2865 | // The same but from QJsonValue perspective |
2866 | QCOMPARE(QJsonValue(left) == QJsonValue(right), result); |
2867 | QCOMPARE(QJsonValue(left) != QJsonValue(right), !result); |
2868 | QCOMPARE(QJsonValue(right) == QJsonValue(left), result); |
2869 | QCOMPARE(QJsonValue(right) != QJsonValue(left), !result); |
2870 | |
2871 | // The same, but from a QJsonDocument perspective |
2872 | QCOMPARE(QJsonDocument(left) == QJsonDocument(right), result); |
2873 | QCOMPARE(QJsonDocument(left) != QJsonDocument(right), !result); |
2874 | QCOMPARE(QJsonDocument(right) == QJsonDocument(left), result); |
2875 | QCOMPARE(QJsonDocument(right) != QJsonDocument(left), !result); |
2876 | } |
2877 | |
2878 | void tst_QtJson::arrayEquals_data() |
2879 | { |
2880 | QTest::addColumn<QJsonArray>(name: "left" ); |
2881 | QTest::addColumn<QJsonArray>(name: "right" ); |
2882 | QTest::addColumn<bool>(name: "result" ); |
2883 | |
2884 | QTest::newRow(dataTag: "two defaults" ) << QJsonArray() << QJsonArray() << true; |
2885 | |
2886 | QJsonArray array1; |
2887 | array1.append(value: 1); |
2888 | QJsonArray array2; |
2889 | array2.append(value: 2111); |
2890 | array2[0] = 1; |
2891 | QJsonArray array3; |
2892 | array3.insert(i: 0, value: 1); |
2893 | array3.insert(i: 1, value: 2); |
2894 | |
2895 | QTest::newRow(dataTag: "the same array (1 vs 2)" ) << array1 << array2 << true; |
2896 | QTest::newRow(dataTag: "the same array (3 vs 3)" ) << array3 << array3 << true; |
2897 | QTest::newRow(dataTag: "different arrays (2 vs 3)" ) << array2 << array3 << false; |
2898 | QTest::newRow(dataTag: "array vs default" ) << array1 << QJsonArray() << false; |
2899 | |
2900 | QJsonArray empty; |
2901 | empty.append(value: 1); |
2902 | empty.takeAt(i: 0); |
2903 | QTest::newRow(dataTag: "default vs empty" ) << QJsonArray() << empty << true; |
2904 | QTest::newRow(dataTag: "empty vs default" ) << empty << QJsonArray() << true; |
2905 | QTest::newRow(dataTag: "empty vs empty" ) << empty << empty << true; |
2906 | QTest::newRow(dataTag: "array vs empty" ) << array1 << empty << false; |
2907 | } |
2908 | |
2909 | void tst_QtJson::arrayEquals() |
2910 | { |
2911 | QFETCH(QJsonArray, left); |
2912 | QFETCH(QJsonArray, right); |
2913 | QFETCH(bool, result); |
2914 | |
2915 | QCOMPARE(left == right, result); |
2916 | QCOMPARE(right == left, result); |
2917 | |
2918 | // invariants checks |
2919 | QCOMPARE(left, left); |
2920 | QCOMPARE(right, right); |
2921 | QCOMPARE(left != right, !result); |
2922 | QCOMPARE(right != left, !result); |
2923 | |
2924 | // The same but from QJsonValue perspective |
2925 | QCOMPARE(QJsonValue(left) == QJsonValue(right), result); |
2926 | QCOMPARE(QJsonValue(left) != QJsonValue(right), !result); |
2927 | QCOMPARE(QJsonValue(right) == QJsonValue(left), result); |
2928 | QCOMPARE(QJsonValue(right) != QJsonValue(left), !result); |
2929 | |
2930 | // The same but from QJsonDocument perspective |
2931 | QCOMPARE(QJsonDocument(left) == QJsonDocument(right), result); |
2932 | QCOMPARE(QJsonDocument(left) != QJsonDocument(right), !result); |
2933 | QCOMPARE(QJsonDocument(right) == QJsonDocument(left), result); |
2934 | QCOMPARE(QJsonDocument(right) != QJsonDocument(left), !result); |
2935 | } |
2936 | |
2937 | void tst_QtJson::documentEquals_data() |
2938 | { |
2939 | QTest::addColumn<QJsonDocument>(name: "left" ); |
2940 | QTest::addColumn<QJsonDocument>(name: "right" ); |
2941 | QTest::addColumn<bool>(name: "result" ); |
2942 | |
2943 | QTest::newRow(dataTag: "two defaults" ) << QJsonDocument() << QJsonDocument() << true; |
2944 | |
2945 | QJsonDocument emptyobj(QJsonObject{}); |
2946 | QJsonDocument emptyarr(QJsonArray{}); |
2947 | QTest::newRow(dataTag: "emptyarray vs default" ) << emptyarr << QJsonDocument() << false; |
2948 | QTest::newRow(dataTag: "emptyobject vs default" ) << emptyobj << QJsonDocument() << false; |
2949 | QTest::newRow(dataTag: "emptyarray vs emptyobject" ) << emptyarr << emptyobj << false; |
2950 | |
2951 | QJsonDocument array1(QJsonArray{1}); |
2952 | QJsonDocument array2(QJsonArray{2}); |
2953 | QTest::newRow(dataTag: "emptyarray vs emptyarray" ) << emptyarr << emptyarr << true; |
2954 | QTest::newRow(dataTag: "emptyarray vs array" ) << emptyarr << array1 << false; |
2955 | QTest::newRow(dataTag: "array vs array" ) << array1 << array1 << true; |
2956 | QTest::newRow(dataTag: "array vs otherarray" ) << array1 << array2 << false; |
2957 | |
2958 | QJsonDocument object1(QJsonObject{{"hello" , "world" }}); |
2959 | QJsonDocument object2(QJsonObject{{"hello" , 2}}); |
2960 | QTest::newRow(dataTag: "emptyobject vs emptyobject" ) << emptyobj << emptyobj << true; |
2961 | QTest::newRow(dataTag: "emptyobject vs object" ) << emptyobj << object1 << false; |
2962 | QTest::newRow(dataTag: "object vs object" ) << object1 << object1 << true; |
2963 | QTest::newRow(dataTag: "object vs otherobject" ) << object1 << object2 << false; |
2964 | |
2965 | QTest::newRow(dataTag: "object vs array" ) << array1 << object1 << false; |
2966 | } |
2967 | |
2968 | void tst_QtJson::documentEquals() |
2969 | { |
2970 | QFETCH(QJsonDocument, left); |
2971 | QFETCH(QJsonDocument, right); |
2972 | QFETCH(bool, result); |
2973 | |
2974 | QCOMPARE(left == right, result); |
2975 | QCOMPARE(right == left, result); |
2976 | |
2977 | // invariants checks |
2978 | QCOMPARE(left, left); |
2979 | QCOMPARE(right, right); |
2980 | QCOMPARE(left != right, !result); |
2981 | QCOMPARE(right != left, !result); |
2982 | } |
2983 | |
2984 | void tst_QtJson::bom() |
2985 | { |
2986 | QFile file(testDataDir + "/bom.json" ); |
2987 | file.open(flags: QFile::ReadOnly); |
2988 | QByteArray json = file.readAll(); |
2989 | |
2990 | // Import json document into a QJsonDocument |
2991 | QJsonParseError error; |
2992 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
2993 | |
2994 | QVERIFY(!doc.isNull()); |
2995 | QCOMPARE(error.error, QJsonParseError::NoError); |
2996 | } |
2997 | |
2998 | void tst_QtJson::nesting() |
2999 | { |
3000 | // check that we abort parsing too deeply nested json documents. |
3001 | // this is to make sure we don't crash because the parser exhausts the |
3002 | // stack. |
3003 | |
3004 | const char *array_data = |
3005 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3006 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3007 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3008 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3009 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3010 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3011 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3012 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
3013 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3014 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3015 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3016 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3017 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3018 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3019 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" |
3020 | "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" ; |
3021 | |
3022 | QByteArray json(array_data); |
3023 | QJsonParseError error; |
3024 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
3025 | |
3026 | QVERIFY(!doc.isNull()); |
3027 | QCOMPARE(error.error, QJsonParseError::NoError); |
3028 | |
3029 | json.prepend(c: '['); |
3030 | json.append(c: ']'); |
3031 | doc = QJsonDocument::fromJson(json, error: &error); |
3032 | |
3033 | QVERIFY(doc.isNull()); |
3034 | QCOMPARE(error.error, QJsonParseError::DeepNesting); |
3035 | |
3036 | json = QByteArray("true " ); |
3037 | |
3038 | for (int i = 0; i < 1024; ++i) { |
3039 | json.prepend(s: "{ \"Key\": " ); |
3040 | json.append(s: " }" ); |
3041 | } |
3042 | |
3043 | doc = QJsonDocument::fromJson(json, error: &error); |
3044 | |
3045 | QVERIFY(!doc.isNull()); |
3046 | QCOMPARE(error.error, QJsonParseError::NoError); |
3047 | |
3048 | json.prepend(c: '['); |
3049 | json.append(c: ']'); |
3050 | doc = QJsonDocument::fromJson(json, error: &error); |
3051 | |
3052 | QVERIFY(doc.isNull()); |
3053 | QCOMPARE(error.error, QJsonParseError::DeepNesting); |
3054 | |
3055 | } |
3056 | |
3057 | void tst_QtJson::longStrings() |
3058 | { |
3059 | // test around 15 and 16 bit boundaries, as these are limits |
3060 | // in the data structures (for Latin1String in qjson_p.h) |
3061 | QString s(0x7ff0, 'a'); |
3062 | QByteArray ba(0x7ff0, 'a'); |
3063 | ba.append(n: 0x8010 - 0x7ff0, ch: 'c'); |
3064 | for (int i = 0x7ff0; i < 0x8010; i++) { |
3065 | s.append(c: QLatin1Char('c')); |
3066 | |
3067 | QMap <QString, QVariant> map; |
3068 | map["key" ] = s; |
3069 | |
3070 | /* Create a QJsonDocument from the QMap ... */ |
3071 | QJsonDocument d1 = QJsonDocument::fromVariant(variant: QVariant(map)); |
3072 | /* ... and a QByteArray from the QJsonDocument */ |
3073 | QByteArray a1 = d1.toJson(); |
3074 | |
3075 | /* Create a QJsonDocument from the QByteArray ... */ |
3076 | QJsonDocument d2 = QJsonDocument::fromJson(json: a1); |
3077 | /* ... and a QByteArray from the QJsonDocument */ |
3078 | QByteArray a2 = d2.toJson(); |
3079 | QCOMPARE(a1, a2); |
3080 | |
3081 | // Test long keys |
3082 | QJsonObject o1, o2; |
3083 | o1[s] = 42; |
3084 | o2[QLatin1String(ba.data(), i + 1)] = 42; |
3085 | d1.setObject(o1); |
3086 | d2.setObject(o2); |
3087 | a1 = d1.toJson(); |
3088 | a2 = d2.toJson(); |
3089 | QCOMPARE(a1, a2); |
3090 | } |
3091 | |
3092 | s = QString(0xfff0, 'a'); |
3093 | ba = QByteArray(0xfff0, 'a'); |
3094 | ba.append(n: 0x10010 - 0xfff0, ch: 'c'); |
3095 | for (int i = 0xfff0; i < 0x10010; i++) { |
3096 | s.append(c: QLatin1Char('c')); |
3097 | |
3098 | QMap <QString, QVariant> map; |
3099 | map["key" ] = s; |
3100 | |
3101 | /* Create a QJsonDocument from the QMap ... */ |
3102 | QJsonDocument d1 = QJsonDocument::fromVariant(variant: QVariant(map)); |
3103 | /* ... and a QByteArray from the QJsonDocument */ |
3104 | QByteArray a1 = d1.toJson(); |
3105 | |
3106 | /* Create a QJsonDocument from the QByteArray ... */ |
3107 | QJsonDocument d2 = QJsonDocument::fromJson(json: a1); |
3108 | /* ... and a QByteArray from the QJsonDocument */ |
3109 | QByteArray a2 = d2.toJson(); |
3110 | QCOMPARE(a1, a2); |
3111 | |
3112 | // Test long keys |
3113 | QJsonObject o1, o2; |
3114 | o1[s] = 42; |
3115 | o2[QLatin1String(ba.data(), i + 1)] = 42; |
3116 | d1.setObject(o1); |
3117 | d2.setObject(o2); |
3118 | a1 = d1.toJson(); |
3119 | a2 = d2.toJson(); |
3120 | QCOMPARE(a1, a2); |
3121 | } |
3122 | } |
3123 | |
3124 | void tst_QtJson::testJsonValueRefDefault() |
3125 | { |
3126 | QJsonObject empty; |
3127 | |
3128 | QCOMPARE(empty["n/a" ].toString(), QString()); |
3129 | QCOMPARE(empty["n/a" ].toString("default" ), QStringLiteral("default" )); |
3130 | |
3131 | QCOMPARE(empty["n/a" ].toBool(), false); |
3132 | QCOMPARE(empty["n/a" ].toBool(true), true); |
3133 | |
3134 | QCOMPARE(empty["n/a" ].toInt(), 0); |
3135 | QCOMPARE(empty["n/a" ].toInt(42), 42); |
3136 | |
3137 | QCOMPARE(empty["n/a" ].toDouble(), 0.0); |
3138 | QCOMPARE(empty["n/a" ].toDouble(42.0), 42.0); |
3139 | } |
3140 | |
3141 | void tst_QtJson::arrayInitializerList() |
3142 | { |
3143 | QVERIFY(QJsonArray{}.isEmpty()); |
3144 | QCOMPARE(QJsonArray{"one" }.count(), 1); |
3145 | QCOMPARE(QJsonArray{1}.count(), 1); |
3146 | |
3147 | { |
3148 | QJsonArray a{1.3, "hello" , 0}; |
3149 | QCOMPARE(QJsonValue(a[0]), QJsonValue(1.3)); |
3150 | QCOMPARE(QJsonValue(a[1]), QJsonValue("hello" )); |
3151 | QCOMPARE(QJsonValue(a[2]), QJsonValue(0)); |
3152 | QCOMPARE(a.count(), 3); |
3153 | } |
3154 | { |
3155 | QJsonObject o; |
3156 | o["property" ] = 1; |
3157 | QJsonArray a1 {o}; |
3158 | QCOMPARE(a1.count(), 1); |
3159 | QCOMPARE(a1[0].toObject(), o); |
3160 | |
3161 | QJsonArray a2 {o, 23}; |
3162 | QCOMPARE(a2.count(), 2); |
3163 | QCOMPARE(a2[0].toObject(), o); |
3164 | QCOMPARE(QJsonValue(a2[1]), QJsonValue(23)); |
3165 | |
3166 | QJsonArray a3 { a1, o, a2 }; |
3167 | QCOMPARE(QJsonValue(a3[0]), QJsonValue(a1)); |
3168 | QCOMPARE(QJsonValue(a3[1]), QJsonValue(o)); |
3169 | QCOMPARE(QJsonValue(a3[2]), QJsonValue(a2)); |
3170 | |
3171 | QJsonArray a4 { 1, QJsonArray{1,2,3}, QJsonArray{"hello" , 2}, QJsonObject{{"one" , 1}} }; |
3172 | QCOMPARE(a4.count(), 4); |
3173 | QCOMPARE(QJsonValue(a4[0]), QJsonValue(1)); |
3174 | |
3175 | { |
3176 | QJsonArray a41 = a4[1].toArray(); |
3177 | QJsonArray a42 = a4[2].toArray(); |
3178 | QJsonObject a43 = a4[3].toObject(); |
3179 | QCOMPARE(a41.count(), 3); |
3180 | QCOMPARE(a42.count(), 2); |
3181 | QCOMPARE(a43.count(), 1); |
3182 | |
3183 | QCOMPARE(QJsonValue(a41[2]), QJsonValue(3)); |
3184 | QCOMPARE(QJsonValue(a42[1]), QJsonValue(2)); |
3185 | QCOMPARE(QJsonValue(a43["one" ]), QJsonValue(1)); |
3186 | } |
3187 | } |
3188 | } |
3189 | |
3190 | void tst_QtJson::objectInitializerList() |
3191 | { |
3192 | QVERIFY(QJsonObject{}.isEmpty()); |
3193 | |
3194 | { // one property |
3195 | QJsonObject one {{"one" , 1}}; |
3196 | QCOMPARE(one.count(), 1); |
3197 | QVERIFY(one.contains("one" )); |
3198 | QCOMPARE(QJsonValue(one["one" ]), QJsonValue(1)); |
3199 | } |
3200 | { // two properties |
3201 | QJsonObject two { |
3202 | {"one" , 1}, |
3203 | {"two" , 2} |
3204 | }; |
3205 | QCOMPARE(two.count(), 2); |
3206 | QVERIFY(two.contains("one" )); |
3207 | QVERIFY(two.contains("two" )); |
3208 | QCOMPARE(QJsonValue(two["one" ]), QJsonValue(1)); |
3209 | QCOMPARE(QJsonValue(two["two" ]), QJsonValue(2)); |
3210 | } |
3211 | { // nested object |
3212 | QJsonObject object{{"nested" , QJsonObject{{"innerProperty" , 2}}}}; |
3213 | QCOMPARE(object.count(), 1); |
3214 | QVERIFY(object.contains("nested" )); |
3215 | QVERIFY(object["nested" ].isObject()); |
3216 | |
3217 | QJsonObject nested = object["nested" ].toObject(); |
3218 | QCOMPARE(QJsonValue(nested["innerProperty" ]), QJsonValue(2)); |
3219 | } |
3220 | { // nested array |
3221 | QJsonObject object{{"nested" , QJsonArray{"innerValue" , 2.1, "bum cyk cyk" }}}; |
3222 | QCOMPARE(object.count(), 1); |
3223 | QVERIFY(object.contains("nested" )); |
3224 | QVERIFY(object["nested" ].isArray()); |
3225 | |
3226 | QJsonArray nested = object["nested" ].toArray(); |
3227 | QCOMPARE(nested.count(), 3); |
3228 | QCOMPARE(QJsonValue(nested[0]), QJsonValue("innerValue" )); |
3229 | QCOMPARE(QJsonValue(nested[1]), QJsonValue(2.1)); |
3230 | } |
3231 | } |
3232 | |
3233 | void tst_QtJson::unicodeKeys() |
3234 | { |
3235 | QByteArray json = "{" |
3236 | "\"x\\u2090_1\": \"hello_1\"," |
3237 | "\"y\\u2090_2\": \"hello_2\"," |
3238 | "\"T\\u2090_3\": \"hello_3\"," |
3239 | "\"xyz_4\": \"hello_4\"," |
3240 | "\"abc_5\": \"hello_5\"" |
3241 | "}" ; |
3242 | |
3243 | QJsonParseError error; |
3244 | QJsonDocument doc = QJsonDocument::fromJson(json, error: &error); |
3245 | QCOMPARE(error.error, QJsonParseError::NoError); |
3246 | QJsonObject o = doc.object(); |
3247 | |
3248 | const auto keys = o.keys(); |
3249 | QCOMPARE(keys.size(), 5); |
3250 | for (const QString &key : keys) { |
3251 | QString suffix = key.mid(position: key.indexOf(c: QLatin1Char('_'))); |
3252 | QCOMPARE(o[key].toString(), QString("hello" ) + suffix); |
3253 | } |
3254 | } |
3255 | |
3256 | void tst_QtJson::garbageAtEnd() |
3257 | { |
3258 | QJsonParseError error; |
3259 | QJsonDocument doc = QJsonDocument::fromJson(json: "{}," , error: &error); |
3260 | QCOMPARE(error.error, QJsonParseError::GarbageAtEnd); |
3261 | QCOMPARE(error.offset, 2); |
3262 | QVERIFY(doc.isEmpty()); |
3263 | |
3264 | doc = QJsonDocument::fromJson(json: "{} " , error: &error); |
3265 | QCOMPARE(error.error, QJsonParseError::NoError); |
3266 | QVERIFY(!doc.isEmpty()); |
3267 | } |
3268 | |
3269 | void tst_QtJson::removeNonLatinKey() |
3270 | { |
3271 | const QString nonLatinKeyName = QString::fromUtf8(str: "Атрибут100500" ); |
3272 | |
3273 | QJsonObject sourceObject; |
3274 | |
3275 | sourceObject.insert(key: "code" , value: 1); |
3276 | sourceObject.remove(key: "code" ); |
3277 | |
3278 | sourceObject.insert(key: nonLatinKeyName, value: 1); |
3279 | |
3280 | const QByteArray json = QJsonDocument(sourceObject).toJson(); |
3281 | const QJsonObject restoredObject = QJsonDocument::fromJson(json).object(); |
3282 | |
3283 | QCOMPARE(sourceObject.keys(), restoredObject.keys()); |
3284 | QVERIFY(sourceObject.contains(nonLatinKeyName)); |
3285 | QVERIFY(restoredObject.contains(nonLatinKeyName)); |
3286 | } |
3287 | |
3288 | void tst_QtJson::documentFromVariant() |
3289 | { |
3290 | // Test the valid forms of QJsonDocument::fromVariant. |
3291 | |
3292 | QString string = QStringLiteral("value" ); |
3293 | |
3294 | QStringList strList; |
3295 | strList.append(t: string); |
3296 | |
3297 | QJsonDocument da1 = QJsonDocument::fromVariant(variant: QVariant(strList)); |
3298 | QVERIFY(da1.isArray()); |
3299 | |
3300 | QVariantList list; |
3301 | list.append(t: string); |
3302 | |
3303 | QJsonDocument da2 = QJsonDocument::fromVariant(variant: list); |
3304 | QVERIFY(da2.isArray()); |
3305 | |
3306 | // As JSON arrays they should be equal. |
3307 | QCOMPARE(da1.array(), da2.array()); |
3308 | |
3309 | |
3310 | QMap <QString, QVariant> map; |
3311 | map["key" ] = string; |
3312 | |
3313 | QJsonDocument do1 = QJsonDocument::fromVariant(variant: QVariant(map)); |
3314 | QVERIFY(do1.isObject()); |
3315 | |
3316 | QHash <QString, QVariant> hash; |
3317 | hash["key" ] = string; |
3318 | |
3319 | QJsonDocument do2 = QJsonDocument::fromVariant(variant: QVariant(hash)); |
3320 | QVERIFY(do2.isObject()); |
3321 | |
3322 | // As JSON objects they should be equal. |
3323 | QCOMPARE(do1.object(), do2.object()); |
3324 | } |
3325 | |
3326 | void tst_QtJson::parseErrorOffset_data() |
3327 | { |
3328 | QTest::addColumn<QByteArray>(name: "json" ); |
3329 | QTest::addColumn<int>(name: "errorOffset" ); |
3330 | |
3331 | QTest::newRow(dataTag: "Trailing comma in object" ) << QByteArray("{ \"value\": false, }" ) << 19; |
3332 | QTest::newRow(dataTag: "Trailing comma in object plus whitespace" ) << QByteArray("{ \"value\": false, } " ) << 19; |
3333 | QTest::newRow(dataTag: "Trailing comma in array" ) << QByteArray("[ false, ]" ) << 10; |
3334 | QTest::newRow(dataTag: "Trailing comma in array plus whitespace" ) << QByteArray("[ false, ] " ) << 10; |
3335 | QTest::newRow(dataTag: "Missing value in object" ) << QByteArray("{ \"value\": , } " ) << 12; |
3336 | QTest::newRow(dataTag: "Missing value in array" ) << QByteArray("[ \"value\" , , ] " ) << 13; |
3337 | QTest::newRow(dataTag: "Leading comma in object" ) << QByteArray("{ , \"value\": false}" ) << 3; |
3338 | QTest::newRow(dataTag: "Leading comma in array" ) << QByteArray("[ , false]" ) << 3; |
3339 | QTest::newRow(dataTag: "Stray ," ) << QByteArray(" , " ) << 3; |
3340 | QTest::newRow(dataTag: "Stray [" ) << QByteArray(" [ " ) << 5; |
3341 | QTest::newRow(dataTag: "Stray }" ) << QByteArray(" } " ) << 3; |
3342 | } |
3343 | |
3344 | void tst_QtJson::parseErrorOffset() |
3345 | { |
3346 | QFETCH(QByteArray, json); |
3347 | QFETCH(int, errorOffset); |
3348 | |
3349 | QJsonParseError error; |
3350 | QJsonDocument::fromJson(json, error: &error); |
3351 | |
3352 | QVERIFY(error.error != QJsonParseError::NoError); |
3353 | QCOMPARE(error.offset, errorOffset); |
3354 | } |
3355 | |
3356 | void tst_QtJson::implicitValueType() |
3357 | { |
3358 | QJsonObject rootObject{ |
3359 | {"object" , QJsonObject{{"value" , 42}}}, |
3360 | {"array" , QJsonArray{665, 666, 667}} |
3361 | }; |
3362 | |
3363 | QJsonValue objectValue = rootObject["object" ]; |
3364 | QCOMPARE(objectValue["value" ].toInt(), 42); |
3365 | QCOMPARE(objectValue["missingValue" ], QJsonValue(QJsonValue::Undefined)); |
3366 | QCOMPARE(objectValue[123], QJsonValue(QJsonValue::Undefined)); |
3367 | QCOMPARE(objectValue["missingValue" ].toInt(123), 123); |
3368 | |
3369 | QJsonValue arrayValue = rootObject["array" ]; |
3370 | QCOMPARE(arrayValue[1].toInt(), 666); |
3371 | QCOMPARE(arrayValue[-1], QJsonValue(QJsonValue::Undefined)); |
3372 | QCOMPARE(arrayValue["asObject" ], QJsonValue(QJsonValue::Undefined)); |
3373 | QCOMPARE(arrayValue[-1].toInt(123), 123); |
3374 | |
3375 | const QJsonObject constObject = rootObject; |
3376 | QCOMPARE(constObject["object" ]["value" ].toInt(), 42); |
3377 | QCOMPARE(constObject["array" ][1].toInt(), 666); |
3378 | |
3379 | QJsonValue objectAsValue(rootObject); |
3380 | QCOMPARE(objectAsValue["object" ]["value" ].toInt(), 42); |
3381 | QCOMPARE(objectAsValue["array" ][1].toInt(), 666); |
3382 | } |
3383 | |
3384 | void tst_QtJson::implicitDocumentType() |
3385 | { |
3386 | QJsonDocument emptyDocument; |
3387 | QCOMPARE(emptyDocument["asObject" ], QJsonValue(QJsonValue::Undefined)); |
3388 | QCOMPARE(emptyDocument[123], QJsonValue(QJsonValue::Undefined)); |
3389 | |
3390 | QJsonDocument objectDocument(QJsonObject{{"value" , 42}}); |
3391 | QCOMPARE(objectDocument["value" ].toInt(), 42); |
3392 | QCOMPARE(objectDocument["missingValue" ], QJsonValue(QJsonValue::Undefined)); |
3393 | QCOMPARE(objectDocument[123], QJsonValue(QJsonValue::Undefined)); |
3394 | QCOMPARE(objectDocument["missingValue" ].toInt(123), 123); |
3395 | |
3396 | QJsonDocument arrayDocument(QJsonArray{665, 666, 667}); |
3397 | QCOMPARE(arrayDocument[1].toInt(), 666); |
3398 | QCOMPARE(arrayDocument[-1], QJsonValue(QJsonValue::Undefined)); |
3399 | QCOMPARE(arrayDocument["asObject" ], QJsonValue(QJsonValue::Undefined)); |
3400 | QCOMPARE(arrayDocument[-1].toInt(123), 123); |
3401 | } |
3402 | |
3403 | void tst_QtJson::streamSerializationQJsonDocument_data() |
3404 | { |
3405 | QTest::addColumn<QJsonDocument>(name: "document" ); |
3406 | QTest::newRow(dataTag: "empty" ) << QJsonDocument(); |
3407 | QTest::newRow(dataTag: "object" ) << QJsonDocument(QJsonObject{{"value" , 42}}); |
3408 | } |
3409 | |
3410 | void tst_QtJson::streamSerializationQJsonDocument() |
3411 | { |
3412 | // Check interface only, implementation is tested through to and from |
3413 | // json functions. |
3414 | QByteArray buffer; |
3415 | QFETCH(QJsonDocument, document); |
3416 | QJsonDocument output; |
3417 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3418 | save << document; |
3419 | QDataStream load(buffer); |
3420 | load >> output; |
3421 | QCOMPARE(output, document); |
3422 | } |
3423 | |
3424 | void tst_QtJson::streamSerializationQJsonArray_data() |
3425 | { |
3426 | QTest::addColumn<QJsonArray>(name: "array" ); |
3427 | QTest::newRow(dataTag: "empty" ) << QJsonArray(); |
3428 | QTest::newRow(dataTag: "values" ) << QJsonArray{665, 666, 667}; |
3429 | } |
3430 | |
3431 | void tst_QtJson::streamSerializationQJsonArray() |
3432 | { |
3433 | // Check interface only, implementation is tested through to and from |
3434 | // json functions. |
3435 | QByteArray buffer; |
3436 | QFETCH(QJsonArray, array); |
3437 | QJsonArray output; |
3438 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3439 | save << array; |
3440 | QDataStream load(buffer); |
3441 | load >> output; |
3442 | QCOMPARE(output, array); |
3443 | } |
3444 | |
3445 | void tst_QtJson::streamSerializationQJsonObject_data() |
3446 | { |
3447 | QTest::addColumn<QJsonObject>(name: "object" ); |
3448 | QTest::newRow(dataTag: "empty" ) << QJsonObject(); |
3449 | QTest::newRow(dataTag: "non-empty" ) << QJsonObject{{"foo" , 665}, {"bar" , 666}}; |
3450 | } |
3451 | |
3452 | void tst_QtJson::streamSerializationQJsonObject() |
3453 | { |
3454 | // Check interface only, implementation is tested through to and from |
3455 | // json functions. |
3456 | QByteArray buffer; |
3457 | QFETCH(QJsonObject, object); |
3458 | QJsonObject output; |
3459 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3460 | save << object; |
3461 | QDataStream load(buffer); |
3462 | load >> output; |
3463 | QCOMPARE(output, object); |
3464 | } |
3465 | |
3466 | void tst_QtJson::streamSerializationQJsonValue_data() |
3467 | { |
3468 | QTest::addColumn<QJsonValue>(name: "value" ); |
3469 | QTest::newRow(dataTag: "double" ) << QJsonValue{665}; |
3470 | QTest::newRow(dataTag: "bool" ) << QJsonValue{true}; |
3471 | QTest::newRow(dataTag: "string" ) << QJsonValue{QStringLiteral("bum" )}; |
3472 | QTest::newRow(dataTag: "array" ) << QJsonValue{QJsonArray{12,1,5,6,7}}; |
3473 | QTest::newRow(dataTag: "object" ) << QJsonValue{QJsonObject{{"foo" , 665}, {"bar" , 666}}}; |
3474 | // test json escape sequence |
3475 | QTest::newRow(dataTag: "array with 0xD800" ) << QJsonValue(QJsonArray{QString(0xD800)}); |
3476 | QTest::newRow(dataTag: "array with 0xDF06,0xD834" ) << QJsonValue(QJsonArray{QString(0xDF06).append(c: 0xD834)}); |
3477 | } |
3478 | |
3479 | void tst_QtJson::streamSerializationQJsonValue() |
3480 | { |
3481 | QByteArray buffer; |
3482 | QFETCH(QJsonValue, value); |
3483 | QJsonValue output; |
3484 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3485 | save << value; |
3486 | QDataStream load(buffer); |
3487 | load >> output; |
3488 | QCOMPARE(output, value); |
3489 | } |
3490 | |
3491 | void tst_QtJson::streamSerializationQJsonValueEmpty() |
3492 | { |
3493 | QByteArray buffer; |
3494 | { |
3495 | QJsonValue undef{QJsonValue::Undefined}; |
3496 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3497 | save << undef; |
3498 | QDataStream load(buffer); |
3499 | QJsonValue output; |
3500 | load >> output; |
3501 | QVERIFY(output.isUndefined()); |
3502 | } |
3503 | { |
3504 | QJsonValue null{QJsonValue::Null}; |
3505 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3506 | save << null; |
3507 | QDataStream load(buffer); |
3508 | QJsonValue output; |
3509 | load >> output; |
3510 | QVERIFY(output.isNull()); |
3511 | } |
3512 | } |
3513 | |
3514 | void tst_QtJson::streamVariantSerialization() |
3515 | { |
3516 | // Check interface only, implementation is tested through to and from |
3517 | // json functions. |
3518 | QByteArray buffer; |
3519 | { |
3520 | QJsonDocument objectDoc(QJsonArray{665, 666, 667}); |
3521 | QVariant output; |
3522 | QVariant variant(objectDoc); |
3523 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3524 | save << variant; |
3525 | QDataStream load(buffer); |
3526 | load >> output; |
3527 | QCOMPARE(output.userType(), QMetaType::QJsonDocument); |
3528 | QCOMPARE(output.toJsonDocument(), objectDoc); |
3529 | } |
3530 | { |
3531 | QJsonArray array{665, 666, 667}; |
3532 | QVariant output; |
3533 | QVariant variant(array); |
3534 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3535 | save << variant; |
3536 | QDataStream load(buffer); |
3537 | load >> output; |
3538 | QCOMPARE(output.userType(), QMetaType::QJsonArray); |
3539 | QCOMPARE(output.toJsonArray(), array); |
3540 | } |
3541 | { |
3542 | QJsonObject obj{{"foo" , 42}}; |
3543 | QVariant output; |
3544 | QVariant variant(obj); |
3545 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3546 | save << variant; |
3547 | QDataStream load(buffer); |
3548 | load >> output; |
3549 | QCOMPARE(output.userType(), QMetaType::QJsonObject); |
3550 | QCOMPARE(output.toJsonObject(), obj); |
3551 | } |
3552 | { |
3553 | QJsonValue value{42}; |
3554 | QVariant output; |
3555 | QVariant variant(value); |
3556 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3557 | save << variant; |
3558 | QDataStream load(buffer); |
3559 | load >> output; |
3560 | QCOMPARE(output.userType(), QMetaType::QJsonValue); |
3561 | QCOMPARE(output.toJsonValue(), value); |
3562 | } |
3563 | } |
3564 | |
3565 | void tst_QtJson::escapeSurrogateCodePoints_data() |
3566 | { |
3567 | QTest::addColumn<QString>(name: "str" ); |
3568 | QTest::addColumn<QByteArray>(name: "escStr" ); |
3569 | QTest::newRow(dataTag: "0xD800" ) << QString(0xD800) << QByteArray("\\ud800" ); |
3570 | QTest::newRow(dataTag: "0xDF06,0xD834" ) << QString(0xDF06).append(c: 0xD834) << QByteArray("\\udf06\\ud834" ); |
3571 | } |
3572 | |
3573 | void tst_QtJson::escapeSurrogateCodePoints() |
3574 | { |
3575 | QFETCH(QString, str); |
3576 | QFETCH(QByteArray, escStr); |
3577 | QJsonArray array; |
3578 | array.append(value: str); |
3579 | QByteArray buffer; |
3580 | QDataStream save(&buffer, QIODevice::WriteOnly); |
3581 | save << array; |
3582 | // verify the buffer has escaped values |
3583 | QVERIFY(buffer.contains(escStr)); |
3584 | } |
3585 | |
3586 | void tst_QtJson::fromToVariantConversions_data() |
3587 | { |
3588 | QTest::addColumn<QVariant>(name: "variant" ); |
3589 | QTest::addColumn<QJsonValue>(name: "json" ); |
3590 | QTest::addColumn<QVariant>(name: "jsonToVariant" ); |
3591 | |
3592 | QByteArray utf8("\xC4\x90\xC4\x81\xC5\xA3\xC3\xA2" ); // Đāţâ |
3593 | QDateTime dt = QDateTime::currentDateTimeUtc(); |
3594 | QUuid uuid = QUuid::createUuid(); |
3595 | |
3596 | constexpr double maxDouble = std::numeric_limits<double>::max(); |
3597 | constexpr double minDouble = std::numeric_limits<double>::min(); |
3598 | |
3599 | QTest::newRow(dataTag: "default" ) << QVariant() << QJsonValue(QJsonValue::Null) |
3600 | << QVariant::fromValue(value: nullptr); |
3601 | QTest::newRow(dataTag: "nullptr" ) << QVariant::fromValue(value: nullptr) << QJsonValue(QJsonValue::Null) |
3602 | << QVariant::fromValue(value: nullptr); |
3603 | QTest::newRow(dataTag: "bool" ) << QVariant(true) << QJsonValue(true) << QVariant(true); |
3604 | QTest::newRow(dataTag: "int pos" ) << QVariant(123) << QJsonValue(123) << QVariant(qlonglong(123)); |
3605 | QTest::newRow(dataTag: "int neg" ) << QVariant(-123) << QJsonValue(-123) << QVariant(qlonglong(-123)); |
3606 | QTest::newRow(dataTag: "int big pos" ) << QVariant((1ll << 52) +1) << QJsonValue((1ll << 52) + 1) |
3607 | << QVariant(qlonglong((1ll << 52) + 1)); |
3608 | QTest::newRow(dataTag: "int big neg" ) << QVariant(-(1ll << 52) + 1) << QJsonValue(-(1ll << 52) + 1) |
3609 | << QVariant(qlonglong(-(1ll << 52) + 1)); |
3610 | QTest::newRow(dataTag: "double pos" ) << QVariant(123.) << QJsonValue(123.) << QVariant(qlonglong(123.)); |
3611 | QTest::newRow(dataTag: "double neg" ) << QVariant(-123.) << QJsonValue(-123.) |
3612 | << QVariant(qlonglong(-123.)); |
3613 | QTest::newRow(dataTag: "double big" ) << QVariant(maxDouble - 1000) << QJsonValue(maxDouble - 1000) |
3614 | << QVariant(maxDouble - 1000); |
3615 | QTest::newRow(dataTag: "double max" ) << QVariant(maxDouble) << QJsonValue(maxDouble) |
3616 | << QVariant(maxDouble); |
3617 | QTest::newRow(dataTag: "double min" ) << QVariant(minDouble) << QJsonValue(minDouble) |
3618 | << QVariant(minDouble); |
3619 | QTest::newRow(dataTag: "double big neg" ) << QVariant(1000 - maxDouble) << QJsonValue(1000 - maxDouble) |
3620 | << QVariant(1000 - maxDouble); |
3621 | QTest::newRow(dataTag: "double max neg" ) << QVariant(-maxDouble) << QJsonValue(-maxDouble) |
3622 | << QVariant(-maxDouble); |
3623 | QTest::newRow(dataTag: "double min neg" ) << QVariant(-minDouble) << QJsonValue(-minDouble) |
3624 | << QVariant(-minDouble); |
3625 | |
3626 | QTest::newRow(dataTag: "string null" ) << QVariant(QString()) << QJsonValue(QString()) |
3627 | << QVariant(QString()); |
3628 | QTest::newRow(dataTag: "string empty" ) << QVariant(QString("" )) << QJsonValue(QString("" )) |
3629 | << QVariant(QString("" )); |
3630 | QTest::newRow(dataTag: "string ascii" ) << QVariant(QString("Data" )) << QJsonValue(QString("Data" )) |
3631 | << QVariant(QString("Data" )); |
3632 | QTest::newRow(dataTag: "string utf8" ) << QVariant(QString(utf8)) << QJsonValue(QString(utf8)) |
3633 | << QVariant(QString(utf8)); |
3634 | |
3635 | QTest::newRow(dataTag: "bytearray null" ) << QVariant(QByteArray()) << QJsonValue(QJsonValue::Null) |
3636 | << QVariant::fromValue(value: nullptr); |
3637 | QTest::newRow(dataTag: "bytearray empty" ) << QVariant(QByteArray()) << QJsonValue(QJsonValue::Null) |
3638 | << QVariant::fromValue(value: nullptr); |
3639 | QTest::newRow(dataTag: "bytearray ascii" ) << QVariant(QByteArray("Data" )) << QJsonValue(QString("Data" )) |
3640 | << QVariant(QString("Data" )); |
3641 | QTest::newRow(dataTag: "bytearray utf8" ) << QVariant(utf8) << QJsonValue(QString(utf8)) |
3642 | << QVariant(QString(utf8)); |
3643 | |
3644 | QTest::newRow(dataTag: "datetime" ) << QVariant(dt) << QJsonValue(dt.toString(format: Qt::ISODateWithMs)) |
3645 | << QVariant(dt.toString(format: Qt::ISODateWithMs)); |
3646 | QTest::newRow(dataTag: "url" ) << QVariant(QUrl("http://example.com/{q}" )) |
3647 | << QJsonValue("http://example.com/%7Bq%7D" ) |
3648 | << QVariant(QString("http://example.com/%7Bq%7D" )); |
3649 | QTest::newRow(dataTag: "uuid" ) << QVariant(QUuid(uuid)) |
3650 | << QJsonValue(uuid.toString(mode: QUuid::WithoutBraces)) |
3651 | << QVariant(uuid.toString(mode: QUuid::WithoutBraces)); |
3652 | QTest::newRow(dataTag: "regexp" ) << QVariant(QRegularExpression("." )) << QJsonValue(QJsonValue::Null) |
3653 | << QVariant::fromValue(value: nullptr); |
3654 | |
3655 | QTest::newRow(dataTag: "inf" ) << QVariant(qInf()) << QJsonValue(QJsonValue::Null) |
3656 | << QVariant::fromValue(value: nullptr); |
3657 | QTest::newRow(dataTag: "-inf" ) << QVariant(-qInf()) << QJsonValue(QJsonValue::Null) |
3658 | << QVariant::fromValue(value: nullptr); |
3659 | QTest::newRow(dataTag: "NaN" ) << QVariant(qQNaN()) << QJsonValue(QJsonValue::Null) |
3660 | << QVariant::fromValue(value: nullptr); |
3661 | |
3662 | const qulonglong ulongValue = (1ul << 63) + 1; |
3663 | const double uLongToDouble = ulongValue; |
3664 | qint64 n; |
3665 | if (convertDoubleTo(v: uLongToDouble, value: &n)) { |
3666 | QTest::newRow(dataTag: "ulonglong" ) << QVariant(ulongValue) << QJsonValue(uLongToDouble) |
3667 | << QVariant(n); |
3668 | } else { |
3669 | QTest::newRow(dataTag: "ulonglong" ) << QVariant(ulongValue) << QJsonValue(uLongToDouble) |
3670 | << QVariant(uLongToDouble); |
3671 | } |
3672 | } |
3673 | |
3674 | void tst_QtJson::fromToVariantConversions() |
3675 | { |
3676 | QFETCH(QVariant, variant); |
3677 | QFETCH(QJsonValue, json); |
3678 | QFETCH(QVariant, jsonToVariant); |
3679 | |
3680 | QVariant variantFromJson(json); |
3681 | QVariant variantFromJsonArray(QJsonArray { json }); |
3682 | QVariant variantFromJsonObject(QVariantMap { { "foo" , variant } }); |
3683 | |
3684 | QJsonObject object { QPair<QString, QJsonValue>("foo" , json) }; |
3685 | |
3686 | // QJsonValue <> QVariant |
3687 | { |
3688 | QCOMPARE(QJsonValue::fromVariant(variant), json); |
3689 | |
3690 | // test the same for QVariant from QJsonValue/QJsonArray/QJsonObject |
3691 | QCOMPARE(QJsonValue::fromVariant(variantFromJson), json); |
3692 | QCOMPARE(QJsonValue::fromVariant(variantFromJsonArray), QJsonArray { json }); |
3693 | QCOMPARE(QJsonValue::fromVariant(variantFromJsonObject), object); |
3694 | |
3695 | // QJsonValue to variant |
3696 | QCOMPARE(json.toVariant(), jsonToVariant); |
3697 | QCOMPARE(json.toVariant().userType(), jsonToVariant.userType()); |
3698 | |
3699 | // variant to QJsonValue |
3700 | QCOMPARE(QVariant(json).toJsonValue(), json); |
3701 | } |
3702 | |
3703 | // QJsonArray <> QVariantList |
3704 | { |
3705 | QCOMPARE(QJsonArray::fromVariantList(QVariantList { variant }), QJsonArray { json }); |
3706 | |
3707 | // test the same for QVariantList from QJsonValue/QJsonArray/QJsonObject |
3708 | QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJson }), |
3709 | QJsonArray { json }); |
3710 | QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJsonArray }), |
3711 | QJsonArray {{ QJsonArray { json } }}); |
3712 | QCOMPARE(QJsonArray::fromVariantList(QVariantList { variantFromJsonObject }), |
3713 | QJsonArray { object }); |
3714 | |
3715 | // QJsonArray to variant |
3716 | QCOMPARE(QJsonArray { json }.toVariantList(), QVariantList { jsonToVariant }); |
3717 | // variant to QJsonArray |
3718 | QCOMPARE(QVariant(QJsonArray { json }).toJsonArray(), QJsonArray { json }); |
3719 | } |
3720 | |
3721 | // QJsonObject <> QVariantMap |
3722 | { |
3723 | QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "foo" , variant } }), object); |
3724 | |
3725 | // test the same for QVariantMap from QJsonValue/QJsonArray/QJsonObject |
3726 | QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "foo" , variantFromJson } }), object); |
3727 | |
3728 | QJsonObject nestedArray { QPair<QString, QJsonArray>("bar" , QJsonArray { json }) }; |
3729 | QJsonObject nestedObject { QPair<QString, QJsonObject>("bar" , object) }; |
3730 | QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "bar" , variantFromJsonArray } }), |
3731 | nestedArray); |
3732 | QCOMPARE(QJsonObject::fromVariantMap(QVariantMap { { "bar" , variantFromJsonObject } }), |
3733 | nestedObject); |
3734 | |
3735 | // QJsonObject to variant |
3736 | QCOMPARE(object.toVariantMap(), QVariantMap({ { "foo" , jsonToVariant } })); |
3737 | // variant to QJsonObject |
3738 | QCOMPARE(QVariant(object).toJsonObject(), object); |
3739 | } |
3740 | } |
3741 | |
3742 | void tst_QtJson::noLeakOnNameClash() |
3743 | { |
3744 | QJsonDocument doc = QJsonDocument::fromJson(json: "{\"\":{\"\":0},\"\":0}" ); |
3745 | QVERIFY(!doc.isNull()); |
3746 | const QJsonObject obj = doc.object(); |
3747 | |
3748 | // Removed the duplicate key. |
3749 | QCOMPARE(obj.length(), 1); |
3750 | |
3751 | // Retained the last of the duplicates. |
3752 | const QJsonValue val = obj.begin().value(); |
3753 | QVERIFY(val.isDouble()); |
3754 | QCOMPARE(val.toDouble(), 0.0); |
3755 | |
3756 | // It should not leak. |
3757 | // In particular it should not forget to deref the container for the inner object. |
3758 | } |
3759 | |
3760 | QTEST_MAIN(tst_QtJson) |
3761 | #include "tst_qtjson.moc" |
3762 | |