Browse Source

Test radio member snapshots, updates and route withdrawal

master
evgeny 1 week ago
parent
commit
51467296c3
  1. 101
      tests/test_radio.c

101
tests/test_radio.c

@ -13,6 +13,7 @@
#include "chat_core.h"
#include "chat_sync.h"
#include "chat_event.h"
#include "chat_member.h"
#include "member_sync.h"
#include "../radio/radio.h"
#include "../routing_layer/topo_node_sqlite.h"
@ -59,6 +60,9 @@ enum rad_phase {
P_WAIT_OFF_C,
P_TALK2,
P_WAIT_RX2,
P_REENABLE_C,
P_WAIT_REENABLE_C,
P_WITHDRAW_C,
P_DONE,
};
@ -89,6 +93,63 @@ struct rad_shared {
};
static struct rad_shared g_sh;
static struct rx_evt g_rx[3];
static int g_member_radio[3][3];
static int g_member_lists[3];
static int g_member_event_bad;
/* Проверяем именно UI-события: self, прямой пир и пир за посредником. */
static void member_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len) {
if (type != CHAT_EVT_MEMBER_UPDATED && type != CHAT_EVT_MEMBER_LIST) return;
if (!data || len < 3) { g_member_event_bad = 1; return; }
int cl = data[0];
if (cl != (int)strlen(CH_ID) || len < cl + 3 || memcmp(data + 1, CH_ID, cl)) return;
int count = data[cl + 1];
int off = cl + 2;
if (type == CHAT_EVT_MEMBER_LIST) { count |= data[cl + 2] << 8; off++; }
if (len != off + count * CHAT_MEMBER_DISPLAY_SIZE) { g_member_event_bad = 1; return; }
int dst = -1;
for (int i = 0; i < 3; i++) if (inst->node_id == g_sh.nid[i]) dst = i;
if (dst < 0) { g_member_event_bad = 1; return; }
if (type == CHAT_EVT_MEMBER_LIST) g_member_lists[dst]++;
for (int i = 0; i < count; i++) {
struct chat_member_display member;
if (chat_core_deserialize_member(data + off + i * CHAT_MEMBER_DISPLAY_SIZE, &member) < 0) {
g_member_event_bad = 1; return;
}
for (int j = 0; j < 3; j++)
if (member.node_id == g_sh.nid[j]) g_member_radio[dst][j] = !!(member.flags & CHAT_MEMBER_FLAG_RADIO);
}
}
/* Полный список, одиночная строка и текущие события должны совпадать. */
static int check_member_radios(struct UTUN_INSTANCE* inst, int dst, int c_radio) {
uint8_t* wire = NULL; int count = 0;
if (chat_core_get_member_list(inst, CH_ID, &wire, &count) < 0 || count != 3 || g_member_event_bad) {
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "test: invalid member list dst=%d count=%d event_bad=%d", dst, count, g_member_event_bad);
u_free(wire); return 0;
}
int seen = 0;
for (int i = 0; i < count; i++) {
struct chat_member_display member;
chat_core_deserialize_member(wire + i * CHAT_MEMBER_DISPLAY_SIZE, &member);
for (int j = 0; j < 3; j++) {
if (member.node_id != g_sh.nid[j]) continue;
uint8_t single[CHAT_MEMBER_DISPLAY_SIZE];
int expected = j == IDX_C ? c_radio : 1;
int radio = !!(member.flags & CHAT_MEMBER_FLAG_RADIO);
if (radio != expected || g_member_radio[dst][j] != expected
|| chat_core_get_single_member(inst, CH_ID, member.node_id, single) < 0
|| !!(single[10] & CHAT_MEMBER_FLAG_RADIO) != expected) {
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "test: member radio dst=%d src=%d list=%d event=%d expected=%d",
dst, j, radio, g_member_radio[dst][j], expected);
u_free(wire); return 0;
}
seen |= 1 << j;
}
}
u_free(wire);
return seen == 7;
}
static int G_PASSED = 0, G_FAILED = 0, G_TOTAL = 0;
#define TEST(n) do { G_TOTAL++; printf(" %-58s", n); fflush(stdout); } while(0)
@ -325,6 +386,14 @@ static void rad_tick(void* arg) {
t->phase = P_TALK1;
break;
case P_TALK1:
for (int i = 0; i < 3; i++) {
if (!check_member_radios(t->inst[i], i, 1)) { t->result = 2; uasync_stop(t->ua); return; }
struct chat_member_list_req* req = u_calloc(1, sizeof(*req));
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "test: member request OOM"); t->result = 2; uasync_stop(t->ua); return; }
req->inst = t->inst[i]; strcpy(req->ch_id, CH_ID);
chat_core_request_member_list_trampoline(req);
if (g_member_lists[i] != 1) { t->result = 2; uasync_stop(t->ua); return; }
}
memset(g_rx, 0, sizeof(g_rx));
do_talk(A);
t->phase = P_WAIT_RX1;
@ -346,14 +415,40 @@ static void rad_tick(void* arg) {
}
break;
case P_TALK2:
for (int i = 0; i < 3; i++)
if (!check_member_radios(t->inst[i], i, 0)) { t->result = 2; uasync_stop(t->ua); return; }
do_talk(A);
t->phase = P_WAIT_RX2;
break;
case P_WAIT_RX2:
if (g_rx[IDX_B].frames == N_FRAMES && g_rx[IDX_B].fin_seen && g_rx[IDX_B].seq_ok
&& g_rx[IDX_C].frames == 0 && !g_rx[IDX_C].fin_seen)
t->phase = P_DONE;
t->phase = P_REENABLE_C;
break;
case P_REENABLE_C:
radio_set_active(C, gid, 1);
t->phase = P_WAIT_REENABLE_C;
break;
case P_WAIT_REENABLE_C:
if (node_radio(A, nc) && node_radio(B, nc)) t->phase = P_WITHDRAW_C;
break;
case P_WITHDRAW_C: {
struct TOPO_GROUP* group = topo_groups_find(A->topo_groups, gid);
struct TOPO_GROUP_NODE* node = topo_node_find_by_id(group, nc);
while (node && node->paths) {
struct ETCP_CONN* from = ((struct TOPO_NODEPATH*)node->paths->head)->conn;
struct TOPOMSG_WITHDRAW_PKT withdraw = { .node_id = nc };
if (topo_group_process_withdraw(group, from, (const uint8_t*)&withdraw, sizeof(withdraw)) < 0) {
t->result = 2; uasync_stop(t->ua); return;
}
node = topo_node_find_by_id(group, nc);
}
if (!radio_is_active(C, gid) || !check_member_radios(A, IDX_A, 0)) {
t->result = 2; uasync_stop(t->ua); return;
}
t->phase = P_DONE;
break;
}
case P_DONE:
t->result = 1;
uasync_stop(t->ua);
@ -397,7 +492,7 @@ int main(int argc, char** argv) {
printf("=== test_radio ===\n"); fflush(stdout);
TEST("single-thread: radio flag + talk (seq/fin) + relay + prune (3 nodes)"); {
TEST("radio: member snapshots/events + relay + off + withdraw"); {
struct UASYNC* ua = uasync_create();
if (!ua) { FAIL("uasync_create failed"); return 1; }
@ -422,6 +517,8 @@ int main(int argc, char** argv) {
utun_instance_init(t.inst[IDX_A]);
utun_instance_init(t.inst[IDX_B]);
utun_instance_init(t.inst[IDX_C]);
memset(g_member_radio, -1, sizeof(g_member_radio));
for (int i = 0; i < 3; i++) chat_event_set_handler(t.inst[i], member_event);
radio_set_frame_cb(t.inst[IDX_A], rx_frame_cb, &g_rx[IDX_A]);
radio_set_frame_cb(t.inst[IDX_B], rx_frame_cb, &g_rx[IDX_B]);

Loading…
Cancel
Save