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 #include <math.h> 36 37 CX_TEST(test_json_init_default) { 38 CxJson json; 39 CX_TEST_DO { 40 cxJsonInit(&json, NULL); 41 CX_TEST_ASSERT(json.states.data == json.states_internal); 42 CX_TEST_ASSERT(json.states.size == 1); 43 CX_TEST_ASSERT(json.states.capacity >= 8); 44 CX_TEST_ASSERT(json.vbuf.data == json.vbuf_internal); 45 CX_TEST_ASSERT(json.vbuf.size == 0); 46 CX_TEST_ASSERT(json.vbuf.capacity >= 8); 47 cxJsonDestroy(&json); 48 } 49 } 50 51 CX_TEST(test_json_simple_object) { 52 cxstring text = cx_str( 53 "{\n" 54 "\t\"message\":\"success\",\n" 55 "\t\"position\":{\n" 56 "\t\t\"longitude\":-94.7099,\n" 57 "\t\t\"latitude\":51.5539\n" 58 "\t},\n" 59 "\t\"timestamp\":1729348561,\n" 60 "\t\"alive\":true\n" 61 "}" 62 ); 63 64 CX_TEST_DO { 65 CxJsonStatus result; 66 67 CxJson json; 68 cxJsonInit(&json, NULL); 69 cxJsonFill(&json, text); 70 71 // parse the big fat object 72 CxJsonValue *obj; 73 result = cxJsonNext(&json, &obj); 74 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 75 76 // check the contents 77 CX_TEST_ASSERT(cxJsonIsObject(obj)); 78 79 CxJsonValue *message = cxJsonObjGet(obj, "message"); 80 CX_TEST_ASSERT(cxJsonIsString(message)); 81 CX_TEST_ASSERT(0 == cx_strcmp( 82 cxJsonAsCxString(message), 83 "success") 84 ); 85 86 CxJsonValue *position = cxJsonObjGet(obj, "position"); 87 CX_TEST_ASSERT(cxJsonIsObject(position)); 88 CxJsonValue *longitude = cxJsonObjGet(position, "longitude"); 89 CX_TEST_ASSERT(cxJsonIsNumber(longitude)); 90 CX_TEST_ASSERT(!cxJsonIsInteger(longitude)); 91 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(longitude), -94.7099)); 92 CX_TEST_ASSERT(cxJsonAsInteger(longitude) == -94); 93 CxJsonValue *latitude = cxJsonObjGet(position, "latitude"); 94 CX_TEST_ASSERT(cxJsonIsNumber(latitude)); 95 CX_TEST_ASSERT(!cxJsonIsInteger(latitude)); 96 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(latitude), 51.5539)); 97 CX_TEST_ASSERT(cxJsonAsInteger(latitude) == 51); 98 99 CxJsonValue *timestamp = cxJsonObjGet(obj, "timestamp"); 100 CX_TEST_ASSERT(cxJsonIsInteger(timestamp)); 101 CX_TEST_ASSERT(cxJsonIsNumber(timestamp)); 102 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) == 1729348561); 103 CX_TEST_ASSERT(cxJsonAsDouble(timestamp) == 1729348561.0); 104 105 CxJsonValue *alive = cxJsonObjGet(obj, "alive"); 106 CX_TEST_ASSERT(cxJsonIsBool(alive)); 107 CX_TEST_ASSERT(cxJsonIsTrue(alive)); 108 CX_TEST_ASSERT(!cxJsonIsFalse(alive)); 109 CX_TEST_ASSERT(cxJsonAsBool(alive)); 110 111 // this recursively frees everything else 112 cxJsonValueFree(obj); 113 114 // we only have one object that already contained all the data 115 result = cxJsonNext(&json, &obj); 116 CX_TEST_ASSERT(result == CX_JSON_NO_DATA); 117 118 cxJsonDestroy(&json); 119 } 120 } 121 122 CX_TEST(test_json_large_object) { 123 CxJsonValue *obj = cxJsonCreateObj(NULL); 124 CX_TEST_DO { 125 cxJsonObjPutString(obj, "mystring", "test"); 126 char buf[10]; 127 for (unsigned i = 0 ; i < 300 ; i++) { 128 snprintf(buf, 10, "key %d", i); 129 cxJsonObjPutInteger(obj, buf, i); 130 } 131 CX_TEST_ASSERT(301 == cxJsonObjSize(obj)); 132 // some samples 133 CxJsonValue *v; 134 v = cxJsonObjGet(obj, "key 64"); 135 CX_TEST_ASSERT(cxJsonIsInteger(v)); 136 CX_TEST_ASSERT(cxJsonAsInteger(v) == 64); 137 v = cxJsonObjGet(obj, "key 228"); 138 CX_TEST_ASSERT(cxJsonIsInteger(v)); 139 CX_TEST_ASSERT(cxJsonAsInteger(v) == 228); 140 141 v = cxJsonObjGet(obj, "mystring"); 142 CX_TEST_ASSERT(cxJsonIsString(v)); 143 CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxMutStr(v), "test")); 144 } 145 cxJsonValueFree(obj); 146 } 147 148 CX_TEST(test_json_simple_array) { 149 cxstring empty_array = cx_str("[]"); 150 cxstring int_array = cx_str("[ 0, 1, 2 ]"); 151 cxstring str_array = cx_str("[ \"str1\", \"str2\" ]"); 152 cxstring mixed_array = cx_str("[ true, false, 12, { \"a\": \"b\" }, [ 1,2,3,4] ]"); 153 154 CxJsonValue *value; 155 CxJsonStatus result; 156 CxJson json0, json1, json2, json3; 157 158 CX_TEST_DO { 159 // empty array 160 cxJsonInit(&json0, NULL); 161 cxJsonFill(&json0, empty_array); 162 result = cxJsonNext(&json0, &value); 163 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 164 CX_TEST_ASSERT(value); 165 CX_TEST_ASSERT(cxJsonIsArray(value)); 166 167 cxJsonValueFree(value); 168 cxJsonDestroy(&json0); 169 170 // int array 171 cxJsonInit(&json1, NULL); 172 cxJsonFill(&json1, int_array); 173 result = cxJsonNext(&json1, &value); 174 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 175 CX_TEST_ASSERT(value); 176 CX_TEST_ASSERT(cxJsonIsArray(value)); 177 CX_TEST_ASSERT(value->array.size == 3); 178 for(int i=0;i<3;i++) { 179 CxJsonValue *v = cxJsonArrGet(value, i); 180 CX_TEST_ASSERT(v); 181 CX_TEST_ASSERT(cxJsonIsInteger(v)); 182 CX_TEST_ASSERT(v->integer == i); 183 } 184 185 cxJsonValueFree(value); 186 cxJsonDestroy(&json1); 187 188 // str array 189 cxJsonInit(&json2, NULL); 190 cxJsonFill(&json2, str_array); 191 result = cxJsonNext(&json2, &value); 192 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 193 CX_TEST_ASSERT(value); 194 CX_TEST_ASSERT(cxJsonIsArray(value)); 195 CX_TEST_ASSERT(value->array.size == 2); 196 CxJsonValue *s0 = cxJsonArrGet(value, 0); 197 CxJsonValue *s1 = cxJsonArrGet(value, 1); 198 CX_TEST_ASSERT(s0 && s1); 199 CX_TEST_ASSERT(cxJsonIsString(s0) && cxJsonIsString(s1)); 200 CX_TEST_ASSERT(cx_strcmp(s0->string, "str1") == 0); 201 CX_TEST_ASSERT(cx_strcmp(s1->string, "str2") == 0); 202 203 cxJsonValueFree(value); 204 cxJsonDestroy(&json2); 205 206 // mixed array 207 cxJsonInit(&json3, NULL); 208 cxJsonFill(&json3, mixed_array); 209 result = cxJsonNext(&json3, &value); 210 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 211 CX_TEST_ASSERT(value); 212 CX_TEST_ASSERT(cxJsonIsArray(value)); 213 CX_TEST_ASSERT(value->array.size == 5); 214 CxJsonValue *m0 = cxJsonArrGet(value, 0); 215 CxJsonValue *m1 = cxJsonArrGet(value, 1); 216 CxJsonValue *m2 = cxJsonArrGet(value, 2); 217 CxJsonValue *m3 = cxJsonArrGet(value, 3); 218 CxJsonValue *m4 = cxJsonArrGet(value, 4); 219 CX_TEST_ASSERT(m0 && m1 && m2 && m3 && m4); 220 CX_TEST_ASSERT(cxJsonIsLiteral(m0)); 221 CX_TEST_ASSERT(cxJsonIsLiteral(m1)); 222 CX_TEST_ASSERT(cxJsonIsInteger(m2)); 223 CX_TEST_ASSERT(cxJsonIsObject(m3)); 224 CX_TEST_ASSERT(cxJsonIsArray(m4)); 225 226 cxJsonValueFree(value); 227 cxJsonDestroy(&json3); 228 } 229 } 230 231 CX_TEST(test_json_from_string) { 232 cxstring text = cx_str( 233 "{\n" 234 "\t\"message\":\"success\",\n" 235 "\t\"position\":{\n" 236 "\t\t\"longitude\":-94.7099,\n" 237 "\t\t\"latitude\":51.5539\n" 238 "\t},\n" 239 "\t\"timestamp\":1729348561,\n" 240 "\t\"alive\":true\n" 241 "}" 242 ); 243 244 CX_TEST_DO { 245 CxJsonValue *obj; 246 CX_TEST_ASSERT(cxJsonFromString(NULL, text, &obj) == CX_JSON_NO_ERROR); 247 248 // check the contents 249 CX_TEST_ASSERT(cxJsonIsObject(obj)); 250 251 CxJsonValue *message = cxJsonObjGet(obj, "message"); 252 CX_TEST_ASSERT(cxJsonIsString(message)); 253 CX_TEST_ASSERT(0 == cx_strcmp( 254 cxJsonAsCxString(message), 255 "success") 256 ); 257 258 CxJsonValue *position = cxJsonObjGet(obj, "position"); 259 CX_TEST_ASSERT(cxJsonIsObject(position)); 260 CxJsonValue *longitude = cxJsonObjGet(position, "longitude"); 261 CX_TEST_ASSERT(cxJsonIsNumber(longitude)); 262 CX_TEST_ASSERT(!cxJsonIsInteger(longitude)); 263 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(longitude), -94.7099)); 264 CX_TEST_ASSERT(cxJsonAsInteger(longitude) == -94); 265 CxJsonValue *latitude = cxJsonObjGet(position, "latitude"); 266 CX_TEST_ASSERT(cxJsonIsNumber(latitude)); 267 CX_TEST_ASSERT(!cxJsonIsInteger(latitude)); 268 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(latitude), 51.5539)); 269 CX_TEST_ASSERT(cxJsonAsInteger(latitude) == 51); 270 271 CxJsonValue *timestamp = cxJsonObjGet(obj, "timestamp"); 272 CX_TEST_ASSERT(cxJsonIsInteger(timestamp)); 273 CX_TEST_ASSERT(cxJsonIsNumber(timestamp)); 274 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) == 1729348561); 275 CX_TEST_ASSERT(cxJsonAsDouble(timestamp) == 1729348561.0); 276 277 CxJsonValue *alive = cxJsonObjGet(obj, "alive"); 278 CX_TEST_ASSERT(cxJsonIsBool(alive)); 279 CX_TEST_ASSERT(cxJsonIsTrue(alive)); 280 CX_TEST_ASSERT(!cxJsonIsFalse(alive)); 281 CX_TEST_ASSERT(cxJsonAsBool(alive)); 282 283 cxJsonValueFree(obj); 284 } 285 } 286 287 CX_TEST(test_json_from_string_errors) { 288 CX_TEST_DO { 289 CxJsonValue *obj = NULL; 290 CX_TEST_ASSERT(cxJsonFromString(NULL, "", &obj) == CX_JSON_NO_DATA); 291 CX_TEST_ASSERT(cxJsonFromString(NULL, cx_str(NULL), &obj) == CX_JSON_NO_DATA); 292 CX_TEST_ASSERT(cxJsonFromString(NULL, "\"incomplete", &obj) == CX_JSON_INCOMPLETE_DATA); 293 CX_TEST_ASSERT(cxJsonFromString(NULL, "{ \"incomplete\": ", &obj) == CX_JSON_INCOMPLETE_DATA); 294 CX_TEST_ASSERT(cxJsonFromString(NULL, "{\"number\": 47110815!}", &obj) == CX_JSON_FORMAT_ERROR_NUMBER); 295 CX_TEST_ASSERT(cxJsonFromString(NULL, "[ \"unexpected token\" : true ]", &obj) == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 296 CX_TEST_ASSERT(cxJsonFromString(NULL, "} [", &obj) == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 297 CX_TEST_ASSERT(obj && obj->type == CX_JSON_NOTHING); 298 } 299 } 300 301 CX_TEST(test_json_from_string_multiple_values) { 302 CxJsonStatus status; 303 CxJsonValue *obj; 304 CX_TEST_DO { 305 obj = NULL; 306 status = cxJsonFromString(NULL, "{ \"obj1\": \"hello\" }\n\"value2\"\n", &obj); 307 CX_TEST_ASSERT(obj != NULL); 308 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 309 CX_TEST_ASSERT(status == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 310 311 obj = NULL; 312 status = cxJsonFromString(NULL, "\"value\" \n ] syntax error [", &obj); 313 CX_TEST_ASSERT(obj != NULL); 314 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 315 CX_TEST_ASSERT(status == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 316 } 317 } 318 319 CX_TEST(test_json_from_string_untrimmed) { 320 CxJsonStatus status; 321 CxJsonValue *obj; 322 CX_TEST_DO { 323 obj = NULL; 324 status = cxJsonFromString(NULL, "\n\t{ \"obj1\": \"hello\" } \n", &obj); 325 CX_TEST_ASSERT(status == CX_JSON_NO_ERROR); 326 CX_TEST_ASSERT(cxJsonIsObject(obj)); 327 CxJsonValue *obj1 = cxJsonObjGet(obj, "obj1"); 328 CX_TEST_ASSERT(cxJsonIsString(obj1)); 329 CX_TEST_ASSERT(cx_strcmp(cxJsonAsCxString(obj1), "hello") == 0); 330 331 cxJsonValueFree(obj); 332 } 333 } 334 335 CX_TEST(test_json_escaped_strings) { 336 cxstring text = cx_str( 337 "{\n" 338 "\t\"object\":\"{\\n\\t\\\"object\\\":null\\n}\",\n" 339 "\t\"ctrl-chars\":\"\\\\foo\\r\\nbar\\f*ring\\/ring*\\b\"\n" 340 "}" 341 ); 342 343 CxJson json; 344 cxJsonInit(&json, NULL); 345 CX_TEST_DO { 346 cxJsonFill(&json, text); 347 CxJsonValue *obj; 348 CxJsonStatus result = cxJsonNext(&json, &obj); 349 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 350 CX_TEST_ASSERT(cxJsonIsObject(obj)); 351 CxJsonValue *object = cxJsonObjGet(obj, "object"); 352 CX_TEST_ASSERT(cxJsonIsString(object)); 353 CX_TEST_ASSERT(0 == cx_strcmp( 354 cxJsonAsCxString(object), 355 "{\n\t\"object\":null\n}") 356 ); 357 CxJsonValue *ctrl = cxJsonObjGet(obj, "ctrl-chars"); 358 CX_TEST_ASSERT(cxJsonIsString(ctrl)); 359 CX_TEST_ASSERT(0 == cx_strcmp( 360 cxJsonAsCxString(ctrl), 361 "\\foo\r\nbar\f*ring/ring*\b") 362 ); 363 cxJsonValueFree(obj); 364 } 365 cxJsonDestroy(&json); 366 } 367 368 CX_TEST(test_json_escaped_unicode_strings) { 369 cxstring text = cx_str( 370 "{\n" 371 "\"ascii\":\"\\u0041\\u0053\\u0043\\u0049\\u0049\",\n" 372 "\"unicode\":\"\\u00df\\u00DF\",\n" 373 "\"mixed\":\"mixed ä ö \\u00e4 \\u00f6\",\n" 374 "\"wide\":\"\\u03a3\\u29b0\",\n" 375 "\"surrogatepair1\":\"\\ud83e\\udff5\",\n" 376 "\"surrogatepair2\":\"test\\ud83e\\udff1AA\"\n," 377 "\"mixed2\":\"123\\u03a3\\ud83e\\udfc5\\u00df\"" 378 "}" 379 ); 380 381 CxJson json; 382 cxJsonInit(&json, NULL); 383 CX_TEST_DO { 384 cxJsonFill(&json, text); 385 CxJsonValue *obj; 386 CxJsonStatus result = cxJsonNext(&json, &obj); 387 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 388 CX_TEST_ASSERT(cxJsonIsObject(obj)); 389 390 CxJsonValue *ascii = cxJsonObjGet(obj, "ascii"); 391 CX_TEST_ASSERT(cxJsonIsString(ascii)); 392 CX_TEST_ASSERT(0 == cx_strcmp( 393 cxJsonAsCxString(ascii), 394 "ASCII") 395 ); 396 397 CxJsonValue *unicode = cxJsonObjGet(obj, "unicode"); 398 CX_TEST_ASSERT(cxJsonIsString(unicode)); 399 CX_TEST_ASSERT(0 == cx_strcmp( 400 cxJsonAsCxString(unicode), 401 "ßß") 402 ); 403 404 CxJsonValue *mixed = cxJsonObjGet(obj, "mixed"); 405 CX_TEST_ASSERT(cxJsonIsString(mixed)); 406 CX_TEST_ASSERT(0 == cx_strcmp( 407 cxJsonAsCxString(mixed), 408 "mixed ä ö ä ö") 409 ); 410 411 CxJsonValue *wide = cxJsonObjGet(obj, "wide"); 412 CX_TEST_ASSERT(cxJsonIsString(wide)); 413 CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxString(wide), "Σ⦰")); 414 415 CxJsonValue *surrogatepair1 = cxJsonObjGet(obj, "surrogatepair1"); 416 CX_TEST_ASSERT(cxJsonIsString(surrogatepair1)); 417 CX_TEST_ASSERT(0 == cx_strcmp( 418 cxJsonAsCxString(surrogatepair1), 419 "\xf0\x9f\xaf\xb5") 420 ); 421 422 CxJsonValue *surrogatepair2 = cxJsonObjGet(obj, "surrogatepair2"); 423 CX_TEST_ASSERT(cxJsonIsString(surrogatepair2)); 424 CX_TEST_ASSERT(0 == cx_strcmp( 425 cxJsonAsCxString(surrogatepair2), 426 "test\xf0\x9f\xaf\xb1" "AA") 427 ); 428 429 CxJsonValue *mixed2 = cxJsonObjGet(obj, "mixed2"); 430 char test[16]; 431 strncpy(test, mixed2->string.ptr, 15); 432 CX_TEST_ASSERT(cxJsonIsString(mixed2)); 433 CX_TEST_ASSERT(0 == cx_strcmp( 434 cxJsonAsCxString(mixed2), 435 "123\xce\xa3\xf0\x9f\xaf\x85ß") 436 ); 437 438 cxJsonValueFree(obj); 439 } 440 cxJsonDestroy(&json); 441 } 442 443 CX_TEST(test_json_escaped_unicode_malformed) { 444 CxJson json; 445 cxJsonInit(&json, NULL); 446 CxJsonValue *obj; 447 CxJsonStatus result; 448 CX_TEST_DO { 449 cxJsonFill(&json, "\"too few digits \\u123\""); 450 result = cxJsonNext(&json, &obj); 451 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 452 CX_TEST_ASSERT(cxJsonIsString(obj)); 453 CX_TEST_ASSERT(0 == cx_strcmp( 454 cxJsonAsCxString(obj), 455 "too few digits \\u123" 456 )); 457 cxJsonValueFree(obj); 458 cxJsonFill(&json, "\"too many digits \\u00E456\""); 459 result = cxJsonNext(&json, &obj); 460 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 461 CX_TEST_ASSERT(cxJsonIsString(obj)); 462 CX_TEST_ASSERT(0 == cx_strcmp( 463 cxJsonAsCxString(obj), 464 "too many digits ä56" 465 )); 466 cxJsonValueFree(obj); 467 cxJsonFill(&json, "\"only high \\uD800 surrogate\""); 468 result = cxJsonNext(&json, &obj); 469 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 470 CX_TEST_ASSERT(cxJsonIsString(obj)); 471 CX_TEST_ASSERT(0 == cx_strcmp( 472 cxJsonAsCxString(obj), 473 "only high \\uD800 surrogate" 474 )); 475 cxJsonValueFree(obj); 476 cxJsonFill(&json, "\"only low \\uDC00 surrogate\""); 477 result = cxJsonNext(&json, &obj); 478 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 479 CX_TEST_ASSERT(cxJsonIsString(obj)); 480 CX_TEST_ASSERT(0 == cx_strcmp( 481 cxJsonAsCxString(obj), 482 "only low \\uDC00 surrogate" 483 )); 484 cxJsonValueFree(obj); 485 cxJsonFill(&json, "\"two high \\uD800\\uD800 surrogates\""); 486 result = cxJsonNext(&json, &obj); 487 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 488 CX_TEST_ASSERT(cxJsonIsString(obj)); 489 CX_TEST_ASSERT(0 == cx_strcmp( 490 cxJsonAsCxString(obj), 491 "two high \\uD800\\uD800 surrogates" 492 )); 493 cxJsonValueFree(obj); 494 cxJsonFill(&json, "\"high plus bullshit \\uD800\\u567 foo\""); 495 result = cxJsonNext(&json, &obj); 496 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 497 CX_TEST_ASSERT(cxJsonIsString(obj)); 498 CX_TEST_ASSERT(0 == cx_strcmp( 499 cxJsonAsCxString(obj), 500 "high plus bullshit \\uD800\\u567 foo" 501 )); 502 cxJsonValueFree(obj); 503 } 504 cxJsonDestroy(&json); 505 } 506 507 CX_TEST(test_json_escaped_end_of_string) { 508 CxJson json; 509 cxJsonInit(&json, NULL); 510 CX_TEST_DO { 511 // first test, normal scenario 512 cxJsonFill(&json, "\"a \\\"test\\\" string\""); 513 CxJsonValue *val; 514 CxJsonStatus result = cxJsonNext(&json, &val); 515 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 516 CX_TEST_ASSERT(cxJsonIsString(val)); 517 CX_TEST_ASSERT(0 == cx_strcmp( 518 cxJsonAsCxString(val), 519 "a \"test\" string") 520 ); 521 cxJsonValueFree(val); 522 523 // second test - uncompleted token with hanging escape char 524 cxJsonFill(&json, "\"a \\\"test\\"); 525 result = cxJsonNext(&json, &val); 526 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 527 cxJsonFill(&json, "\" string\""); 528 result = cxJsonNext(&json, &val); 529 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 530 CX_TEST_ASSERT(cxJsonIsString(val)); 531 CX_TEST_ASSERT(0 == cx_strcmp( 532 cxJsonAsCxString(val), 533 "a \"test\" string") 534 ); 535 cxJsonValueFree(val); 536 } 537 cxJsonDestroy(&json); 538 } 539 540 CX_TEST(test_json_object_incomplete_token) { 541 cxstring text = cx_str( 542 "{\"message\":\"success\" , \"__timestamp\":1729348561}"); 543 cxstring parts[16]; 544 size_t nparts = 0; // split the json text into mulple parts 545 for(size_t i=0;i<text.length;i+=4) { 546 parts[nparts++] = cx_strsubsl(text, i, 4); 547 } 548 549 CX_TEST_DO { 550 CxJsonStatus result; 551 552 CxJson json; 553 cxJsonInit(&json, NULL); 554 CxJsonValue *obj; 555 556 size_t part = 0; 557 while(part < nparts - 1) { 558 cxJsonFill(&json, parts[part]); 559 part++; 560 result = cxJsonNext(&json, &obj); 561 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 562 } 563 cxJsonFill(&json, parts[nparts - 1]); 564 result = cxJsonNext(&json, &obj); 565 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 566 CX_TEST_ASSERT(cxJsonIsObject(obj)); 567 568 CxJsonValue *message = cxJsonObjGet(obj, "message"); 569 CX_TEST_ASSERT(cxJsonIsString(message)); 570 CX_TEST_ASSERT(0 == cx_strcmp( 571 cxJsonAsCxString(message), 572 "success") 573 ); 574 CxJsonValue *timestamp = cxJsonObjGet(obj, "__timestamp"); 575 CX_TEST_ASSERT(message->type == CX_JSON_STRING); 576 CX_TEST_ASSERT(cxJsonIsInteger(timestamp)); 577 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) == 1729348561); 578 579 // this recursively frees everything else 580 cxJsonValueFree(obj); 581 582 // now there is everything read 583 result = cxJsonNext(&json, &obj); 584 CX_TEST_ASSERT(result == CX_JSON_NO_DATA); 585 586 // Test 2: abort after incomplete token 587 cxJsonReset(&json); 588 589 cxJsonFill(&json, "\"incomplete token"); 590 result = cxJsonNext(&json, &obj); 591 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 592 593 cxJsonDestroy(&json); 594 } 595 } 596 597 CX_TEST(test_json_token_wrongly_completed) { 598 CxTestingAllocator talloc; 599 cx_testing_allocator_init(&talloc); 600 const CxAllocator *alloc = &talloc.base; 601 602 cxstring text = cx_str("{\"number\": 47110815!}"); 603 cxstring part1 = cx_strsubsl(text, 0, 16); 604 cxstring part2 = cx_strsubs(text, 16); 605 606 CX_TEST_DO { 607 CxJson json; 608 cxJsonInit(&json, alloc); 609 610 CxJsonStatus result; 611 CxJsonValue *obj; 612 613 cxJsonFill(&json, part1); 614 result = cxJsonNext(&json, &obj); 615 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 616 cxJsonFill(&json, part2); 617 result = cxJsonNext(&json, &obj); 618 CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_NUMBER); 619 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 620 621 cxJsonDestroy(&json); 622 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 623 } 624 cx_testing_allocator_destroy(&talloc); 625 } 626 627 CX_TEST(test_json_parenthesis_mismatch) { 628 CxTestingAllocator talloc; 629 cx_testing_allocator_init(&talloc); 630 const CxAllocator *alloc = &talloc.base; 631 632 CX_TEST_DO { 633 CxJson json; 634 cxJsonInit(&json, alloc); 635 636 CxJsonStatus result; 637 CxJsonValue *obj; 638 639 cxJsonFill(&json, "[0, 1, 2, 3}"); 640 result = cxJsonNext(&json, &obj); 641 CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 642 643 cxJsonReset(&json); 644 cxJsonFill(&json, "{\"test\": 42]"); 645 result = cxJsonNext(&json, &obj); 646 CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 647 648 cxJsonDestroy(&json); 649 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 650 } 651 cx_testing_allocator_destroy(&talloc); 652 } 653 654 CX_TEST(test_json_object_name_is_no_string) { 655 CxTestingAllocator talloc; 656 cx_testing_allocator_init(&talloc); 657 const CxAllocator *alloc = &talloc.base; 658 659 CX_TEST_DO { 660 CxJson json; 661 cxJsonInit(&json, alloc); 662 663 CxJsonStatus result; 664 CxJsonValue *obj; 665 666 cxJsonFill(&json, "{42: \"test\"}"); 667 result = cxJsonNext(&json, &obj); 668 CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 669 670 cxJsonDestroy(&json); 671 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 672 } 673 cx_testing_allocator_destroy(&talloc); 674 } 675 676 CX_TEST(test_json_subsequent_fill) { 677 cxstring text = cx_str( 678 "{\"message\":\"success\" , \"__timestamp\":1729348561}"); 679 680 cxstring part1 = cx_strsubsl(text, 0, 25); 681 cxstring part2 = cx_strsubs(text, 25); 682 683 CX_TEST_DO { 684 CxJson json; 685 cxJsonInit(&json, NULL); 686 CxJsonValue *obj; 687 688 cxJsonFill(&json, part1); 689 cxJsonFill(&json, part2); 690 CxJsonStatus result = cxJsonNext(&json, &obj); 691 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 692 CX_TEST_ASSERT(cxJsonIsObject(obj)); 693 694 CxJsonValue *message = cxJsonObjGet(obj, "message"); 695 CX_TEST_ASSERT(cxJsonIsString(message)); 696 CX_TEST_ASSERT(0 == cx_strcmp( 697 cxJsonAsCxString(message), 698 "success") 699 ); 700 CxJsonValue *timestamp = cxJsonObjGet(obj, "__timestamp"); 701 CX_TEST_ASSERT(message->type == CX_JSON_STRING); 702 CX_TEST_ASSERT(cxJsonIsInteger(timestamp)); 703 CX_TEST_ASSERT(cxJsonAsInteger(timestamp) == 1729348561); 704 705 cxJsonValueFree(obj); 706 result = cxJsonNext(&json, &obj); 707 CX_TEST_ASSERT(result == CX_JSON_NO_DATA); 708 709 cxJsonDestroy(&json); 710 } 711 } 712 713 CX_TEST(test_json_no_fill) { 714 CxJson json; 715 cxJsonInit(&json, NULL); 716 CX_TEST_DO { 717 CxJsonValue *obj = NULL; 718 CxJsonStatus result = cxJsonNext(&json, &obj); 719 CX_TEST_ASSERT(result == CX_JSON_NULL_DATA); 720 CX_TEST_ASSERT(obj != NULL); 721 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 722 } 723 cxJsonDestroy(&json); 724 } 725 726 CX_TEST(test_json_null_fill) { 727 CxJson json; 728 cxJsonInit(&json, NULL); 729 CX_TEST_DO { 730 CxJsonStatus result; 731 CxJsonValue *obj = NULL; 732 cxstring nullstr = cx_strn(NULL, 0); 733 cxJsonFill(&json, nullstr); 734 result = cxJsonNext(&json, &obj); 735 CX_TEST_ASSERT(result == CX_JSON_NO_DATA); 736 CX_TEST_ASSERT(obj != NULL); 737 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 738 obj = NULL; 739 740 cxJsonFill(&json, "[0, 1"); 741 result = cxJsonNext(&json, &obj); 742 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 743 CX_TEST_ASSERT(obj != NULL); 744 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 745 obj = NULL; 746 747 cxJsonFill(&json, nullstr); 748 result = cxJsonNext(&json, &obj); 749 CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); 750 CX_TEST_ASSERT(obj != NULL); 751 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 752 obj = NULL; 753 754 cxJsonFill(&json, ", 2]"); 755 result = cxJsonNext(&json, &obj); 756 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 757 CX_TEST_ASSERT(obj != NULL); 758 CX_TEST_ASSERT(cxJsonIsArray(obj)); 759 cxJsonValueFree(obj); 760 761 cxJsonFill(&json, nullstr); 762 result = cxJsonNext(&json, &obj); 763 CX_TEST_ASSERT(result == CX_JSON_NO_DATA); 764 CX_TEST_ASSERT(obj != NULL); 765 CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); 766 } 767 cxJsonDestroy(&json); 768 } 769 770 CX_TEST(test_json_object_error) { 771 cxstring text0 = cx_str( 772 "{\n" 773 "\t\"message\":\"success\",\n" 774 "\t\"data\":{\n" 775 "\t\t\"obj\":{\n" 776 "\t\t\t\"array\": [1, 2, 3, ?syntaxerror? ]\n" 777 "\t\t}\n" 778 "\t},\n" 779 "\t\"timestamp\":1729348561,\n" 780 "}" 781 ); 782 cxstring text1 = cx_str("{ \"string\" }"); 783 cxstring text2 = cx_str("{ \"a\" : }"); 784 cxstring text3 = cx_str("{ \"a\" : \"b\" ]"); 785 cxstring text4 = cx_str("{ \"name\": \"value\" ]"); 786 787 cxstring tests[] = { text0, text1, text2, text3, text4 }; 788 CxJsonStatus errors[] = { 789 CX_JSON_FORMAT_ERROR_NUMBER, 790 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, 791 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, 792 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, 793 CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN 794 }; 795 796 CX_TEST_DO { 797 CxJsonStatus result; 798 CxJson json; 799 CxJsonValue *obj = NULL; 800 801 for(int i=0;i<5;i++) { 802 cxJsonInit(&json, NULL); 803 cxJsonFill(&json, tests[i]); 804 result = cxJsonNext(&json, &obj); 805 806 CX_TEST_ASSERT(result == errors[i]); 807 CX_TEST_ASSERT(obj != NULL && obj->type == CX_JSON_NOTHING); 808 cxJsonDestroy(&json); 809 } 810 } 811 } 812 813 CX_TEST(test_json_object_remove_member) { 814 CxTestingAllocator talloc; 815 cx_testing_allocator_init(&talloc); 816 const CxAllocator *alloc = &talloc.base; 817 CX_TEST_DO { 818 CxJson json; 819 cxJsonInit(&json, alloc); 820 cxJsonFill(&json, 821 "{\n" 822 "\t\"message\":\"success\",\n" 823 "\t\"data\":{\n" 824 "\t\t\"obj\":{\n" 825 "\t\t\t\"array\": [1, 2, 3]\n" 826 "\t\t}\n" 827 "\t},\n" 828 "\t\"timestamp\":1729348561\n" 829 "}" 830 ); 831 CxJsonValue *obj; 832 CX_TEST_ASSERT(CX_JSON_NO_ERROR == cxJsonNext(&json, &obj)); 833 cxJsonDestroy(&json); 834 835 CX_TEST_ASSERT(cxJsonIsObject(cxJsonObjGet(obj, "data"))); 836 CxJsonValue *data = cxJsonObjRemove(obj, "data"); 837 CX_TEST_ASSERT(cxJsonIsObject(data)); 838 CX_TEST_ASSERT(!cxJsonIsObject(cxJsonObjGet(obj, "data"))); 839 CX_TEST_ASSERT(cxJsonIsObject(cxJsonObjGet(data, "obj"))); 840 841 CX_TEST_ASSERT(NULL == cxJsonObjRemove(obj, "data")); 842 843 // does not verify, yet, because we extracted an object 844 cxJsonValueFree(obj); 845 CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); 846 847 cxJsonValueFree(data); 848 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 849 } 850 cx_testing_allocator_destroy(&talloc); 851 } 852 853 CX_TEST(test_json_large_nesting_depth) { 854 CxJson json; 855 CxJsonValue *d1; 856 cxstring text = cx_str("{\"test\": [{},{\"foo\": [[{\"bar\":[4, 2, [null, {\"key\": 47}]]}]]}]}"); 857 CX_TEST_DO { 858 cxJsonInit(&json, NULL); 859 cxJsonFill(&json, text); 860 cxJsonNext(&json, &d1); 861 862 CX_TEST_ASSERT(d1 != NULL); 863 CX_TEST_ASSERT(cxJsonIsObject(d1)); 864 CxJsonValue *d2 = cxJsonObjGet(d1, "test"); 865 CX_TEST_ASSERT(cxJsonIsArray(d2)); 866 CX_TEST_ASSERT(cxJsonArrSize(d2) == 2); 867 CxJsonValue *d3 = cxJsonArrGet(d2, 1); 868 CX_TEST_ASSERT(cxJsonIsObject(d3)); 869 CxJsonValue *d4 = cxJsonObjGet(d3, "foo"); 870 CX_TEST_ASSERT(cxJsonIsArray(d4)); 871 CX_TEST_ASSERT(cxJsonArrSize(d4) == 1); 872 CxJsonValue *d5 = cxJsonArrGet(d4, 0); 873 CX_TEST_ASSERT(cxJsonIsArray(d5)); 874 CX_TEST_ASSERT(cxJsonArrSize(d5) == 1); 875 CxJsonValue *d6 = cxJsonArrGet(d5, 0); 876 CX_TEST_ASSERT(cxJsonIsObject(d6)); 877 CxJsonValue *d7 = cxJsonObjGet(d6, "bar"); 878 CX_TEST_ASSERT(cxJsonIsArray(d7)); 879 CX_TEST_ASSERT(cxJsonArrSize(d7) == 3); 880 CxJsonValue *d8 = cxJsonArrGet(d7, 2); 881 CX_TEST_ASSERT(cxJsonIsArray(d8)); 882 CX_TEST_ASSERT(cxJsonArrSize(d8) == 2); 883 CxJsonValue *d9a = cxJsonArrGet(d8, 0); 884 CX_TEST_ASSERT(cxJsonIsNull(d9a)); 885 CxJsonValue *d9b = cxJsonArrGet(d8, 1); 886 CX_TEST_ASSERT(cxJsonIsObject(d9b)); 887 CxJsonValue *d10 = cxJsonObjGet(d9b, "key"); 888 CX_TEST_ASSERT(cxJsonIsInteger(d10)); 889 CX_TEST_ASSERT(cxJsonAsInteger(d10) == 47); 890 891 CX_TEST_ASSERT(json.states.data != json.states_internal); 892 CX_TEST_ASSERT(json.states.capacity > cx_nmemb(json.states_internal)); 893 894 cxJsonValueFree(d1); 895 cxJsonDestroy(&json); 896 } 897 } 898 899 CX_TEST(test_json_number) { 900 CxJson json; 901 cxJsonInit(&json, NULL); 902 CX_TEST_DO { 903 CxJsonValue *v; 904 CxJsonStatus result; 905 906 cxJsonFill(&json, "3.1415 "); 907 result = cxJsonNext(&json, &v); 908 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 909 CX_TEST_ASSERT(cxJsonIsNumber(v)); 910 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(v), 3.1415)); 911 cxJsonValueFree(v); 912 913 cxJsonFill(&json, "-47.11e2 "); 914 result = cxJsonNext(&json, &v); 915 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 916 CX_TEST_ASSERT(cxJsonIsNumber(v)); 917 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(v), -4711.0)); 918 cxJsonValueFree(v); 919 920 cxJsonFill(&json, "0.815e-3 "); 921 result = cxJsonNext(&json, &v); 922 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 923 CX_TEST_ASSERT(cxJsonIsNumber(v)); 924 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(v), 0.000815)); 925 cxJsonValueFree(v); 926 927 cxJsonFill(&json, "1.23E4 "); 928 result = cxJsonNext(&json, &v); 929 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 930 CX_TEST_ASSERT(cxJsonIsNumber(v)); 931 CX_TEST_ASSERT(cxJsonAsInteger(v) == 12300); 932 CX_TEST_ASSERT(cxJsonAsDouble(v) == 12300.0); 933 cxJsonValueFree(v); 934 935 cxJsonFill(&json, "18446744073709551615.0123456789 "); 936 result = cxJsonNext(&json, &v); 937 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 938 CX_TEST_ASSERT(cxJsonIsNumber(v)); 939 // be as precise as possible 940 // TODO: this might produce format error / out of range in future implementations 941 CX_TEST_ASSERT(0 == cx_vcmp_double(cxJsonAsDouble(v), 1.8446744073709552e+19)); 942 cxJsonValueFree(v); 943 } 944 cxJsonDestroy(&json); 945 } 946 947 CX_TEST(test_json_number_format_errors) { 948 CxJson json; 949 cxJsonInit(&json, NULL); 950 CX_TEST_DO { 951 CxJsonValue *v; 952 CxJsonStatus result; 953 954 cxJsonFill(&json, "+3.1415 "); 955 result = cxJsonNext(&json, &v); 956 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 957 "leading plus is not RFC-8259 compliant"); 958 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 959 cxJsonReset(&json); 960 961 cxJsonFill(&json, "0.815e-3.0 "); 962 result = cxJsonNext(&json, &v); 963 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 964 "exponent must be an integer"); 965 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 966 cxJsonReset(&json); 967 968 cxJsonFill(&json, "3.14e "); 969 result = cxJsonNext(&json, &v); 970 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 971 "exponent cannot be empty"); 972 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 973 cxJsonReset(&json); 974 975 cxJsonFill(&json, "3.14e~7 "); 976 result = cxJsonNext(&json, &v); 977 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 978 "exponent cannot start with bullshit"); 979 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 980 cxJsonReset(&json); 981 982 cxJsonFill(&json, "1.23e4f "); 983 result = cxJsonNext(&json, &v); 984 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 985 "non-digits in exponent"); 986 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 987 cxJsonReset(&json); 988 989 cxJsonFill(&json, "1.23f "); 990 result = cxJsonNext(&json, &v); 991 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 992 "non-digits in value"); 993 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 994 cxJsonReset(&json); 995 996 cxJsonFill(&json, "1.23.45 "); 997 result = cxJsonNext(&json, &v); 998 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 999 "multiple decimal separators"); 1000 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 1001 cxJsonReset(&json); 1002 1003 cxJsonFill(&json, "184467440737095516150123456789 "); 1004 result = cxJsonNext(&json, &v); 1005 CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, 1006 "30 digit int does not fit into 64-bit int"); 1007 CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); 1008 cxJsonReset(&json); 1009 } 1010 cxJsonDestroy(&json); 1011 } 1012 1013 CX_TEST(test_json_multiple_values) { 1014 CxJson json; 1015 cxJsonInit(&json, NULL); 1016 CX_TEST_DO { 1017 CxJsonValue *v; 1018 CxJsonStatus result; 1019 1020 // read number 1021 cxJsonFill(&json, "10\n"); 1022 result = cxJsonNext(&json, &v); 1023 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1024 CX_TEST_ASSERT(cxJsonIsNumber(v)); 1025 CX_TEST_ASSERT(cxJsonAsInteger(v) == 10); 1026 cxJsonValueFree(v); 1027 // read remaining '\n' 1028 result = cxJsonNext(&json, &v); 1029 CX_TEST_ASSERT(result == CX_JSON_NO_DATA); 1030 // read string 1031 cxJsonFill(&json, "\"hello world\"\n"); 1032 result = cxJsonNext(&json, &v); 1033 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1034 CX_TEST_ASSERT(cxJsonIsString(v)); 1035 CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v), "hello world")); 1036 cxJsonValueFree(v); 1037 // don't process the remaining newline this time 1038 // read obj 1039 cxJsonFill(&json, "{ \"value\": \"test\" }\n"); 1040 result = cxJsonNext(&json, &v); 1041 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1042 CX_TEST_ASSERT(cxJsonIsObject(v)); 1043 CxJsonValue *value = cxJsonObjGet(v, "value"); 1044 CX_TEST_ASSERT(cxJsonAsString(value)); 1045 cxJsonValueFree(v); 1046 // read array 1047 cxJsonFill(&json, "[ 0, 1, 2, 3, 4, 5 ]\n"); 1048 result = cxJsonNext(&json, &v); 1049 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1050 CX_TEST_ASSERT(cxJsonIsArray(v)); 1051 CxJsonValue *a0 = cxJsonArrGet(v, 0); 1052 CxJsonValue *a3 = cxJsonArrGet(v, 3); 1053 CX_TEST_ASSERT(cxJsonIsNumber(a0)); 1054 CX_TEST_ASSERT(cxJsonAsInteger(a0) == 0); 1055 CX_TEST_ASSERT(cxJsonIsNumber(a3)); 1056 CX_TEST_ASSERT(cxJsonAsInteger(a3) == 3); 1057 cxJsonValueFree(v); 1058 // read literal 1059 cxJsonFill(&json, "true\n"); 1060 result = cxJsonNext(&json, &v); 1061 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1062 CX_TEST_ASSERT(cxJsonIsLiteral(v)); 1063 CX_TEST_ASSERT(cxJsonIsBool(v)); 1064 CX_TEST_ASSERT(cxJsonIsTrue(v)); 1065 CX_TEST_ASSERT(cxJsonAsBool(v)); 1066 cxJsonValueFree(v); 1067 cxJsonFill(&json, "false\n"); 1068 result = cxJsonNext(&json, &v); 1069 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1070 CX_TEST_ASSERT(cxJsonIsLiteral(v)); 1071 CX_TEST_ASSERT(cxJsonIsBool(v)); 1072 CX_TEST_ASSERT(cxJsonIsFalse(v)); 1073 CX_TEST_ASSERT(!cxJsonAsBool(v)); 1074 cxJsonValueFree(v); 1075 cxJsonFill(&json, "null\n"); 1076 result = cxJsonNext(&json, &v); 1077 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1078 CX_TEST_ASSERT(cxJsonIsLiteral(v)); 1079 CX_TEST_ASSERT(!cxJsonIsBool(v)); 1080 CX_TEST_ASSERT(cxJsonIsNull(v)); 1081 cxJsonValueFree(v); 1082 } 1083 cxJsonDestroy(&json); 1084 } 1085 1086 CX_TEST(test_json_array) { 1087 CxTestingAllocator talloc; 1088 cx_testing_allocator_init(&talloc); 1089 const CxAllocator *allocator = &talloc.base; 1090 CX_TEST_DO { 1091 CxJsonValue *arr = cxJsonCreateArr(allocator, 0); 1092 cxJsonArrAddIntegers(arr, (int64_t[]){ 0, 3, 6, 9, 12, 15}, 6); 1093 CX_TEST_ASSERT(cxJsonArrSize(arr) == 6); 1094 CxJsonValue *e = cxJsonArrGet(arr, 3); 1095 CX_TEST_ASSERT(cxJsonIsNumber(e)); 1096 CX_TEST_ASSERT(cxJsonAsInteger(e) == 9); 1097 CX_TEST_ASSERT(cxJsonAsDouble(e) == 9.0); 1098 CX_TEST_ASSERT(cxJsonArrSize(arr) == 6); 1099 e = cxJsonArrGet(arr, 6); 1100 CX_TEST_ASSERT(!cxJsonIsNumber(e)); 1101 // also not null, because "nothing" is not null 1102 CX_TEST_ASSERT(!cxJsonIsNull(e)); 1103 CX_TEST_ASSERT(e->type == CX_JSON_NOTHING); 1104 CxJsonValue *removed = cxJsonArrRemove(arr, 2); 1105 CX_TEST_ASSERT(cxJsonIsNumber(removed)); 1106 CX_TEST_ASSERT(cxJsonAsInteger(removed) == 6); 1107 CX_TEST_ASSERT(cxJsonArrSize(arr) == 5); 1108 e = cxJsonArrGet(arr, 3); 1109 CX_TEST_ASSERT(cxJsonIsNumber(e)); 1110 CX_TEST_ASSERT(cxJsonAsInteger(e) == 12); 1111 e = cxJsonArrRemove(arr, 5); 1112 CX_TEST_ASSERT(e == NULL); 1113 cxJsonValueFree(arr); 1114 // the removed element still needs to be freed 1115 CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); 1116 cxJsonValueFree(removed); 1117 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1118 } 1119 cx_testing_allocator_destroy(&talloc); 1120 } 1121 1122 CX_TEST(test_json_array_iterator) { 1123 CxJson json; 1124 cxJsonInit(&json, NULL); 1125 CX_TEST_DO { 1126 CxJsonValue *v; 1127 CxJsonStatus result; 1128 cxJsonFill(&json, "[ 0, 3, 6, 9, 12, 15 ]\n"); 1129 result = cxJsonNext(&json, &v); 1130 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1131 CX_TEST_ASSERT(cxJsonIsArray(v)); 1132 CxIterator iter = cxJsonArrIter(v); 1133 unsigned i = 0; 1134 cx_foreach(CxJsonValue*, elem, iter) { 1135 CX_TEST_ASSERT(cxJsonIsNumber(elem)); 1136 CX_TEST_ASSERT(i == cxJsonAsInteger(elem)); 1137 i += 3; 1138 } 1139 cxJsonValueFree(v); 1140 } 1141 cxJsonDestroy(&json); 1142 } 1143 1144 CX_TEST(test_json_allocator) { 1145 CxTestingAllocator talloc; 1146 cx_testing_allocator_init(&talloc); 1147 CxAllocator *allocator = &talloc.base; 1148 1149 cxstring text = cx_str( 1150 "{\n" 1151 "\t\"message\":\"success\",\n" 1152 "\t\"data\":[\"value1\",{\"x\":123, \"y\":523 }]\n" 1153 "}" 1154 ); 1155 1156 CX_TEST_DO { 1157 CxJson json; 1158 cxJsonInit(&json, allocator); 1159 cxJsonFill(&json, text); 1160 1161 CxJsonValue *obj; 1162 CxJsonStatus result = cxJsonNext(&json, &obj); 1163 CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); 1164 CX_TEST_ASSERT(obj->allocator == allocator); 1165 1166 // this recursively frees everything 1167 cxJsonValueFree(obj); 1168 cxJsonDestroy(&json); 1169 1170 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1171 } 1172 cx_testing_allocator_destroy(&talloc); 1173 } 1174 1175 CX_TEST(test_json_allocator_parse_error) { 1176 CxTestingAllocator talloc; 1177 cx_testing_allocator_init(&talloc); 1178 CxAllocator *allocator = &talloc.base; 1179 1180 cxstring text = cx_str( 1181 "{\n" 1182 "\t\"message\":\"success\"\n" // <-- missing comma 1183 "\t\"data\":[\"value1\",{\"x\":123, \"y\":523 }]\n" 1184 "}" 1185 ); 1186 1187 CX_TEST_DO { 1188 CxJson json; 1189 cxJsonInit(&json, allocator); 1190 cxJsonFill(&json, text); 1191 1192 CxJsonValue *obj = NULL; 1193 CxJsonStatus result = cxJsonNext(&json, &obj); 1194 CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 1195 CX_TEST_ASSERT(obj != NULL && obj->type == CX_JSON_NOTHING); 1196 1197 // clean-up any left-over memory 1198 cxJsonDestroy(&json); 1199 1200 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1201 } 1202 cx_testing_allocator_destroy(&talloc); 1203 } 1204 1205 CX_TEST(test_json_create_value) { 1206 CxTestingAllocator talloc; 1207 cx_testing_allocator_init(&talloc); 1208 CxAllocator *allocator = &talloc.base; 1209 CX_TEST_DO { 1210 /* 1211 * This is the value we want to create in this test: 1212 * 1213 * { 1214 * "bool": false, 1215 * "int": 47, 1216 * "strings": [ "hello", "world" ], 1217 * "nested": { 1218 * "string": "test", 1219 * "floats": [ 3.1415, 47.11, 8.15 ], 1220 * "ints": [ 4, 8, 15, 16, 23, 42 ], 1221 * "literals": [ true, null, false ] 1222 * } 1223 * } 1224 */ 1225 1226 1227 // create the object 1228 CxJsonValue *obj = cxJsonCreateObj(allocator); 1229 CX_TEST_ASSERT(obj != NULL); 1230 CX_TEST_ASSERT(cxJsonIsObject(obj)); 1231 CX_TEST_ASSERT(obj->allocator == allocator); 1232 1233 // add the members 1234 { 1235 cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); 1236 cxJsonObjPutInteger(obj, "int", 47); 1237 CxJsonValue *strings = cxJsonObjPutArr(obj, "strings", 0); 1238 CX_TEST_ASSERT(strings != NULL); 1239 CX_TEST_ASSERT(cxJsonIsArray(strings)); 1240 const char* str[] = {"hello", "world"}; 1241 CX_TEST_ASSERT(0 == cxJsonArrAddStrings(strings, str, 2)); 1242 1243 CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); 1244 CX_TEST_ASSERT(nested != NULL); 1245 CX_TEST_ASSERT(cxJsonIsObject(nested)); 1246 cxJsonObjPutString(nested, "string", "test"); 1247 1248 cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats", 0), 1249 (double[]){3.1415, 47.11, 8.15}, 3); 1250 cxJsonArrAddIntegers(cxJsonObjPutArr(nested, "ints", 0), 1251 (int64_t[]){4, 8, 15, 16, 23, 42}, 6); 1252 cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals", 0), 1253 (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); 1254 } 1255 1256 // verify the contents 1257 { 1258 CX_TEST_ASSERT(cxJsonIsFalse(cxJsonObjGet(obj, "bool"))); 1259 CX_TEST_ASSERT(47 == cxJsonAsInteger(cxJsonObjGet(obj, "int"))); 1260 CxJsonValue *strings = cxJsonObjGet(obj, "strings"); 1261 CX_TEST_ASSERT(cxJsonIsArray(strings)); 1262 CX_TEST_ASSERT(2 == cxJsonArrSize(strings)); 1263 CX_TEST_ASSERT(0 == cx_strcmp("hello", cxJsonAsString(cxJsonArrGet(strings, 0)))); 1264 CX_TEST_ASSERT(0 == cx_strcmp("world", cxJsonAsString(cxJsonArrGet(strings, 1)))); 1265 1266 CxJsonValue *nested = cxJsonObjGet(obj, "nested"); 1267 CX_TEST_ASSERT(cxJsonIsObject(nested)); 1268 CX_TEST_ASSERT(0 == strcmp("test", cxJsonAsString(cxJsonObjGet(nested, "string")))); 1269 CxJsonValue *floats = cxJsonObjGet(nested, "floats"); 1270 CX_TEST_ASSERT(cxJsonIsArray(floats)); 1271 CX_TEST_ASSERT(3 == cxJsonArrSize(floats)); 1272 CX_TEST_ASSERT(3.1415 == cxJsonAsDouble(cxJsonArrGet(floats, 0))); 1273 CX_TEST_ASSERT(47.11 == cxJsonAsDouble(cxJsonArrGet(floats, 1))); 1274 CX_TEST_ASSERT(8.15 == cxJsonAsDouble(cxJsonArrGet(floats, 2))); 1275 CxJsonValue *ints = cxJsonObjGet(nested, "ints"); 1276 CX_TEST_ASSERT(cxJsonIsArray(ints)); 1277 CX_TEST_ASSERT(6 == cxJsonArrSize(ints)); 1278 CX_TEST_ASSERT(4 == cxJsonAsInteger(cxJsonArrGet(ints, 0))); 1279 CX_TEST_ASSERT(8 == cxJsonAsInteger(cxJsonArrGet(ints, 1))); 1280 CX_TEST_ASSERT(15 == cxJsonAsInteger(cxJsonArrGet(ints, 2))); 1281 CX_TEST_ASSERT(16 == cxJsonAsInteger(cxJsonArrGet(ints, 3))); 1282 CX_TEST_ASSERT(23 == cxJsonAsInteger(cxJsonArrGet(ints, 4))); 1283 CX_TEST_ASSERT(42 == cxJsonAsInteger(cxJsonArrGet(ints, 5))); 1284 CxJsonValue *literals = cxJsonObjGet(nested, "literals"); 1285 CX_TEST_ASSERT(cxJsonIsArray(literals)); 1286 CX_TEST_ASSERT(3 == cxJsonArrSize(literals)); 1287 CX_TEST_ASSERT(cxJsonIsTrue(cxJsonArrGet(literals, 0))); 1288 CX_TEST_ASSERT(cxJsonIsNull(cxJsonArrGet(literals, 1))); 1289 CX_TEST_ASSERT(cxJsonIsFalse(cxJsonArrGet(literals, 2))); 1290 } 1291 1292 // destroy the value and verify the allocations 1293 cxJsonValueFree(obj); 1294 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1295 } 1296 cx_testing_allocator_destroy(&talloc); 1297 } 1298 1299 CX_TEST(test_json_overwrite_value) { 1300 CxTestingAllocator talloc; 1301 cx_testing_allocator_init(&talloc); 1302 CxAllocator *allocator = &talloc.base; 1303 CX_TEST_DO { 1304 CxJsonValue *obj = cxJsonCreateObj(allocator); 1305 1306 // put some values 1307 cxJsonObjPutInteger(obj, "test1", 1); 1308 cxJsonObjPutInteger(obj, "test2", 2); 1309 cxJsonObjPutInteger(obj, "test3", 3); 1310 1311 // overwrite test2 1312 cxJsonObjPutInteger(obj, "test2", 0); 1313 1314 // verify the values 1315 CxMapIterator iter = cxJsonObjIter(obj); 1316 bool found[5] = {0}; 1317 cx_foreach(CxMapEntry *, ov, iter) { 1318 CxJsonValue *v = ov->value; 1319 CX_TEST_ASSERT(cxJsonIsInteger(v)); 1320 int64_t i = cxJsonAsInteger(v); 1321 CX_TEST_ASSERT(i >= 0 && i <= 4); 1322 found[i] = true; 1323 } 1324 CX_TEST_ASSERT(found[0]); 1325 CX_TEST_ASSERT(found[1]); 1326 CX_TEST_ASSERT(!found[2]); 1327 CX_TEST_ASSERT(found[3]); 1328 CX_TEST_ASSERT(!found[4]); 1329 1330 // destroy the value and verify the allocations 1331 cxJsonValueFree(obj); 1332 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1333 } 1334 cx_testing_allocator_destroy(&talloc); 1335 } 1336 1337 CX_TEST(test_json_compare_primitives) { 1338 CxJsonValue *a[14]; 1339 a[0] = cxJsonCreateLiteral(NULL, CX_JSON_NULL); 1340 a[1] = cxJsonCreateLiteral(NULL, CX_JSON_TRUE); 1341 a[2] = cxJsonCreateLiteral(NULL, CX_JSON_FALSE); 1342 a[3] = cxJsonCreateInteger(NULL, 1234); 1343 a[4] = cxJsonCreateInteger(NULL, 5432); 1344 a[5] = cxJsonCreateInteger(NULL, -10); 1345 a[6] = cxJsonCreateInteger(NULL, 0); 1346 a[7] = cxJsonCreateNumber(NULL, 0.0f); 1347 a[8] = cxJsonCreateNumber(NULL, 13.37f); 1348 a[9] = cxJsonCreateNumber(NULL, -123.456f); 1349 a[10] = cxJsonCreateNumber(NULL, 1234.0f); 1350 a[11] = cxJsonCreateNumber(NULL, -10.3f); 1351 a[12] = cxJsonCreateString(NULL, "hello"); 1352 a[13] = cxJsonCreateString(NULL, "world"); 1353 1354 CxJsonValue *b[14]; 1355 b[0] = cxJsonCreateLiteral(NULL, CX_JSON_NULL); 1356 b[1] = cxJsonCreateLiteral(NULL, CX_JSON_TRUE); 1357 b[2] = cxJsonCreateLiteral(NULL, CX_JSON_FALSE); 1358 b[3] = cxJsonCreateInteger(NULL, 1234); 1359 b[4] = cxJsonCreateInteger(NULL, 5432); 1360 b[5] = cxJsonCreateInteger(NULL, -10); 1361 b[6] = cxJsonCreateInteger(NULL, 0); 1362 b[7] = cxJsonCreateNumber(NULL, 0.0f); 1363 b[8] = cxJsonCreateNumber(NULL, 13.37f); 1364 b[9] = cxJsonCreateNumber(NULL, -123.456f); 1365 b[10] = cxJsonCreateNumber(NULL, 1234.0f); 1366 b[11] = cxJsonCreateNumber(NULL, -10.3f); 1367 b[12] = cxJsonCreateString(NULL, "hello"); 1368 b[13] = cxJsonCreateString(NULL, "world"); 1369 1370 CX_TEST_DO { 1371 for(int i=0;i<12;i++) { 1372 for(int j=0;j<12;j++) { 1373 int ret = cxJsonCompare(a[i], b[j]); 1374 if(i == j) { 1375 CX_TEST_ASSERT(ret == 0); 1376 } else { 1377 if(cxJsonIsNumber(a[i]) && cxJsonIsNumber(b[j])) { 1378 double diff = fabs(cxJsonAsDouble(a[i]) - cxJsonAsDouble(b[j])); 1379 if(diff < 0.001) { 1380 CX_TEST_ASSERT(ret == 0); 1381 } else { 1382 CX_TEST_ASSERT(ret != 0); 1383 } 1384 } else { 1385 CX_TEST_ASSERT(ret != 0); 1386 } 1387 } 1388 } 1389 } 1390 1391 } 1392 for(int i=0;i<14;i++) { 1393 cxJsonValueFree(a[i]); 1394 cxJsonValueFree(b[i]); 1395 } 1396 } 1397 1398 CX_TEST(test_json_compare_objects) { 1399 CxJsonValue *json[10]; 1400 cxJsonFromString(NULL, "{ }", &json[0]); 1401 cxJsonFromString(NULL, "{ }", &json[1]); 1402 cxJsonFromString(NULL, "{ \"name\": \"value\" }", &json[2]); 1403 cxJsonFromString(NULL, "{ \"name\": \"value\" }", &json[7]); 1404 cxJsonFromString(NULL, "{ \"key0\": \"value0\", \"key1\": \"value1\", \"key2\":null }", &json[3]); 1405 cxJsonFromString(NULL, "{ \"key1\": \"value1\", \"key2\":null, \"key0\": \"value0\" }", &json[4]); 1406 1407 const char *json_complex_object = 1408 "{\n" 1409 " \"bool\": false,\n" 1410 " \"int\": 47,\n" 1411 " \"strings\": [ \"hello\", \"world\" ],\n" 1412 " \"nested\": {\n" 1413 " \"string\": \"test\",\n" 1414 " \"floats\": [ 3.1415, 47.11, 8.15 ],\n" 1415 " \"ints\": [ 4, 8, 15, 16, 23, 42 ],\n" 1416 " \"literals\": [ true, null, false ],\n" 1417 " \"objects\": [\n" 1418 " {},\n" 1419 " { \"unicode\": \"\\u03a3\\u29b0\" },\n" 1420 " { \"array\": [ 1,2,3] }\n" 1421 " ]\n" 1422 " }\n" 1423 "}\n"; 1424 cxJsonFromString(NULL, json_complex_object, &json[5]); 1425 cxJsonFromString(NULL, json_complex_object, &json[6]); 1426 const char *json_complex_object_int_array_mismatch = 1427 "{\n" 1428 " \"bool\": false,\n" 1429 " \"int\": 47,\n" 1430 " \"strings\": [ \"hello\", \"world\" ],\n" 1431 " \"nested\": {\n" 1432 " \"string\": \"test\",\n" 1433 " \"floats\": [ 3.1415, 47.11, 8.15 ],\n" 1434 " \"ints\": [ 4, 8, 16, 15, 23, 42 ],\n" 1435 " \"literals\": [ true, null, false ],\n" 1436 " \"objects\": [\n" 1437 " {},\n" 1438 " { \"unicode\": \"\\u03a3\\u29b0\" },\n" 1439 " { \"array\": [ 1,2,3] }\n" 1440 " ]\n" 1441 " }\n" 1442 "}\n"; 1443 cxJsonFromString(NULL, json_complex_object_int_array_mismatch, &json[8]); 1444 const char *json_complex_object_different_order = 1445 "{\n" 1446 " \"bool\": false,\n" 1447 " \"nested\": {\n" 1448 " \"string\": \"test\",\n" 1449 " \"ints\": [ 4, 8, 15, 16, 23, 42 ],\n" 1450 " \"floats\": [ 3.1415, 47.11, 8.15 ],\n" 1451 " \"objects\": [\n" 1452 " {},\n" 1453 " { \"unicode\": \"\\u03a3\\u29b0\" },\n" 1454 " { \"array\": [ 1,2,3] }\n" 1455 " ],\n" 1456 " \"literals\": [ true, null, false ]\n" 1457 " },\n" 1458 " \"int\": 47,\n" 1459 " \"strings\": [ \"hello\", \"world\" ]\n" 1460 "}\n"; 1461 cxJsonFromString(NULL, json_complex_object_different_order, &json[9]); 1462 1463 CX_TEST_DO { 1464 for (unsigned i = 0; i < cx_nmemb(json); i++) { 1465 CX_TEST_ASSERT(cxJsonIsObject(json[i])); 1466 } 1467 1468 CX_TEST_ASSERT(cxJsonCompare(json[0], json[0]) == 0); 1469 CX_TEST_ASSERT(cxJsonCompare(json[0], json[1]) == 0); 1470 CX_TEST_ASSERT(cxJsonCompare(json[1], json[0]) == 0); 1471 CX_TEST_ASSERT(cxJsonCompare(json[2], json[7]) == 0); 1472 CX_TEST_ASSERT(cxJsonCompare(json[7], json[2]) == 0); 1473 1474 // compare different objects 1475 CX_TEST_ASSERT(cxJsonCompare(json[2], json[3]) != 0); 1476 CX_TEST_ASSERT(cxJsonCompare(json[2], json[5]) != 0); 1477 1478 // compare equal objects with different orders 1479 CX_TEST_ASSERT(cxJsonCompare(json[3], json[4]) == 0); 1480 1481 // test the same complex object 1482 CX_TEST_ASSERT(cxJsonCompare(json[5], json[6]) == 0); 1483 CX_TEST_ASSERT(cxJsonCompare(json[6], json[5]) == 0); 1484 1485 // object has a deep difference 1486 CX_TEST_ASSERT(cxJsonCompare(json[5], json[8]) != 0); 1487 CX_TEST_ASSERT(cxJsonCompare(json[8], json[5]) != 0); 1488 1489 // equal except for the member order 1490 CX_TEST_ASSERT(cxJsonCompare(json[5], json[9]) == 0); 1491 CX_TEST_ASSERT(cxJsonCompare(json[6], json[9]) == 0); 1492 CX_TEST_ASSERT(cxJsonCompare(json[9], json[5]) == 0); 1493 CX_TEST_ASSERT(cxJsonCompare(json[9], json[6]) == 0); 1494 } 1495 1496 for(unsigned i=0;i<cx_nmemb(json);i++) { 1497 cxJsonValueFree(json[i]); 1498 } 1499 } 1500 1501 CX_TEST(test_json_compare_arrays) { 1502 char *str[6]; 1503 str[0] = "[]"; 1504 str[1] = "[[]]"; 1505 str[2] = "[[ {} ], { \"a\": [[[[ {} ], null]]]} ]"; 1506 str[3] = "[ true, false ]"; 1507 str[4] = "[ 0, 1, \"hello\", true, false, null, {}, {\"a\":\"b\"}]"; 1508 str[5] = "[ \"test\", [ 1, 2, [ \"sub\", \"sub1\", [{\"end\":null}]]]]"; 1509 1510 CxJsonValue *a[6]; 1511 CxJsonValue *b[6]; 1512 for(int i=0;i<6;i++) { 1513 cxJsonFromString(NULL, str[i], &a[i]); 1514 cxJsonFromString(NULL, str[i], &b[i]); 1515 } 1516 1517 CX_TEST_DO { 1518 for(int i=0;i<6;i++) { 1519 // make sure the test values are arrays 1520 CX_TEST_ASSERT(cxJsonIsArray(a[i])); 1521 1522 for(int j=0;j<6;j++) { 1523 int ret = cxJsonCompare(a[i], b[j]); 1524 CX_TEST_ASSERT(i == j ? ret == 0 : ret != 0); 1525 } 1526 } 1527 1528 // manual test 1529 CxJsonValue *value; 1530 cxJsonFromString(NULL, 1531 "{ \"array1\" : [ 1, 2, 10, 20, true, false, null, {} ],\n" 1532 " \"array2\" : [\n" 1533 " 1,\n" 1534 " 2,\n" 1535 " 10,\n" 1536 " 20,\n" 1537 " true,\n" 1538 " false,\n" 1539 " null,\n" 1540 " {},\n" 1541 " ]\n" 1542 "}\n", 1543 &value); 1544 CX_TEST_ASSERT(value); 1545 CX_TEST_ASSERT(cxJsonIsObject(value)); 1546 CxJsonValue *arr1 = cxJsonObjGet(value, "array1"); 1547 CxJsonValue *arr2 = cxJsonObjGet(value, "array2"); 1548 CX_TEST_ASSERT(arr1 && arr2); 1549 CX_TEST_ASSERT(cxJsonIsArray(arr1) && cxJsonIsArray(arr2)); 1550 CX_TEST_ASSERT(cxJsonCompare(arr1, arr2) == 0); 1551 cxJsonValueFree(value); 1552 1553 cxJsonFromString(NULL, "[ 0, 1, 2, 3, 4, 5 ]", &value); 1554 CX_TEST_ASSERT(value); 1555 CX_TEST_ASSERT(cxJsonIsArray(value)); 1556 1557 CxJsonValue *value2 = cxJsonCreateArr(NULL, 16); 1558 int64_t ints[] = { 0, 1, 2, 3, 4, 5 }; 1559 cxJsonArrAddIntegers(value2, ints, 6); 1560 CX_TEST_ASSERT(cxJsonCompare(value, value2) == 0); 1561 1562 cxJsonValueFree(value); 1563 cxJsonValueFree(value2); 1564 } 1565 1566 for(int i=0;i<6;i++) { 1567 cxJsonValueFree(a[i]); 1568 cxJsonValueFree(b[i]); 1569 } 1570 } 1571 1572 CX_TEST(test_json_clone_primitives) { 1573 CxJsonValue *a[14]; 1574 a[0] = cxJsonCreateLiteral(NULL, CX_JSON_NULL); 1575 a[1] = cxJsonCreateLiteral(NULL, CX_JSON_TRUE); 1576 a[2] = cxJsonCreateLiteral(NULL, CX_JSON_FALSE); 1577 a[3] = cxJsonCreateInteger(NULL, 1234); 1578 a[4] = cxJsonCreateInteger(NULL, 5432); 1579 a[5] = cxJsonCreateInteger(NULL, -10); 1580 a[6] = cxJsonCreateInteger(NULL, 0); 1581 a[7] = cxJsonCreateNumber(NULL, 0.0f); 1582 a[8] = cxJsonCreateNumber(NULL, 13.37f); 1583 a[9] = cxJsonCreateNumber(NULL, -123.456f); 1584 a[10] = cxJsonCreateNumber(NULL, 1234.0f); 1585 a[11] = cxJsonCreateNumber(NULL, -10.3f); 1586 a[12] = cxJsonCreateString(NULL, ""); 1587 a[13] = cxJsonCreateString(NULL, "test"); 1588 1589 CX_TEST_DO { 1590 CxJsonValue *n = cxJsonClone(NULL, NULL); 1591 CX_TEST_ASSERT(n != NULL); 1592 CX_TEST_ASSERT(n->type == CX_JSON_NOTHING); 1593 1594 for(int i=0;i<14;i++) { 1595 // make sure the test setup is not broken 1596 CX_TEST_ASSERT(a[i]->type != CX_JSON_NOTHING); 1597 1598 CxJsonValue *b = cxJsonClone(a[i], NULL); 1599 CX_TEST_ASSERT(b != NULL); 1600 CX_TEST_ASSERT(a[i]->type == b->type); 1601 CX_TEST_ASSERT(cxJsonCompare(a[i], b) == 0); 1602 1603 // alternative comparison using cxJsonToString 1604 cxmutstr aStr = cxJsonToString(NULL, a[i]); 1605 cxmutstr bStr = cxJsonToString(NULL, b); 1606 CX_TEST_ASSERT(cx_strcmp(aStr, bStr) == 0); 1607 cxFree(cxDefaultAllocator, aStr.ptr); 1608 cxFree(cxDefaultAllocator, bStr.ptr); 1609 1610 cxJsonValueFree(b); 1611 } 1612 1613 CxJsonValue *nan1 = cxJsonCreateNumber(NULL, NAN); 1614 CxJsonValue *nan2 = cxJsonClone(nan1, NULL); 1615 CX_TEST_ASSERT(nan2 != NULL); 1616 CX_TEST_ASSERT(nan2->type == CX_JSON_NUMBER); 1617 CX_TEST_ASSERT(isnan(nan2->number)); 1618 1619 cxJsonValueFree(nan1); 1620 cxJsonValueFree(nan2); 1621 } 1622 1623 for(int i=0;i<14;i++) { 1624 cxJsonValueFree(a[i]); 1625 } 1626 } 1627 1628 #ifndef TEST_CLONE_OBJECTS_LARGE_OBJ 1629 #define TEST_CLONE_OBJECTS_LARGE_OBJ 100 1630 #endif 1631 #ifndef TEST_CLONE_OBJECTS_LARGE_ARR 1632 #define TEST_CLONE_OBJECTS_LARGE_ARR 200 1633 #endif 1634 CX_TEST(test_json_clone_objects) { 1635 CxJsonValue *a[10]; 1636 1637 cxJsonFromString(NULL, "{}", &a[0]); 1638 cxJsonFromString(NULL, "{ \"key\":\"value\" }", &a[1]); 1639 cxJsonFromString(NULL, "{ \"abc\": {} }", &a[2]); 1640 cxJsonFromString(NULL, "{ \"abc\": [ false ] }", &a[3]); 1641 cxJsonFromString(NULL, "{ \"a4\": { \"x1\": 123, \"x2\": 456 } }", &a[4]); 1642 cxJsonFromString(NULL, "{ \"a5\": [ true, false, null ] }", &a[5]); 1643 cxJsonFromString(NULL, "{ \"a5\": [ true, false, null, {} ] }", &a[6]); 1644 cxJsonFromString(NULL, "{ \"string\": \"hello\", \"int\": 12, \"literal\":true }", &a[7]); 1645 cxJsonFromString(NULL, 1646 "{ \"x0\": {\n" 1647 " \"x1\": {\n" 1648 " \"x2\": {\n" 1649 " \"x3\": {\n" 1650 " \"x4\": [\n" 1651 " 1, 2, [3, [4, [[[{\"x5\":6}]]]]]\n" 1652 " ]\n" 1653 " }\n" 1654 " }\n" 1655 " }\n" 1656 " }\n" 1657 "}", 1658 &a[8]); 1659 a[9] = cxJsonCreateObj(NULL); 1660 1661 // fill the very large object (a[9]) 1662 for(int i=0;i<TEST_CLONE_OBJECTS_LARGE_OBJ;i++) { 1663 char buf[32]; 1664 snprintf(buf, 32, "int%d", i); 1665 cxJsonObjPutInteger(a[9], buf, i); 1666 1667 1668 CxJsonValue *arr = cxJsonCreateArr(NULL, TEST_CLONE_OBJECTS_LARGE_ARR); 1669 int64_t *ints = calloc(TEST_CLONE_OBJECTS_LARGE_ARR, sizeof(int64_t)); 1670 for(int n=0;n<TEST_CLONE_OBJECTS_LARGE_ARR;n++) { 1671 ints[i] = n; 1672 } 1673 cxJsonArrAddIntegers(arr, ints, TEST_CLONE_OBJECTS_LARGE_ARR); 1674 free(ints); 1675 cxJsonObjPut(a[9], "array", arr); 1676 } 1677 1678 CX_TEST_DO { 1679 for(unsigned i=0;i<cx_nmemb(a);i++) { 1680 CX_TEST_ASSERT(cxJsonIsObject(a[i])); 1681 1682 CxJsonValue *b = cxJsonClone(a[i], NULL); 1683 1684 CX_TEST_ASSERT(b != NULL); 1685 CX_TEST_ASSERT(b->type == a[i]->type); 1686 CX_TEST_ASSERT(cxJsonCompare(a[i], b) == 0); 1687 1688 cxmutstr a_str = cxJsonToString(NULL, a[i]); 1689 cxmutstr b_str = cxJsonToString(NULL, b); 1690 CX_TEST_ASSERT(cx_strcmp(a_str, b_str) == 0); 1691 cx_strfree(&a_str); 1692 cx_strfree(&b_str); 1693 1694 cxJsonValueFree(b); 1695 } 1696 } 1697 1698 for(int i=0;i<10;i++) { 1699 cxJsonValueFree(a[i]); 1700 } 1701 } 1702 1703 CX_TEST(test_json_clone_arrays) { 1704 CxJsonValue *a[6]; 1705 cxJsonFromString(NULL, "[]", &a[0]); 1706 cxJsonFromString(NULL, "[ 1, 2, 4, 8, 16, 32, 64 ]", &a[1]); 1707 cxJsonFromString(NULL, "[ \"string\", 12, 3.14, true, null, false, [] ]", &a[2]); 1708 cxJsonFromString(NULL, "[[1, [[2, [3, 4, 5]]], true], false]", &a[3]); 1709 cxJsonFromString(NULL, "[ { \"abc\": 200, \"str\": \"hello\" }, { }, null ]", &a[4]); 1710 cxJsonFromString(NULL, "[ { \"array\": [ 1,2,3 ]} ]", &a[5]); 1711 1712 CX_TEST_DO { 1713 for(unsigned i=0;i<cx_nmemb(a);i++) { 1714 CX_TEST_ASSERT(cxJsonIsArray(a[i])); 1715 1716 CxJsonValue *b = cxJsonClone(a[i], NULL); 1717 CX_TEST_ASSERT(b); 1718 CX_TEST_ASSERT(cxJsonIsArray(b)); 1719 CX_TEST_ASSERT(cxJsonCompare(a[i], b) == 0); 1720 1721 cxmutstr a_str = cxJsonToString(NULL, a[i]); 1722 cxmutstr b_str = cxJsonToString(NULL, b); 1723 CX_TEST_ASSERT(cx_strcmp(a_str, b_str) == 0); 1724 cx_strfree(&a_str); 1725 cx_strfree(&b_str); 1726 1727 cxJsonValueFree(b); 1728 } 1729 } 1730 1731 for(int i=0;i<6;i++) { 1732 cxJsonValueFree(a[i]); 1733 } 1734 } 1735 1736 1737 1738 static CxJsonValue *test_json_write_create_test_object(const CxAllocator *allocator) { 1739 CxJsonValue *obj = cxJsonCreateObj(allocator); 1740 cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); 1741 cxJsonObjPutNumber(obj, "int", 47); // purposely use PutNumber to put an int 1742 CxJsonValue *strings = cxJsonObjPutArr(obj, "strings", 0); 1743 cxJsonArrAddCxStrings(strings, (cxstring[]) {cx_str("hello"), cx_str("world")}, 2); 1744 CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); 1745 CxJsonValue *objects = cxJsonObjPutArr(nested, "objects", 0); 1746 CxJsonValue *obj_in_arr[2] = {cxJsonCreateObj(allocator), cxJsonCreateObj(allocator)}; 1747 cxJsonObjPutInteger(obj_in_arr[0], "name1", 1); 1748 cxJsonObjPutInteger(obj_in_arr[0], "name2", 3); 1749 cxJsonObjPutInteger(obj_in_arr[1], "name2", 7); 1750 cxJsonObjPutInteger(obj_in_arr[1], "name1", 3); 1751 cxJsonArrAddValues(objects, obj_in_arr, 2); 1752 cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats", 0), 1753 (double[]){3.1415, 47.11, 8.15}, 3); 1754 cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals", 2), 1755 (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); 1756 CxJsonValue *ints = cxJsonObjPutArr(nested, "ints", 3); 1757 cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); 1758 CxJsonValue *nested_array = cxJsonCreateArr(allocator, 0); 1759 cxJsonArrAddValues(ints, &nested_array, 1); 1760 cxJsonArrAddIntegers(nested_array, (int64_t[]){16, 23}, 2); 1761 cxJsonArrAddIntegers(ints, (int64_t[]){42}, 1); 1762 return obj; 1763 } 1764 1765 CX_TEST_SUBROUTINE(test_json_write_sub, 1766 const CxAllocator *allocator, 1767 cxstring expected, 1768 const CxJsonWriter *writer 1769 ) { 1770 // create the value 1771 CxJsonValue *obj = test_json_write_create_test_object(allocator); 1772 1773 // write it to a buffer 1774 CxBuffer buf; 1775 cxBufferInit(&buf, NULL, NULL, 512, CX_BUFFER_DEFAULT); 1776 int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, writer); 1777 cxBufferTerminate(&buf); // makes debugging easier 1778 CX_TEST_ASSERT(result == 0); 1779 1780 // compare the string 1781 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), expected)); 1782 1783 // destroy everything 1784 cxBufferDestroy(&buf); 1785 cxJsonValueFree(obj); 1786 } 1787 1788 CX_TEST(test_json_write_default_format) { 1789 CxTestingAllocator talloc; 1790 cx_testing_allocator_init(&talloc); 1791 CxAllocator *allocator = &talloc.base; 1792 CX_TEST_DO { 1793 // expected value 1794 cxstring expected = cx_str( 1795 "{\"bool\":false," 1796 "\"int\":47," 1797 "\"strings\":[\"hello\",\"world\"]," 1798 "\"nested\":{" 1799 "\"objects\":[{" 1800 "\"name1\":1," 1801 "\"name2\":3" 1802 "},{" 1803 "\"name2\":7," 1804 "\"name1\":3" 1805 "}]," 1806 "\"floats\":[3.1415,47.11,8.15]," 1807 "\"literals\":[true,null,false]," 1808 "\"ints\":[4,8,15,[16,23],42]" 1809 "}" 1810 "}" 1811 ); 1812 1813 CxJsonWriter writer = cxJsonWriterCompact(); 1814 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer); 1815 // try again, but this time with implicitly defaulted writer 1816 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, NULL); 1817 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1818 } 1819 cx_testing_allocator_destroy(&talloc); 1820 } 1821 1822 CX_TEST(test_json_write_pretty_default_spaces) { 1823 CxTestingAllocator talloc; 1824 cx_testing_allocator_init(&talloc); 1825 CxAllocator *allocator = &talloc.base; 1826 CX_TEST_DO { 1827 cxstring expected = cx_str( 1828 "{\n" 1829 " \"bool\": false,\n" 1830 " \"int\": 47,\n" 1831 " \"strings\": [\"hello\", \"world\"],\n" 1832 " \"nested\": {\n" 1833 " \"objects\": [{\n" 1834 " \"name1\": 1,\n" 1835 " \"name2\": 3\n" 1836 " }, {\n" 1837 " \"name2\": 7,\n" 1838 " \"name1\": 3\n" 1839 " }],\n" 1840 " \"floats\": [3.1415, 47.11, 8.15],\n" 1841 " \"literals\": [true, null, false],\n" 1842 " \"ints\": [4, 8, 15, [16, 23], 42]\n" 1843 " }\n" 1844 "}" 1845 ); 1846 1847 CxJsonWriter writer = cxJsonWriterPretty(true); 1848 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer); 1849 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1850 } 1851 cx_testing_allocator_destroy(&talloc); 1852 } 1853 1854 CX_TEST(test_json_write_pretty_default_tabs) { 1855 CxTestingAllocator talloc; 1856 cx_testing_allocator_init(&talloc); 1857 CxAllocator *allocator = &talloc.base; 1858 CX_TEST_DO { 1859 cxstring expected = cx_str( 1860 "{\n" 1861 "\t\"bool\": false,\n" 1862 "\t\"int\": 47,\n" 1863 "\t\"strings\": [\"hello\", \"world\"],\n" 1864 "\t\"nested\": {\n" 1865 "\t\t\"objects\": [{\n" 1866 "\t\t\t\"name1\": 1,\n" 1867 "\t\t\t\"name2\": 3\n" 1868 "\t\t}, {\n" 1869 "\t\t\t\"name2\": 7,\n" 1870 "\t\t\t\"name1\": 3\n" 1871 "\t\t}],\n" 1872 "\t\t\"floats\": [3.1415, 47.11, 8.15],\n" 1873 "\t\t\"literals\": [true, null, false],\n" 1874 "\t\t\"ints\": [4, 8, 15, [16, 23], 42]\n" 1875 "\t}\n" 1876 "}" 1877 ); 1878 CxJsonWriter writer = cxJsonWriterPretty(false); 1879 CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer); 1880 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1881 } 1882 cx_testing_allocator_destroy(&talloc); 1883 } 1884 1885 CX_TEST(test_json_write_pretty_deep_nesting) { 1886 CxTestingAllocator talloc; 1887 cx_testing_allocator_init(&talloc); 1888 CxAllocator *allocator = &talloc.base; 1889 CX_TEST_DO { 1890 cxstring expected = cx_str( 1891 "{\n" 1892 " \"test\": {\n" 1893 " \"test\": {\n" 1894 " \"test\": {\n" 1895 " \"test\": {\n" 1896 " \"test\": {\n" 1897 " \"test\": 47\n" 1898 " }\n" 1899 " }\n" 1900 " }\n" 1901 " }\n" 1902 " }\n" 1903 "}" 1904 ); 1905 1906 CxJsonValue *obj = cxJsonCreateObj(allocator); 1907 CxJsonValue *test = obj; 1908 for (unsigned i = 0 ; i < 5 ; i++) { 1909 test = cxJsonObjPutObj(test, "test"); 1910 } 1911 cxJsonObjPutInteger(test, "test", 47); 1912 1913 CxJsonWriter writer = cxJsonWriterPretty(true); 1914 writer.indent = 8; 1915 CxBuffer buf; 1916 cxBufferInit(&buf, NULL, NULL, 512, CX_BUFFER_DEFAULT); 1917 int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer); 1918 cxBufferTerminate(&buf); // makes debugging easier 1919 CX_TEST_ASSERT(result == 0); 1920 1921 // compare the string 1922 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), expected)); 1923 1924 // destroy everything 1925 cxBufferDestroy(&buf); 1926 cxJsonValueFree(obj); 1927 1928 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 1929 } 1930 cx_testing_allocator_destroy(&talloc); 1931 } 1932 1933 CX_TEST(test_json_write_frac_max_digits) { 1934 CxJsonValue* num = cxJsonCreateNumber(NULL, 3.141592653589793); 1935 CxJsonWriter writer = cxJsonWriterCompact(); 1936 CxBuffer buf; 1937 cxBufferInit(&buf, NULL, NULL, 32, 0); 1938 CX_TEST_DO { 1939 // test default settings (6 digits) 1940 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1941 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141592")); 1942 1943 // test too many digits 1944 cxBufferReset(&buf); 1945 writer.frac_max_digits = 200; 1946 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1947 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141592653589793")); 1948 1949 // test 0 digits 1950 cxBufferReset(&buf); 1951 writer.frac_max_digits = 0; 1952 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1953 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3")); 1954 1955 // test 2 digits 1956 cxBufferReset(&buf); 1957 writer.frac_max_digits = 2; 1958 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1959 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.14")); 1960 1961 // test 3 digits 1962 cxBufferReset(&buf); 1963 writer.frac_max_digits = 3; 1964 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1965 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141")); 1966 1967 // test 6 digits, but two are left of the decimal point 1968 num->number = 47.110815; 1969 cxBufferReset(&buf); 1970 writer.frac_max_digits = 6; 1971 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1972 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "47.110815")); 1973 1974 // test 4 digits with exponent 1975 num->number = 5.11223344e23; 1976 cxBufferReset(&buf); 1977 writer.frac_max_digits = 4; 1978 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer)); 1979 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "5.1122e+23")); 1980 } 1981 cxBufferDestroy(&buf); 1982 cxJsonValueFree(num); 1983 } 1984 1985 CX_TEST(test_json_write_string_escape) { 1986 /** 1987 * According to RFC-8259 we have to test the following characters: 1988 * " quotation mark 1989 * \ reverse solidus 1990 * / solidus ---> we make this optional, see test_json_write_solidus 1991 * b backspace 1992 * f form feed 1993 * n line feed 1994 * r carriage return 1995 * t tab 1996 * And all other control characters must be encoded uXXXX - in our example the bell character. 1997 * Also, all unicode characters are encoded that way - in our example the 'ö'. 1998 */ 1999 CxJsonValue* str = cxJsonCreateString(NULL, 2000 "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f"); 2001 CxJsonWriter writer = cxJsonWriterCompact(); 2002 CxBuffer buf; 2003 cxBufferInit(&buf, NULL, NULL, 128, 0); 2004 CX_TEST_DO { 2005 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer)); 2006 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), 2007 "\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\"")); 2008 } 2009 cxBufferDestroy(&buf); 2010 cxJsonValueFree(str); 2011 } 2012 2013 CX_TEST(test_json_write_name_escape) { 2014 CxJsonValue* obj = cxJsonCreateObj(NULL); 2015 cxJsonObjPutLiteral(obj, 2016 "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f", CX_JSON_TRUE); 2017 CxJsonWriter writer = cxJsonWriterCompact(); 2018 CxBuffer buf; 2019 cxBufferInit(&buf, NULL, NULL, 128, 0); 2020 CX_TEST_DO { 2021 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer)); 2022 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), 2023 "{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}")); 2024 } 2025 cxBufferDestroy(&buf); 2026 cxJsonValueFree(obj); 2027 } 2028 2029 CX_TEST(test_json_write_solidus) { 2030 CxJsonValue* str = cxJsonCreateString(NULL,"test/solidus"); 2031 CxJsonWriter writer = cxJsonWriterCompact(); 2032 CxBuffer buf; 2033 cxBufferInit(&buf, NULL, NULL, 16, 0); 2034 CX_TEST_DO { 2035 // default: do not escape 2036 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer)); 2037 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test/solidus\"")); 2038 2039 // enable escaping 2040 writer.escape_slash = true; 2041 cxBufferReset(&buf); 2042 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer)); 2043 CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test\\/solidus\"")); 2044 } 2045 cxBufferDestroy(&buf); 2046 cxJsonValueFree(str); 2047 } 2048 2049 CX_TEST(test_json_write_nothing) { 2050 CxBuffer buf; 2051 cxBufferInit(&buf, NULL, NULL, 16, 0); 2052 CX_TEST_DO { 2053 CxJsonValue nothing; 2054 nothing.type = CX_JSON_NOTHING; 2055 CX_TEST_ASSERT(0 == cxJsonWrite(&buf, &nothing, cxBufferWriteFunc, NULL)); 2056 CX_TEST_ASSERT(buf.size == 0); 2057 } 2058 cxBufferDestroy(&buf); 2059 } 2060 2061 CX_TEST(test_json_to_string) { 2062 CxTestingAllocator talloc; 2063 cx_testing_allocator_init(&talloc); 2064 CxAllocator *allocator = &talloc.base; 2065 CX_TEST_DO { 2066 // expected value 2067 cxstring expected = cx_str( 2068 "{\"bool\":false," 2069 "\"int\":47," 2070 "\"strings\":[\"hello\",\"world\"]," 2071 "\"nested\":{" 2072 "\"objects\":[{" 2073 "\"name1\":1," 2074 "\"name2\":3" 2075 "},{" 2076 "\"name2\":7," 2077 "\"name1\":3" 2078 "}]," 2079 "\"floats\":[3.1415,47.11,8.15]," 2080 "\"literals\":[true,null,false]," 2081 "\"ints\":[4,8,15,[16,23],42]" 2082 "}" 2083 "}" 2084 ); 2085 2086 CxJsonValue *obj = test_json_write_create_test_object(allocator); 2087 cxmutstr result = cxJsonToString(allocator, obj); 2088 CX_TEST_ASSERT(0 == cx_strcmp(result, expected)); 2089 CX_TEST_ASSERT(result.ptr[result.length] == '\0'); 2090 2091 cx_strfree_a(allocator, &result); 2092 cxJsonValueFree(obj); 2093 2094 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 2095 } 2096 cx_testing_allocator_destroy(&talloc); 2097 } 2098 2099 CX_TEST(test_json_to_pretty_string) { 2100 CxTestingAllocator talloc; 2101 cx_testing_allocator_init(&talloc); 2102 CxAllocator *allocator = &talloc.base; 2103 CX_TEST_DO { 2104 cxstring expected = cx_str( 2105 "{\n" 2106 " \"bool\": false,\n" 2107 " \"int\": 47,\n" 2108 " \"strings\": [\"hello\", \"world\"],\n" 2109 " \"nested\": {\n" 2110 " \"objects\": [{\n" 2111 " \"name1\": 1,\n" 2112 " \"name2\": 3\n" 2113 " }, {\n" 2114 " \"name2\": 7,\n" 2115 " \"name1\": 3\n" 2116 " }],\n" 2117 " \"floats\": [3.1415, 47.11, 8.15],\n" 2118 " \"literals\": [true, null, false],\n" 2119 " \"ints\": [4, 8, 15, [16, 23], 42]\n" 2120 " }\n" 2121 "}" 2122 ); 2123 2124 CxJsonValue *obj = test_json_write_create_test_object(allocator); 2125 cxmutstr result = cxJsonToPrettyString(allocator, obj); 2126 CX_TEST_ASSERT(0 == cx_strcmp(result, expected)); 2127 CX_TEST_ASSERT(result.ptr[result.length] == '\0'); 2128 2129 cx_strfree_a(allocator, &result); 2130 cxJsonValueFree(obj); 2131 2132 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 2133 } 2134 cx_testing_allocator_destroy(&talloc); 2135 } 2136 2137 CxTestSuite *cx_test_suite_json(void) { 2138 CxTestSuite *suite = cx_test_suite_new("json"); 2139 2140 cx_test_register(suite, test_json_init_default); 2141 cx_test_register(suite, test_json_simple_object); 2142 cx_test_register(suite, test_json_large_object); 2143 cx_test_register(suite, test_json_simple_array); 2144 cx_test_register(suite, test_json_from_string); 2145 cx_test_register(suite, test_json_from_string_errors); 2146 cx_test_register(suite, test_json_from_string_multiple_values); 2147 cx_test_register(suite, test_json_from_string_untrimmed); 2148 cx_test_register(suite, test_json_escaped_strings); 2149 cx_test_register(suite, test_json_escaped_unicode_strings); 2150 cx_test_register(suite, test_json_escaped_unicode_malformed); 2151 cx_test_register(suite, test_json_escaped_end_of_string); 2152 cx_test_register(suite, test_json_object_incomplete_token); 2153 cx_test_register(suite, test_json_parenthesis_mismatch); 2154 cx_test_register(suite, test_json_object_name_is_no_string); 2155 cx_test_register(suite, test_json_token_wrongly_completed); 2156 cx_test_register(suite, test_json_object_error); 2157 cx_test_register(suite, test_json_object_remove_member); 2158 cx_test_register(suite, test_json_subsequent_fill); 2159 cx_test_register(suite, test_json_no_fill); 2160 cx_test_register(suite, test_json_null_fill); 2161 cx_test_register(suite, test_json_large_nesting_depth); 2162 cx_test_register(suite, test_json_number); 2163 cx_test_register(suite, test_json_number_format_errors); 2164 cx_test_register(suite, test_json_multiple_values); 2165 cx_test_register(suite, test_json_array); 2166 cx_test_register(suite, test_json_array_iterator); 2167 cx_test_register(suite, test_json_allocator); 2168 cx_test_register(suite, test_json_allocator_parse_error); 2169 cx_test_register(suite, test_json_create_value); 2170 cx_test_register(suite, test_json_overwrite_value); 2171 cx_test_register(suite, test_json_compare_primitives); 2172 cx_test_register(suite, test_json_compare_objects); 2173 cx_test_register(suite, test_json_compare_arrays); 2174 cx_test_register(suite, test_json_clone_primitives); 2175 cx_test_register(suite, test_json_clone_objects); 2176 cx_test_register(suite, test_json_clone_arrays); 2177 cx_test_register(suite, test_json_write_default_format); 2178 cx_test_register(suite, test_json_write_pretty_default_spaces); 2179 cx_test_register(suite, test_json_write_pretty_default_tabs); 2180 cx_test_register(suite, test_json_write_pretty_deep_nesting); 2181 cx_test_register(suite, test_json_write_frac_max_digits); 2182 cx_test_register(suite, test_json_write_string_escape); 2183 cx_test_register(suite, test_json_write_name_escape); 2184 cx_test_register(suite, test_json_write_solidus); 2185 cx_test_register(suite, test_json_write_nothing); 2186 cx_test_register(suite, test_json_to_string); 2187 cx_test_register(suite, test_json_to_pretty_string); 2188 2189 return suite; 2190 } 2191 2192