//! BWE test for allocation probing mechanics when entering ALR. use std::sync::Arc; use std::time::Duration; use netem::{DataSize, NetemConfig}; use str0m::RtcError; use str0m::bwe::Bitrate; use crate::common::{BweTestContext, Step, connect_with_bwe, init_crypto_default, init_log}; #[test] pub fn bwe_allocation_probe_on_desired_increase_in_alr() -> Result<(), RtcError> { init_log(); init_crypto_default(); // Start with lower max_bitrate, enter ALR, then increase desired let mut plan = alr_preamble_steps(Bitrate::mbps(3)); plan.extend(enter_alr_steps(Bitrate::mbps(3))); plan.extend(vec![ Step::CheckProbe { description: "First periodic ALR probe", check: Arc::new(|_index, probe| probe.is_alr_probe()), }, // Now increase desired bitrate while in ALR - should trigger IncreaseAlr probes Step::Media { description: "Increase desired bitrate while in ALR", desired_bitrate: Bitrate::mbps(5), // Increased from 3 to 5 media_send_rate: Bitrate::kbps(500), // Stay in ALR }, Step::Run { description: "Allow probe to be triggered", duration: Duration::from_millis(100), }, Step::CheckProbe { description: "Allocation probe triggered by desired increase", check: Arc::new(|_index, probe| probe.is_alr_probe()), }, ]); let (mut l, mut r) = connect_with_bwe(Bitrate::mbps(2), Bitrate::mbps(3)); let mut ctx = BweTestContext::new(&mut l, &mut r); ctx.run_plan(&mut l, &mut r, &plan)?; Ok(()) } #[test] pub fn bwe_periodic_allocation_probing_in_alr() -> Result<(), RtcError> { init_log(); init_crypto_default(); let mut plan = alr_setup_steps(Bitrate::mbps(5)); plan.extend(vec![ // First allocation probe on ALR entry Step::CheckProbe { description: "First allocation probe on ALR entry", check: Arc::new(|_index, probe| probe.is_alr_probe()), }, // Wait for periodic probe interval (MIN_TIME_BETWEEN_ALR_PROBES = 5 seconds) Step::Run { description: "Wait 6 seconds for periodic probe", duration: Duration::from_secs(6), }, // Should see another allocation probe after 5 seconds Step::CheckProbe { description: "Periodic allocation probe fires after 5 seconds", check: Arc::new(|_index, probe| probe.is_alr_probe()), }, ]); let (mut l, mut r) = connect_with_bwe(Bitrate::mbps(2), Bitrate::mbps(5)); let mut ctx = BweTestContext::new(&mut l, &mut r); ctx.run_plan(&mut l, &mut r, &plan)?; Ok(()) } #[test] pub fn bwe_no_allocation_probing_when_estimate_at_max() -> Result<(), RtcError> { init_log(); init_crypto_default(); let plan = vec![ Step::Conditions { description: "Moderate bandwidth network", config: NetemConfig::new() .latency(Duration::from_millis(10)) .jitter(Duration::from_millis(2)) .link(Bitrate::mbps(3), DataSize::kbytes(100)) .seed(42), }, // Start with bitrate close to max desired Step::Media { description: "Start at 1.5 Mbps, desire 2 Mbps", desired_bitrate: Bitrate::mbps(2), media_send_rate: Bitrate::mbps(1), }, // Let estimate grow to near max Step::Run { description: "Grow to desired bitrate", duration: Duration::from_secs(10), }, Step::Check { description: "Estimate should reach desired", at_least: Bitrate::mbps(1), }, // Enter ALR with estimate >= max_bitrate Step::Media { description: "Enter ALR with estimate at max", desired_bitrate: Bitrate::mbps(2), media_send_rate: Bitrate::kbps(500), }, // Note: We can't easily test ABSENCE of probes with current CheckProbe mechanism // Instead, we verify estimate stays stable without ALR probing Step::Run { description: "Wait and verify no growth from allocation probes", duration: Duration::from_secs(6), }, Step::Check { description: "Estimate stable around max bitrate", at_least: Bitrate::mbps(1), }, ]; let (mut l, mut r) = connect_with_bwe(Bitrate::mbps(1), Bitrate::mbps(2)); let mut ctx = BweTestContext::new(&mut l, &mut r); ctx.run_plan(&mut l, &mut r, &plan)?; Ok(()) } #[test] fn alr_exit_has_hysteresis() -> Result<(), RtcError> { init_log(); init_crypto_default(); let mut plan = alr_setup_steps(Bitrate::mbps(5)); plan.extend(vec![ Step::CheckProbe { description: "First periodic ALR probe", check: Arc::new(|_index, probe| probe.is_alr_probe()), }, // Increase send rate to the hysteresis gap (between entry and exit thresholds) // Entry: ratio > 0.80 (utilization < 20%) // Exit: ratio < 0.50 (utilization > 50%) // Gap: 0.50-0.80 ratio (20-50% utilization) - should STAY in ALR Step::Media { description: "Increase to ~35% utilization (in hysteresis gap)", desired_bitrate: Bitrate::mbps(5), media_send_rate: Bitrate::mbps(1), // ~35% of 3 Mbps estimate }, Step::Run { description: "Wait over 500ms window for ALR check", duration: Duration::from_secs(1), }, // Should still be in ALR - periodic probes should continue Step::Run { description: "Wait for periodic probe (should still be in ALR)", duration: Duration::from_secs(5), }, Step::CheckProbe { description: "Periodic ALR probe confirms still in ALR", check: Arc::new(|_index, probe| probe.is_alr_probe()), }, // Now increase above exit threshold to confirm we eventually exit Step::Media { description: "Increase to 60% utilization (above exit threshold)", desired_bitrate: Bitrate::mbps(5), media_send_rate: Bitrate::mbps(2), // ~60% of 3 Mbps estimate }, Step::Run { description: "Wait for ALR exit", duration: Duration::from_secs(2), }, // Test completes - we've verified hysteresis by staying in ALR in the gap ]); let (mut l, mut r) = connect_with_bwe(Bitrate::mbps(2), Bitrate::mbps(5)); let mut ctx = BweTestContext::new(&mut l, &mut r); ctx.run_plan(&mut l, &mut r, &plan)?; Ok(()) } /// Common setup steps for ALR tests: establish connection, wait for probes to complete, /// then enter ALR with low send rate. Tests can append their specific scenarios. fn alr_setup_steps(max_bitrate: Bitrate) -> Vec { let mut plan = alr_preamble_steps(max_bitrate); plan.extend(enter_alr_steps(max_bitrate)); plan } /// Common ALR test preamble: establish connection and allow initial probing to settle /// without entering ALR. fn alr_preamble_steps(max_bitrate: Bitrate) -> Vec { vec![ Step::Conditions { description: "Moderate bandwidth network", config: NetemConfig::new() .link(Bitrate::mbps(3), DataSize::kbytes(100)) .seed(42), }, Step::Media { description: "Start with 2 Mbps to avoid early ALR", desired_bitrate: max_bitrate, media_send_rate: Bitrate::mbps(2), // Higher rate prevents ALR during initial probes }, Step::Run { description: "Wait for initial probes to complete", duration: Duration::from_secs(2), }, ] } /// Enter ALR and give the ALR detector time to latch. fn enter_alr_steps(max_bitrate: Bitrate) -> Vec { vec![ Step::Media { description: "Enter ALR with low send rate", desired_bitrate: max_bitrate, media_send_rate: Bitrate::kbps(500), }, Step::Run { description: "Allow ALR detection", duration: Duration::from_secs(1), }, // No probe on ALR entry - wait for first periodic probe (5s after last probe) Step::Run { description: "Wait for first periodic ALR probe", duration: Duration::from_secs(5), }, ] }