fdf_component/testing/
dut.rs1use crate::macros::{DriverRegistration, make_driver_registration};
6use crate::testing::get_driver_from_token;
7use crate::testing::harness::TestHarness;
8use crate::testing::node::NodeHandle;
9use crate::{Driver, Incoming};
10use fdf::{AsAsyncDispatcherRef, AsyncDispatcher, DispatcherBuilder, OnDispatcher};
11use fdf_env::Environment;
12use fdf_fidl::DriverChannel;
13use fidl_next::{Client as NextClient, ClientDispatcher, ClientEnd as NextClientEnd};
14use fidl_next_fuchsia_driver_framework::{
15 Driver as NextDriver, DriverClientHandler, DriverStartArgs, HealthAlert, HealthCheckError,
16 HealthCheckRequest, HealthCheckResponse, driver,
17};
18use fuchsia_async as fasync;
19use futures::channel::oneshot;
20use std::marker::PhantomData;
21use std::sync::{Arc, mpsc};
22use zx::Status;
23
24pub struct DriverUnderTest<'a, D> {
26 driver_outgoing: Incoming,
27 driver: Option<fdf_env::Driver<u32>>,
28 dispatcher: AsyncDispatcher,
29 registration: DriverRegistration,
30 token: usize,
31 client: NextClient<NextDriver, DriverChannel>,
32 alert_rx: futures::channel::mpsc::Receiver<HealthAlert>,
33 client_exit_rx: Option<mpsc::Receiver<()>>,
34 started: bool,
35 harness: &'a mut TestHarness<D>,
36 node_id: usize,
37 _d: PhantomData<D>,
38}
39
40#[derive(Debug)]
42struct DutClientHandler {
43 alert_tx: futures::channel::mpsc::Sender<HealthAlert>,
44}
45
46impl DriverClientHandler for DutClientHandler {
47 async fn on_health_alert(
48 &mut self,
49 request: fidl_next::Request<driver::OnHealthAlert, DriverChannel>,
50 ) {
51 let _ = self.alert_tx.try_send(request.payload().alert);
53 }
54}
55
56impl<D> Drop for DriverUnderTest<'_, D> {
57 fn drop(&mut self) {
58 if !self.started {
59 self.client_exit_rx.take().expect("exit rx").recv().unwrap();
60 }
61 assert!(
62 self.client_exit_rx.is_none(),
63 "DriverUnderTest's stop_driver must be called before letting it go out of scope."
64 );
65
66 let (shutdown_tx, shutdown_rx) = mpsc::channel();
67 let destroy_fn = self.registration.v1.destroy.unwrap();
68 let driver_token = self.token;
69 self.driver.take().expect("driver").shutdown(move |driver_ref| {
70 let driver_value = unsafe { Box::from_raw(driver_ref.0 as *mut u32) };
72 assert_eq!(*driver_value, 0x1337);
73
74 unsafe {
77 destroy_fn(driver_token as *mut _);
78 }
79
80 shutdown_tx.send(()).unwrap();
81 });
82
83 shutdown_rx.recv().unwrap();
84 }
85}
86
87impl<'a, D: Driver> DriverUnderTest<'a, D> {
88 pub(crate) async fn new(
89 harness: &'a mut TestHarness<D>,
90 fdf_env_environment: Arc<Environment>,
91 driver_outgoing: Incoming,
92 node_id: usize,
93 ) -> Self {
94 let driver_value_ptr = Box::into_raw(Box::new(0x1337_u32));
96
97 let driver = fdf_env_environment.new_driver(driver_value_ptr);
98 let dispatcher_builder = DispatcherBuilder::new()
99 .name("driver_under_test")
100 .shutdown_observer(move |dispatcher| {
101 assert!(
104 !fdf_env_environment
105 .dispatcher_has_queued_tasks(dispatcher.as_dispatcher_ref())
106 );
107 });
108
109 let registration = make_driver_registration::<D>();
110 let dispatcher =
111 AsyncDispatcher::new(&driver.new_dispatcher(dispatcher_builder).unwrap().release());
112 let (server_chan, client_chan) = fdf::Channel::<[fidl_next::Chunk]>::create();
113 let channel_handle = server_chan.into_driver_handle().into_raw().get();
114 let (client_exit_tx, client_exit_rx) = mpsc::channel();
115 let (alert_tx, alert_rx) = futures::channel::mpsc::channel(64);
116 let (token_tx, token_rx) = oneshot::channel();
117 let initialize_fn = registration.v1.initialize.unwrap();
118 dispatcher
119 .post_task_sync(move |status| {
120 assert_eq!(status, Ok(()));
121 token_tx.send(unsafe { initialize_fn(channel_handle) }.addr()).unwrap();
124 })
125 .unwrap();
126 let token = token_rx.await.unwrap();
127
128 let client_end: NextClientEnd<NextDriver, DriverChannel> =
129 NextClientEnd::from_untyped(DriverChannel::new(client_chan));
130 let client_dispatcher = ClientDispatcher::new(client_end);
131 let client = client_dispatcher.client();
132 dispatcher.spawn(async move {
133 client_dispatcher.run(DutClientHandler { alert_tx }).await.unwrap_err();
137 client_exit_tx.send(()).unwrap();
138 });
139
140 Self {
141 driver_outgoing,
142 driver: Some(driver),
143 dispatcher,
144 registration,
145 token,
146 client,
147 alert_rx,
148 client_exit_rx: Some(client_exit_rx),
149 started: false,
150 harness,
151 node_id,
152 _d: PhantomData,
153 }
154 }
155
156 pub(crate) async fn start_driver(&mut self, start_args: DriverStartArgs) -> Result<(), Status> {
157 self.client.start(start_args).await.expect("start call success")?;
158 self.started = true;
159 Ok(())
160 }
161
162 pub fn driver_outgoing(&self) -> &Incoming {
165 &self.driver_outgoing
166 }
167
168 pub fn get_driver(&self) -> Option<&'_ D> {
170 unsafe {
171 get_driver_from_token(self.token)
173 }
174 }
175
176 pub fn dispatcher(&self) -> AsyncDispatcher {
178 self.dispatcher.clone()
179 }
180
181 pub fn node(&self) -> NodeHandle {
183 NodeHandle::new(self.harness.node_manager(), self.node_id)
184 }
185
186 pub fn harness(&self) -> &'_ TestHarness<D> {
188 self.harness
189 }
190
191 pub async fn health_check(
194 &self,
195 request: HealthCheckRequest,
196 ) -> Result<HealthCheckResponse, HealthCheckError> {
197 self.client.health_check(request).await.expect("health_check FIDL call success")
198 }
199
200 pub async fn next_health_alert(&mut self) -> Option<HealthAlert> {
203 futures::StreamExt::next(&mut self.alert_rx).await
204 }
205
206 pub async fn stop_driver(mut self) {
208 if self.started {
210 let _stop_res = self.client.stop().await;
212 let client_exit_rx = self.client_exit_rx.take().expect("exit rx");
213 fasync::unblock(move || client_exit_rx.recv().unwrap()).await;
214 }
215 }
216}