Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "shvclient"
description = "A Rust framework for Silicon Heaven RPC devices"
license = "MIT"
repository = "https://github.com/silicon-heaven/libshvclient-rs"
version = "4.1.0"
version = "4.2.0"
edition = "2024"

[lib]
Expand Down
4 changes: 2 additions & 2 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ impl<V: ClientVariant> Client<V> {

#[cfg(feature = "mocking")]
async fn mock_run_with_init_opt<H>(
&mut self,
&self,
init_handler: Option<H>,
conn_evt_rx: futures::channel::mpsc::UnboundedReceiver::<ConnectionEvent>,
) -> shvrpc::Result<()>
Expand All @@ -261,7 +261,7 @@ impl<V: ClientVariant> Client<V> {

#[cfg(feature = "mocking")]
pub async fn mock_run_with_init<H>(
mut self,
self,
handler: H,
channel: futures::channel::mpsc::UnboundedReceiver::<ConnectionEvent>
) -> shvrpc::Result<()>
Expand Down
56 changes: 46 additions & 10 deletions src/mocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ impl PendingResponse<'_> {
match expected_result {
Ok(expected_result) => {
let result = response.expect("Expected a success response");
assert_eq!(*result, RpcValue::from_cpon(expected_result).unwrap_or_else(|err| panic!("Invalid CPON '{expected_result}': {err}")));
assert_eq!(*result, RpcValue::from_cpon(expected_result).unwrap_or_else(|err| panic!("Invalid CPON '{expected_result}': {err}")), "Unexpected value of the result");
},
Err(err) => {
let result = response.expect_err("Expected an Err response");
assert_eq!(result.to_string(), err);
assert_eq!(result.to_string(), err, "Unexpected value of the result");
},
}
}
Expand All @@ -47,6 +47,27 @@ impl PendingRequest {
}
}

pub trait ParamMatcher {
fn matches(&self, actual: &RpcValue) -> bool;
}

impl ParamMatcher for &str {
fn matches(&self, actual: &RpcValue) -> bool {
let expected = RpcValue::from_cpon(self)
.unwrap_or_else(|err| panic!("Invalid CPON '{self}': {err}"));
actual == &expected
}
}

impl<F> ParamMatcher for F
where
F: Fn(&RpcValue) -> bool
{
fn matches(&self, actual: &RpcValue) -> bool {
self(actual)
}
}

pub struct TestApp {
app: JoinHandle<Result<(), Box<dyn Error + Send + Sync>>>,
msg_task: JoinHandle<()>,
Expand Down Expand Up @@ -99,10 +120,13 @@ impl TestApp {
self.conn_evt_tx.unbounded_send(ConnectionEvent::RpcFrameReceived(rpc_message.to_frame().expect("to_frame must work"))).expect("events must work");
}

pub async fn await_signal(&self, expected_ri: impl TryInto<ShvRI, Error = impl std::fmt::Display>, expected_param: &str) {
pub async fn await_signal(
&self,
expected_ri: impl TryInto<ShvRI, Error = impl std::fmt::Display>,
expected_param: impl ParamMatcher,
) {
let expected_ri = expected_ri.try_into().unwrap_or_else(|err| panic!("Invalid RI: {err}"));
let expected_signal = expected_ri.signal().unwrap_or_else(|| panic!("Signal RI must have a signal: {expected_ri}"));
let expected_param = RpcValue::from_cpon(expected_param).unwrap_or_else(|err| panic!("Invalid CPON '{expected_param}': {err}"));

self.await_and_remove(|rpc_message| {
if !rpc_message.is_signal() {
Expand All @@ -111,13 +135,19 @@ impl TestApp {
let shv_path = rpc_message.shv_path().expect("msg must have a path");
let method = rpc_message.method().expect("msg must have a method");
let param = rpc_message.param().cloned().unwrap_or_else(RpcValue::null);
shv_path == expected_ri.path() && method == expected_signal && param == expected_param

shv_path == expected_ri.path() &&
method == expected_signal &&
expected_param.matches(&param)
}).await;
}

pub async fn await_request(&self, expected_ri: impl TryInto<ShvRI, Error = impl std::fmt::Display>, expected_param: &str) -> PendingRequest {
pub async fn await_request(
&self,
expected_ri: impl TryInto<ShvRI, Error = impl std::fmt::Display>,
expected_param: impl ParamMatcher,
) -> PendingRequest {
let expected_ri = expected_ri.try_into().unwrap_or_else(|err| panic!("Invalid RI: {err}"));
let expected_param = RpcValue::from_cpon(expected_param).unwrap_or_else(|err| panic!("Invalid CPON '{expected_param}': {err}"));

let rpc_message = self.await_and_remove(|rpc_message| {
if !rpc_message.is_request() {
Expand All @@ -126,16 +156,22 @@ impl TestApp {
let shv_path = rpc_message.shv_path().expect("msg must have a path");
let method = rpc_message.method().expect("msg must have a method");
let param = rpc_message.param().cloned().unwrap_or_else(RpcValue::null);
shv_path == expected_ri.path() && method == expected_ri.method() && param == expected_param

shv_path == expected_ri.path() &&
method == expected_ri.method() &&
expected_param.matches(&param)
}).await;

PendingRequest(rpc_message.prepare_response().expect("prepare_response must work"), self.conn_evt_tx.clone())
PendingRequest(
rpc_message.prepare_response().expect("prepare_response must work"),
self.conn_evt_tx.clone()
)
}

pub async fn await_subscription(&self, expected_ri: impl TryInto<ShvRI, Error = impl std::fmt::Display>) {
let expected_ri = expected_ri.try_into().unwrap_or_else(|err| panic!("Invalid RI: {err}"));
let expected_ri = expected_ri.as_str();
self.await_request(".broker/currentClient:subscribe", &format!(r#"["{expected_ri}",null]""#)).await
self.await_request(".broker/currentClient:subscribe", format!(r#"["{expected_ri}",null]""#).as_str()).await
.respond(Ok("true"));
}

Expand Down
Loading