fidl_test_util/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![deny(missing_docs)]
6
7//! Utilities for tests that interact with fidl.
8
9use fidl::endpoints::{create_proxy_and_stream, Proxy, Request, RequestStream};
10use fuchsia_async::Task;
11use futures::{FutureExt, TryFutureExt, TryStreamExt};
12use log::error;
13
14/// Utility that spawns a new task to handle requests of a particular type, requiring a
15/// singlethreaded executor. The requests are handled one at a time.
16pub fn spawn_local_stream_handler<P, F, Fut>(f: F) -> P
17where
18    P: Proxy,
19    F: FnMut(Request<P::Protocol>) -> Fut + 'static,
20    Fut: Future<Output = ()> + 'static,
21{
22    let (proxy, stream) = create_proxy_and_stream::<P::Protocol>();
23    Task::local(for_each_or_log(stream, f)).detach();
24    proxy
25}
26
27/// Utility that spawns a new task to handle requests of a particular type. The request handler
28/// must be threadsafe. The requests are handled one at a time.
29pub fn spawn_stream_handler<P, F, Fut>(f: F) -> P
30where
31    P: Proxy,
32    F: FnMut(Request<P::Protocol>) -> Fut + 'static + Send,
33    Fut: Future<Output = ()> + 'static + Send,
34{
35    let (proxy, stream) = create_proxy_and_stream::<P::Protocol>();
36    Task::spawn(for_each_or_log(stream, f)).detach();
37    proxy
38}
39
40fn for_each_or_log<St, F, Fut>(stream: St, mut f: F) -> impl Future<Output = ()>
41where
42    St: RequestStream,
43    F: FnMut(St::Ok) -> Fut,
44    Fut: Future<Output = ()>,
45{
46    stream
47        .try_for_each(move |r| f(r).map(Ok))
48        .unwrap_or_else(|e| error!("FIDL stream handler failed: {}", e))
49}
50
51#[cfg(test)]
52mod test {
53    use super::*;
54    use fidl_test_placeholders::{EchoProxy, EchoRequest};
55
56    #[fuchsia::test]
57    async fn test_spawn_local_stream_handler() {
58        let f = |req| {
59            let EchoRequest::EchoString { value, responder } = req;
60            async move {
61                responder.send(Some(&value.unwrap())).expect("responder failed");
62            }
63        };
64        let proxy: EchoProxy = spawn_local_stream_handler(f);
65        let res = proxy.echo_string(Some("hello world")).await.expect("echo failed");
66        assert_eq!(res, Some("hello world".to_string()));
67        let res = proxy.echo_string(Some("goodbye world")).await.expect("echo failed");
68        assert_eq!(res, Some("goodbye world".to_string()));
69    }
70
71    #[fuchsia::test(threads = 2)]
72    async fn test_spawn_stream_handler() {
73        let f = |req| {
74            let EchoRequest::EchoString { value, responder } = req;
75            async move {
76                responder.send(Some(&value.unwrap())).expect("responder failed");
77            }
78        };
79        let proxy: EchoProxy = spawn_stream_handler(f);
80        let res = proxy.echo_string(Some("hello world")).await.expect("echo failed");
81        assert_eq!(res, Some("hello world".to_string()));
82        let res = proxy.echo_string(Some("goodbye world")).await.expect("echo failed");
83        assert_eq!(res, Some("goodbye world".to_string()));
84    }
85}