bacnet-objects 0.15.0

BACnet object model: traits, database, and standard object types
Documentation
//! Shared Device identity validation and segmentation-derived declarations.
use super::*;
use bacnet_types::error::RemoteError;

#[test]
fn mode_derived_max_segments_accepted() {
    let cases = [
        ("none", Segmentation::NONE, None),
        ("transmit", Segmentation::TRANSMIT, Some(1)),
        ("receive", Segmentation::RECEIVE, Some(65)),
        ("both", Segmentation::BOTH, Some(65)),
    ];

    for (name, segmentation, expected_max_segments) in cases {
        let dev = DeviceObject::new(DeviceConfig {
            identity: crate::DeviceIdentity::default().with_segmentation(segmentation),
            ..DeviceConfig::default()
        })
        .unwrap();

        assert_eq!(
            dev.read_property(PropertyIdentifier::SEGMENTATION_SUPPORTED, None)
                .unwrap(),
            PropertyValue::Enumerated(segmentation.to_raw() as u32),
            "{name} segmentation readback"
        );

        let PropertyValue::List(property_list) = dev
            .read_property(PropertyIdentifier::PROPERTY_LIST, None)
            .unwrap()
        else {
            panic!("{name} Property_List was not a list");
        };
        assert_eq!(
            property_list.contains(&PropertyValue::Enumerated(
                PropertyIdentifier::MAX_SEGMENTS_ACCEPTED.to_raw(),
            )),
            expected_max_segments.is_some(),
            "{name} Property_List presence"
        );

        let max_segments = dev.read_property(PropertyIdentifier::MAX_SEGMENTS_ACCEPTED, None);
        match expected_max_segments {
            Some(expected) => assert_eq!(
                max_segments.unwrap(),
                PropertyValue::Unsigned(expected),
                "{name} Max_Segments_Accepted"
            ),
            None => assert!(matches!(
                max_segments,
                Err(Error::Remote(RemoteError::Error { class, code }))
                    if class == ErrorClass::PROPERTY
                        && code == ErrorCode::UNKNOWN_PROPERTY
            )),
        }
    }
}

#[test]
fn segment_timeout_absence_custom_value_and_zero_validation() {
    let none = DeviceObject::new(DeviceConfig {
        identity: crate::DeviceIdentity::default().with_apdu_segment_timeout_ms(0),
        ..Default::default()
    })
    .unwrap();
    assert!(
        matches!(none.read_property(PropertyIdentifier::APDU_SEGMENT_TIMEOUT, None),
        Err(Error::Remote(RemoteError::Error { code, .. })) if code == ErrorCode::UNKNOWN_PROPERTY)
    );
    assert!(
        !none
            .property_list()
            .contains(&PropertyIdentifier::APDU_SEGMENT_TIMEOUT)
    );
    for mode in [
        Segmentation::TRANSMIT,
        Segmentation::RECEIVE,
        Segmentation::BOTH,
    ] {
        assert!(
            DeviceObject::new(DeviceConfig {
                identity: crate::DeviceIdentity::default()
                    .with_segmentation(mode)
                    .with_apdu_segment_timeout_ms(0),
                ..Default::default()
            })
            .is_err()
        );
        let device = DeviceObject::new(DeviceConfig {
            identity: crate::DeviceIdentity::default()
                .with_segmentation(mode)
                .with_apdu_segment_timeout_ms(7300),
            ..Default::default()
        })
        .unwrap();
        assert_eq!(
            device
                .read_property(PropertyIdentifier::APDU_SEGMENT_TIMEOUT, None)
                .unwrap(),
            PropertyValue::Unsigned(7300)
        );
        assert_eq!(
            device
                .read_property(PropertyIdentifier::APDU_TIMEOUT, None)
                .unwrap(),
            PropertyValue::Unsigned(6000)
        );
        assert_eq!(
            device
                .read_property(PropertyIdentifier::NUMBER_OF_APDU_RETRIES, None)
                .unwrap(),
            PropertyValue::Unsigned(3)
        );
        assert!(
            device
                .property_list()
                .contains(&PropertyIdentifier::APDU_SEGMENT_TIMEOUT)
        );
    }
}

#[test]
fn segment_timeout_is_present_for_segmented_devices() {
    for segmentation in [
        Segmentation::TRANSMIT,
        Segmentation::RECEIVE,
        Segmentation::BOTH,
    ] {
        let dev = DeviceObject::new(DeviceConfig {
            identity: crate::DeviceIdentity::default().with_segmentation(segmentation),
            ..DeviceConfig::default()
        })
        .unwrap();
        assert_eq!(
            dev.read_property(PropertyIdentifier::APDU_SEGMENT_TIMEOUT, None)
                .unwrap(),
            PropertyValue::Unsigned(5000)
        );
    }
}

#[test]
fn invalid_segmentation_is_rejected_at_construction() {
    assert!(
        DeviceObject::new(DeviceConfig {
            identity: crate::DeviceIdentity::default()
                .with_segmentation(Segmentation::from_raw(64)),
            ..DeviceConfig::default()
        })
        .is_err()
    );
}