use bacnet_npdu::Mac;
use bacnet_router_core::PortId;
use bacnet_router_core::table::Reachability;
use super::priority_harness::*;
use super::{BACnetRouter, Duplicates, RouterOptions};
async fn until(router: &BACnetRouter, done: impl Fn(&BACnetRouter) -> bool) {
for _ in 0..100_000 {
if done(router) {
return;
}
tokio::task::yield_now().await;
}
panic!("never got there: {:?}", router.counters());
}
const GLOBAL_WHO_IS: [u8; 8] = [1, 0x20, 0xFF, 0xFF, 0, 255, 0x10, 0x08];
#[tokio::test]
async fn a_repeated_global_broadcast_is_dropped_when_asked() {
let options = RouterOptions::new().duplicates(Duplicates::DEFAULT);
let (mut router, mut apdus, mut peers) = start_with(2, options).await;
receive(&peers[0], GLOBAL_WHO_IS.to_vec(), 0).await;
let relayed = sending(&mut peers[1]).await;
assert_eq!(relayed.to, None);
relayed.complete.send(Ok(())).unwrap();
assert_eq!(apdus.recv().await.unwrap().apdu.as_ref(), [0x10, 0x08]);
receive(&peers[0], GLOBAL_WHO_IS.to_vec(), 0).await;
until(&router, |router| router.counters().duplicate_drops == 1).await;
assert!(peers[1].sent.try_recv().is_err(), "not relayed again");
assert!(apdus.try_recv().is_err(), "not delivered again");
router.stop().await.unwrap();
}
#[tokio::test]
async fn by_default_a_repeat_goes_through() {
let (mut router, mut apdus, mut peers) = start(2).await;
for _ in 0..2 {
receive(&peers[0], GLOBAL_WHO_IS.to_vec(), 0).await;
sending(&mut peers[1]).await.complete.send(Ok(())).unwrap();
assert_eq!(apdus.recv().await.unwrap().apdu.as_ref(), [0x10, 0x08]);
}
assert_eq!(router.counters().duplicate_drops, 0);
router.stop().await.unwrap();
}
#[tokio::test]
async fn the_routers_own_global_broadcast_coming_back_is_dropped() {
let own = vec![1, 0x28, 0xFF, 0xFF, 0, 0x07, 0xD0, 1, 2, 255, 0x10, 0x08];
let (mut router, mut apdus, mut peers) = start(2).await;
receive(&peers[0], own.clone(), 0).await;
receive(&peers[0], own, 0).await;
until(&router, |router| router.counters().own_source_drops == 2).await;
assert!(peers[1].sent.try_recv().is_err(), "not relayed");
assert!(apdus.try_recv().is_err(), "not delivered");
router.stop().await.unwrap();
}
async fn off_path_reject(options: RouterOptions) -> (Option<Reachability>, u64) {
let (mut router, _apdus, peers) = start_with(2, options).await;
let now = router.now();
assert!(
router
.table()
.add_learned(3000, PortId(1), Mac::from_array([7]), now)
);
let reject = vec![1, 0x80, 0x03, 1, 0x0B, 0xB8];
receive(&peers[0], reject, 0).await;
until(&router, |router| {
let unreachable = router
.table()
.lookup(3000)
.is_some_and(|route| route.reachability() == Reachability::Unreachable);
unreachable || router.counters().not_next_hop > 0
})
.await;
let reachability = router
.table()
.lookup(3000)
.map(|route| route.reachability());
let counted = router.counters().not_next_hop;
router.stop().await.unwrap();
(reachability, counted)
}
#[tokio::test]
async fn next_hop_rejects_leave_an_off_path_route_alone_when_asked() {
assert_eq!(
off_path_reject(RouterOptions::new().next_hop_rejects()).await,
(Some(Reachability::Reachable), 1)
);
assert_eq!(
off_path_reject(RouterOptions::new()).await,
(Some(Reachability::Unreachable), 0)
);
}