#![allow(clippy::unwrap_used, clippy::expect_used)]
use base64::Engine as _;
use matter_clusters::gen;
use matter_clusters::types::Nullable;
use std::fs;
use std::path::PathBuf;
fn vec_bytes(rel: &str, key: &str) -> Vec<u8> {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.join("test-vectors/clusters")
.join(rel);
let v: serde_json::Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
base64::engine::general_purpose::STANDARD
.decode(v[key].as_str().unwrap())
.unwrap()
}
fn attr(rel: &str) -> Vec<u8> {
vec_bytes(rel, "value_tlv_b64")
}
fn cmd(rel: &str) -> Vec<u8> {
vec_bytes(rel, "payload_tlv_b64")
}
#[test]
fn on_time_roundtrips() {
let bytes = attr("on_off/attr_on_time.json");
let v = gen::on_off::decode_on_time(&bytes).unwrap();
assert_eq!(gen::on_off::encode_on_time(v), bytes);
}
#[test]
fn start_up_on_off_present_roundtrips() {
let bytes = attr("on_off/attr_start_up_on_off_present.json");
let v = gen::on_off::decode_start_up_on_off(&bytes).unwrap();
assert_eq!(gen::on_off::encode_start_up_on_off(v), bytes);
}
#[test]
fn start_up_on_off_null_roundtrips() {
let bytes = attr("on_off/attr_start_up_on_off_null.json");
let v = gen::on_off::decode_start_up_on_off(&bytes).unwrap();
assert!(matches!(v, Nullable::Null));
assert_eq!(gen::on_off::encode_start_up_on_off(v), bytes);
}
#[test]
fn node_label_roundtrips() {
let bytes = attr("basic_information/attr_node_label.json");
let v = gen::basic_information::decode_node_label(&bytes).unwrap();
assert_eq!(v, "matter-rust");
assert_eq!(gen::basic_information::encode_node_label(&v), bytes);
}
#[test]
fn bool_and_nullable_uint_decode() {
assert!(gen::on_off::decode_on_off(&attr("on_off/attr_on_off.json")).unwrap());
let lvl = gen::level_control::decode_current_level(&attr(
"level_control/attr_current_level_present.json",
))
.unwrap();
assert_eq!(lvl, Nullable::Value(254));
}
#[test]
fn temperature_signed_decode() {
let t = gen::temperature_measurement::decode_measured_value(&attr(
"temperature_measurement/attr_measured_value.json",
))
.unwrap();
assert_eq!(t, Nullable::Value(-1234));
}
#[test]
fn bitmap_u16_decode() {
let caps = gen::color_control::decode_color_capabilities(&attr(
"color_control/attr_color_capabilities.json",
))
.unwrap();
assert_eq!(caps.bits(), 0b10101);
}
#[test]
fn struct_attribute_decode() {
let m = gen::basic_information::decode_capability_minima(&attr(
"basic_information/attr_capability_minima.json",
))
.unwrap();
assert_eq!(m.case_sessions_per_fabric, 3);
assert_eq!(m.subscriptions_per_fabric, 4);
}
#[test]
fn list_of_scalars_and_structs_decode() {
let server =
gen::descriptor::decode_server_list(&attr("descriptor/attr_server_list.json")).unwrap();
assert_eq!(server, vec![0x06, 0x1d, 0x28]);
let dts =
gen::descriptor::decode_device_type_list(&attr("descriptor/attr_device_type_list.json"))
.unwrap();
assert_eq!(dts.len(), 1);
assert_eq!(dts[0].device_type, 256);
assert_eq!(dts[0].revision, 1);
}
#[test]
fn measurement_accuracy_struct_decodes() {
let acc = gen::electrical_energy_measurement::decode_accuracy(&attr(
"electrical_energy_measurement/attr_accuracy.json",
))
.unwrap();
assert_eq!(acc.measurement_type.to_raw(), 0); assert!(acc.measured);
assert_eq!(acc.min_measured_value, 1000);
assert_eq!(acc.max_measured_value, 50000);
assert_eq!(acc.accuracy_ranges.len(), 2);
let r0 = &acc.accuracy_ranges[0];
assert_eq!(r0.range_min, 0);
assert_eq!(r0.range_max, 10000);
assert_eq!(r0.percent_max, Some(500));
assert_eq!(r0.fixed_max, None);
let r1 = &acc.accuracy_ranges[1];
assert_eq!(r1.range_min, 10001);
assert_eq!(r1.fixed_max, Some(100));
assert_eq!(r1.percent_max, None);
}
#[test]
fn toggle_command() {
assert_eq!(gen::on_off::encode_toggle(), cmd("on_off/cmd_toggle.json"));
}
#[test]
fn on_with_timed_off_command() {
let got = gen::on_off::encode_on_with_timed_off(
gen::on_off::OnOffControlBitmap::from_bits_truncate(1),
60,
0,
);
assert_eq!(got, cmd("on_off/cmd_on_with_timed_off.json"));
}
#[test]
fn move_to_level_command() {
let got = gen::level_control::encode_move_to_level(
128,
Nullable::Value(10),
gen::level_control::OptionsBitmap::from_bits_truncate(0),
gen::level_control::OptionsBitmap::from_bits_truncate(0),
);
assert_eq!(got, cmd("level_control/cmd_move_to_level.json"));
}
#[test]
fn lock_door_optional_field() {
let with = gen::door_lock::encode_lock_door(Some(vec![1, 2, 3, 4]));
assert_eq!(with, cmd("door_lock/cmd_lock_door_with_pin.json"));
let without = gen::door_lock::encode_lock_door(None);
assert_eq!(without, cmd("door_lock/cmd_lock_door_no_pin.json"));
}
#[test]
fn atomic_request_command_encodes() {
assert_eq!(
gen::thermostat::encode_atomic_request(0, &vec![5, 6], Some(1000)),
cmd("thermostat/cmd_atomic_request.json")
);
}
#[test]
fn network_interface_struct_decodes() {
let ifaces = gen::general_diagnostics::decode_network_interfaces(&attr(
"general_diagnostics/attr_network_interfaces.json",
))
.unwrap();
assert_eq!(ifaces.len(), 1);
let ni = &ifaces[0];
assert_eq!(ni.name, "eth0");
assert!(ni.is_operational);
assert_eq!(
ni.hardware_address,
vec![0xde, 0xad, 0xbe, 0xef, 0x00, 0x01]
);
assert_eq!(ni.i_pv4_addresses, vec![vec![192, 168, 1, 5]]);
assert_eq!(ni.i_pv6_addresses.len(), 1);
assert_eq!(ni.i_pv6_addresses[0].len(), 16);
assert_eq!(ni.r#type.to_raw(), 1); }
#[test]
fn review_fabric_restrictions_recursive_list_encodes() {
let arl = vec![
gen::access_control::CommissioningAccessRestrictionEntryStruct {
endpoint: 1,
cluster: 0x0006,
restrictions: vec![gen::access_control::AccessRestrictionStruct {
r#type: gen::access_control::AccessRestrictionTypeEnum::from_raw(0),
id: Nullable::Value(0x1234),
}],
},
];
assert_eq!(
gen::access_control::encode_review_fabric_restrictions(&arl),
cmd("access_control/cmd_review_fabric_restrictions.json")
);
}