bt_fidl_mocks/
expect.rs
1use anyhow::{format_err, Error};
6use fidl::endpoints::RequestStream;
7use fuchsia_async::{DurationExt, TimeoutExt};
8use futures::{TryStream, TryStreamExt};
9use zx::MonotonicDuration;
10
11pub(crate) enum Status<T> {
13 Pending,
14 Satisfied(T),
15}
16
17pub(crate) async fn expect_call<H, T, R, S>(
20 stream: &mut S,
21 timeout: MonotonicDuration,
22 mut handler: H,
23) -> Result<T, Error>
24where
25 H: FnMut(R) -> Result<Status<T>, Error>,
26 S: RequestStream + TryStream<Ok = R, Error = fidl::Error>,
27{
28 async {
29 while let Some(msg) = stream.try_next().await? {
30 match handler(msg)? {
31 Status::Satisfied(t) => {
32 return Ok(t);
33 }
34 Status::Pending => (),
35 }
36 }
37 Err(format_err!("Stream closed before expectation was satisifed"))
38 }
39 .on_timeout(timeout.after_now(), || {
40 Err(format_err!("timed out before expectation was satisfied"))
41 })
42 .await
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use fidl::endpoints::create_proxy_and_stream;
49 use fidl_fuchsia_bluetooth::DeviceClass;
50 use fidl_fuchsia_bluetooth_sys::{AccessMarker, AccessRequest, AccessRequestStream};
51
52 async fn expect_set_local_name(
58 mut stream: AccessRequestStream,
59 expected_name: String,
60 ) -> Result<(), Error> {
61 expect_call(&mut stream, zx::MonotonicDuration::from_millis(500), move |req| match req {
62 AccessRequest::SetLocalName { name, control_handle: _ } => {
63 if name == expected_name {
64 Ok(Status::Satisfied(()))
65 } else {
66 Err(format_err!("received incorrect name"))
67 }
68 }
69 _ => Ok(Status::Pending),
70 })
71 .await
72 }
73
74 #[fuchsia_async::run_until_stalled(test)]
75 async fn test_satisfied() {
76 let (proxy, stream) = create_proxy_and_stream::<AccessMarker>();
77 let name = "TEST".to_string();
78
79 let _ = proxy.set_local_name(&name);
80 let mock_result = expect_set_local_name(stream, name).await;
81 assert!(mock_result.is_ok());
82 }
83
84 #[fuchsia_async::run_until_stalled(test)]
85 async fn test_unsatisfied_due_to_mismatch() {
86 let (proxy, stream) = create_proxy_and_stream::<AccessMarker>();
87 let expected_name = "TEST".to_string();
88 let wrong_name = "💩".to_string();
89
90 let _ = proxy.set_local_name(&wrong_name);
91 let mock_result = expect_set_local_name(stream, expected_name).await;
92 assert!(mock_result.is_err());
93 }
94
95 #[fuchsia_async::run_singlethreaded(test)]
96 async fn test_timeout_without_any_message() {
97 let (_proxy, stream) = create_proxy_and_stream::<AccessMarker>();
98 let expected_name = "TEST".to_string();
99 let result = expect_set_local_name(stream, expected_name).await;
100 assert!(result.is_err());
101 }
102
103 #[fuchsia_async::run_singlethreaded(test)]
104 async fn test_timeout_after_unexpected_message() {
105 let (proxy, stream) = create_proxy_and_stream::<AccessMarker>();
106 let expected_name = "TEST".to_string();
107
108 let _ = proxy.set_device_class(&DeviceClass { value: 0 });
109 let mock_result = expect_set_local_name(stream, expected_name).await;
110 assert!(mock_result.is_err());
111 }
112
113 #[fuchsia_async::run_until_stalled(test)]
114 async fn test_error_after_handle_closure() {
115 let (proxy, stream) = create_proxy_and_stream::<AccessMarker>();
116 let expected_name = "TEST".to_string();
117
118 drop(proxy);
119 let result = expect_set_local_name(stream, expected_name).await;
120 assert!(result.is_err());
121 }
122
123 #[fuchsia_async::run_until_stalled(test)]
124 async fn test_satisfied_with_expected_message_after_unexpected_message() {
125 let (proxy, stream) = create_proxy_and_stream::<AccessMarker>();
126 let expected_name = "TEST".to_string();
127
128 let _ = proxy.set_device_class(&DeviceClass { value: 0 });
129 let _ = proxy.set_local_name(&expected_name);
130 let mock_result = expect_set_local_name(stream, expected_name.clone()).await;
131 assert!(mock_result.is_ok());
132 }
133}