- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
197 lines
6.5 KiB
C
197 lines
6.5 KiB
C
// test_serialize.c - Unit tests for serialize library (with linked list + error cases)
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include "../lib/serialize.h"
|
|
|
|
#define TEST(name) do { \
|
|
printf("TEST: %-40s ", name); fflush(stdout); \
|
|
} while(0)
|
|
#define PASS() do { puts("PASS"); } while(0)
|
|
#define FAIL(msg) do { printf("FAIL: %s\n", msg); exit(1); } while(0)
|
|
|
|
#pragma pack(push, 1)
|
|
typedef struct TestNode {
|
|
uint32_t value;
|
|
struct TestNode* next;
|
|
} TestNode;
|
|
|
|
typedef struct {
|
|
uint8_t id;
|
|
uint16_t port;
|
|
uint32_t ip;
|
|
uint64_t big_val;
|
|
int8_t signed_val;
|
|
int32_t signed32;
|
|
uint8_t name_len;
|
|
char* name;
|
|
uint8_t addr_cnt;
|
|
uint8_t* addrs;
|
|
TestNode* list; // linked list (type 6)
|
|
} TestStruct;
|
|
#pragma pack(pop)
|
|
|
|
static const struct SerializeField test_fields[] = {
|
|
{offsetof(TestStruct, id), 1, SERIALIZE_TYPE_FIXED, 0},
|
|
{offsetof(TestStruct, port), 2, SERIALIZE_TYPE_FIXED, 0},
|
|
{offsetof(TestStruct, ip), 4, SERIALIZE_TYPE_FIXED, 0},
|
|
{offsetof(TestStruct, big_val), 8, SERIALIZE_TYPE_FIXED, 0},
|
|
{offsetof(TestStruct, signed_val),1, SERIALIZE_TYPE_FIXED, 0},
|
|
{offsetof(TestStruct, signed32), 4, SERIALIZE_TYPE_FIXED, 0},
|
|
{offsetof(TestStruct, name), 1, SERIALIZE_TYPE_ARRAY_U8, offsetof(TestStruct, name_len)},
|
|
{offsetof(TestStruct, addrs), 1, SERIALIZE_TYPE_ARRAY_U8, offsetof(TestStruct, addr_cnt)},
|
|
{offsetof(TestStruct, list), sizeof(TestNode), SERIALIZE_TYPE_LINKED, offsetof(TestNode, next)}
|
|
};
|
|
|
|
static const struct SerializeSchema test_schema = {
|
|
.field_count = sizeof(test_fields) / sizeof(test_fields[0]),
|
|
.struct_size = sizeof(TestStruct),
|
|
.max_size = 0,
|
|
.header_len = 4,
|
|
.fields = test_fields
|
|
};
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
static void test_alloc_roundtrip(void) {
|
|
uint8_t* buf = NULL;
|
|
size_t len;
|
|
|
|
TestStruct orig = {
|
|
.id = 1,
|
|
.port = 443,
|
|
.ip = 0x01020304,
|
|
.big_val = 100,
|
|
.signed_val = 10,
|
|
.signed32 = 5000,
|
|
.list = NULL
|
|
};
|
|
orig.name = strdup("hello");
|
|
orig.name_len = 5;
|
|
orig.addrs = u_malloc(8);
|
|
orig.addrs[0] = 192; orig.addrs[1] = 168; orig.addrs[2] = 1; orig.addrs[3] = 1;
|
|
orig.addrs[4] = 10; orig.addrs[5] = 0; orig.addrs[6] = 0; orig.addrs[7] = 1;
|
|
orig.addr_cnt = 8;
|
|
|
|
// === linked list ===
|
|
TestNode* n1 = u_malloc(sizeof(TestNode));
|
|
TestNode* n2 = u_malloc(sizeof(TestNode));
|
|
n1->value = 100;
|
|
n1->next = n2;
|
|
n2->value = 200;
|
|
n2->next = NULL;
|
|
orig.list = n1;
|
|
|
|
uint8_t header[] = {0x01, 0x02, 0x03, 0x04};
|
|
TEST("serialize_encode with header + linked list");
|
|
int r = serialize_encode(&orig, &test_schema, header, &buf, &len);
|
|
if (r != SERIALIZE_ERR_OK) FAIL("serialize_encode failed");
|
|
PASS();
|
|
|
|
TestStruct* restored = NULL;
|
|
TEST("serialize_decode (data without header)");
|
|
uint8_t* data_ptr = buf + test_schema.header_len;
|
|
r = serialize_decode(data_ptr, len - test_schema.header_len, &test_schema, (void**)&restored);
|
|
if (r != SERIALIZE_ERR_OK) FAIL("serialize_decode failed");
|
|
PASS();
|
|
|
|
TEST("check restored not NULL");
|
|
if (!restored) FAIL("restored is NULL");
|
|
PASS();
|
|
|
|
TEST("check fixed fields");
|
|
if (restored->id != orig.id || restored->port != orig.port) FAIL("fixed fields mismatch");
|
|
PASS();
|
|
|
|
TEST("check asciiz");
|
|
if (!restored->name || strcmp(restored->name, "hello") != 0) FAIL("name mismatch");
|
|
PASS();
|
|
|
|
TEST("check array");
|
|
if (!restored->addrs || restored->addrs[0] != 192) FAIL("addrs mismatch");
|
|
PASS();
|
|
|
|
TEST("check name_len / addr_cnt");
|
|
if (restored->name_len != 5 || restored->addr_cnt != 8) FAIL("len fields mismatch");
|
|
PASS();
|
|
|
|
TEST("check linked list");
|
|
if (!restored->list || restored->list->value != 100) FAIL("list head mismatch");
|
|
if (!restored->list->next || restored->list->next->value != 200) FAIL("list next mismatch");
|
|
if (restored->list->next->next != NULL) FAIL("list tail not NULL");
|
|
PASS();
|
|
|
|
TEST("serialize_free (full structure with list)");
|
|
serialize_free(&test_schema, (void**)&restored);
|
|
if (restored) FAIL("ptr should be NULL after free");
|
|
PASS();
|
|
|
|
u_free(buf);
|
|
|
|
// очистка orig
|
|
free(orig.name);
|
|
u_free(orig.addrs);
|
|
u_free(n1);
|
|
u_free(n2);
|
|
}
|
|
|
|
static void test_decode_error(void) {
|
|
uint8_t* buf = NULL;
|
|
size_t len;
|
|
|
|
TestStruct orig = { .id = 42, .port = 1234, .list = NULL };
|
|
orig.name = strdup("error_test");
|
|
orig.name_len = 10;
|
|
orig.addrs = u_malloc(2);
|
|
orig.addrs[0] = 0xAA; orig.addrs[1] = 0xBB;
|
|
orig.addr_cnt = 2;
|
|
|
|
uint8_t header[] = {0xAA, 0xBB, 0xCC, 0xDD};
|
|
int r = serialize_encode(&orig, &test_schema, header, &buf, &len);
|
|
if (r != SERIALIZE_ERR_OK) FAIL("encode for error test failed");
|
|
PASS(); // не выводим, просто подготовка
|
|
|
|
TestStruct* restored = NULL;
|
|
uint8_t* data_ptr = buf + test_schema.header_len;
|
|
size_t short_len = (len - test_schema.header_len) / 2; // намеренно неполный буфер
|
|
|
|
TEST("serialize_decode on short buffer (should free internally)");
|
|
r = serialize_decode(data_ptr, short_len, &test_schema, (void**)&restored);
|
|
if (r != SERIALIZE_ERR_BUF) FAIL("expected SERIALIZE_ERR_BUF on truncated data");
|
|
if (restored != NULL) FAIL("restored must be NULL after error (free was called)");
|
|
PASS();
|
|
|
|
u_free(buf);
|
|
free(orig.name);
|
|
u_free(orig.addrs);
|
|
}
|
|
|
|
static void test_free_null_and_double(void) {
|
|
TestStruct* p = NULL;
|
|
TEST("serialize_free on NULL pointer");
|
|
serialize_free(&test_schema, (void**)&p); // должно ничего не делать
|
|
if (p != NULL) FAIL("NULL pointer was changed");
|
|
PASS();
|
|
|
|
// уже освобождённая структура
|
|
TestStruct* dummy = u_malloc(sizeof(TestStruct));
|
|
memset(dummy, 0, sizeof(TestStruct));
|
|
TEST("serialize_free after already freed");
|
|
serialize_free(&test_schema, (void**)&dummy);
|
|
if (dummy != NULL) FAIL("pointer not set to NULL");
|
|
PASS();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
int main(void) {
|
|
printf("=== Serialize Library Tests (linked list + error handling) ===\n\n");
|
|
|
|
test_alloc_roundtrip();
|
|
test_decode_error();
|
|
test_free_null_and_double();
|
|
|
|
printf("\n=== All tests passed ===\n");
|
|
return 0;
|
|
}
|