diff --git a/tests/Makefile.am b/tests/Makefile.am index 207f8b64..2347e6a9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -80,7 +80,9 @@ if OS_LINUX check_PROGRAMS += \ test_reality_fallback \ test_etcp_padding \ - test_ll_queue_regressions + test_ll_queue_regressions \ + test_standby \ + test_standby_transport endif # Короткие интеграционные проверки (до 1с). @@ -116,6 +118,7 @@ check_PROGRAMS += \ test_etcp_router_tcp \ test_etcp_connect \ test_call \ + test_group_connect \ test_etcp_router \ test_dm_worker \ test_chat_join \ @@ -130,6 +133,7 @@ check_PROGRAMS += \ test_stcp_reality_rotation \ test_android_udp_log \ test_pool_corruption_log \ + test_group_connect_standby \ test_android_replies endif @@ -148,17 +152,10 @@ check_PROGRAMS += \ test_chat_join_e2e \ test_media_delivery_chat \ test_member_files_network \ - test_group_connect \ test_etcp_reconnect \ test_conn_mgr_phases \ test_socks_http_proxy -if OS_LINUX -check_PROGRAMS += \ - test_standby_transport \ - test_group_connect_standby -endif - # Длительные проверки и нагрузка. check_PROGRAMS += \ test_proxy_regressions \ @@ -168,7 +165,6 @@ check_PROGRAMS += \ if OS_LINUX check_PROGRAMS += \ - test_standby \ test_uasync_load \ test_auto_socket_dynamic endif @@ -221,16 +217,18 @@ if OS_LINUX test_stcp_LDFLAGS = -Wl,--wrap=send -Wl,--wrap=recv -Wl,--wrap=random_bytes -Wl,--wrap=u_malloc_impl -Wl,--wrap=u_calloc_impl standby_test_sources = ../tools/chatgui-android/libutun_lite/standby.c ../src/utun_instance.c ../src/transport_layer/etcp_keepalive.c standby_test_cppflags = $(AM_CPPFLAGS) -I$(top_srcdir)/src/chat -I$(top_srcdir)/tools/chatgui-android/libutun_lite -DUTUN_HAVE_STANDBY -test_standby_SOURCES = ../tools/chatgui-android/libutun_lite/tests/test_standby.c $(standby_test_sources) +test_standby_SOURCES = ../tools/chatgui-android/libutun_lite/tests/test_standby.c $(standby_test_sources) $(test_clock_sources) test_standby_CPPFLAGS = $(standby_test_cppflags) test_standby_LDADD = $(LIBUTUN) $(COMMON_LIBS) -test_standby_transport_SOURCES = test_standby_transport.c $(standby_test_sources) +test_standby_LDFLAGS = $(test_clock_ldflags) +test_standby_transport_SOURCES = test_standby_transport.c $(standby_test_sources) $(test_clock_sources) test_standby_transport_CPPFLAGS = $(standby_test_cppflags) test_standby_transport_LDADD = $(LIBUTUN) $(COMMON_LIBS) -test_standby_transport_LDFLAGS = -Wl,--wrap=etcp_encrypt_send -Wl,--wrap=uasync_set_timeout -test_group_connect_standby_SOURCES = test_group_connect.c ../src/routing_layer/topo_group_connect.c $(standby_test_sources) +test_standby_transport_LDFLAGS = $(test_clock_ldflags) -Wl,--wrap=etcp_encrypt_send -Wl,--wrap=uasync_set_timeout +test_group_connect_standby_SOURCES = test_group_connect.c ../src/routing_layer/topo_group_connect.c $(standby_test_sources) $(test_clock_sources) test_group_connect_standby_CPPFLAGS = $(standby_test_cppflags) test_group_connect_standby_LDADD = $(LIBUTUN) $(COMMON_LIBS) +test_group_connect_standby_LDFLAGS = $(test_clock_ldflags) test_android_udp_log_SOURCES = test_android_udp_log.c ../tools/chatgui-android/jni_bridge/android_udp_log.c test_android_udp_log_LDADD = $(COMMON_LIBS) test_android_udp_log_LDFLAGS = -Wl,--wrap=uasync_set_timeout -Wl,--wrap=uasync_cancel_timeout @@ -548,9 +546,10 @@ test_group_recovery_SOURCES = test_group_recovery.c test_group_recovery_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_group_recovery_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) -test_group_connect_SOURCES = test_group_connect.c +test_group_connect_SOURCES = test_group_connect.c $(test_clock_sources) test_group_connect_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_group_connect_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_group_connect_LDFLAGS = $(test_clock_ldflags) test_node_snapshot_SOURCES = test_node_snapshot.c test_node_snapshot_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib diff --git a/tests/test_clock.c b/tests/test_clock.c index 47670e34..289e068e 100644 --- a/tests/test_clock.c +++ b/tests/test_clock.c @@ -2,6 +2,7 @@ #include "../lib/debug_config.h" #include #include +#include #ifdef __linux__ #include @@ -120,3 +121,21 @@ void test_clock_finish(void) { (unsigned long long)advances, (unsigned long long)io_events); #endif } + +void test_clock_next_timer(struct UASYNC* ua) { + if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: null timer instance"); abort(); } + TimeoutEntry next; + for (;;) { + if (timeout_heap_peek(ua->timeout_heap, &next)) { + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: no timer to wait for"); abort(); + } + if (next.expiration) break; + // Отмена помечает heap-entry нулевым expiration; освобождение оставляем штатному uasync. + uasync_poll(ua, 0); + } + uint64_t now = get_time_us(), end = next.expiration * 1000; + uint64_t duration_tb = end > now ? (end - now + 99) / 100 : 0; + if (duration_tb > INT_MAX) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: timer interval overflow"); abort(); } + test_clock_run_for(ua, (int)duration_tb); + uasync_poll(ua, 0); +} diff --git a/tests/test_clock.h b/tests/test_clock.h index a0500358..55079faa 100644 --- a/tests/test_clock.h +++ b/tests/test_clock.h @@ -12,6 +12,8 @@ void test_clock_sync_wall_time(void); void test_clock_poll(struct UASYNC* ua, int timeout_tb); void test_clock_mainloop(struct UASYNC* ua); void test_clock_run_for(struct UASYNC* ua, int duration_tb); +// Дождаться ближайшего неотменённого таймера; требуется хотя бы один таймер, callbacks выполняет uasync. +void test_clock_next_timer(struct UASYNC* ua); void test_clock_finish(void); #endif diff --git a/tests/test_group_connect.c b/tests/test_group_connect.c index 954bad33..cd312ba7 100644 --- a/tests/test_group_connect.c +++ b/tests/test_group_connect.c @@ -12,6 +12,7 @@ #include "../lib/mem.h" #include "../lib/memory_pool.h" #include "../lib/debug_config.h" +#include "test_clock.h" #ifdef UTUN_HAVE_STANDBY #include "standby.h" #endif @@ -160,7 +161,7 @@ static void retry_done(void* arg) { *(int*)arg = 1; } static void wait_retry(struct fixture* f) { int done = 0; assert(uasync_set_timeout(f->ua, 12000, &done, retry_done, "connect_test_retry")); - while (!done) uasync_poll(f->ua, 100); + while (!done) test_clock_poll(f->ua, 100); poll_events(f); } @@ -231,7 +232,7 @@ static void standby_down(void) { up(&f, 0); up(&f, 1); standby_set_intervals_ms(100, 200, 0); assert(standby_set_enabled(f.inst, 1) == 0); - while (!standby_get_sleep_tb()) uasync_poll(f.ua, 100); + while (!standby_get_sleep_tb()) test_clock_poll(f.ua, 100); down(&f, 0); assert(topo_group_connect_active_count(f.group) == 1 && !peer(&f, 0) && !peer(&f, 2)); assert(standby_set_enabled(f.inst, 0) == 0); poll_events(&f); @@ -241,11 +242,11 @@ static void standby_down(void) { create(&f, CLIENT_TYPE_MOBILE, 0); standby_set_intervals_ms(100, 200, 0); assert(standby_set_enabled(f.inst, 1) == 0); - while (!standby_get_sleep_tb()) uasync_poll(f.ua, 100); + while (!standby_get_sleep_tb()) test_clock_poll(f.ua, 100); assert(topo_group_connect_init(f.group) == 0); poll_events(&f); assert(!peer(&f, 0) && !peer(&f, 1)); /* Остаток SLEEP может обнулиться раньше доставки фазового callback. */ - while (!standby_get_active_remaining_tb()) uasync_poll(f.ua, 100); + while (!standby_get_active_remaining_tb()) test_clock_poll(f.ua, 100); poll_events(&f); assert(peer(&f, 0) && peer(&f, 1)); assert(standby_set_enabled(f.inst, 0) == 0); destroy(&f); @@ -255,6 +256,8 @@ static void standby_down(void) { int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_INFO); + test_clock_init(1000); + test_clock_sync_wall_time(); utun_instance_set_tun_init_enabled(0); persisted_classification(); two_supernodes(); @@ -265,5 +268,6 @@ int main(void) { standby_down(); #endif DEBUG_INFO(DEBUG_CATEGORY_BGP, "group connect tests passed: UP, two/all supernodes, desktop/mobile fallback, retries, ownership"); + test_clock_finish(); return 0; } diff --git a/tests/test_standby_transport.c b/tests/test_standby_transport.c index 8e403a84..cde03378 100644 --- a/tests/test_standby_transport.c +++ b/tests/test_standby_transport.c @@ -7,6 +7,7 @@ #include "etcp_keepalive.h" #include "standby.h" #include "debug_config.h" +#include "test_clock.h" static struct UASYNC* ua; static struct UTUN_INSTANCE inst; @@ -125,7 +126,7 @@ static void check_transitions(void) { uint64_t timers = ua->timer_alloc_count; assert(utun_set_client_activity(&inst, 0) == 0 && ua->timer_alloc_count == timers); assert(sent[0].count == 2 && sent[1].count == 2 && sent[2].count == 2); - uasync_poll(ua, -1); assert(standby_get_sleep_tb() > 0); + test_clock_next_timer(ua); assert(standby_get_sleep_tb() > 0); for (int i = 0; i < 3; i++) { etcp_keepalive_link_ready(&links[i]); assert(sent[i].flags == KA_FLAG_SLEEP && sent[i].active == 0 && sent[i].sleep > 0 && !links[i].keepalive_timer); @@ -138,13 +139,13 @@ static void check_transitions(void) { assert(standby_get_sleep_tb() > 0 && wait_calls == 0 && sent[0].count == wait_after_packets); fail_phase_timer = 0; standby_notify_network_activity(); assert(wait_calls == 1); - uasync_poll(ua, -1); assert(standby_get_sleep_tb() > 0); + test_clock_next_timer(ua); assert(standby_get_sleep_tb() > 0); wait_after_packets = sent[0].count; void* cancelled = standby_wait(NULL, waited); assert(cancelled); standby_wait_cancel(cancelled); assert(standby_wait(NULL, waited)); - uasync_poll(ua, -1); assert(wait_calls == 2 && standby_get_active_remaining_tb() > 0); - uasync_poll(ua, -1); assert(standby_get_sleep_tb() > 0); + test_clock_next_timer(ua); assert(wait_calls == 2 && standby_get_active_remaining_tb() > 0); + test_clock_next_timer(ua); assert(standby_get_sleep_tb() > 0); wait_after_packets = sent[0].count; assert(standby_wait(NULL, waited)); links[0].ka_peer_sleeping = 1; assert(utun_set_client_activity(&inst, 1) == 0 && activity_events == 1 && wait_calls == 3); @@ -170,7 +171,7 @@ static void check_timer_failures(void) { fail_phase_timer = 0; assert(utun_set_client_activity(&inst, 0) == 0); fail_phase_timer = 1; - uasync_poll(ua, -1); + test_clock_next_timer(ua); assert(!standby_is_enabled() && inst.client_activity == CLIENT_ACTIVITY_ACTIVE && activity_events == 3); for (int i = 0; i < 3; i++) assert(!links[i].ka_my_sleeping && links[i].keepalive_timer); fail_phase_timer = 0; teardown(); @@ -179,7 +180,9 @@ static void check_timer_failures(void) { int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); + test_clock_init(1000); check_transitions(); check_transitions(); check_timer_failures(); DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "PASS: standby transport regression tests"); + test_clock_finish(); return 0; } diff --git a/tools/chatgui-android/libutun_lite/tests/test_standby.c b/tools/chatgui-android/libutun_lite/tests/test_standby.c index 0e7e32ab..9b2879c9 100644 --- a/tools/chatgui-android/libutun_lite/tests/test_standby.c +++ b/tools/chatgui-android/libutun_lite/tests/test_standby.c @@ -6,11 +6,12 @@ * пробуждение), clamp min_sleep и re-entry в wake_all_waiters. * * Интервалы задаются через standby_set_intervals_ms() (быстрые: 100-300мс). - * Время продвигается через uasync_poll(ua, -1) (до ближайшего таймера) - * и uasync_poll(ua, X) (частичное ожидание). + * Тестовые часы ждут ближайший таймер или заданный интервал; + * callbacks и смену фаз выполняет настоящий uasync. */ #include "standby.h" #include "debug_config.h" +#include "../../../../tests/test_clock.h" #include #include @@ -72,12 +73,13 @@ static void reset_standby(void) { /* продвинуть время до ближайшего таймера (фаза переключится) */ static void run_to_next_phase(void) { - uasync_poll(g_ua, -1); + test_clock_next_timer(g_ua); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); + test_clock_init(1000); g_ua = uasync_create(); if (!g_ua) { printf("uasync_create failed\n"); return 1; } @@ -133,7 +135,7 @@ int main(void) { run_to_next_phase(); /* → SLEEP */ CHECK(standby_get_sleep_tb() > 0, "wait(SLEEP): in sleep"); standby_wait(NULL, wait_cb); - uasync_poll(g_ua, -1); /* конец сна */ + run_to_next_phase(); /* конец сна */ uasync_poll(g_ua, 0); CHECK(g_wait_calls == 1, "wait(SLEEP): fired at sleep end"); standby_set_enabled(NULL, 0); @@ -144,7 +146,7 @@ int main(void) { run_to_next_phase(); /* → SLEEP */ void* h = standby_wait(NULL, wait_cb); standby_wait_cancel(h); - uasync_poll(g_ua, -1); /* конец сна */ + run_to_next_phase(); /* конец сна */ uasync_poll(g_ua, 0); CHECK(g_wait_calls == 0, "wait_cancel: cb not called"); standby_set_enabled(NULL, 0); @@ -170,7 +172,7 @@ int main(void) { CHECK(standby_get_sleep_tb() > 0, "notify(min_sleep): woke → sleep_tb 0"); CHECK(standby_get_active_remaining_tb() > 0, "notify(>min_sleep): woke → active_remaining > 0"); @@ -181,7 +183,7 @@ int main(void) { standby_set_enabled(NULL, 1); run_to_next_phase(); /* → SLEEP */ standby_wait(NULL, wait_cb); - uasync_poll(g_ua, 2000); /* +200ms → min_sleep прошёл */ + test_clock_run_for(g_ua, 2000); /* +200ms → min_sleep прошёл */ standby_notify_network_activity(); uasync_poll(g_ua, 0); CHECK(g_wait_calls == 1, "early wake: waiter fired immediately"); @@ -194,7 +196,7 @@ int main(void) { standby_set_switch_callback(switch_cb, NULL); standby_set_enabled(NULL, 1); run_to_next_phase(); /* → SLEEP */ - uasync_poll(g_ua, 2000); /* +200ms */ + test_clock_run_for(g_ua, 2000); /* +200ms */ standby_notify_network_activity(); CHECK(standby_get_sleep_tb() > 0, "clamp: min_sleep==sleep → notify ignored"); standby_set_enabled(NULL, 0); @@ -204,7 +206,7 @@ int main(void) { standby_set_enabled(NULL, 1); run_to_next_phase(); /* → SLEEP */ standby_wait(NULL, reentry_cb); - uasync_poll(g_ua, 2000); /* +200ms → min_sleep прошёл */ + test_clock_run_for(g_ua, 2000); /* +200ms → min_sleep прошёл */ standby_notify_network_activity(); /* раннее пробуждение → wake_all_waiters */ CHECK(g_reentry_calls == 1, "re-entry: wake fired waiter once (no infinite loop)"); CHECK(standby_is_enabled() == 1, "re-entry: still enabled after early wake"); @@ -238,5 +240,6 @@ int main(void) { printf("=== standby unit test: %d checks, %d failures ===\n", g_checks, g_failures); if (g_failures == 0) printf("PASS\n"); else printf("FAIL\n"); + test_clock_finish(); return g_failures ? 1 : 0; }