use super::super::*;
#[test]
fn create_event_enrollment() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
assert_eq!(
ee.object_identifier().object_type(),
ObjectType::EVENT_ENROLLMENT
);
assert_eq!(ee.object_identifier().instance_number(), 1);
assert_eq!(ee.object_name(), "EE-1");
}
#[test]
fn read_object_type() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let val = ee
.read_property(PropertyIdentifier::OBJECT_TYPE, None)
.unwrap();
assert_eq!(
val,
PropertyValue::Enumerated(ObjectType::EVENT_ENROLLMENT.to_raw())
);
}
#[test]
fn read_event_type() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::COMMAND_FAILURE).unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_TYPE, None)
.unwrap();
assert_eq!(val, PropertyValue::Enumerated(3));
}
#[test]
fn read_event_type_preserves_a_proprietary_value() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::from_raw(600)).unwrap();
assert_eq!(
ee.read_property(PropertyIdentifier::EVENT_TYPE, None)
.unwrap(),
PropertyValue::Enumerated(600)
);
}
#[test]
fn read_event_enable() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap();
assert_eq!(
val,
PropertyValue::BitString {
unused_bits: 5,
data: vec![0b1110_0000],
}
);
}
#[test]
fn read_notification_class() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let val = ee
.read_property(PropertyIdentifier::NOTIFICATION_CLASS, None)
.unwrap();
assert_eq!(val, PropertyValue::Unsigned(0));
}
#[test]
fn write_notify_type() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
ee.write_property(
PropertyIdentifier::NOTIFY_TYPE,
None,
PropertyValue::Enumerated(1),
None,
)
.unwrap();
let val = ee
.read_property(PropertyIdentifier::NOTIFY_TYPE, None)
.unwrap();
assert_eq!(val, PropertyValue::Enumerated(1));
}
#[test]
fn write_notify_type_wrong_type() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let result = ee.write_property(
PropertyIdentifier::NOTIFY_TYPE,
None,
PropertyValue::Real(1.0),
None,
);
assert!(result.is_err());
}
#[test]
fn read_acked_transitions() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let val = ee
.read_property(PropertyIdentifier::ACKED_TRANSITIONS, None)
.unwrap();
assert_eq!(
val,
PropertyValue::BitString {
unused_bits: 5,
data: vec![0b1110_0000],
}
);
}
#[test]
fn read_object_property_reference_none() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let read = |ee: &EventEnrollmentObject| {
ee.read_property(PropertyIdentifier::OBJECT_PROPERTY_REFERENCE, None)
.unwrap()
};
let unset = PropertyValue::ApplicationData(vec![0x0C, 0x00, 0x3F, 0xFF, 0xFF, 0x19, 0x55]);
assert_eq!(read(&ee), unset);
let wildcard = ObjectIdentifier::new(ObjectType::DEVICE, 4_194_303).unwrap();
let ai5 = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 5).unwrap();
let reference = BACnetDeviceObjectPropertyReference::new_remote(ai5, 85, wildcard);
ee.set_object_property_reference(Some(reference)).unwrap();
assert_eq!(read(&ee), unset);
}
#[test]
fn read_object_property_reference_some() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let ai_oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 5).unwrap();
ee.set_object_property_reference(Some(BACnetDeviceObjectPropertyReference {
object_identifier: ai_oid,
property_identifier: PropertyIdentifier::PRESENT_VALUE.to_raw(),
property_array_index: None,
device_identifier: None,
}))
.unwrap();
assert_eq!(
ee.read_property(PropertyIdentifier::OBJECT_PROPERTY_REFERENCE, None)
.unwrap(),
PropertyValue::ApplicationData(vec![0x0C, 0x00, 0x00, 0x00, 0x05, 0x19, 0x55])
);
}
#[test]
fn read_object_property_reference_serves_every_member() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
ee.set_object_property_reference(Some(BACnetDeviceObjectPropertyReference {
object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 5).unwrap(),
property_identifier: PropertyIdentifier::PRESENT_VALUE.to_raw(),
property_array_index: Some(1),
device_identifier: Some(ObjectIdentifier::new(ObjectType::DEVICE, 260).unwrap()),
}))
.unwrap();
assert_eq!(
ee.read_property(PropertyIdentifier::OBJECT_PROPERTY_REFERENCE, None)
.unwrap(),
PropertyValue::ApplicationData(vec![
0x0C, 0x00, 0x00, 0x00, 0x05, 0x19, 0x55, 0x29, 0x01, 0x3C, 0x02, 0x00, 0x01, 0x04, ])
);
}
#[test]
fn write_notification_class() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
ee.write_property(
PropertyIdentifier::NOTIFICATION_CLASS,
None,
PropertyValue::Unsigned(42),
None,
)
.unwrap();
let val = ee
.read_property(PropertyIdentifier::NOTIFICATION_CLASS, None)
.unwrap();
assert_eq!(val, PropertyValue::Unsigned(42));
}
#[test]
fn write_event_enable() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
ee.write_property(
PropertyIdentifier::EVENT_ENABLE,
None,
PropertyValue::BitString {
unused_bits: 5,
data: vec![0b1000_0000],
},
None,
)
.unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap();
assert_eq!(
val,
PropertyValue::BitString {
unused_bits: 5,
data: vec![0b1000_0000],
}
);
}
#[test]
fn property_list_complete() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let props = ee.property_list();
assert!(props.contains(&PropertyIdentifier::EVENT_TYPE));
assert!(props.contains(&PropertyIdentifier::NOTIFY_TYPE));
assert!(props.contains(&PropertyIdentifier::EVENT_PARAMETERS));
assert!(props.contains(&PropertyIdentifier::OBJECT_PROPERTY_REFERENCE));
assert!(props.contains(&PropertyIdentifier::EVENT_STATE));
assert!(props.contains(&PropertyIdentifier::EVENT_ENABLE));
assert!(props.contains(&PropertyIdentifier::ACKED_TRANSITIONS));
assert!(props.contains(&PropertyIdentifier::NOTIFICATION_CLASS));
}
fn decode_framed_event_parameters(
val: PropertyValue,
) -> bacnet_types::constructed::BACnetEventParameter {
let PropertyValue::ApplicationData(bytes) = val else {
panic!("expected framed ApplicationData, got {val:?}");
};
bacnet_encoding::constructed::decode_event_parameter(&bytes, 0)
.unwrap()
.0
}
#[test]
fn write_event_parameters_structured_round_trip() {
use bacnet_types::constructed::{event_parameter_tag, BACnetEventParameter};
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let params = BACnetEventParameter::OutOfRange {
time_delay: 5,
low_limit: 10.0,
high_limit: 90.0,
deadband: 1.0,
};
ee.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
params.encode(),
None,
)
.unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
assert_eq!(decode_framed_event_parameters(val), params);
assert_eq!(params.tag(), event_parameter_tag::OUT_OF_RANGE);
}
#[test]
fn write_event_parameters_framed_round_trip() {
use bacnet_types::constructed::BACnetEventParameter;
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let params = BACnetEventParameter::OutOfRange {
time_delay: 5,
low_limit: 10.0,
high_limit: 90.0,
deadband: 1.0,
};
let mut buf = bytes::BytesMut::new();
bacnet_encoding::constructed::encode_event_parameter(&mut buf, ¶ms).unwrap();
ee.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
PropertyValue::ApplicationData(buf.to_vec()),
None,
)
.unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
assert_eq!(decode_framed_event_parameters(val), params);
let val2 = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
let PropertyValue::ApplicationData(bytes) = val2 else {
panic!("expected ApplicationData");
};
assert_eq!(bytes, buf.to_vec());
}
#[test]
fn write_event_parameters_framed_trailing_garbage_rejected() {
use bacnet_types::constructed::BACnetEventParameter;
let params = BACnetEventParameter::OutOfRange {
time_delay: 5,
low_limit: 10.0,
high_limit: 90.0,
deadband: 1.0,
};
let mut good = bytes::BytesMut::new();
bacnet_encoding::constructed::encode_event_parameter(&mut good, ¶ms).unwrap();
for extra in 1..=4usize {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let mut bytes = good.to_vec();
bytes.extend_from_slice(&vec![0xAA; extra]);
let result = ee.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
PropertyValue::ApplicationData(bytes),
None,
);
match result.unwrap_err() {
bacnet_types::error::Error::Protocol { class, code } => {
assert_eq!(
class,
bacnet_types::enums::ErrorClass::PROPERTY.to_raw() as u32
);
assert_eq!(
code,
bacnet_types::enums::ErrorCode::INVALID_DATA_TYPE.to_raw() as u32
);
}
other => panic!("expected PROPERTY/INVALID_DATA_TYPE, got {other:?}"),
}
let val = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
let PropertyValue::ApplicationData(bytes) = &val else {
panic!("expected ApplicationData");
};
let (decoded, _) = bacnet_encoding::constructed::decode_event_parameter(bytes, 0).unwrap();
assert_eq!(
decoded,
BACnetEventParameter::Opaque {
tag: 0xFF,
data: Vec::new()
}
);
}
}
#[test]
fn write_event_parameters_framed_malformed_rejected() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
for malformed in [
vec![0x5E, 0x09, 0x07], vec![0x58], ] {
assert!(ee
.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
PropertyValue::ApplicationData(malformed),
None,
)
.is_err());
}
}
#[test]
fn write_event_parameters_flat_malformed_proprietary_body_rejected() {
use bacnet_types::constructed::{BACnetPropertyStates, BACnetProprietaryPropertyState};
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let before = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
let malformed = BACnetEventParameter::ChangeOfState {
time_delay: 0,
list_of_values: vec![BACnetPropertyStates::Other(
BACnetProprietaryPropertyState::constructed(64, vec![0xde]).unwrap(),
)],
};
assert!(ee
.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
malformed.encode(),
None,
)
.is_err());
assert_eq!(
ee.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap(),
before
);
}
#[test]
fn write_event_parameters_flat_reference_malformed_rejected() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let before = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
let PropertyValue::List(base) = (BACnetEventParameter::FloatingLimit {
time_delay: 1,
setpoint_reference: BACnetDeviceObjectPropertyReference::new_local(
ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
PropertyIdentifier::PRESENT_VALUE.to_raw(),
),
low_diff_limit: 1.0,
high_diff_limit: 2.0,
deadband: 0.5,
})
.encode() else {
unreachable!();
};
let mut overflow = base.clone();
let PropertyValue::List(reference) = &mut overflow[2] else {
unreachable!();
};
reference[1] = PropertyValue::Unsigned(u64::MAX);
let mut trailing = base;
let PropertyValue::List(reference) = &mut trailing[2] else {
unreachable!();
};
reference.push(PropertyValue::Null);
for malformed in [overflow, trailing] {
assert!(ee
.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
PropertyValue::List(malformed),
None,
)
.is_err());
assert_eq!(
ee.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap(),
before
);
}
}
#[test]
fn write_event_parameters_opaque_octets_preserved() {
use bacnet_types::constructed::BACnetEventParameter;
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let bytes = vec![0x01, 0x02, 0x03];
ee.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
PropertyValue::OctetString(bytes.clone()),
None,
)
.unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_PARAMETERS, None)
.unwrap();
match decode_framed_event_parameters(val) {
BACnetEventParameter::Opaque { data, .. } => assert_eq!(data, bytes),
other => panic!("expected Opaque, got {other:?}"),
}
}
#[test]
fn write_event_parameters_rejects_non_list() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let result = ee.write_property(
PropertyIdentifier::EVENT_PARAMETERS,
None,
PropertyValue::Boolean(true),
None,
);
assert!(result.is_err());
}
#[test]
fn read_event_state_default() {
let ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let val = ee
.read_property(PropertyIdentifier::EVENT_STATE, None)
.unwrap();
assert_eq!(val, PropertyValue::Enumerated(0)); }
#[test]
fn write_event_state_rejected_over_network() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let result = ee.write_property(
PropertyIdentifier::EVENT_STATE,
None,
PropertyValue::Enumerated(EventState::OFFNORMAL.to_raw()),
None,
);
assert!(
result.is_err(),
"network EVENT_STATE write must be rejected"
);
let val = ee
.read_property(PropertyIdentifier::EVENT_STATE, None)
.unwrap();
assert_eq!(val, PropertyValue::Enumerated(EventState::NORMAL.to_raw()));
}
#[test]
fn set_event_state_internal_updates_field() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let result = ee.set_event_state_internal(EventState::HIGH_LIMIT);
assert!(
result.is_ok(),
"internal setter must accept a modeled state"
);
let val = ee
.read_property(PropertyIdentifier::EVENT_STATE, None)
.unwrap();
assert_eq!(
val,
PropertyValue::Enumerated(EventState::HIGH_LIMIT.to_raw())
);
}
#[test]
fn set_event_state_seeds_field() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
ee.set_event_state(EventState::LOW_LIMIT);
let val = ee
.read_property(PropertyIdentifier::EVENT_STATE, None)
.unwrap();
assert_eq!(
val,
PropertyValue::Enumerated(EventState::LOW_LIMIT.to_raw())
);
}
#[test]
fn set_event_state_internal_default_rejects() {
use bacnet_types::enums::{ErrorClass, ErrorCode};
use bacnet_types::primitives::PropertyValue;
use std::borrow::Cow;
struct DefaultOnly {
oid: ObjectIdentifier,
}
impl BACnetObject for DefaultOnly {
fn object_identifier(&self) -> ObjectIdentifier {
self.oid
}
fn object_name(&self) -> &str {
"default-only"
}
fn read_property(
&self,
property: PropertyIdentifier,
_array_index: Option<u32>,
) -> Result<PropertyValue, bacnet_types::error::Error> {
if property == PropertyIdentifier::OBJECT_NAME {
Ok(PropertyValue::CharacterString("default-only".to_string()))
} else {
Err(bacnet_types::error::Error::Protocol {
class: ErrorClass::PROPERTY.to_raw() as u32,
code: ErrorCode::UNKNOWN_PROPERTY.to_raw() as u32,
})
}
}
fn write_property(
&mut self,
_property: PropertyIdentifier,
_array_index: Option<u32>,
_value: PropertyValue,
_priority: Option<u8>,
) -> Result<(), bacnet_types::error::Error> {
Err(bacnet_types::error::Error::Protocol {
class: ErrorClass::PROPERTY.to_raw() as u32,
code: ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32,
})
}
fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
Cow::Borrowed(&[PropertyIdentifier::OBJECT_NAME])
}
}
let mut obj = DefaultOnly {
oid: ObjectIdentifier::new(ObjectType::EVENT_ENROLLMENT, 1).unwrap(),
};
let result = obj.set_event_state_internal(EventState::FAULT);
assert!(
result.is_err(),
"default set_event_state_internal must reject"
);
let err = result.unwrap_err();
match err {
bacnet_types::error::Error::Protocol { class, code } => {
assert_eq!(class, ErrorClass::OBJECT.to_raw() as u32);
assert_eq!(
code,
ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED.to_raw() as u32
);
}
_ => panic!("expected Error::Protocol from default set_event_state_internal"),
}
}
#[test]
fn write_unknown_property_denied() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let result = ee.write_property(
PropertyIdentifier::PRESENT_VALUE,
None,
PropertyValue::Real(1.0),
None,
);
assert!(result.is_err());
}
use bacnet_types::enums::{ErrorClass, ErrorCode};
#[test]
fn ee_notify_type_rejects_out_of_production_values() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
assert_eq!(
ee.read_property(PropertyIdentifier::NOTIFY_TYPE, None)
.unwrap(),
PropertyValue::Enumerated(0)
);
for out_of_production in [3u32, 99, u32::MAX] {
match ee
.write_property(
PropertyIdentifier::NOTIFY_TYPE,
None,
PropertyValue::Enumerated(out_of_production),
None,
)
.expect_err("an out-of-production Notify_Type write must be refused")
{
Error::Protocol { class, code } => {
assert_eq!(class, ErrorClass::PROPERTY.to_raw() as u32);
assert_eq!(code, ErrorCode::VALUE_OUT_OF_RANGE.to_raw() as u32);
}
other => panic!("expected PROPERTY / VALUE_OUT_OF_RANGE, got {other:?}"),
}
assert_eq!(
ee.read_property(PropertyIdentifier::NOTIFY_TYPE, None)
.unwrap(),
PropertyValue::Enumerated(0),
"a refused Notify_Type write must leave the value untouched ({out_of_production})"
);
}
for in_production in [0u32, 1, 2] {
ee.write_property(
PropertyIdentifier::NOTIFY_TYPE,
None,
PropertyValue::Enumerated(in_production),
None,
)
.expect("named Notify_Type values must be accepted");
}
}
#[test]
fn ee_event_enable_rejects_noncanonical_bit_strings() {
let mut ee = EventEnrollmentObject::new(1, "EE-1", EventType::CHANGE_OF_BITSTRING).unwrap();
let canonical = ee
.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap();
for (unused_bits, data) in [
(0u8, vec![0xFFu8]), (5u8, vec![0xFF, 0xFF]), (4u8, vec![0xF0u8]), (5u8, vec![]), ] {
match ee
.write_property(
PropertyIdentifier::EVENT_ENABLE,
None,
PropertyValue::BitString { unused_bits, data },
None,
)
.expect_err("a noncanonical Event_Enable shape must be refused")
{
Error::Protocol { class, code } => {
assert_eq!(class, ErrorClass::PROPERTY.to_raw() as u32);
assert_eq!(code, ErrorCode::INVALID_DATA_ENCODING.to_raw() as u32);
}
other => panic!("expected PROPERTY / INVALID_DATA_ENCODING, got {other:?}"),
}
assert_eq!(
ee.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap(),
canonical,
"a refused Event_Enable write must leave the value untouched"
);
}
ee.write_property(
PropertyIdentifier::EVENT_ENABLE,
None,
PropertyValue::BitString {
unused_bits: 5,
data: vec![0xA0], },
None,
)
.unwrap();
assert_eq!(
ee.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap(),
PropertyValue::BitString {
unused_bits: 5,
data: vec![0xA0],
}
);
}
#[test]
fn alert_enrollment_event_enable_rejects_noncanonical_bit_strings() {
let source = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
let mut ae = AlertEnrollmentObject::new(1, "AE-1", source).unwrap();
let canonical = ae
.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap();
match ae
.write_property(
PropertyIdentifier::EVENT_ENABLE,
None,
PropertyValue::BitString {
unused_bits: 0,
data: vec![0xFF],
},
None,
)
.expect_err("an 8-bit string must be refused where 3 bits are defined")
{
Error::Protocol { class, code } => {
assert_eq!(class, ErrorClass::PROPERTY.to_raw() as u32);
assert_eq!(code, ErrorCode::INVALID_DATA_ENCODING.to_raw() as u32);
}
other => panic!("expected PROPERTY / INVALID_DATA_ENCODING, got {other:?}"),
}
assert_eq!(
ae.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.unwrap(),
canonical,
"a refused Event_Enable write must leave the value untouched"
);
}