--- a/tests/test_json.c Mon Nov 24 22:39:18 2025 +0100 +++ b/tests/test_json.c Tue Nov 25 20:35:27 2025 +0100 @@ -78,7 +78,7 @@ CX_TEST_ASSERT(cxJsonIsString(message)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(message), - cx_str("success")) + "success") ); CxJsonValue *position = cxJsonObjGet(obj, "position"); @@ -117,6 +117,32 @@ } } +CX_TEST(test_json_large_object) { + CxJsonValue *obj = cxJsonCreateObj(NULL); + CX_TEST_DO { + cxJsonObjPutString(obj, "mystring", "test"); + char buf[10]; + for (unsigned i = 0 ; i < 300 ; i++) { + sprintf(buf, "key %d", i); + cxJsonObjPutInteger(obj, buf, i); + } + CX_TEST_ASSERT(301 == cxJsonObjSize(obj)); + // some samples + CxJsonValue *v; + v = cxJsonObjGet(obj, "key 64"); + CX_TEST_ASSERT(cxJsonIsInteger(v)); + CX_TEST_ASSERT(cxJsonAsInteger(v) == 64); + v = cxJsonObjGet(obj, "key 228"); + CX_TEST_ASSERT(cxJsonIsInteger(v)); + CX_TEST_ASSERT(cxJsonAsInteger(v) == 228); + + v = cxJsonObjGet(obj, "mystring"); + CX_TEST_ASSERT(cxJsonIsString(v)); + CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxMutStr(v), "test")); + } + cxJsonValueFree(obj); +} + CX_TEST(test_json_escaped_strings) { cxstring text = cx_str( "{\n" @@ -137,13 +163,13 @@ CX_TEST_ASSERT(cxJsonIsString(object)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(object), - CX_STR("{\n\t\"object\":null\n}")) + "{\n\t\"object\":null\n}") ); CxJsonValue *ctrl = cxJsonObjGet(obj, "ctrl-chars"); CX_TEST_ASSERT(cxJsonIsString(ctrl)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(ctrl), - CX_STR("\\foo\r\nbar\f*ring/ring*\b")) + "\\foo\r\nbar\f*ring/ring*\b") ); cxJsonValueFree(obj); } @@ -176,39 +202,39 @@ CX_TEST_ASSERT(cxJsonIsString(ascii)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(ascii), - CX_STR("ASCII")) + "ASCII") ); CxJsonValue *unicode = cxJsonObjGet(obj, "unicode"); CX_TEST_ASSERT(cxJsonIsString(unicode)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(unicode), - CX_STR("ßß")) + "ßß") ); CxJsonValue *mixed = cxJsonObjGet(obj, "mixed"); CX_TEST_ASSERT(cxJsonIsString(mixed)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(mixed), - CX_STR("mixed ä ö ä ö")) + "mixed ä ö ä ö") ); CxJsonValue *wide = cxJsonObjGet(obj, "wide"); CX_TEST_ASSERT(cxJsonIsString(wide)); - CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxString(wide), CX_STR("Σ⦰"))); + CX_TEST_ASSERT(0 == cx_strcmp(cxJsonAsCxString(wide), "Σ⦰")); CxJsonValue *surrogatepair1 = cxJsonObjGet(obj, "surrogatepair1"); CX_TEST_ASSERT(cxJsonIsString(surrogatepair1)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(surrogatepair1), - CX_STR("\xf0\x9f\xaf\xb5")) + "\xf0\x9f\xaf\xb5") ); CxJsonValue *surrogatepair2 = cxJsonObjGet(obj, "surrogatepair2"); CX_TEST_ASSERT(cxJsonIsString(surrogatepair2)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(surrogatepair2), - CX_STR("test\xf0\x9f\xaf\xb1" "AA")) + "test\xf0\x9f\xaf\xb1" "AA") ); CxJsonValue *mixed2 = cxJsonObjGet(obj, "mixed2"); @@ -217,7 +243,7 @@ CX_TEST_ASSERT(cxJsonIsString(mixed2)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(mixed2), - CX_STR("123\xce\xa3\xf0\x9f\xaf\x85ß")) + "123\xce\xa3\xf0\x9f\xaf\x85ß") ); cxJsonValueFree(obj); @@ -231,22 +257,22 @@ CxJsonValue *obj; CxJsonStatus result; CX_TEST_DO { - cxJsonFill(&json, "\"too few \\u123 digits\""); + cxJsonFill(&json, "\"too few digits \\u123\""); result = cxJsonNext(&json, &obj); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsString(obj)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(obj), - CX_STR("too few \\u123 digits") + "too few digits \\u123" )); cxJsonValueFree(obj); - cxJsonFill(&json, "\"too many \\u00E456 digits\""); + cxJsonFill(&json, "\"too many digits \\u00E456\""); result = cxJsonNext(&json, &obj); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsString(obj)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(obj), - CX_STR("too many ä56 digits") + "too many digits ä56" )); cxJsonValueFree(obj); cxJsonFill(&json, "\"only high \\uD800 surrogate\""); @@ -255,7 +281,7 @@ CX_TEST_ASSERT(cxJsonIsString(obj)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(obj), - CX_STR("only high \\uD800 surrogate") + "only high \\uD800 surrogate" )); cxJsonValueFree(obj); cxJsonFill(&json, "\"only low \\uDC00 surrogate\""); @@ -264,7 +290,7 @@ CX_TEST_ASSERT(cxJsonIsString(obj)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(obj), - CX_STR("only low \\uDC00 surrogate") + "only low \\uDC00 surrogate" )); cxJsonValueFree(obj); cxJsonFill(&json, "\"two high \\uD800\\uD800 surrogates\""); @@ -273,7 +299,7 @@ CX_TEST_ASSERT(cxJsonIsString(obj)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(obj), - CX_STR("two high \\uD800\\uD800 surrogates") + "two high \\uD800\\uD800 surrogates" )); cxJsonValueFree(obj); cxJsonFill(&json, "\"high plus bullshit \\uD800\\u567 foo\""); @@ -282,7 +308,7 @@ CX_TEST_ASSERT(cxJsonIsString(obj)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(obj), - CX_STR("high plus bullshit \\uD800\\u567 foo") + "high plus bullshit \\uD800\\u567 foo" )); cxJsonValueFree(obj); } @@ -301,7 +327,7 @@ CX_TEST_ASSERT(cxJsonIsString(val)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(val), - cx_str("a \"test\" string")) + "a \"test\" string") ); cxJsonValueFree(val); @@ -315,7 +341,7 @@ CX_TEST_ASSERT(cxJsonIsString(val)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(val), - cx_str("a \"test\" string")) + "a \"test\" string") ); cxJsonValueFree(val); } @@ -354,7 +380,7 @@ CX_TEST_ASSERT(cxJsonIsString(message)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(message), - cx_str("success")) + "success") ); CxJsonValue *timestamp = cxJsonObjGet(obj, "__timestamp"); CX_TEST_ASSERT(message->type == CX_JSON_STRING); @@ -402,6 +428,55 @@ cx_testing_allocator_destroy(&talloc); } +CX_TEST(test_json_parenthesis_mismatch) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + const CxAllocator *alloc = &talloc.base; + + CX_TEST_DO { + CxJson json; + cxJsonInit(&json, alloc); + + CxJsonStatus result; + CxJsonValue *obj; + + cxJsonFill(&json, "[0, 1, 2, 3}"); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); + + cxJsonReset(&json); + cxJsonFill(&json, "{\"test\": 42]"); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); + + cxJsonDestroy(&json); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); +} + +CX_TEST(test_json_object_name_is_no_string) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + const CxAllocator *alloc = &talloc.base; + + CX_TEST_DO { + CxJson json; + cxJsonInit(&json, alloc); + + CxJsonStatus result; + CxJsonValue *obj; + + cxJsonFill(&json, "{42: \"test\"}"); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); + + cxJsonDestroy(&json); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); +} + CX_TEST(test_json_subsequent_fill) { cxstring text = cx_str( "{\"message\":\"success\" , \"__timestamp\":1729348561}"); @@ -424,7 +499,7 @@ CX_TEST_ASSERT(cxJsonIsString(message)); CX_TEST_ASSERT(0 == cx_strcmp( cxJsonAsCxString(message), - cx_str("success")) + "success") ); CxJsonValue *timestamp = cxJsonObjGet(obj, "__timestamp"); CX_TEST_ASSERT(message->type == CX_JSON_STRING); @@ -439,6 +514,63 @@ } } +CX_TEST(test_json_no_fill) { + CxJson json; + cxJsonInit(&json, NULL); + CX_TEST_DO { + CxJsonValue *obj = NULL; + CxJsonStatus result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NULL_DATA); + CX_TEST_ASSERT(obj != NULL); + CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); + } + cxJsonDestroy(&json); +} + +CX_TEST(test_json_null_fill) { + CxJson json; + cxJsonInit(&json, NULL); + CX_TEST_DO { + CxJsonStatus result; + CxJsonValue *obj = NULL; + cxstring nullstr = cx_strn(NULL, 0); + cxJsonFill(&json, nullstr); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NO_DATA); + CX_TEST_ASSERT(obj != NULL); + CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); + obj = NULL; + + cxJsonFill(&json, "[0, 1"); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); + CX_TEST_ASSERT(obj != NULL); + CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); + obj = NULL; + + cxJsonFill(&json, nullstr); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); + CX_TEST_ASSERT(obj != NULL); + CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); + obj = NULL; + + cxJsonFill(&json, ", 2]"); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(obj != NULL); + CX_TEST_ASSERT(cxJsonIsArray(obj)); + cxJsonValueFree(obj); + + cxJsonFill(&json, nullstr); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NO_DATA); + CX_TEST_ASSERT(obj != NULL); + CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); + } + cxJsonDestroy(&json); +} + CX_TEST(test_json_object_error) { cxstring text0 = cx_str( "{\n" @@ -489,7 +621,7 @@ CX_TEST_DO { CxJson json; cxJsonInit(&json, alloc); - cxJsonFill(&json, cx_str( + cxJsonFill(&json, "{\n" "\t\"message\":\"success\",\n" "\t\"data\":{\n" @@ -499,7 +631,7 @@ "\t},\n" "\t\"timestamp\":1729348561\n" "}" - )); + ); CxJsonValue *obj; CX_TEST_ASSERT(CX_JSON_NO_ERROR == cxJsonNext(&json, &obj)); cxJsonDestroy(&json); @@ -704,7 +836,7 @@ result = cxJsonNext(&json, &v); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsString(v)); - CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v), CX_STR("hello world"))); + CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v), "hello world")); cxJsonValueFree(v); // don't process the remaining newline this time // read obj @@ -904,24 +1036,24 @@ // add the members { - cxJsonObjPutLiteral(obj, CX_STR("bool"), CX_JSON_FALSE); - cxJsonObjPutInteger(obj, CX_STR("int"), 47); - CxJsonValue *strings = cxJsonObjPutArr(obj, CX_STR("strings")); + cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); + cxJsonObjPutInteger(obj, "int", 47); + CxJsonValue *strings = cxJsonObjPutArr(obj, "strings"); CX_TEST_ASSERT(strings != NULL); CX_TEST_ASSERT(cxJsonIsArray(strings)); const char* str[] = {"hello", "world"}; CX_TEST_ASSERT(0 == cxJsonArrAddStrings(strings, str, 2)); - CxJsonValue *nested = cxJsonObjPutObj(obj, CX_STR("nested")); + CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); CX_TEST_ASSERT(nested != NULL); CX_TEST_ASSERT(cxJsonIsObject(nested)); - cxJsonObjPutCxString(nested, CX_STR("string"), CX_STR("test")); + cxJsonObjPutString(nested, "string", "test"); - cxJsonArrAddNumbers(cxJsonObjPutArr(nested, CX_STR("floats")), + cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"), (double[]){3.1415, 47.11, 8.15}, 3); - cxJsonArrAddIntegers(cxJsonObjPutArr(nested, CX_STR("ints")), + cxJsonArrAddIntegers(cxJsonObjPutArr(nested, "ints"), (int64_t[]){4, 8, 15, 16, 23, 42}, 6); - cxJsonArrAddLiterals(cxJsonObjPutArr(nested, CX_STR("literals")), + cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"), (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); } @@ -932,8 +1064,8 @@ CxJsonValue *strings = cxJsonObjGet(obj, "strings"); CX_TEST_ASSERT(cxJsonIsArray(strings)); CX_TEST_ASSERT(2 == cxJsonArrSize(strings)); - CX_TEST_ASSERT(0 == cx_strcmp(CX_STR("hello"), cxJsonAsCxString(cxJsonArrGet(strings, 0)))); - CX_TEST_ASSERT(0 == cx_strcmp(CX_STR("world"), cxJsonAsCxString(cxJsonArrGet(strings, 1)))); + CX_TEST_ASSERT(0 == cx_strcmp("hello", cxJsonAsString(cxJsonArrGet(strings, 0)))); + CX_TEST_ASSERT(0 == cx_strcmp("world", cxJsonAsString(cxJsonArrGet(strings, 1)))); CxJsonValue *nested = cxJsonObjGet(obj, "nested"); CX_TEST_ASSERT(cxJsonIsObject(nested)); @@ -968,6 +1100,43 @@ cx_testing_allocator_destroy(&talloc); } +CX_TEST(test_json_overwrite_value) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + CxAllocator *allocator = &talloc.base; + CX_TEST_DO { + CxJsonValue *obj = cxJsonCreateObj(allocator); + + // put some values + cxJsonObjPutInteger(obj, "test1", 1); + cxJsonObjPutInteger(obj, "test2", 2); + cxJsonObjPutInteger(obj, "test3", 3); + + // overwrite test2 + cxJsonObjPutInteger(obj, "test2", 0); + + // verify the values + CxIterator iter = cxJsonObjIter(obj); + bool found[5] = {0}; + cx_foreach(CxJsonObjValue *, ov, iter) { + CxJsonValue *v = ov->value; + CX_TEST_ASSERT(cxJsonIsInteger(v)); + int64_t i = cxJsonAsInteger(v); + CX_TEST_ASSERT(i >= 0 && i <= 4); + found[i] = true; + } + CX_TEST_ASSERT(found[0]); + CX_TEST_ASSERT(found[1]); + CX_TEST_ASSERT(!found[2]); + CX_TEST_ASSERT(found[3]); + CX_TEST_ASSERT(!found[4]); + + // destroy the value and verify the allocations + cxJsonValueFree(obj); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); +} CX_TEST_SUBROUTINE(test_json_write_sub, const CxAllocator *allocator, @@ -976,23 +1145,23 @@ ) { // create the value CxJsonValue *obj = cxJsonCreateObj(allocator); - cxJsonObjPutLiteral(obj, CX_STR("bool"), CX_JSON_FALSE); - cxJsonObjPutNumber(obj, CX_STR("int"), 47); // purposely use PutNumber to put an int - CxJsonValue *strings = cxJsonObjPutArr(obj, CX_STR("strings")); + cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); + cxJsonObjPutNumber(obj, "int", 47); // purposely use PutNumber to put an int + CxJsonValue *strings = cxJsonObjPutArr(obj, "strings"); cxJsonArrAddCxStrings(strings, (cxstring[]) {CX_STR("hello"), CX_STR("world")}, 2); - CxJsonValue *nested = cxJsonObjPutObj(obj, CX_STR("nested")); - CxJsonValue *objects = cxJsonObjPutArr(nested, CX_STR("objects")); + CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); + CxJsonValue *objects = cxJsonObjPutArr(nested, "objects"); CxJsonValue *obj_in_arr[2] = {cxJsonCreateObj(allocator), cxJsonCreateObj(allocator)}; - cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name1"), 1); - cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name2"), 3); - cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name2"), 7); - cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name1"), 3); + cxJsonObjPutInteger(obj_in_arr[0], "name1", 1); + cxJsonObjPutInteger(obj_in_arr[0], "name2", 3); + cxJsonObjPutInteger(obj_in_arr[1], "name2", 7); + cxJsonObjPutInteger(obj_in_arr[1], "name1", 3); cxJsonArrAddValues(objects, obj_in_arr, 2); - cxJsonArrAddNumbers(cxJsonObjPutArr(nested, CX_STR("floats")), + cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"), (double[]){3.1415, 47.11, 8.15}, 3); - cxJsonArrAddLiterals(cxJsonObjPutArr(nested, CX_STR("literals")), + cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"), (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); - CxJsonValue *ints = cxJsonObjPutArr(nested, CX_STR("ints")); + CxJsonValue *ints = cxJsonObjPutArr(nested, "ints"); cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); CxJsonValue *nested_array = cxJsonCreateArr(allocator); cxJsonArrAddValues(ints, &nested_array, 1); @@ -1020,7 +1189,7 @@ CxAllocator *allocator = &talloc.base; CX_TEST_DO { // expected value - cxstring expected = CX_STR( + cxstring expected = cx_str( "{\"bool\":false," "\"int\":47," "\"nested\":{" @@ -1041,6 +1210,8 @@ CxJsonWriter writer = cxJsonWriterCompact(); CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, &writer); + // try again, but this time with implicitly defaulted writer + CX_TEST_CALL_SUBROUTINE(test_json_write_sub, allocator, expected, NULL); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); @@ -1051,7 +1222,7 @@ cx_testing_allocator_init(&talloc); CxAllocator *allocator = &talloc.base; CX_TEST_DO { - cxstring expected = CX_STR( + cxstring expected = cx_str( "{\n" " \"bool\": false,\n" " \"int\": 47,\n" @@ -1083,7 +1254,7 @@ cx_testing_allocator_init(&talloc); CxAllocator *allocator = &talloc.base; CX_TEST_DO { - cxstring expected = CX_STR( + cxstring expected = cx_str( "{\n" "\t\"bool\": false,\n" "\t\"int\": 47,\n" @@ -1114,7 +1285,7 @@ cx_testing_allocator_init(&talloc); CxAllocator *allocator = &talloc.base; CX_TEST_DO { - cxstring expected = CX_STR( + cxstring expected = cx_str( "{\n" " \"bool\": false,\n" " \"int\": 47,\n" @@ -1142,6 +1313,54 @@ cx_testing_allocator_destroy(&talloc); } +CX_TEST(test_json_write_pretty_deep_nesting) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + CxAllocator *allocator = &talloc.base; + CX_TEST_DO { + cxstring expected = cx_str( +"{\n" +" \"test\": {\n" +" \"test\": {\n" +" \"test\": {\n" +" \"test\": {\n" +" \"test\": {\n" +" \"test\": 47\n" +" }\n" +" }\n" +" }\n" +" }\n" +" }\n" +"}" + ); + + CxJsonValue *obj = cxJsonCreateObj(allocator); + CxJsonValue *test = obj; + for (unsigned i = 0 ; i < 5 ; i++) { + test = cxJsonObjPutObj(test, "test"); + } + cxJsonObjPutInteger(test, "test", 47); + + CxJsonWriter writer = cxJsonWriterPretty(true); + writer.indent = 8; + CxBuffer buf; + cxBufferInit(&buf, NULL, 512, NULL, CX_BUFFER_DEFAULT); + int result = cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer); + cxBufferTerminate(&buf); // makes debugging easier + CX_TEST_ASSERT(result == 0); + + // compare the string + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), expected)); + + // destroy everything + cxBufferDestroy(&buf); + cxJsonValueFree(obj); + + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); +} + CX_TEST(test_json_write_frac_max_digits) { CxJsonValue* num = cxJsonCreateNumber(NULL, 3.141592653589793); CxJsonWriter writer = cxJsonWriterCompact(); @@ -1150,45 +1369,45 @@ CX_TEST_DO { // test default settings (6 digits) cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.141592"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141592")); // test too many digits cxBufferReset(&buf); writer.frac_max_digits = 200; cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.141592653589793"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141592653589793")); // test 0 digits cxBufferReset(&buf); writer.frac_max_digits = 0; cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3")); // test 2 digits cxBufferReset(&buf); writer.frac_max_digits = 2; cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.14"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.14")); // test 3 digits cxBufferReset(&buf); writer.frac_max_digits = 3; cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("3.141"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "3.141")); // test 6 digits, but two are left of the decimal point num->value.number = 47.110815; cxBufferReset(&buf); writer.frac_max_digits = 6; cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("47.110815"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "47.110815")); // test 4 digits with exponent num->value.number = 5.11223344e23; cxBufferReset(&buf); writer.frac_max_digits = 4; cxJsonWrite(&buf, num, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("5.1122e+23"))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "5.1122e+23")); } cxBufferDestroy(&buf); cxJsonValueFree(num); @@ -1216,7 +1435,7 @@ CX_TEST_DO { cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer); CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), - CX_STR("\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\""))); + "\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\"")); } cxBufferDestroy(&buf); cxJsonValueFree(str); @@ -1225,14 +1444,14 @@ CX_TEST(test_json_write_name_escape) { CxJsonValue* obj = cxJsonCreateObj(NULL); cxJsonObjPutLiteral(obj, - CX_STR("hello\twörld\r\nthis is\\a \"string\"\b in \a string\f"), CX_JSON_TRUE); + "hello\twörld\r\nthis is\\a \"string\"\b in \a string\f", CX_JSON_TRUE); CxJsonWriter writer = cxJsonWriterCompact(); CxBuffer buf; cxBufferInit(&buf, NULL, 128, NULL, 0); CX_TEST_DO { cxJsonWrite(&buf, obj, cxBufferWriteFunc, &writer); CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), - CX_STR("{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}"))); + "{\"hello\\twörld\\r\\nthis is\\\\a \\\"string\\\"\\b in \\u0007 string\\f\":true}")); } cxBufferDestroy(&buf); cxJsonValueFree(obj); @@ -1246,32 +1465,49 @@ CX_TEST_DO { // default: do not escape cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("\"test/solidus\""))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test/solidus\"")); // enable escaping writer.escape_slash = true; cxBufferReset(&buf); cxJsonWrite(&buf, str, cxBufferWriteFunc, &writer); - CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), CX_STR("\"test\\/solidus\""))); + CX_TEST_ASSERT(0 == cx_strcmp(cx_strn(buf.space, buf.size), "\"test\\/solidus\"")); } cxBufferDestroy(&buf); cxJsonValueFree(str); } +CX_TEST(test_json_write_nothing) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, NULL, 0); + CX_TEST_DO { + CxJsonValue nothing; + nothing.type = CX_JSON_NOTHING; + cxJsonWrite(&buf, ¬hing, cxBufferWriteFunc, NULL); + CX_TEST_ASSERT(buf.size == 0); + } + cxBufferDestroy(&buf); +} + CxTestSuite *cx_test_suite_json(void) { CxTestSuite *suite = cx_test_suite_new("json"); cx_test_register(suite, test_json_init_default); cx_test_register(suite, test_json_simple_object); + cx_test_register(suite, test_json_large_object); cx_test_register(suite, test_json_escaped_strings); cx_test_register(suite, test_json_escaped_unicode_strings); cx_test_register(suite, test_json_escaped_unicode_malformed); cx_test_register(suite, test_json_escaped_end_of_string); cx_test_register(suite, test_json_object_incomplete_token); + cx_test_register(suite, test_json_parenthesis_mismatch); + cx_test_register(suite, test_json_object_name_is_no_string); cx_test_register(suite, test_json_token_wrongly_completed); cx_test_register(suite, test_json_object_error); cx_test_register(suite, test_json_object_remove_member); cx_test_register(suite, test_json_subsequent_fill); + cx_test_register(suite, test_json_no_fill); + cx_test_register(suite, test_json_null_fill); cx_test_register(suite, test_json_large_nesting_depth); cx_test_register(suite, test_json_number); cx_test_register(suite, test_json_number_format_errors); @@ -1281,14 +1517,17 @@ cx_test_register(suite, test_json_allocator); cx_test_register(suite, test_json_allocator_parse_error); cx_test_register(suite, test_json_create_value); + cx_test_register(suite, test_json_overwrite_value); cx_test_register(suite, test_json_write_default_format); cx_test_register(suite, test_json_write_pretty_default_spaces); cx_test_register(suite, test_json_write_pretty_default_tabs); cx_test_register(suite, test_json_write_pretty_preserve_order); + cx_test_register(suite, test_json_write_pretty_deep_nesting); cx_test_register(suite, test_json_write_frac_max_digits); cx_test_register(suite, test_json_write_string_escape); cx_test_register(suite, test_json_write_name_escape); cx_test_register(suite, test_json_write_solidus); + cx_test_register(suite, test_json_write_nothing); return suite; }