1use crate::event::{self, Handler};
5use crate::netdevice_helper;
6use crate::wlancfg_helper::{NetworkConfigBuilder, start_ap_and_wait_for_confirmation};
7use fidl::endpoints::{Proxy, create_endpoints, create_proxy};
8use fuchsia_async::{DurationExt, MonotonicInstant, TimeoutExt, Timer};
9use fuchsia_component::client::{connect_to_protocol, connect_to_protocol_at};
10use zx::prelude::*;
11
12use futures::channel::oneshot;
13use futures::{FutureExt, StreamExt};
14use ieee80211::{MacAddr, MacAddrBytes};
15use log::{debug, info, warn};
16use realm_client::{InstalledNamespace, extend_namespace};
17use std::fmt::Display;
18use std::future::Future;
19use std::pin::Pin;
20use std::sync::Arc;
21use std::task::{Context, Poll};
22use test_realm_helpers::tracing::Tracing;
23use wlan_common::test_utils::ExpectWithin;
24use wlantap_client::Wlantap;
25use {
26 fidl_fuchsia_driver_test as fidl_driver_test, fidl_fuchsia_wlan_policy as fidl_policy,
27 fidl_fuchsia_wlan_tap as wlantap, fidl_test_wlan_realm as fidl_realm,
28};
29
30const TIMEOUT_WARN_THRESHOLD: f64 = 0.8;
32
33pub struct TestRealmContext {
62 test_ns: Arc<InstalledNamespace>,
66
67 devfs: fidl_fuchsia_io::DirectoryProxy,
69 _tracing: Tracing,
70}
71
72impl TestRealmContext {
73 pub async fn new(config: fidl_realm::WlanConfig) -> Arc<Self> {
78 let realm_factory = connect_to_protocol::<fidl_realm::RealmFactoryMarker>()
79 .expect("Could not connect to realm factory protocol");
80
81 let (dict_1, dict_2) = zx::EventPair::create();
82 let (devfs_proxy, devfs_server) = create_proxy();
83
84 let trace_manager_hermeticity = config.trace_manager_hermeticity.clone();
89 let options = fidl_realm::RealmOptions {
90 devfs_server_end: Some(devfs_server),
91 wlan_config: Some(config),
92 ..Default::default()
93 };
94 let _ = realm_factory.create_realm3(options, dict_1).await.expect("Could not create realm");
95 let test_ns =
96 Arc::new(extend_namespace(realm_factory, dict_2).await.expect("failed to extend ns"));
97
98 let driver_test_realm_proxy =
100 connect_to_protocol_at::<fidl_driver_test::RealmMarker>(&*test_ns)
101 .expect("Failed to connect to driver test realm");
102
103 let (pkg_client, pkg_server) = create_endpoints();
104 fuchsia_fs::directory::open_channel_in_namespace(
105 "/pkg",
106 fidl_fuchsia_io::PERM_READABLE | fidl_fuchsia_io::PERM_EXECUTABLE,
107 pkg_server,
108 )
109 .expect("Could not open /pkg");
110
111 let test_component = fidl_fuchsia_component_resolution::Component {
112 package: Some(fidl_fuchsia_component_resolution::Package {
113 directory: Some(pkg_client),
114 ..Default::default()
115 }),
116 ..Default::default()
117 };
118
119 driver_test_realm_proxy
120 .start(fidl_driver_test::RealmArgs {
121 test_component: Some(test_component),
122 ..Default::default()
123 })
124 .await
125 .expect("FIDL error when starting driver test realm")
126 .expect("Driver test realm server returned an error");
127
128 let _tracing = match trace_manager_hermeticity {
129 Some(fidl_realm::TraceManagerHermeticity::Hermetic) | None => {
130 Tracing::start_at(Arc::clone(&test_ns)).await.unwrap()
131 }
132 Some(fidl_realm::TraceManagerHermeticity::NonHermetic) => {
133 Tracing::start_non_hermetic(test_ns.prefix().strip_prefix("/").unwrap())
134 .await
135 .unwrap()
136 }
137 };
138
139 Arc::new(Self { test_ns, devfs: devfs_proxy, _tracing })
140 }
141
142 pub fn test_ns_prefix(&self) -> &str {
143 self.test_ns.prefix()
144 }
145
146 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
147 &self.devfs
148 }
149}
150
151type EventStream = wlantap::WlantapPhyEventStream;
152pub struct TestHelper {
153 ctx: Arc<TestRealmContext>,
154 netdevice_task_handles: Vec<fuchsia_async::Task<()>>,
155 _wlantap: Wlantap,
156 proxy: Arc<wlantap::WlantapPhyProxy>,
157 event_stream: Option<EventStream>,
158}
159struct TestHelperFuture<H, F>
160where
161 H: Handler<(), wlantap::WlantapPhyEvent>,
162 F: Future + Unpin,
163{
164 event_stream: Option<EventStream>,
165 handler: H,
166 future: F,
167}
168impl<H, F> Unpin for TestHelperFuture<H, F>
169where
170 H: Handler<(), wlantap::WlantapPhyEvent>,
171 F: Future + Unpin,
172{
173}
174impl<H, F> Future for TestHelperFuture<H, F>
175where
176 H: Handler<(), wlantap::WlantapPhyEvent>,
177 F: Future + Unpin,
178{
179 type Output = (F::Output, EventStream);
180 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
183 let helper = &mut *self;
184 let stream = helper.event_stream.as_mut().unwrap();
185 while let Poll::Ready(optional_result) = stream.poll_next_unpin(cx) {
186 let event = optional_result
187 .expect("Unexpected end of the WlantapPhy event stream")
188 .expect("WlantapPhy event stream returned an error");
189 helper.handler.call(&mut (), &event);
190 }
191 match helper.future.poll_unpin(cx) {
192 Poll::Pending => {
193 debug!("Main future poll response is pending. Waiting for completion.");
194 Poll::Pending
195 }
196 Poll::Ready(x) => Poll::Ready((x, helper.event_stream.take().unwrap())),
197 }
198 }
199}
200impl TestHelper {
201 pub async fn begin_test(
205 phy_config: wlantap::WlantapPhyConfig,
206 realm_config: fidl_realm::WlanConfig,
207 ) -> Self {
208 let ctx = TestRealmContext::new(realm_config).await;
209 Self::begin_test_with_context(ctx, phy_config).await
210 }
211
212 pub async fn begin_test_with_context(
231 ctx: Arc<TestRealmContext>,
232 config: wlantap::WlantapPhyConfig,
233 ) -> Self {
234 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
235 helper.wait_for_wlan_softmac_start().await;
236 helper
237 }
238
239 pub async fn begin_ap_test(
243 phy_config: wlantap::WlantapPhyConfig,
244 network_config: NetworkConfigBuilder,
245 realm_config: fidl_realm::WlanConfig,
246 ) -> Self {
247 let ctx = TestRealmContext::new(realm_config).await;
248 Self::begin_ap_test_with_context(ctx, phy_config, network_config).await
249 }
250
251 pub async fn begin_ap_test_with_context(
273 ctx: Arc<TestRealmContext>,
274 config: wlantap::WlantapPhyConfig,
275 network_config: NetworkConfigBuilder,
276 ) -> Self {
277 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
278 start_ap_and_wait_for_confirmation(helper.ctx.test_ns_prefix(), network_config).await;
279 helper.wait_for_wlan_softmac_start().await;
280 helper
281 }
282
283 async fn create_phy_and_helper(
284 config: wlantap::WlantapPhyConfig,
285 ctx: Arc<TestRealmContext>,
286 ) -> Self {
287 let wlantap =
289 Wlantap::open_from_devfs(&ctx.devfs).await.expect("Failed to open wlantapctl");
290 let proxy = wlantap.create_phy(config).await.expect("Failed to create wlantap PHY");
291 let event_stream = Some(proxy.take_event_stream());
292 TestHelper {
293 ctx,
294 netdevice_task_handles: vec![],
295 _wlantap: wlantap,
296 proxy: Arc::new(proxy),
297 event_stream,
298 }
299 }
300
301 async fn wait_for_wlan_softmac_start(&mut self) {
302 let (sender, receiver) = oneshot::channel::<()>();
303 self.run_until_complete_or_timeout(
304 zx::MonotonicDuration::from_seconds(12),
305 "receive a WlanSoftmacStart event",
306 event::on_start_mac(event::once(|_, _| sender.send(()))),
307 receiver,
308 )
309 .await
310 .unwrap_or_else(|oneshot::Canceled| panic!());
311 }
312
313 pub fn proxy(&self) -> Arc<wlantap::WlantapPhyProxy> {
318 Arc::clone(&self.proxy)
319 }
320
321 pub fn test_ns_prefix(&self) -> &str {
322 self.ctx.test_ns_prefix()
323 }
324
325 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
326 self.ctx.devfs()
327 }
328
329 pub async fn start_netdevice_session(
330 &mut self,
331 mac: MacAddr,
332 ) -> (netdevice_client::Session, netdevice_client::Port) {
333 let mac = fidl_fuchsia_net::MacAddress { octets: mac.to_array() };
334 let (client, port) = netdevice_helper::create_client(self.devfs(), mac)
335 .await
336 .expect("failed to create netdevice client");
337 let (session, task_handle) = netdevice_helper::start_session(client, port).await;
338 self.netdevice_task_handles.push(task_handle);
339 (session, port)
340 }
341
342 pub async fn run_until_complete_or_timeout<H, F>(
348 &mut self,
349 timeout: zx::MonotonicDuration,
350 context: impl Display,
351 handler: H,
352 future: F,
353 ) -> F::Output
354 where
355 H: Handler<(), wlantap::WlantapPhyEvent>,
356 F: Future + Unpin,
357 {
358 info!("Running main future until completion or timeout with event handler: {}", context);
359 let start_time = zx::MonotonicInstant::get();
360 let (item, stream) = TestHelperFuture {
361 event_stream: Some(self.event_stream.take().unwrap()),
362 handler,
363 future,
364 }
365 .expect_within(timeout, format!("Main future timed out: {}", context))
366 .await;
367 let end_time = zx::MonotonicInstant::get();
368 let elapsed = end_time - start_time;
369 let elapsed_seconds = elapsed.into_seconds_f64();
370 let elapsed_ratio = elapsed_seconds / timeout.into_seconds_f64();
371 if elapsed_ratio < TIMEOUT_WARN_THRESHOLD {
372 info!("Main future completed in {:.2} seconds: {}", elapsed_seconds, context);
373 } else {
374 warn!(
375 "Main future completed in {:.2} seconds ({:.1}% of timeout): {}",
376 elapsed_seconds,
377 elapsed_ratio * 100.,
378 context,
379 );
380 }
381 self.event_stream = Some(stream);
382 item
383 }
384}
385impl Drop for TestHelper {
386 fn drop(&mut self) {
387 while let Some(task_handle) = self.netdevice_task_handles.pop() {
391 drop(task_handle);
392 }
393
394 let (placeholder_proxy, _server_end) =
397 fidl::endpoints::create_proxy::<wlantap::WlantapPhyMarker>();
398 let mut proxy = Arc::new(placeholder_proxy);
399 std::mem::swap(&mut self.proxy, &mut proxy);
400
401 let event_stream = self.event_stream.take();
405 drop(event_stream);
406
407 let sync_proxy = wlantap::WlantapPhySynchronousProxy::new(fidl::Channel::from_handle(
408 Arc::<wlantap::WlantapPhyProxy>::into_inner(proxy)
411 .expect("Outstanding references to WlantapPhyProxy! Failed to drop TestHelper.")
412 .into_channel()
413 .expect("failed to get fidl::AsyncChannel from proxy")
414 .into_zx_channel()
415 .into_handle(),
416 ));
417
418 sync_proxy
436 .shutdown(zx::MonotonicInstant::INFINITE)
437 .expect("Failed to shutdown WlantapPhy gracefully.");
438 }
439}
440
441pub struct RetryWithBackoff {
442 deadline: MonotonicInstant,
443 prev_delay: zx::MonotonicDuration,
444 next_delay: zx::MonotonicDuration,
445 max_delay: zx::MonotonicDuration,
446}
447impl RetryWithBackoff {
448 pub fn new(timeout: zx::MonotonicDuration) -> Self {
449 RetryWithBackoff {
450 deadline: MonotonicInstant::after(timeout),
451 prev_delay: zx::MonotonicDuration::from_millis(0),
452 next_delay: zx::MonotonicDuration::from_millis(1),
453 max_delay: zx::MonotonicDuration::INFINITE,
454 }
455 }
456 pub fn infinite_with_max_interval(max_delay: zx::MonotonicDuration) -> Self {
457 Self {
458 deadline: MonotonicInstant::INFINITE,
459 max_delay,
460 ..Self::new(zx::MonotonicDuration::from_nanos(0))
461 }
462 }
463
464 async fn sleep_unless_after_deadline_(
470 &mut self,
471 verbose: bool,
472 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
473 {
478 if MonotonicInstant::after(zx::MonotonicDuration::from_millis(0)) > self.deadline {
479 if verbose {
480 info!("Skipping sleep. Deadline exceeded.");
481 }
482 return Err(self.deadline - MonotonicInstant::now());
483 }
484
485 let sleep_deadline =
486 std::cmp::min(MonotonicInstant::after(self.next_delay), self.deadline);
487 if verbose {
488 let micros = sleep_deadline.into_nanos() / 1_000;
489 info!("Sleeping until {}.{} 😴", micros / 1_000_000, micros % 1_000_000);
490 }
491
492 Timer::new(sleep_deadline).await;
493 }
494
495 if self.next_delay < self.max_delay {
498 let next_delay = std::cmp::min(
499 self.max_delay,
500 zx::MonotonicDuration::from_nanos(
501 self.prev_delay.into_nanos().saturating_add(self.next_delay.into_nanos()),
502 ),
503 );
504 self.prev_delay = self.next_delay;
505 self.next_delay = next_delay;
506 }
507
508 Ok(self.deadline - MonotonicInstant::now())
509 }
510
511 pub async fn sleep_unless_after_deadline(
512 &mut self,
513 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
514 self.sleep_unless_after_deadline_(false).await
515 }
516
517 pub async fn sleep_unless_after_deadline_verbose(
518 &mut self,
519 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
520 self.sleep_unless_after_deadline_(true).await
521 }
522}
523
524pub fn strip_timestamp_nanos_from_scan_results(
528 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
529) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
530 for scan_result in &mut scan_result_list {
531 scan_result
532 .entries
533 .as_mut()
534 .unwrap()
535 .sort_by(|a, b| a.bssid.as_ref().unwrap().cmp(&b.bssid.as_ref().unwrap()));
536 for entry in scan_result.entries.as_mut().unwrap() {
537 entry.timestamp_nanos.take();
539 }
540 }
541 scan_result_list
542}
543
544pub fn sort_policy_scan_result_list(
549 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
550) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
551 scan_result_list
552 .sort_by(|a, b| a.id.as_ref().expect("empty id").cmp(&b.id.as_ref().expect("empty id")));
553 scan_result_list
554}
555
556pub async fn policy_scan_for_networks<'a>(
563 client_controller: fidl_policy::ClientControllerProxy,
564) -> Vec<fidl_policy::ScanResult> {
565 let (scan_proxy, server_end) = create_proxy();
567 client_controller.scan_for_networks(server_end).expect("requesting scan");
568 let mut scan_result_list = Vec::new();
569 loop {
570 let proxy_result = scan_proxy.get_next().await.expect("getting scan results");
571 let next_scan_result_list = proxy_result.expect("scanning failed");
572 if next_scan_result_list.is_empty() {
573 break;
574 }
575 scan_result_list.extend(next_scan_result_list);
576 }
577 sort_policy_scan_result_list(strip_timestamp_nanos_from_scan_results(scan_result_list))
578}
579
580pub async fn timeout_after<R, F: Future<Output = R> + Unpin>(
583 timeout: zx::MonotonicDuration,
584 main_future: &mut F,
585) -> Result<R, ()> {
586 async { Ok(main_future.await) }.on_timeout(timeout.after_now(), || Err(())).await
587}