tests/test_json.c

changeset 1513
4d641c6a2f82
parent 1338
cc153bffea28
--- 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, &nothing, 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;
 }

mercurial