UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 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 // parse the big fat object 70 CxJsonValue *obj; 71 result = cxJsonNext(&json, &obj); 72 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 73 74 // check the contents 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 // this recursively frees everything else 110 cxJsonValueFree(obj); 111 112 // we only have one object that already contained all the data 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 // some samples 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 // check the contents 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 // first test, normal scenario 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 // second test - uncompleted token with hanging escape char 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; // split the json text into mulple parts 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 // this recursively frees everything else 447 cxJsonValueFree(obj); 448 449 // now there is everything read 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 // does not verify, yet, because we extracted an object 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 // be as precise as possible 800 // TODO: this might produce format error / out of range in future implementations 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 // read number 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 // read remaining '\n' 888 result = cxJsonNext(&json, &v); 889 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 890 // read string 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 // don't process the remaining newline this time 898 // read obj 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 // read array 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 // read literal 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 // also not null, because "nothing" is not null 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 // the removed element still needs to be freed 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 // this recursively frees everything 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" // <-- missing comma 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 // clean-up any left-over memory 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 * This is the value we want to create in this test: 1072 * 1073 * { 1074 * "bool": false, 1075 * "int": 47, 1076 * "strings": [ "hello", "world" ], 1077 * "nested": { 1078 * "string": "test", 1079 * "floats": [ 3.1415, 47.11, 8.15 ], 1080 * "ints": [ 4, 8, 15, 16, 23, 42 ], 1081 * "literals": [ true, null, false ] 1082 * } 1083 * } 1084 */ 1085 1086 1087 // create the object 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 // add the members 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 // verify the contents 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 // destroy the value and verify the allocations 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 // put some values 1167 cxJsonObjPutInteger(obj, "test1", 1); 1168 cxJsonObjPutInteger(obj, "test2", 2); 1169 cxJsonObjPutInteger(obj, "test3", 3); 1170 1171 // overwrite test2 1172 cxJsonObjPutInteger(obj, "test2", 0); 1173 1174 // verify the values 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 // destroy the value and verify the allocations 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 // create the value 1203 CxJsonValue *obj = cxJsonCreateObj(allocator); 1204 cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); 1205 cxJsonObjPutNumber(obj, "int", 47); // purposely use PutNumber to put an int 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 // write it to a buffer 1228 CxBuffer buf; 1229 cxBufferInit(&buf, NULL, 512, NULL, CX_BUFFER_DEFAULT); 1230 int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, writer); 1231 cxBufferTerminate(&buf); // makes debugging easier 1232 CX_TEST_ASSERT(result == 0); 1233 1234 // compare the string 1235 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), expected)); 1236 1237 // destroy everything 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 // expected value 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 // try again, but this time with implicitly defaulted writer 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); // makes debugging easier 1373 CX_TEST_ASSERT(result == 0); 1374 1375 // compare the string 1376 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), expected)); 1377 1378 // destroy everything 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 // test default settings (6 digits) 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 // test too many digits 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 // test 0 digits 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 // test 2 digits 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 // test 3 digits 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 // test 6 digits, but two are left of the decimal point 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 // test 4 digits with exponent 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 * According to RFC-8259 we have to test the following characters: 1442 * " quotation mark 1443 * \ reverse solidus 1444 * / solidus ---> we make this optional, see test_json_write_solidus 1445 * b backspace 1446 * f form feed 1447 * n line feed 1448 * r carriage return 1449 * t tab 1450 * And all other control characters must be encoded uXXXX - in our example the bell character. 1451 * Also, all unicode characters are encoded that way - in our example the 'ö'. 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 // default: do not escape 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 // enable escaping 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, &nothing, 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