1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "util_allocator.h"
30 #include "cx/test.h"
31
32 #include "cx/json.h"
33 #include "cx/compare.h"
34
35 CX_TEST(test_json_init_default) {
36 CxJson json;
37 CX_TEST_DO {
38 cxJsonInit(&json,
NULL);
39 CX_TEST_ASSERT(json.states == json.states_internal);
40 CX_TEST_ASSERT(json.states_size ==
1);
41 CX_TEST_ASSERT(json.states_capacity >=
8);
42 CX_TEST_ASSERT(json.vbuf == json.vbuf_internal);
43 CX_TEST_ASSERT(json.vbuf_size ==
0);
44 CX_TEST_ASSERT(json.vbuf_capacity >=
8);
45 cxJsonDestroy(&json);
46 }
47 }
48
49 CX_TEST(test_json_simple_object) {
50 cxstring text = cx_str(
51 "{\n"
52 "\t\"message\":\"success\",\n"
53 "\t\"position\":{\n"
54 "\t\t\"longitude\":-94.7099,\n"
55 "\t\t\"latitude\":51.5539\n"
56 "\t},\n"
57 "\t\"timestamp\":1729348561,\n"
58 "\t\"alive\":true\n"
59 "}"
60 );
61
62 CX_TEST_DO {
63 CxJsonStatus result;
64
65 CxJson json;
66 cxJsonInit(&json,
NULL);
67 cxJsonFill(&json, text);
68
69
70 CxJsonValue *obj;
71 result = cxJsonNext(&json, &obj);
72 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
73
74
75 CX_TEST_ASSERT(cxJsonIsObject(obj));
76
77 CxJsonValue *message = cxJsonObjGet(obj,
"message");
78 CX_TEST_ASSERT(cxJsonIsString(message));
79 CX_TEST_ASSERT(
0 == cx_strcmp(
80 cxJsonAsCxString(message),
81 "success")
82 );
83
84 CxJsonValue *position = cxJsonObjGet(obj,
"position");
85 CX_TEST_ASSERT(cxJsonIsObject(position));
86 CxJsonValue *longitude = cxJsonObjGet(position,
"longitude");
87 CX_TEST_ASSERT(cxJsonIsNumber(longitude));
88 CX_TEST_ASSERT(!cxJsonIsInteger(longitude));
89 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(longitude),
-94.7099));
90 CX_TEST_ASSERT(cxJsonAsInteger(longitude) ==
-94);
91 CxJsonValue *latitude = cxJsonObjGet(position,
"latitude");
92 CX_TEST_ASSERT(cxJsonIsNumber(latitude));
93 CX_TEST_ASSERT(!cxJsonIsInteger(latitude));
94 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(latitude),
51.5539));
95 CX_TEST_ASSERT(cxJsonAsInteger(latitude) ==
51);
96
97 CxJsonValue *timestamp = cxJsonObjGet(obj,
"timestamp");
98 CX_TEST_ASSERT(cxJsonIsInteger(timestamp));
99 CX_TEST_ASSERT(cxJsonIsNumber(timestamp));
100 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) ==
1729348561);
101 CX_TEST_ASSERT(cxJsonAsDouble(timestamp) ==
1729348561.0);
102
103 CxJsonValue *alive = cxJsonObjGet(obj,
"alive");
104 CX_TEST_ASSERT(cxJsonIsBool(alive));
105 CX_TEST_ASSERT(cxJsonIsTrue(alive));
106 CX_TEST_ASSERT(!cxJsonIsFalse(alive));
107 CX_TEST_ASSERT(cxJsonAsBool(alive));
108
109
110 cxJsonValueFree(obj);
111
112
113 result = cxJsonNext(&json, &obj);
114 CX_TEST_ASSERT(result ==
CX_JSON_NO_DATA);
115
116 cxJsonDestroy(&json);
117 }
118 }
119
120 CX_TEST(test_json_large_object) {
121 CxJsonValue *obj = cxJsonCreateObj(
NULL);
122 CX_TEST_DO {
123 cxJsonObjPutString(obj,
"mystring",
"test");
124 char buf[
10];
125 for (
unsigned i =
0 ; i <
300 ; i++) {
126 sprintf(buf,
"key %d", i);
127 cxJsonObjPutInteger(obj, buf, i);
128 }
129 CX_TEST_ASSERT(
301 == cxJsonObjSize(obj));
130
131 CxJsonValue *v;
132 v = cxJsonObjGet(obj,
"key 64");
133 CX_TEST_ASSERT(cxJsonIsInteger(v));
134 CX_TEST_ASSERT(cxJsonAsInteger(v) ==
64);
135 v = cxJsonObjGet(obj,
"key 228");
136 CX_TEST_ASSERT(cxJsonIsInteger(v));
137 CX_TEST_ASSERT(cxJsonAsInteger(v) ==
228);
138
139 v = cxJsonObjGet(obj,
"mystring");
140 CX_TEST_ASSERT(cxJsonIsString(v));
141 CX_TEST_ASSERT(
0 == cx_strcmp(cxJsonAsCxMutStr(v),
"test"));
142 }
143 cxJsonValueFree(obj);
144 }
145
146 CX_TEST(test_json_from_string) {
147 cxstring text = cx_str(
148 "{\n"
149 "\t\"message\":\"success\",\n"
150 "\t\"position\":{\n"
151 "\t\t\"longitude\":-94.7099,\n"
152 "\t\t\"latitude\":51.5539\n"
153 "\t},\n"
154 "\t\"timestamp\":1729348561,\n"
155 "\t\"alive\":true\n"
156 "}"
157 );
158
159 CX_TEST_DO {
160 CxJsonValue *obj;
161 CX_TEST_ASSERT(cxJsonFromString(
NULL, text, &obj) ==
CX_JSON_NO_ERROR);
162
163
164 CX_TEST_ASSERT(cxJsonIsObject(obj));
165
166 CxJsonValue *message = cxJsonObjGet(obj,
"message");
167 CX_TEST_ASSERT(cxJsonIsString(message));
168 CX_TEST_ASSERT(
0 == cx_strcmp(
169 cxJsonAsCxString(message),
170 "success")
171 );
172
173 CxJsonValue *position = cxJsonObjGet(obj,
"position");
174 CX_TEST_ASSERT(cxJsonIsObject(position));
175 CxJsonValue *longitude = cxJsonObjGet(position,
"longitude");
176 CX_TEST_ASSERT(cxJsonIsNumber(longitude));
177 CX_TEST_ASSERT(!cxJsonIsInteger(longitude));
178 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(longitude),
-94.7099));
179 CX_TEST_ASSERT(cxJsonAsInteger(longitude) ==
-94);
180 CxJsonValue *latitude = cxJsonObjGet(position,
"latitude");
181 CX_TEST_ASSERT(cxJsonIsNumber(latitude));
182 CX_TEST_ASSERT(!cxJsonIsInteger(latitude));
183 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(latitude),
51.5539));
184 CX_TEST_ASSERT(cxJsonAsInteger(latitude) ==
51);
185
186 CxJsonValue *timestamp = cxJsonObjGet(obj,
"timestamp");
187 CX_TEST_ASSERT(cxJsonIsInteger(timestamp));
188 CX_TEST_ASSERT(cxJsonIsNumber(timestamp));
189 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) ==
1729348561);
190 CX_TEST_ASSERT(cxJsonAsDouble(timestamp) ==
1729348561.0);
191
192 CxJsonValue *alive = cxJsonObjGet(obj,
"alive");
193 CX_TEST_ASSERT(cxJsonIsBool(alive));
194 CX_TEST_ASSERT(cxJsonIsTrue(alive));
195 CX_TEST_ASSERT(!cxJsonIsFalse(alive));
196 CX_TEST_ASSERT(cxJsonAsBool(alive));
197
198 cxJsonValueFree(obj);
199 }
200 }
201
202 CX_TEST(test_json_escaped_strings) {
203 cxstring text = cx_str(
204 "{\n"
205 "\t\"object\":\"{\\n\\t\\\"object\\\":null\\n}\",\n"
206 "\t\"ctrl-chars\":\"\\\\foo\\r\\nbar\\f*ring\\/ring*\\b\"\n"
207 "}"
208 );
209
210 CxJson json;
211 cxJsonInit(&json,
NULL);
212 CX_TEST_DO {
213 cxJsonFill(&json, text);
214 CxJsonValue *obj;
215 CxJsonStatus result = cxJsonNext(&json, &obj);
216 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
217 CX_TEST_ASSERT(cxJsonIsObject(obj));
218 CxJsonValue *object = cxJsonObjGet(obj,
"object");
219 CX_TEST_ASSERT(cxJsonIsString(object));
220 CX_TEST_ASSERT(
0 == cx_strcmp(
221 cxJsonAsCxString(object),
222 "{\n\t\"object\":null\n}")
223 );
224 CxJsonValue *ctrl = cxJsonObjGet(obj,
"ctrl-chars");
225 CX_TEST_ASSERT(cxJsonIsString(ctrl));
226 CX_TEST_ASSERT(
0 == cx_strcmp(
227 cxJsonAsCxString(ctrl),
228 "\\foo\r\nbar\f*ring/ring*\b")
229 );
230 cxJsonValueFree(obj);
231 }
232 cxJsonDestroy(&json);
233 }
234
235 CX_TEST(test_json_escaped_unicode_strings) {
236 cxstring text = cx_str(
237 "{\n"
238 "\"ascii\":\"\\u0041\\u0053\\u0043\\u0049\\u0049\",\n"
239 "\"unicode\":\"\\u00df\\u00DF\",\n"
240 "\"mixed\":\"mixed ä ö \\u00e4 \\u00f6\",\n"
241 "\"wide\":\"\\u03a3\\u29b0\",\n"
242 "\"surrogatepair1\":\"\\ud83e\\udff5\",\n"
243 "\"surrogatepair2\":\"test\\ud83e\\udff1AA\"\n,"
244 "\"mixed2\":\"123\\u03a3\\ud83e\\udfc5\\u00df\""
245 "}"
246 );
247
248 CxJson json;
249 cxJsonInit(&json,
NULL);
250 CX_TEST_DO {
251 cxJsonFill(&json, text);
252 CxJsonValue *obj;
253 CxJsonStatus result = cxJsonNext(&json, &obj);
254 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
255 CX_TEST_ASSERT(cxJsonIsObject(obj));
256
257 CxJsonValue *ascii = cxJsonObjGet(obj,
"ascii");
258 CX_TEST_ASSERT(cxJsonIsString(ascii));
259 CX_TEST_ASSERT(
0 == cx_strcmp(
260 cxJsonAsCxString(ascii),
261 "ASCII")
262 );
263
264 CxJsonValue *unicode = cxJsonObjGet(obj,
"unicode");
265 CX_TEST_ASSERT(cxJsonIsString(unicode));
266 CX_TEST_ASSERT(
0 == cx_strcmp(
267 cxJsonAsCxString(unicode),
268 "ßß")
269 );
270
271 CxJsonValue *mixed = cxJsonObjGet(obj,
"mixed");
272 CX_TEST_ASSERT(cxJsonIsString(mixed));
273 CX_TEST_ASSERT(
0 == cx_strcmp(
274 cxJsonAsCxString(mixed),
275 "mixed ä ö ä ö")
276 );
277
278 CxJsonValue *wide = cxJsonObjGet(obj,
"wide");
279 CX_TEST_ASSERT(cxJsonIsString(wide));
280 CX_TEST_ASSERT(
0 == cx_strcmp(cxJsonAsCxString(wide),
"Σ⦰"));
281
282 CxJsonValue *surrogatepair1 = cxJsonObjGet(obj,
"surrogatepair1");
283 CX_TEST_ASSERT(cxJsonIsString(surrogatepair1));
284 CX_TEST_ASSERT(
0 == cx_strcmp(
285 cxJsonAsCxString(surrogatepair1),
286 "\xf0\x9f\xaf\xb5")
287 );
288
289 CxJsonValue *surrogatepair2 = cxJsonObjGet(obj,
"surrogatepair2");
290 CX_TEST_ASSERT(cxJsonIsString(surrogatepair2));
291 CX_TEST_ASSERT(
0 == cx_strcmp(
292 cxJsonAsCxString(surrogatepair2),
293 "test\xf0\x9f\xaf\xb1" "AA")
294 );
295
296 CxJsonValue *mixed2 = cxJsonObjGet(obj,
"mixed2");
297 char test[
16];
298 strncpy(test, mixed2->string.ptr,
15);
299 CX_TEST_ASSERT(cxJsonIsString(mixed2));
300 CX_TEST_ASSERT(
0 == cx_strcmp(
301 cxJsonAsCxString(mixed2),
302 "123\xce\xa3\xf0\x9f\xaf\x85ß")
303 );
304
305 cxJsonValueFree(obj);
306 }
307 cxJsonDestroy(&json);
308 }
309
310 CX_TEST(test_json_escaped_unicode_malformed) {
311 CxJson json;
312 cxJsonInit(&json,
NULL);
313 CxJsonValue *obj;
314 CxJsonStatus result;
315 CX_TEST_DO {
316 cxJsonFill(&json,
"\"too few digits \\u123\"");
317 result = cxJsonNext(&json, &obj);
318 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
319 CX_TEST_ASSERT(cxJsonIsString(obj));
320 CX_TEST_ASSERT(
0 == cx_strcmp(
321 cxJsonAsCxString(obj),
322 "too few digits \\u123"
323 ));
324 cxJsonValueFree(obj);
325 cxJsonFill(&json,
"\"too many digits \\u00E456\"");
326 result = cxJsonNext(&json, &obj);
327 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
328 CX_TEST_ASSERT(cxJsonIsString(obj));
329 CX_TEST_ASSERT(
0 == cx_strcmp(
330 cxJsonAsCxString(obj),
331 "too many digits ä56"
332 ));
333 cxJsonValueFree(obj);
334 cxJsonFill(&json,
"\"only high \\uD800 surrogate\"");
335 result = cxJsonNext(&json, &obj);
336 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
337 CX_TEST_ASSERT(cxJsonIsString(obj));
338 CX_TEST_ASSERT(
0 == cx_strcmp(
339 cxJsonAsCxString(obj),
340 "only high \\uD800 surrogate"
341 ));
342 cxJsonValueFree(obj);
343 cxJsonFill(&json,
"\"only low \\uDC00 surrogate\"");
344 result = cxJsonNext(&json, &obj);
345 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
346 CX_TEST_ASSERT(cxJsonIsString(obj));
347 CX_TEST_ASSERT(
0 == cx_strcmp(
348 cxJsonAsCxString(obj),
349 "only low \\uDC00 surrogate"
350 ));
351 cxJsonValueFree(obj);
352 cxJsonFill(&json,
"\"two high \\uD800\\uD800 surrogates\"");
353 result = cxJsonNext(&json, &obj);
354 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
355 CX_TEST_ASSERT(cxJsonIsString(obj));
356 CX_TEST_ASSERT(
0 == cx_strcmp(
357 cxJsonAsCxString(obj),
358 "two high \\uD800\\uD800 surrogates"
359 ));
360 cxJsonValueFree(obj);
361 cxJsonFill(&json,
"\"high plus bullshit \\uD800\\u567 foo\"");
362 result = cxJsonNext(&json, &obj);
363 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
364 CX_TEST_ASSERT(cxJsonIsString(obj));
365 CX_TEST_ASSERT(
0 == cx_strcmp(
366 cxJsonAsCxString(obj),
367 "high plus bullshit \\uD800\\u567 foo"
368 ));
369 cxJsonValueFree(obj);
370 }
371 cxJsonDestroy(&json);
372 }
373
374 CX_TEST(test_json_escaped_end_of_string) {
375 CxJson json;
376 cxJsonInit(&json,
NULL);
377 CX_TEST_DO {
378
379 cxJsonFill(&json,
"\"a \\\"test\\\" string\"");
380 CxJsonValue *val;
381 CxJsonStatus result = cxJsonNext(&json, &val);
382 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
383 CX_TEST_ASSERT(cxJsonIsString(val));
384 CX_TEST_ASSERT(
0 == cx_strcmp(
385 cxJsonAsCxString(val),
386 "a \"test\" string")
387 );
388 cxJsonValueFree(val);
389
390
391 cxJsonFill(&json,
"\"a \\\"test\\");
392 result = cxJsonNext(&json, &val);
393 CX_TEST_ASSERT(result ==
CX_JSON_INCOMPLETE_DATA);
394 cxJsonFill(&json,
"\" string\"");
395 result = cxJsonNext(&json, &val);
396 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
397 CX_TEST_ASSERT(cxJsonIsString(val));
398 CX_TEST_ASSERT(
0 == cx_strcmp(
399 cxJsonAsCxString(val),
400 "a \"test\" string")
401 );
402 cxJsonValueFree(val);
403 }
404 cxJsonDestroy(&json);
405 }
406
407 CX_TEST(test_json_object_incomplete_token) {
408 cxstring text = cx_str(
409 "{\"message\":\"success\" , \"__timestamp\":1729348561}");
410 cxstring parts[
16];
411 size_t nparts =
0;
412 for(
size_t i=
0;i<text.length;i+=
4) {
413 parts[nparts++] = cx_strsubsl(text, i,
4);
414 }
415
416 CX_TEST_DO {
417 CxJsonStatus result;
418
419 CxJson json;
420 cxJsonInit(&json,
NULL);
421 CxJsonValue *obj;
422
423 size_t part =
0;
424 while(part < nparts -
1) {
425 cxJsonFill(&json, parts[part]);
426 part++;
427 result = cxJsonNext(&json, &obj);
428 CX_TEST_ASSERT(result ==
CX_JSON_INCOMPLETE_DATA);
429 }
430 cxJsonFill(&json, parts[nparts -
1]);
431 result = cxJsonNext(&json, &obj);
432 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
433 CX_TEST_ASSERT(cxJsonIsObject(obj));
434
435 CxJsonValue *message = cxJsonObjGet(obj,
"message");
436 CX_TEST_ASSERT(cxJsonIsString(message));
437 CX_TEST_ASSERT(
0 == cx_strcmp(
438 cxJsonAsCxString(message),
439 "success")
440 );
441 CxJsonValue *timestamp = cxJsonObjGet(obj,
"__timestamp");
442 CX_TEST_ASSERT(message->type ==
CX_JSON_STRING);
443 CX_TEST_ASSERT(cxJsonIsInteger(timestamp));
444 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) ==
1729348561);
445
446
447 cxJsonValueFree(obj);
448
449
450 result = cxJsonNext(&json, &obj);
451 CX_TEST_ASSERT(result ==
CX_JSON_NO_DATA);
452
453 cxJsonDestroy(&json);
454 }
455 }
456
457 CX_TEST(test_json_token_wrongly_completed) {
458 CxTestingAllocator talloc;
459 cx_testing_allocator_init(&talloc);
460 const CxAllocator *alloc = &talloc.base;
461
462 cxstring text = cx_str(
"{\"number\": 47110815!}");
463 cxstring part1 = cx_strsubsl(text,
0,
16);
464 cxstring part2 = cx_strsubs(text,
16);
465
466 CX_TEST_DO {
467 CxJson json;
468 cxJsonInit(&json, alloc);
469
470 CxJsonStatus result;
471 CxJsonValue *obj;
472
473 cxJsonFill(&json, part1);
474 result = cxJsonNext(&json, &obj);
475 CX_TEST_ASSERT(result ==
CX_JSON_INCOMPLETE_DATA);
476 cxJsonFill(&json, part2);
477 result = cxJsonNext(&json, &obj);
478 CX_TEST_ASSERT(result ==
CX_JSON_FORMAT_ERROR_NUMBER);
479 CX_TEST_ASSERT(obj->type ==
CX_JSON_NOTHING);
480
481 cxJsonDestroy(&json);
482 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
483 }
484 cx_testing_allocator_destroy(&talloc);
485 }
486
487 CX_TEST(test_json_parenthesis_mismatch) {
488 CxTestingAllocator talloc;
489 cx_testing_allocator_init(&talloc);
490 const CxAllocator *alloc = &talloc.base;
491
492 CX_TEST_DO {
493 CxJson json;
494 cxJsonInit(&json, alloc);
495
496 CxJsonStatus result;
497 CxJsonValue *obj;
498
499 cxJsonFill(&json,
"[0, 1, 2, 3}");
500 result = cxJsonNext(&json, &obj);
501 CX_TEST_ASSERT(result ==
CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
502
503 cxJsonReset(&json);
504 cxJsonFill(&json,
"{\"test\": 42]");
505 result = cxJsonNext(&json, &obj);
506 CX_TEST_ASSERT(result ==
CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
507
508 cxJsonDestroy(&json);
509 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
510 }
511 cx_testing_allocator_destroy(&talloc);
512 }
513
514 CX_TEST(test_json_object_name_is_no_string) {
515 CxTestingAllocator talloc;
516 cx_testing_allocator_init(&talloc);
517 const CxAllocator *alloc = &talloc.base;
518
519 CX_TEST_DO {
520 CxJson json;
521 cxJsonInit(&json, alloc);
522
523 CxJsonStatus result;
524 CxJsonValue *obj;
525
526 cxJsonFill(&json,
"{42: \"test\"}");
527 result = cxJsonNext(&json, &obj);
528 CX_TEST_ASSERT(result ==
CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
529
530 cxJsonDestroy(&json);
531 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
532 }
533 cx_testing_allocator_destroy(&talloc);
534 }
535
536 CX_TEST(test_json_subsequent_fill) {
537 cxstring text = cx_str(
538 "{\"message\":\"success\" , \"__timestamp\":1729348561}");
539
540 cxstring part1 = cx_strsubsl(text,
0,
25);
541 cxstring part2 = cx_strsubs(text,
25);
542
543 CX_TEST_DO {
544 CxJson json;
545 cxJsonInit(&json,
NULL);
546 CxJsonValue *obj;
547
548 cxJsonFill(&json, part1);
549 cxJsonFill(&json, part2);
550 CxJsonStatus result = cxJsonNext(&json, &obj);
551 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
552 CX_TEST_ASSERT(cxJsonIsObject(obj));
553
554 CxJsonValue *message = cxJsonObjGet(obj,
"message");
555 CX_TEST_ASSERT(cxJsonIsString(message));
556 CX_TEST_ASSERT(
0 == cx_strcmp(
557 cxJsonAsCxString(message),
558 "success")
559 );
560 CxJsonValue *timestamp = cxJsonObjGet(obj,
"__timestamp");
561 CX_TEST_ASSERT(message->type ==
CX_JSON_STRING);
562 CX_TEST_ASSERT(cxJsonIsInteger(timestamp));
563 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) ==
1729348561);
564
565 cxJsonValueFree(obj);
566 result = cxJsonNext(&json, &obj);
567 CX_TEST_ASSERT(result ==
CX_JSON_NO_DATA);
568
569 cxJsonDestroy(&json);
570 }
571 }
572
573 CX_TEST(test_json_no_fill) {
574 CxJson json;
575 cxJsonInit(&json,
NULL);
576 CX_TEST_DO {
577 CxJsonValue *obj =
NULL;
578 CxJsonStatus result = cxJsonNext(&json, &obj);
579 CX_TEST_ASSERT(result ==
CX_JSON_NULL_DATA);
580 CX_TEST_ASSERT(obj !=
NULL);
581 CX_TEST_ASSERT(obj->type ==
CX_JSON_NOTHING);
582 }
583 cxJsonDestroy(&json);
584 }
585
586 CX_TEST(test_json_null_fill) {
587 CxJson json;
588 cxJsonInit(&json,
NULL);
589 CX_TEST_DO {
590 CxJsonStatus result;
591 CxJsonValue *obj =
NULL;
592 cxstring nullstr = cx_strn(
NULL,
0);
593 cxJsonFill(&json, nullstr);
594 result = cxJsonNext(&json, &obj);
595 CX_TEST_ASSERT(result ==
CX_JSON_NO_DATA);
596 CX_TEST_ASSERT(obj !=
NULL);
597 CX_TEST_ASSERT(obj->type ==
CX_JSON_NOTHING);
598 obj =
NULL;
599
600 cxJsonFill(&json,
"[0, 1");
601 result = cxJsonNext(&json, &obj);
602 CX_TEST_ASSERT(result ==
CX_JSON_INCOMPLETE_DATA);
603 CX_TEST_ASSERT(obj !=
NULL);
604 CX_TEST_ASSERT(obj->type ==
CX_JSON_NOTHING);
605 obj =
NULL;
606
607 cxJsonFill(&json, nullstr);
608 result = cxJsonNext(&json, &obj);
609 CX_TEST_ASSERT(result ==
CX_JSON_INCOMPLETE_DATA);
610 CX_TEST_ASSERT(obj !=
NULL);
611 CX_TEST_ASSERT(obj->type ==
CX_JSON_NOTHING);
612 obj =
NULL;
613
614 cxJsonFill(&json,
", 2]");
615 result = cxJsonNext(&json, &obj);
616 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
617 CX_TEST_ASSERT(obj !=
NULL);
618 CX_TEST_ASSERT(cxJsonIsArray(obj));
619 cxJsonValueFree(obj);
620
621 cxJsonFill(&json, nullstr);
622 result = cxJsonNext(&json, &obj);
623 CX_TEST_ASSERT(result ==
CX_JSON_NO_DATA);
624 CX_TEST_ASSERT(obj !=
NULL);
625 CX_TEST_ASSERT(obj->type ==
CX_JSON_NOTHING);
626 }
627 cxJsonDestroy(&json);
628 }
629
630 CX_TEST(test_json_object_error) {
631 cxstring text0 = cx_str(
632 "{\n"
633 "\t\"message\":\"success\",\n"
634 "\t\"data\":{\n"
635 "\t\t\"obj\":{\n"
636 "\t\t\t\"array\": [1, 2, 3, ?syntaxerror? ]\n"
637 "\t\t}\n"
638 "\t},\n"
639 "\t\"timestamp\":1729348561,\n"
640 "}"
641 );
642 cxstring text1 = cx_str(
"{ \"string\" }");
643 cxstring text2 = cx_str(
"{ \"a\" : }");
644 cxstring text3 = cx_str(
"{ \"a\" : \"b\" ]");
645 cxstring text4 = cx_str(
"{ \"name\": \"value\" ]");
646
647 cxstring tests[] = { text0, text1, text2, text3, text4 };
648 CxJsonStatus errors[] = {
649 CX_JSON_FORMAT_ERROR_NUMBER,
650 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN,
651 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN,
652 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN,
653 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN
654 };
655
656 CX_TEST_DO {
657 CxJsonStatus result;
658 CxJson json;
659 CxJsonValue *obj =
NULL;
660
661 for(
int i=
0;i<
5;i++) {
662 cxJsonInit(&json,
NULL);
663 cxJsonFill(&json, tests[i]);
664 result = cxJsonNext(&json, &obj);
665
666 CX_TEST_ASSERT(result == errors[i]);
667 CX_TEST_ASSERT(obj !=
NULL && obj->type ==
CX_JSON_NOTHING);
668 cxJsonDestroy(&json);
669 }
670 }
671 }
672
673 CX_TEST(test_json_object_remove_member) {
674 CxTestingAllocator talloc;
675 cx_testing_allocator_init(&talloc);
676 const CxAllocator *alloc = &talloc.base;
677 CX_TEST_DO {
678 CxJson json;
679 cxJsonInit(&json, alloc);
680 cxJsonFill(&json,
681 "{\n"
682 "\t\"message\":\"success\",\n"
683 "\t\"data\":{\n"
684 "\t\t\"obj\":{\n"
685 "\t\t\t\"array\": [1, 2, 3]\n"
686 "\t\t}\n"
687 "\t},\n"
688 "\t\"timestamp\":1729348561\n"
689 "}"
690 );
691 CxJsonValue *obj;
692 CX_TEST_ASSERT(
CX_JSON_NO_ERROR == cxJsonNext(&json, &obj));
693 cxJsonDestroy(&json);
694
695 CX_TEST_ASSERT(cxJsonIsObject(cxJsonObjGet(obj,
"data")));
696 CxJsonValue *data = cxJsonObjRemove(obj,
"data");
697 CX_TEST_ASSERT(cxJsonIsObject(data));
698 CX_TEST_ASSERT(!cxJsonIsObject(cxJsonObjGet(obj,
"data")));
699 CX_TEST_ASSERT(cxJsonIsObject(cxJsonObjGet(data,
"obj")));
700
701 CX_TEST_ASSERT(
NULL == cxJsonObjRemove(obj,
"data"));
702
703
704 cxJsonValueFree(obj);
705 CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
706
707 cxJsonValueFree(data);
708 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
709 }
710 cx_testing_allocator_destroy(&talloc);
711 }
712
713 CX_TEST(test_json_large_nesting_depth) {
714 CxJson json;
715 CxJsonValue *d1;
716 cxstring text = cx_str(
"{\"test\": [{},{\"foo\": [[{\"bar\":[4, 2, [null, {\"key\": 47}]]}]]}]}");
717 CX_TEST_DO {
718 cxJsonInit(&json,
NULL);
719 cxJsonFill(&json, text);
720 cxJsonNext(&json, &d1);
721
722 CX_TEST_ASSERT(d1 !=
NULL);
723 CX_TEST_ASSERT(cxJsonIsObject(d1));
724 CxJsonValue *d2 = cxJsonObjGet(d1,
"test");
725 CX_TEST_ASSERT(cxJsonIsArray(d2));
726 CX_TEST_ASSERT(cxJsonArrSize(d2) ==
2);
727 CxJsonValue *d3 = cxJsonArrGet(d2,
1);
728 CX_TEST_ASSERT(cxJsonIsObject(d3));
729 CxJsonValue *d4 = cxJsonObjGet(d3,
"foo");
730 CX_TEST_ASSERT(cxJsonIsArray(d4));
731 CX_TEST_ASSERT(cxJsonArrSize(d4) ==
1);
732 CxJsonValue *d5 = cxJsonArrGet(d4,
0);
733 CX_TEST_ASSERT(cxJsonIsArray(d5));
734 CX_TEST_ASSERT(cxJsonArrSize(d5) ==
1);
735 CxJsonValue *d6 = cxJsonArrGet(d5,
0);
736 CX_TEST_ASSERT(cxJsonIsObject(d6));
737 CxJsonValue *d7 = cxJsonObjGet(d6,
"bar");
738 CX_TEST_ASSERT(cxJsonIsArray(d7));
739 CX_TEST_ASSERT(cxJsonArrSize(d7) ==
3);
740 CxJsonValue *d8 = cxJsonArrGet(d7,
2);
741 CX_TEST_ASSERT(cxJsonIsArray(d8));
742 CX_TEST_ASSERT(cxJsonArrSize(d8) ==
2);
743 CxJsonValue *d9a = cxJsonArrGet(d8,
0);
744 CX_TEST_ASSERT(cxJsonIsNull(d9a));
745 CxJsonValue *d9b = cxJsonArrGet(d8,
1);
746 CX_TEST_ASSERT(cxJsonIsObject(d9b));
747 CxJsonValue *d10 = cxJsonObjGet(d9b,
"key");
748 CX_TEST_ASSERT(cxJsonIsInteger(d10));
749 CX_TEST_ASSERT(cxJsonAsInteger(d10) ==
47);
750
751 CX_TEST_ASSERT(json.states != json.states_internal);
752 CX_TEST_ASSERT(json.states_capacity > cx_nmemb(json.states_internal));
753
754 cxJsonValueFree(d1);
755 cxJsonDestroy(&json);
756 }
757 }
758
759 CX_TEST(test_json_number) {
760 CxJson json;
761 cxJsonInit(&json,
NULL);
762 CX_TEST_DO {
763 CxJsonValue *v;
764 CxJsonStatus result;
765
766 cxJsonFill(&json,
"3.1415 ");
767 result = cxJsonNext(&json, &v);
768 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
769 CX_TEST_ASSERT(cxJsonIsNumber(v));
770 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(v),
3.1415));
771 cxJsonValueFree(v);
772
773 cxJsonFill(&json,
"-47.11e2 ");
774 result = cxJsonNext(&json, &v);
775 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
776 CX_TEST_ASSERT(cxJsonIsNumber(v));
777 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(v),
-4711.0));
778 cxJsonValueFree(v);
779
780 cxJsonFill(&json,
"0.815e-3 ");
781 result = cxJsonNext(&json, &v);
782 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
783 CX_TEST_ASSERT(cxJsonIsNumber(v));
784 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(v),
0.000815));
785 cxJsonValueFree(v);
786
787 cxJsonFill(&json,
"1.23E4 ");
788 result = cxJsonNext(&json, &v);
789 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
790 CX_TEST_ASSERT(cxJsonIsNumber(v));
791 CX_TEST_ASSERT(cxJsonAsInteger(v) ==
12300);
792 CX_TEST_ASSERT(cxJsonAsDouble(v) ==
12300.0);
793 cxJsonValueFree(v);
794
795 cxJsonFill(&json,
"18446744073709551615.0123456789 ");
796 result = cxJsonNext(&json, &v);
797 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
798 CX_TEST_ASSERT(cxJsonIsNumber(v));
799
800
801 CX_TEST_ASSERT(
0 == cx_vcmp_double(cxJsonAsDouble(v),
1.8446744073709552e+19));
802 cxJsonValueFree(v);
803 }
804 cxJsonDestroy(&json);
805 }
806
807 CX_TEST(test_json_number_format_errors) {
808 CxJson json;
809 cxJsonInit(&json,
NULL);
810 CX_TEST_DO {
811 CxJsonValue *v;
812 CxJsonStatus result;
813
814 cxJsonFill(&json,
"+3.1415 ");
815 result = cxJsonNext(&json, &v);
816 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
817 "leading plus is not RFC-8259 compliant");
818 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
819 cxJsonReset(&json);
820
821 cxJsonFill(&json,
"0.815e-3.0 ");
822 result = cxJsonNext(&json, &v);
823 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
824 "exponent must be an integer");
825 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
826 cxJsonReset(&json);
827
828 cxJsonFill(&json,
"3.14e ");
829 result = cxJsonNext(&json, &v);
830 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
831 "exponent cannot be empty");
832 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
833 cxJsonReset(&json);
834
835 cxJsonFill(&json,
"3.14e~7 ");
836 result = cxJsonNext(&json, &v);
837 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
838 "exponent cannot start with bullshit");
839 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
840 cxJsonReset(&json);
841
842 cxJsonFill(&json,
"1.23e4f ");
843 result = cxJsonNext(&json, &v);
844 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
845 "non-digits in exponent");
846 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
847 cxJsonReset(&json);
848
849 cxJsonFill(&json,
"1.23f ");
850 result = cxJsonNext(&json, &v);
851 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
852 "non-digits in value");
853 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
854 cxJsonReset(&json);
855
856 cxJsonFill(&json,
"1.23.45 ");
857 result = cxJsonNext(&json, &v);
858 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
859 "multiple decimal separators");
860 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
861 cxJsonReset(&json);
862
863 cxJsonFill(&json,
"184467440737095516150123456789 ");
864 result = cxJsonNext(&json, &v);
865 CX_TEST_ASSERTM(result ==
CX_JSON_FORMAT_ERROR_NUMBER,
866 "30 digit int does not fit into 64-bit int");
867 CX_TEST_ASSERT(v->type ==
CX_JSON_NOTHING);
868 cxJsonReset(&json);
869 }
870 cxJsonDestroy(&json);
871 }
872
873 CX_TEST(test_json_multiple_values) {
874 CxJson json;
875 cxJsonInit(&json,
NULL);
876 CX_TEST_DO {
877 CxJsonValue *v;
878 CxJsonStatus result;
879
880
881 cxJsonFill(&json,
"10\n");
882 result = cxJsonNext(&json, &v);
883 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
884 CX_TEST_ASSERT(cxJsonIsNumber(v));
885 CX_TEST_ASSERT(cxJsonAsInteger(v) ==
10);
886 cxJsonValueFree(v);
887
888 result = cxJsonNext(&json, &v);
889 CX_TEST_ASSERT(result ==
CX_JSON_INCOMPLETE_DATA);
890
891 cxJsonFill(&json,
"\"hello world\"\n");
892 result = cxJsonNext(&json, &v);
893 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
894 CX_TEST_ASSERT(cxJsonIsString(v));
895 CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v),
"hello world"));
896 cxJsonValueFree(v);
897
898
899 cxJsonFill(&json,
"{ \"value\": \"test\" }\n");
900 result = cxJsonNext(&json, &v);
901 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
902 CX_TEST_ASSERT(cxJsonIsObject(v));
903 CxJsonValue *value = cxJsonObjGet(v,
"value");
904 CX_TEST_ASSERT(cxJsonAsString(value));
905 cxJsonValueFree(v);
906
907 cxJsonFill(&json,
"[ 0, 1, 2, 3, 4, 5 ]\n");
908 result = cxJsonNext(&json, &v);
909 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
910 CX_TEST_ASSERT(cxJsonIsArray(v));
911 CxJsonValue *a0 = cxJsonArrGet(v,
0);
912 CxJsonValue *a3 = cxJsonArrGet(v,
3);
913 CX_TEST_ASSERT(cxJsonIsNumber(a0));
914 CX_TEST_ASSERT(cxJsonAsInteger(a0) ==
0);
915 CX_TEST_ASSERT(cxJsonIsNumber(a3));
916 CX_TEST_ASSERT(cxJsonAsInteger(a3) ==
3);
917 cxJsonValueFree(v);
918
919 cxJsonFill(&json,
"true\n");
920 result = cxJsonNext(&json, &v);
921 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
922 CX_TEST_ASSERT(cxJsonIsLiteral(v));
923 CX_TEST_ASSERT(cxJsonIsBool(v));
924 CX_TEST_ASSERT(cxJsonIsTrue(v));
925 CX_TEST_ASSERT(cxJsonAsBool(v));
926 cxJsonValueFree(v);
927 cxJsonFill(&json,
"false\n");
928 result = cxJsonNext(&json, &v);
929 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
930 CX_TEST_ASSERT(cxJsonIsLiteral(v));
931 CX_TEST_ASSERT(cxJsonIsBool(v));
932 CX_TEST_ASSERT(cxJsonIsFalse(v));
933 CX_TEST_ASSERT(!cxJsonAsBool(v));
934 cxJsonValueFree(v);
935 cxJsonFill(&json,
"null\n");
936 result = cxJsonNext(&json, &v);
937 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
938 CX_TEST_ASSERT(cxJsonIsLiteral(v));
939 CX_TEST_ASSERT(!cxJsonIsBool(v));
940 CX_TEST_ASSERT(cxJsonIsNull(v));
941 cxJsonValueFree(v);
942 }
943 cxJsonDestroy(&json);
944 }
945
946 CX_TEST(test_json_array) {
947 CxTestingAllocator talloc;
948 cx_testing_allocator_init(&talloc);
949 const CxAllocator *allocator = &talloc.base;
950 CX_TEST_DO {
951 CxJsonValue *arr = cxJsonCreateArr(allocator);
952 cxJsonArrAddIntegers(arr, (
int64_t[]){
0,
3,
6,
9,
12,
15},
6);
953 CX_TEST_ASSERT(cxJsonArrSize(arr) ==
6);
954 CxJsonValue *e = cxJsonArrGet(arr,
3);
955 CX_TEST_ASSERT(cxJsonIsNumber(e));
956 CX_TEST_ASSERT(cxJsonAsInteger(e) ==
9);
957 CX_TEST_ASSERT(cxJsonAsDouble(e) ==
9.0);
958 CX_TEST_ASSERT(cxJsonArrSize(arr) ==
6);
959 e = cxJsonArrGet(arr,
6);
960 CX_TEST_ASSERT(!cxJsonIsNumber(e));
961
962 CX_TEST_ASSERT(!cxJsonIsNull(e));
963 CX_TEST_ASSERT(e->type ==
CX_JSON_NOTHING);
964 CxJsonValue *removed = cxJsonArrRemove(arr,
2);
965 CX_TEST_ASSERT(cxJsonIsNumber(removed));
966 CX_TEST_ASSERT(cxJsonAsInteger(removed) ==
6);
967 CX_TEST_ASSERT(cxJsonArrSize(arr) ==
5);
968 e = cxJsonArrGet(arr,
3);
969 CX_TEST_ASSERT(cxJsonIsNumber(e));
970 CX_TEST_ASSERT(cxJsonAsInteger(e) ==
12);
971 e = cxJsonArrRemove(arr,
5);
972 CX_TEST_ASSERT(e ==
NULL);
973 cxJsonValueFree(arr);
974
975 CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
976 cxJsonValueFree(removed);
977 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
978 }
979 cx_testing_allocator_destroy(&talloc);
980 }
981
982 CX_TEST(test_json_array_iterator) {
983 CxJson json;
984 cxJsonInit(&json,
NULL);
985 CX_TEST_DO {
986 CxJsonValue *v;
987 CxJsonStatus result;
988 cxJsonFill(&json,
"[ 0, 3, 6, 9, 12, 15 ]\n");
989 result = cxJsonNext(&json, &v);
990 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
991 CX_TEST_ASSERT(cxJsonIsArray(v));
992 CxIterator iter = cxJsonArrIter(v);
993 unsigned i =
0;
994 cx_foreach(CxJsonValue*, elem, iter) {
995 CX_TEST_ASSERT(cxJsonIsNumber(elem));
996 CX_TEST_ASSERT(i == cxJsonAsInteger(elem));
997 i +=
3;
998 }
999 cxJsonValueFree(v);
1000 }
1001 cxJsonDestroy(&json);
1002 }
1003
1004 CX_TEST(test_json_allocator) {
1005 CxTestingAllocator talloc;
1006 cx_testing_allocator_init(&talloc);
1007 CxAllocator *allocator = &talloc.base;
1008
1009 cxstring text = cx_str(
1010 "{\n"
1011 "\t\"message\":\"success\",\n"
1012 "\t\"data\":[\"value1\",{\"x\":123, \"y\":523 }]\n"
1013 "}"
1014 );
1015
1016 CX_TEST_DO {
1017 CxJson json;
1018 cxJsonInit(&json, allocator);
1019 cxJsonFill(&json, text);
1020
1021 CxJsonValue *obj;
1022 CxJsonStatus result = cxJsonNext(&json, &obj);
1023 CX_TEST_ASSERT(result ==
CX_JSON_NO_ERROR);
1024 CX_TEST_ASSERT(obj->allocator == allocator);
1025
1026
1027 cxJsonValueFree(obj);
1028 cxJsonDestroy(&json);
1029
1030 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1031 }
1032 cx_testing_allocator_destroy(&talloc);
1033 }
1034
1035 CX_TEST(test_json_allocator_parse_error) {
1036 CxTestingAllocator talloc;
1037 cx_testing_allocator_init(&talloc);
1038 CxAllocator *allocator = &talloc.base;
1039
1040 cxstring text = cx_str(
1041 "{\n"
1042 "\t\"message\":\"success\"\n"
1043 "\t\"data\":[\"value1\",{\"x\":123, \"y\":523 }]\n"
1044 "}"
1045 );
1046
1047 CX_TEST_DO {
1048 CxJson json;
1049 cxJsonInit(&json, allocator);
1050 cxJsonFill(&json, text);
1051
1052 CxJsonValue *obj =
NULL;
1053 CxJsonStatus result = cxJsonNext(&json, &obj);
1054 CX_TEST_ASSERT(result ==
CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
1055 CX_TEST_ASSERT(obj !=
NULL && obj->type ==
CX_JSON_NOTHING);
1056
1057
1058 cxJsonDestroy(&json);
1059
1060 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1061 }
1062 cx_testing_allocator_destroy(&talloc);
1063 }
1064
1065 CX_TEST(test_json_create_value) {
1066 CxTestingAllocator talloc;
1067 cx_testing_allocator_init(&talloc);
1068 CxAllocator *allocator = &talloc.base;
1069 CX_TEST_DO {
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088 CxJsonValue *obj = cxJsonCreateObj(allocator);
1089 CX_TEST_ASSERT(obj !=
NULL);
1090 CX_TEST_ASSERT(cxJsonIsObject(obj));
1091 CX_TEST_ASSERT(obj->allocator == allocator);
1092
1093
1094 {
1095 cxJsonObjPutLiteral(obj,
"bool",
CX_JSON_FALSE);
1096 cxJsonObjPutInteger(obj,
"int",
47);
1097 CxJsonValue *strings = cxJsonObjPutArr(obj,
"strings");
1098 CX_TEST_ASSERT(strings !=
NULL);
1099 CX_TEST_ASSERT(cxJsonIsArray(strings));
1100 const char* str[] = {
"hello",
"world"};
1101 CX_TEST_ASSERT(
0 == cxJsonArrAddStrings(strings, str,
2));
1102
1103 CxJsonValue *nested = cxJsonObjPutObj(obj,
"nested");
1104 CX_TEST_ASSERT(nested !=
NULL);
1105 CX_TEST_ASSERT(cxJsonIsObject(nested));
1106 cxJsonObjPutString(nested,
"string",
"test");
1107
1108 cxJsonArrAddNumbers(cxJsonObjPutArr(nested,
"floats"),
1109 (
double[]){
3.1415,
47.11,
8.15},
3);
1110 cxJsonArrAddIntegers(cxJsonObjPutArr(nested,
"ints"),
1111 (
int64_t[]){
4,
8,
15,
16,
23,
42},
6);
1112 cxJsonArrAddLiterals(cxJsonObjPutArr(nested,
"literals"),
1113 (CxJsonLiteral[]){
CX_JSON_TRUE,
CX_JSON_NULL,
CX_JSON_FALSE},
3);
1114 }
1115
1116
1117 {
1118 CX_TEST_ASSERT(cxJsonIsFalse(cxJsonObjGet(obj,
"bool")));
1119 CX_TEST_ASSERT(
47 == cxJsonAsInteger(cxJsonObjGet(obj,
"int")));
1120 CxJsonValue *strings = cxJsonObjGet(obj,
"strings");
1121 CX_TEST_ASSERT(cxJsonIsArray(strings));
1122 CX_TEST_ASSERT(
2 == cxJsonArrSize(strings));
1123 CX_TEST_ASSERT(
0 == cx_strcmp(
"hello", cxJsonAsString(cxJsonArrGet(strings,
0))));
1124 CX_TEST_ASSERT(
0 == cx_strcmp(
"world", cxJsonAsString(cxJsonArrGet(strings,
1))));
1125
1126 CxJsonValue *nested = cxJsonObjGet(obj,
"nested");
1127 CX_TEST_ASSERT(cxJsonIsObject(nested));
1128 CX_TEST_ASSERT(
0 == strcmp(
"test", cxJsonAsString(cxJsonObjGet(nested,
"string"))));
1129 CxJsonValue *floats = cxJsonObjGet(nested,
"floats");
1130 CX_TEST_ASSERT(cxJsonIsArray(floats));
1131 CX_TEST_ASSERT(
3 == cxJsonArrSize(floats));
1132 CX_TEST_ASSERT(
3.1415 == cxJsonAsDouble(cxJsonArrGet(floats,
0)));
1133 CX_TEST_ASSERT(
47.11 == cxJsonAsDouble(cxJsonArrGet(floats,
1)));
1134 CX_TEST_ASSERT(
8.15 == cxJsonAsDouble(cxJsonArrGet(floats,
2)));
1135 CxJsonValue *ints = cxJsonObjGet(nested,
"ints");
1136 CX_TEST_ASSERT(cxJsonIsArray(ints));
1137 CX_TEST_ASSERT(
6 == cxJsonArrSize(ints));
1138 CX_TEST_ASSERT(
4 == cxJsonAsInteger(cxJsonArrGet(ints,
0)));
1139 CX_TEST_ASSERT(
8 == cxJsonAsInteger(cxJsonArrGet(ints,
1)));
1140 CX_TEST_ASSERT(
15 == cxJsonAsInteger(cxJsonArrGet(ints,
2)));
1141 CX_TEST_ASSERT(
16 == cxJsonAsInteger(cxJsonArrGet(ints,
3)));
1142 CX_TEST_ASSERT(
23 == cxJsonAsInteger(cxJsonArrGet(ints,
4)));
1143 CX_TEST_ASSERT(
42 == cxJsonAsInteger(cxJsonArrGet(ints,
5)));
1144 CxJsonValue *literals = cxJsonObjGet(nested,
"literals");
1145 CX_TEST_ASSERT(cxJsonIsArray(literals));
1146 CX_TEST_ASSERT(
3 == cxJsonArrSize(literals));
1147 CX_TEST_ASSERT(cxJsonIsTrue(cxJsonArrGet(literals,
0)));
1148 CX_TEST_ASSERT(cxJsonIsNull(cxJsonArrGet(literals,
1)));
1149 CX_TEST_ASSERT(cxJsonIsFalse(cxJsonArrGet(literals,
2)));
1150 }
1151
1152
1153 cxJsonValueFree(obj);
1154 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1155 }
1156 cx_testing_allocator_destroy(&talloc);
1157 }
1158
1159 CX_TEST(test_json_overwrite_value) {
1160 CxTestingAllocator talloc;
1161 cx_testing_allocator_init(&talloc);
1162 CxAllocator *allocator = &talloc.base;
1163 CX_TEST_DO {
1164 CxJsonValue *obj = cxJsonCreateObj(allocator);
1165
1166
1167 cxJsonObjPutInteger(obj,
"test1",
1);
1168 cxJsonObjPutInteger(obj,
"test2",
2);
1169 cxJsonObjPutInteger(obj,
"test3",
3);
1170
1171
1172 cxJsonObjPutInteger(obj,
"test2",
0);
1173
1174
1175 CxMapIterator iter = cxJsonObjIter(obj);
1176 bool found[
5] = {
0};
1177 cx_foreach(CxMapEntry *, ov, iter) {
1178 CxJsonValue *v = ov->value;
1179 CX_TEST_ASSERT(cxJsonIsInteger(v));
1180 int64_t i = cxJsonAsInteger(v);
1181 CX_TEST_ASSERT(i >=
0 && i <=
4);
1182 found[i] = true;
1183 }
1184 CX_TEST_ASSERT(found[
0]);
1185 CX_TEST_ASSERT(found[
1]);
1186 CX_TEST_ASSERT(!found[
2]);
1187 CX_TEST_ASSERT(found[
3]);
1188 CX_TEST_ASSERT(!found[
4]);
1189
1190
1191 cxJsonValueFree(obj);
1192 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1193 }
1194 cx_testing_allocator_destroy(&talloc);
1195 }
1196
1197 CX_TEST_SUBROUTINE(test_json_write_sub,
1198 const CxAllocator *allocator,
1199 cxstring expected,
1200 const CxJsonWriter *writer
1201 ) {
1202
1203 CxJsonValue *obj = cxJsonCreateObj(allocator);
1204 cxJsonObjPutLiteral(obj,
"bool",
CX_JSON_FALSE);
1205 cxJsonObjPutNumber(obj,
"int",
47);
1206 CxJsonValue *strings = cxJsonObjPutArr(obj,
"strings");
1207 cxJsonArrAddCxStrings(strings, (cxstring[]) {
CX_STR(
"hello"),
CX_STR(
"world")},
2);
1208 CxJsonValue *nested = cxJsonObjPutObj(obj,
"nested");
1209 CxJsonValue *objects = cxJsonObjPutArr(nested,
"objects");
1210 CxJsonValue *obj_in_arr[
2] = {cxJsonCreateObj(allocator), cxJsonCreateObj(allocator)};
1211 cxJsonObjPutInteger(obj_in_arr[
0],
"name1",
1);
1212 cxJsonObjPutInteger(obj_in_arr[
0],
"name2",
3);
1213 cxJsonObjPutInteger(obj_in_arr[
1],
"name2",
7);
1214 cxJsonObjPutInteger(obj_in_arr[
1],
"name1",
3);
1215 cxJsonArrAddValues(objects, obj_in_arr,
2);
1216 cxJsonArrAddNumbers(cxJsonObjPutArr(nested,
"floats"),
1217 (
double[]){
3.1415,
47.11,
8.15},
3);
1218 cxJsonArrAddLiterals(cxJsonObjPutArr(nested,
"literals"),
1219 (CxJsonLiteral[]){
CX_JSON_TRUE,
CX_JSON_NULL,
CX_JSON_FALSE},
3);
1220 CxJsonValue *ints = cxJsonObjPutArr(nested,
"ints");
1221 cxJsonArrAddIntegers(ints, (
int64_t[]){
4,
8,
15},
3);
1222 CxJsonValue *nested_array = cxJsonCreateArr(allocator);
1223 cxJsonArrAddValues(ints, &nested_array,
1);
1224 cxJsonArrAddIntegers(nested_array, (
int64_t[]){
16,
23},
2);
1225 cxJsonArrAddIntegers(ints, (
int64_t[]){
42},
1);
1226
1227
1228 CxBuffer buf;
1229 cxBufferInit(&buf,
NULL,
512,
NULL,
CX_BUFFER_DEFAULT);
1230 int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, writer);
1231 cxBufferTerminate(&buf);
1232 CX_TEST_ASSERT(result ==
0);
1233
1234
1235 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size), expected));
1236
1237
1238 cxBufferDestroy(&buf);
1239 cxJsonValueFree(obj);
1240 }
1241
1242 CX_TEST(test_json_write_default_format) {
1243 CxTestingAllocator talloc;
1244 cx_testing_allocator_init(&talloc);
1245 CxAllocator *allocator = &talloc.base;
1246 CX_TEST_DO {
1247
1248 cxstring expected = cx_str(
1249 "{\"bool\":false,"
1250 "\"int\":47,"
1251 "\"strings\":[\"hello\",\"world\"],"
1252 "\"nested\":{"
1253 "\"objects\":[{"
1254 "\"name1\":1,"
1255 "\"name2\":3"
1256 "},{"
1257 "\"name2\":7,"
1258 "\"name1\":3"
1259 "}],"
1260 "\"floats\":[3.1415,47.11,8.15],"
1261 "\"literals\":[true,null,false],"
1262 "\"ints\":[4,8,15,[16,23],42]"
1263 "}"
1264 "}"
1265 );
1266
1267 CxJsonWriter writer = cxJsonWriterCompact();
1268 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer);
1269
1270 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected,
NULL);
1271 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1272 }
1273 cx_testing_allocator_destroy(&talloc);
1274 }
1275
1276 CX_TEST(test_json_write_pretty_default_spaces) {
1277 CxTestingAllocator talloc;
1278 cx_testing_allocator_init(&talloc);
1279 CxAllocator *allocator = &talloc.base;
1280 CX_TEST_DO {
1281 cxstring expected = cx_str(
1282 "{\n"
1283 " \"bool\": false,\n"
1284 " \"int\": 47,\n"
1285 " \"strings\": [\"hello\", \"world\"],\n"
1286 " \"nested\": {\n"
1287 " \"objects\": [{\n"
1288 " \"name1\": 1,\n"
1289 " \"name2\": 3\n"
1290 " }, {\n"
1291 " \"name2\": 7,\n"
1292 " \"name1\": 3\n"
1293 " }],\n"
1294 " \"floats\": [3.1415, 47.11, 8.15],\n"
1295 " \"literals\": [true, null, false],\n"
1296 " \"ints\": [4, 8, 15, [16, 23], 42]\n"
1297 " }\n"
1298 "}"
1299 );
1300
1301 CxJsonWriter writer = cxJsonWriterPretty(true);
1302 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer);
1303 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1304 }
1305 cx_testing_allocator_destroy(&talloc);
1306 }
1307
1308 CX_TEST(test_json_write_pretty_default_tabs) {
1309 CxTestingAllocator talloc;
1310 cx_testing_allocator_init(&talloc);
1311 CxAllocator *allocator = &talloc.base;
1312 CX_TEST_DO {
1313 cxstring expected = cx_str(
1314 "{\n"
1315 "\t\"bool\": false,\n"
1316 "\t\"int\": 47,\n"
1317 "\t\"strings\": [\"hello\", \"world\"],\n"
1318 "\t\"nested\": {\n"
1319 "\t\t\"objects\": [{\n"
1320 "\t\t\t\"name1\": 1,\n"
1321 "\t\t\t\"name2\": 3\n"
1322 "\t\t}, {\n"
1323 "\t\t\t\"name2\": 7,\n"
1324 "\t\t\t\"name1\": 3\n"
1325 "\t\t}],\n"
1326 "\t\t\"floats\": [3.1415, 47.11, 8.15],\n"
1327 "\t\t\"literals\": [true, null, false],\n"
1328 "\t\t\"ints\": [4, 8, 15, [16, 23], 42]\n"
1329 "\t}\n"
1330 "}"
1331 );
1332 CxJsonWriter writer = cxJsonWriterPretty(false);
1333 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer);
1334 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1335 }
1336 cx_testing_allocator_destroy(&talloc);
1337 }
1338
1339 CX_TEST(test_json_write_pretty_deep_nesting) {
1340 CxTestingAllocator talloc;
1341 cx_testing_allocator_init(&talloc);
1342 CxAllocator *allocator = &talloc.base;
1343 CX_TEST_DO {
1344 cxstring expected = cx_str(
1345 "{\n"
1346 " \"test\": {\n"
1347 " \"test\": {\n"
1348 " \"test\": {\n"
1349 " \"test\": {\n"
1350 " \"test\": {\n"
1351 " \"test\": 47\n"
1352 " }\n"
1353 " }\n"
1354 " }\n"
1355 " }\n"
1356 " }\n"
1357 "}"
1358 );
1359
1360 CxJsonValue *obj = cxJsonCreateObj(allocator);
1361 CxJsonValue *test = obj;
1362 for (
unsigned i =
0 ; i <
5 ; i++) {
1363 test = cxJsonObjPutObj(test,
"test");
1364 }
1365 cxJsonObjPutInteger(test,
"test",
47);
1366
1367 CxJsonWriter writer = cxJsonWriterPretty(true);
1368 writer.indent =
8;
1369 CxBuffer buf;
1370 cxBufferInit(&buf,
NULL,
512,
NULL,
CX_BUFFER_DEFAULT);
1371 int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer);
1372 cxBufferTerminate(&buf);
1373 CX_TEST_ASSERT(result ==
0);
1374
1375
1376 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size), expected));
1377
1378
1379 cxBufferDestroy(&buf);
1380 cxJsonValueFree(obj);
1381
1382 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
1383 }
1384 cx_testing_allocator_destroy(&talloc);
1385 }
1386
1387 CX_TEST(test_json_write_frac_max_digits) {
1388 CxJsonValue* num = cxJsonCreateNumber(
NULL,
3.141592653589793);
1389 CxJsonWriter writer = cxJsonWriterCompact();
1390 CxBuffer buf;
1391 cxBufferInit(&buf,
NULL,
32,
NULL,
0);
1392 CX_TEST_DO {
1393
1394 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1395 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"3.141592"));
1396
1397
1398 cxBufferReset(&buf);
1399 writer.frac_max_digits =
200;
1400 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1401 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"3.141592653589793"));
1402
1403
1404 cxBufferReset(&buf);
1405 writer.frac_max_digits =
0;
1406 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1407 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"3"));
1408
1409
1410 cxBufferReset(&buf);
1411 writer.frac_max_digits =
2;
1412 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1413 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"3.14"));
1414
1415
1416 cxBufferReset(&buf);
1417 writer.frac_max_digits =
3;
1418 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1419 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"3.141"));
1420
1421
1422 num->number =
47.110815;
1423 cxBufferReset(&buf);
1424 writer.frac_max_digits =
6;
1425 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1426 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"47.110815"));
1427
1428
1429 num->number =
5.11223344e23;
1430 cxBufferReset(&buf);
1431 writer.frac_max_digits =
4;
1432 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer));
1433 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"5.1122e+23"));
1434 }
1435 cxBufferDestroy(&buf);
1436 cxJsonValueFree(num);
1437 }
1438
1439 CX_TEST(test_json_write_string_escape) {
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453 CxJsonValue* str = cxJsonCreateString(
NULL,
1454 "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f");
1455 CxJsonWriter writer = cxJsonWriterCompact();
1456 CxBuffer buf;
1457 cxBufferInit(&buf,
NULL,
128,
NULL,
0);
1458 CX_TEST_DO {
1459 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer));
1460 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
1461 "\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\""));
1462 }
1463 cxBufferDestroy(&buf);
1464 cxJsonValueFree(str);
1465 }
1466
1467 CX_TEST(test_json_write_name_escape) {
1468 CxJsonValue* obj = cxJsonCreateObj(
NULL);
1469 cxJsonObjPutLiteral(obj,
1470 "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f",
CX_JSON_TRUE);
1471 CxJsonWriter writer = cxJsonWriterCompact();
1472 CxBuffer buf;
1473 cxBufferInit(&buf,
NULL,
128,
NULL,
0);
1474 CX_TEST_DO {
1475 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer));
1476 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
1477 "{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}"));
1478 }
1479 cxBufferDestroy(&buf);
1480 cxJsonValueFree(obj);
1481 }
1482
1483 CX_TEST(test_json_write_solidus) {
1484 CxJsonValue* str = cxJsonCreateString(
NULL,
"test/solidus");
1485 CxJsonWriter writer = cxJsonWriterCompact();
1486 CxBuffer buf;
1487 cxBufferInit(&buf,
NULL,
16,
NULL,
0);
1488 CX_TEST_DO {
1489
1490 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer));
1491 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"\"test/solidus\""));
1492
1493
1494 writer.escape_slash = true;
1495 cxBufferReset(&buf);
1496 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer));
1497 CX_TEST_ASSERT(
0 == cx_strcmp(cx_strn(buf.space, buf.size),
"\"test\\/solidus\""));
1498 }
1499 cxBufferDestroy(&buf);
1500 cxJsonValueFree(str);
1501 }
1502
1503 CX_TEST(test_json_write_nothing) {
1504 CxBuffer buf;
1505 cxBufferInit(&buf,
NULL,
16,
NULL,
0);
1506 CX_TEST_DO {
1507 CxJsonValue nothing;
1508 nothing.type =
CX_JSON_NOTHING;
1509 CX_TEST_ASSERT(
0 == cxJsonWrite(&buf, ¬hing, cxBufferWriteFunc,
NULL));
1510 CX_TEST_ASSERT(buf.size ==
0);
1511 }
1512 cxBufferDestroy(&buf);
1513 }
1514
1515 CxTestSuite *cx_test_suite_json(
void) {
1516 CxTestSuite *suite = cx_test_suite_new(
"json");
1517
1518 cx_test_register(suite, test_json_init_default);
1519 cx_test_register(suite, test_json_simple_object);
1520 cx_test_register(suite, test_json_large_object);
1521 cx_test_register(suite, test_json_from_string);
1522 cx_test_register(suite, test_json_escaped_strings);
1523 cx_test_register(suite, test_json_escaped_unicode_strings);
1524 cx_test_register(suite, test_json_escaped_unicode_malformed);
1525 cx_test_register(suite, test_json_escaped_end_of_string);
1526 cx_test_register(suite, test_json_object_incomplete_token);
1527 cx_test_register(suite, test_json_parenthesis_mismatch);
1528 cx_test_register(suite, test_json_object_name_is_no_string);
1529 cx_test_register(suite, test_json_token_wrongly_completed);
1530 cx_test_register(suite, test_json_object_error);
1531 cx_test_register(suite, test_json_object_remove_member);
1532 cx_test_register(suite, test_json_subsequent_fill);
1533 cx_test_register(suite, test_json_no_fill);
1534 cx_test_register(suite, test_json_null_fill);
1535 cx_test_register(suite, test_json_large_nesting_depth);
1536 cx_test_register(suite, test_json_number);
1537 cx_test_register(suite, test_json_number_format_errors);
1538 cx_test_register(suite, test_json_multiple_values);
1539 cx_test_register(suite, test_json_array);
1540 cx_test_register(suite, test_json_array_iterator);
1541 cx_test_register(suite, test_json_allocator);
1542 cx_test_register(suite, test_json_allocator_parse_error);
1543 cx_test_register(suite, test_json_create_value);
1544 cx_test_register(suite, test_json_overwrite_value);
1545 cx_test_register(suite, test_json_write_default_format);
1546 cx_test_register(suite, test_json_write_pretty_default_spaces);
1547 cx_test_register(suite, test_json_write_pretty_default_tabs);
1548 cx_test_register(suite, test_json_write_pretty_deep_nesting);
1549 cx_test_register(suite, test_json_write_frac_max_digits);
1550 cx_test_register(suite, test_json_write_string_escape);
1551 cx_test_register(suite, test_json_write_name_escape);
1552 cx_test_register(suite, test_json_write_solidus);
1553 cx_test_register(suite, test_json_write_nothing);
1554
1555 return suite;
1556 }
1557
1558