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 let event_stream = self.event_stream.take();
417 drop(event_stream);
418
419 let sync_proxy = wlantap::WlantapPhySynchronousProxy::new(
420 Arc::<wlantap::WlantapPhyProxy>::into_inner(proxy)
423 .expect("Outstanding references to WlantapPhyProxy! Failed to drop TestHelper.")
424 .into_channel()
425 .expect("failed to get fidl::AsyncChannel from proxy")
426 .into_zx_channel(),
427 );
428
429 sync_proxy
447 .shutdown(zx::MonotonicInstant::INFINITE)
448 .expect("Failed to shutdown WlantapPhy gracefully.");
449 }
450}
451
452pub struct RetryWithBackoff {
453 deadline: MonotonicInstant,
454 prev_delay: zx::MonotonicDuration,
455 next_delay: zx::MonotonicDuration,
456 max_delay: zx::MonotonicDuration,
457}
458impl RetryWithBackoff {
459 pub fn new(timeout: zx::MonotonicDuration) -> Self {
460 RetryWithBackoff {
461 deadline: MonotonicInstant::after(timeout),
462 prev_delay: zx::MonotonicDuration::from_millis(0),
463 next_delay: zx::MonotonicDuration::from_millis(1),
464 max_delay: zx::MonotonicDuration::INFINITE,
465 }
466 }
467 pub fn infinite_with_max_interval(max_delay: zx::MonotonicDuration) -> Self {
468 Self {
469 deadline: MonotonicInstant::INFINITE,
470 max_delay,
471 ..Self::new(zx::MonotonicDuration::from_nanos(0))
472 }
473 }
474
475 async fn sleep_unless_after_deadline_(
481 &mut self,
482 verbose: bool,
483 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
484 {
489 if MonotonicInstant::after(zx::MonotonicDuration::from_millis(0)) > self.deadline {
490 if verbose {
491 info!("Skipping sleep. Deadline exceeded.");
492 }
493 return Err(self.deadline - MonotonicInstant::now());
494 }
495
496 let sleep_deadline =
497 std::cmp::min(MonotonicInstant::after(self.next_delay), self.deadline);
498 if verbose {
499 let micros = sleep_deadline.into_nanos() / 1_000;
500 info!("Sleeping until {}.{} 😴", micros / 1_000_000, micros % 1_000_000);
501 }
502
503 Timer::new(sleep_deadline).await;
504 }
505
506 if self.next_delay < self.max_delay {
509 let next_delay = std::cmp::min(
510 self.max_delay,
511 zx::MonotonicDuration::from_nanos(
512 self.prev_delay.into_nanos().saturating_add(self.next_delay.into_nanos()),
513 ),
514 );
515 self.prev_delay = self.next_delay;
516 self.next_delay = next_delay;
517 }
518
519 Ok(self.deadline - MonotonicInstant::now())
520 }
521
522 pub async fn sleep_unless_after_deadline(
523 &mut self,
524 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
525 self.sleep_unless_after_deadline_(false).await
526 }
527
528 pub async fn sleep_unless_after_deadline_verbose(
529 &mut self,
530 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
531 self.sleep_unless_after_deadline_(true).await
532 }
533}
534
535pub fn strip_timestamp_nanos_from_scan_results(
539 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
540) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
541 for scan_result in &mut scan_result_list {
542 scan_result
543 .entries
544 .as_mut()
545 .unwrap()
546 .sort_by(|a, b| a.bssid.as_ref().unwrap().cmp(&b.bssid.as_ref().unwrap()));
547 for entry in scan_result.entries.as_mut().unwrap() {
548 entry.timestamp_nanos.take();
550 }
551 }
552 scan_result_list
553}
554
555pub fn sort_policy_scan_result_list(
560 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
561) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
562 scan_result_list
563 .sort_by(|a, b| a.id.as_ref().expect("empty id").cmp(&b.id.as_ref().expect("empty id")));
564 scan_result_list
565}
566
567pub async fn policy_scan_for_networks<'a>(
574 client_controller: fidl_policy::ClientControllerProxy,
575) -> Vec<fidl_policy::ScanResult> {
576 let (scan_proxy, server_end) = create_proxy();
578 client_controller.scan_for_networks(server_end).expect("requesting scan");
579 let mut scan_result_list = Vec::new();
580 loop {
581 let proxy_result = scan_proxy.get_next().await.expect("getting scan results");
582 let next_scan_result_list = proxy_result.expect("scanning failed");
583 if next_scan_result_list.is_empty() {
584 break;
585 }
586 scan_result_list.extend(next_scan_result_list);
587 }
588 sort_policy_scan_result_list(strip_timestamp_nanos_from_scan_results(scan_result_list))
589}
590
591pub async fn timeout_after<R, F: Future<Output = R> + Unpin>(
594 timeout: zx::MonotonicDuration,
595 main_future: &mut F,
596) -> Result<R, ()> {
597 async { Ok(main_future.await) }.on_timeout(timeout.after_now(), || Err(())).await
598}