1use crate::directory::entry::DirectoryEntry;
8use crate::directory::entry_container::Directory;
9use crate::directory::helper::DirectlyMutable;
10use crate::directory::immutable::Simple;
11use crate::execution_scope::ExecutionScope;
12use crate::path::Path;
13
14use fidl::endpoints::{create_proxy, ProtocolMarker};
15use fidl_fuchsia_io as fio;
16use fuchsia_async::TestExecutor;
17use std::future::Future;
18use std::pin::Pin;
19use std::sync::Arc;
20
21#[cfg(target_os = "fuchsia")]
22use std::task::Poll;
23
24pub fn run_server_client<Marker, GetClient, GetClientRes>(
31 flags: fio::OpenFlags,
32 server: Arc<dyn DirectoryEntry>,
33 get_client: GetClient,
34) where
35 Marker: ProtocolMarker,
36 GetClient: FnOnce(Marker::Proxy) -> GetClientRes,
37 GetClientRes: Future<Output = ()>,
38{
39 test_server_client::<Marker, _, _>(flags, server, get_client).run();
40}
41
42pub fn run_client<GetClient, GetClientRes>(exec: TestExecutor, get_client: GetClient)
55where
56 GetClient: FnOnce() -> GetClientRes,
57 GetClientRes: Future<Output = ()>,
58{
59 test_client(get_client).exec(exec).run();
60}
61
62pub struct TestController<'test_refs> {
73 exec: TestExecutor,
74 client: Pin<Box<dyn Future<Output = ()> + 'test_refs>>,
75}
76
77impl<'test_refs> TestController<'test_refs> {
78 fn new(exec: TestExecutor, client: Pin<Box<dyn Future<Output = ()> + 'test_refs>>) -> Self {
79 Self { exec, client }
80 }
81
82 #[cfg(target_os = "fuchsia")]
85 pub fn run_until_stalled(&mut self) {
86 let res = self.exec.run_until_stalled(&mut self.client);
95 assert_eq!(res, Poll::Pending, "Test was not expected to complete");
96 }
97
98 pub fn run_until_complete(self) {
102 }
104}
105
106#[cfg(target_os = "fuchsia")]
107impl<'test_refs> Drop for TestController<'test_refs> {
108 fn drop(&mut self) {
109 let res = self.exec.run_until_stalled(&mut self.client);
111 assert_eq!(res, Poll::Ready(()), "Test did not complete");
112 }
113}
114
115#[cfg(not(target_os = "fuchsia"))]
116impl<'test_refs> Drop for TestController<'test_refs> {
117 fn drop(&mut self) {
118 self.exec.run_singlethreaded(&mut self.client);
119 }
120}
121
122pub fn test_server_client<'test_refs, Marker, GetClient, GetClientRes>(
127 flags: fio::OpenFlags,
128 server: Arc<dyn DirectoryEntry>,
129 get_client: GetClient,
130) -> AsyncServerClientTestParams<'test_refs, Marker>
131where
132 Marker: ProtocolMarker,
133 GetClient: FnOnce(Marker::Proxy) -> GetClientRes + 'test_refs,
134 GetClientRes: Future<Output = ()> + 'test_refs,
135{
136 AsyncServerClientTestParams {
137 exec: None,
138 flags,
139 server,
140 get_client: Box::new(move |proxy| Box::pin(get_client(proxy))),
141 coordinator: None,
142 }
143}
144
145pub fn test_client<'test_refs, GetClient, GetClientRes>(
149 get_client: GetClient,
150) -> AsyncClientTestParams<'test_refs>
151where
152 GetClient: FnOnce() -> GetClientRes + 'test_refs,
153 GetClientRes: Future<Output = ()> + 'test_refs,
154{
155 AsyncClientTestParams {
156 exec: None,
157 get_client: Box::new(move || Box::pin(get_client())),
158 coordinator: None,
159 }
160}
161
162#[must_use = "Need to call `run` to actually run the test"]
165pub struct AsyncServerClientTestParams<'test_refs, Marker>
166where
167 Marker: ProtocolMarker,
168{
169 exec: Option<TestExecutor>,
170 flags: fio::OpenFlags,
171 server: Arc<dyn DirectoryEntry>,
172 get_client: Box<
173 dyn FnOnce(Marker::Proxy) -> Pin<Box<dyn Future<Output = ()> + 'test_refs>> + 'test_refs,
174 >,
175 coordinator: Option<Box<dyn FnOnce(TestController<'_>) + 'test_refs>>,
176}
177
178#[must_use = "Need to call `run` to actually run the test"]
180pub struct AsyncClientTestParams<'test_refs> {
181 exec: Option<TestExecutor>,
182 get_client: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + 'test_refs>> + 'test_refs>,
183 coordinator: Option<Box<dyn FnOnce(TestController<'_>) + 'test_refs>>,
184}
185
186macro_rules! field_setter {
187 ($name:ident, $type:ty) => {
188 pub fn $name(mut self, $name: $type) -> Self {
189 assert!(self.$name.is_none(), concat!("`", stringify!($name), "` is already set"));
190 self.$name = Some($name);
191 self
192 }
193 };
194}
195
196impl<'test_refs, Marker> AsyncServerClientTestParams<'test_refs, Marker>
197where
198 Marker: ProtocolMarker,
199{
200 field_setter!(exec, TestExecutor);
201
202 pub fn coordinator(
203 mut self,
204 get_coordinator: impl FnOnce(TestController<'_>) + 'test_refs,
205 ) -> Self {
206 assert!(self.coordinator.is_none(), "`coordinator` is already set");
207 self.coordinator = Some(Box::new(get_coordinator));
208 self
209 }
210
211 pub fn run(self) {
214 let exec = self.exec.unwrap_or_else(|| TestExecutor::new());
215
216 let (client_proxy, server_end) = create_proxy::<Marker>();
217
218 let root = Simple::new();
219 root.add_entry("server", self.server).unwrap();
220
221 root.deprecated_open(
222 ExecutionScope::new(),
223 self.flags,
224 Path::validate_and_split("server").unwrap(),
225 server_end.into_channel().into(),
226 );
227
228 let client = (self.get_client)(client_proxy);
229
230 let coordinator = self.coordinator.unwrap_or_else(|| Box::new(|_controller| ()));
231
232 let controller = TestController::new(exec, client);
233 coordinator(controller);
234 }
235}
236
237impl<'test_refs> AsyncClientTestParams<'test_refs> {
238 field_setter!(exec, TestExecutor);
239
240 pub fn coordinator(
241 mut self,
242 get_coordinator: impl FnOnce(TestController<'_>) + 'test_refs,
243 ) -> Self {
244 assert!(self.coordinator.is_none(), "`coordinator` is already set");
245 self.coordinator = Some(Box::new(get_coordinator));
246 self
247 }
248
249 pub fn run(self) {
252 let exec = self.exec.unwrap_or_else(|| TestExecutor::new());
253
254 let client = (self.get_client)();
255
256 let coordinator = self.coordinator.unwrap_or_else(|| Box::new(|_controller| ()));
257
258 let controller = TestController::new(exec, client);
259 coordinator(controller);
260 }
261}