use bacnet_transport::loopback::LoopbackTransport;
use bacnet_transport::port::TransportPort;
use bacnet_types::enums::{NetworkMessageType, RejectMessageReason};
use crate::layer::NetworkLayer;
use crate::loopback_fixture::{next_from_router, recv, reject_of, wire, RouterFixture, APDU};
use bacnet_encoding::npdu::{decode_npdu, NpduAddress, RejectMessageToNetwork};
const DADR_LENGTHS: [u8; 3] = [1, 6, NpduAddress::MAX_MAC_LEN as u8];
fn global(dlen: u8, control: Option<u8>) -> Vec<u8> {
wire(Some((0xFFFF, dlen)), None, control)
}
#[tokio::test]
async fn non_router_drops_and_counts_a_global_broadcast_that_names_a_device() {
let (transport, mut peer) = LoopbackTransport::pair(vec![0x01], vec![0x02]);
let mut network = NetworkLayer::new(transport);
let mut controls = network.enable_network_control_receiver().unwrap();
let mut apdus = network.start().await.unwrap();
let control = Some(NetworkMessageType::I_AM_ROUTER_TO_NETWORK.to_raw());
for dlen in DADR_LENGTHS {
peer.send_unicast(&global(dlen, None), &[0x01])
.await
.unwrap();
peer.send_broadcast(&global(dlen, control)).await.unwrap();
}
for bytes in [wire(None, None, None), global(0, None)] {
peer.send_unicast(&bytes, &[0x01]).await.unwrap();
}
for bytes in [wire(None, None, control), global(0, control)] {
peer.send_unicast(&bytes, &[0x01]).await.unwrap();
}
let local = recv(&mut apdus).await;
assert_eq!(local.apdu, APDU[..]);
assert!(!local.global_broadcast, "the local APDU comes out first");
let delivered = recv(&mut apdus).await;
assert!(delivered.global_broadcast && delivered.is_group);
let local = recv(&mut controls).await;
assert_eq!(
local.npdu.destination, None,
"the local control comes first"
);
let delivered = recv(&mut controls).await;
let destination = delivered.npdu.destination.expect("a global broadcast");
assert_eq!(
(destination.network, destination.mac_address.len()),
(0xFFFF, 0)
);
assert_eq!(
network.global_broadcast_dadr_drops(),
2 * DADR_LENGTHS.len() as u64
);
assert_eq!(network.address_length_drops(), 0, "the lengths were fine");
network.stop().await.unwrap();
peer.stop().await.unwrap();
}
#[tokio::test]
async fn router_neither_forwards_nor_delivers_nor_answers_a_global_broadcast_that_names_a_device() {
let mut fixture = RouterFixture::start().await;
let who_is_router = Some(NetworkMessageType::WHO_IS_ROUTER_TO_NETWORK.to_raw());
for dlen in DADR_LENGTHS {
fixture.send_from_a(&global(dlen, None)).await;
fixture
.peer_a
.send_broadcast(&global(dlen, who_is_router))
.await
.unwrap();
}
fixture
.send_from_a(&wire(Some((5000, 0)), None, None))
.await;
let (frame, to) = fixture.from_router_a.recv().await;
assert_eq!(to.as_deref(), Some(&[0x0A][..]));
assert_eq!(
reject_of(&decode_npdu(frame.npdu).unwrap()),
RejectMessageToNetwork {
reason: RejectMessageReason::NOT_DIRECTLY_CONNECTED,
dnet: 5000,
}
);
let (solicit, _) = next_from_router(&mut fixture.from_router_b).await;
assert_eq!(solicit.message_type, who_is_router);
assert_eq!(solicit.payload[..], 5000u16.to_be_bytes());
fixture.send_from_a(&wire(None, None, None)).await;
let local = recv(&mut fixture.local).await;
assert!(!local.global_broadcast, "the local APDU comes out first");
fixture.send_from_a(&global(0, None)).await;
let (forwarded, to) = next_from_router(&mut fixture.from_router_b).await;
assert_eq!(to, None, "a data-link broadcast");
assert_eq!(forwarded.payload, APDU[..]);
let destination = forwarded.destination.expect("still a global broadcast");
assert_eq!(
(destination.network, destination.mac_address.len()),
(0xFFFF, 0)
);
assert_eq!(forwarded.source.expect("SNET added").network, 1000);
assert!(recv(&mut fixture.local).await.global_broadcast);
assert_eq!(
fixture.router.global_broadcast_dadr_drops(),
2 * DADR_LENGTHS.len() as u64
);
assert_eq!(
fixture.router.address_length_drops(),
0,
"the lengths were fine"
);
fixture.stop().await;
}