use crate::protocol::{
constants::{addr_mgmt_pgns::COMMAND_ADDR_65240, address::CLAIMABLE_COUNT},
transport::can_id::CanIdBuilder,
};
use super::*;
const ADDR_1: u8 = 1;
const ADDR_2: u8 = 2;
const ADDR_3: u8 = 144;
const ADDR_4: u8 = 244;
const STARTING_TIME: u64 = 123456789;
fn claiming(tx: CanFrame, address: u8, deadline_ms: u64) -> ClaimOutput {
ClaimOutput {
tx: Some(tx),
status: ClaimStatus::Claiming(address),
wake_at_ms: Some(deadline_ms),
}
}
fn waiting_claim(address: u8, deadline_ms: u64) -> ClaimOutput {
ClaimOutput {
tx: None,
status: ClaimStatus::Claiming(address),
wake_at_ms: Some(deadline_ms),
}
}
fn claimed(address: u8) -> ClaimOutput {
ClaimOutput {
tx: None,
status: ClaimStatus::Claimed(address),
wake_at_ms: None,
}
}
fn defending(tx: CanFrame, address: u8) -> ClaimOutput {
ClaimOutput {
tx: Some(tx),
status: ClaimStatus::Claimed(address),
wake_at_ms: None,
}
}
fn cannot_claim(tx: Option<CanFrame>, retry_at_ms: u64) -> ClaimOutput {
ClaimOutput {
tx,
status: ClaimStatus::CannotClaim,
wake_at_ms: Some(retry_at_ms),
}
}
struct TestEngine<'a>(AddressClaimEngine<'a>);
impl TestEngine<'_> {
fn poll(&mut self, now_ms: u64, rx: Option<&CanFrame>) -> ClaimOutput {
let output = self.0.poll(now_ms, rx);
if output.tx.is_none() {
return output;
}
let sent = self.0.tx_sent(now_ms);
ClaimOutput {
tx: output.tx,
..sent
}
}
}
impl<'a> core::ops::Deref for TestEngine<'a> {
type Target = AddressClaimEngine<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for TestEngine<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
struct ClockTest {
ms: u64,
}
impl ClockTest {
fn new(start_value: u64) -> Self {
Self { ms: start_value }
}
fn tick(&mut self, d: u64) -> u64 {
self.ms += d;
self.ms
}
}
#[derive(Clone, Copy)]
struct Name(IsoName);
impl Default for Name {
fn default() -> Self {
Self(IsoName::from_raw(0x1234567890ABCDEF))
}
}
impl Name {
fn from_raw(raw: u64) -> Self {
Self(IsoName::from_raw(raw))
}
}
impl Name {
fn new(strategy: AddressClaimStrategy) -> Self {
let name = Name::default();
match strategy {
AddressClaimStrategy::Fixed { preferred: _ } => name,
AddressClaimStrategy::SelfConfigurable { addresses: _ } => name,
AddressClaimStrategy::Arbitrary { preferred: _ } => {
let r_name = name.0.raw() | 1u64 << 63;
assert_eq!(r_name, 0x9234567890ABCDEF);
Name::from_raw(r_name)
}
}
}
fn conflict_priority_builder(mut self, name_priority: ConflictPriority) -> Self {
match name_priority {
ConflictPriority::BuiltToWin => self.0 = IsoName::from_raw(self.0.raw() & !0xFFFF),
ConflictPriority::Normal => {}
ConflictPriority::BuiltToLose => self.0 = IsoName::from_raw(self.0.raw() | 0xFFFF),
}
self
}
}
fn build_request_frame(destination: u8, requested_pgn: u32, len: usize) -> CanFrame {
let mut data = [0xFFu8; 8];
data[0..REQUEST_PGN_LEN].copy_from_slice(&requested_pgn.to_le_bytes()[0..REQUEST_PGN_LEN]);
CanFrame {
id: CanIdBuilder::new(REQUEST_PGN_59904, ADDR_4)
.to_destination(destination)
.with_priority(6)
.build()
.expect("must be valid"),
data,
len,
}
}
struct Instance<'a> {
name: Name,
preferred_addr: u8,
can_frame_origin: CanFrame,
can_frame_next: Option<CanFrame>,
claimer: TestEngine<'a>,
}
enum CanFrameClass {
Claiming,
Normal,
}
enum ConflictPriority {
BuiltToWin,
Normal,
BuiltToLose,
}
impl<'a> Instance<'a> {
fn new(
strategy: AddressClaimStrategy<'a>,
can_frame_class: CanFrameClass,
conflict_design: ConflictPriority,
) -> Self {
let name = Name::new(strategy).conflict_priority_builder(conflict_design);
let preferred_addr: u8 = match strategy {
AddressClaimStrategy::Fixed { preferred } => preferred,
AddressClaimStrategy::SelfConfigurable { addresses } => addresses[0],
AddressClaimStrategy::Arbitrary { preferred } => preferred,
};
let can_frame: CanFrame = match can_frame_class {
CanFrameClass::Claiming => build_address_claim_frame(name.0, preferred_addr),
CanFrameClass::Normal => {
let id = CanIdBuilder::new(129044, preferred_addr)
.build()
.expect("must be valid");
CanFrame {
id,
data: [1u8; 8],
len: 8,
}
}
};
let claimer = TestEngine(
AddressClaimEngine::new(name.0, strategy)
.expect("Instance::new() // Claimer must be valid"),
);
Self {
name,
preferred_addr,
can_frame_origin: can_frame,
can_frame_next: None,
claimer,
}
}
}
#[test]
fn test_addr_claim_machine_aac_without_rx() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let mut timer = ClockTest::new(STARTING_TIME);
assert_eq!(my_inst.claimer.state, State::Unclaimed);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
timer.tick(10);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
waiting_claim(my_preferred, deadline_ms)
);
timer.tick(240);
assert_eq!(my_inst.claimer.poll(timer.ms, None), claimed(my_preferred));
}
#[test]
fn test_claim_timer_starts_after_send() {
let strategy = AddressClaimStrategy::Arbitrary { preferred: ADDR_1 };
let mut instance = Instance::new(strategy, CanFrameClass::Claiming, ConflictPriority::Normal);
let mut timer = ClockTest::new(STARTING_TIME);
let output = instance.claimer.0.poll(timer.ms, None);
assert_eq!(output.wake_at_ms, None);
assert!(output.tx.is_some());
timer.tick(300);
let output = instance.claimer.0.poll(timer.ms, None);
assert_eq!(output.wake_at_ms, None);
assert!(output.tx.is_some());
let deadline_ms = timer.ms + CLAIM_DELAY_MS as u64;
assert_eq!(
instance.claimer.tx_sent(timer.ms),
waiting_claim(ADDR_1, deadline_ms)
);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(instance.claimer.0.poll(timer.ms, None), claimed(ADDR_1));
}
#[test]
fn test_addr_claim_machine_with_claiming_rx_different_sa() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_preferred = ADDR_2;
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: their_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.name.0 < my_inst.name.0); assert!(their_inst.preferred_addr != my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
timer.tick(10);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
waiting_claim(my_preferred, deadline_ms)
);
timer.tick(240);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claimed(my_preferred)
);
}
#[test]
fn test_addr_claim_machine_aac_with_conflict_we_win() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_preferred = ADDR_1;
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: their_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToLose,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.can_frame_origin.id.pgn() == 60928); assert!(their_inst.name.0 > my_inst.name.0); assert!(their_inst.preferred_addr == my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_origin,
deadline_ms
}
);
timer.tick(10);
assert!(timer.ms == STARTING_TIME + 10);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_origin,
deadline_ms: 240 + timer.ms
}
);
timer.tick(99);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_origin,
deadline_ms: 141 + timer.ms
}
);
timer.tick(240);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claimed(my_inst.preferred_addr)
);
}
#[test]
fn test_addr_claim_machine_aac_with_conflict_we_lose_and_no_addr_available() {
let my_strategy = AddressClaimStrategy::Arbitrary { preferred: ADDR_3 };
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_strategy = AddressClaimStrategy::Arbitrary { preferred: ADDR_3 };
let mut their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.name.0 < my_inst.name.0); assert!(their_inst.preferred_addr == my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_inst.preferred_addr,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
timer.tick(1);
let deadline_ms = timer.ms + CLAIM_DELAY_MS as u64;
let expected_next_addr = my_inst.preferred_addr + 1;
let expected_canframe = build_address_claim_frame(my_inst.name.0, expected_next_addr);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claiming(expected_canframe, expected_next_addr, deadline_ms)
);
let mut tested_addr: u16 = 1;
while tested_addr < CLAIMABLE_COUNT as u16 {
their_inst.can_frame_next = Some(build_address_claim_frame(
their_inst.name.0,
((my_inst.preferred_addr as u16 + tested_addr) % CLAIMABLE_COUNT as u16) as u8,
));
if tested_addr == 251 {
let expected_canframe =
build_address_claim_frame(my_inst.name.0, address::NULL_ADDR_254);
assert_eq!(
my_inst
.claimer
.poll(timer.ms, their_inst.can_frame_next.as_ref(),),
cannot_claim(
Some(expected_canframe),
timer.ms + CANNOT_CLAIM_RETRY_DELAY_MS as u64
)
);
break;
}
let expected_next_addr: u16 =
(my_inst.preferred_addr as u16 + tested_addr + 1) % CLAIMABLE_COUNT as u16;
assert!(expected_next_addr < CLAIMABLE_COUNT as u16);
let expected_canframe = build_address_claim_frame(my_inst.name.0, expected_next_addr as u8);
assert_eq!(
my_inst
.claimer
.poll(timer.ms, their_inst.can_frame_next.as_ref(),),
claiming(expected_canframe, expected_next_addr as u8, deadline_ms)
);
tested_addr += 1;
}
}
#[test]
fn test_addr_claim_machine_aac_with_conflict_we_lose() {
let my_preferred = ADDR_3;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_preferred = ADDR_3;
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: their_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.name.0 < my_inst.name.0); assert!(their_inst.preferred_addr == my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_preferred,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
timer.tick(10);
my_inst.can_frame_next = Some(build_address_claim_frame(
my_inst.name.0,
my_inst.preferred_addr + 1,
));
let deadline_ms = timer.ms + CLAIM_DELAY_MS as u64;
let captured_claim = my_inst.claimer.poll(timer.ms, their_rx);
assert_ne!(
captured_claim,
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
assert_eq!(
captured_claim,
claiming(
my_inst.can_frame_next.expect("must be valid"),
my_inst.preferred_addr + 1,
deadline_ms
)
);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_next.expect("must be valid"),
deadline_ms
}
);
timer.tick(250);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claimed(my_inst.preferred_addr + 1)
);
assert!(timer.ms == STARTING_TIME + 260);
}
#[test]
fn test_addr_claim_machine_non_aac_fixed_addr_we_lose() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Fixed {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_preferred = ADDR_1;
let their_strategy = AddressClaimStrategy::Fixed {
preferred: their_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.name.0 < my_inst.name.0); assert!(their_inst.preferred_addr == my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_preferred,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
timer.tick(45);
let expected_frame = build_address_claim_frame(my_inst.name.0, address::NULL_ADDR_254);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
cannot_claim(
Some(expected_frame),
timer.ms + CANNOT_CLAIM_RETRY_DELAY_MS as u64
)
);
}
#[test]
fn test_addr_claim_machine_non_aac_self_config_strategy_with_conflict_we_lose() {
let my_preferred = &[ADDR_3, ADDR_1, ADDR_2, ADDR_4];
let my_strategy = AddressClaimStrategy::SelfConfigurable {
addresses: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
my_inst.can_frame_next = Some(build_address_claim_frame(my_inst.name.0, ADDR_1));
let their_preferred = &[ADDR_3, ADDR_2, ADDR_1, ADDR_4];
let their_strategy = AddressClaimStrategy::SelfConfigurable {
addresses: their_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.name.0 < my_inst.name.0); assert!(their_inst.preferred_addr == my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claiming(
my_inst.can_frame_origin,
ADDR_3,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
timer.tick(45);
let deadline_ms = timer.ms + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claiming(my_inst.can_frame_next.unwrap(), ADDR_1, deadline_ms)
);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_next.unwrap(),
deadline_ms
}
);
timer.tick(250);
assert_eq!(my_inst.claimer.poll(timer.ms, their_rx), claimed(ADDR_1));
assert!(timer.ms == STARTING_TIME + 295);
}
#[test]
fn test_addr_claim_machine_aac_disturbed_with_no_effect() {
let my_preferred = ADDR_3;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_preferred = ADDR_3;
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: their_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Normal,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert!(their_inst.name.0 < my_inst.name.0); assert!(their_inst.preferred_addr == my_inst.preferred_addr); assert_eq!(my_inst.claimer.state, State::Unclaimed); assert!(timer.ms == STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
timer.tick(10);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
waiting_claim(my_preferred, deadline_ms)
);
timer.tick(239);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
waiting_claim(my_preferred, deadline_ms)
);
timer.tick(1);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claimed(my_inst.preferred_addr)
);
}
#[test]
fn test_iso_request_from_unclaimed_starts_the_campaign() {
let my_strategy = AddressClaimStrategy::Arbitrary { preferred: ADDR_1 };
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let timer = ClockTest::new(STARTING_TIME);
assert_eq!(my_inst.claimer.state, State::Unclaimed);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
claiming(
my_inst.can_frame_origin,
ADDR_1,
timer.ms + CLAIM_DELAY_MS as u64
)
);
}
#[test]
fn test_a_strategy_with_no_claimable_address_answers_cannot_claim() {
let name = Name::default().0;
let strategy = AddressClaimStrategy::SelfConfigurable { addresses: &[] };
let mut engine =
TestEngine(AddressClaimEngine::new(name, strategy).expect("SAC name, SAC strategy"));
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let timer = ClockTest::new(STARTING_TIME);
let expected_frame = build_address_claim_frame(name, address::NULL_ADDR_254);
assert_eq!(
engine.poll(timer.ms, Some(&request_rx)),
cannot_claim(
Some(expected_frame),
timer.ms + CANNOT_CLAIM_RETRY_DELAY_MS as u64
)
);
}
#[test]
fn test_continuous_requests_from_unclaimed_do_not_starve_the_first_claim() {
let my_strategy = AddressClaimStrategy::Arbitrary { preferred: ADDR_1 };
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let mut timer = ClockTest::new(STARTING_TIME);
for _ in 0..10 {
my_inst.claimer.poll(timer.ms, Some(&request_rx));
assert!(
matches!(my_inst.claimer.state, State::Claiming { .. }),
"a request stream must not keep the engine out of a campaign"
);
timer.tick(10);
}
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
defending(my_inst.can_frame_origin, ADDR_1)
);
}
#[test]
fn test_iso_request_while_claiming_resends_current_claim() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let mut timer = ClockTest::new(STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_origin,
deadline_ms
}
);
timer.tick(10);
let request_rx = build_request_frame(my_preferred, CLAIM_PGN_60928, REQUEST_PGN_LEN);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
assert_eq!(
my_inst.claimer.state,
State::Claiming {
frame: my_inst.can_frame_origin,
deadline_ms
}
);
timer.tick(240);
assert_eq!(my_inst.claimer.poll(timer.ms, None), claimed(my_preferred));
}
#[test]
fn test_iso_request_while_claimed_resends_current_claim() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let mut timer = ClockTest::new(STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_preferred,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(my_inst.claimer.poll(timer.ms, None), claimed(my_preferred));
assert_eq!(
my_inst.claimer.state,
State::Claimed {
frame: my_inst.can_frame_origin
}
);
let global_request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&global_request_rx)),
defending(my_inst.can_frame_origin, my_preferred)
);
let addressed_request_rx = build_request_frame(my_preferred, CLAIM_PGN_60928, REQUEST_PGN_LEN);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&addressed_request_rx)),
defending(my_inst.can_frame_origin, my_preferred)
);
assert_eq!(
my_inst.claimer.state,
State::Claimed {
frame: my_inst.can_frame_origin
}
);
}
#[test]
fn test_iso_request_from_cannot_claim_answers_cannot_claim() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Fixed {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_strategy = AddressClaimStrategy::Fixed {
preferred: my_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
let mut timer = ClockTest::new(STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_preferred,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
let expected_frame = build_address_claim_frame(my_inst.name.0, address::NULL_ADDR_254);
let retry_at_ms = STARTING_TIME + CANNOT_CLAIM_RETRY_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
cannot_claim(Some(expected_frame), retry_at_ms)
);
assert_eq!(my_inst.claimer.state, State::CannotClaim { retry_at_ms });
timer.tick(10);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
cannot_claim(Some(expected_frame), retry_at_ms)
);
assert_eq!(my_inst.claimer.state, State::CannotClaim { retry_at_ms });
timer.tick(CANNOT_CLAIM_RETRY_DELAY_MS as u64);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_preferred,
timer.ms + CLAIM_DELAY_MS as u64
)
);
}
#[test]
fn test_iso_request_is_ignored_when_not_for_us() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let mut timer = ClockTest::new(STARTING_TIME);
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(
my_inst.can_frame_origin,
my_preferred,
STARTING_TIME + CLAIM_DELAY_MS as u64
)
);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(my_inst.claimer.poll(timer.ms, None), claimed(my_preferred));
const _: () = assert!(ADDR_2 != ADDR_1 && ADDR_2 != GLOBAL);
let other_node_rx = build_request_frame(ADDR_2, CLAIM_PGN_60928, REQUEST_PGN_LEN);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&other_node_rx)),
claimed(my_preferred)
);
let other_pgn_rx = build_request_frame(GLOBAL, COMMAND_ADDR_65240, REQUEST_PGN_LEN);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&other_pgn_rx)),
claimed(my_preferred)
);
let truncated_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN - 1);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&truncated_rx)),
claimed(my_preferred)
);
assert_eq!(
my_inst.claimer.state,
State::Claimed {
frame: my_inst.can_frame_origin
}
);
}
#[test]
fn test_iso_request_payload_padding_is_not_part_of_the_pgn() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let mut timer = ClockTest::new(STARTING_TIME);
assert_eq!(request_rx.data[0..3], [0x00, 0xEE, 0x00]);
assert_eq!(request_rx.data[3..8], [0xFF; 5]);
my_inst.claimer.poll(timer.ms, None);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(my_inst.claimer.poll(timer.ms, None), claimed(my_preferred));
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
defending(my_inst.can_frame_origin, my_preferred)
);
}
#[test]
fn test_a_request_on_every_poll_does_not_starve_the_claim() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let mut timer = ClockTest::new(STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
for _ in 0..24 {
timer.tick(10);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
}
timer.tick(10);
assert!(timer.ms >= deadline_ms);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
defending(my_inst.can_frame_origin, my_preferred)
);
}
#[test]
fn test_continuous_we_win_conflicts_do_not_starve_the_claim() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToLose,
);
let their_rx = Some(&their_inst.can_frame_origin);
assert!(their_inst.name.0 > my_inst.name.0);
let mut timer = ClockTest::new(STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
for _ in 0..24 {
timer.tick(10);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
}
timer.tick(10);
assert!(timer.ms >= deadline_ms);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
defending(my_inst.can_frame_origin, my_preferred)
);
}
#[test]
fn test_a_request_on_every_poll_does_not_starve_the_cannot_claim_retry() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Fixed {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_strategy = AddressClaimStrategy::Fixed {
preferred: my_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let mut timer = ClockTest::new(STARTING_TIME);
let retry_at_ms = STARTING_TIME + CANNOT_CLAIM_RETRY_DELAY_MS as u64;
let cannot_claim_frame = build_address_claim_frame(my_inst.name.0, address::NULL_ADDR_254);
my_inst.claimer.poll(timer.ms, None);
assert_eq!(
my_inst
.claimer
.poll(timer.ms, Some(&their_inst.can_frame_origin)),
cannot_claim(Some(cannot_claim_frame), retry_at_ms)
);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
for _ in 0..99 {
timer.tick(100);
assert!(timer.ms < retry_at_ms);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
cannot_claim(Some(cannot_claim_frame), retry_at_ms)
);
}
timer.tick(100);
assert!(timer.ms >= retry_at_ms);
assert_eq!(
my_inst.claimer.poll(timer.ms, Some(&request_rx)),
claiming(
my_inst.can_frame_origin,
my_preferred,
timer.ms + CLAIM_DELAY_MS as u64
)
);
}
#[test]
fn test_a_lost_conflict_on_the_exact_deadline_outranks_it() {
let my_preferred = ADDR_3;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let their_rx = Some(&their_inst.can_frame_origin);
assert!(their_inst.name.0 < my_inst.name.0);
let mut timer = ClockTest::new(STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(timer.ms, deadline_ms);
let expected_frame = build_address_claim_frame(my_inst.name.0, my_preferred + 1);
assert_eq!(
my_inst.claimer.poll(timer.ms, their_rx),
claiming(
expected_frame,
my_preferred + 1,
timer.ms + CLAIM_DELAY_MS as u64
)
);
assert_eq!(my_inst.claimer.claimed_address(), None);
}
#[test]
fn test_a_request_on_the_deadline_yields_both_a_frame_and_the_address() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let mut timer = ClockTest::new(STARTING_TIME);
let deadline_ms = STARTING_TIME + CLAIM_DELAY_MS as u64;
assert_eq!(
my_inst.claimer.poll(timer.ms, None),
claiming(my_inst.can_frame_origin, my_preferred, deadline_ms)
);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(timer.ms, deadline_ms);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let output = my_inst.claimer.poll(timer.ms, Some(&request_rx));
assert_eq!(output.tx, Some(my_inst.can_frame_origin));
assert_eq!(output.status, ClaimStatus::Claimed(my_preferred));
assert_eq!(output.wake_at_ms, None);
}
#[test]
fn test_claimed_without_activity_has_no_deadline() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let mut timer = ClockTest::new(STARTING_TIME);
my_inst.claimer.poll(timer.ms, None);
timer.tick(CLAIM_DELAY_MS as u64);
assert_eq!(my_inst.claimer.poll(timer.ms, None), claimed(my_preferred));
timer.tick(6 * 3600 * 1000);
let output = my_inst.claimer.poll(timer.ms, None);
assert_eq!(output.tx, None);
assert_eq!(output.wake_at_ms, None);
assert_eq!(output.status, ClaimStatus::Claimed(my_preferred));
}
#[test]
fn test_deadlines_near_u64_max_do_not_panic() {
let my_preferred = ADDR_1;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let now_ms = u64::MAX - 10;
assert_eq!(
my_inst.claimer.poll(now_ms, None),
claiming(my_inst.can_frame_origin, my_preferred, u64::MAX)
);
assert_eq!(my_inst.claimer.poll(u64::MAX, None), claimed(my_preferred));
let fixed_strategy = AddressClaimStrategy::Fixed {
preferred: my_preferred,
};
let mut fixed_inst = Instance::new(
fixed_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let rival = Instance::new(
fixed_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
fixed_inst.claimer.poll(now_ms, None);
let expected_frame = build_address_claim_frame(fixed_inst.name.0, address::NULL_ADDR_254);
assert_eq!(
fixed_inst
.claimer
.poll(now_ms, Some(&rival.can_frame_origin)),
cannot_claim(Some(expected_frame), u64::MAX)
);
}
#[test]
fn test_status_and_claimed_address_never_disagree() {
let my_preferred = ADDR_3;
let my_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let mut my_inst = Instance::new(
my_strategy,
CanFrameClass::Claiming,
ConflictPriority::Normal,
);
let their_strategy = AddressClaimStrategy::Arbitrary {
preferred: my_preferred,
};
let their_inst = Instance::new(
their_strategy,
CanFrameClass::Claiming,
ConflictPriority::BuiltToWin,
);
let request_rx = build_request_frame(GLOBAL, CLAIM_PGN_60928, REQUEST_PGN_LEN);
let mut timer = ClockTest::new(STARTING_TIME);
let script: [Option<&CanFrame>; 6] = [
None,
None,
None,
Some(&request_rx),
Some(&their_inst.can_frame_origin),
None,
];
for rx in script {
let output = my_inst.claimer.poll(timer.ms, rx);
assert_eq!(
output.status.claimed_address(),
my_inst.claimer.claimed_address(),
"status {:?} disagrees with claimed_address()",
output.status
);
timer.tick(CLAIM_DELAY_MS as u64);
}
}