use super::*;
use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType, PropertyIdentifier};
fn oid(object_type: ObjectType, instance: u32) -> ObjectIdentifier {
ObjectIdentifier::new(object_type, instance).unwrap()
}
fn assert_property_error(error: Error, code: ErrorCode) {
assert!(
matches!(error, Error::Protocol { class, code: c }
if class == ErrorClass::PROPERTY.to_raw() as u32 && c == code.to_raw() as u32),
"expected PROPERTY / {code:?}, got {error:?}"
);
}
const FULL: [u8; 14] = [
0x0C, 0x00, 0x80, 0x00, 0x07, 0x19, 0x55, 0x29, 0x02, 0x3C, 0x02, 0x00, 0x00, 0x09, ];
fn full() -> BACnetDeviceObjectPropertyReference {
BACnetDeviceObjectPropertyReference {
object_identifier: oid(ObjectType::ANALOG_VALUE, 7),
property_identifier: PropertyIdentifier::PRESENT_VALUE.to_raw(),
property_array_index: Some(2),
device_identifier: Some(oid(ObjectType::DEVICE, 9)),
}
}
#[test]
fn property_reference_value_is_the_context_tagged_members() {
assert_eq!(
reference_value(&full()),
PropertyValue::ApplicationData(FULL.to_vec())
);
let local = BACnetDeviceObjectPropertyReference::new_local(
oid(ObjectType::ANALOG_VALUE, 7),
PropertyIdentifier::PRESENT_VALUE.to_raw(),
);
assert_eq!(
reference_value(&local),
PropertyValue::ApplicationData(FULL[..7].to_vec())
);
}
#[test]
fn object_reference_list_is_one_encoding_per_element() {
let remote = BACnetDeviceObjectReference {
device_identifier: Some(oid(ObjectType::DEVICE, 9)),
object_identifier: oid(ObjectType::LIFE_SAFETY_ZONE, 3),
};
let local = BACnetDeviceObjectReference::from(oid(ObjectType::LIFE_SAFETY_ZONE, 4));
assert_eq!(
reference_list(&[remote, local]),
PropertyValue::List(vec![
PropertyValue::ApplicationData(vec![
0x0C, 0x02, 0x00, 0x00, 0x09, 0x1C, 0x05, 0x80, 0x00, 0x03, ]),
PropertyValue::ApplicationData(vec![0x1C, 0x05, 0x80, 0x00, 0x04]),
])
);
}
#[test]
fn decode_accepts_raw_bytes_and_the_read_shape() {
let mut two = FULL.to_vec();
two.extend_from_slice(&FULL[..7]);
let expected = vec![
full(),
BACnetDeviceObjectPropertyReference::new_local(
oid(ObjectType::ANALOG_VALUE, 7),
PropertyIdentifier::PRESENT_VALUE.to_raw(),
),
];
assert_eq!(
decode_references::<BACnetDeviceObjectPropertyReference>(&PropertyValue::ApplicationData(
two
))
.unwrap(),
expected
);
let read_shape = PropertyValue::List(vec![
PropertyValue::ApplicationData(FULL.to_vec()),
PropertyValue::ApplicationData(FULL[..7].to_vec()),
]);
assert_eq!(
decode_references::<BACnetDeviceObjectPropertyReference>(&read_shape).unwrap(),
expected
);
assert_eq!(
decode_references::<BACnetDeviceObjectPropertyReference>(&PropertyValue::List(Vec::new()))
.unwrap(),
Vec::new()
);
assert_eq!(
decode_reference::<BACnetDeviceObjectPropertyReference>(&PropertyValue::ApplicationData(
FULL.to_vec()
))
.unwrap(),
full()
);
}
#[test]
fn decode_refuses_other_shapes_with_the_matching_error() {
for value in [
PropertyValue::Null,
PropertyValue::ObjectIdentifier(oid(ObjectType::ANALOG_VALUE, 7)),
PropertyValue::List(vec![
PropertyValue::ObjectIdentifier(oid(ObjectType::ANALOG_VALUE, 7)),
PropertyValue::Unsigned(85),
]),
PropertyValue::ApplicationData(vec![0xC4, 0x00, 0x80, 0x00, 0x07]),
] {
assert_property_error(
decode_references::<BACnetDeviceObjectPropertyReference>(&value).unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
}
for bytes in [
FULL[..5].to_vec(), FULL[..12].to_vec(), vec![0x0C, 0x00, 0x80, 0x00], ] {
assert_property_error(
decode_references::<BACnetDeviceObjectPropertyReference>(
&PropertyValue::ApplicationData(bytes),
)
.unwrap_err(),
ErrorCode::INVALID_DATA_ENCODING,
);
}
let mut trailing = FULL.to_vec();
trailing.extend_from_slice(&[0x49, 0x01]); assert_property_error(
decode_references::<BACnetDeviceObjectPropertyReference>(&PropertyValue::ApplicationData(
trailing,
))
.unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
let mut two = FULL.to_vec();
two.extend_from_slice(&FULL);
let after = |trailing: &[u8]| PropertyValue::ApplicationData([&FULL[..], trailing].concat());
for value in [
PropertyValue::ApplicationData(two),
PropertyValue::ApplicationData(Vec::new()),
PropertyValue::List(Vec::new()),
after(&[0x49, 0x01]), after(&[0x21, 0x01]), after(&[0x09, 0x01]), ] {
assert_property_error(
decode_reference::<BACnetDeviceObjectPropertyReference>(&value).unwrap_err(),
ErrorCode::INVALID_DATA_ENCODING,
);
}
for value in [
PropertyValue::Null,
PropertyValue::ApplicationData(vec![0xC4, 0x00, 0x80, 0x00, 0x07]),
PropertyValue::ApplicationData(vec![0x19, 0x55]),
] {
assert_property_error(
decode_reference::<BACnetDeviceObjectPropertyReference>(&value).unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
}
let object = [0x1C, 0x05, 0x80, 0x00, 0x03]; assert!(
decode_reference::<BACnetDeviceObjectReference>(&PropertyValue::ApplicationData(
object.to_vec()
))
.is_ok()
);
assert_property_error(
decode_reference::<BACnetDeviceObjectReference>(&PropertyValue::ApplicationData(
[&object[..], &[0x49, 0x01]].concat(),
))
.unwrap_err(),
ErrorCode::INVALID_DATA_ENCODING,
);
assert_property_error(
decode_reference::<BACnetDeviceObjectReference>(&PropertyValue::ApplicationData(vec![
0x29, 0x01,
]))
.unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
}
#[test]
fn device_member_must_be_a_device() {
check_device_member(None).unwrap();
check_device_member(Some(oid(ObjectType::DEVICE, 9))).unwrap();
assert_property_error(
check_device_member(Some(oid(ObjectType::ANALOG_INPUT, 9))).unwrap_err(),
ErrorCode::VALUE_OUT_OF_RANGE,
);
}