You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
5.6 KiB
123 lines
5.6 KiB
// test_media_download_timeout.c — интеграционный тест watchdog-таймера скачивания |
|
// |
|
// Проверяет, что реальный uasync-таймер простоя срабатывает: |
|
// - загрузка с недостижимым автором → watchdog (100ms) → re-QUERY → суперноды |
|
// исчерпаны → done_cb(-1). |
|
// - загрузка с держателем, который «завис» (нет чанков) → фейловер → 3 попытки → |
|
// done_cb(-1). |
|
// |
|
// Таймауты укорочены (dl_stall_timeout_tb=100ms), фейковое время не используется — |
|
// гоняем реальный цикл uasync_poll. |
|
|
|
#include "media_delivery.h" |
|
#include "media_delivery_proto.h" |
|
#include "media_download.h" |
|
#include "media_index.h" |
|
#include "../lib/u_async.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
#include "../src/utun_instance.h" |
|
#include <sqlite3.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
|
|
static int G_PASSED = 0, G_FAILED = 0, G_TOTAL = 0; |
|
#define TEST(n) do { G_TOTAL++; printf(" %-58s", n); fflush(stdout); } while(0) |
|
#define OK() do { G_PASSED++; printf("OK\n"); } while(0) |
|
#define FAIL(f,...) do { G_FAILED++; printf("FAIL: " f "\n", ##__VA_ARGS__); } while(0) |
|
|
|
static struct UASYNC* g_ua = NULL; |
|
static int g_done = 0, g_err = 0; |
|
|
|
static void make_uuid(uint8_t b[16]) { for (int i = 0; i < 16; i++) b[i] = (uint8_t)(rand() & 0xFF); } |
|
|
|
static void done_cb(void* a, int e) { (void)a; g_done = 1; g_err = e; printf(" [done_cb err=%d]\n", e); } |
|
|
|
static struct UTUN_INSTANCE* make_minimal(void) { |
|
struct UTUN_INSTANCE* inst = u_calloc(1, sizeof(*inst)); |
|
if (!inst) return NULL; |
|
inst->ua = g_ua; |
|
inst->node_id = 0xDEADBEEFDEADBEEFULL; |
|
sqlite3* db = NULL; sqlite3_open(":memory:", &db); |
|
inst->topo_sqlite_db = db; |
|
for (int i = 0; i < 32; i++) inst->my_ed25519_privkey[i] = (uint8_t)(rand() & 0xFF); |
|
memset(inst->my_ed25519_pubkey, 0xDD, 32); |
|
inst->md.inst = inst; inst->md.db = db; inst->md.self_node_id = inst->node_id; inst->md.initialized = 1; |
|
inst->md.dl_stall_timeout_tb = 1000; /* 100ms */ |
|
inst->md.dl_max_attempts = 3; |
|
return inst; |
|
} |
|
|
|
static void make_result(struct media_index_result* r, int num_blocks) { |
|
memset(r, 0, sizeof(*r)); |
|
make_uuid(r->media_id); |
|
r->num_blocks = num_blocks; r->file_size = 100 * num_blocks; r->block_size = 100; |
|
r->block_ids = u_calloc((size_t)num_blocks, 16); |
|
r->block_sigs = u_calloc((size_t)num_blocks, 64); |
|
for (int i = 0; i < num_blocks; i++) make_uuid(r->block_ids + i * 16); |
|
} |
|
|
|
static int poll_until_done(int max_ms) { |
|
int el = 0; |
|
while (!g_done && el < max_ms) { uasync_poll(g_ua, 5); el += 5; } |
|
return g_done; |
|
} |
|
|
|
int main(void) { |
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); |
|
if (getenv("UTUN_TEST_DEBUG")) { debug_set_category_level_by_name("media", "trace"); debug_set_category_level_by_name("general", "info"); } |
|
srand(1); |
|
printf("=== test_media_download_timeout ===\n"); |
|
g_ua = uasync_create(); |
|
|
|
/* ── 1. недостижимый автор → watchdog → done_cb(-1) ── */ |
|
TEST("unreachable author → watchdog → fail"); { |
|
struct UTUN_INSTANCE* inst = make_minimal(); |
|
struct media_index_result r; make_result(&r, 1); |
|
g_done = 0; g_err = 0; |
|
int rc = media_download_start(inst, 0x1234, &r, "/tmp/utun_mdt_1.bin", "/tmp", 0xBADF00D00000001ULL, done_cb, NULL, NULL, NULL); |
|
if (rc != 0) { FAIL("start rc=%d", rc); } |
|
else if (poll_until_done(3000) && g_err != 0) OK(); |
|
else FAIL("done=%d err=%d", g_done, g_err); |
|
|
|
media_index_result_free(&r); |
|
/* нет активной загрузки после finish */ |
|
if (inst->md.downloads) { struct ll_entry* e = inst->md.downloads->head; int n = 0; while (e) { n++; e = e->next; } if (n != 0) FAIL("downloads leftover=%d", n); } |
|
sqlite3_close(inst->topo_sqlite_db); u_free(inst); |
|
} |
|
|
|
/* ── 2. зависший держатель → фейловер → re-QUERY → fail ── */ |
|
TEST("stalled holder → failover → fail"); { |
|
struct UTUN_INSTANCE* inst = make_minimal(); |
|
struct media_index_result r; make_result(&r, 1); |
|
g_done = 0; g_err = 0; |
|
int rc = media_download_start(inst, 0x1234, &r, "/tmp/utun_mdt_2.bin", "/tmp", 0xBADF00D00000002ULL, done_cb, NULL, NULL, NULL); |
|
if (rc != 0) { |
|
FAIL("start rc=%d", rc); |
|
} else { |
|
/* имитируем QUERY_RESP: автор сообщает, что держит блок */ |
|
struct media_pkt_query_resp_entry e; |
|
memset(&e, 0, sizeof(e)); |
|
e.node_id = 0xBADF00D00000002ULL; |
|
memcpy(e.block_id, r.block_ids, 16); |
|
uint8_t pkt[3 + sizeof(e)]; |
|
pkt[0] = MEDIA_SUBCMD_QUERY_RESP; |
|
uint16_t ne = 1; memcpy(pkt + 1, &ne, 2); |
|
memcpy(pkt + 3, &e, sizeof(e)); |
|
media_download_handle_query_resp(inst, pkt, sizeof(pkt)); |
|
|
|
/* держатель «завис» — чанков нет, watchdog сделает фейловер → попытки → fail */ |
|
if (poll_until_done(5000) && g_err != 0) OK(); |
|
else FAIL("done=%d err=%d (expected error after 3 attempts)", g_done, g_err); |
|
} |
|
|
|
media_index_result_free(&r); |
|
sqlite3_close(inst->topo_sqlite_db); u_free(inst); |
|
} |
|
|
|
printf("\n%d/%d passed, %d failed\n", G_PASSED, G_TOTAL, G_FAILED); |
|
if (g_ua) { uasync_destroy(g_ua, 0); g_ua = NULL; } |
|
return G_FAILED > 0 ? 1 : 0; |
|
}
|
|
|