#![allow(clippy::unwrap_used, clippy::expect_used)]
use matter_clusters::gen;
use matter_clusters::types::Nullable;
use matter_codec::{Tag, TlvWriter};
fn uint_attr(v: u64) -> Vec<u8> {
let mut buf = Vec::new();
TlvWriter::new(&mut buf)
.put_uint(Tag::Anonymous, v)
.unwrap();
buf
}
fn int_attr(v: i64) -> Vec<u8> {
let mut buf = Vec::new();
TlvWriter::new(&mut buf).put_int(Tag::Anonymous, v).unwrap();
buf
}
fn bool_attr(v: bool) -> Vec<u8> {
let mut buf = Vec::new();
TlvWriter::new(&mut buf)
.put_bool(Tag::Anonymous, v)
.unwrap();
buf
}
fn null_attr() -> Vec<u8> {
let mut buf = Vec::new();
TlvWriter::new(&mut buf).put_null(Tag::Anonymous).unwrap();
buf
}
#[test]
fn illuminance_measured_value_decodes() {
assert_eq!(
gen::illuminance_measurement::decode_measured_value(&uint_attr(12345)).unwrap(),
Nullable::Value(12345)
);
assert_eq!(
gen::illuminance_measurement::decode_measured_value(&null_attr()).unwrap(),
Nullable::Null
);
}
#[test]
fn pressure_measured_value_decodes() {
assert_eq!(
gen::pressure_measurement::decode_measured_value(&int_attr(-50)).unwrap(),
Nullable::Value(-50)
);
}
#[test]
fn flow_measured_value_decodes() {
assert_eq!(
gen::flow_measurement::decode_measured_value(&uint_attr(200)).unwrap(),
Nullable::Value(200)
);
}
#[test]
fn boolean_state_state_value_decodes() {
assert!(gen::boolean_state::decode_state_value(&bool_attr(true)).unwrap());
}
#[test]
fn switch_current_position_decodes() {
assert_eq!(
gen::switch::decode_current_position(&uint_attr(2)).unwrap(),
2
);
}
fn uint_array_attr(values: &[u64]) -> Vec<u8> {
let mut buf = Vec::new();
{
let mut w = TlvWriter::new(&mut buf);
w.start_array(Tag::Anonymous).unwrap();
for &v in values {
w.put_uint(Tag::Anonymous, v).unwrap();
}
w.end_container().unwrap();
}
buf
}
#[test]
fn air_quality_decodes() {
use gen::air_quality::AirQualityEnum;
assert_eq!(
gen::air_quality::decode_air_quality(&uint_attr(1)).unwrap(),
AirQualityEnum::Good
);
assert_eq!(
gen::air_quality::decode_air_quality(&uint_attr(0)).unwrap(),
AirQualityEnum::Unknown
);
assert_eq!(
gen::air_quality::decode_air_quality(&uint_attr(99)).unwrap(),
AirQualityEnum::Unrecognized(99)
);
}
#[test]
fn power_source_status_and_lists_decode() {
use gen::power_source::{PowerSourceStatusEnum, WiredFaultEnum};
assert_eq!(
gen::power_source::decode_status(&uint_attr(1)).unwrap(),
PowerSourceStatusEnum::Active
);
assert_eq!(
gen::power_source::decode_active_wired_faults(&uint_array_attr(&[1])).unwrap(),
vec![WiredFaultEnum::OverVoltage]
);
assert_eq!(
gen::power_source::decode_endpoint_list(&uint_array_attr(&[1, 2])).unwrap(),
vec![1u16, 2u16]
);
}
#[test]
fn electrical_power_measurement_decodes() {
use gen::electrical_power_measurement as epm;
assert_eq!(
epm::decode_power_mode(&uint_attr(2)).unwrap(),
epm::PowerModeEnum::Ac
);
assert_eq!(
epm::decode_voltage(&int_attr(230_000)).unwrap(),
Nullable::Value(230_000)
);
assert!(epm::decode_accuracy(&uint_array_attr(&[]))
.unwrap()
.is_empty());
assert!(matches!(
epm::decode_harmonic_currents(&null_attr()).unwrap(),
Nullable::Null
));
}
#[test]
fn electrical_energy_measurement_nullable_struct_attr_decodes() {
assert!(matches!(
gen::electrical_energy_measurement::decode_cumulative_energy_imported(&null_attr())
.unwrap(),
Nullable::Null
));
}
#[test]
fn thermostat_system_mode_decodes() {
use gen::thermostat::SystemModeEnum;
assert_eq!(
gen::thermostat::decode_system_mode(&uint_attr(4)).unwrap(),
SystemModeEnum::Heat
);
}
#[test]
fn thermostat_atomic_response_decodes_synth_struct() {
use matter_codec::{ContainerKind, Element, TlvReader};
let mut buf = Vec::new();
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_uint(Tag::Context(0), 0).unwrap();
w.start_array(Tag::Context(1)).unwrap();
w.start_structure(Tag::Anonymous).unwrap();
w.put_uint(Tag::Context(0), 0x1234).unwrap();
w.put_uint(Tag::Context(1), 0).unwrap();
w.end_container().unwrap();
w.end_container().unwrap();
w.put_uint(Tag::Context(2), 1000).unwrap();
w.end_container().unwrap();
}
let mut r = TlvReader::new(&buf);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart {
kind: ContainerKind::Structure,
..
})
));
let resp = gen::thermostat::AtomicResponse::decode_from(&mut r).unwrap();
assert_eq!(resp.status_code, 0);
assert_eq!(resp.attribute_status.len(), 1);
assert_eq!(resp.attribute_status[0].attribute_id, 0x1234);
assert_eq!(resp.attribute_status[0].status_code, 0);
assert_eq!(resp.timeout, Some(1000));
}
#[test]
fn fan_control_fan_mode_decodes() {
use gen::fan_control::FanModeEnum;
assert_eq!(
gen::fan_control::decode_fan_mode(&uint_attr(3)).unwrap(),
FanModeEnum::High
);
}
#[test]
fn tuic_keypad_lockout_decodes() {
use gen::thermostat_user_interface_configuration::KeypadLockoutEnum;
assert_eq!(
gen::thermostat_user_interface_configuration::decode_keypad_lockout(&uint_attr(0)).unwrap(),
KeypadLockoutEnum::NoLockout
);
}
#[test]
fn pump_operation_mode_decodes() {
use gen::pump_configuration_and_control::OperationModeEnum;
assert_eq!(
gen::pump_configuration_and_control::decode_operation_mode(&uint_attr(0)).unwrap(),
OperationModeEnum::Normal
);
}
#[test]
fn window_covering_mode_decodes() {
let m = gen::window_covering::decode_mode(&uint_attr(1)).unwrap();
assert_eq!(m.bits(), 1);
}
#[test]
fn binding_target_struct_decodes_fabric_index() {
use matter_codec::{ContainerKind, Element, TlvReader};
let mut buf = Vec::new();
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_uint(Tag::Context(4), 0x0006).unwrap();
w.put_uint(Tag::Context(254), 1).unwrap();
w.end_container().unwrap();
}
let mut r = TlvReader::new(&buf);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart {
kind: ContainerKind::Structure,
..
})
));
let t = gen::binding::TargetStruct::decode_from(&mut r).unwrap();
assert_eq!(t.cluster, Some(0x0006));
assert_eq!(t.fabric_index, 1u8);
}
#[test]
fn fixed_label_label_struct_decodes() {
use matter_codec::{ContainerKind, Element, TlvReader};
let mut buf = Vec::new();
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_utf8(Tag::Context(0), "room").unwrap();
w.put_utf8(Tag::Context(1), "kitchen").unwrap();
w.end_container().unwrap();
}
let mut r = TlvReader::new(&buf);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart {
kind: ContainerKind::Structure,
..
})
));
let l = gen::fixed_label::LabelStruct::decode_from(&mut r).unwrap();
assert_eq!(l.label, "room");
assert_eq!(l.value, "kitchen");
}
#[test]
fn groups_add_group_command_encodes_wellformed() {
use matter_codec::{Element, TlvReader, Value};
let bytes = gen::groups::encode_add_group(0x0007, &"den".to_string());
let mut r = TlvReader::new(&bytes);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Uint(7)
})
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar { tag: Tag::Context(1), value: Value::Utf8(ref s) }) if s == "den"
));
}
#[test]
fn groups_get_group_membership_command_encodes_list() {
use matter_codec::{ContainerKind, Element, TlvReader, Value};
let bytes = gen::groups::encode_get_group_membership(&vec![1u16, 2u16]);
let mut r = TlvReader::new(&bytes);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart {
tag: Tag::Context(0),
kind: ContainerKind::Array
})
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar {
value: Value::Uint(1),
..
})
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar {
value: Value::Uint(2),
..
})
));
}
#[test]
fn access_control_entry_decodes_subjects_u64() {
use matter_codec::{ContainerKind, Element, TlvReader};
let mut buf = Vec::new();
{
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_uint(Tag::Context(1), 5).unwrap();
w.put_uint(Tag::Context(2), 2).unwrap();
w.start_array(Tag::Context(3)).unwrap();
w.put_uint(Tag::Anonymous, 0x1122_3344_5566_7788).unwrap();
w.end_container().unwrap();
w.put_null(Tag::Context(4)).unwrap();
w.put_uint(Tag::Context(254), 1).unwrap();
w.end_container().unwrap();
}
let mut r = TlvReader::new(&buf);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart {
kind: ContainerKind::Structure,
..
})
));
let e = gen::access_control::AccessControlEntryStruct::decode_from(&mut r).unwrap();
assert_eq!(e.subjects, Nullable::Value(vec![0x1122_3344_5566_7788u64]));
assert!(matches!(e.targets, Nullable::Null));
assert_eq!(e.fabric_index, 1u8);
}
#[test]
fn group_key_set_write_encodes_wellformed() {
use matter_codec::{Element, TlvReader};
let gks = gen::group_key_management::GroupKeySetStruct {
group_key_set_id: 0x0042,
group_key_security_policy: gen::group_key_management::GroupKeySecurityPolicyEnum::from_raw(
0,
),
epoch_key0: Nullable::Value(vec![0xab; 16]),
epoch_start_time0: Nullable::Value(1234),
epoch_key1: Nullable::Null,
epoch_start_time1: Nullable::Null,
epoch_key2: Nullable::Null,
epoch_start_time2: Nullable::Null,
group_key_multicast_policy: None,
fabric_index: None,
};
let bytes = gen::group_key_management::encode_key_set_write(gks);
let mut r = TlvReader::new(&bytes);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart {
tag: Tag::Context(0),
..
})
));
}
#[test]
fn admin_open_basic_commissioning_window_encodes() {
use matter_codec::{Element, TlvReader, Value};
let bytes = gen::administrator_commissioning::encode_open_basic_commissioning_window(180);
let mut r = TlvReader::new(&bytes);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Uint(180)
})
));
}
#[test]
fn ota_announce_provider_encodes_scalars_and_enum() {
use gen::ota_software_update_requestor::AnnouncementReasonEnum;
use matter_codec::{Element, TlvReader, Value};
let bytes = gen::ota_software_update_requestor::encode_announce_ota_provider(
0x0000_0000_0000_1234,
0xFFF1,
AnnouncementReasonEnum::SimpleAnnouncement,
None,
1,
);
let mut r = TlvReader::new(&bytes);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Uint(0x1234)
})
));
}
#[test]
fn ota_provider_query_image_encodes_scalars() {
use gen::ota_software_update_provider::{encode_query_image, DownloadProtocolEnum};
use matter_codec::{Element, TlvReader, Value};
let bytes = encode_query_image(
0xFFF1,
0x8000,
5,
&vec![DownloadProtocolEnum::BdxSynchronous],
None,
None,
None,
None,
);
let mut r = TlvReader::new(&bytes);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
assert!(matches!(
r.next().unwrap(),
Some(Element::Scalar {
tag: Tag::Context(0),
value: Value::Uint(0xFFF1)
})
));
}
#[test]
fn ota_provider_query_image_response_decodes() {
use gen::ota_software_update_provider::{QueryImageResponse, StatusEnum};
use matter_codec::{Element, Tag, TlvReader, TlvWriter};
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_uint(Tag::Context(0), 0).unwrap(); w.end_container().unwrap();
let mut r = TlvReader::new(&buf);
assert!(matches!(
r.next().unwrap(),
Some(Element::ContainerStart { .. })
));
let decoded = QueryImageResponse::decode_from(&mut r).expect("decode QueryImageResponse");
assert_eq!(decoded.status, StatusEnum::UpdateAvailable);
}
#[test]
fn time_sync_utc_time_and_granularity_decode() {
use gen::time_synchronization::{decode_granularity, decode_utc_time, GranularityEnum};
let decoded = decode_utc_time(&uint_attr(780_000_000_000_000)).unwrap();
assert_eq!(decoded, Nullable::Value(780_000_000_000_000));
let g = decode_granularity(&uint_attr(2)).unwrap();
assert_eq!(g, GranularityEnum::SecondsGranularity);
}
#[test]
fn icd_register_client_response_and_operating_mode_decode() {
use gen::icd_management::{decode_operating_mode, OperatingModeEnum, RegisterClientResponse};
use matter_codec::{Tag, TlvWriter};
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_uint(Tag::Context(0), 7).unwrap();
w.end_container().unwrap();
let decoded = RegisterClientResponse::decode(&buf).expect("decode RegisterClientResponse");
assert_eq!(decoded.icd_counter, 7);
let m = decode_operating_mode(&uint_attr(1)).unwrap();
assert_eq!(m, OperatingModeEnum::Lit);
}
#[test]
fn time_sync_set_time_zone_response_decodes() {
use gen::time_synchronization::SetTimeZoneResponse;
use matter_codec::{Tag, TlvWriter};
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous).unwrap();
w.put_bool(Tag::Context(0), true).unwrap();
w.end_container().unwrap();
let decoded = SetTimeZoneResponse::decode(&buf).expect("decode SetTimeZoneResponse");
assert!(decoded.dst_offset_required);
}
fn float_attr(v: f32) -> Vec<u8> {
let mut buf = Vec::new();
TlvWriter::new(&mut buf)
.put_float(Tag::Anonymous, v)
.unwrap();
buf
}
fn double_attr(v: f64) -> Vec<u8> {
let mut buf = Vec::new();
TlvWriter::new(&mut buf)
.put_double(Tag::Anonymous, v)
.unwrap();
buf
}
#[track_caller]
fn assert_f32_eq(actual: f32, expected: f32) {
assert_eq!(
actual.to_bits(),
expected.to_bits(),
"expected {expected}, got {actual}"
);
}
#[test]
fn carbon_dioxide_concentration_full_attribute_set_decodes() {
use co2::{LevelValueEnum, MeasurementMediumEnum, MeasurementUnitEnum};
use gen::carbon_dioxide_concentration_measurement as co2;
assert_eq!(
co2::decode_measured_value(&float_attr(415.5)).unwrap(),
Nullable::Value(415.5)
);
assert_eq!(
co2::decode_measured_value(&null_attr()).unwrap(),
Nullable::Null
);
assert_eq!(
co2::decode_min_measured_value(&float_attr(0.0)).unwrap(),
Nullable::Value(0.0)
);
assert_eq!(
co2::decode_max_measured_value(&float_attr(5000.0)).unwrap(),
Nullable::Value(5000.0)
);
assert_eq!(
co2::decode_peak_measured_value(&float_attr(1200.25)).unwrap(),
Nullable::Value(1200.25)
);
assert_eq!(
co2::decode_average_measured_value(&float_attr(-12.5)).unwrap(),
Nullable::Value(-12.5)
);
assert_f32_eq(co2::decode_uncertainty(&float_attr(0.25)).unwrap(), 0.25);
assert_eq!(
co2::decode_peak_measured_value_window(&uint_attr(3600)).unwrap(),
3600
);
assert_eq!(
co2::decode_average_measured_value_window(&uint_attr(300)).unwrap(),
300
);
assert_eq!(
co2::decode_measurement_unit(&uint_attr(0)).unwrap(),
MeasurementUnitEnum::Ppm
);
assert_eq!(
co2::decode_measurement_medium(&uint_attr(0)).unwrap(),
MeasurementMediumEnum::Air
);
assert_eq!(
co2::decode_level_value(&uint_attr(4)).unwrap(),
LevelValueEnum::Critical
);
assert_eq!(
co2::decode_level_value(&uint_attr(99)).unwrap(),
LevelValueEnum::Unrecognized(99)
);
}
#[test]
fn float_attribute_rejects_non_float_wire_types() {
use gen::carbon_dioxide_concentration_measurement as co2;
assert!(co2::decode_measured_value(&uint_attr(415)).is_err());
assert!(co2::decode_uncertainty(&int_attr(-1)).is_err());
assert!(co2::decode_uncertainty(&bool_attr(true)).is_err());
assert!(co2::decode_uncertainty(&null_attr()).is_err());
assert_eq!(
double_attr(415.5)[0],
0x0B,
"anonymous FLOAT64 control byte"
);
assert!(co2::decode_measured_value(&double_attr(415.5)).is_err());
assert!(co2::decode_uncertainty(&double_attr(0.25)).is_err());
}
#[test]
fn float_wire_roundtrip_including_edge_values() {
use gen::carbon_dioxide_concentration_measurement as co2;
for v in [
0.0_f32,
-0.0,
1.0,
-1.0,
f32::MIN,
f32::MAX,
f32::MIN_POSITIVE,
f32::from_bits(1),
-f32::from_bits(1),
f32::INFINITY,
f32::NEG_INFINITY,
f32::NAN,
] {
match co2::decode_measured_value(&float_attr(v)).unwrap() {
Nullable::Value(d) => assert_f32_eq(d, v),
Nullable::Null => panic!("float {v} decoded as null"),
}
}
}
#[test]
fn every_concentration_cluster_decodes_its_measured_value() {
macro_rules! assert_family_member {
($m:ident, $id:expr) => {{
use gen::$m as m;
assert_eq!(m::CLUSTER_ID, $id, concat!(stringify!($m), " cluster id"));
assert_eq!(
m::decode_measured_value(&float_attr(1.5)).unwrap(),
Nullable::Value(1.5)
);
assert_eq!(
m::decode_measured_value(&null_attr()).unwrap(),
Nullable::Null
);
assert_f32_eq(m::decode_uncertainty(&float_attr(0.5)).unwrap(), 0.5);
}};
}
assert_family_member!(carbon_monoxide_concentration_measurement, 0x040C);
assert_family_member!(carbon_dioxide_concentration_measurement, 0x040D);
assert_family_member!(nitrogen_dioxide_concentration_measurement, 0x0413);
assert_family_member!(ozone_concentration_measurement, 0x0415);
assert_family_member!(pm25_concentration_measurement, 0x042A);
assert_family_member!(formaldehyde_concentration_measurement, 0x042B);
assert_family_member!(pm1_concentration_measurement, 0x042C);
assert_family_member!(pm10_concentration_measurement, 0x042D);
assert_family_member!(
total_volatile_organic_compounds_concentration_measurement,
0x042E
);
assert_family_member!(radon_concentration_measurement, 0x042F);
}