use super::*;
const LIGHTING: [u8; 9] = [0x0E, 0x09, 0x01, 0x1C, 0x42, 0x48, 0x00, 0x00, 0x0F];
const XY: [u8; 12] = [
0x1E, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x44, 0x3E, 0x80, 0x00, 0x00, 0x1F,
];
const COLOR_COMMAND: [u8; 9] = [0x2E, 0x09, 0x02, 0x2A, 0x0A, 0x8C, 0x39, 0x64, 0x2F];
fn is_lighting_command_channel_value(value: &[u8]) -> bool {
constructed_channel_value(value) == Some(ConstructedChannelValue::LightingCommand)
}
#[test]
fn primitives_and_a_lighting_command_are_channel_values() {
let values: [&[u8]; 7] = [
&[0x00], &[0x44, 0x42, 0x90, 0x00, 0x00], &[0x91, 0x01], &[0x73, 0x00, 0x6F, 0x6E], &LIGHTING,
&XY,
&COLOR_COMMAND,
];
for value in values {
let followed = [value, &[0x21, 0x05]].concat();
assert_eq!(
channel_value_end(&followed, 0).unwrap(),
value.len(),
"{value:02X?}"
);
assert_eq!(
is_lighting_command_channel_value(value),
value == LIGHTING,
"{value:02X?}"
);
}
assert!(is_lighting_command_channel_value(&[
0x0E, 0x09, 0x03, 0x59, 0x10, 0x0F
]));
assert!(!is_lighting_command_channel_value(
&[&LIGHTING[..], &[0x00]].concat()
));
}
#[test]
fn each_constructed_alternative_is_told_by_its_tag() {
use ConstructedChannelValue as C;
let stop: &[u8] = &[0x2E, 0x09, 0x06, 0x2F];
let fade_to_color: &[u8] = &[
0x2E, 0x09, 0x01, 0x1E, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x44, 0x3E, 0x80, 0x00, 0x00, 0x1F,
0x2F,
];
let any_reals: &[u8] = &[
0x1E, 0x44, 0x7F, 0xC0, 0x00, 0x00, 0x44, 0xBF, 0x80, 0x00, 0x00, 0x1F,
];
for (value, kind) in [
(&LIGHTING[..], C::LightingCommand),
(&XY[..], C::XyColor),
(&COLOR_COMMAND[..], C::ColorCommand),
(stop, C::ColorCommand),
(fade_to_color, C::ColorCommand),
(any_reals, C::XyColor),
] {
assert_eq!(channel_value_end(value, 0).unwrap(), value.len());
assert_eq!(constructed_channel_value(value), Some(kind), "{value:02X?}");
}
assert_eq!(
constructed_channel_value(&[0x44, 0x42, 0x90, 0x00, 0x00]),
None
);
assert_eq!(
constructed_channel_value(&[&XY[..], &[0x00]].concat()),
None
);
assert_eq!(constructed_channel_value(&[]), None);
}
#[test]
fn malformed_colour_alternatives_are_refused() {
let bad: [&[u8]; 9] = [
&[0x1E, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x1F],
&[
0x1E, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x44, 0x3F, 0x00,
0x00, 0x00, 0x1F,
],
&[0x1E, 0x21, 0x01, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x1F],
&[
0x1E, 0x44, 0x3F, 0x00, 0x00, 0x00, 0x44, 0x3E, 0x80, 0x00, 0x00, 0x2F,
],
&[0x2E, 0x2A, 0x0A, 0x8C, 0x2F],
&[0x2E, 0x44, 0x42, 0x90, 0x00, 0x00, 0x2F],
&[0x2E, 0x0D, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2F],
&[0x2E, 0x09, 0x02, 0x39, 0x64, 0x2A, 0x0A, 0x8C, 0x2F],
&[0x2E, 0x09, 0x04, 0x69, 0x01, 0x2F],
];
for value in bad {
assert!(
matches!(
channel_value_end(value, 0),
Err(bacnet_types::error::Error::Decoding { .. })
),
"{value:02X?}"
);
assert_eq!(constructed_channel_value(value), None, "{value:02X?}");
}
assert!(channel_value_end(&[0x3E, 0x09, 0x06, 0x3F], 0).is_err());
assert!(channel_value_end(&[0x29, 0x01], 0).is_err());
assert!(matches!(
channel_value_end(&XY[..4], 0),
Err(bacnet_types::error::Error::BufferTooShort { .. })
));
}
#[test]
fn malformed_lighting_commands_are_refused() {
let bad: [&[u8]; 12] = [
&[0x0E, 0x1C, 0x42, 0x48, 0x00, 0x00, 0x0F],
&[0x0E, 0x0F],
&[0x0E, 0x09, 0x01, 0x5A, 0x01, 0x2C, 0x0F],
&[0x0E, 0x0D, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0F],
&[0x0E, 0x0D, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0F],
&[0x0E, 0x09, 0x01, 0x1E, 0x1F, 0x0F],
&[
0x0E, 0x09, 0x01, 0x2C, 0x40, 0x00, 0x00, 0x00, 0x1C, 0x42, 0x48, 0x00, 0x00, 0x0F,
],
&[0x0E, 0x09, 0x01, 0x1B, 0x42, 0x48, 0x00, 0x0F],
&[0x0E, 0x09, 0x01, 0x69, 0x00, 0x0F],
&[0x0E, 0x09, 0x01, 0x59, 0x00, 0x0F],
&[0x0E, 0x09, 0x01, 0x59, 0x11, 0x0F],
&[0x0E, 0x09, 0x01],
];
for value in bad {
assert!(
matches!(
channel_value_end(value, 0),
Err(bacnet_types::error::Error::Decoding { .. })
),
"{value:02X?}"
);
assert!(!is_lighting_command_channel_value(value), "{value:02X?}");
}
assert!(channel_value_end(&[0x19, 0x01], 0).is_err());
assert!(channel_value_end(&[], 0).is_err());
}
#[test]
fn contents_cut_short_are_a_short_buffer() {
let short = |value: &[u8]| match channel_value_end(value, 0) {
Err(bacnet_types::error::Error::BufferTooShort { need, have }) => (need, have),
other => panic!("expected a short buffer for {value:02X?}, got {other:?}"),
};
assert_eq!(short(&[0x0E, 0x09]), (3, 2));
assert_eq!(short(&[0x0E, 0x09, 0x01, 0x1C, 0x42, 0x48]), (8, 6));
assert_eq!(short(&[0x44, 0x42, 0x90]), (5, 3));
}
#[test]
fn a_level_of_the_wrong_length_is_malformed_even_when_cut_short() {
match channel_value_end(&[0x0E, 0x09, 0x01, 0x1B, 0x42, 0x48], 0) {
Err(bacnet_types::error::Error::Decoding {
offset, message, ..
}) => {
assert_eq!(offset, 3);
assert_eq!(
message,
"lighting command: [1] REAL has 3 contents octets, expected 4"
);
}
other => panic!("expected a decoding error, got {other:?}"),
}
}