use crate::constructed::{
decode_property_reference, decode_read_access_specification, encode_property_reference,
encode_read_access_specification,
};
use crate::primitives;
use bacnet_types::constructed::{PropertyReference, ReadAccessSpecification};
use bacnet_types::enums::{ObjectType, PropertyIdentifier};
use bacnet_types::error::Error;
use bacnet_types::primitives::ObjectIdentifier;
use bytes::BytesMut;
fn reference(property: PropertyIdentifier, index: Option<u32>) -> PropertyReference {
PropertyReference {
property_identifier: property,
property_array_index: index,
}
}
#[test]
fn property_references_encode_and_decode_with_and_without_an_index() {
for (value, octets) in [
(
reference(PropertyIdentifier::PRESENT_VALUE, None),
&[0x09, 85][..],
),
(
reference(PropertyIdentifier::PRIORITY_ARRAY, Some(8)),
&[0x09, 87, 0x19, 8][..],
),
] {
let mut buf = BytesMut::new();
encode_property_reference(&mut buf, &value);
assert_eq!(&buf[..], octets);
assert_eq!(
decode_property_reference(octets, 0).unwrap(),
(value, octets.len())
);
}
}
#[test]
fn malformed_property_references_are_refused() {
let mut indexed = BytesMut::new();
encode_property_reference(
&mut indexed,
&reference(PropertyIdentifier::PRESENT_VALUE, Some(8)),
);
for bad in [&[][..], &indexed[..1], &[0xFF, 0xFF, 0xFF][..]] {
assert!(decode_property_reference(bad, 0).is_err(), "{bad:02X?}");
}
for overflow in [u64::from(u32::MAX) + 1, u64::MAX] {
let mut property = BytesMut::new();
primitives::encode_ctx_unsigned(&mut property, 0, overflow);
assert!(decode_property_reference(&property, 0).is_err());
let mut index = BytesMut::new();
primitives::encode_ctx_unsigned(&mut index, 0, 1);
primitives::encode_ctx_unsigned(&mut index, 1, overflow);
assert!(decode_property_reference(&index, 0).is_err());
}
}
#[test]
fn read_access_specifications_decode_one_element_at_an_offset() {
let spec = ReadAccessSpecification {
object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_VALUE, 2).unwrap(),
list_of_property_references: vec![
reference(PropertyIdentifier::PRESENT_VALUE, None),
reference(PropertyIdentifier::PRIORITY_ARRAY, Some(16)),
],
};
let octets = [
0x0C, 0, 0x80, 0, 2, 0x1E, 0x09, 85, 0x09, 87, 0x19, 16, 0x1F,
];
let mut buf = BytesMut::from(&b"xy"[..]);
encode_read_access_specification(&mut buf, &spec);
assert_eq!(&buf[2..], &octets);
buf.extend_from_slice(&[0xAA]);
assert_eq!(
decode_read_access_specification(&buf, 2).unwrap(),
(spec, 2 + octets.len())
);
let empty = ReadAccessSpecification {
object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_VALUE, 2).unwrap(),
list_of_property_references: vec![],
};
let mut encoded = BytesMut::new();
encode_read_access_specification(&mut encoded, &empty);
assert_eq!(&encoded[..], &[0x0C, 0, 0x80, 0, 2, 0x1E, 0x1F]);
assert_eq!(
decode_read_access_specification(&encoded, 0).unwrap(),
(empty, encoded.len())
);
}
#[test]
fn malformed_read_access_specifications_are_refused() {
let octets = [
0x0C, 0, 0x80, 0, 2, 0x1E, 0x09, 85, 0x09, 87, 0x19, 16, 0x1F,
];
for end in [0, 4, 5, 6, 9, octets.len() - 1] {
assert!(decode_read_access_specification(&octets[..end], 0).is_err());
}
assert!(decode_read_access_specification(&[0x1C, 0, 0x80, 0, 2, 0x1E, 0x1F], 0).is_err());
assert!(decode_read_access_specification(&[0x0C, 0, 0x80, 0, 2, 0x2E, 0x2F], 0).is_err());
}
fn short<T: std::fmt::Debug>(result: Result<T, Error>) -> (usize, usize) {
match result {
Err(Error::BufferTooShort { need, have }) => (need, have),
other => panic!("expected a short buffer, got {other:?}"),
}
}
#[test]
fn contents_cut_short_are_a_short_buffer() {
assert_eq!(short(decode_property_reference(&[0x0A, 0x55], 0)), (3, 2));
assert_eq!(
short(decode_property_reference(&[0x09, 0x55, 0x1A, 0x00], 0)),
(5, 4)
);
assert_eq!(
short(decode_read_access_specification(&[0x0C, 0, 0x80, 0], 0)),
(5, 4)
);
assert_eq!(
short(decode_read_access_specification(
&[0x0C, 0, 0x80, 0, 2, 0x1E, 0x0A, 0x00],
0
)),
(9, 8)
);
}