From 0448a84286006b21454dd708e5ea987fd1752fa4 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 10 Oct 2026 14:19:27 +0200 Subject: [PATCH] tests: replace slow disk sleep with six-second clock-driven gate --- tests/Makefile.am | 3 +- tests/test_member_files_network.c | 51 ++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index ebf07885..acdfaa18 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -785,9 +785,10 @@ test_services_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) test_member_files_SOURCES = test_member_files.c test_member_files_CFLAGS = $(test_services_CFLAGS) test_member_files_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) -test_member_files_network_SOURCES = test_member_files_network.c +test_member_files_network_SOURCES = test_member_files_network.c $(test_clock_sources) test_member_files_network_CFLAGS = $(test_services_CFLAGS) test_member_files_network_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_member_files_network_LDFLAGS = $(test_clock_ldflags) test_group_avatar_SOURCES = test_group_avatar.c test_group_avatar_CFLAGS = $(test_services_CFLAGS) test_group_avatar_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) diff --git a/tests/test_member_files_network.c b/tests/test_member_files_network.c index 8daa9398..dfdf3e94 100644 --- a/tests/test_member_files_network.c +++ b/tests/test_member_files_network.c @@ -13,9 +13,11 @@ #include "../lib/debug_config.h" #include "../lib/mem.h" #include "test_utils.h" +#include "test_clock.h" #include #include #include +#include #define GROUP 5858585858585858ULL #define CHANNEL "5858585858585858" @@ -38,6 +40,10 @@ struct network_test { unsigned phase, ticks, progress; int answered, finished, transfer_status, failed; int slow_queued, slow_done, receive_pauses; + pthread_mutex_t disk_mutex; + pthread_cond_t disk_condition; + uint64_t disk_started_tb; + int disk_released, disk_error; unsigned peak_inflight; int (*saved_receive_ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t); unsigned restart_tick; @@ -53,16 +59,20 @@ static void fail(struct network_test* t, const char* why) { static void hex(const uint8_t* input, char* out) { for (unsigned i = 0; i < 32; i++) snprintf(out + 2 * i, 3, "%02x", input[i]); } static void slow_disk(void* arg) { - (void)arg; -#ifdef _WIN32 - Sleep(6000); -#else - struct timespec delay = {6, 0}; nanosleep(&delay, NULL); -#endif + struct network_test* t = arg; + pthread_mutex_lock(&t->disk_mutex); + t->disk_started_tb = get_time_tb(); + DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: disk worker blocked started_tb=%llu", + (unsigned long long)t->disk_started_tb); + while (!t->disk_released) { + int rc = pthread_cond_wait(&t->disk_condition, &t->disk_mutex); + if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files test: disk wait failed rc=%d", rc); t->disk_error = rc; break; } + } + pthread_mutex_unlock(&t->disk_mutex); } static void slow_done(void* arg, int error) { struct network_test* t = arg; - if (error) fail(t, "slow disk task rejected"); + if (error || t->disk_error) fail(t, "slow disk task rejected or wait failed"); t->slow_done = 1; } static void capture_log(int level, const char* category, const char* message) { @@ -197,6 +207,15 @@ static int downloaded(struct network_test* t) { static void tick(void* arg) { struct network_test* t = arg; t->timer = NULL; if (++t->ticks > 6000) { fail(t, "60s deadline"); return; } + // Отсчитываем полные 6с от фактической блокировки worker, не от постановки задания. + pthread_mutex_lock(&t->disk_mutex); + if (t->disk_started_tb && !t->disk_released && get_time_tb() - t->disk_started_tb >= 60000) { + t->disk_released = 1; + pthread_cond_signal(&t->disk_condition); + DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: disk released elapsed_tb=%llu receive_pauses=%d peak_inflight=%u", + (unsigned long long)(get_time_tb() - t->disk_started_tb), t->receive_pauses, t->peak_inflight); + } + pthread_mutex_unlock(&t->disk_mutex); struct ETCP_ROUTER_CONN* route = etcp_router_conn_get(t->nodes[AUTHOR], GROUP, t->ids[SERVER], ETCP_RT_ID_MEMBER_FILES); if (route->inflight_q->count > t->peak_inflight) t->peak_inflight = route->inflight_q->count; if (t->phase == 0) { @@ -304,10 +323,11 @@ again: int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG); + test_clock_init(1000); debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_TRACE); debug_set_category_level(DEBUG_CATEGORY_ETCPROUTE, DEBUG_LEVEL_DEBUG); utun_instance_set_tun_init_enabled(0); - struct network_test t = {0}; active = &t; + struct network_test t = { .disk_mutex = PTHREAD_MUTEX_INITIALIZER, .disk_condition = PTHREAD_COND_INITIALIZER }; active = &t; debug_set_log_hook(capture_log); snprintf(t.dir, sizeof(t.dir), "/tmp/utun_member_files_net_XXXXXX"); if (test_mkdtemp(t.dir)) return 1; @@ -316,28 +336,29 @@ int main(void) { int port = 39000 + (int)(get_time_tb() % 8000); for (unsigned i = 0; i < NODE_COUNT; i++) if (start(&t, i, port)) { t.failed = 1; goto stop; } if (setup(&t)) { t.failed = 1; goto stop; } - t.timer = uasync_set_timeout(t.ua, 100, &t, tick, "member_files_test"); uasync_mainloop(t.ua); + t.timer = uasync_set_timeout(t.ua, 100, &t, tick, "member_files_test"); test_clock_mainloop(t.ua); if (!t.failed) { // Stop/destroy вне callbacks: server cleanup ещё использует storage после текущего I/O. api_start(&t, 0, AUTHOR, 1, "stop.bin", t.source); api_start(&t, 1, AUTHOR, 0, "file.bin", NULL); uint64_t deadline = get_time_tb() + 50000; while (!t.nodes[SERVER]->member_files->server_transfer_count && get_time_tb() < deadline) { - uasync_poll(t.ua, 0); -#ifdef _WIN32 - Sleep(1); -#else - struct timespec delay = {0, 1000000}; nanosleep(&delay, NULL); -#endif + test_clock_poll(t.ua, 10); } if (!t.nodes[SERVER]->member_files->server_transfer_count) fail(&t, "server stream did not start before teardown"); } stop: if (t.timer) uasync_cancel_timeout(t.ua, t.timer); + // При ошибке сценария освобождаем worker до destroy, иначе drain будет ждать блокировку. + pthread_mutex_lock(&t.disk_mutex); + t.disk_released = 1; pthread_cond_signal(&t.disk_condition); + pthread_mutex_unlock(&t.disk_mutex); for (unsigned i = 0; i < NODE_COUNT; i++) if (t.nodes[i]) utun_instance_destroy(t.nodes[i]); if (!t.failed && (!t.api[0].finished || !t.api[1].finished || t.api[0].status != MF_CANCELLED || t.api[1].status != MF_CANCELLED)) t.failed = 1; uasync_destroy(t.ua, 0); + pthread_cond_destroy(&t.disk_condition); pthread_mutex_destroy(&t.disk_mutex); debug_set_log_hook(NULL); + test_clock_finish(); printf("member_files network: %s phase=%u\n", t.failed ? "FAIL" : "PASS", t.phase); return t.failed; }