Test retry waiting for another available ETCP path

This commit is contained in:
evgeny committed 2026-10-24 09:34:21 +02:00
1 parent ff2860ffbc
commit 412b9bc713
1 file changed
+67
+67
View File
@@ -773,6 +773,72 @@ static void retry_rotation(void) {
ack_all(); teardown();
}
/* Готовый прежний путь не обходит занятую UP-альтернативу; ждём её события без новой попытки/RTO. */
static void retry_waits_for_alternative(void) {
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
struct INFLIGHT_PACKET* packet = (struct INFLIGHT_PACKET*)conn->input_send_q->head;
CHECK(packet && packet->last_link == a && !packet->active_attempts && packet->attempt_count == 1);
b->shaper_timer = uasync_set_timeout(ua, 300, b, shaper_ready, "test_retry_alternative"); CHECK(b->shaper_timer);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Retry must wait: seq=%u previous=%u ready=1 alternative=%u pacing_blocked=1",
packet->seq, a->local_link_id, b->local_link_id);
CHECK(!request());
uint64_t allocated = ua->timer_alloc_count;
test_clock_run_for(ua, 5);
CHECK(!sends && packet->attempt_count == 1 && !packet->active_attempts && !a->retrans_timer && !b->retrans_timer);
CHECK(packet->state == INFLIGHT_STATE_WAIT_SEND && packet->last_link == a && ua->timer_alloc_count == allocated);
CHECK(etcp_tx_queue_ack(a, 99, etcp_tx_attempt_id(99, 1)) == 0);
struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->link == a && !pkt->payload_len && !pkt->attempt);
memory_pool_free(inst.pkt_pool, pkt);
a->burst_active = 1; a->burst_count = 1;
pkt = request(); CHECK(pkt && pkt->link == a && pkt->burst_packet && !pkt->payload_len && !pkt->attempt);
memory_pool_free(inst.pkt_pool, pkt);
CHECK(packet->last_link == a && packet->attempt_count == 1 && !packet->active_attempts);
wait_sends(1); CHECK(last_sent_link == b && packet->last_link == b && packet->attempt_count == 2);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Retry resumed: seq=%u selected=%u attempts=%u", packet->seq, b->local_link_id, packet->attempt_count);
ack_all(); teardown();
setup(); fill(a, 1, 100); fill(b, 1, 100); b->data_rtt_us = 1000000; b->inflight_lim_bytes = 100;
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
packet = (struct INFLIGHT_PACKET*)conn->input_send_q->head;
CHECK(packet && packet->last_link == a && !request());
CHECK(a->inflight_bytes == 0 && b->inflight_bytes == 100 && !packet->active_attempts && packet->attempt_count == 1);
ack(2); wait_sends(1); CHECK(last_sent_link == b && packet->last_link == b);
ack_all(); teardown();
}
/* Потеря последней альтернативы будит отправителя; поздний ACK убирает ожидающий повтор. */
static void retry_alternative_lifecycle(void) {
setup(); fill(a, 1, 100); b->inflight_lim_bytes = 1;
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
CHECK(!request() && !conn->tx_token);
b->link_status = 0; etcp_on_link_down(conn, b);
CHECK(conn->tx_token); uasync_drain_immediate(ua);
CHECK(sends == 1 && last_sent_link == a); ack_all(); teardown();
setup(); fill(a, 1, 100); b->inflight_lim_bytes = 1;
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
CHECK(!request());
etcp_link_close(b); b = NULL;
uasync_drain_immediate(ua); CHECK(sends == 1 && last_sent_link == a && conn->links == a && !a->next);
ack_all(); teardown();
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
struct INFLIGHT_PACKET* packet = (struct INFLIGHT_PACKET*)conn->input_send_q->head;
CHECK(packet); uint32_t seq = packet->seq; uint64_t old_id = packet->last_attempt_id;
b->shaper_timer = uasync_set_timeout(ua, 300, b, shaper_ready, "test_retry_late_ack"); CHECK(b->shaper_timer);
CHECK(!request()); etcp_tx_ack(a, seq, old_id, 0, 0, 0);
test_clock_run_for(ua, 40); CHECK(!sends && !conn->unacked_bytes && !conn->active_attempts && !conn->input_send_q->count);
teardown();
setup(); fill(a, 1, 100);
conn->initialized = 0; test_clock_run_for(ua, 120); conn->initialized = 1;
b->initialized = 0;
struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->link == a);
memory_pool_free(inst.pkt_pool, pkt); ack_all(); teardown();
}
/* Поздняя копия и общий till не освобождают окно/не обучают модель другого канала. */
static void independent_attempt_acks(void) {
setup(); enable_models(); fill(a, 1, 100);
@@ -1227,6 +1293,7 @@ int main(void) {
grow_and_session, allocation_and_send_failure, removed_link_and_bbr, pacing_reset_close, initial_readiness,
config_limits, timer_failure, immediate_send, ack_without_data_pacing, bbr_delivery_clock, bbr_probe_window, tcp_initial_window,
retransmission_timeout, pacing_rate_changes, pacing_timer_failure, retry_rotation, independent_attempt_acks,
retry_waits_for_alternative, retry_alternative_lifecycle,
receive_admission_and_path_acks, peer_window_queries, pacing_continuity, pacing_data_units, attempt_memory_limits,
normalizer_rx_oom, normalizer_tx_oom, normalizer_callback_abort, reset_without_allocation, restart_allocation_failures,
receiver_bandwidth_bounds, receiver_point_lifecycle, receiver_point_wire, receiver_point_truncated,