Browse Source

Test stream announcements after foreground wake and packet reordering

master
evgeny 4 days ago
parent
commit
fc526d16c0
  1. 17
      doc/tasks.md
  2. 17
      tests/test_etcp_server_lifecycle.c
  3. 58
      tests/test_stream_service.c

17
doc/tasks.md

@ -4,6 +4,23 @@
## Текущие задачи ## Текущие задачи
[+] **Снять устаревший сон пира при пробуждении через новый линк** — 16.10.2026:
После перезапуска SM colo2 сохранял peer_phase_timer прежнего соединения; normal keepalive нового линка
не снимал его из-за ka_peer_sleeping=0. Merkle отменял сессии, объявление трансляции задерживалось примерно на 110 с.
Normal keepalive теперь отменяет фазу/таймер всего ETCP_CONN независимо от флага линка, включая будущее засыпание.
Повторный RX не отодвигает TX; ACK сна не пробуждает пира; REINIT сам по себе не меняет режим сна.
Проверены UDP/TCP, новый линк, min_sleep=90 с, быстрый hide/show, повтор normal после потери/запоздалого SLEEP.
Сценарий desktop → промежуточный узел → mobile доставил источник в каталог за 1045–1047 мс после foreground,
отменив таймер сна 120 с; удержание ACTIVE трансляцией и освобождение последнего владельца также проверены.
Семь профильных C-тестов и 106 Android unit-тестов прошли; ядро и APK собраны.
Общий check.sh: 156/156, proxy 9/9, burst/load PASS (120 с).
Логи: /tmp/stream-wake-{server,standby,catalogue,service-final,android-tests,full-check}.log.
[ ] **Проверить исправление сна на SM через обновлённый colo2** — 16.10.2026:
Требуется установить исправление keepalive на colo2 и перезапустить ядро, затем повторить открытие приложения
во время сна и появление значка трансляции. Доступ logs читает рабочую копию, процесс и бинарник принадлежат root,
sudo отсутствует. В этой задаче colo2 и устройства не обновлялись; локальная сетевая проверка выполнена.
[ ] **Равномерный показ видео с минимальной задержкой** — 15.10.2026: [ ] **Равномерный показ видео с минимальной задержкой** — 15.10.2026:
Общий call_video_playout подключён к Android/desktop: абсолютные сроки 1.04x, A/V -50..+40мс, Общий call_video_playout подключён к Android/desktop: абсолютные сроки 1.04x, A/V -50..+40мс,
отдельная метка доступности кадра в RX port, отсутствие накопления опозданий CPU. отдельная метка доступности кадра в RX port, отсутствие накопления опозданий CPU.

17
tests/test_etcp_server_lifecycle.c

