1use crate::event::{self, Handler};
6use crate::netdevice_helper;
7use crate::wlancfg_helper::{NetworkConfigBuilder, start_ap_and_wait_for_confirmation};
8use fidl::endpoints::{Proxy, create_endpoints, create_proxy};
9use fidl_fuchsia_driver_test as fidl_driver_test;
10use fidl_fuchsia_wlan_policy as fidl_policy;
11use fidl_fuchsia_wlan_tap as wlantap;
12use fidl_test_wlan_realm as fidl_realm;
13use fuchsia_async::{DurationExt, MonotonicInstant, TimeoutExt, Timer};
14use fuchsia_component::client::{connect_to_protocol, connect_to_protocol_at};
15use futures::channel::oneshot;
16use futures::{FutureExt, StreamExt};
17use ieee80211::{MacAddr, MacAddrBytes};
18use log::{debug, info, warn};
19use realm_client::{InstalledNamespace, extend_namespace};
20use std::fmt::Display;
21use std::future::Future;
22use std::pin::Pin;
23use std::sync::Arc;
24use std::task::{Context, Poll};
25use test_realm_helpers::tracing::Tracing;
26use wlan_common::test_utils::ExpectWithin;
27use wlantap_client::Wlantap;
28
29const TIMEOUT_WARN_THRESHOLD: f64 = 0.8;
31
32pub struct TestRealmContext {
61 test_ns: Arc<InstalledNamespace>,
65
66 devfs: fidl_fuchsia_io::DirectoryProxy,
68 _tracing: Tracing,
69}
70
71impl TestRealmContext {
72 pub async fn new(config: fidl_realm::WlanConfig) -> Arc<Self> {
77 let realm_factory = connect_to_protocol::<fidl_realm::RealmFactoryMarker>()
78 .expect("Could not connect to realm factory protocol");
79
80 let (dict_1, dict_2) = zx::EventPair::create();
81 let (devfs_proxy, devfs_server) = create_proxy();
82
83 let trace_manager_hermeticity = config.trace_manager_hermeticity.clone();
88 let options = fidl_realm::RealmOptions {
89 devfs_server_end: Some(devfs_server),
90 wlan_config: Some(config),
91 ..Default::default()
92 };
93 let _ = realm_factory.create_realm3(options, dict_1).await.expect("Could not create realm");
94 let test_ns =
95 Arc::new(extend_namespace(realm_factory, dict_2).await.expect("failed to extend ns"));
96
97 let driver_test_realm_proxy =
99 connect_to_protocol_at::<fidl_driver_test::RealmMarker>(&*test_ns)
100 .expect("Failed to connect to driver test realm");
101
102 let (pkg_client, pkg_server) = create_endpoints();
103 fuchsia_fs::directory::open_channel_in_namespace(
104 "/pkg",
105 fidl_fuchsia_io::PERM_READABLE | fidl_fuchsia_io::PERM_EXECUTABLE,
106 pkg_server,
107 )
108 .expect("Could not open /pkg");
109
110 let test_component = fidl_fuchsia_component_resolution::Component {
111 package: Some(fidl_fuchsia_component_resolution::Package {
112 directory: Some(pkg_client),
113 ..Default::default()
114 }),
115 ..Default::default()
116 };
117
118 driver_test_realm_proxy
119 .start(fidl_driver_test::RealmArgs {
120 test_component: Some(test_component),
121 ..Default::default()
122 })
123 .await
124 .expect("FIDL error when starting driver test realm")
125 .expect("Driver test realm server returned an error");
126
127 let _tracing = match trace_manager_hermeticity {
128 Some(fidl_realm::TraceManagerHermeticity::Hermetic) | None => {
129 Tracing::start_at(Arc::clone(&test_ns)).await.unwrap()
130 }
131 Some(fidl_realm::TraceManagerHermeticity::NonHermetic) => {
132 Tracing::start_non_hermetic(test_ns.prefix().strip_prefix("/").unwrap())
133 .await
134 .unwrap()
135 }
136 };
137
138 Arc::new(Self { test_ns, devfs: devfs_proxy, _tracing })
139 }
140
141 pub fn test_ns_prefix(&self) -> &str {
142 self.test_ns.prefix()
143 }
144
145 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
146 &self.devfs
147 }
148}
149
150type EventStream = wlantap::WlantapPhyEventStream;
151pub struct TestHelper {
152 ctx: Arc<TestRealmContext>,
153 netdevice_task_handles: Vec<fuchsia_async::Task<()>>,
154 _wlantap: Wlantap,
155 proxy: Arc<wlantap::WlantapPhyProxy>,
156 event_stream: Option<EventStream>,
157}
158struct TestHelperFuture<H, F>
159where
160 H: Handler<(), wlantap::WlantapPhyEvent>,
161 F: Future + Unpin,
162{
163 event_stream: Option<EventStream>,
164 handler: H,
165 future: F,
166}
167impl<H, F> Unpin for TestHelperFuture<H, F>
168where
169 H: Handler<(), wlantap::WlantapPhyEvent>,
170 F: Future + Unpin,
171{
172}
173impl<H, F> Future for TestHelperFuture<H, F>
174where
175 H: Handler<(), wlantap::WlantapPhyEvent>,
176 F: Future + Unpin,
177{
178 type Output = (F::Output, EventStream);
179 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
181 let helper = &mut *self;
182 let stream = helper.event_stream.as_mut().unwrap();
183 loop {
184 if let Poll::Ready(x) = helper.future.poll_unpin(cx) {
192 return Poll::Ready((x, helper.event_stream.take().unwrap()));
193 }
194
195 match stream.poll_next_unpin(cx) {
196 Poll::Ready(optional_result) => {
197 let event = optional_result
198 .expect("Unexpected end of the WlantapPhy event stream")
199 .expect("WlantapPhy event stream returned an error");
200 helper.handler.call(&mut (), &event);
201 }
202 Poll::Pending => {
203 debug!(
204 "Main future poll response is pending and there are no events to process."
205 );
206 return Poll::Pending;
207 }
208 }
209 }
210 }
211}
212impl TestHelper {
213 pub async fn begin_test(
217 phy_config: wlantap::WlantapPhyConfig,
218 realm_config: fidl_realm::WlanConfig,
219 ) -> Self {
220 let ctx = TestRealmContext::new(realm_config).await;
221 Self::begin_test_with_context(ctx, phy_config).await
222 }
223
224 pub async fn begin_test_with_context(
243 ctx: Arc<TestRealmContext>,
244 config: wlantap::WlantapPhyConfig,
245 ) -> Self {
246 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
247 helper.wait_for_wlan_softmac_start().await;
248 helper
249 }
250
251 pub async fn begin_ap_test(
255 phy_config: wlantap::WlantapPhyConfig,
256 network_config: NetworkConfigBuilder,
257 realm_config: fidl_realm::WlanConfig,
258 ) -> Self {
259 let ctx = TestRealmContext::new(realm_config).await;
260 Self::begin_ap_test_with_context(ctx, phy_config, network_config).await
261 }
262
263 pub async fn begin_ap_test_with_context(
285 ctx: Arc<TestRealmContext>,
286 config: wlantap::WlantapPhyConfig,
287 network_config: NetworkConfigBuilder,
288 ) -> Self {
289 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
290 start_ap_and_wait_for_confirmation(helper.ctx.test_ns_prefix(), network_config).await;
291 helper.wait_for_wlan_softmac_start().await;
292 helper
293 }
294
295 async fn create_phy_and_helper(
296 config: wlantap::WlantapPhyConfig,
297 ctx: Arc<TestRealmContext>,
298 ) -> Self {
299 let wlantap =
301 Wlantap::open_from_devfs(&ctx.devfs).await.expect("Failed to open wlantapctl");
302 let proxy = wlantap.create_phy(config).await.expect("Failed to create wlantap PHY");
303 let event_stream = Some(proxy.take_event_stream());
304 TestHelper {
305 ctx,
306 netdevice_task_handles: vec![],
307 _wlantap: wlantap,
308 proxy: Arc::new(proxy),
309 event_stream,
310 }
311 }
312
313 async fn wait_for_wlan_softmac_start(&mut self) {
314 let (sender, receiver) = oneshot::channel::<()>();
315 self.run_until_complete_or_timeout(
316 zx::MonotonicDuration::from_seconds(12),
317 "receive a WlanSoftmacStart event",
318 event::on_start_mac(event::once(|_, _| sender.send(()))),
319 receiver,
320 )
321 .await
322 .unwrap_or_else(|oneshot::Canceled| panic!());
323 }
324
325 pub fn proxy(&self) -> Arc<wlantap::WlantapPhyProxy> {
330 Arc::clone(&self.proxy)
331 }
332
333 pub fn test_ns_prefix(&self) -> &str {
334 self.ctx.test_ns_prefix()
335 }
336
337 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
338 self.ctx.devfs()
339 }
340
341 pub async fn start_netdevice_session(
342 &mut self,
343 mac: MacAddr,
344 ) -> (netdevice_client::Session, netdevice_client::Port) {
345 let mac = fidl_fuchsia_net::MacAddress { octets: mac.to_array() };
346 let (client, port) = netdevice_helper::create_client(self.devfs(), mac)
347 .await
348 .expect("failed to create netdevice client");
349 let (session, task_handle) = netdevice_helper::start_session(client, port).await;
350 self.netdevice_task_handles.push(task_handle);
351 (session, port)
352 }
353
354 pub async fn run_until_complete_or_timeout<H, F>(
360 &mut self,
361 timeout: zx::MonotonicDuration,
362 context: impl Display,
363 handler: H,
364 future: F,
365 ) -> F::Output
366 where
367 H: Handler<(), wlantap::WlantapPhyEvent>,
368 F: Future + Unpin,
369 {
370 info!("Running main future until completion or timeout with event handler: {}", context);
371 let start_time = zx::MonotonicInstant::get();
372 let (item, stream) = TestHelperFuture {
373 event_stream: Some(self.event_stream.take().unwrap()),
374 handler,
375 future,
376 }
377 .expect_within(timeout, format!("Main future timed out: {}", context))
378 .await;
379 let end_time = zx::MonotonicInstant::get();
380 let elapsed = end_time - start_time;
381 let elapsed_seconds = elapsed.into_seconds_f64();
382 let elapsed_ratio = elapsed_seconds / timeout.into_seconds_f64();
383 if elapsed_ratio < TIMEOUT_WARN_THRESHOLD {
384 info!("Main future completed in {:.2} seconds: {}", elapsed_seconds, context);
385 } else {
386 warn!(
387 "Main future completed in {:.2} seconds ({:.1}% of timeout): {}",
388 elapsed_seconds,
389 elapsed_ratio * 100.,
390 context,
391 );
392 }
393 self.event_stream = Some(stream);
394 item
395 }
396}
397impl Drop for TestHelper {
398 fn drop(&mut self) {
399 while let Some(task_handle) = self.netdevice_task_handles.pop() {
403 drop(task_handle);
404 }
405
406 let (placeholder_proxy, _server_end) =
409 fidl::endpoints::create_proxy::<wlantap::WlantapPhyMarker>();
410 let mut proxy = Arc::new(placeholder_proxy);
411 std::mem::swap(&mut self.proxy, &mut proxy);
412
413 if let Some(mut stream) = self.event_stream.take() {
417 let waker = futures::task::noop_waker();
420 let mut cx = futures::task::Context::from_waker(&waker);
421 while let std::task::Poll::Ready(Some(result)) = stream.poll_next_unpin(&mut cx) {
422 if let Ok(event) = result {
423 warn!("Draining unhandled event during teardown: {:?}", event);
424 }
425 }
426 }
427
428 let sync_proxy = wlantap::WlantapPhySynchronousProxy::new(
429 Arc::<wlantap::WlantapPhyProxy>::into_inner(proxy)
432 .expect("Outstanding references to WlantapPhyProxy! Failed to drop TestHelper.")
433 .into_channel()
434 .expect("failed to get fidl::AsyncChannel from proxy")
435 .into_zx_channel(),
436 );
437
438 sync_proxy
456 .shutdown(zx::MonotonicInstant::INFINITE)
457 .expect("Failed to shutdown WlantapPhy gracefully.");
458 }
459}
460
461pub struct RetryWithBackoff {
462 deadline: MonotonicInstant,
463 prev_delay: zx::MonotonicDuration,
464 next_delay: zx::MonotonicDuration,
465 max_delay: zx::MonotonicDuration,
466}
467impl RetryWithBackoff {
468 pub fn new(timeout: zx::MonotonicDuration) -> Self {
469 RetryWithBackoff {
470 deadline: MonotonicInstant::after(timeout),
471 prev_delay: zx::MonotonicDuration::from_millis(0),
472 next_delay: zx::MonotonicDuration::from_millis(1),
473 max_delay: zx::MonotonicDuration::INFINITE,
474 }
475 }
476 pub fn infinite_with_max_interval(max_delay: zx::MonotonicDuration) -> Self {
477 Self {
478 deadline: MonotonicInstant::INFINITE,
479 max_delay,
480 ..Self::new(zx::MonotonicDuration::from_nanos(0))
481 }
482 }
483
484 async fn sleep_unless_after_deadline_(
490 &mut self,
491 verbose: bool,
492 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
493 {
498 if MonotonicInstant::after(zx::MonotonicDuration::from_millis(0)) > self.deadline {
499 if verbose {
500 info!("Skipping sleep. Deadline exceeded.");
501 }
502 return Err(self.deadline - MonotonicInstant::now());
503 }
504
505 let sleep_deadline =
506 std::cmp::min(MonotonicInstant::after(self.next_delay), self.deadline);
507 if verbose {
508 let micros = sleep_deadline.into_nanos() / 1_000;
509 info!("Sleeping until {}.{} 😴", micros / 1_000_000, micros % 1_000_000);
510 }
511
512 Timer::new(sleep_deadline).await;
513 }
514
515 if self.next_delay < self.max_delay {
518 let next_delay = std::cmp::min(
519 self.max_delay,
520 zx::MonotonicDuration::from_nanos(
521 self.prev_delay.into_nanos().saturating_add(self.next_delay.into_nanos()),
522 ),
523 );
524 self.prev_delay = self.next_delay;
525 self.next_delay = next_delay;
526 }
527
528 Ok(self.deadline - MonotonicInstant::now())
529 }
530
531 pub async fn sleep_unless_after_deadline(
532 &mut self,
533 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
534 self.sleep_unless_after_deadline_(false).await
535 }
536
537 pub async fn sleep_unless_after_deadline_verbose(
538 &mut self,
539 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
540 self.sleep_unless_after_deadline_(true).await
541 }
542}
543
544pub fn strip_timestamp_nanos_from_scan_results(
548 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
549) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
550 for scan_result in &mut scan_result_list {
551 scan_result
552 .entries
553 .as_mut()
554 .unwrap()
555 .sort_by(|a, b| a.bssid.as_ref().unwrap().cmp(&b.bssid.as_ref().unwrap()));
556 for entry in scan_result.entries.as_mut().unwrap() {
557 entry.timestamp_nanos.take();
559 }
560 }
561 scan_result_list
562}
563
564pub fn sort_policy_scan_result_list(
569 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
570) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
571 scan_result_list
572 .sort_by(|a, b| a.id.as_ref().expect("empty id").cmp(&b.id.as_ref().expect("empty id")));
573 scan_result_list
574}
575
576pub async fn policy_scan_for_networks<'a>(
583 client_controller: fidl_policy::ClientControllerProxy,
584) -> Vec<fidl_policy::ScanResult> {
585 let (scan_proxy, server_end) = create_proxy();
587 client_controller.scan_for_networks(server_end).expect("requesting scan");
588 let mut scan_result_list = Vec::new();
589 loop {
590 let proxy_result = scan_proxy.get_next().await.expect("getting scan results");
591 let next_scan_result_list = proxy_result.expect("scanning failed");
592 if next_scan_result_list.is_empty() {
593 break;
594 }
595 scan_result_list.extend(next_scan_result_list);
596 }
597 sort_policy_scan_result_list(strip_timestamp_nanos_from_scan_results(scan_result_list))
598}
599
600pub async fn timeout_after<R, F: Future<Output = R> + Unpin>(
603 timeout: zx::MonotonicDuration,
604 main_future: &mut F,
605) -> Result<R, ()> {
606 async { Ok(main_future.await) }.on_timeout(timeout.after_now(), || Err(())).await
607}