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: 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_client, dict_server) = create_endpoints();
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
94 .create_realm2(options, dict_server)
95 .await
96 .expect("Could not create realm");
97 let test_ns =
98 extend_namespace(realm_factory, dict_client).await.expect("failed to extend ns");
99
100 let driver_test_realm_proxy =
102 connect_to_protocol_at::<fidl_driver_test::RealmMarker>(&test_ns)
103 .expect("Failed to connect to driver test realm");
104
105 let (pkg_client, pkg_server) = create_endpoints();
106 fuchsia_fs::directory::open_channel_in_namespace(
107 "/pkg",
108 fidl_fuchsia_io::PERM_READABLE | fidl_fuchsia_io::PERM_EXECUTABLE,
109 pkg_server,
110 )
111 .expect("Could not open /pkg");
112
113 let test_component = fidl_fuchsia_component_resolution::Component {
114 package: Some(fidl_fuchsia_component_resolution::Package {
115 directory: Some(pkg_client),
116 ..Default::default()
117 }),
118 ..Default::default()
119 };
120
121 driver_test_realm_proxy
122 .start(fidl_driver_test::RealmArgs {
123 test_component: Some(test_component),
124 ..Default::default()
125 })
126 .await
127 .expect("FIDL error when starting driver test realm")
128 .expect("Driver test realm server returned an error");
129
130 let _tracing = match trace_manager_hermeticity {
131 Some(fidl_realm::TraceManagerHermeticity::Hermetic) | None => {
132 Tracing::start_at(test_ns.prefix()).await.unwrap()
133 }
134 Some(fidl_realm::TraceManagerHermeticity::NonHermetic) => {
135 Tracing::start_non_hermetic(test_ns.prefix().strip_prefix("/").unwrap())
136 .await
137 .unwrap()
138 }
139 };
140
141 Arc::new(Self { test_ns, devfs: devfs_proxy, _tracing })
142 }
143
144 pub fn test_ns_prefix(&self) -> &str {
145 self.test_ns.prefix()
146 }
147
148 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
149 &self.devfs
150 }
151}
152
153type EventStream = wlantap::WlantapPhyEventStream;
154pub struct TestHelper {
155 ctx: Arc<TestRealmContext>,
156 netdevice_task_handles: Vec<fuchsia_async::Task<()>>,
157 _wlantap: Wlantap,
158 proxy: Arc<wlantap::WlantapPhyProxy>,
159 event_stream: Option<EventStream>,
160}
161struct TestHelperFuture<H, F>
162where
163 H: Handler<(), wlantap::WlantapPhyEvent>,
164 F: Future + Unpin,
165{
166 event_stream: Option<EventStream>,
167 handler: H,
168 future: F,
169}
170impl<H, F> Unpin for TestHelperFuture<H, F>
171where
172 H: Handler<(), wlantap::WlantapPhyEvent>,
173 F: Future + Unpin,
174{
175}
176impl<H, F> Future for TestHelperFuture<H, F>
177where
178 H: Handler<(), wlantap::WlantapPhyEvent>,
179 F: Future + Unpin,
180{
181 type Output = (F::Output, EventStream);
182 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
185 let helper = &mut *self;
186 let stream = helper.event_stream.as_mut().unwrap();
187 while let Poll::Ready(optional_result) = stream.poll_next_unpin(cx) {
188 let event = optional_result
189 .expect("Unexpected end of the WlantapPhy event stream")
190 .expect("WlantapPhy event stream returned an error");
191 helper.handler.call(&mut (), &event);
192 }
193 match helper.future.poll_unpin(cx) {
194 Poll::Pending => {
195 debug!("Main future poll response is pending. Waiting for completion.");
196 Poll::Pending
197 }
198 Poll::Ready(x) => Poll::Ready((x, helper.event_stream.take().unwrap())),
199 }
200 }
201}
202impl TestHelper {
203 pub async fn begin_test(
207 phy_config: wlantap::WlantapPhyConfig,
208 realm_config: fidl_realm::WlanConfig,
209 ) -> Self {
210 let ctx = TestRealmContext::new(realm_config).await;
211 Self::begin_test_with_context(ctx, phy_config).await
212 }
213
214 pub async fn begin_test_with_context(
233 ctx: Arc<TestRealmContext>,
234 config: wlantap::WlantapPhyConfig,
235 ) -> Self {
236 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
237 helper.wait_for_wlan_softmac_start().await;
238 helper
239 }
240
241 pub async fn begin_ap_test(
245 phy_config: wlantap::WlantapPhyConfig,
246 network_config: NetworkConfigBuilder,
247 realm_config: fidl_realm::WlanConfig,
248 ) -> Self {
249 let ctx = TestRealmContext::new(realm_config).await;
250 Self::begin_ap_test_with_context(ctx, phy_config, network_config).await
251 }
252
253 pub async fn begin_ap_test_with_context(
275 ctx: Arc<TestRealmContext>,
276 config: wlantap::WlantapPhyConfig,
277 network_config: NetworkConfigBuilder,
278 ) -> Self {
279 let mut helper = TestHelper::create_phy_and_helper(config, ctx).await;
280 start_ap_and_wait_for_confirmation(helper.ctx.test_ns_prefix(), network_config).await;
281 helper.wait_for_wlan_softmac_start().await;
282 helper
283 }
284
285 async fn create_phy_and_helper(
286 config: wlantap::WlantapPhyConfig,
287 ctx: Arc<TestRealmContext>,
288 ) -> Self {
289 let wlantap =
291 Wlantap::open_from_devfs(&ctx.devfs).await.expect("Failed to open wlantapctl");
292 let proxy = wlantap.create_phy(config).await.expect("Failed to create wlantap PHY");
293 let event_stream = Some(proxy.take_event_stream());
294 TestHelper {
295 ctx,
296 netdevice_task_handles: vec![],
297 _wlantap: wlantap,
298 proxy: Arc::new(proxy),
299 event_stream,
300 }
301 }
302
303 async fn wait_for_wlan_softmac_start(&mut self) {
304 let (sender, receiver) = oneshot::channel::<()>();
305 self.run_until_complete_or_timeout(
306 zx::MonotonicDuration::from_seconds(12),
307 "receive a WlanSoftmacStart event",
308 event::on_start_mac(event::once(|_, _| sender.send(()))),
309 receiver,
310 )
311 .await
312 .unwrap_or_else(|oneshot::Canceled| panic!());
313 }
314
315 pub fn proxy(&self) -> Arc<wlantap::WlantapPhyProxy> {
320 Arc::clone(&self.proxy)
321 }
322
323 pub fn test_ns_prefix(&self) -> &str {
324 self.ctx.test_ns_prefix()
325 }
326
327 pub fn devfs(&self) -> &fidl_fuchsia_io::DirectoryProxy {
328 self.ctx.devfs()
329 }
330
331 pub async fn start_netdevice_session(
332 &mut self,
333 mac: MacAddr,
334 ) -> (netdevice_client::Session, netdevice_client::Port) {
335 let mac = fidl_fuchsia_net::MacAddress { octets: mac.to_array() };
336 let (client, port) = netdevice_helper::create_client(self.devfs(), mac)
337 .await
338 .expect("failed to create netdevice client");
339 let (session, task_handle) = netdevice_helper::start_session(client, port).await;
340 self.netdevice_task_handles.push(task_handle);
341 (session, port)
342 }
343
344 pub async fn run_until_complete_or_timeout<H, F>(
350 &mut self,
351 timeout: zx::MonotonicDuration,
352 context: impl Display,
353 handler: H,
354 future: F,
355 ) -> F::Output
356 where
357 H: Handler<(), wlantap::WlantapPhyEvent>,
358 F: Future + Unpin,
359 {
360 info!("Running main future until completion or timeout with event handler: {}", context);
361 let start_time = zx::MonotonicInstant::get();
362 let (item, stream) = TestHelperFuture {
363 event_stream: Some(self.event_stream.take().unwrap()),
364 handler,
365 future,
366 }
367 .expect_within(timeout, format!("Main future timed out: {}", context))
368 .await;
369 let end_time = zx::MonotonicInstant::get();
370 let elapsed = end_time - start_time;
371 let elapsed_seconds = elapsed.into_seconds_f64();
372 let elapsed_ratio = elapsed_seconds / timeout.into_seconds_f64();
373 if elapsed_ratio < TIMEOUT_WARN_THRESHOLD {
374 info!("Main future completed in {:.2} seconds: {}", elapsed_seconds, context);
375 } else {
376 warn!(
377 "Main future completed in {:.2} seconds ({:.1}% of timeout): {}",
378 elapsed_seconds,
379 elapsed_ratio * 100.,
380 context,
381 );
382 }
383 self.event_stream = Some(stream);
384 item
385 }
386}
387impl Drop for TestHelper {
388 fn drop(&mut self) {
389 while let Some(task_handle) = self.netdevice_task_handles.pop() {
393 drop(task_handle);
394 }
395
396 let (placeholder_proxy, _server_end) =
399 fidl::endpoints::create_proxy::<wlantap::WlantapPhyMarker>();
400 let mut proxy = Arc::new(placeholder_proxy);
401 std::mem::swap(&mut self.proxy, &mut proxy);
402
403 let event_stream = self.event_stream.take();
407 drop(event_stream);
408
409 let sync_proxy = wlantap::WlantapPhySynchronousProxy::new(fidl::Channel::from_handle(
410 Arc::<wlantap::WlantapPhyProxy>::into_inner(proxy)
413 .expect("Outstanding references to WlantapPhyProxy! Failed to drop TestHelper.")
414 .into_channel()
415 .expect("failed to get fidl::AsyncChannel from proxy")
416 .into_zx_channel()
417 .into_handle(),
418 ));
419
420 sync_proxy
438 .shutdown(zx::MonotonicInstant::INFINITE)
439 .expect("Failed to shutdown WlantapPhy gracefully.");
440 }
441}
442
443pub struct RetryWithBackoff {
444 deadline: MonotonicInstant,
445 prev_delay: zx::MonotonicDuration,
446 next_delay: zx::MonotonicDuration,
447 max_delay: zx::MonotonicDuration,
448}
449impl RetryWithBackoff {
450 pub fn new(timeout: zx::MonotonicDuration) -> Self {
451 RetryWithBackoff {
452 deadline: MonotonicInstant::after(timeout),
453 prev_delay: zx::MonotonicDuration::from_millis(0),
454 next_delay: zx::MonotonicDuration::from_millis(1),
455 max_delay: zx::MonotonicDuration::INFINITE,
456 }
457 }
458 pub fn infinite_with_max_interval(max_delay: zx::MonotonicDuration) -> Self {
459 Self {
460 deadline: MonotonicInstant::INFINITE,
461 max_delay,
462 ..Self::new(zx::MonotonicDuration::from_nanos(0))
463 }
464 }
465
466 async fn sleep_unless_after_deadline_(
472 &mut self,
473 verbose: bool,
474 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
475 {
480 if MonotonicInstant::after(zx::MonotonicDuration::from_millis(0)) > self.deadline {
481 if verbose {
482 info!("Skipping sleep. Deadline exceeded.");
483 }
484 return Err(self.deadline - MonotonicInstant::now());
485 }
486
487 let sleep_deadline =
488 std::cmp::min(MonotonicInstant::after(self.next_delay), self.deadline);
489 if verbose {
490 let micros = sleep_deadline.into_nanos() / 1_000;
491 info!("Sleeping until {}.{} 😴", micros / 1_000_000, micros % 1_000_000);
492 }
493
494 Timer::new(sleep_deadline).await;
495 }
496
497 if self.next_delay < self.max_delay {
500 let next_delay = std::cmp::min(
501 self.max_delay,
502 zx::MonotonicDuration::from_nanos(
503 self.prev_delay.into_nanos().saturating_add(self.next_delay.into_nanos()),
504 ),
505 );
506 self.prev_delay = self.next_delay;
507 self.next_delay = next_delay;
508 }
509
510 Ok(self.deadline - MonotonicInstant::now())
511 }
512
513 pub async fn sleep_unless_after_deadline(
514 &mut self,
515 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
516 self.sleep_unless_after_deadline_(false).await
517 }
518
519 pub async fn sleep_unless_after_deadline_verbose(
520 &mut self,
521 ) -> Result<zx::MonotonicDuration, zx::MonotonicDuration> {
522 self.sleep_unless_after_deadline_(true).await
523 }
524}
525
526pub fn strip_timestamp_nanos_from_scan_results(
530 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
531) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
532 for scan_result in &mut scan_result_list {
533 scan_result
534 .entries
535 .as_mut()
536 .unwrap()
537 .sort_by(|a, b| a.bssid.as_ref().unwrap().cmp(&b.bssid.as_ref().unwrap()));
538 for entry in scan_result.entries.as_mut().unwrap() {
539 entry.timestamp_nanos.take();
541 }
542 }
543 scan_result_list
544}
545
546pub fn sort_policy_scan_result_list(
551 mut scan_result_list: Vec<fidl_fuchsia_wlan_policy::ScanResult>,
552) -> Vec<fidl_fuchsia_wlan_policy::ScanResult> {
553 scan_result_list
554 .sort_by(|a, b| a.id.as_ref().expect("empty id").cmp(&b.id.as_ref().expect("empty id")));
555 scan_result_list
556}
557
558pub async fn policy_scan_for_networks<'a>(
565 client_controller: fidl_policy::ClientControllerProxy,
566) -> Vec<fidl_policy::ScanResult> {
567 let (scan_proxy, server_end) = create_proxy();
569 client_controller.scan_for_networks(server_end).expect("requesting scan");
570 let mut scan_result_list = Vec::new();
571 loop {
572 let proxy_result = scan_proxy.get_next().await.expect("getting scan results");
573 let next_scan_result_list = proxy_result.expect("scanning failed");
574 if next_scan_result_list.is_empty() {
575 break;
576 }
577 scan_result_list.extend(next_scan_result_list);
578 }
579 sort_policy_scan_result_list(strip_timestamp_nanos_from_scan_results(scan_result_list))
580}
581
582pub async fn timeout_after<R, F: Future<Output = R> + Unpin>(
585 timeout: zx::MonotonicDuration,
586 main_future: &mut F,
587) -> Result<R, ()> {
588 async { Ok(main_future.await) }.on_timeout(timeout.after_now(), || Err(())).await
589}