rs-matter/matter/tests/common/handlers.rs
ivmarkov d446007f6b Support for no_std
Support for no_std

Further no_std compat
2023-05-14 09:08:51 +00:00

317 lines
9.3 KiB
Rust

use core::time;
use std::thread;
use log::{info, warn};
use matter::{
interaction_model::{
core::{IMStatusCode, OpCode},
messages::{
ib::{AttrData, AttrPath, AttrResp, AttrStatus, CmdData, DataVersionFilter},
msg::{
self, InvReq, ReadReq, ReportDataMsg, StatusResp, TimedReq, WriteReq, WriteResp,
WriteRespTag,
},
},
},
tlv::{self, FromTLV, TLVArray, ToTLV},
transport::{
exchange::{self, Exchange},
session::NocCatIds,
},
Matter,
};
use super::{
attributes::assert_attr_report,
commands::{assert_inv_response, ExpectedInvResp},
im_engine::{ImEngine, ImInput, IM_ENGINE_PEER_ID},
};
pub enum WriteResponse<'a> {
TransactionError,
TransactionSuccess(&'a [AttrStatus]),
}
pub enum TimedInvResponse<'a> {
TransactionError(IMStatusCode),
TransactionSuccess(&'a [ExpectedInvResp]),
}
impl<'a> ImEngine<'a> {
// Helper for handling Read Req sequences for this file
pub fn handle_read_reqs(
&mut self,
peer_node_id: u64,
input: &[AttrPath],
expected: &[AttrResp],
) {
let mut out_buf = [0u8; 400];
let received = self.gen_read_reqs_output(peer_node_id, input, None, &mut out_buf);
assert_attr_report(&received, expected)
}
pub fn new_with_read_reqs(
matter: &'a Matter<'a>,
input: &[AttrPath],
expected: &[AttrResp],
) -> Self {
let mut im = Self::new(matter);
let mut out_buf = [0u8; 400];
let received = im.gen_read_reqs_output(IM_ENGINE_PEER_ID, input, None, &mut out_buf);
assert_attr_report(&received, expected);
im
}
pub fn gen_read_reqs_output<'b>(
&mut self,
peer_node_id: u64,
input: &[AttrPath],
dataver_filters: Option<TLVArray<'b, DataVersionFilter>>,
out_buf: &'b mut [u8],
) -> ReportDataMsg<'b> {
let mut read_req = ReadReq::new(true).set_attr_requests(input);
read_req.dataver_filters = dataver_filters;
let mut input = ImInput::new(OpCode::ReadRequest, &read_req);
input.set_peer_node_id(peer_node_id);
let (_, out_buf) = self.process(&input, out_buf);
tlv::print_tlv_list(out_buf);
let root = tlv::get_root_node_struct(out_buf).unwrap();
ReportDataMsg::from_tlv(&root).unwrap()
}
pub fn handle_write_reqs(
&mut self,
peer_node_id: u64,
peer_cat_ids: Option<&NocCatIds>,
input: &[AttrData],
expected: &[AttrStatus],
) {
let mut out_buf = [0u8; 400];
let write_req = WriteReq::new(false, input);
let mut input = ImInput::new(OpCode::WriteRequest, &write_req);
input.set_peer_node_id(peer_node_id);
if let Some(cat_ids) = peer_cat_ids {
input.set_cat_ids(cat_ids);
}
let (_, out_buf) = self.process(&input, &mut out_buf);
tlv::print_tlv_list(out_buf);
let root = tlv::get_root_node_struct(out_buf).unwrap();
let mut index = 0;
let response_iter = root
.find_tag(WriteRespTag::WriteResponses as u32)
.unwrap()
.confirm_array()
.unwrap()
.enter()
.unwrap();
for response in response_iter {
info!("Validating index {}", index);
let status = AttrStatus::from_tlv(&response).unwrap();
assert_eq!(expected[index], status);
info!("Index {} success", index);
index += 1;
}
assert_eq!(index, expected.len());
}
pub fn new_with_write_reqs(
matter: &'a Matter<'a>,
input: &[AttrData],
expected: &[AttrStatus],
) -> Self {
let mut im = Self::new(matter);
im.handle_write_reqs(IM_ENGINE_PEER_ID, None, input, expected);
im
}
// Helper for handling Invoke Command sequences
pub fn handle_commands(
&mut self,
peer_node_id: u64,
input: &[CmdData],
expected: &[ExpectedInvResp],
) {
let mut out_buf = [0u8; 400];
let req = InvReq {
suppress_response: Some(false),
timed_request: Some(false),
inv_requests: Some(TLVArray::Slice(input)),
};
let mut input = ImInput::new(OpCode::InvokeRequest, &req);
input.set_peer_node_id(peer_node_id);
let (_, out_buf) = self.process(&input, &mut out_buf);
tlv::print_tlv_list(out_buf);
let root = tlv::get_root_node_struct(out_buf).unwrap();
let resp = msg::InvResp::from_tlv(&root).unwrap();
assert_inv_response(&resp, expected)
}
pub fn new_with_commands(
matter: &'a Matter<'a>,
input: &[CmdData],
expected: &[ExpectedInvResp],
) -> Self {
let mut im = ImEngine::new(matter);
im.handle_commands(IM_ENGINE_PEER_ID, input, expected);
im
}
fn handle_timed_reqs<'b>(
&mut self,
opcode: OpCode,
request: &dyn ToTLV,
timeout: u16,
delay: u16,
output: &'b mut [u8],
) -> (u8, &'b [u8]) {
// Use the same exchange for all parts of the transaction
self.exch = Some(Exchange::new(1, 0, exchange::Role::Responder));
if timeout != 0 {
// Send Timed Req
let mut tmp_buf = [0u8; 400];
let timed_req = TimedReq { timeout };
let im_input = ImInput::new(OpCode::TimedRequest, &timed_req);
let (_, out_buf) = self.process(&im_input, &mut tmp_buf);
tlv::print_tlv_list(out_buf);
} else {
warn!("Skipping timed request");
}
// Process any delays
let delay = time::Duration::from_millis(delay.into());
thread::sleep(delay);
// Send Write Req
let input = ImInput::new(opcode, request);
let (resp_opcode, output) = self.process(&input, output);
(resp_opcode, output)
}
// Helper for handling Write Attribute sequences
pub fn handle_timed_write_reqs(
&mut self,
input: &[AttrData],
expected: &WriteResponse,
timeout: u16,
delay: u16,
) {
let mut out_buf = [0u8; 400];
let write_req = WriteReq::new(false, input);
let (resp_opcode, out_buf) = self.handle_timed_reqs(
OpCode::WriteRequest,
&write_req,
timeout,
delay,
&mut out_buf,
);
tlv::print_tlv_list(out_buf);
let root = tlv::get_root_node_struct(out_buf).unwrap();
match expected {
WriteResponse::TransactionSuccess(t) => {
assert_eq!(
num::FromPrimitive::from_u8(resp_opcode),
Some(OpCode::WriteResponse)
);
let resp = WriteResp::from_tlv(&root).unwrap();
assert_eq!(resp.write_responses, t);
}
WriteResponse::TransactionError => {
assert_eq!(
num::FromPrimitive::from_u8(resp_opcode),
Some(OpCode::StatusResponse)
);
let status_resp = StatusResp::from_tlv(&root).unwrap();
assert_eq!(status_resp.status, IMStatusCode::Timeout);
}
}
}
pub fn new_with_timed_write_reqs(
matter: &'a Matter<'a>,
input: &[AttrData],
expected: &WriteResponse,
timeout: u16,
delay: u16,
) -> Self {
let mut im = ImEngine::new(matter);
im.handle_timed_write_reqs(input, expected, timeout, delay);
im
}
// Helper for handling Invoke Command sequences
pub fn handle_timed_commands(
&mut self,
input: &[CmdData],
expected: &TimedInvResponse,
timeout: u16,
delay: u16,
set_timed_request: bool,
) {
let mut out_buf = [0u8; 400];
let req = InvReq {
suppress_response: Some(false),
timed_request: Some(set_timed_request),
inv_requests: Some(TLVArray::Slice(input)),
};
let (resp_opcode, out_buf) =
self.handle_timed_reqs(OpCode::InvokeRequest, &req, timeout, delay, &mut out_buf);
tlv::print_tlv_list(out_buf);
let root = tlv::get_root_node_struct(out_buf).unwrap();
match expected {
TimedInvResponse::TransactionSuccess(t) => {
assert_eq!(
num::FromPrimitive::from_u8(resp_opcode),
Some(OpCode::InvokeResponse)
);
let resp = msg::InvResp::from_tlv(&root).unwrap();
assert_inv_response(&resp, t)
}
TimedInvResponse::TransactionError(e) => {
assert_eq!(
num::FromPrimitive::from_u8(resp_opcode),
Some(OpCode::StatusResponse)
);
let status_resp = StatusResp::from_tlv(&root).unwrap();
assert_eq!(status_resp.status, *e);
}
}
}
pub fn new_with_timed_commands(
matter: &'a Matter<'a>,
input: &[CmdData],
expected: &TimedInvResponse,
timeout: u16,
delay: u16,
set_timed_request: bool,
) -> Self {
let mut im = ImEngine::new(matter);
im.handle_timed_commands(input, expected, timeout, delay, set_timed_request);
im
}
}