/* * test_json_serialization.c - Unit tests for JSON serialization */ #include #include #include #include #include #include #include #include #include #include #include typedef struct { char *data; size_t len; size_t cap; apr_pool_t *pool; } testbuf_t; static void testbuf_init(testbuf_t *buf, apr_pool_t *pool, size_t initial_capacity) { buf->pool = pool; buf->cap = initial_capacity; buf->len = 0; buf->data = apr_palloc(pool, initial_capacity); buf->data[0] = '\0'; } static void testbuf_append(testbuf_t *buf, const char *str, size_t len) { if (str == NULL) { return; } if (len == (size_t)-1) { len = strlen(str); } if (buf->len + len + 1 > buf->cap) { size_t new_cap = (buf->len + len + 1) * 2; char *new_data = apr_palloc(buf->pool, new_cap); memcpy(new_data, buf->data, buf->len); new_data[buf->len] = '\0'; buf->data = new_data; buf->cap = new_cap; } memcpy(buf->data + buf->len, str, len); buf->len += len; buf->data[buf->len] = '\0'; } static void testbuf_append_char(testbuf_t *buf, char c) { if (buf->len + 2 > buf->cap) { size_t new_cap = (buf->cap * 2); char *new_data = apr_palloc(buf->pool, new_cap); memcpy(new_data, buf->data, buf->len + 1); buf->data = new_data; buf->cap = new_cap; } buf->data[buf->len++] = c; buf->data[buf->len] = '\0'; } /* Mock JSON string escaping function for testing */ static void append_json_string(testbuf_t *buf, const char *str) { if (str == NULL) { return; } for (const char *p = str; *p; p++) { char c = *p; switch (c) { case '"': testbuf_append(buf, "\\\"", 2); break; case '\\': testbuf_append(buf, "\\\\", 2); break; case '\b': testbuf_append(buf, "\\b", 2); break; case '\f': testbuf_append(buf, "\\f", 2); break; case '\n': testbuf_append(buf, "\\n", 2); break; case '\r': testbuf_append(buf, "\\r", 2); break; case '\t': testbuf_append(buf, "\\t", 2); break; default: if ((unsigned char)c < 0x20) { char unicode[8]; apr_snprintf(unicode, sizeof(unicode), "\\u%04x", (unsigned char)c); testbuf_append(buf, unicode, (size_t)-1); } else { testbuf_append_char(buf, c); } break; } } } /* Test: Empty string */ static void test_json_escape_empty_string(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); append_json_string(&buf, ""); assert_string_equal(buf.data, ""); apr_pool_destroy(pool); } /* Test: Simple string without special characters */ static void test_json_escape_simple_string(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); append_json_string(&buf, "hello world"); assert_string_equal(buf.data, "hello world"); apr_pool_destroy(pool); } /* Test: String with double quotes */ static void test_json_escape_quotes(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); append_json_string(&buf, "hello \"world\""); assert_string_equal(buf.data, "hello \\\"world\\\""); apr_pool_destroy(pool); } /* Test: String with backslashes */ static void test_json_escape_backslashes(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); append_json_string(&buf, "path\\to\\file"); assert_string_equal(buf.data, "path\\\\to\\\\file"); apr_pool_destroy(pool); } /* Test: String with newlines and tabs */ static void test_json_escape_newlines_tabs(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); append_json_string(&buf, "line1\nline2\ttab"); assert_string_equal(buf.data, "line1\\nline2\\ttab"); apr_pool_destroy(pool); } /* Test: String with control characters */ static void test_json_escape_control_chars(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); /* Test with bell character (0x07) - use octal literal */ append_json_string(&buf, "test\007bell"); /* Should contain unicode escape for bell (0x07) */ assert_true(strstr(buf.data, "\\u0007") != NULL); apr_pool_destroy(pool); } /* Test: NULL string */ static void test_json_escape_null_string(void **state) { apr_pool_t *pool; testbuf_t buf; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 256); append_json_string(&buf, NULL); assert_string_equal(buf.data, ""); apr_pool_destroy(pool); } /* Test: Complex user agent string */ static void test_json_escape_user_agent(void **state) { apr_pool_t *pool; testbuf_t buf; const char *ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) \"Test\""; (void)state; apr_pool_create(&pool, NULL); testbuf_init(&buf, pool, 512); append_json_string(&buf, ua); assert_true(strstr(buf.data, "\\\"Test\\\"") != NULL); apr_pool_destroy(pool); } static int group_setup(void **state) { (void)state; return apr_initialize(); } static int group_teardown(void **state) { (void)state; apr_terminate(); return 0; } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_json_escape_empty_string), cmocka_unit_test(test_json_escape_simple_string), cmocka_unit_test(test_json_escape_quotes), cmocka_unit_test(test_json_escape_backslashes), cmocka_unit_test(test_json_escape_newlines_tabs), cmocka_unit_test(test_json_escape_control_chars), cmocka_unit_test(test_json_escape_null_string), cmocka_unit_test(test_json_escape_user_agent), }; return cmocka_run_group_tests(tests, group_setup, group_teardown); }