@ -284,6 +284,22 @@ static void check_peer_wake_new_link(int tcp, int active_window) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS peer wake via new link tcp=%d active_window=%d: phase cancelled, TX preserved", tcp, active_window); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS peer wake via new link tcp=%d active_window=%d: phase cancelled, TX preserved", tcp, active_window);
} }
/* Потеря первого normal и запоздалый SLEEP исправляются следующим normal без ожидания конца сна. */
static void check_peer_wake_reordered(void) {
setup(); struct ETCP_CONN* conn = ready_conn(0); struct ETCP_LINK* link = conn->links;
receive_keepalive(link, KA_FLAG_SLEEP, 0);
test_clock_run_for(inst.ua, 2000); // первый normal потерян в сети
CHECK(conn->peer_sleep_phase && conn->peer_phase_timer);
receive_keepalive(link, 0, 0);
CHECK(!conn->peer_sleep_phase && !conn->peer_phase_timer && link->keepalive_timer);
receive_keepalive(link, KA_FLAG_SLEEP, 0); // старый анонс пришёл после пробуждения
CHECK(conn->peer_sleep_phase && conn->peer_phase_timer);
test_clock_run_for(inst.ua, 2000);
receive_keepalive(link, 0, 0);
CHECK(!conn->peer_sleep_phase && !conn->peer_phase_timer && link->keepalive_timer);
teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS normal keepalive restores awake state after loss and reordered sleep");
}
/* Мягкое закрытие работает без NCD entry; KEEP_ALIVE сохраняет транспорт дольше прежних 5 секунд. */ /* Мягкое закрытие работает без NCD entry; KEEP_ALIVE сохраняет транспорт дольше прежних 5 секунд. */
static void check_graceful(void) { static void check_graceful(void) {
setup(); struct ETCP_CONN* conn = ready_conn(0); setup(); struct ETCP_CONN* conn = ready_conn(0);
@ -320,6 +336,7 @@ int main(void) {
for (int tcp = 0; tcp <= 1; tcp++) for (int active = 0; active <= 1; active++) { for (int tcp = 0; tcp <= 1; tcp++) for (int active = 0; active <= 1; active++) {
check_peer_wake_new_link(tcp, active); CHECK(u_get_allocated_count() == baseline); check_peer_wake_new_link(tcp, active); CHECK(u_get_allocated_count() == baseline);
} }
check_peer_wake_reordered(); CHECK(u_get_allocated_count() == baseline);
check_remote_down(); CHECK(u_get_allocated_count() == baseline); check_remote_down(); CHECK(u_get_allocated_count() == baseline);
check_graceful(); CHECK(u_get_allocated_count() == baseline); check_graceful(); CHECK(u_get_allocated_count() == baseline);
test_clock_finish(); return 0; test_clock_finish(); return 0;

58
tests/test_stream_service.c

@ -15,6 +15,7 @@
#include "../src/routing_layer/topo_node_sqlite.h" #include "../src/routing_layer/topo_node_sqlite.h"
#include "../src/routing_layer/route_crypto.h" #include "../src/routing_layer/route_crypto.h"
#include "../src/transport_layer/etcp.h" #include "../src/transport_layer/etcp.h"
#include "../src/transport_layer/etcp_keepalive.h"
#include "../src/transport_layer/node_conn_direct.h" #include "../src/transport_layer/node_conn_direct.h"
#include "../src/utun_instance.h" #include "../src/utun_instance.h"
#include "../lib/debug_config.h" #include "../lib/debug_config.h"
@ -1620,6 +1621,55 @@ static void test_partial_bootstrap_and_end(void) {
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] completed receive releases upstream while retaining reader and shared transport"); DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] completed receive releases upstream while retaining reader and shared transport");
} }
/* Объявление источника ждёт спящего пира; foreground возобновляет настоящий Merkle-обмен. */
static void test_sources_after_wake(void) {
debug_level_t level = g_debug_config.category_levels[DEBUG_CATEGORY_KEEPALIVE];
debug_set_category_level(DEBUG_CATEGORY_KEEPALIVE, DEBUG_LEVEL_INFO);
CHECK(utun_set_client_activity(nodes[2], 1) == 0);
/* Каналы стенда внесены напрямую в БД после init; ждём первого обновления кеша chat_sync (5 с). */
test_clock_run_for(ua, 51000);
CHECK(utun_set_client_activity(nodes[2], 0) == 0);
struct ETCP_CONN *conn = instance_find_conn(nodes[0], nodes[2]->node_id); CHECK(conn && conn->links_up);
uint64_t deadline = get_time_tb() + 10000;
while (!conn->peer_sleep_phase && get_time_tb() < deadline) test_clock_poll(ua, 100);
CHECK(conn->peer_sleep_phase);
/* Linux-стенд без Android duty-cycle: задаём полученный от мобильного пира тайминг 120 с. */
struct ETCP_LINK *link = conn->links;
while (link && (!link->initialized || !link->ka_peer_sleeping)) link = link->next;
CHECK(link);
struct ETCP_DGRAM *pkt = memory_pool_alloc(nodes[0]->pkt_pool); CHECK(pkt);
memset(pkt, 0, sizeof(*pkt) + 8); pkt->link = link; pkt->data_len = 8;
pkt->data[0] = ETCP_KEEPALIVE; pkt->data[1] = KA_FLAG_SLEEP;
pkt->data[2] = 0x30; pkt->data[3] = 0x75; // period=30000 мс
pkt->data[6] = 0xb0; pkt->data[7] = 4; // sleep=1200 * 0.1 с
CHECK(etcp_keepalive_on_recv(pkt) == 1 && conn->peer_phase_timer);
struct STREAM_HANDLE *publisher = NULL;
CHECK(stream_publish_open(nodes[1], GROUP1, NULL, &publisher) == STREAM_OK);
struct stream_info info; CHECK(stream_get_info(publisher, &info) == STREAM_OK);
test_clock_run_for(ua, 5000);
struct stream_member_state state;
CHECK(stream_member_load(nodes[2], GROUP1, nodes[1]->node_id, &state, NULL) == STREAM_OK && !state.published);
CHECK(conn->peer_sleep_phase && conn->peer_phase_timer);
uint64_t started = get_time_tb();
CHECK(utun_set_client_activity(nodes[2], 1) == 0);
wait_state(2, 1, GROUP1, info.descriptor.source_epoch, 0);
CHECK(!conn->peer_sleep_phase && !conn->peer_phase_timer && get_time_tb() - started < 30000);
char *catalog = NULL, id[64];
CHECK(stream_sources_json(nodes[2], GROUP1, &catalog) == STREAM_OK);
snprintf(id, sizeof(id), "\"id\":\"%llu\"", (unsigned long long)nodes[1]->node_id);
CHECK(strstr(catalog, id)); u_free(catalog);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] source catalogue after foreground: elapsed_ms=%llu, cancelled sleep=120000ms",
(unsigned long long)((get_time_tb() - started) / 10));
stream_close(publisher);
deadline = get_time_tb() + 150000;
do {
test_clock_poll(ua, 100);
CHECK(stream_member_load(nodes[2], GROUP1, nodes[1]->node_id, &state, NULL) == STREAM_OK);
} while (state.published && get_time_tb() < deadline);
CHECK(!state.published);
debug_set_category_level(DEBUG_CATEGORY_KEEPALIVE, level);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_DEBUG); debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_DEBUG);
@ -1685,9 +1735,10 @@ int main(int argc, char **argv) {
CHECK(member_sync_start(nodes[0], nodes[i]->node_id, "710001", NULL, NULL) == 0); CHECK(member_sync_start(nodes[0], nodes[i]->node_id, "710001", NULL, NULL) == 0);
CHECK(member_sync_start(nodes[i], nodes[0]->node_id, "710001", NULL, NULL) == 0); CHECK(member_sync_start(nodes[i], nodes[0]->node_id, "710001", NULL, NULL) == 0);
} }
if (argc == 2 && (!strcmp(argv[1], "--relay-only") || !strcmp(argv[1], "--lifecycle-only"))) { if (argc == 2 && (!strcmp(argv[1], "--relay-only") || !strcmp(argv[1], "--lifecycle-only") || !strcmp(argv[1], "--wake-only"))) {
if (!strcmp(argv[1], "--relay-only")) test_supernode_relay(); if (!strcmp(argv[1], "--relay-only")) test_supernode_relay();
else test_partial_bootstrap_and_end(); else if (!strcmp(argv[1], "--lifecycle-only")) test_partial_bootstrap_and_end();
else test_sources_after_wake();
for (unsigned i = 0; i < 3; i++) topo_group_peer_close(connections[i]); for (unsigned i = 0; i < 3; i++) topo_group_peer_close(connections[i]);
for (unsigned i = 0; i < 4; i++) { stream_set_event_callback(nodes[i], NULL, NULL); utun_instance_destroy(nodes[i]); } for (unsigned i = 0; i < 4; i++) { stream_set_event_callback(nodes[i], NULL, NULL); utun_instance_destroy(nodes[i]); }
uasync_destroy(ua, 1); test_clock_finish(); uasync_destroy(ua, 1); test_clock_finish();
@ -1697,9 +1748,10 @@ int main(int argc, char **argv) {
u_report_unfreed_blocks(); u_report_unfreed_blocks();
} }
CHECK(u_get_allocated_count() == allocations); CHECK(u_get_allocated_count() == allocations);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] isolated supernode relay checks complete, no tracked allocations remain"); DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] isolated stream checks complete, no tracked allocations remain");
return 0; return 0;
} }
test_sources_after_wake();
test_profile_atomicity(); test_profile_atomicity();
stream_set_event_callback(nodes[0], on_stream, NULL); stream_set_event_callback(nodes[1], on_stream, NULL); stream_set_event_callback(nodes[0], on_stream, NULL); stream_set_event_callback(nodes[1], on_stream, NULL);
struct STREAM_HANDLE *publisher = NULL, *publisher2 = NULL, *other_group = NULL, *mobile = NULL, *desktop = NULL; struct STREAM_HANDLE *publisher = NULL, *publisher2 = NULL, *other_group = NULL, *mobile = NULL, *desktop = NULL;

Loading…
Cancel
Save