diff -r 56eb7da4f3e1 -r a0922b925d2a tests/test_json.c --- a/tests/test_json.c Sun Dec 15 14:32:39 2024 +0100 +++ b/tests/test_json.c Sun Dec 15 14:32:51 2024 +0100 @@ -116,6 +116,31 @@ } } +CX_TEST(test_json_escaped_strings) { + cxstring text = cx_str( + "{\n" + "\t\"object\":\"{\\n\\t\\\"object\\\":null\\n}\"}\"\n" + "}" + ); + + CxJson json; + cxJsonInit(&json, NULL); + CX_TEST_DO { + cxJsonFill(&json, text); + CxJsonValue *obj; + CxJsonStatus result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsObject(obj)); + CxJsonValue *object = cxJsonObjGet(obj, "object"); + CX_TEST_ASSERT(cxJsonIsString(object)); + CX_TEST_ASSERT(0 == cx_strcmp( + cxJsonAsCxString(object), + CX_STR("{\n\t\"object\":null\n}")) + ); + } + cxJsonDestroy(&json); +} + CX_TEST(test_json_object_incomplete_token) { cxstring text = cx_str( "{\"message\":\"success\" , \"__timestamp\":1729348561}"); @@ -166,6 +191,28 @@ } } +CX_TEST(test_json_token_wrongly_completed) { + cxstring text = cx_str("{\"number\": 47110815!}"); + cxstring part1 = cx_strsubsl(text, 0, 16); + cxstring part2 = cx_strsubs(text, 16); + + CxJson json; + cxJsonInit(&json, NULL); + CX_TEST_DO { + CxJsonStatus result; + CxJsonValue *obj; + + cxJsonFill(&json, part1); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); + cxJsonFill(&json, part2); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_NUMBER); + CX_TEST_ASSERT(obj->type == CX_JSON_NOTHING); + } + cxJsonDestroy(&json); +} + CX_TEST(test_json_subsequent_fill) { cxstring text = cx_str( "{\"message\":\"success\" , \"__timestamp\":1729348561}"); @@ -296,21 +343,119 @@ CxJson json; cxJsonInit(&json, NULL); CX_TEST_DO { - // TODO: find a better way to terminate values that are not arrays/objects CxJsonValue *v; CxJsonStatus result; + cxJsonFill(&json, "3.1415 "); result = cxJsonNext(&json, &v); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsNumber(v)); CX_TEST_ASSERT(cxJsonAsDouble(v) == 3.1415); cxJsonValueFree(v); + cxJsonFill(&json, "-47.11e2 "); result = cxJsonNext(&json, &v); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsNumber(v)); CX_TEST_ASSERT(cxJsonAsDouble(v) == -4711.0); cxJsonValueFree(v); + + cxJsonFill(&json, "0.815e-3 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsNumber(v)); + CX_TEST_ASSERT(cxJsonAsDouble(v) == 0.000815); + + cxJsonFill(&json, "1.23E4 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsNumber(v)); + CX_TEST_ASSERT(cxJsonAsInteger(v) == 12300); + CX_TEST_ASSERT(cxJsonAsDouble(v) == 12300.0); + + cxJsonValueFree(v); + } + cxJsonDestroy(&json); +} + +CX_TEST(test_json_number_format_errors) { + CxJson json; + cxJsonInit(&json, NULL); + CX_TEST_DO { + CxJsonValue *v; + CxJsonStatus result; + + cxJsonFill(&json, "+3.1415 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "leading plus is not RFC-8259 compliant"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + +#if 0 // TODO: discuss if it is intended that this produces -47 as valid value + cxJsonFill(&json, "-47,11e2 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, + "decimal separator cannot be a comma"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); +#endif + + cxJsonFill(&json, "0.815e-3.0 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "exponent must be an integer"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "3.14e "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "exponent cannot be empty"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "3.14e~7 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "exponent cannot start with bullshit"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "1.23e4f "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "non-digits in exponent"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "1.23f "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "non-digits in value"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "1.23.45 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "multiple decimal separators"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "184467440737095516150123456789 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "30 digit int does not fit into 64-bit int"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); + + cxJsonFill(&json, "18446744073709551615.0123456789 "); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERTM(result == CX_JSON_FORMAT_ERROR_NUMBER, + "numbers with more than 30 characters are unsupported in general"); + CX_TEST_ASSERT(v->type == CX_JSON_NOTHING); + cxJsonReset(&json); } cxJsonDestroy(&json); } @@ -365,12 +510,23 @@ result = cxJsonNext(&json, &v); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsLiteral(v)); + CX_TEST_ASSERT(cxJsonIsBool(v)); + CX_TEST_ASSERT(cxJsonIsTrue(v)); CX_TEST_ASSERT(cxJsonAsBool(v)); cxJsonValueFree(v); - // read null + cxJsonFill(&json, "false\n"); + result = cxJsonNext(&json, &v); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsLiteral(v)); + CX_TEST_ASSERT(cxJsonIsBool(v)); + CX_TEST_ASSERT(cxJsonIsFalse(v)); + CX_TEST_ASSERT(!cxJsonAsBool(v)); + cxJsonValueFree(v); cxJsonFill(&json, "null\n"); result = cxJsonNext(&json, &v); CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); + CX_TEST_ASSERT(cxJsonIsLiteral(v)); + CX_TEST_ASSERT(!cxJsonIsBool(v)); CX_TEST_ASSERT(cxJsonIsNull(v)); cxJsonValueFree(v); } @@ -443,11 +599,14 @@ cx_test_register(suite, test_json_init_default); cx_test_register(suite, test_json_simple_object); + cx_test_register(suite, test_json_escaped_strings); cx_test_register(suite, test_json_object_incomplete_token); + cx_test_register(suite, test_json_token_wrongly_completed); cx_test_register(suite, test_json_object_error); cx_test_register(suite, test_json_subsequent_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); cx_test_register(suite, test_json_multiple_values); cx_test_register(suite, test_json_allocator); cx_test_register(suite, test_json_allocator_parse_error);