use crate::{
event,
packet::number::PacketNumberSpace,
path,
path::MINIMUM_MAX_DATAGRAM_SIZE,
recovery::{
congestion_controller::PathPublisher,
pacing::{Pacer, INITIAL_INTERVAL, N, SLOW_START_N},
RttEstimator, MAX_BURST_PACKETS,
},
time::{Clock, NoopClock, Timestamp},
};
use bolero::{check, generator::*};
use core::time::Duration;
use num_traits::ToPrimitive;
#[test]
fn earliest_departure_time() {
let mut pacer = Pacer::default();
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
assert_eq!(None, pacer.next_packet_departure_time);
let now = NoopClock.get_time();
let rtt = RttEstimator::default();
let cwnd = 12000;
pacer.on_packet_sent(
now,
MINIMUM_MAX_DATAGRAM_SIZE as usize,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
&mut publisher,
);
assert_eq!(
Some(now + INITIAL_INTERVAL),
pacer.earliest_departure_time()
);
}
#[test]
fn on_packet_sent_large_bytes_sent() {
let mut pacer = Pacer::default();
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let now = NoopClock.get_time();
let rtt = RttEstimator::default();
let cwnd = 12000;
pacer.on_packet_sent(
now,
usize::MAX,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
&mut publisher,
);
assert_eq!(
Some(now + INITIAL_INTERVAL),
pacer.earliest_departure_time()
);
}
#[test]
fn slow_start() {
test_one_rtt(true);
}
#[test]
fn post_slow_start() {
test_one_rtt(false);
}
fn test_one_rtt(slow_start: bool) {
let mut pacer = Pacer::default();
let now = NoopClock.get_time();
let rtt = RttEstimator::default();
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let cwnd = MINIMUM_MAX_DATAGRAM_SIZE as u32 * 100;
let n = if slow_start {
SLOW_START_N.to_f32().unwrap()
} else {
N.to_f32().unwrap()
};
let bytes_to_send = ((cwnd as f32) * n) as u32;
pacer.on_packet_sent(
now,
MINIMUM_MAX_DATAGRAM_SIZE as usize,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
slow_start,
&mut publisher,
);
assert_eq!(
Some(now + INITIAL_INTERVAL),
pacer.earliest_departure_time()
);
let mut sent_bytes = 0;
let now = now + INITIAL_INTERVAL;
while sent_bytes < bytes_to_send {
assert!(pacer
.earliest_departure_time()
.is_none_or(|departure_time| departure_time < now + rtt.smoothed_rtt()));
pacer.on_packet_sent(
now,
MINIMUM_MAX_DATAGRAM_SIZE as usize,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
slow_start,
&mut publisher,
);
sent_bytes += MINIMUM_MAX_DATAGRAM_SIZE as u32;
}
assert!(pacer
.earliest_departure_time()
.unwrap()
.has_elapsed(now + rtt.smoothed_rtt()));
}
#[test]
fn earliest_departure_time_before_now() {
let mut pacer = Pacer::default();
let now = NoopClock.get_time();
let rtt = RttEstimator::default();
let mut publisher = event::testing::Publisher::snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let cwnd = MINIMUM_MAX_DATAGRAM_SIZE as u32 * 100;
loop {
pacer.on_packet_sent(
now,
MINIMUM_MAX_DATAGRAM_SIZE as usize,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
&mut publisher,
);
if pacer.capacity == 0 {
break;
}
}
assert_eq!(
Some(now + INITIAL_INTERVAL),
pacer.earliest_departure_time()
);
let now = now + Duration::from_secs(1);
pacer.on_packet_sent(
now,
MINIMUM_MAX_DATAGRAM_SIZE as usize,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
&mut publisher,
);
assert_eq!(Some(now), pacer.earliest_departure_time());
}
#[test]
fn interval_change() {
let mut pacer = Pacer::default();
let now = NoopClock.get_time();
let mut rtt = RttEstimator::default();
let mut cwnd = MINIMUM_MAX_DATAGRAM_SIZE as u32 * 100;
if INITIAL_INTERVAL > Duration::ZERO {
let interval = get_interval(
now,
&mut pacer,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
);
assert_eq!(INITIAL_INTERVAL, interval);
}
let interval = get_interval(
now,
&mut pacer,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
);
cwnd += MINIMUM_MAX_DATAGRAM_SIZE as u32;
let new_interval = get_interval(
now,
&mut pacer,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
);
assert!(new_interval < interval);
let interval = new_interval;
rtt.update_rtt(
Duration::default(),
Duration::from_millis(750),
now,
true,
PacketNumberSpace::ApplicationData,
);
let new_interval = get_interval(
now,
&mut pacer,
&rtt,
cwnd,
MINIMUM_MAX_DATAGRAM_SIZE,
false,
);
assert!(new_interval > interval);
let interval = new_interval;
let max_datagram_size = MINIMUM_MAX_DATAGRAM_SIZE + 300;
let new_interval = get_interval(now, &mut pacer, &rtt, cwnd, max_datagram_size, false);
assert!(new_interval > interval);
let interval = new_interval;
let new_interval = get_interval(now, &mut pacer, &rtt, cwnd, max_datagram_size, true);
assert!(new_interval < interval);
}
#[test]
#[cfg_attr(miri, ignore)] fn interval_differential_test() {
check!()
.with_generator((
1_000_000..u32::MAX, 2400..u32::MAX, MINIMUM_MAX_DATAGRAM_SIZE..=9000, produce(),
))
.cloned()
.for_each(|(rtt, congestion_window, max_datagram_size, slow_start)| {
let mut publisher = event::testing::Publisher::no_snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let rtt = Duration::from_nanos(rtt as _);
let actual = Pacer::interval(
rtt,
congestion_window,
max_datagram_size,
slow_start,
&mut publisher,
);
let expected = rfc_interval(rtt, congestion_window, max_datagram_size, slow_start);
assert!(
actual.abs_diff(expected) < Duration::from_nanos(1_100_000),
"expected: {expected:?}; actual: {actual:?}"
);
});
}
fn rfc_interval(
rtt: Duration,
congestion_window: u32,
max_datagram_size: u16,
slow_start: bool,
) -> Duration {
let packet_size = MAX_BURST_PACKETS * max_datagram_size as u32;
let result = rtt * packet_size / congestion_window;
let n = if slow_start { SLOW_START_N } else { N };
result * *n.denom() as u32 / *n.numer() as u32
}
fn get_interval(
now: Timestamp,
pacer: &mut Pacer,
rtt_estimator: &RttEstimator,
congestion_window: u32,
max_datagram_size: u16,
slow_start: bool,
) -> Duration {
let mut publisher = event::testing::Publisher::no_snapshot();
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
let starting_departure_time = pacer.earliest_departure_time().unwrap_or(now);
loop {
pacer.on_packet_sent(
now,
max_datagram_size as usize,
rtt_estimator,
congestion_window,
max_datagram_size,
slow_start,
&mut publisher,
);
if let Some(departure_time) = pacer.earliest_departure_time() {
if departure_time > starting_departure_time {
return departure_time - starting_departure_time;
}
}
}
}