tests: Add tests for timed invoke
This commit is contained in:
parent
4a041e1f8c
commit
91b451db06
4 changed files with 257 additions and 98 deletions
98
matter/tests/common/commands.rs
Normal file
98
matter/tests/common/commands.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2023 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::objects::EncodeValue,
|
||||
interaction_model::{
|
||||
messages::ib::{CmdPath, CmdStatus, InvResp},
|
||||
messages::msg,
|
||||
},
|
||||
};
|
||||
|
||||
pub enum ExpectedInvResp {
|
||||
Cmd(CmdPath, u8),
|
||||
Status(CmdStatus),
|
||||
}
|
||||
|
||||
pub fn assert_inv_response(resp: &msg::InvResp, expected: &[ExpectedInvResp]) {
|
||||
let mut index = 0;
|
||||
for inv_response in resp.inv_responses.unwrap().iter() {
|
||||
println!("Validating index {}", index);
|
||||
match expected[index] {
|
||||
ExpectedInvResp::Cmd(e_c, e_d) => match inv_response {
|
||||
InvResp::Cmd(c) => {
|
||||
assert_eq!(e_c, c.path);
|
||||
match c.data {
|
||||
EncodeValue::Tlv(t) => {
|
||||
assert_eq!(e_d, t.find_tag(0).unwrap().u8().unwrap())
|
||||
}
|
||||
_ => panic!("Incorrect CmdDataType"),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid response, expected InvResponse::Cmd");
|
||||
}
|
||||
},
|
||||
ExpectedInvResp::Status(e_status) => match inv_response {
|
||||
InvResp::Status(status) => {
|
||||
assert_eq!(e_status, status);
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid response, expected InvResponse::Status");
|
||||
}
|
||||
},
|
||||
}
|
||||
println!("Index {} success", index);
|
||||
index += 1;
|
||||
}
|
||||
assert_eq!(index, expected.len());
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cmd_data {
|
||||
($path:ident, $data:literal) => {
|
||||
CmdData::new($path, EncodeValue::Value(&($data as u32)))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! echo_req {
|
||||
($endpoint:literal, $data:literal) => {
|
||||
CmdData::new(
|
||||
CmdPath::new(
|
||||
Some($endpoint),
|
||||
Some(echo_cluster::ID),
|
||||
Some(echo_cluster::Commands::EchoReq as u16),
|
||||
),
|
||||
EncodeValue::Value(&($data as u32)),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! echo_resp {
|
||||
($endpoint:literal, $data:literal) => {
|
||||
ExpectedInvResp::Cmd(
|
||||
CmdPath::new(
|
||||
Some($endpoint),
|
||||
Some(echo_cluster::ID),
|
||||
Some(echo_cluster::Commands::EchoResp as u16),
|
||||
),
|
||||
$data,
|
||||
)
|
||||
};
|
||||
}
|
|
@ -16,5 +16,6 @@
|
|||
*/
|
||||
|
||||
pub mod attributes;
|
||||
pub mod commands;
|
||||
pub mod echo_cluster;
|
||||
pub mod im_engine;
|
||||
|
|
|
@ -15,26 +15,25 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
cmd_data,
|
||||
common::{commands::*, echo_cluster, im_engine::im_engine},
|
||||
echo_req, echo_resp,
|
||||
};
|
||||
|
||||
use matter::{
|
||||
data_model::{cluster_on_off, objects::EncodeValue},
|
||||
interaction_model::{
|
||||
core::{IMStatusCode, OpCode},
|
||||
messages::msg,
|
||||
messages::{
|
||||
ib::{CmdData, CmdPath, CmdStatus, InvResp},
|
||||
ib::{CmdData, CmdPath, CmdStatus},
|
||||
msg,
|
||||
msg::InvReq,
|
||||
},
|
||||
},
|
||||
tlv::{self, FromTLV, TLVArray},
|
||||
};
|
||||
|
||||
use crate::common::{echo_cluster, im_engine::im_engine};
|
||||
|
||||
enum ExpectedInvResp {
|
||||
Cmd(CmdPath, u8),
|
||||
Status(CmdStatus),
|
||||
}
|
||||
|
||||
// Helper for handling Invoke Command sequences
|
||||
fn handle_commands(input: &[CmdData], expected: &[ExpectedInvResp]) {
|
||||
let mut out_buf = [0u8; 400];
|
||||
|
@ -47,71 +46,8 @@ fn handle_commands(input: &[CmdData], expected: &[ExpectedInvResp]) {
|
|||
let (_, _, out_buf) = im_engine(OpCode::InvokeRequest, &req, &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();
|
||||
let mut index = 0;
|
||||
for inv_response in resp.inv_responses.unwrap().iter() {
|
||||
println!("Validating index {}", index);
|
||||
match expected[index] {
|
||||
ExpectedInvResp::Cmd(e_c, e_d) => match inv_response {
|
||||
InvResp::Cmd(c) => {
|
||||
assert_eq!(e_c, c.path);
|
||||
match c.data {
|
||||
EncodeValue::Tlv(t) => {
|
||||
assert_eq!(e_d, t.find_tag(0).unwrap().u8().unwrap())
|
||||
}
|
||||
_ => panic!("Incorrect CmdDataType"),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid response, expected InvResponse::Cmd");
|
||||
}
|
||||
},
|
||||
ExpectedInvResp::Status(e_status) => match inv_response {
|
||||
InvResp::Status(status) => {
|
||||
assert_eq!(e_status, status);
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid response, expected InvResponse::Status");
|
||||
}
|
||||
},
|
||||
}
|
||||
println!("Index {} success", index);
|
||||
index += 1;
|
||||
}
|
||||
assert_eq!(index, expected.len());
|
||||
}
|
||||
|
||||
macro_rules! cmd_data {
|
||||
($path:ident, $data:literal) => {
|
||||
CmdData::new($path, EncodeValue::Value(&($data as u32)))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! echo_req {
|
||||
($endpoint:literal, $data:literal) => {
|
||||
CmdData::new(
|
||||
CmdPath::new(
|
||||
Some($endpoint),
|
||||
Some(echo_cluster::ID),
|
||||
Some(echo_cluster::Commands::EchoReq as u16),
|
||||
),
|
||||
EncodeValue::Value(&($data as u32)),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! echo_resp {
|
||||
($endpoint:literal, $data:literal) => {
|
||||
ExpectedInvResp::Cmd(
|
||||
CmdPath::new(
|
||||
Some($endpoint),
|
||||
Some(echo_cluster::ID),
|
||||
Some(echo_cluster::Commands::EchoResp as u16),
|
||||
),
|
||||
$data,
|
||||
)
|
||||
};
|
||||
assert_inv_response(&resp, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -25,21 +25,56 @@ use matter::{
|
|||
},
|
||||
interaction_model::{
|
||||
core::{IMStatusCode, OpCode},
|
||||
messages::GenericPath,
|
||||
messages::{ib::CmdData, ib::CmdPath, msg::InvReq, GenericPath},
|
||||
messages::{
|
||||
ib::{AttrData, AttrPath, AttrStatus},
|
||||
msg::{StatusResp, TimedReq, WriteReq, WriteResp},
|
||||
msg::{self, StatusResp, TimedReq, WriteReq, WriteResp},
|
||||
},
|
||||
},
|
||||
tlv::{self, FromTLV, TLVWriter},
|
||||
tlv::{self, FromTLV, TLVArray, TLVWriter, ToTLV},
|
||||
transport::exchange::{self, Exchange},
|
||||
};
|
||||
|
||||
use crate::common::{
|
||||
echo_cluster,
|
||||
im_engine::{ImEngine, ImInput},
|
||||
use crate::{
|
||||
common::{
|
||||
commands::*,
|
||||
echo_cluster,
|
||||
im_engine::{ImEngine, ImInput},
|
||||
},
|
||||
echo_req, echo_resp,
|
||||
};
|
||||
|
||||
fn handle_timed_reqs<'a>(
|
||||
opcode: OpCode,
|
||||
request: &dyn ToTLV,
|
||||
timeout: u16,
|
||||
delay: u16,
|
||||
output: &'a mut [u8],
|
||||
) -> (u8, DataModel, &'a [u8]) {
|
||||
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));
|
||||
|
||||
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) = im_engine.process(&im_input, &mut tmp_buf);
|
||||
tlv::print_tlv_list(out_buf);
|
||||
} else {
|
||||
println!("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) = im_engine.process(&input, output);
|
||||
(resp_opcode, im_engine.dm, output)
|
||||
}
|
||||
enum WriteResponse<'a> {
|
||||
TransactionError,
|
||||
TransactionSuccess(&'a [AttrStatus]),
|
||||
|
@ -52,27 +87,16 @@ fn handle_timed_write_reqs(
|
|||
timeout: u16,
|
||||
delay: u16,
|
||||
) -> DataModel {
|
||||
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));
|
||||
|
||||
// Send Timed Req
|
||||
let mut out_buf = [0u8; 400];
|
||||
let timed_req = TimedReq { timeout };
|
||||
let im_input = ImInput::new(OpCode::TimedRequest, &timed_req);
|
||||
let (_, out_buf) = im_engine.process(&im_input, &mut out_buf);
|
||||
tlv::print_tlv_list(out_buf);
|
||||
|
||||
// Process any delays
|
||||
let delay = time::Duration::from_millis(delay.into());
|
||||
thread::sleep(delay);
|
||||
|
||||
// Send Write Req
|
||||
let mut out_buf = [0u8; 400];
|
||||
let write_req = WriteReq::new(false, input);
|
||||
let input = ImInput::new(OpCode::WriteRequest, &write_req);
|
||||
let (resp_opcode, out_buf) = im_engine.process(&input, &mut out_buf);
|
||||
|
||||
let (resp_opcode, dm, out_buf) = 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();
|
||||
|
||||
|
@ -94,7 +118,7 @@ fn handle_timed_write_reqs(
|
|||
assert_eq!(status_resp.status, IMStatusCode::Timeout);
|
||||
}
|
||||
}
|
||||
im_engine.dm
|
||||
dm
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -158,3 +182,103 @@ fn test_timed_write_fail_and_success() {
|
|||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
enum TimedInvResponse<'a> {
|
||||
TransactionError(IMStatusCode),
|
||||
TransactionSuccess(&'a [ExpectedInvResp]),
|
||||
}
|
||||
// Helper for handling Invoke Command sequences
|
||||
fn handle_timed_commands(
|
||||
input: &[CmdData],
|
||||
expected: TimedInvResponse,
|
||||
timeout: u16,
|
||||
delay: u16,
|
||||
set_timed_request: bool,
|
||||
) -> DataModel {
|
||||
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, dm, out_buf) =
|
||||
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);
|
||||
}
|
||||
}
|
||||
dm
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timed_cmd_success() {
|
||||
// A timed request that works
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let input = &[echo_req!(0, 5), echo_req!(1, 10)];
|
||||
let expected = &[echo_resp!(0, 10), echo_resp!(1, 30)];
|
||||
handle_timed_commands(
|
||||
input,
|
||||
TimedInvResponse::TransactionSuccess(expected),
|
||||
400,
|
||||
0,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timed_cmd_timeout() {
|
||||
// A timed request that is executed after t imeout
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let input = &[echo_req!(0, 5), echo_req!(1, 10)];
|
||||
handle_timed_commands(
|
||||
input,
|
||||
TimedInvResponse::TransactionError(IMStatusCode::Timeout),
|
||||
400,
|
||||
500,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timed_cmd_timedout_mismatch() {
|
||||
// A timed request with timeout mismatch
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let input = &[echo_req!(0, 5), echo_req!(1, 10)];
|
||||
handle_timed_commands(
|
||||
input,
|
||||
TimedInvResponse::TransactionError(IMStatusCode::TimedRequestMisMatch),
|
||||
400,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
|
||||
let input = &[echo_req!(0, 5), echo_req!(1, 10)];
|
||||
handle_timed_commands(
|
||||
input,
|
||||
TimedInvResponse::TransactionError(IMStatusCode::TimedRequestMisMatch),
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue