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 fuchsia_async::{DurationExt, MonotonicInstant, TimeoutExt, Timer};
10use fuchsia_component::client::{connect_to_protocol, connect_to_protocol_at};
11use futures::channel::oneshot;
12use futures::{FutureExt, StreamExt};
13use ieee80211::{MacAddr, MacAddrBytes};
14use log::{debug, info, warn};
15use realm_client::{InstalledNamespace, extend_namespace};
16use std::fmt::Display;
17use std::future::Future;
18use std::pin::Pin;
19use std::sync::Arc;
20use std::task::{Context, Poll};
21use test_realm_helpers::tracing::Tracing;
22use wlan_common::test_utils::ExpectWithin;
23use wlantap_client::Wlantap;
24use {
25 fidl_fuchsia_driver_test as fidl_driver_test, fidl_fuchsia_wlan_policy as fidl_policy,
26 fidl_fuchsia_wlan_tap as wlantap, fidl_test_wlan_realm as fidl_realm,
27};
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> {
182 let helper = &mut *self;
183 let stream = helper.event_stream.as_mut().unwrap();
184 while let Poll::Ready(optional_result) = stream.poll_next_unpin(cx) {
185 let event = optional_result
186 .expect("Unexpected end of the WlantapPhy event stream")
187 .expect("WlantapPhy event stream returned an error");
188 helper.handler.call(&mut (), &event);
189 }
190 match helper.future.poll_unpin(cx) {
191 Poll::Pending => {
192 debug!("Main future poll response is pending. Waiting for completion.");
193 Poll::Pending
194 }
195 Poll::Ready(x) => Poll::Ready((x, helper.event_stream.take().unwrap())),
196 }
197 }
198}
199impl TestHelper {
200 pub async fn begin_test(
204 phy_config: wlantap::WlantapPhyConfig,
205 realm_config: fidl_realm::WlanConfig,
206 ) -> Self {
207 let ctx = TestRealmContext::new(realm_config).await;
208 Self::begin_test_with_context(ctx, phy_config).await
209 }
210
211 pub async fn begin_test_with_context(
230 ctx: Arc<TestRealmContext>,
231 config: wlantap::WlantapPhyConfig,
232 ) -> Self {
233 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
234 helper.wait_for_wlan_softmac_start().await;
235 helper
236 }
237
238 pub async fn begin_ap_test(
242 phy_config: wlantap::WlantapPhyConfig,
243 network_config: NetworkConfigBuilder,
244 realm_config: fidl_realm::WlanConfig,
245 ) -> Self {
246 let ctx = TestRealmContext::new(realm_config).await;
247 Self::begin_ap_test_with_context(ctx, phy_config, network_config).await
248 }
249
250 pub async fn begin_ap_test_with_context(
272 ctx: Arc<TestRealmContext>,
273 config: wlantap::WlantapPhyConfig,
274 network_config: NetworkConfigBuilder,
275 ) -> Self {
276 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
277 start_ap_and_wait_for_confirmation(helper.ctx.test_ns_prefix(), network_config).await;
278 helper.wait_for_wlan_softmac_start().await;
279 helper
280 }
281
282 async fn create_phy_and_helper(
283 config: wlantap::WlantapPhyConfig,
284 ctx: Arc<TestRealmContext>,
285 ) -> Self {
286 let wlantap =
288 Wlantap::open_from_devfs(&ctx.devfs).await.expect("Failed to open wlantapctl");
289 let proxy = wlantap.create_phy(config).await.expect("Failed to create wlantap PHY");
290 let event_stream = Some(proxy.take_event_stream());
291 TestHelper {
292 ctx,
293 netdevice_task_handles: vec![],
294 _wlantap: wlantap,
295 proxy: Arc::new(proxy),
296 event_stream,
297 }
298 }
299
300 async fn wait_for_wlan_softmac_start(&mut self) {
301 let (sender, receiver) = oneshot::channel::<()>();
302 self.run_until_complete_or_timeout(
303 zx::MonotonicDuration::from_seconds(12),
304 "receive a WlanSoftmacStart event",
305 event::on_start_mac(event::once(|_, _| sender.send(()))),
306 receiver,
307 )
308 .await
309 .unwrap_or_else(|oneshot::Canceled| panic!());
310 }
311
312 pub fn proxy(&self) -> Arc<wlantap::WlantapPhyProxy> {
317 Arc::clone(&self.proxy)
318 }
319
320 pub fn test_ns_prefix(&self) -> &str {
321 self.ctx.test_ns_prefix()
322 }
323
324 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
325 self.ctx.devfs()
326 }
327
328 pub async fn start_netdevice_session(
329 &mut self,
330 mac: MacAddr,
331 ) -> (netdevice_client::Session, netdevice_client::Port) {
332 let mac = fidl_fuchsia_net::MacAddress { octets: mac.to_array() };
333 let (client, port) = netdevice_helper::create_client(self.devfs(), mac)
334 .await
335 .expect("failed to create netdevice client");
336 let (session, task_handle) = netdevice_helper::start_session(client, port).await;
337 self.netdevice_task_handles.push(task_handle);
338 (session, port)
339 }
340
341 pub async fn run_until_complete_or_timeout<H, F>(
347 &mut self,
348 timeout: zx::MonotonicDuration,
349 context: impl Display,
350 handler: H,
351 future: F,
352 ) -> F::Output
353 where
354 H: Handler<(), wlantap::WlantapPhyEvent>,
355 F: Future + Unpin,
356 {
357 info!("Running main future until completion or timeout with event handler: {}", context);
358 let start_time = zx::MonotonicInstant::get();
359 let (item, stream) = TestHelperFuture {
360 event_stream: Some(self.event_stream.take().unwrap()),
361 handler,
362 future,
363 }
364 .expect_within(timeout, format!("Main future timed out: {}", context))
365 .await;
366 let end_time = zx::MonotonicInstant::get();
367 let elapsed = end_time - start_time;
368 let elapsed_seconds = elapsed.into_seconds_f64();
369 let elapsed_ratio = elapsed_seconds / timeout.into_seconds_f64();
370 if elapsed_ratio < TIMEOUT_WARN_THRESHOLD {
371 info!("Main future completed in {:.2} seconds: {}", elapsed_seconds, context);
372 } else {
373 warn!(
374 "Main future completed in {:.2} seconds ({:.1}% of timeout): {}",
375 elapsed_seconds,
376 elapsed_ratio * 100.,
377 context,
378 );
379 }
380 self.event_stream = Some(stream);
381 item
382 }
383}
384impl Drop for TestHelper {
385 fn drop(&mut self) {
386 while let Some(task_handle) = self.netdevice_task_handles.pop() {
390 drop(task_handle);
391 }
392
393 let (placeholder_proxy, _server_end) =
396 fidl::endpoints::create_proxy::<wlantap::WlantapPhyMarker>();
397 let mut proxy = Arc::new(placeholder_proxy);
398 std::mem::swap(&mut self.proxy, &mut proxy);
399
400 let event_stream = self.event_stream.take();
404 drop(event_stream);
405
406 let sync_proxy = wlantap::WlantapPhySynchronousProxy::new(
407 Arc::<wlantap::WlantapPhyProxy>::into_inner(proxy)
410 .expect("Outstanding references to WlantapPhyProxy! Failed to drop TestHelper.")
411 .into_channel()
412 .expect("failed to get fidl::AsyncChannel from proxy")
413 .into_zx_channel(),
414 );
415
416 sync_proxy
434 .shutdown(zx::MonotonicInstant::INFINITE)
435 .expect("Failed to shutdown WlantapPhy gracefully.");
436 }
437}
438
439pub struct RetryWithBackoff {
440 deadline: MonotonicInstant,
441 prev_delay: zx::MonotonicDuration,
442 next_delay: zx::MonotonicDuration,
443 max_delay: zx::MonotonicDuration,
444}
445impl RetryWithBackoff {
446 pub fn new(timeout: zx::MonotonicDuration) -> Self {
447 RetryWithBackoff {
448 deadline: MonotonicInstant::after(timeout),
449 prev_delay: zx::MonotonicDuration::from_millis(0),
450 next_delay: zx::MonotonicDuration::from_millis(1),
451 max_delay: zx::MonotonicDuration::INFINITE,
452 }
453 }
454 pub fn infinite_with_max_interval(max_delay: zx::MonotonicDuration) -> Self {
455 Self {
456 deadline: MonotonicInstant::INFINITE,
457 max_delay,
458 ..Self::new(zx::MonotonicDuration::from_nanos(0))
459 }
460 }
461
462 async fn sleep_unless_after_deadline_(
468 &mut self,
469 verbose: bool,
470 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
471 {
476 if MonotonicInstant::after(zx::MonotonicDuration::from_millis(0)) > self.deadline {
477 if verbose {
478 info!("Skipping sleep. Deadline exceeded.");
479 }
480 return Err(self.deadline - MonotonicInstant::now());
481 }
482
483 let sleep_deadline =
484 std::cmp::min(MonotonicInstant::after(self.next_delay), self.deadline);
485 if verbose {
486 let micros = sleep_deadline.into_nanos() / 1_000;
487 info!("Sleeping until {}.{} 😴", micros / 1_000_000, micros % 1_000_000);
488 }
489
490 Timer::new(sleep_deadline).await;
491 }
492
493 if self.next_delay < self.max_delay {
496 let next_delay = std::cmp::min(
497 self.max_delay,
498 zx::MonotonicDuration::from_nanos(
499 self.prev_delay.into_nanos().saturating_add(self.next_delay.into_nanos()),
500 ),
501 );
502 self.prev_delay = self.next_delay;
503 self.next_delay = next_delay;
504 }
505
506 Ok(self.deadline - MonotonicInstant::now())
507 }
508
509 pub async fn sleep_unless_after_deadline(
510 &mut self,
511 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
512 self.sleep_unless_after_deadline_(false).await
513 }
514
515 pub async fn sleep_unless_after_deadline_verbose(
516 &mut self,
517 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
518 self.sleep_unless_after_deadline_(true).await
519 }
520}
521
522pub fn strip_timestamp_nanos_from_scan_results(
526 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
527) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
528 for scan_result in &mut scan_result_list {
529 scan_result
530 .entries
531 .as_mut()
532 .unwrap()
533 .sort_by(|a, b| a.bssid.as_ref().unwrap().cmp(&b.bssid.as_ref().unwrap()));
534 for entry in scan_result.entries.as_mut().unwrap() {
535 entry.timestamp_nanos.take();
537 }
538 }
539 scan_result_list
540}
541
542pub fn sort_policy_scan_result_list(
547 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
548) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
549 scan_result_list
550 .sort_by(|a, b| a.id.as_ref().expect("empty id").cmp(&b.id.as_ref().expect("empty id")));
551 scan_result_list
552}
553
554pub async fn policy_scan_for_networks<'a>(
561 client_controller: fidl_policy::ClientControllerProxy,
562) -> Vec<fidl_policy::ScanResult> {
563 let (scan_proxy, server_end) = create_proxy();
565 client_controller.scan_for_networks(server_end).expect("requesting scan");
566 let mut scan_result_list = Vec::new();
567 loop {
568 let proxy_result = scan_proxy.get_next().await.expect("getting scan results");
569 let next_scan_result_list = proxy_result.expect("scanning failed");
570 if next_scan_result_list.is_empty() {
571 break;
572 }
573 scan_result_list.extend(next_scan_result_list);
574 }
575 sort_policy_scan_result_list(strip_timestamp_nanos_from_scan_results(scan_result_list))
576}
577
578pub async fn timeout_after<R, F: Future<Output = R> + Unpin>(
581 timeout: zx::MonotonicDuration,
582 main_future: &mut F,
583) -> Result<R, ()> {
584 async { Ok(main_future.await) }.on_timeout(timeout.after_now(), || Err(())).await
585}