use super::*;
use bacnet_types::enums::{ErrorClass, ErrorCode};
fn code(error: Error) -> (u32, u32) {
match error {
Error::Protocol { class, code } => (class, code),
other => panic!("expected protocol error, got {other:?}"),
}
}
#[test]
fn unknown_writes_classify_every_builtin_with_null_and_ordinary_values() {
let mut objects = supported_representatives();
audit_object_type_coverage(&objects);
let mut failures = Vec::new();
for object in &mut objects {
for value in [PropertyValue::Null, PropertyValue::Unsigned(17)] {
for (property, expected) in [
(
PropertyIdentifier::from_raw(5555),
ErrorCode::UNKNOWN_PROPERTY,
),
(
PropertyIdentifier::OBJECT_IDENTIFIER,
ErrorCode::WRITE_ACCESS_DENIED,
),
(
PropertyIdentifier::PROPERTY_LIST,
ErrorCode::WRITE_ACCESS_DENIED,
),
] {
let actual = code(
object
.write_property(property, None, value.clone(), None)
.unwrap_err(),
);
if actual
!= (
ErrorClass::PROPERTY.to_raw() as u32,
expected.to_raw() as u32,
)
{
failures.push(format!(
"{:?} {property:?} {value:?}: {actual:?}, expected {expected:?}",
object.object_identifier()
));
}
}
}
}
assert!(failures.is_empty(), "{}", failures.join("\n"));
for object in &mut objects {
let expected = match object.object_identifier().object_type() {
ObjectType::NETWORK_PORT => ErrorCode::UNKNOWN_PROPERTY,
ObjectType::STAGING | ObjectType::CHANNEL => ErrorCode::PROPERTY_IS_NOT_AN_ARRAY,
_ => ErrorCode::WRITE_ACCESS_DENIED,
};
for index in [0, 1] {
assert_eq!(
code(
object
.write_property(
PropertyIdentifier::from_raw(5555),
Some(index),
PropertyValue::Null,
None
)
.unwrap_err()
),
(2, expected.to_raw() as u32),
"{:?}",
object.object_identifier()
);
}
}
}
#[test]
fn unknown_writes_optional_staging_names_use_current_presence() {
let mut config = staging_config();
config.stage_names = None;
let mut absent = StagingObject::new(1, "absent", config).unwrap();
assert!(!absent
.property_metadata()
.iter()
.any(|row| row.property_identifier == PropertyIdentifier::STAGE_NAMES));
for value in [PropertyValue::Null, PropertyValue::List(vec![])] {
assert_eq!(
code(
absent
.write_property(PropertyIdentifier::STAGE_NAMES, None, value, None)
.unwrap_err()
),
(2, ErrorCode::UNKNOWN_PROPERTY.to_raw() as u32)
);
}
assert_eq!(
code(
absent
.write_property(
PropertyIdentifier::STAGE_NAMES,
Some(0),
PropertyValue::Null,
None
)
.unwrap_err()
),
(2, ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32)
);
let mut present = StagingObject::new(2, "present", staging_config()).unwrap();
assert_eq!(
code(
present
.write_property(
PropertyIdentifier::STAGE_NAMES,
Some(0),
PropertyValue::Unsigned(3),
None
)
.unwrap_err()
),
(2, ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32)
);
let names = PropertyValue::List(vec![PropertyValue::CharacterString("new".into()); 3]);
present
.write_property(PropertyIdentifier::STAGE_NAMES, None, names.clone(), None)
.unwrap();
assert_eq!(
present
.read_property(PropertyIdentifier::STAGE_NAMES, None)
.unwrap(),
names
);
}
#[test]
fn unknown_writes_file_record_count_distinguishes_absence_from_read_only() {
use bacnet_types::enums::FileAccessMethod;
for records in [false, true] {
let mut file = FileObject::new(1, "File", "raw").unwrap();
if records {
file.set_file_access_method(FileAccessMethod::RECORD_ACCESS.to_raw());
file.set_read_only(true);
}
assert_eq!(
file.property_metadata()
.iter()
.any(|row| row.property_identifier == PropertyIdentifier::RECORD_COUNT),
records
);
for value in [PropertyValue::Null, PropertyValue::Unsigned(17)] {
let expected = if records {
ErrorCode::WRITE_ACCESS_DENIED
} else {
ErrorCode::UNKNOWN_PROPERTY
};
assert_eq!(
code(
file.write_property(PropertyIdentifier::RECORD_COUNT, None, value, None)
.unwrap_err()
),
(2, expected.to_raw() as u32)
);
}
assert_eq!(
code(
file.write_property(
PropertyIdentifier::RECORD_COUNT,
Some(0),
PropertyValue::Null,
None
)
.unwrap_err()
),
(2, ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32)
);
}
}