use super::*;
use crate::{
event::{builder::Path, testing::Publisher},
time::{clock::testing as time, timer::Provider},
varint::VarInt,
};
use std::ops::Deref;
fn helper_ecn_counts(ect0: u8, ect1: u8, ce: u8) -> EcnCounts {
EcnCounts {
ect_0_count: VarInt::from_u8(ect0),
ect_1_count: VarInt::from_u8(ect1),
ce_count: VarInt::from_u8(ce),
}
}
#[test]
fn default() {
let controller = Controller::default();
assert_eq!(0, *controller.black_hole_counter.deref());
assert_eq!(State::Testing(0), controller.state);
assert_eq!(None, controller.last_acked_ecn_packet_timestamp);
}
#[test]
fn restart() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Failed(Timer::default()),
..Default::default()
};
controller.black_hole_counter += 1;
controller.restart(Path::test(), &mut publisher);
assert_eq!(State::Testing(0), controller.state);
assert_eq!(0, *controller.black_hole_counter.deref());
}
#[test]
fn restart_already_in_testing_0() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Testing(0),
..Default::default()
};
controller.black_hole_counter += 1;
controller.restart(Path::test(), &mut publisher);
assert_eq!(State::Testing(0), controller.state);
assert_eq!(0, *controller.black_hole_counter.deref());
}
#[test]
fn on_timeout_failed() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
controller.fail(now, Path::test(), &mut publisher);
if let State::Failed(timer) = &controller.state {
assert!(timer.is_armed());
} else {
panic!("State should be Failed");
}
assert_eq!(0, *controller.black_hole_counter.deref());
let now = now + RETEST_COOL_OFF_DURATION - Duration::from_secs(1);
controller.on_timeout(
now,
Path::test(),
&mut random::testing::Generator(123),
Duration::default(),
&mut publisher,
);
if let State::Failed(timer) = &controller.state {
assert!(timer.is_armed());
} else {
panic!("State should be Failed");
}
assert_eq!(0, *controller.black_hole_counter.deref());
let now = now + Duration::from_secs(1);
controller.on_timeout(
now,
Path::test(),
&mut random::testing::Generator(123),
Duration::default(),
&mut publisher,
);
assert_eq!(State::Testing(0), controller.state);
assert_eq!(0, *controller.black_hole_counter.deref());
}
#[test]
fn on_timeout_capable() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let rtt = Duration::from_millis(50);
let ce_suppression_time = now + *CE_SUPPRESSION_TESTING_RTT_MULTIPLIER.start() as u32 * rtt;
let mut ce_suppression_timer = Timer::default();
ce_suppression_timer.set(ce_suppression_time);
controller.state = State::Capable(ce_suppression_timer);
let now = now + rtt;
controller.on_timeout(
now,
Path::test(),
&mut random::testing::Generator(123),
rtt,
&mut publisher,
);
if let State::Capable(timer) = &controller.state {
assert!(timer.is_armed());
assert_eq!(Some(ce_suppression_time), timer.next_expiration());
} else {
panic!("State should be Capable");
}
controller.state = State::Capable(Timer::default());
controller.on_timeout(
now,
Path::test(),
&mut random::testing::Generator(123),
rtt,
&mut publisher,
);
if let State::Capable(timer) = &controller.state {
assert!(timer.is_armed());
assert!(timer.next_expiration().unwrap() > now);
} else {
panic!("State should be Capable");
}
}
#[test]
fn ecn() {
let mut publisher = Publisher::snapshot();
let now = time::now();
for &transmission_mode in &[
transmission::Mode::Normal,
transmission::Mode::MtuProbing,
transmission::Mode::PathValidationOnly,
] {
let mut controller = Controller::default();
assert!(controller.ecn(transmission_mode, now).using_ecn());
let mut ce_suppression_timer = Timer::default();
ce_suppression_timer.set(now + Duration::from_secs(10));
controller.state = State::Capable(ce_suppression_timer);
assert!(controller.ecn(transmission_mode, now).using_ecn());
if let State::Capable(ref timer) = controller.state {
assert!(timer.is_armed());
} else {
panic!("State should be Capable");
}
controller.fail(time::now(), Path::test(), &mut publisher);
assert!(!controller.ecn(transmission_mode, now).using_ecn());
controller.state = State::Unknown;
assert!(!controller.ecn(transmission_mode, now).using_ecn());
}
}
#[test]
fn ecn_ce_suppression() {
let now = time::now();
for &transmission_mode in &[
transmission::Mode::Normal,
transmission::Mode::MtuProbing,
transmission::Mode::PathValidationOnly,
] {
let mut controller = Controller::default();
assert!(controller.ecn(transmission_mode, now).using_ecn());
let mut ce_suppression_timer = Timer::default();
ce_suppression_timer.set(now);
controller.state = State::Capable(ce_suppression_timer);
assert!(controller
.ecn(transmission_mode, now)
.congestion_experienced());
if let State::Capable(timer) = controller.state {
assert!(!timer.is_armed());
} else {
panic!("State should be Capable");
}
}
}
#[test]
fn ecn_loss_recovery_probing() {
let now = time::now();
for state in [
State::Capable(Timer::default()),
State::Testing(0),
State::Unknown,
State::Failed(Timer::default()),
] {
let mut controller = Controller {
state,
..Default::default()
};
assert!(!controller
.ecn(transmission::Mode::LossRecoveryProbing, now)
.using_ecn());
}
}
#[test]
fn is_capable() {
for state in [
State::Testing(0),
State::Unknown,
State::Failed(Timer::default()),
] {
let controller = Controller {
state,
..Default::default()
};
assert!(!controller.is_capable());
}
let controller = Controller {
state: State::Capable(Timer::default()),
..Default::default()
};
assert!(controller.is_capable());
}
#[test]
fn validate_already_failed() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
controller.fail(now, Path::test(), &mut publisher);
let outcome = controller.validate(
EcnCounts::default(),
EcnCounts::default(),
EcnCounts::default(),
None,
now + Duration::from_secs(5),
Duration::default(),
Path::test(),
&mut publisher,
);
if let State::Failed(timer) = &controller.state {
assert!(timer.is_armed());
assert_eq!(
controller.next_expiration(),
Some(now + RETEST_COOL_OFF_DURATION)
);
} else {
panic!("State should be Failed");
}
assert_eq!(ValidationOutcome::Skipped, outcome);
}
#[test]
fn validate_ecn_counts_not_in_ack() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(1, 0, 0);
let outcome = controller.validate(
expected_ecn_counts,
EcnCounts::default(),
EcnCounts::default(),
None,
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_ecn_ce_remarking() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(1, 0, 0);
let sent_packet_ecn_counts = helper_ecn_counts(1, 0, 0);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(EcnCounts::default()),
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_ect_0_remarking() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(1, 0, 0);
let sent_packet_ecn_counts = helper_ecn_counts(1, 0, 0);
let ack_frame_ecn_counts = helper_ecn_counts(1, 1, 0);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_ect_0_remarking_after_restart() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(1, 0, 0);
let ack_frame_ecn_counts = helper_ecn_counts(0, 3, 0);
let baseline_ecn_counts = helper_ecn_counts(0, 2, 0);
let sent_packet_ecn_counts = helper_ecn_counts(1, 0, 0);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
baseline_ecn_counts,
Some(ack_frame_ecn_counts),
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_no_ecn_counts() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let outcome = controller.validate(
EcnCounts::default(),
EcnCounts::default(),
EcnCounts::default(),
None,
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Skipped, outcome);
assert_eq!(State::Unknown, controller.state);
}
#[test]
fn validate_ecn_decrease() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let baseline_ecn_counts = helper_ecn_counts(1, 0, 0);
let outcome = controller.validate(
EcnCounts::default(),
EcnCounts::default(),
baseline_ecn_counts,
None,
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_no_marked_packets_acked() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let outcome = controller.validate(
EcnCounts::default(),
EcnCounts::default(),
EcnCounts::default(),
Some(EcnCounts::default()),
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Passed, outcome);
assert_eq!(State::Unknown, controller.state);
}
#[test]
fn validate_ce_suppression_remarked_to_not_ect() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let newly_acked_ecn_counts = helper_ecn_counts(1, 0, 1);
let sent_packet_ecn_counts = helper_ecn_counts(1, 0, 1);
let ack_frame_ecn_counts = helper_ecn_counts(1, 0, 0);
let outcome = controller.validate(
newly_acked_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_ce_suppression_remarked_to_ect0() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let newly_acked_ecn_counts = helper_ecn_counts(1, 0, 1);
let sent_packet_ecn_counts = helper_ecn_counts(1, 0, 1);
let ack_frame_ecn_counts = helper_ecn_counts(2, 0, 0);
let outcome = controller.validate(
newly_acked_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
Duration::default(),
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Failed, outcome);
assert!(matches!(controller.state, State::Failed(_)));
}
#[test]
fn validate_capable() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(2, 0, 0);
let ack_frame_ecn_counts = helper_ecn_counts(2, 0, 0);
let sent_packet_ecn_counts = helper_ecn_counts(2, 0, 0);
let rtt = Duration::from_millis(50);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
rtt,
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Passed, outcome);
assert!(controller.is_capable());
if let State::Capable(timer) = controller.state {
assert_eq!(
Some(now + *CE_SUPPRESSION_TESTING_RTT_MULTIPLIER.start() as u32 * rtt),
timer.next_expiration()
);
}
}
#[test]
fn validate_capable_congestion_experienced() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(2, 0, 5);
let ack_frame_ecn_counts = helper_ecn_counts(1, 0, 12);
let sent_packet_ecn_counts = helper_ecn_counts(2, 0, 5);
let rtt = Duration::from_millis(50);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
rtt,
Path::test(),
&mut publisher,
);
assert_eq!(
ValidationOutcome::CongestionExperienced(7_u8.into()),
outcome
);
assert!(controller.is_capable());
if let State::Capable(timer) = controller.state {
assert_eq!(
Some(now + *CE_SUPPRESSION_TESTING_RTT_MULTIPLIER.start() as u32 * rtt),
timer.next_expiration()
);
}
}
#[test]
fn validate_capable_ce_suppression_test() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(2, 0, 1);
let ack_frame_ecn_counts = helper_ecn_counts(2, 0, 1);
let sent_packet_ecn_counts = helper_ecn_counts(2, 0, 1);
let rtt = Duration::from_millis(50);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
rtt,
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Passed, outcome);
assert!(controller.is_capable());
if let State::Capable(timer) = controller.state {
assert_eq!(
Some(now + *CE_SUPPRESSION_TESTING_RTT_MULTIPLIER.start() as u32 * rtt),
timer.next_expiration()
);
}
}
#[test]
fn validate_capable_not_in_unknown_state() {
let mut publisher = Publisher::snapshot();
for state in [
State::Testing(0),
State::Capable(Timer::default()),
State::Failed(Timer::default()),
] {
let mut controller = Controller {
state,
..Default::default()
};
let now = time::now();
let expected_ecn_counts = helper_ecn_counts(1, 0, 0);
let ack_frame_ecn_counts = helper_ecn_counts(1, 0, 0);
let sent_packet_ecn_counts = helper_ecn_counts(1, 0, 0);
let rtt = Duration::from_millis(50);
let expected_state = controller.state.clone();
controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
rtt,
Path::test(),
&mut publisher,
);
assert_eq!(expected_state, controller.state);
}
}
#[test]
fn validate_capable_lost_ack_frame() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let sent_packet_ecn_counts = helper_ecn_counts(3, 0, 0);
let ack_frame_ecn_counts = helper_ecn_counts(3, 0, 0);
let expected_ecn_counts = helper_ecn_counts(2, 0, 0);
let rtt = Duration::from_millis(50);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
EcnCounts::default(),
Some(ack_frame_ecn_counts),
now,
rtt,
Path::test(),
&mut publisher,
);
assert_eq!(ValidationOutcome::Passed, outcome);
assert!(controller.is_capable());
if let State::Capable(timer) = controller.state {
assert_eq!(
Some(now + *CE_SUPPRESSION_TESTING_RTT_MULTIPLIER.start() as u32 * rtt),
timer.next_expiration()
);
}
}
#[test]
fn validate_capable_after_restart() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller {
state: State::Unknown,
..Default::default()
};
let now = time::now();
let sent_packet_ecn_counts = helper_ecn_counts(2, 0, 0);
let expected_ecn_counts = helper_ecn_counts(2, 0, 0);
let ack_frame_ecn_counts = helper_ecn_counts(1, 2, 1);
let baseline_ecn_counts = helper_ecn_counts(0, 2, 0);
let rtt = Duration::from_millis(50);
let outcome = controller.validate(
expected_ecn_counts,
sent_packet_ecn_counts,
baseline_ecn_counts,
Some(ack_frame_ecn_counts),
now,
rtt,
Path::test(),
&mut publisher,
);
assert_eq!(
ValidationOutcome::CongestionExperienced(1_u8.into()),
outcome
);
assert!(controller.is_capable());
if let State::Capable(timer) = controller.state {
assert_eq!(
Some(now + *CE_SUPPRESSION_TESTING_RTT_MULTIPLIER.start() as u32 * rtt),
timer.next_expiration()
);
}
}
#[test]
fn on_packet_sent() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
for i in 0..TESTING_PACKET_THRESHOLD {
assert_eq!(State::Testing(i), controller.state);
controller.on_packet_sent(
ExplicitCongestionNotification::Ect0,
Path::test(),
&mut publisher,
);
}
assert_eq!(State::Unknown, controller.state);
}
#[test]
fn on_packet_loss() {
let mut publisher = Publisher::snapshot();
for state in [
State::Testing(0),
State::Capable(Timer::default()),
State::Unknown,
] {
let mut controller = Controller {
state,
..Default::default()
};
let now = time::now();
let time_sent = now + Duration::from_secs(1);
controller.last_acked_ecn_packet_timestamp = Some(now);
for i in 0..TESTING_PACKET_THRESHOLD + 1 {
assert_eq!(i, *controller.black_hole_counter.deref());
assert!(!matches!(controller.state, State::Failed(_)));
controller.on_packet_loss(
time_sent,
ExplicitCongestionNotification::Ect0,
time_sent,
Path::test(),
&mut publisher,
);
}
if let State::Failed(timer) = &controller.state {
assert!(timer.is_armed());
assert_eq!(
Some(time_sent + RETEST_COOL_OFF_DURATION),
controller.next_expiration()
);
} else {
panic!("State should be Failed");
}
assert_eq!(0, *controller.black_hole_counter.deref());
}
}
#[test]
fn on_packet_loss_already_failed() {
let mut publisher = Publisher::snapshot();
let mut controller = Controller::default();
let now = time::now();
let time_sent = now + Duration::from_secs(1);
controller.last_acked_ecn_packet_timestamp = Some(now);
controller.fail(now, Path::test(), &mut publisher);
for _i in 0..TESTING_PACKET_THRESHOLD + 1 {
assert_eq!(0, *controller.black_hole_counter.deref());
assert!(matches!(controller.state, State::Failed(_)));
controller.on_packet_loss(
time_sent,
ExplicitCongestionNotification::Ect0,
time_sent,
Path::test(),
&mut publisher,
);
}
if let State::Failed(timer) = &controller.state {
assert!(timer.is_armed());
assert_eq!(
Some(now + RETEST_COOL_OFF_DURATION),
controller.next_expiration()
);
} else {
panic!("State should be Failed");
}
assert_eq!(0, *controller.black_hole_counter.deref());
}
#[test]
fn fuzz_validate() {
let now = time::now();
bolero::check!()
.with_type::<(EcnCounts, EcnCounts, EcnCounts, Option<EcnCounts>, Duration)>()
.cloned()
.for_each(
|(
newly_acked_ecn_counts,
sent_packet_ecn_counts,
baseline_ecn_counts,
ack_frame_ecn_counts,
rtt,
)| {
let mut controller = Controller::default();
let outcome = controller.validate(
newly_acked_ecn_counts,
sent_packet_ecn_counts,
baseline_ecn_counts,
ack_frame_ecn_counts,
now,
rtt,
Path::test(),
&mut Publisher::no_snapshot(),
);
if outcome == ValidationOutcome::Failed {
assert!(!controller.is_capable());
assert_eq!(
ExplicitCongestionNotification::NotEct,
controller.ecn(transmission::Mode::Normal, now)
);
assert!(matches!(controller.state, State::Failed(_)));
}
},
);
}