diff --git a/matter/src/core.rs b/matter/src/core.rs index 68c07f4..9f1b13b 100644 --- a/matter/src/core.rs +++ b/matter/src/core.rs @@ -61,12 +61,14 @@ impl Matter { let mdns = Mdns::get()?; mdns.set_values(dev_det.vid, dev_det.pid, &dev_det.device_name); - print_pairing_code_and_qr(&dev_det, &dev_comm, DiscoveryCapabilities::default()); - let fabric_mgr = Arc::new(FabricMgr::new()?); + let open_comm_window = fabric_mgr.is_empty(); + if open_comm_window { + print_pairing_code_and_qr(&dev_det, &dev_comm, DiscoveryCapabilities::default()); + } + let acl_mgr = Arc::new(AclMgr::new()?); let mut pase = PaseMgr::new(); - let open_comm_window = fabric_mgr.is_empty(); let data_model = DataModel::new(dev_det, dev_att, fabric_mgr.clone(), acl_mgr, pase.clone())?; let mut matter = Box::new(Matter { diff --git a/matter/src/data_model/cluster_basic_information.rs b/matter/src/data_model/cluster_basic_information.rs index 3026138..4560cc5 100644 --- a/matter/src/data_model/cluster_basic_information.rs +++ b/matter/src/data_model/cluster_basic_information.rs @@ -22,7 +22,7 @@ use num_derive::FromPrimitive; pub const ID: u32 = 0x0028; #[derive(FromPrimitive)] -enum Attributes { +pub enum Attributes { DMRevision = 0, VendorId = 2, ProductId = 4, diff --git a/matter/src/data_model/cluster_media_playback.rs b/matter/src/data_model/cluster_media_playback.rs index c53162f..92f7b39 100644 --- a/matter/src/data_model/cluster_media_playback.rs +++ b/matter/src/data_model/cluster_media_playback.rs @@ -55,7 +55,7 @@ enum PlaybackState { Playing = 0, Paused = 1, NotPlaying = 2, - BUFFERING = 3, + Buffering = 3, } #[derive(FromPrimitive)] enum CommandStatus { @@ -186,10 +186,7 @@ impl MediaPlaybackCluster { } pub fn add_callback(&mut self, name: Commands, callback: Box) { - self.callbacks.push(ClusterCallback { - name, - callback: callback, - }); + self.callbacks.push(ClusterCallback { name, callback }); } fn run_callback(&mut self, name: Commands) { diff --git a/matter/src/data_model/core/read.rs b/matter/src/data_model/core/read.rs index eb12a5b..c450073 100644 --- a/matter/src/data_model/core/read.rs +++ b/matter/src/data_model/core/read.rs @@ -221,7 +221,7 @@ impl DataModel { // // This is the amount of space we reserve for other things to be attached towards // the end - const RESERVE_SIZE: usize = 18; + const RESERVE_SIZE: usize = 24; let mut new_wb = wb_shrink!(old_wb, RESERVE_SIZE); let mut tw = TLVWriter::new(&mut new_wb); diff --git a/matter/src/data_model/objects/encoder.rs b/matter/src/data_model/objects/encoder.rs index 24a81aa..d565316 100644 --- a/matter/src/data_model/objects/encoder.rs +++ b/matter/src/data_model/objects/encoder.rs @@ -42,7 +42,7 @@ pub enum EncodeValue<'a> { Tlv(TLVElement<'a>), /// This indicates a static value. This variant is typically used in the transmit/ /// to-tlv path - Value(&'a (dyn ToTLV)), + Value(&'a dyn ToTLV), } impl<'a> EncodeValue<'a> { diff --git a/matter/src/data_model/sdm/noc.rs b/matter/src/data_model/sdm/noc.rs index b8c5e94..7785fbd 100644 --- a/matter/src/data_model/sdm/noc.rs +++ b/matter/src/data_model/sdm/noc.rs @@ -81,7 +81,7 @@ pub enum Commands { } #[derive(FromPrimitive)] -enum Attributes { +pub enum Attributes { NOCs = 0, Fabrics = 1, SupportedFabrics = 2, diff --git a/matter/src/data_model/system_model/descriptor.rs b/matter/src/data_model/system_model/descriptor.rs index ac778bb..010ed0a 100644 --- a/matter/src/data_model/system_model/descriptor.rs +++ b/matter/src/data_model/system_model/descriptor.rs @@ -28,7 +28,7 @@ pub const ID: u32 = 0x001D; #[derive(FromPrimitive)] #[allow(clippy::enum_variant_names)] -enum Attributes { +pub enum Attributes { DeviceTypeList = 0, ServerList = 1, ClientList = 2, diff --git a/matter/src/interaction_model/messages.rs b/matter/src/interaction_model/messages.rs index 3c164ae..d1de33f 100644 --- a/matter/src/interaction_model/messages.rs +++ b/matter/src/interaction_model/messages.rs @@ -76,7 +76,7 @@ pub mod msg { EventPath, }; - #[derive(FromTLV)] + #[derive(Default, FromTLV, ToTLV)] #[tlvargs(lifetime = "'a")] pub struct SubscribeReq<'a> { pub keep_subs: bool, @@ -92,6 +92,20 @@ pub mod msg { } impl<'a> SubscribeReq<'a> { + pub fn new(fabric_filtered: bool, min_int_floor: u16, max_int_ceil: u16) -> Self { + Self { + fabric_filtered, + min_int_floor, + max_int_ceil, + ..Default::default() + } + } + + pub fn set_attr_requests(mut self, requests: &'a [AttrPath]) -> Self { + self.attr_requests = Some(TLVArray::new(requests)); + self + } + pub fn to_read_req(&self) -> ReadReq<'a> { ReadReq { attr_requests: self.attr_requests, @@ -103,7 +117,7 @@ pub mod msg { } } - #[derive(ToTLV)] + #[derive(Debug, FromTLV, ToTLV)] pub struct SubscribeResp { pub subs_id: u32, // The Context Tags are discontiguous for some reason diff --git a/matter/tests/common/attributes.rs b/matter/tests/common/attributes.rs index 1131c27..1879adf 100644 --- a/matter/tests/common/attributes.rs +++ b/matter/tests/common/attributes.rs @@ -15,10 +15,14 @@ * limitations under the License. */ -use matter::interaction_model::{messages::ib::AttrResp, messages::msg::ReportDataMsg}; +use matter::{ + interaction_model::{messages::ib::AttrResp, messages::msg::ReportDataMsg}, + tlv::{TLVElement, TLVList, TLVWriter, TagType, ToTLV}, + utils::writebuf::WriteBuf, +}; /// Assert that the data received in the outbuf matches our expectations -pub fn assert_attr_report(received: &ReportDataMsg, expected: &[AttrResp]) { +pub fn __assert_attr_report(received: &ReportDataMsg, expected: &[AttrResp], skip_data: bool) { let mut index = 0; // We can't use assert_eq because it will also try to match data-version @@ -29,7 +33,9 @@ pub fn assert_attr_report(received: &ReportDataMsg, expected: &[AttrResp]) { AttrResp::Data(d) => { // We don't match the data-version assert_eq!(e_d.path, d.path); - assert_eq!(e_d.data, d.data); + if !skip_data { + assert_eq!(e_d.data, d.data); + } } _ => { panic!("Invalid response, expected AttrRespIn::Data"); @@ -43,12 +49,36 @@ pub fn assert_attr_report(received: &ReportDataMsg, expected: &[AttrResp]) { assert_eq!(index, expected.len()); } +pub fn assert_attr_report(received: &ReportDataMsg, expected: &[AttrResp]) { + __assert_attr_report(received, expected, false) +} + +pub fn assert_attr_report_skip_data(received: &ReportDataMsg, expected: &[AttrResp]) { + __assert_attr_report(received, expected, true) +} + // We have to hard-code this here, and it should match the tag // of the 'data' part in AttrData pub const ATTR_DATA_TAG_DATA: u8 = 2; #[macro_export] macro_rules! attr_data { + ($endpoint:expr, $cluster:expr, $attr: expr, $data:expr) => { + AttrResp::Data(AttrData { + data_ver: None, + path: AttrPath { + endpoint: Some($endpoint), + cluster: Some($cluster), + attr: Some($attr as u16), + ..Default::default() + }, + data: EncodeValue::Tlv(TLVElement::new(TagType::Context(ATTR_DATA_TAG_DATA), $data)), + }) + }; +} + +#[macro_export] +macro_rules! attr_data_path { ($path:expr, $data:expr) => { AttrResp::Data(AttrData { data_ver: None, @@ -69,3 +99,37 @@ macro_rules! attr_status { AttrResp::Status(AttrStatus::new($path, $status, 0)) }; } + +pub struct TLVHolder { + buf: [u8; 100], + used_len: usize, +} + +impl TLVHolder { + pub fn new_array<'a, T, I>(ctx_tag: u8, data: I) -> Self + where + T: ToTLV + 'a, + I: IntoIterator, + { + let mut s = Self { + buf: [0; 100], + used_len: 0, + }; + let buf_len = s.buf.len(); + let mut wb = WriteBuf::new(&mut s.buf, buf_len); + let mut tw = TLVWriter::new(&mut wb); + let _ = tw.start_array(TagType::Context(ctx_tag)); + for e in data { + let _ = e.to_tlv(&mut tw, TagType::Anonymous); + } + let _ = tw.end_container(); + + s.used_len = wb.as_slice().len(); + s + } + + pub fn to_tlv(&self) -> TLVElement { + let s = &self.buf[..self.used_len]; + TLVList::new(s).iter().next().unwrap() + } +} diff --git a/matter/tests/data_model/acl_and_dataver.rs b/matter/tests/data_model/acl_and_dataver.rs index 6627cde..493a282 100644 --- a/matter/tests/data_model/acl_and_dataver.rs +++ b/matter/tests/data_model/acl_and_dataver.rs @@ -34,7 +34,7 @@ use matter::{ }; use crate::{ - attr_data, attr_status, + attr_data_path, attr_status, common::{ attributes::*, echo_cluster::{self, ATTR_WRITE_DEFAULT_VALUE}, @@ -151,7 +151,7 @@ fn wc_read_attribute() { // Test2: Only Single response as only single endpoint is allowed let input = &[AttrPath::new(&wc_att1)]; - let expected = &[attr_data!(ep0_att1, ElementType::U16(0x1234))]; + let expected = &[attr_data_path!(ep0_att1, ElementType::U16(0x1234))]; handle_read_reqs(&mut im, peer, input, expected); // Add ACL to allow our peer to only access endpoint 1 @@ -163,8 +163,8 @@ fn wc_read_attribute() { // Test3: Both responses are valid let input = &[AttrPath::new(&wc_att1)]; let expected = &[ - attr_data!(ep0_att1, ElementType::U16(0x1234)), - attr_data!(ep1_att1, ElementType::U16(0x1234)), + attr_data_path!(ep0_att1, ElementType::U16(0x1234)), + attr_data_path!(ep1_att1, ElementType::U16(0x1234)), ]; handle_read_reqs(&mut im, peer, input, expected); } @@ -201,7 +201,7 @@ fn exact_read_attribute() { // Test2: Only Single response as only single endpoint is allowed let input = &[AttrPath::new(&wc_att1)]; - let expected = &[attr_data!(ep0_att1, ElementType::U16(0x1234))]; + let expected = &[attr_data_path!(ep0_att1, ElementType::U16(0x1234))]; handle_read_reqs(&mut im, peer, input, expected); } @@ -565,7 +565,7 @@ fn test_read_data_ver() { let input = &[AttrPath::new(&wc_ep_att1)]; let expected = &[ - attr_data!( + attr_data_path!( GenericPath::new( Some(0), Some(echo_cluster::ID), @@ -573,7 +573,7 @@ fn test_read_data_ver() { ), ElementType::U16(0x1234) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), @@ -614,7 +614,7 @@ fn test_read_data_ver() { Some(TLVArray::Slice(&dataver_filter)), &mut out_buf, ); - let expected_only_one = &[attr_data!( + let expected_only_one = &[attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), diff --git a/matter/tests/data_model/attributes.rs b/matter/tests/data_model/attributes.rs index f0a5111..091b89f 100644 --- a/matter/tests/data_model/attributes.rs +++ b/matter/tests/data_model/attributes.rs @@ -29,12 +29,11 @@ use matter::{ msg::{ReadReq, ReportDataMsg, WriteReq, WriteResp}, }, }, - tlv::{self, ElementType, FromTLV, TLVElement, TLVList, TLVWriter, TagType}, - utils::writebuf::WriteBuf, + tlv::{self, ElementType, FromTLV, TLVElement, TLVWriter, TagType}, }; use crate::{ - attr_data, attr_status, + attr_data, attr_data_path, attr_status, common::{attributes::*, echo_cluster, im_engine::im_engine}, }; @@ -95,9 +94,9 @@ fn test_read_success() { AttrPath::new(&ep1_attcustom), ]; let expected = &[ - attr_data!(ep0_att1, ElementType::U16(0x1234)), - attr_data!(ep1_att2, ElementType::U16(0x5678)), - attr_data!( + attr_data_path!(ep0_att1, ElementType::U16(0x1234)), + attr_data_path!(ep1_att2, ElementType::U16(0x5678)), + attr_data_path!( ep1_attcustom, ElementType::U32(echo_cluster::ATTR_CUSTOM_VALUE) ), @@ -168,19 +167,15 @@ fn test_read_wc_endpoint_all_have_clusters() { let expected = &[ attr_data!( - GenericPath::new( - Some(0), - Some(echo_cluster::ID), - Some(echo_cluster::Attributes::Att1 as u32) - ), + 0, + echo_cluster::ID, + echo_cluster::Attributes::Att1, ElementType::U16(0x1234) ), attr_data!( - GenericPath::new( - Some(1), - Some(echo_cluster::ID), - Some(echo_cluster::Attributes::Att1 as u32) - ), + 1, + echo_cluster::ID, + echo_cluster::Attributes::Att1, ElementType::U16(0x1234) ), ]; @@ -201,7 +196,7 @@ fn test_read_wc_endpoint_only_1_has_cluster() { ); let input = &[AttrPath::new(&wc_ep_onoff)]; - let expected = &[attr_data!( + let expected = &[attr_data_path!( GenericPath::new( Some(1), Some(cluster_on_off::ID), @@ -212,19 +207,6 @@ fn test_read_wc_endpoint_only_1_has_cluster() { handle_read_reqs(input, expected); } -fn get_tlvs<'a>(buf: &'a mut [u8], data: &[u16]) -> TLVElement<'a> { - let buf_len = buf.len(); - let mut wb = WriteBuf::new(buf, buf_len); - let mut tw = TLVWriter::new(&mut wb); - let _ = tw.start_array(TagType::Context(2)); - for e in data { - let _ = tw.u16(TagType::Anonymous, *e); - } - let _ = tw.end_container(); - let tlv_array = TLVList::new(wb.as_slice()).iter().next().unwrap(); - tlv_array -} - #[test] fn test_read_wc_endpoint_wc_attribute() { // 1 Attr Read Request @@ -234,9 +216,8 @@ fn test_read_wc_endpoint_wc_attribute() { let wc_ep_wc_attr = GenericPath::new(None, Some(echo_cluster::ID), None); let input = &[AttrPath::new(&wc_ep_wc_attr)]; - let mut buf = [0u8; 100]; - let attr_list_tlvs = get_tlvs( - &mut buf, + let attr_list = TLVHolder::new_array( + 2, &[ GlobalElements::FeatureMap as u16, GlobalElements::AttributeList as u16, @@ -246,9 +227,10 @@ fn test_read_wc_endpoint_wc_attribute() { echo_cluster::Attributes::AttCustom as u16, ], ); + let attr_list_tlv = attr_list.to_tlv(); let expected = &[ - attr_data!( + attr_data_path!( GenericPath::new( Some(0), Some(echo_cluster::ID), @@ -256,15 +238,15 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U8(0) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(0), Some(echo_cluster::ID), Some(GlobalElements::AttributeList as u32), ), - attr_list_tlvs.get_element_type() + attr_list_tlv.get_element_type() ), - attr_data!( + attr_data_path!( GenericPath::new( Some(0), Some(echo_cluster::ID), @@ -272,7 +254,7 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U16(0x1234) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(0), Some(echo_cluster::ID), @@ -280,7 +262,7 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U16(0x5678) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(0), Some(echo_cluster::ID), @@ -288,7 +270,7 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U32(echo_cluster::ATTR_CUSTOM_VALUE) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), @@ -296,15 +278,15 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U8(0) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), Some(GlobalElements::AttributeList as u32), ), - attr_list_tlvs.get_element_type() + attr_list_tlv.get_element_type() ), - attr_data!( + attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), @@ -312,7 +294,7 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U16(0x1234) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), @@ -320,7 +302,7 @@ fn test_read_wc_endpoint_wc_attribute() { ), ElementType::U16(0x5678) ), - attr_data!( + attr_data_path!( GenericPath::new( Some(1), Some(echo_cluster::ID), diff --git a/matter/tests/data_model/long_reads.rs b/matter/tests/data_model/long_reads.rs new file mode 100644 index 0000000..9f7957a --- /dev/null +++ b/matter/tests/data_model/long_reads.rs @@ -0,0 +1,224 @@ +/* + * + * Copyright (c) 2020-2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use matter::{ + data_model::{ + cluster_basic_information as basic_info, cluster_on_off as onoff, + objects::{EncodeValue, GlobalElements}, + sdm::{admin_commissioning as adm_comm, general_commissioning as gen_comm, noc}, + system_model::{access_control as acl, descriptor}, + }, + interaction_model::{ + core::{IMStatusCode, OpCode}, + messages::{ + ib::{AttrData, AttrPath, AttrResp}, + msg::{ReadReq, ReportDataMsg, StatusResp, SubscribeResp}, + }, + messages::{msg::SubscribeReq, GenericPath}, + }, + tlv::{self, ElementType, FromTLV, TLVElement, TagType, ToTLV}, + transport::{ + exchange::{self, Exchange}, + udp::MAX_RX_BUF_SIZE, + }, +}; + +use crate::{ + attr_data, + common::{ + attributes::*, + echo_cluster as echo, + im_engine::{ImEngine, ImInput}, + }, +}; + +pub struct LongRead { + im_engine: ImEngine, +} + +impl LongRead { + pub fn new() -> Self { + let mut im_engine = ImEngine::new(); + // Use the same exchange for all parts of the transaction + im_engine.exch = Some(Exchange::new(1, 0, exchange::Role::Responder)); + Self { im_engine } + } + + pub fn process<'a>( + &mut self, + action: OpCode, + data: &dyn ToTLV, + data_out: &'a mut [u8], + ) -> (u8, &'a mut [u8]) { + let input = ImInput::new(action, data); + let (response, output) = self.im_engine.process(&input, data_out); + (response, output) + } +} + +fn wildcard_read_resp(part: u8) -> Vec> { + // For brevity, we only check the AttrPath, not the actual 'data' + let dont_care = ElementType::U8(0); + let part1 = vec![ + attr_data!(0, 29, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 29, GlobalElements::AttributeList, dont_care), + attr_data!(0, 29, descriptor::Attributes::DeviceTypeList, dont_care), + attr_data!(0, 29, descriptor::Attributes::ServerList, dont_care), + attr_data!(0, 29, descriptor::Attributes::PartsList, dont_care), + attr_data!(0, 29, descriptor::Attributes::ClientList, dont_care), + attr_data!(0, 40, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 40, GlobalElements::AttributeList, dont_care), + attr_data!(0, 40, basic_info::Attributes::DMRevision, dont_care), + attr_data!(0, 40, basic_info::Attributes::VendorId, dont_care), + attr_data!(0, 40, basic_info::Attributes::ProductId, dont_care), + attr_data!(0, 40, basic_info::Attributes::HwVer, dont_care), + attr_data!(0, 40, basic_info::Attributes::SwVer, dont_care), + attr_data!(0, 40, basic_info::Attributes::SwVerString, dont_care), + attr_data!(0, 40, basic_info::Attributes::SerialNo, dont_care), + attr_data!(0, 48, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 48, GlobalElements::AttributeList, dont_care), + attr_data!(0, 48, gen_comm::Attributes::BreadCrumb, dont_care), + attr_data!(0, 48, gen_comm::Attributes::RegConfig, dont_care), + attr_data!(0, 48, gen_comm::Attributes::LocationCapability, dont_care), + attr_data!( + 0, + 48, + gen_comm::Attributes::BasicCommissioningInfo, + dont_care + ), + attr_data!(0, 49, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 49, GlobalElements::AttributeList, dont_care), + attr_data!(0, 60, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 60, GlobalElements::AttributeList, dont_care), + attr_data!(0, 60, adm_comm::Attributes::WindowStatus, dont_care), + attr_data!(0, 60, adm_comm::Attributes::AdminFabricIndex, dont_care), + attr_data!(0, 60, adm_comm::Attributes::AdminVendorId, dont_care), + attr_data!(0, 62, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 62, GlobalElements::AttributeList, dont_care), + attr_data!(0, 62, noc::Attributes::CurrentFabricIndex, dont_care), + attr_data!(0, 62, noc::Attributes::Fabrics, dont_care), + attr_data!(0, 62, noc::Attributes::SupportedFabrics, dont_care), + attr_data!(0, 62, noc::Attributes::CommissionedFabrics, dont_care), + attr_data!(0, 31, GlobalElements::FeatureMap, dont_care), + attr_data!(0, 31, GlobalElements::AttributeList, dont_care), + attr_data!(0, 31, acl::Attributes::Acl, dont_care), + attr_data!(0, 31, acl::Attributes::Extension, dont_care), + attr_data!(0, 31, acl::Attributes::SubjectsPerEntry, dont_care), + attr_data!(0, 31, acl::Attributes::TargetsPerEntry, dont_care), + attr_data!(0, 31, acl::Attributes::EntriesPerFabric, dont_care), + attr_data!(0, echo::ID, GlobalElements::FeatureMap, dont_care), + attr_data!(0, echo::ID, GlobalElements::AttributeList, dont_care), + attr_data!(0, echo::ID, echo::Attributes::Att1, dont_care), + attr_data!(0, echo::ID, echo::Attributes::Att2, dont_care), + attr_data!(0, echo::ID, echo::Attributes::AttCustom, dont_care), + attr_data!(1, 29, GlobalElements::FeatureMap, dont_care), + attr_data!(1, 29, GlobalElements::AttributeList, dont_care), + attr_data!(1, 29, descriptor::Attributes::DeviceTypeList, dont_care), + ]; + + let part2 = vec![ + attr_data!(1, 29, descriptor::Attributes::ServerList, dont_care), + attr_data!(1, 29, descriptor::Attributes::PartsList, dont_care), + attr_data!(1, 29, descriptor::Attributes::ClientList, dont_care), + attr_data!(1, 6, GlobalElements::FeatureMap, dont_care), + attr_data!(1, 6, GlobalElements::AttributeList, dont_care), + attr_data!(1, 6, onoff::Attributes::OnOff, dont_care), + attr_data!(1, echo::ID, GlobalElements::FeatureMap, dont_care), + attr_data!(1, echo::ID, GlobalElements::AttributeList, dont_care), + attr_data!(1, echo::ID, echo::Attributes::Att1, dont_care), + attr_data!(1, echo::ID, echo::Attributes::Att2, dont_care), + attr_data!(1, echo::ID, echo::Attributes::AttCustom, dont_care), + ]; + + if part == 1 { + part1 + } else { + part2 + } +} + +#[test] +fn test_long_read_success() { + // Read the entire attribute database, which requires 2 reads to complete + let _ = env_logger::try_init(); + let mut lr = LongRead::new(); + let mut output = [0_u8; MAX_RX_BUF_SIZE + 100]; + + let wc_path = GenericPath::new(None, None, None); + + let read_all = [AttrPath::new(&wc_path)]; + let read_req = ReadReq::new(true).set_attr_requests(&read_all); + let expected_part1 = wildcard_read_resp(1); + let (out_code, out_data) = lr.process(OpCode::ReadRequest, &read_req, &mut output); + let root = tlv::get_root_node_struct(out_data).unwrap(); + let report_data = ReportDataMsg::from_tlv(&root).unwrap(); + assert_attr_report_skip_data(&report_data, &expected_part1); + assert_eq!(report_data.more_chunks, Some(true)); + assert_eq!(out_code, OpCode::ReportData as u8); + + // Ask for the next read by sending a status report + let status_report = StatusResp { + status: IMStatusCode::Success, + }; + let expected_part2 = wildcard_read_resp(2); + let (out_code, out_data) = lr.process(OpCode::StatusResponse, &status_report, &mut output); + let root = tlv::get_root_node_struct(out_data).unwrap(); + let report_data = ReportDataMsg::from_tlv(&root).unwrap(); + assert_attr_report_skip_data(&report_data, &expected_part2); + assert_eq!(report_data.more_chunks, None); + assert_eq!(out_code, OpCode::ReportData as u8); +} + +#[test] +fn test_long_read_subscription_success() { + // Subscribe to the entire attribute database, which requires 2 reads to complete + let _ = env_logger::try_init(); + let mut lr = LongRead::new(); + let mut output = [0_u8; MAX_RX_BUF_SIZE + 100]; + + let wc_path = GenericPath::new(None, None, None); + + let read_all = [AttrPath::new(&wc_path)]; + let subs_req = SubscribeReq::new(true, 1, 20).set_attr_requests(&read_all); + let expected_part1 = wildcard_read_resp(1); + let (out_code, out_data) = lr.process(OpCode::SubscribeRequest, &subs_req, &mut output); + let root = tlv::get_root_node_struct(out_data).unwrap(); + let report_data = ReportDataMsg::from_tlv(&root).unwrap(); + assert_attr_report_skip_data(&report_data, &expected_part1); + assert_eq!(report_data.more_chunks, Some(true)); + assert_eq!(out_code, OpCode::ReportData as u8); + + // Ask for the next read by sending a status report + let status_report = StatusResp { + status: IMStatusCode::Success, + }; + let expected_part2 = wildcard_read_resp(2); + let (out_code, out_data) = lr.process(OpCode::StatusResponse, &status_report, &mut output); + let root = tlv::get_root_node_struct(out_data).unwrap(); + let report_data = ReportDataMsg::from_tlv(&root).unwrap(); + assert_attr_report_skip_data(&report_data, &expected_part2); + assert_eq!(report_data.more_chunks, None); + assert_eq!(out_code, OpCode::ReportData as u8); + + // Finally confirm subscription + let (out_code, out_data) = lr.process(OpCode::StatusResponse, &status_report, &mut output); + tlv::print_tlv_list(out_data); + let root = tlv::get_root_node_struct(out_data).unwrap(); + let subs_resp = SubscribeResp::from_tlv(&root).unwrap(); + assert_eq!(out_code, OpCode::SubscriptResponse as u8); + assert_eq!(subs_resp.subs_id, 1); +} diff --git a/matter/tests/data_model_tests.rs b/matter/tests/data_model_tests.rs index 64a6f90..392909f 100644 --- a/matter/tests/data_model_tests.rs +++ b/matter/tests/data_model_tests.rs @@ -22,5 +22,6 @@ mod data_model { mod attribute_lists; mod attributes; mod commands; + mod long_reads; mod timed_requests; }