From 34e3d9bba091408d41b2fcca1d1ab7da6df09df7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 23 Oct 2026 01:02:15 +0200 Subject: [PATCH] Verify queue budget saturation and the BBR fast path contract --- tests/test_etcp_bbr.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_etcp_bbr.c b/tests/test_etcp_bbr.c index 0c9c3f24..9adf4fc3 100644 --- a/tests/test_etcp_bbr.c +++ b/tests/test_etcp_bbr.c @@ -141,6 +141,7 @@ static int test_cwnd_saturation(void) { bbr_main(&s, &rs, &cwnd, &pacing, 1400, UINT32_MAX - 500, 0); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BBR saturated window: cwnd=%u expected=%u", cwnd, UINT32_MAX); if (cwnd != UINT32_MAX) FAIL("BDP plus ACK and cwnd plus ACK must saturate, not wrap"); + if (s.queue_cwnd_cap != UINT32_MAX) FAIL("large queue budget cap must saturate, not wrap"); PASS(); return 0; } @@ -327,6 +328,30 @@ static int test_queue_budget_floor_and_startup(void) { PASS(); return 0; } +static void record_window(void* arg, uint32_t window) { *(uint32_t*)arg = window; } + +static int test_queue_budget_fast_path(void) { + MSEG("queue budget also bounds fast path and callback"); + struct bbr s = { .now_tb = 10000 }; bbr_init(&s); + s.mode = BBR_PROBE_BW; s.full_bw_reached = 1; s.cycle_idx = BBR_BW_PROBE_CRUISE; + s.try_fast_path = 1; s.min_rtt_us = s.probe_rtt_min_us = 20000; s.has_seen_rtt = 1; + s.cycle_mstamp_tb = s.now_tb; s.probe_wait_us = 2000000; s.bw_hi[0] = BW_UNIT; s.queue_budget_us = 5000; + uint32_t cwnd = 100000, pacing = 990000, observed = 0; + s.on_cwnd_update = record_window; s.cwnd_update_ctx = &observed; + struct bbr_rate_sample rs = { .delivered = 1000, .interval_us = 20000, .rtt_us = 20000, + .acked_sacked = 1000, .is_app_limited = 1, .tx_in_flight = 100000 }; + bbr_main(&s, &rs, &cwnd, &pacing, 1400, 10000, 0); + if (cwnd != 25000 || observed != cwnd || s.queue_cwnd_cap != cwnd) FAIL("fast path callback must see the bounded window"); + s.queue_budget_us = 0; cwnd = 100000; rs.prior_delivered = s.delivered; + bbr_main(&s, &rs, &cwnd, &pacing, 1400, 10000, 0); + if (cwnd != 100000 || observed != cwnd || s.queue_cwnd_cap) FAIL("disabling budget must clear its active cap"); + s.queue_budget_us = 5000; s.min_rtt_us = s.probe_rtt_min_us = UINT32_MAX; rs.rtt_us = 0; + rs.prior_delivered = s.delivered; + bbr_main(&s, &rs, &cwnd, &pacing, 1400, 10000, 0); + if (s.queue_cwnd_cap || cwnd != 100000) FAIL("unknown min RTT must not create a fabricated queue cap"); + PASS(); return 0; +} + int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO); @@ -347,6 +372,7 @@ int main(void) test_probe_rtt_lifecycle(); test_queue_budget(); test_queue_budget_floor_and_startup(); + test_queue_budget_fast_path(); printf("\n=== Results: %d/%d passed, %d failed ===\n", test_passed, test_total, test_failed); return test_failed ? 1 : 0; }