Correctifs de bugs critiques: - Overflow entier dans le calcul du timestamp (nanoseconds) - Validation des composantes temporelles dans format_iso8601 - Race condition mutex: échec dur pour MPM threadés (worker/event) - Rejet des espaces en tête dans parse_int_strict Nouveaux tests unitaires (38 ajoutés): - Overflow timestamp, limites ISO8601, format fixe 20 chars - Limite de taille JSON 64KB - Détection headers sensibles (blacklist) - Validation parse_int_strict - dynbuf NULL handling et strlen mode Migration packaging: - Suppression CentOS 7 (EOL) - Ajout AlmaLinux 10 (el10) - RPMs supportés: el8, el9, el10 Mise à jour CI/CD et documentation: - .gitlab-ci.yml: jobs verify pour el8/el9/el10 - architecture.yml: OS supportés à jour - 70/70 tests pass Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
267 lines
6.3 KiB
C
267 lines
6.3 KiB
C
/*
|
|
* test_json_serialization.c - Unit tests for JSON serialization
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <setjmp.h>
|
|
#include <cmocka.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <apr_pools.h>
|
|
#include <apr_strings.h>
|
|
#include <apr_time.h>
|
|
#include <apr_lib.h>
|
|
#include <apr_general.h>
|
|
|
|
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 + 1);
|
|
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);
|
|
}
|