use bacnet_npdu::{Address, Mac, NetworkPriority};
use bacnet_router_core::PortId;
use super::priority_harness::*;
use super::send_queue::CAPACITY;
use super::{OriginateError, OriginateFailure, Request, SourceSelection};
const READ_PROPERTY: [u8; 4] = [0x00, 0x05, 0x01, 0x0C];
fn to_3000(source: SourceSelection) -> Request<'static> {
let mut request = Request::new(
Address {
network: 3000,
mac: &[5],
},
&READ_PROPERTY,
);
request.expecting_reply = true;
request.source = source;
request
}
#[tokio::test]
async fn a_request_is_routed_by_the_table_and_its_reply_comes_home() {
let (mut router, mut apdus, mut peers) = start(2).await;
let now = router.now();
assert!(
router
.table()
.add_learned(3000, PortId(1), Mac::from_array([7]), now)
);
router
.originate(&to_3000(SourceSelection::EgressPort))
.unwrap();
let sent = sending(&mut peers[1]).await;
assert_eq!(sent.to, Some(vec![7]));
assert_eq!(
sent.npdu,
[&[1, 0x24, 0x0B, 0xB8, 1, 5, 255][..], &READ_PROPERTY].concat()
);
assert!(sent.attributes.is_empty());
sent.complete.send(Ok(())).unwrap();
let mut urgent = to_3000(SourceSelection::Port(PortId(0)));
urgent.priority = NetworkPriority::Urgent;
router.originate(&urgent).unwrap();
let sent = sending(&mut peers[1]).await;
assert_eq!(
sent.npdu,
[
&[1, 0x2D, 0x0B, 0xB8, 1, 5, 0x03, 0xE8, 1, 1, 255][..],
&READ_PROPERTY
]
.concat()
);
sent.complete.send(Ok(())).unwrap();
assert!(peers[0].sent.try_recv().is_err(), "nothing on port 0");
assert_eq!(router.counters().originated, 2);
let reply = [1, 0x08, 0x0B, 0xB8, 1, 5, 0x30, 0x05, 0x0C];
receive(&peers[1], reply.to_vec(), 0).await;
let delivered = apdus.recv().await.unwrap();
assert_eq!(delivered.apdu.as_ref(), [0x30, 0x05, 0x0C]);
assert_eq!(delivered.ingress_network, Some(2000));
let source = delivered.source_network.expect("its SNET");
assert_eq!(
(source.network, source.mac_address.as_slice()),
(3000, &[5][..])
);
router.stop().await.unwrap();
}
#[tokio::test]
async fn no_route_searches_and_refuses() {
let (mut router, _apdus, mut peers) = start(2).await;
assert_eq!(
router.originate(&to_3000(SourceSelection::EgressPort)),
Err(OriginateFailure::Refused(OriginateError::NoRoute))
);
for peer in &mut peers {
let search = sending(peer).await;
assert_eq!(search.to, None);
assert_eq!(search.npdu, [1, 0x80, 0x00, 0x0B, 0xB8]);
search.complete.send(Ok(())).unwrap();
}
assert_eq!(router.counters().originated, 0);
router.stop().await.unwrap();
}
#[tokio::test]
async fn a_full_queue_refuses_and_a_stopped_router_refuses_everything() {
let (mut router, _apdus, mut peers) = start(2).await;
let now = router.now();
assert!(
router
.table()
.add_learned(3000, PortId(1), Mac::from_array([7]), now)
);
let request = to_3000(SourceSelection::EgressPort);
router.originate(&request).unwrap();
let _in_progress = sending(&mut peers[1]).await;
for _ in 0..CAPACITY {
router.originate(&request).unwrap();
}
assert_eq!(router.originate(&request), Err(OriginateFailure::QueueFull));
assert_eq!(router.counters().refused, 1);
router.stop().await.unwrap();
assert_eq!(router.originate(&request), Err(OriginateFailure::Stopped));
}
fn to_port(network: u16) -> Request<'static> {
Request::new(Address { network, mac: &[5] }, &READ_PROPERTY)
}
#[tokio::test(start_paused = true)]
async fn a_busy_refused_in_the_same_drain_doesnt_fail_a_queued_request() {
let options = super::RouterOptions::new().congestion(super::Congestion::DEFAULT);
let (mut router, _apdus, mut peers) = start_with(2, options).await;
router.originate(&to_port(1000)).unwrap();
let _port_0 = sending(&mut peers[0]).await;
for _ in 0..CAPACITY {
router.originate(&to_port(1000)).unwrap();
}
router.originate(&to_port(2000)).unwrap();
let in_progress = sending(&mut peers[1]).await;
for _ in 0..CAPACITY {
router.originate(&to_port(2000)).unwrap();
}
in_progress.complete.send(Ok(())).unwrap();
let _port_1 = sending(&mut peers[1]).await;
tokio::time::advance(std::time::Duration::from_secs(2)).await;
let refused = router.counters().refused;
assert_eq!(router.originate(&to_port(2000)), Ok(()));
let counts = router.counters();
assert!(counts.busy_announced >= 1);
assert!(counts.refused > refused, "the Busy was refused");
assert_eq!(
router.originate(&to_port(2000)),
Err(OriginateFailure::QueueFull)
);
router.stop().await.unwrap();
}
#[tokio::test]
async fn a_global_broadcast_leaves_by_every_port() {
const WHO_IS: [u8; 2] = [0x10, 0x08];
let (mut router, _apdus, mut peers) = start(3).await;
let global = |source| {
let mut request = Request::new(
Address {
network: 0xFFFF,
mac: &[],
},
&WHO_IS,
);
request.source = source;
request
};
let bare = [&[1, 0x20, 0xFF, 0xFF, 0, 255][..], &WHO_IS].concat();
router
.originate(&global(SourceSelection::EgressPort))
.unwrap();
for peer in &mut peers {
let sent = sending(peer).await;
assert_eq!(sent.to, None, "the link's broadcast");
assert_eq!(sent.npdu, bare);
assert!(sent.attributes.is_empty());
sent.complete.send(Ok(())).unwrap();
}
router
.originate(&global(SourceSelection::Port(PortId(1))))
.unwrap();
let sourced = [
&[1, 0x28, 0xFF, 0xFF, 0, 0x07, 0xD0, 1, 2, 255][..],
&WHO_IS,
]
.concat();
for (index, peer) in peers.iter_mut().enumerate() {
let sent = sending(peer).await;
assert_eq!(sent.to, None, "port {index}");
let expected = if index == 1 { &bare } else { &sourced };
assert_eq!(&sent.npdu, expected, "port {index}");
sent.complete.send(Ok(())).unwrap();
}
assert_eq!(router.counters().originated, 2);
router.stop().await.unwrap();
}