diff --git a/tests/test_etcp_windows.c b/tests/test_etcp_windows.c index d7dec5b2..b9edc2dd 100644 --- a/tests/test_etcp_windows.c +++ b/tests/test_etcp_windows.c @@ -217,6 +217,12 @@ static struct ETCP_DGRAM* request(void) { return pkt; } +/* Wire/window-тесты моделируют уже ожидающий ACK, не меняя часы и не запуская sender через uasync. */ +static void mature_acks(struct ETCP_LINK* link) { + for (struct ll_entry* e = link->ack_q->head; e; e = e->next) + ((struct ACK_PACKET*)e)->received_at_us = get_time_us() - ETCP_ACK_DELAY_US; +} + static void fill(struct ETCP_LINK* target, unsigned count, unsigned len) { int up_a = a->link_status, up_b = b->link_status; a->link_status = target == a; b->link_status = target == b; @@ -801,6 +807,7 @@ static void retry_waits_for_alternative(void) { 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); + mature_acks(a); 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; @@ -1152,6 +1159,111 @@ static void ack_absolute_deadline(void) { teardown(); } +static void ack_piggyback_deadlines(void) { + setup(); conn->got_initial_pkt = 1; + ack_now_us = get_time_us(); ack_clock_active = capture_ack_timer = 1; + uint64_t begin = ack_now_us; + receive_fragment(a, 2, 1); ack_now_us += 1000; receive_fragment(b, 3, 1); + CHECK(conn->ack_resp_deadline_us == begin + ETCP_ACK_DELAY_US); + enqueue(100); + struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->link == a && pkt->payload_len && pkt->data[1] == 1); + memory_pool_free(inst.pkt_pool, pkt); CHECK(!a->ack_q->count && b->ack_q->count == 1); + CHECK(conn->ack_resp_timer && conn->ack_resp_deadline_us == begin + 3000); + ack_now_us = begin + 2000; CHECK(!request()); + ack_now_us = begin + 2999; CHECK(!request()); + ack_now_us++; pkt = request(); CHECK(pkt && pkt->link == b && !pkt->payload_len && pkt->data[1] == 1); + memory_pool_free(inst.pkt_pool, pkt); CHECK(!conn->ack_resp_timer && !conn->ack_resp_deadline_us); + ack_all(); teardown(); +} + +/* Частичный piggyback меняет голову: прежний deadline больше не делает молодой ACK готовым. */ +static void ack_partial_piggyback(void) { + setup(); conn->got_initial_pkt = 1; b->link_status = 0; + ack_now_us = get_time_us(); ack_clock_active = capture_ack_timer = 1; + uint64_t begin = ack_now_us; + receive_fragment(a, 2, 1); ack_now_us += 500; receive_fragment(a, 3, 1); + unsigned max_body = a->mtu - WIRE_OVERHEAD(AF_INET); + enqueue(max_body - ETCP_ACK_BASE_SIZE - ETCP_PAYLOAD_HEADER_SIZE - ETCP_ACK_ENTRY_SIZE - ETCP_ACK_RX_POINT_SIZE); + ack_now_us = begin + 1999; + struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->payload_len && pkt->data[1] == 1 && a->ack_q->count == 1); + memory_pool_free(inst.pkt_pool, pkt); CHECK(conn->ack_resp_deadline_us == begin + 2500); + timeout_callback_t callback = captured_ack_callback; void* arg = captured_ack_arg; + ack_now_us = begin + 2000; uasync_cancel_timeout(ua, conn->ack_resp_timer); callback(arg); + CHECK(conn->ack_resp_timer && conn->ack_resp_deadline_us == begin + 2500 && !request()); + ack_now_us = begin + 2100; receive_fragment(a, 4, 1); + CHECK(conn->ack_resp_deadline_us == begin + 2500); + ack_now_us = begin + 2500; pkt = request(); CHECK(pkt && !pkt->payload_len && pkt->data[1] == 2); + memory_pool_free(inst.pkt_pool, pkt); CHECK(!a->ack_q->count && !conn->ack_resp_timer); + ack_all(); teardown(); +} + +/* Новый RX между timer и отложенным sender не запускает для старой головы очередные 2мс. */ +static void ack_timer_interleaving(void) { + setup(); conn->got_initial_pkt = 1; + ack_now_us = get_time_us(); ack_clock_active = capture_ack_timer = 1; + uint64_t begin = ack_now_us; + receive_fragment(a, 2, 1); + timeout_callback_t callback = captured_ack_callback; void* arg = captured_ack_arg; + ack_now_us += ETCP_ACK_DELAY_US; + uasync_cancel_timeout(ua, conn->ack_resp_timer); callback(arg); + CHECK(!conn->ack_resp_timer && conn->tx_token); + receive_fragment(a, 3, 1); CHECK(!conn->ack_resp_timer && conn->tx_token); + struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->data[1] == 2 && !pkt->payload_len); + const uint8_t* entry = pkt->data + ETCP_ACK_BASE_SIZE; + uint32_t delay = (uint32_t)entry[6] | ((uint32_t)entry[7] << 8) | ((uint32_t)entry[8] << 16) | ((uint32_t)entry[9] << 24); + CHECK(delay == ETCP_ACK_DELAY_US && ack_now_us == begin + ETCP_ACK_DELAY_US); + memory_pool_free(inst.pkt_pool, pkt); teardown(); +} + +static void ack_urgent_and_oom(void) { + for (unsigned mode = 0; mode < 2; mode++) { + setup(); conn->got_initial_pkt = 1; + ack_now_us = get_time_us(); ack_clock_active = 1; + fail_ack_timer = mode; + receive_fragment(a, 2, 1); + if (!mode) receive_till(a, 0, 1); + CHECK(a->ack_reply_pending && !fail_ack_timer); + CHECK(((struct ACK_PACKET*)a->ack_q->head)->received_at_us == ack_now_us); + struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->data[1] == 1 && !pkt->payload_len); + memory_pool_free(inst.pkt_pool, pkt); + CHECK(!a->ack_reply_pending && !conn->ack_resp_timer && !conn->ack_resp_deadline_us); + teardown(); + } +} + +static void ack_partial_frame_and_lifecycle(void) { + setup(); ack_now_us = get_time_us(); ack_clock_active = 1; + for (unsigned n = 0; n < 200; n++) CHECK(etcp_tx_queue_ack(a, 100+n, etcp_tx_attempt_id(100+n, 1)) == 0); + ack_now_us += ETCP_ACK_DELAY_US; + struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->data[1] && a->ack_q->count); + memory_pool_free(inst.pkt_pool, pkt); CHECK(!conn->ack_resp_timer); + pkt = request(); CHECK(pkt && pkt->data[1] && !a->ack_q->count); + memory_pool_free(inst.pkt_pool, pkt); teardown(); + + setup(); ack_now_us = get_time_us(); ack_clock_active = 1; + conn->ack_till_deadline_us = ack_now_us + ETCP_ACK_DELAY_US; conn->last_delivered_id = 17; + CHECK(!request() && conn->ack_resp_timer); + ack_now_us += ETCP_ACK_DELAY_US; + pkt = request(); CHECK(pkt && !pkt->data[1] && pkt->data[2] == 17); + memory_pool_free(inst.pkt_pool, pkt); CHECK(!conn->ack_till_deadline_us && !conn->ack_resp_timer); + CHECK(etcp_tx_queue_ack(a, 99, etcp_tx_attempt_id(99, 1)) == 0); CHECK(!request() && conn->ack_resp_timer); + etcp_conn_reset(conn); CHECK(!conn->ack_resp_timer && !conn->ack_resp_deadline_us && !conn->ack_till_deadline_us); + teardown(); +} + +static void ack_sender_failure(void) { + setup(); conn->got_initial_pkt = 1; CHECK(etcp_conn_ref_take(conn) == 0); + ack_now_us = get_time_us(); ack_clock_active = capture_ack_timer = 1; + receive_fragment(a, 2, 1); + timeout_callback_t callback = captured_ack_callback; void* arg = captured_ack_arg; + ack_now_us += ETCP_ACK_DELAY_US; fail_tx_timer = 1; + uasync_cancel_timeout(ua, conn->ack_resp_timer); callback(arg); + CHECK(!fail_tx_timer && conn->close_requested && !conn->ack_resp_timer); + ack_clock_active = 0; uasync_drain_immediate(ua); + CHECK(conn->state == 2 && !conn->links && !conn->ack_resp_deadline_us && !conn->ack_till_deadline_us); + etcp_conn_ref_free(conn); teardown(); +} + static void receive_admission_and_path_acks(void) { setup(); conn->got_initial_pkt = 1; fail_rx_data = 1; receive_fragment(a, 2, 1); @@ -1278,6 +1390,7 @@ static void receiver_point_wire(void) { CHECK(a->rx_data_bytes == 2 && a->ack_q->count == 1 && conn->recv_q->count == 1); a->rx_data_bytes = UINT64_C(0xfedcba9876543210); a->rx_data_time_us = UINT64_C(0x0011223344556677); const uint8_t expected[] = {0x10,0x32,0x54,0x76,0x98,0xba,0xdc,0xfe,0x77,0x66,0x55,0x44,0x33,0x22,0x11,0x00}; + mature_acks(a); struct ETCP_DGRAM* pkt = request(); CHECK(pkt && pkt->data[1] == 1 && !pkt->payload_len); CHECK(pkt->data_len == etcp_ack_section_size(1) && !memcmp(pkt->data+ETCP_ACK_BASE_SIZE+ETCP_ACK_ENTRY_SIZE,expected,16)); memory_pool_free(inst.pkt_pool,pkt); @@ -1294,12 +1407,14 @@ static void receiver_point_wire(void) { else CHECK(!pkt->data[1] && a->ack_q->count == 1); memory_pool_free(inst.pkt_pool,pkt); if (a->ack_q->count) { + mature_acks(a); pkt = request(); CHECK(pkt && pkt->data[1] == 1 && !pkt->payload_len); memory_pool_free(inst.pkt_pool,pkt); } ack(seq); } for (unsigned i = 100; i < 300; i++) receive_fragment(a,i,1000+i); + mature_acks(a); unsigned count = (max_body-ETCP_ACK_BASE_SIZE-ETCP_ACK_RX_POINT_SIZE)/ETCP_ACK_ENTRY_SIZE; pkt = request(); CHECK(pkt && pkt->data[1] == count && pkt->data_len <= max_body && a->ack_q->count == 200-(int)count); memory_pool_free(inst.pkt_pool,pkt); @@ -1500,6 +1615,7 @@ static void compact_attempt_identity(void) { conn->got_initial_pkt = 1; receive_fragment(a,2,1); receive_fragment(a,3,1); receive_fragment(a,2,1); CHECK(a->ack_q->count == 2 && a->rx_data_bytes == 3); + mature_acks(a); struct ETCP_DGRAM* pkt = request(); CHECK(pkt && !pkt->payload_len && pkt->data[1] == 2); const uint8_t entry1[] = {2,0,0,0,1,0}, entry2[] = {3,0,0,0,1,0}; CHECK(!memcmp(pkt->data+ETCP_ACK_BASE_SIZE,entry1,sizeof(entry1))); @@ -1573,7 +1689,9 @@ int main(void) { retry_waits_for_alternative, retry_alternative_lifecycle, send_queue_scan, send_queue_input_admission, retry_full_round_history, retry_history_slot_reuse, fast_stall_progress, stalled_retry_selection, input_admission_callbacks, - ack_absolute_deadline, receive_admission_and_path_acks, peer_window_queries, pacing_continuity, pacing_data_units, attempt_memory_limits, + ack_absolute_deadline, ack_piggyback_deadlines, ack_partial_piggyback, ack_timer_interleaving, + ack_urgent_and_oom, ack_partial_frame_and_lifecycle, ack_sender_failure, + 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, compact_attempt_identity, compact_attempt_exhaustion, compact_sequence_exhaustion,