use std::time::Duration;
use irtt_proto::{Clock, Params, ReceivedStats, StampAt};
use super::support::{
core_with_sources, echo_at, echo_params, echo_request, expect_echo_reply, manual_core,
open_negotiated, other_peer, peer, ManualClock, ScriptedTokens,
};
use crate::{OutboundDatagram, ServerConfig};
const TOKEN_A: u64 = 0x0102_0304_0506_0708;
const TOKEN_B: u64 = 0x1112_1314_1516_1718;
const MS: i64 = 1_000_000;
const SECOND: i64 = 1_000_000_000;
fn params_at(interval_ns: i64) -> Params {
Params {
interval_ns,
..echo_params(
ReceivedStats::Both,
StampAt::None,
Clock::Unspecified,
0,
)
}
}
fn count_of(reply: Option<OutboundDatagram>, params: &Params) -> Option<u32> {
let reply = reply?;
expect_echo_reply(reply, params, None).recv_count
}
#[test]
fn a_blast_is_answered_up_to_the_burst_and_then_replenishes() {
let config = ServerConfig::default()
.with_min_send_interval(Duration::from_millis(100))
.with_burst_allowance(5);
let (mut core, clock, token, negotiated) = manual_core(
config,
¶ms_at(SECOND),
ScriptedTokens::new([TOKEN_A]),
None,
);
assert_eq!(negotiated.interval_ns, SECOND);
for sequence in 0..12 {
let count = count_of(
echo_at(&mut core, &clock, 0, token, sequence, &negotiated, None),
&negotiated,
);
let expected = (sequence < 5).then_some(sequence + 1);
assert_eq!(count, expected, "request {sequence} of the blast");
}
for (offset, sequence, expected) in [(600, 12, 6), (700, 13, 7), (800, 14, 8)] {
let count = count_of(
echo_at(
&mut core,
&clock,
offset * MS,
token,
sequence,
&negotiated,
None,
),
&negotiated,
);
assert_eq!(count, Some(expected), "request at {offset} ms");
}
}
#[test]
fn a_burst_of_one_answers_one_request_per_interval() {
let config = ServerConfig::default()
.with_min_send_interval(Duration::from_millis(100))
.with_burst_allowance(1);
let (mut core, clock, token, negotiated) = manual_core(
config,
¶ms_at(SECOND),
ScriptedTokens::new([TOKEN_A]),
None,
);
for sequence in 0..4 {
let count = count_of(
echo_at(&mut core, &clock, 0, token, sequence, &negotiated, None),
&negotiated,
);
assert_eq!(count, (sequence == 0).then_some(1), "request {sequence}");
}
let count = count_of(
echo_at(&mut core, &clock, 100 * MS, token, 4, &negotiated, None),
&negotiated,
);
assert_eq!(count, Some(2));
}
#[test]
fn the_refill_cadence_is_never_slower_than_the_negotiated_interval() {
let config = ServerConfig::default()
.with_min_send_interval(Duration::from_secs(5))
.with_idle_timeout(Duration::from_secs(8))
.with_burst_allowance(1);
let (mut core, clock, token, negotiated) = manual_core(
config,
¶ms_at(10 * MS),
ScriptedTokens::new([TOKEN_A]),
None,
);
assert_eq!(
negotiated.interval_ns,
2 * SECOND,
"the idle cap wins over the larger configured minimum"
);
assert_eq!(
count_of(
echo_at(&mut core, &clock, 0, token, 0, &negotiated, None),
&negotiated
),
Some(1),
"the first request spends the burst"
);
assert_eq!(
count_of(
echo_at(&mut core, &clock, 2 * SECOND, token, 1, &negotiated, None),
&negotiated
),
Some(2),
"a client sending at the interval it was given must not be limited"
);
assert_eq!(
count_of(
echo_at(&mut core, &clock, 4 * SECOND, token, 2, &negotiated, None),
&negotiated
),
Some(3),
"and must keep being answered at that cadence"
);
}
#[test]
fn a_zero_burst_refuses_every_echo_and_a_zero_interval_refuses_none() {
let (mut core, clock, token, negotiated) = manual_core(
ServerConfig::default().with_burst_allowance(0),
¶ms_at(SECOND),
ScriptedTokens::new([TOKEN_A]),
None,
);
for (offset, sequence) in [(0, 0), (SECOND, 1), (60 * SECOND, 2)] {
assert!(
echo_at(
&mut core,
&clock,
offset,
token,
sequence,
&negotiated,
None
)
.is_none(),
"a zero burst answers nothing, however long the wait"
);
}
let (mut core, clock, token, negotiated) = manual_core(
ServerConfig::default().with_min_send_interval(Duration::ZERO),
¶ms_at(SECOND),
ScriptedTokens::new([TOKEN_A]),
None,
);
for sequence in 0..12 {
assert_eq!(
count_of(
echo_at(&mut core, &clock, 0, token, sequence, &negotiated, None),
&negotiated
),
Some(sequence + 1),
"a zero interval never withholds allowance, whatever the burst"
);
}
}
#[test]
fn sibling_sessions_hold_their_allowance_independently() {
let config = ServerConfig::default()
.with_min_send_interval(Duration::from_millis(100))
.with_burst_allowance(1);
let clock = ManualClock::at(0);
let mut core = core_with_sources(
config,
ScriptedTokens::new([TOKEN_A, TOKEN_B]),
clock.clone(),
);
let requested = params_at(SECOND);
let (token_a, negotiated) = open_negotiated(&mut core, peer(), &requested, None);
let (token_b, _) = open_negotiated(&mut core, other_peer(), &requested, None);
assert!(echo_at(&mut core, &clock, 0, token_a, 0, &negotiated, None).is_some());
assert!(echo_at(&mut core, &clock, 0, token_a, 1, &negotiated, None).is_none());
clock.set(0);
let packet = core
.handle_datagram(
other_peer(),
&echo_request(token_b, 0, &negotiated, &[], None),
)
.unwrap()
.expect("a sibling session's allowance is its own");
assert_eq!(
expect_echo_reply(&packet, &negotiated, None).recv_count,
Some(1)
);
}