Test broadcast UUID lifetime and callback registration contracts
This commit is contained in:
1 parent
484b620016
commit
56d5fcd75a
2 files changed
+124
No files matched your search
@@ -100,6 +100,7 @@ check_PROGRAMS += \
|
||||
test_stun_nat \
|
||||
test_reality_fallback \
|
||||
test_etcp_padding \
|
||||
test_broadcast_cache \
|
||||
test_ll_queue_regressions \
|
||||
test_standby \
|
||||
test_standby_transport \
|
||||
@@ -301,6 +302,9 @@ async_regression_ldflags = -Wl,--wrap=u_malloc_impl -Wl,--wrap=u_calloc_impl -Wl
|
||||
test_etcp_padding_SOURCES = test_etcp_padding.c
|
||||
test_etcp_padding_LDADD = $(LIBUTUN) $(COMMON_LIBS)
|
||||
test_etcp_padding_LDFLAGS = -Wl,--wrap=uasync_set_timeout -Wl,--wrap=socket_sendto
|
||||
test_broadcast_cache_SOURCES = test_broadcast_cache.c
|
||||
test_broadcast_cache_LDADD = $(LIBUTUN) $(COMMON_LIBS)
|
||||
test_broadcast_cache_LDFLAGS = -Wl,--wrap=clock_gettime -Wl,--wrap=uasync_set_timeout
|
||||
test_uasync_regressions_SOURCES = test_uasync_regressions.c $(async_regression_sources)
|
||||
test_uasync_regressions_LDADD = $(COMMON_LIBS)
|
||||
test_uasync_regressions_LDFLAGS = $(async_regression_ldflags)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/* TTL и callbacks broadcast: реальные очереди/таймеры, управляемая шкала времени. */
|
||||
#define _GNU_SOURCE
|
||||
#include "utun_instance.h"
|
||||
#include "topo_group.h"
|
||||
#include "broadcast.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "FAIL %s:%d: %s\n", __func__, __LINE__, #c); exit(1); } } while (0)
|
||||
|
||||
/* Вход приёма, используемый ETCP-диспетчером внутри broadcast.c. */
|
||||
void broadcast_recv(struct TOPO_GROUP*, struct ETCP_CONN*, const uint8_t*, size_t);
|
||||
|
||||
static uint64_t now_us = 1000000;
|
||||
static int fail_timer, calls, order[16], order_count;
|
||||
|
||||
int __real_clock_gettime(clockid_t id, struct timespec* ts);
|
||||
int __wrap_clock_gettime(clockid_t id, struct timespec* ts) {
|
||||
if (id != CLOCK_BOOTTIME) return __real_clock_gettime(id, ts);
|
||||
ts->tv_sec = now_us / 1000000; ts->tv_nsec = now_us % 1000000 * 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* __real_uasync_set_timeout(struct UASYNC*, int, void*, timeout_callback_t, const char*);
|
||||
void* __wrap_uasync_set_timeout(struct UASYNC* ua, int tb, void* arg, timeout_callback_t cb, const char* name) {
|
||||
if (fail_timer && name && strcmp(name, "broadcast_cleanup_timer") == 0) return NULL;
|
||||
return __real_uasync_set_timeout(ua, tb, arg, cb, name);
|
||||
}
|
||||
|
||||
static void received(const uint8_t* uuid, const uint8_t* data, uint16_t len, void* arg) {
|
||||
CHECK(uuid && len == 3 && data[0] == 1 && data[1] == 0 && data[2] == 2);
|
||||
CHECK(order_count < (int)(sizeof(order) / sizeof(order[0])));
|
||||
order[order_count++] = (int)(intptr_t)arg;
|
||||
calls++;
|
||||
}
|
||||
|
||||
static void receive_id(struct TOPO_GROUP* group, uint8_t id) {
|
||||
uint8_t payload[BROADCAST_UUID_SIZE + 2 + 3] = {0};
|
||||
payload[0] = id; payload[BROADCAST_UUID_SIZE] = 3;
|
||||
payload[BROADCAST_UUID_SIZE + 2] = 1; payload[BROADCAST_UUID_SIZE + 4] = 2;
|
||||
order_count = 0;
|
||||
broadcast_recv(group, NULL, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
static void cleanup_tick(struct UASYNC* ua) {
|
||||
now_us += (uint64_t)BROADCAST_CLEANUP_INTERVAL_TB * 100;
|
||||
uasync_poll(ua, 0);
|
||||
}
|
||||
|
||||
static void test_ttl(struct UASYNC* ua, struct TOPO_GROUP* group) {
|
||||
calls = 0;
|
||||
CHECK(broadcast_init(group) == 0);
|
||||
broadcast_add_cbk(group, received, (void*)(intptr_t)1);
|
||||
receive_id(group, 1); CHECK(calls == 1);
|
||||
receive_id(group, 1); CHECK(calls == 1);
|
||||
cleanup_tick(ua); /* 60с: исходный UUID ещё свежий. */
|
||||
receive_id(group, 1); CHECK(calls == 1);
|
||||
receive_id(group, 2); CHECK(calls == 2);
|
||||
cleanup_tick(ua); /* 120с: равенство TTL ещё не удаляет запись. */
|
||||
receive_id(group, 1); CHECK(calls == 2);
|
||||
receive_id(group, 2); CHECK(calls == 2);
|
||||
cleanup_tick(ua); /* 180с: первый UUID устарел, второму ровно TTL. */
|
||||
receive_id(group, 2); CHECK(calls == 2);
|
||||
receive_id(group, 1); CHECK(calls == 3);
|
||||
cleanup_tick(ua);
|
||||
receive_id(group, 1); CHECK(calls == 3);
|
||||
receive_id(group, 2); CHECK(calls == 4);
|
||||
broadcast_destroy(group); CHECK(!group->broadcast);
|
||||
puts("PASS broadcast TTL: fresh, exact boundary, expired, mixed ages");
|
||||
}
|
||||
|
||||
static void test_callbacks(struct TOPO_GROUP* group) {
|
||||
calls = 0;
|
||||
CHECK(broadcast_init(group) == 0);
|
||||
CHECK(broadcast_init(group) == -1);
|
||||
broadcast_add_cbk(group, received, (void*)(intptr_t)1);
|
||||
broadcast_add_cbk(group, received, (void*)(intptr_t)2);
|
||||
broadcast_add_cbk(group, received, (void*)(intptr_t)2);
|
||||
receive_id(group, 3);
|
||||
CHECK(calls == 3 && order_count == 3 && order[0] == 2 && order[1] == 2 && order[2] == 1);
|
||||
broadcast_remove_cbk(group, received, (void*)(intptr_t)2);
|
||||
receive_id(group, 4);
|
||||
CHECK(calls == 5 && order_count == 2 && order[0] == 2 && order[1] == 1);
|
||||
broadcast_remove_cbk(group, received, (void*)(intptr_t)2);
|
||||
broadcast_remove_cbk(group, received, (void*)(intptr_t)2);
|
||||
receive_id(group, 5); CHECK(calls == 6 && order_count == 1 && order[0] == 1);
|
||||
const uint8_t data[] = {1, 0, 2};
|
||||
CHECK(broadcast_send(group, data, sizeof(data)) == 0 && calls == 6);
|
||||
CHECK(broadcast_send(group, data, 0) == 0 && calls == 6);
|
||||
CHECK(broadcast_send(group, NULL, 0) == -1);
|
||||
CHECK(broadcast_send(group, data, BROADCAST_MAX_DATA + 1) == -1);
|
||||
broadcast_destroy(group); broadcast_destroy(group);
|
||||
puts("PASS broadcast callbacks: duplicates, order, removal, no local echo");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
|
||||
size_t baseline = u_get_allocated_count();
|
||||
struct UASYNC* ua = uasync_create(); CHECK(ua);
|
||||
struct UTUN_INSTANCE inst = {.ua = ua};
|
||||
struct TOPO_GROUP group = {.instance = &inst, .group_id = 42};
|
||||
size_t before = u_get_allocated_count();
|
||||
fail_timer = 1;
|
||||
CHECK(broadcast_init(&group) == -1 && !group.broadcast);
|
||||
CHECK(u_get_allocated_count() == before);
|
||||
fail_timer = 0;
|
||||
test_ttl(ua, &group);
|
||||
test_callbacks(&group);
|
||||
CHECK(broadcast_init(&group) == 0);
|
||||
fail_timer = 1; cleanup_tick(ua);
|
||||
broadcast_destroy(&group);
|
||||
uasync_destroy(ua, 0);
|
||||
CHECK(u_get_allocated_count() == baseline);
|
||||
puts("PASS broadcast timer failures and resource cleanup");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user