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::{Driver as NextDriver, DriverStartArgs};
15use fuchsia_async as fasync;
16use futures::channel::oneshot;
17use std::marker::PhantomData;
18use std::sync::{Arc, mpsc};
19use zx::Status;
20
21pub struct DriverUnderTest<'a, D> {
23 driver_outgoing: Incoming,
24 driver: Option<fdf_env::Driver<u32>>,
25 dispatcher: AsyncDispatcher,
26 registration: DriverRegistration,
27 token: usize,
28 client: NextClient<NextDriver, DriverChannel>,
29 client_exit_rx: Option<mpsc::Receiver<()>>,
30 started: bool,
31 harness: &'a mut TestHarness<D>,
32 node_id: usize,
33 _d: PhantomData<D>,
34}
35
36impl<D> Drop for DriverUnderTest<'_, D> {
37 fn drop(&mut self) {
38 if !self.started {
39 self.client_exit_rx.take().expect("exit rx").recv().unwrap();
40 }
41 assert!(
42 self.client_exit_rx.is_none(),
43 "DriverUnderTest's stop_driver must be called before letting it go out of scope."
44 );
45
46 let (shutdown_tx, shutdown_rx) = mpsc::channel();
47 let destroy_fn = self.registration.v1.destroy.unwrap();
48 let driver_token = self.token;
49 self.driver.take().expect("driver").shutdown(move |driver_ref| {
50 let driver_value = unsafe { Box::from_raw(driver_ref.0 as *mut u32) };
52 assert_eq!(*driver_value, 0x1337);
53
54 unsafe {
57 destroy_fn(driver_token as *mut _);
58 }
59
60 shutdown_tx.send(()).unwrap();
61 });
62
63 shutdown_rx.recv().unwrap();
64 }
65}
66
67impl<'a, D: Driver> DriverUnderTest<'a, D> {
68 pub(crate) async fn new(
69 harness: &'a mut TestHarness<D>,
70 fdf_env_environment: Arc<Environment>,
71 driver_outgoing: Incoming,
72 node_id: usize,
73 ) -> Self {
74 let driver_value_ptr = Box::into_raw(Box::new(0x1337_u32));
76
77 let driver = fdf_env_environment.new_driver(driver_value_ptr);
78 let dispatcher_builder = DispatcherBuilder::new()
79 .name("driver_under_test")
80 .shutdown_observer(move |dispatcher| {
81 assert!(
84 !fdf_env_environment
85 .dispatcher_has_queued_tasks(dispatcher.as_dispatcher_ref())
86 );
87 });
88
89 let registration = make_driver_registration::<D>();
90 let dispatcher =
91 AsyncDispatcher::new(&driver.new_dispatcher(dispatcher_builder).unwrap().release());
92 let (server_chan, client_chan) = fdf::Channel::<[fidl_next::Chunk]>::create();
93 let channel_handle = server_chan.into_driver_handle().into_raw().get();
94 let (client_exit_tx, client_exit_rx) = mpsc::channel();
95 let (token_tx, token_rx) = oneshot::channel();
96 let initialize_fn = registration.v1.initialize.unwrap();
97 dispatcher
98 .post_task_sync(move |status| {
99 assert_eq!(status, Status::OK);
100 token_tx.send(unsafe { initialize_fn(channel_handle) }.addr()).unwrap();
103 })
104 .unwrap();
105 let token = token_rx.await.unwrap();
106
107 let client_end: NextClientEnd<NextDriver, DriverChannel> =
108 NextClientEnd::from_untyped(DriverChannel::new(client_chan));
109 let client_dispatcher = ClientDispatcher::new(client_end);
110 let client = client_dispatcher.client();
111 dispatcher.spawn(async move {
112 client_dispatcher.run_client().await.unwrap_err();
116 client_exit_tx.send(()).unwrap();
117 });
118
119 Self {
120 driver_outgoing,
121 driver: Some(driver),
122 dispatcher,
123 registration,
124 token,
125 client,
126 client_exit_rx: Some(client_exit_rx),
127 started: false,
128 harness,
129 node_id,
130 _d: PhantomData,
131 }
132 }
133
134 pub(crate) async fn start_driver(&mut self, start_args: DriverStartArgs) -> Result<(), Status> {
135 self.client.start(start_args).await.expect("start call success")?;
136 self.started = true;
137 Ok(())
138 }
139
140 pub fn driver_outgoing(&self) -> &Incoming {
143 &self.driver_outgoing
144 }
145
146 pub fn get_driver(&self) -> Option<&'_ D> {
148 unsafe {
149 get_driver_from_token(self.token)
151 }
152 }
153
154 pub fn dispatcher(&self) -> AsyncDispatcher {
156 self.dispatcher.clone()
157 }
158
159 pub fn node(&self) -> NodeHandle {
161 NodeHandle::new(self.harness.node_manager(), self.node_id)
162 }
163
164 pub fn harness(&self) -> &'_ TestHarness<D> {
166 self.harness
167 }
168
169 pub async fn stop_driver(mut self) {
171 if self.started {
173 let _stop_res = self.client.stop().await;
175 let client_exit_rx = self.client_exit_rx.take().expect("exit rx");
176 fasync::unblock(move || client_exit_rx.recv().unwrap()).await;
177 }
178 }
179}