diff --git a/tests/Makefile.am b/tests/Makefile.am index a6c64451..7e12f999 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -550,9 +550,10 @@ test_chat_sync_stress_SOURCES = test_chat_sync_stress.c test_chat_sync_stress_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_chat_sync_stress_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) -test_chat_join_SOURCES = test_chat_join.c +test_chat_join_SOURCES = test_chat_join.c $(test_clock_sources) test_chat_join_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/src/routing_layer -I$(top_srcdir)/src/transport_layer -I$(top_srcdir)/lib test_chat_join_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_chat_join_LDFLAGS = $(test_clock_ldflags) test_chat_join_e2e_SOURCES = test_chat_join_e2e.c test_chat_join_e2e_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/src/routing_layer -I$(top_srcdir)/src/transport_layer -I$(top_srcdir)/lib diff --git a/tests/test_chat_join.c b/tests/test_chat_join.c index 5f9d4f70..94d5c017 100644 --- a/tests/test_chat_join.c +++ b/tests/test_chat_join.c @@ -27,6 +27,7 @@ #include "../lib/debug_config.h" #include "../lib/mem.h" #include "test_utils.h" +#include "test_clock.h" #include #define OPENSSL_API_COMPAT 0x10100000L @@ -343,7 +344,8 @@ static void destroy_instances(int n) { static int wait_connected(int n) { if (n <= 1) return 1; int need = n * (n - 1); - for (int a = 0; a < 2000; a++) { + uint64_t deadline = get_time_tb() + 20000; + while (get_time_tb() < deadline) { int ok = 0; for (int i = 0; i < n; i++) { struct ll_entry* e = g_inst[i]->connections->head; @@ -358,13 +360,15 @@ static int wait_connected(int n) { } } if (ok >= need) return 1; - uasync_poll(g_ua, POLL_MS); + test_clock_poll(g_ua, POLL_MS); } + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "JOIN test: connection timeout nodes=%d required_links=%d budget_ms=2000", n, need); return 0; } static int wait_chat_bgp(int n) { - for (int a = 0; a < 8000; a++) { + uint64_t deadline = get_time_tb() + 80000; + while (get_time_tb() < deadline) { int ok = 1; for (int i = 0; i < n && ok; i++) { struct TOPO_GROUP* g = topo_groups_find(g_inst[i]->topo_groups, g_group_id); @@ -376,8 +380,10 @@ static int wait_chat_bgp(int n) { } } if (ok) return 1; - uasync_poll(g_ua, POLL_MS); + test_clock_poll(g_ua, POLL_MS); } + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "JOIN test: group routes timeout nodes=%d group=%llu budget_ms=8000", + n, (unsigned long long)g_group_id); return 0; } @@ -488,23 +494,37 @@ static void t_validate_pubkey(void) { } } -struct registration_result { int calls, result; }; +struct registration_result { int calls, result; uint64_t fired_tb; }; static void registered(void* arg, int result) { struct registration_result* r = arg; - r->calls++; r->result = result; + r->calls++; r->result = result; r->fired_tb = get_time_tb(); } static void poll_ms(int ms) { - uint64_t until = get_time_tb() + (uint64_t)ms * 10; - while (get_time_tb() < until) uasync_poll(g_ua, POLL_MS); + test_clock_run_for(g_ua, ms * 10); } static int wait_registered(struct registration_result* r) { uint64_t until = get_time_tb() + JOIN_REGISTER_TIMEOUT_TB + 10000; - while (!r->calls && get_time_tb() < until) uasync_poll(g_ua, POLL_MS); + while (!r->calls && get_time_tb() < until) test_clock_poll(g_ua, POLL_MS); return r->calls == 1 && r->result == CHAT_JOIN_OK; } +/* Убеждаемся, что регистрация жива до срока; затем настоящий таймер выдаёт ровно один TIMEOUT. */ +static int wait_registration_timeout(struct registration_result* r, uint64_t started) { + uint64_t before = started + JOIN_REGISTER_TIMEOUT_TB - 1000, now = get_time_tb(); + if (now < before) test_clock_run_for(g_ua, (int)(before - now)); + int pending = !r->calls && g_inst[0]->join_registrations != NULL; + wait_registered(r); + int ok = pending && r->calls == 1 && r->result == CHAT_JOIN_TIMEOUT && r->fired_tb + 10 >= started + JOIN_REGISTER_TIMEOUT_TB; + if (!ok) DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "JOIN timeout failed: pending=%d calls=%d result=%d fired_tb=%llu started_tb=%llu budget_tb=%d", + pending, r->calls, r->result, (unsigned long long)r->fired_tb, + (unsigned long long)started, JOIN_REGISTER_TIMEOUT_TB); + else DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "JOIN timeout: pending before deadline; one TIMEOUT elapsed_tb=%llu budget_tb=%d", + (unsigned long long)(r->fired_tb - started), JOIN_REGISTER_TIMEOUT_TB); + return ok; +} + static etcp_recv_fn original_join_recv; static struct ll_entry* held_ack; static struct ETCP_CONN* held_conn; @@ -573,11 +593,12 @@ static void t_registration_edges(void) { TEST("registration: missing ACK times out once, late ACK ignored"); { struct registration_result r = {0}; uint64_t key = JOIN_KEY + 12; + uint64_t started = get_time_tb(); struct chat_join_registration* req = chat_join_register_key(g_inst[0], g_group_id, g_nid[1], key, registered, &r); - wait_registered(&r); + int timed_out = wait_registration_timeout(&r, started); int had_ack = held_ack != NULL; release_ack(); poll_ms(20); - if (req && had_ack && r.calls == 1 && r.result == CHAT_JOIN_TIMEOUT && !g_inst[0]->join_registrations + if (req && had_ack && timed_out && r.calls == 1 && r.result == CHAT_JOIN_TIMEOUT && !g_inst[0]->join_registrations && !chat_join_lookup_inviter(g_inst[0], g_group_id, key)) OK(); else FAIL(); if (!r.calls && req) chat_join_cancel_registration(req); } @@ -637,11 +658,12 @@ static void t_registration_edges(void) { } TEST("registration: connection node shutdown times out without success"); { struct registration_result r = {0}; + uint64_t started = get_time_tb(); struct chat_join_registration* req = chat_join_register_key(g_inst[0], g_group_id, g_nid[1], JOIN_KEY + 27, registered, &r); chat_join_destroy(g_inst[1]); member_sync_destroy(g_inst[1]); g_inst[1]->running = 0; utun_instance_destroy(g_inst[1]); g_inst[1] = NULL; - wait_registered(&r); - if (req && r.calls == 1 && r.result == CHAT_JOIN_TIMEOUT && !g_inst[0]->join_registrations + int timed_out = wait_registration_timeout(&r, started); + if (req && timed_out && r.calls == 1 && r.result == CHAT_JOIN_TIMEOUT && !g_inst[0]->join_registrations && !chat_join_lookup_inviter(g_inst[0], g_group_id, JOIN_KEY + 27)) OK(); else FAIL(); if (!r.calls && req) chat_join_cancel_registration(req); } @@ -704,6 +726,7 @@ static void t_integration(void) { int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); + test_clock_init(1000); if (getenv("UTUN_TEST_DEBUG")) { debug_set_category_level_by_name("chat_sync", "info"); debug_set_category_level_by_name("member_sync", "info"); @@ -748,5 +771,6 @@ int main(void) { uasync_destroy(g_ua, 0); g_ua = NULL; } t_rmdir(g_tdir); + test_clock_finish(); return G_FAILED > 0 ? 1 : 0; }