| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::{ |
| device::{self, IfaceDevice, IfaceMap, NewIface, PhyDevice, PhyMap}, |
| watcher_service, |
| }, |
| anyhow::{Context, Error}, |
| core::sync::atomic::AtomicUsize, |
| fidl::{endpoints::create_endpoints, HandleBased}, |
| fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_device as fidl_dev, |
| fidl_fuchsia_wlan_device_service::{self as fidl_svc, DeviceMonitorRequest}, |
| fidl_fuchsia_wlan_sme as fidl_sme, |
| fuchsia_inspect::Inspector, |
| fuchsia_zircon as zx, |
| futures::{ |
| channel::mpsc, select, stream::FuturesUnordered, FutureExt, StreamExt, TryStreamExt, |
| }, |
| std::sync::{atomic::Ordering, Arc}, |
| tracing::{error, info, warn}, |
| }; |
| |
| /// Thread-safe counter for spawned ifaces. |
| pub struct IfaceCounter(AtomicUsize); |
| |
| impl IfaceCounter { |
| pub fn new() -> Self { |
| Self(AtomicUsize::new(0)) |
| } |
| |
| /// Provides the caller with a new unique id. |
| pub fn next_iface_id(&self) -> usize { |
| self.0.fetch_add(1, Ordering::SeqCst) |
| } |
| } |
| |
| pub(crate) async fn serve_monitor_requests( |
| mut req_stream: fidl_svc::DeviceMonitorRequestStream, |
| phys: Arc<PhyMap>, |
| ifaces: Arc<IfaceMap>, |
| watcher_service: watcher_service::WatcherService<PhyDevice, IfaceDevice>, |
| new_iface_sink: mpsc::UnboundedSender<NewIface>, |
| iface_counter: Arc<IfaceCounter>, |
| mut ifaces_node: fuchsia_inspect::Node, |
| cfg: wlandevicemonitor_config::Config, |
| ) -> Result<(), Error> { |
| while let Some(req) = req_stream.try_next().await.context("error running DeviceService")? { |
| match req { |
| DeviceMonitorRequest::ListPhys { responder } => responder.send(&list_phys(&phys)), |
| DeviceMonitorRequest::ListIfaces { responder } => responder.send(&list_ifaces(&ifaces)), |
| DeviceMonitorRequest::GetDevPath { phy_id, responder } => { |
| responder.send(get_dev_path(&phys, phy_id).as_deref()) |
| } |
| DeviceMonitorRequest::GetSupportedMacRoles { phy_id, responder } => responder |
| .send(get_supported_mac_roles(&phys, phy_id).await.as_deref().map_err(|e| *e)), |
| DeviceMonitorRequest::WatchDevices { watcher, control_handle: _ } => { |
| watcher_service |
| .add_watcher(watcher) |
| .unwrap_or_else(|e| error!("error registering a device watcher: {}", e)); |
| Ok(()) |
| } |
| DeviceMonitorRequest::GetCountry { phy_id, responder } => { |
| responder.send(get_country(&phys, phy_id).await.as_ref().map_err(|s| s.into_raw())) |
| } |
| DeviceMonitorRequest::SetCountry { req, responder } => { |
| let status = set_country(&phys, req).await; |
| responder.send(status.into_raw()) |
| } |
| DeviceMonitorRequest::ClearCountry { req, responder } => { |
| let status = clear_country(&phys, req).await; |
| responder.send(status.into_raw()) |
| } |
| DeviceMonitorRequest::SetPowerSaveMode { req, responder } => { |
| let status = set_power_save_mode(&phys, req).await; |
| responder.send(status.into_raw()) |
| } |
| DeviceMonitorRequest::GetPowerSaveMode { phy_id, responder } => responder |
| .send(get_power_save_mode(&phys, phy_id).await.as_ref().map_err(|s| s.into_raw())), |
| DeviceMonitorRequest::CreateIface { req, responder } => { |
| match create_iface( |
| &new_iface_sink, |
| &phys, |
| req, |
| &iface_counter, |
| &cfg, |
| &mut ifaces_node, |
| ) |
| .await |
| { |
| Ok(new_iface) => { |
| info!("iface #{} started ({:?})", new_iface.id, new_iface.phy_ownership); |
| ifaces.insert( |
| new_iface.id, |
| IfaceDevice { |
| phy_ownership: new_iface.phy_ownership, |
| generic_sme: new_iface.generic_sme, |
| inspect_node: new_iface.inspect_node, |
| inspect_vmo: new_iface.inspect_vmo, |
| }, |
| ); |
| |
| let resp = fidl_svc::CreateIfaceResponse { iface_id: new_iface.id }; |
| responder.send(zx::sys::ZX_OK, Some(&resp)) |
| } |
| Err(status) => responder.send(status.into_raw(), None), |
| } |
| } |
| DeviceMonitorRequest::QueryIface { iface_id, responder } => { |
| let result = query_iface(&ifaces, iface_id).await; |
| responder.send(result.as_ref().map_err(|e| e.into_raw())) |
| } |
| DeviceMonitorRequest::DestroyIface { req, responder } => { |
| let result = |
| destroy_iface(&phys, &ifaces, ifaces_node.clone_weak(), req.iface_id).await; |
| let status = into_status_and_opt(result).0; |
| responder.send(status.into_raw()) |
| } |
| DeviceMonitorRequest::GetClientSme { iface_id, sme_server, responder } => { |
| let result = get_client_sme(&ifaces, iface_id, sme_server).await; |
| responder.send(result.map_err(|e| e.into_raw())) |
| } |
| DeviceMonitorRequest::GetApSme { iface_id, sme_server, responder } => { |
| let result = get_ap_sme(&ifaces, iface_id, sme_server).await; |
| responder.send(result.map_err(|e| e.into_raw())) |
| } |
| DeviceMonitorRequest::GetSmeTelemetry { iface_id, telemetry_server, responder } => { |
| let result = get_sme_telemetry(&ifaces, iface_id, telemetry_server).await; |
| responder.send(result.map_err(|e| e.into_raw())) |
| } |
| DeviceMonitorRequest::GetFeatureSupport { |
| iface_id, |
| feature_support_server, |
| responder, |
| } => { |
| let result = get_feature_support(&ifaces, iface_id, feature_support_server).await; |
| responder.send(result.map_err(|e| e.into_raw())) |
| } |
| }?; |
| } |
| |
| Ok(()) |
| } |
| |
| pub(crate) async fn handle_new_iface_stream( |
| phys: Arc<PhyMap>, |
| ifaces: Arc<IfaceMap>, |
| ifaces_node: fuchsia_inspect::Node, |
| mut iface_stream: mpsc::UnboundedReceiver<NewIface>, |
| ) -> Result<(), Error> { |
| let mut futures_unordered = FuturesUnordered::new(); |
| loop { |
| select! { |
| _ = futures_unordered.select_next_some() => (), |
| new_iface = iface_stream.next() => { |
| if let Some(new_iface) = new_iface { |
| futures_unordered.push( |
| handle_single_new_iface( |
| phys.clone(), |
| ifaces.clone(), |
| ifaces_node.clone_weak(), |
| new_iface |
| ) |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| async fn handle_single_new_iface( |
| phys: Arc<PhyMap>, |
| ifaces: Arc<IfaceMap>, |
| ifaces_node: fuchsia_inspect::Node, |
| new_iface: NewIface, |
| ) { |
| let mut event_stream = new_iface.generic_sme.take_event_stream().fuse(); |
| // GenericSme does not currently have any client-bound messages, so this is |
| // exclusively to monitor for the channel epitaph and respond appropriately. |
| // A clean SME shutdown should always result in an OK epitaph -- initiate |
| // iface destruction on any other behavior to remove stale info. |
| while !event_stream.is_done() { |
| match event_stream.next().await { |
| None => (), |
| Some(Err(fidl::Error::ClientChannelClosed { status, .. })) => match status { |
| zx::Status::OK => { |
| info!("Generic SME event stream closed cleanly."); |
| return; |
| } |
| e => { |
| error!("Generic SME event stream closed with error: {}", e); |
| break; |
| } |
| }, |
| Some(message) => { |
| error!( |
| "Unexpected message from Generic SME event stream: {:?}. Aborting Generic SME.", |
| message |
| ); |
| break; |
| } |
| } |
| } |
| match destroy_iface(&phys, &ifaces, ifaces_node, new_iface.id).await { |
| Ok(()) => info!("Destroyed iface {}", new_iface.id), |
| Err(e) if e == zx::Status::NOT_FOUND => { |
| warn!("destroy_iface - iface {} not found; assume success", new_iface.id) |
| } |
| Err(e) => error!("Error while destroying iface {}: {}", new_iface.id, e), |
| } |
| } |
| |
| fn list_phys(phys: &PhyMap) -> Vec<u16> { |
| phys.get_snapshot().iter().map(|(phy_id, _)| *phy_id).collect() |
| } |
| |
| fn list_ifaces(ifaces: &IfaceMap) -> Vec<u16> { |
| ifaces.get_snapshot().iter().map(|(iface_id, _)| *iface_id).collect() |
| } |
| |
| fn get_dev_path(phys: &PhyMap, phy_id: u16) -> Option<String> { |
| let phy = phys.get(&phy_id)?; |
| Some(phy.device_path.clone()) |
| } |
| |
| async fn get_country( |
| phys: &PhyMap, |
| phy_id: u16, |
| ) -> Result<fidl_svc::GetCountryResponse, zx::Status> { |
| let phy = phys.get(&phy_id).ok_or(Err(zx::Status::NOT_FOUND))?; |
| match phy.proxy.get_country().await { |
| Ok(result) => match result { |
| Ok(country_code) => Ok(fidl_svc::GetCountryResponse { alpha2: country_code.alpha2 }), |
| Err(status) => Err(zx::Status::from_raw(status)), |
| }, |
| Err(e) => { |
| error!("Error sending 'GetCountry' request to phy #{}: {}", phy_id, e); |
| Err(zx::Status::INTERNAL) |
| } |
| } |
| } |
| |
| async fn set_country(phys: &PhyMap, req: fidl_svc::SetCountryRequest) -> zx::Status { |
| let phy_id = req.phy_id; |
| let phy = match phys.get(&req.phy_id) { |
| None => return zx::Status::NOT_FOUND, |
| Some(p) => p, |
| }; |
| |
| let phy_req = fidl_dev::CountryCode { alpha2: req.alpha2 }; |
| match phy.proxy.set_country(&phy_req).await { |
| Ok(status) => zx::Status::from_raw(status), |
| Err(e) => { |
| error!("Error sending SetCountry set_country request to phy #{}: {}", phy_id, e); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| async fn clear_country(phys: &PhyMap, req: fidl_svc::ClearCountryRequest) -> zx::Status { |
| let phy = match phys.get(&req.phy_id) { |
| None => return zx::Status::NOT_FOUND, |
| Some(p) => p, |
| }; |
| |
| match phy.proxy.clear_country().await { |
| Ok(status) => zx::Status::from_raw(status), |
| Err(e) => { |
| error!( |
| "Error sending ClearCountry clear_country request to phy #{}: {}", |
| req.phy_id, e |
| ); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| async fn set_power_save_mode(phys: &PhyMap, req: fidl_svc::SetPowerSaveModeRequest) -> zx::Status { |
| let phy_id = req.phy_id; |
| let phy = match phys.get(&req.phy_id) { |
| None => return zx::Status::NOT_FOUND, |
| Some(p) => p, |
| }; |
| |
| let phy_req = req.ps_mode; |
| match phy.proxy.set_power_save_mode(phy_req).await { |
| Ok(status) => zx::Status::from_raw(status), |
| Err(e) => { |
| error!("Error sending SetPowerSaveModeRequest request to phy #{}: {}", phy_id, e); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| async fn get_power_save_mode( |
| phys: &PhyMap, |
| phy_id: u16, |
| ) -> Result<fidl_svc::GetPowerSaveModeResponse, zx::Status> { |
| let phy = phys.get(&phy_id).ok_or(Err(zx::Status::NOT_FOUND))?; |
| match phy.proxy.get_power_save_mode().await { |
| Ok(result) => match result { |
| Ok(resp) => Ok(fidl_svc::GetPowerSaveModeResponse { ps_mode: resp }), |
| Err(status) => Err(zx::Status::from_raw(status)), |
| }, |
| Err(e) => { |
| error!("Error sending 'GetPowerSaveMode' request to phy #{}: {}", phy_id, e); |
| Err(zx::Status::INTERNAL) |
| } |
| } |
| } |
| |
| async fn get_supported_mac_roles( |
| phys: &PhyMap, |
| id: u16, |
| ) -> Result<Vec<fidl_common::WlanMacRole>, zx::sys::zx_status_t> { |
| let phy = phys.get(&id).ok_or(zx::sys::ZX_ERR_NOT_FOUND)?; |
| phy.proxy.get_supported_mac_roles().await.map_err(move |fidl_error| { |
| error!("get_supported_mac_roles(id = {}): error sending 'GetSupportedMacRoles' request to phy: {}", id, fidl_error); |
| zx::sys::ZX_ERR_INTERNAL |
| })?.map_err(move |e| { |
| let status = zx::Status::from_raw(e); |
| error!("get_supported_mac_roles(id = {}): returned an error: {}", id, status); |
| status.into_raw() |
| }) |
| } |
| |
| async fn create_iface( |
| new_iface_sink: &mpsc::UnboundedSender<NewIface>, |
| phys: &PhyMap, |
| req: fidl_svc::CreateIfaceRequest, |
| iface_counter: &Arc<IfaceCounter>, |
| cfg: &wlandevicemonitor_config::Config, |
| ifaces_node: &mut fuchsia_inspect::Node, |
| ) -> Result<NewIface, zx::Status> { |
| let phy_id = req.phy_id; |
| let phy = phys.get(&req.phy_id).ok_or(zx::Status::NOT_FOUND)?; |
| |
| // Create the bootstrap channel. This channel is only used for initial communication |
| // with the USME device. |
| let (usme_bootstrap_client, usme_bootstrap_server) = |
| create_endpoints::<fidl_sme::UsmeBootstrapMarker>(); |
| let usme_bootstrap_proxy = usme_bootstrap_client.into_proxy().map_err(|e| { |
| error!("Error creating UsmeBootstrapProxy: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| |
| // Create a GenericSme channel. This channel will be used for continued communication with |
| // the USME driver and hence will be persisted. |
| let (generic_sme_client, generic_sme_server) = create_endpoints::<fidl_sme::GenericSmeMarker>(); |
| let generic_sme_proxy = generic_sme_client.into_proxy().map_err(|e| { |
| error!("Error creating GenericSmeProxy: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| |
| let legacy_privacy_support = fidl_sme::LegacyPrivacySupport { |
| wep_supported: cfg.wep_supported, |
| wpa1_supported: cfg.wpa1_supported, |
| }; |
| |
| // Pipeline the GenericSme channel and the legacy privacy support configuration, via |
| // the bootstrap channel. This request will be handled after our call to |
| // create_iface in the phy proxy, so we cannot await the result yet. |
| let bootstrap_result = usme_bootstrap_proxy.start(generic_sme_server, &legacy_privacy_support); |
| |
| // Send a CreateIfaceRequest. The vendor device will then spawn a USME device and pass |
| // the bootstrap to it. The USME device will then read the config and GenericSme channel, |
| // and continue to listen to requests via the GenericSme channel. |
| let phy_req = fidl_dev::CreateIfaceRequest { |
| role: req.role, |
| mlme_channel: Some(usme_bootstrap_server.into_channel()), |
| init_sta_addr: req.sta_addr, |
| }; |
| let phy_assigned_iface_id = phy |
| .proxy |
| .create_iface(phy_req) |
| .await |
| .map_err(move |e| { |
| error!("CreateIface failed: phy {}, fidl error {}", phy_id, e); |
| zx::Status::INTERNAL |
| })? |
| .map_err(move |e| { |
| error!("CreateIface failed: phy {}, error {}", phy_id, e); |
| zx::Status::ok(e) |
| })?; |
| |
| let inspect_vmo = std::sync::Arc::new(bootstrap_result.await.map_err(|e| { |
| error!("Failed to bootstrap USME: {}", e); |
| zx::Status::INTERNAL |
| })?); |
| |
| let iface_id = iface_counter.next_iface_id() as u16; |
| let iface_node_name = std::format!("{}", iface_id); |
| let inspect_vmo_clone = Arc::clone(&inspect_vmo); |
| let inspect_node = ifaces_node.create_lazy_child(iface_node_name, move || { |
| let inspect_vmo_inner_clone = Arc::clone(&inspect_vmo_clone); |
| async move { |
| let iface_inspector = Inspector::new( |
| fuchsia_inspect::InspectorConfig::default() |
| .vmo(inspect_vmo_inner_clone.duplicate_handle(zx::Rights::SAME_RIGHTS)?), |
| ); |
| Ok(iface_inspector) |
| } |
| .boxed() |
| }); |
| let new_iface = NewIface { |
| id: iface_id, |
| phy_ownership: device::PhyOwnership { phy_id, phy_assigned_id: phy_assigned_iface_id }, |
| generic_sme: generic_sme_proxy, |
| inspect_node: Some(std::sync::Arc::new(inspect_node)), |
| inspect_vmo: Some(inspect_vmo), |
| }; |
| new_iface_sink.unbounded_send(new_iface.clone()).map_err(|e| { |
| error!("Failed to register Generic SME event stream with internal handler: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| |
| Ok(new_iface) |
| } |
| |
| async fn query_iface( |
| ifaces: &IfaceMap, |
| id: u16, |
| ) -> Result<fidl_svc::QueryIfaceResponse, zx::Status> { |
| info!("query_iface(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| let iface_query_info = iface.generic_sme.query().await.map_err(|_| zx::Status::CANCELED)?; |
| let phy_ownership = &iface.phy_ownership; |
| Ok(fidl_svc::QueryIfaceResponse { |
| role: iface_query_info.role, |
| id, |
| phy_id: phy_ownership.phy_id, |
| phy_assigned_id: phy_ownership.phy_assigned_id, |
| sta_addr: iface_query_info.sta_addr, |
| }) |
| } |
| |
| async fn destroy_iface( |
| phys: &PhyMap, |
| ifaces: &IfaceMap, |
| _ifaces_node: fuchsia_inspect::Node, |
| id: u16, |
| ) -> Result<(), zx::Status> { |
| info!("destroy_iface(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| let phy_ownership = &iface.phy_ownership; |
| let phy = match phys.get(&phy_ownership.phy_id) { |
| Some(phy) => phy, |
| None => { |
| ifaces.remove(&id); |
| error!( |
| "Attempting to remove interface (ID: {}) from non-existent PHY (ID: {})", |
| id, phy_ownership.phy_id |
| ); |
| return Err(zx::Status::NOT_FOUND); |
| } |
| }; |
| let phy_req = fidl_dev::DestroyIfaceRequest { id: phy_ownership.phy_assigned_id }; |
| let destroy_iface_result = phy.proxy.destroy_iface(&phy_req).await.map_err(move |e| { |
| error!("Error sending 'DestroyIface' request to phy {:?}: {}", phy_ownership, e); |
| zx::Status::INTERNAL |
| })?; |
| |
| // If the removal is successful or the interface cannot be found, update the internal |
| // accounting. |
| match destroy_iface_result { |
| Ok(()) => { |
| ifaces.remove(&id); |
| zx::Status::ok(zx::sys::ZX_OK) |
| } |
| Err(status) => { |
| match status { |
| zx::sys::ZX_ERR_NOT_FOUND => { |
| if ifaces.get_snapshot().contains_key(&id) { |
| info!( |
| "Encountered NOT_FOUND while removing iface #{} potentially due to recovery.", |
| id |
| ); |
| ifaces.remove(&id); |
| } |
| } |
| _ => {} |
| } |
| zx::Status::ok(status) |
| } |
| } |
| } |
| |
| async fn get_client_sme( |
| ifaces: &IfaceMap, |
| id: u16, |
| sme_server: fidl::endpoints::ServerEnd<fidl_sme::ClientSmeMarker>, |
| ) -> Result<(), zx::Status> { |
| info!("get_client_sme(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| let result = iface.generic_sme.get_client_sme(sme_server).await.map_err(|e| { |
| error!("Failed to request client SME: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| result.map_err(|e| zx::Status::from_raw(e)) |
| } |
| |
| async fn get_ap_sme( |
| ifaces: &IfaceMap, |
| id: u16, |
| sme_server: fidl::endpoints::ServerEnd<fidl_sme::ApSmeMarker>, |
| ) -> Result<(), zx::Status> { |
| info!("get_ap_sme(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| let result = iface.generic_sme.get_ap_sme(sme_server).await.map_err(|e| { |
| error!("Failed to request AP SME: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| result.map_err(|e| zx::Status::from_raw(e)) |
| } |
| |
| async fn get_sme_telemetry( |
| ifaces: &IfaceMap, |
| id: u16, |
| telemetry_server: fidl::endpoints::ServerEnd<fidl_sme::TelemetryMarker>, |
| ) -> Result<(), zx::Status> { |
| info!("get_sme_telemetry(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| let result = iface.generic_sme.get_sme_telemetry(telemetry_server).await.map_err(|e| { |
| error!("Failed to request SME telemetry: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| result.map_err(|e| zx::Status::from_raw(e)) |
| } |
| |
| async fn get_feature_support( |
| ifaces: &IfaceMap, |
| id: u16, |
| feature_support_server: fidl::endpoints::ServerEnd<fidl_sme::FeatureSupportMarker>, |
| ) -> Result<(), zx::Status> { |
| info!("get_feature_support(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| let result = |
| iface.generic_sme.get_feature_support(feature_support_server).await.map_err(|e| { |
| error!("Failed to request feature support: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| result.map_err(|e| zx::Status::from_raw(e)) |
| } |
| |
| fn into_status_and_opt<T>(r: Result<T, zx::Status>) -> (zx::Status, Option<T>) { |
| match r { |
| Ok(x) => (zx::Status::OK, Some(x)), |
| Err(status) => (status, None), |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| crate::device::PhyOwnership, |
| fidl::endpoints::{create_proxy, create_proxy_and_stream, ControlHandle}, |
| fidl_fuchsia_wlan_common as fidl_wlan_common, fuchsia_async as fasync, |
| futures::{future::BoxFuture, task::Poll}, |
| ieee80211::{MacAddrBytes, NULL_ADDR}, |
| std::{convert::Infallible, pin::pin}, |
| test_case::test_case, |
| wlan_common::assert_variant, |
| }; |
| |
| struct TestValues { |
| monitor_proxy: fidl_svc::DeviceMonitorProxy, |
| monitor_req_stream: fidl_svc::DeviceMonitorRequestStream, |
| phys: Arc<PhyMap>, |
| ifaces: Arc<IfaceMap>, |
| watcher_service: watcher_service::WatcherService<PhyDevice, IfaceDevice>, |
| watcher_fut: BoxFuture<'static, Result<Infallible, Error>>, |
| new_iface_stream: mpsc::UnboundedReceiver<NewIface>, |
| new_iface_sink: mpsc::UnboundedSender<NewIface>, |
| iface_counter: Arc<IfaceCounter>, |
| inspector: Inspector, |
| ifaces_node: fuchsia_inspect::Node, |
| } |
| |
| fn test_setup() -> TestValues { |
| let (monitor_proxy, requests) = create_proxy::<fidl_svc::DeviceMonitorMarker>() |
| .expect("failed to create DeviceMonitor proxy"); |
| let monitor_req_stream = requests.into_stream().expect("failed to create request stream"); |
| let (phys, phy_events) = PhyMap::new(); |
| let phys = Arc::new(phys); |
| |
| let (ifaces, iface_events) = IfaceMap::new(); |
| let ifaces = Arc::new(ifaces); |
| |
| let (watcher_service, watcher_fut) = |
| watcher_service::serve_watchers(phys.clone(), ifaces.clone(), phy_events, iface_events); |
| |
| let (new_iface_sink, new_iface_stream) = mpsc::unbounded(); |
| |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| |
| let inspector = Inspector::default(); |
| let ifaces_node = inspector.root().create_child("devices"); |
| |
| TestValues { |
| monitor_proxy, |
| monitor_req_stream, |
| phys, |
| ifaces, |
| watcher_service, |
| watcher_fut: Box::pin(watcher_fut), |
| new_iface_stream: new_iface_stream, |
| new_iface_sink, |
| iface_counter, |
| inspector, |
| ifaces_node, |
| } |
| } |
| |
| fn fake_phy() -> (PhyDevice, fidl_dev::PhyRequestStream) { |
| let (proxy, server) = |
| create_proxy::<fidl_dev::PhyMarker>().expect("fake_phy: create_proxy() failed"); |
| let stream = server.into_stream().expect("fake_phy: failed to create stream"); |
| (PhyDevice { proxy, device_path: String::from("/test/path") }, stream) |
| } |
| |
| fn fake_alpha2() -> [u8; 2] { |
| let mut alpha2: [u8; 2] = [0, 0]; |
| alpha2.copy_from_slice("MX".as_bytes()); |
| alpha2 |
| } |
| |
| fn fake_wlandevicemonitor_config() -> wlandevicemonitor_config::Config { |
| wlandevicemonitor_config::Config { wep_supported: false, wpa1_supported: false } |
| } |
| |
| #[fuchsia::test] |
| fn test_list_phys() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Request the list of available PHYs. |
| let list_fut = test_values.monitor_proxy.list_phys(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the PHYs should complete and no PHYs should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut),Poll::Ready(Ok(phys)) => { |
| assert!(phys.is_empty()) |
| }); |
| |
| // Add a PHY to the PhyMap. |
| let (phy, _req_stream) = fake_phy(); |
| test_values.phys.insert(0, phy); |
| |
| // Request the list of available PHYs. |
| let list_fut = test_values.monitor_proxy.list_phys(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the PHYs should complete and the PHY should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Ready(Ok(phys)) => { |
| assert_eq!(vec![0u16], phys); |
| }); |
| |
| // Remove the PHY from the map. |
| test_values.phys.remove(&0); |
| |
| // Request the list of available PHYs. |
| let list_fut = test_values.monitor_proxy.list_phys(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the PHYs should complete and no PHYs should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Ready(Ok(phys)) => { |
| assert!(phys.is_empty()) |
| }); |
| } |
| |
| #[fuchsia::test] |
| fn test_list_ifaces() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Request the list of available ifaces. |
| let list_fut = test_values.monitor_proxy.list_ifaces(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the ifaces should complete and no ifaces should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut),Poll::Ready(Ok(ifaces)) => { |
| assert!(ifaces.is_empty()) |
| }); |
| |
| // Add a fake iface. |
| let (generic_sme, _) = |
| create_proxy::<fidl_sme::GenericSmeMarker>().expect("Failed to create generic sme"); |
| let fake_iface = IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 }, |
| generic_sme, |
| inspect_node: None, |
| inspect_vmo: None, |
| }; |
| test_values.ifaces.insert(0, fake_iface); |
| |
| // Request the list of available ifaces. |
| let list_fut = test_values.monitor_proxy.list_ifaces(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the ifaces should complete and the iface should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Ready(Ok(ifaces)) => { |
| assert_eq!(vec![0u16], ifaces); |
| }); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_dev_path_success() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, _) = fake_phy(); |
| |
| let expected_path = phy.device_path.clone(); |
| test_values.phys.insert(10u16, phy); |
| |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys, |
| test_values.ifaces, |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Initiate a GetDevPath request. The returned future should not be able |
| // to produce a result immediately |
| let query_fut = test_values.monitor_proxy.get_dev_path(10u16); |
| let mut query_fut = pin!(query_fut); |
| assert_variant!(exec.run_until_stalled(&mut query_fut), Poll::Pending); |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Our original future should complete, and return the dev path. |
| assert_variant!( |
| exec.run_until_stalled(&mut query_fut), |
| Poll::Ready(Ok(Some(path))) => { |
| assert_eq!(path, expected_path); |
| } |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_dev_path_phy_not_found() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys, |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Query a PHY's dev path. |
| let query_fut = test_values.monitor_proxy.get_dev_path(0); |
| let mut query_fut = pin!(query_fut); |
| assert_variant!(exec.run_until_stalled(&mut query_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The attempt to query the PHY's information should fail. |
| assert_variant!(exec.run_until_stalled(&mut query_fut), Poll::Ready(Ok(None))); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_mac_roles_success() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| test_values.phys.insert(10u16, phy); |
| |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys, |
| test_values.ifaces, |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Initiate a GetWlanMacRoles request. The returned future should not be able |
| // to produce a result immediately |
| let get_supported_mac_roles_fut = test_values.monitor_proxy.get_supported_mac_roles(10u16); |
| let mut get_supported_mac_roles_fut = pin!(get_supported_mac_roles_fut); |
| assert_variant!(exec.run_until_stalled(&mut get_supported_mac_roles_fut), Poll::Pending); |
| |
| // The call above should trigger a Query message to the phy. |
| // Pretend that we are the phy and read the message from the other side. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| let responder = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::GetSupportedMacRoles { responder }))) => responder |
| ); |
| |
| // Reply with a fake phy info |
| responder |
| .send(Ok(&[fidl_wlan_common::WlanMacRole::Client])) |
| .expect("failed to send QueryResponse"); |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Our original future should complete now and the client role should be reported. |
| assert_variant!( |
| exec.run_until_stalled(&mut get_supported_mac_roles_fut), |
| Poll::Ready(Ok(Ok(supported_mac_roles))) => { |
| assert_eq!(supported_mac_roles, vec![fidl_wlan_common::WlanMacRole::Client]); |
| } |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_mac_roles_phy_not_found() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys, |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Query a PHY's dev path. |
| let get_supported_mac_roles_fut = test_values.monitor_proxy.get_supported_mac_roles(0); |
| let mut get_supported_mac_roles_fut = pin!(get_supported_mac_roles_fut); |
| assert_variant!(exec.run_until_stalled(&mut get_supported_mac_roles_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The attempt to query the PHY's information should fail. |
| assert_variant!( |
| exec.run_until_stalled(&mut get_supported_mac_roles_fut), |
| Poll::Ready(Ok(Err(zx::sys::ZX_ERR_NOT_FOUND))) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_watch_devices_add_remove_phy() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| let mut watcher_fut = pin!(watcher_fut); |
| |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Watch for new devices. |
| let (watcher_proxy, watcher_server_end) = |
| fidl::endpoints::create_proxy().expect("failed to create watcher proxy"); |
| test_values |
| .monitor_proxy |
| .watch_devices(watcher_server_end) |
| .expect("failed to watch devices"); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Initially there should be no devices and the future should be pending. |
| let mut events_fut = watcher_proxy.take_event_stream(); |
| let next_fut = events_fut.try_next(); |
| let mut next_fut = pin!(next_fut); |
| assert_variant!(exec.run_until_stalled(&mut next_fut), Poll::Pending); |
| |
| // Add a PHY and make sure the update is received. |
| let (phy, _phy_stream) = fake_phy(); |
| test_values.phys.insert(0, phy); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnPhyAdded { phy_id: 0 }))) |
| ); |
| |
| // Remove the PHY and make sure the update is received. |
| test_values.phys.remove(&0); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnPhyRemoved { phy_id: 0 }))) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_watch_devices_remove_existing_phy() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| let mut watcher_fut = pin!(watcher_fut); |
| |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Add a PHY before beginning to watch for devices. |
| let (phy, _phy_stream) = fake_phy(); |
| test_values.phys.insert(0, phy); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| |
| // Watch for new devices. |
| let (watcher_proxy, watcher_server_end) = |
| fidl::endpoints::create_proxy().expect("failed to create watcher proxy"); |
| test_values |
| .monitor_proxy |
| .watch_devices(watcher_server_end) |
| .expect("failed to watch devices"); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Start listening for device events. |
| let mut events_fut = watcher_proxy.take_event_stream(); |
| let next_fut = events_fut.try_next(); |
| let mut next_fut = pin!(next_fut); |
| assert_variant!(exec.run_until_stalled(&mut next_fut), Poll::Pending); |
| |
| // We should be notified of the existing PHY. |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnPhyAdded { phy_id: 0 }))) |
| ); |
| |
| // Remove the PHY and make sure the update is received. |
| test_values.phys.remove(&0); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnPhyRemoved { phy_id: 0 }))) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_watch_devices_add_remove_iface() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| let mut watcher_fut = pin!(watcher_fut); |
| |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Watch for new devices. |
| let (watcher_proxy, watcher_server_end) = |
| fidl::endpoints::create_proxy().expect("failed to create watcher proxy"); |
| test_values |
| .monitor_proxy |
| .watch_devices(watcher_server_end) |
| .expect("failed to watch devices"); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Initially there should be no devices and the future should be pending. |
| let mut events_fut = watcher_proxy.take_event_stream(); |
| let next_fut = events_fut.try_next(); |
| let mut next_fut = pin!(next_fut); |
| assert_variant!(exec.run_until_stalled(&mut next_fut), Poll::Pending); |
| |
| // Create a generic SME proxy but drop the server since we won't use it. |
| let (generic_sme, _) = create_proxy::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy"); |
| // Add an interface and make sure the update is received. |
| let fake_iface = IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 }, |
| generic_sme, |
| inspect_node: None, |
| inspect_vmo: None, |
| }; |
| test_values.ifaces.insert(0, fake_iface); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnIfaceAdded { iface_id: 0 }))) |
| ); |
| |
| // Remove the PHY and make sure the update is received. |
| test_values.ifaces.remove(&0); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnIfaceRemoved { iface_id: 0 }))) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_watch_devices_remove_existing_iface() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| let mut watcher_fut = pin!(watcher_fut); |
| |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Create a generic SME proxy but drop the server since we won't use it. |
| let (generic_sme, _) = create_proxy::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy"); |
| // Add an interface before beginning to watch for devices. |
| let fake_iface = IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 }, |
| generic_sme, |
| inspect_node: None, |
| inspect_vmo: None, |
| }; |
| test_values.ifaces.insert(0, fake_iface); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| |
| // Watch for new devices. |
| let (watcher_proxy, watcher_server_end) = |
| fidl::endpoints::create_proxy().expect("failed to create watcher proxy"); |
| test_values |
| .monitor_proxy |
| .watch_devices(watcher_server_end) |
| .expect("failed to watch devices"); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Start listening for device events. |
| let mut events_fut = watcher_proxy.take_event_stream(); |
| let next_fut = events_fut.try_next(); |
| let mut next_fut = pin!(next_fut); |
| assert_variant!(exec.run_until_stalled(&mut next_fut), Poll::Pending); |
| |
| // We should be notified of the existing interface. |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnIfaceAdded { iface_id: 0 }))) |
| ); |
| |
| // Remove the interface and make sure the update is received. |
| test_values.ifaces.remove(&0); |
| assert_variant!(exec.run_until_stalled(&mut watcher_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut next_fut), |
| Poll::Ready(Ok(Some(fidl_svc::DeviceWatcherEvent::OnIfaceRemoved { iface_id: 0 }))) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_set_country_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let alpha2 = fake_alpha2(); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::SetCountryRequest() |
| let req_msg = fidl_svc::SetCountryRequest { phy_id, alpha2: alpha2.clone() }; |
| let req_fut = super::set_country(&test_values.phys, req_msg); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::SetCountry { req, responder }))) => { |
| assert_eq!(req.alpha2, alpha2.clone()); |
| // Pretend to be a WLAN PHY to return the result. |
| responder.send(zx::Status::OK.into_raw()) |
| .expect("failed to send the response to SetCountry"); |
| } |
| ); |
| |
| // req_fut should have completed by now. Test the result. |
| assert_eq!(exec.run_until_stalled(&mut req_fut), Poll::Ready(zx::Status::OK)); |
| } |
| |
| #[fuchsia::test] |
| fn test_set_country_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let alpha2 = fake_alpha2(); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::SetCountryRequest() |
| let req_msg = fidl_svc::SetCountryRequest { phy_id, alpha2: alpha2.clone() }; |
| let req_fut = super::set_country(&test_values.phys, req_msg); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| let (req, responder) = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::SetCountry { req, responder }))) => (req, responder) |
| ); |
| assert_eq!(req.alpha2, alpha2.clone()); |
| |
| // Failure case #1: WLAN PHY not responding |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Failure case #2: WLAN PHY has not implemented the feature. |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| let resp = zx::Status::NOT_SUPPORTED.into_raw(); |
| responder.send(resp).expect("failed to send the response to SetCountry"); |
| assert_eq!(Poll::Ready(zx::Status::NOT_SUPPORTED), exec.run_until_stalled(&mut req_fut)); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_country_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let alpha2 = fake_alpha2(); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::GetCountryRequest() |
| let req_fut = super::get_country(&test_values.phys, phy_id); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::GetCountry { responder }))) => { |
| // Pretend to be a WLAN PHY to return the result. |
| responder.send( |
| Ok(&fidl_dev::CountryCode { alpha2 }) |
| ).expect("failed to send the response to GetCountry"); |
| } |
| ); |
| |
| assert_eq!( |
| exec.run_until_stalled(&mut req_fut), |
| Poll::Ready(Ok(fidl_svc::GetCountryResponse { alpha2 })) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_country_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::GetCountryRequest() |
| let req_fut = super::get_country(&test_values.phys, phy_id); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::GetCountry { responder }))) => { |
| // Pretend to be a WLAN PHY to return the result. |
| // Right now the returned country code is not optional, so we just return garbage. |
| responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw())) |
| .expect("failed to send the response to GetCountry"); |
| } |
| ); |
| |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| |
| #[fuchsia::test] |
| fn test_clear_country_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::ClearCountryRequest() |
| let req_msg = fidl_svc::ClearCountryRequest { phy_id }; |
| let req_fut = super::clear_country(&test_values.phys, req_msg); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::ClearCountry { responder }))) => { |
| // Pretend to be a WLAN PHY to return the result. |
| responder.send(zx::Status::OK.into_raw()) |
| .expect("failed to send the response to ClearCountry"); |
| } |
| ); |
| |
| // req_fut should have completed by now. Test the result. |
| assert_eq!(exec.run_until_stalled(&mut req_fut), Poll::Ready(zx::Status::OK)); |
| } |
| |
| #[fuchsia::test] |
| fn test_clear_country_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::ClearCountryRequest() |
| let req_msg = fidl_svc::ClearCountryRequest { phy_id }; |
| let req_fut = super::clear_country(&test_values.phys, req_msg); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| let responder = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::ClearCountry { responder }))) => responder |
| ); |
| |
| // Failure case #1: WLAN PHY not responding |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Failure case #2: WLAN PHY has not implemented the feature. |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| let resp = zx::Status::NOT_SUPPORTED.into_raw(); |
| responder.send(resp).expect("failed to send the response to ClearCountry"); |
| assert_eq!(Poll::Ready(zx::Status::NOT_SUPPORTED), exec.run_until_stalled(&mut req_fut)); |
| } |
| |
| #[fuchsia::test] |
| fn test_set_power_save_mode_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::SetPowerSaveModeRequest() |
| let req_msg = fidl_svc::SetPowerSaveModeRequest { |
| phy_id, |
| ps_mode: fidl_wlan_common::PowerSaveType::PsModeBalanced, |
| }; |
| let req_fut = super::set_power_save_mode(&test_values.phys, req_msg); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::SetPowerSaveMode { req, responder }))) => { |
| assert_eq!(req, fidl_wlan_common::PowerSaveType::PsModeBalanced); |
| // Pretend to be a WLAN PHY to return the result. |
| responder.send(zx::Status::OK.into_raw()) |
| .expect("failed to send the response to SetPowerSaveModeRequest"); |
| } |
| ); |
| |
| // req_fut should have completed by now. Test the result. |
| assert_eq!(exec.run_until_stalled(&mut req_fut), Poll::Ready(zx::Status::OK)); |
| } |
| |
| #[fuchsia::test] |
| fn test_set_power_save_mode_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::SetPowerSaveModeRequest() |
| let req_msg = fidl_svc::SetPowerSaveModeRequest { |
| phy_id, |
| ps_mode: fidl_wlan_common::PowerSaveType::PsModeLowPower, |
| }; |
| let req_fut = super::set_power_save_mode(&test_values.phys, req_msg); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| let (req, responder) = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::SetPowerSaveMode { req, responder }))) => (req, responder) |
| ); |
| assert_eq!(req, fidl_wlan_common::PowerSaveType::PsModeLowPower); |
| |
| // Failure case #1: WLAN PHY not responding |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Failure case #2: WLAN PHY has not implemented the feature. |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| let resp = zx::Status::NOT_SUPPORTED.into_raw(); |
| responder.send(resp).expect("failed to send the response to SetPowerSaveMode"); |
| assert_eq!(Poll::Ready(zx::Status::NOT_SUPPORTED), exec.run_until_stalled(&mut req_fut)); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_power_save_mode_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::SetCountryRequest() |
| let req_fut = super::get_power_save_mode(&test_values.phys, phy_id); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::GetPowerSaveMode { responder }))) => { |
| // Pretend to be a WLAN PHY to return the result. |
| responder.send( |
| Ok(fidl_wlan_common::PowerSaveType::PsModePerformance) |
| ).expect("failed to send the response to SetPowerSaveMode"); |
| } |
| ); |
| |
| assert_eq!( |
| exec.run_until_stalled(&mut req_fut), |
| Poll::Ready(Ok(fidl_svc::GetPowerSaveModeResponse { |
| ps_mode: fidl_wlan_common::PowerSaveType::PsModePerformance |
| })) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_get_power_save_mode_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| // Initiate a QueryPhy request. The returned future should not be able |
| // to produce a result immediately |
| // Issue service.fidl::GetCountryRequest() |
| let req_fut = super::get_power_save_mode(&test_values.phys, phy_id); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::GetPowerSaveMode { responder }))) => { |
| // Pretend to be a WLAN PHY to return the result. |
| // Right now the returned country code is not optional, so we just return garbage. |
| responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw())) |
| .expect("failed to send the response to GetPowerSaveMode"); |
| } |
| ); |
| |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| |
| #[fuchsia::test] |
| fn test_iface_counter() { |
| let iface_counter = IfaceCounter::new(); |
| assert_eq!(0, iface_counter.next_iface_id()); |
| assert_eq!(1, iface_counter.next_iface_id()); |
| assert_eq!(2, iface_counter.next_iface_id()); |
| assert_eq!(3, iface_counter.next_iface_id()); |
| } |
| |
| #[test_case(false, false; "CreateIface without MAC succeeds")] |
| #[test_case(true, false; "CreateIface with MAC succeeds")] |
| #[test_case(false, true; "CreateIface fails on interface creation")] |
| fn test_create_iface(with_mac: bool, create_iface_fails: bool) { |
| let mut exec = fasync::TestExecutor::new(); |
| let mut test_values = test_setup(); |
| |
| let (phy, mut phy_stream) = fake_phy(); |
| test_values.phys.insert(10, phy); |
| |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| let inspector = Inspector::default(); |
| let mut ifaces_node = inspector.root().create_child("devices"); |
| |
| // Initiate a CreateIface request. The returned future should not be able |
| // to produce a result immediately |
| let create_fut = super::create_iface( |
| &test_values.new_iface_sink, |
| &test_values.phys, |
| fidl_svc::CreateIfaceRequest { |
| phy_id: 10, |
| role: fidl_wlan_common::WlanMacRole::Client, |
| sta_addr: if with_mac { [0, 1, 2, 3, 4, 5] } else { NULL_ADDR.to_array() }, |
| }, |
| &iface_counter, |
| &wlandevicemonitor_config::Config { wep_supported: true, wpa1_supported: true }, |
| &mut ifaces_node, |
| ); |
| let mut create_fut = pin!(create_fut); |
| assert_variant!(exec.run_until_stalled(&mut create_fut), Poll::Pending); |
| |
| // Validate the PHY request |
| let bootstrap_channel = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::CreateIface { req, responder }))) => { |
| assert_eq!(fidl_wlan_common::WlanMacRole::Client, req.role); |
| |
| if with_mac { |
| assert_eq!(req.init_sta_addr, [0, 1, 2, 3, 4, 5]); |
| } |
| |
| if create_iface_fails { |
| responder.send(Err(zx::sys::ZX_ERR_NOT_SUPPORTED)).expect("failed to send iface_id"); |
| } else { |
| responder.send(Ok(123)).expect("failed to send iface id"); |
| }; |
| |
| req.mlme_channel |
| } |
| ); |
| |
| // If this case should fail on interface creation, the future should complete here with an |
| // error. |
| if create_iface_fails { |
| assert_variant!( |
| exec.run_until_stalled(&mut create_fut), |
| Poll::Ready(Err(zx::Status::NOT_SUPPORTED)) |
| ); |
| return; |
| } |
| |
| if let Some(channel) = bootstrap_channel { |
| let bootstrap_server = |
| fidl::endpoints::ServerEnd::<fidl_sme::UsmeBootstrapMarker>::new(channel); |
| let mut bootstrap_stream = |
| bootstrap_server.into_stream().expect("Failed to make bootstrap stream"); |
| |
| let responder = assert_variant!( |
| exec.run_until_stalled(&mut bootstrap_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::UsmeBootstrapRequest::Start { |
| responder, .. |
| }))) => responder); |
| responder |
| .send(inspector.duplicate_vmo().unwrap()) |
| .expect("Failed to send bootstrap result"); |
| } |
| |
| // The original future should resolve into a response. |
| assert_variant!(exec.run_until_stalled(&mut create_fut), |
| Poll::Ready(response) => { |
| let response = response.expect("CreateIface failed unexpectedly"); |
| assert_eq!(0, response.id); |
| assert_eq!( |
| device::PhyOwnership { phy_id: 10, phy_assigned_id: 123 }, |
| response.phy_ownership |
| ); |
| } |
| ); |
| |
| // The new interface should be pushed into the new iface stream. |
| assert_variant!( |
| exec.run_until_stalled(&mut test_values.new_iface_stream.next()), |
| Poll::Ready(Some(NewIface { .. })), |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn test_create_multiple_ifaces() { |
| let mut exec = fasync::TestExecutor::new(); |
| let mut test_values = test_setup(); |
| let service_fut = serve_monitor_requests( |
| test_values.monitor_req_stream, |
| test_values.phys.clone(), |
| test_values.ifaces, |
| test_values.watcher_service, |
| test_values.new_iface_sink, |
| test_values.iface_counter, |
| test_values.ifaces_node, |
| fake_wlandevicemonitor_config(), |
| ); |
| let mut service_fut = pin!(service_fut); |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Request the list of available ifaces. |
| let list_fut = test_values.monitor_proxy.list_ifaces(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the ifaces should complete and no ifaces should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut),Poll::Ready(Ok(ifaces)) => { |
| assert!(ifaces.is_empty()) |
| }); |
| |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10; |
| test_values.phys.insert(phy_id, phy); |
| |
| for (sta_addr, phy_assigned_iface_id, sme_assigned_iface_id) in |
| [([0x0, 0x1, 0x2, 0x3, 0x4, 0x5], 123, 0), ([0x6, 0x7, 0x8, 0x9, 0xa, 0xb], 0x123, 1)] |
| { |
| let create_iface_fut = |
| test_values.monitor_proxy.create_iface(&fidl_svc::CreateIfaceRequest { |
| phy_id, |
| role: fidl_wlan_common::WlanMacRole::Client, |
| sta_addr, |
| }); |
| let mut create_iface_fut = pin!(create_iface_fut); |
| assert_variant!(exec.run_until_stalled(&mut create_iface_fut), Poll::Pending); |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // Validate the PHY request |
| let bootstrap_channel = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::CreateIface { req, responder }))) => { |
| assert_eq!(fidl_wlan_common::WlanMacRole::Client, req.role); |
| assert_eq!(req.init_sta_addr, sta_addr); |
| responder.send(Ok(phy_assigned_iface_id)).expect("failed to send iface id"); |
| req.mlme_channel.expect("no MLME channel") |
| }); |
| |
| // Complete the USME bootstrap. |
| let mut bootstrap_stream = |
| fidl::endpoints::ServerEnd::<fidl_sme::UsmeBootstrapMarker>::new(bootstrap_channel) |
| .into_stream() |
| .expect("Failed to make bootstrap stream"); |
| assert_variant!( |
| exec.run_until_stalled(&mut bootstrap_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::UsmeBootstrapRequest::Start { |
| responder, .. |
| }))) => responder |
| .send(test_values.inspector.duplicate_vmo().unwrap()) |
| .expect("Failed to send bootstrap result")); |
| |
| // The original future should resolve into a response. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| assert_variant!(exec.run_until_stalled(&mut create_iface_fut), |
| Poll::Ready(Ok((zx::sys::ZX_OK, Some(response)))) => { |
| assert_eq!(sme_assigned_iface_id, response.iface_id); |
| }); |
| |
| assert_variant!( |
| exec.run_until_stalled(&mut test_values.new_iface_stream.next()), |
| Poll::Ready(Some(NewIface { .. })), |
| ); |
| } |
| |
| // Request the list of available ifaces. |
| let list_fut = test_values.monitor_proxy.list_ifaces(); |
| let mut list_fut = pin!(list_fut); |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Pending); |
| |
| // Progress the service loop. |
| assert_variant!(exec.run_until_stalled(&mut service_fut), Poll::Pending); |
| |
| // The future to list the ifaces should complete and the iface should be present. |
| assert_variant!(exec.run_until_stalled(&mut list_fut), Poll::Ready(Ok(mut ifaces)) => { |
| ifaces.sort(); |
| assert_eq!(vec![0, 1], ifaces); |
| }); |
| } |
| |
| #[fuchsia::test] |
| fn create_iface_on_invalid_phy_id() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| let inspector = Inspector::default(); |
| let mut ifaces_node = inspector.root().create_child("devices"); |
| |
| let fut = super::create_iface( |
| &test_values.new_iface_sink, |
| &test_values.phys, |
| fidl_svc::CreateIfaceRequest { |
| phy_id: 10, |
| role: fidl_wlan_common::WlanMacRole::Client, |
| sta_addr: NULL_ADDR.to_array(), |
| }, |
| &iface_counter, |
| &wlandevicemonitor_config::Config { wep_supported: true, wpa1_supported: true }, |
| &mut ifaces_node, |
| ); |
| let mut fut = pin!(fut); |
| assert_variant!( |
| exec.run_until_stalled(&mut fut), |
| Poll::Ready(Err(zx::Status::NOT_FOUND)), |
| "expected failure on invalid PHY" |
| ); |
| } |
| |
| fn fake_destroy_iface_env( |
| phy_map: &PhyMap, |
| iface_map: &IfaceMap, |
| ) -> fidl_dev::PhyRequestStream { |
| let (phy, phy_stream) = fake_phy(); |
| phy_map.insert(10, phy); |
| // Create a generic SME proxy but drop the server since we won't use it. |
| let (proxy, _) = create_proxy::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy"); |
| iface_map.insert( |
| 42, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 10, phy_assigned_id: 0 }, |
| generic_sme: proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| phy_stream |
| } |
| |
| #[fuchsia::test] |
| fn destroy_iface_success() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let mut phy_stream = fake_destroy_iface_env(&test_values.phys, &test_values.ifaces); |
| |
| let destroy_fut = super::destroy_iface( |
| &test_values.phys, |
| &test_values.ifaces, |
| test_values.ifaces_node, |
| 42, |
| ); |
| let mut destroy_fut = pin!(destroy_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut destroy_fut)); |
| |
| let (req, responder) = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::DestroyIface { req, responder }))) => (req, responder) |
| ); |
| |
| // Verify the destroy iface request to the corresponding PHY is correct. |
| assert_eq!(0, req.id); |
| |
| responder.send(Ok(())).expect("failed to send DestroyIfaceResponse"); |
| assert_eq!(Poll::Ready(Ok(())), exec.run_until_stalled(&mut destroy_fut)); |
| |
| // Verify iface was removed from available ifaces. |
| assert!(test_values.ifaces.get(&42u16).is_none(), "iface expected to be deleted"); |
| } |
| |
| #[fuchsia::test] |
| fn destroy_iface_failure() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let mut phy_stream = fake_destroy_iface_env(&test_values.phys, &test_values.ifaces); |
| |
| let destroy_fut = super::destroy_iface( |
| &test_values.phys, |
| &test_values.ifaces, |
| test_values.ifaces_node, |
| 42, |
| ); |
| let mut destroy_fut = pin!(destroy_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut destroy_fut)); |
| |
| let (req, responder) = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::DestroyIface { req, responder }))) => (req, responder) |
| ); |
| |
| // Verify the destroy iface request to the corresponding PHY is correct. |
| assert_eq!(0, req.id); |
| |
| responder.send(Err(zx::sys::ZX_ERR_INTERNAL)).expect("failed to send DestroyIfaceResponse"); |
| assert_eq!( |
| Poll::Ready(Err(zx::Status::INTERNAL)), |
| exec.run_until_stalled(&mut destroy_fut) |
| ); |
| |
| // Verify iface was not removed from available ifaces. |
| assert!(test_values.ifaces.get(&42u16).is_some(), "iface expected to not be deleted"); |
| } |
| |
| #[fuchsia::test] |
| fn destroy_iface_recovery() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let mut phy_stream = fake_destroy_iface_env(&test_values.phys, &test_values.ifaces); |
| |
| let destroy_fut = super::destroy_iface( |
| &test_values.phys, |
| &test_values.ifaces, |
| test_values.ifaces_node, |
| 42, |
| ); |
| let mut destroy_fut = pin!(destroy_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut destroy_fut)); |
| |
| let (req, responder) = assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::DestroyIface { req, responder }))) => (req, responder) |
| ); |
| |
| // Verify the destroy iface request to the corresponding PHY is correct. |
| assert_eq!(0, req.id); |
| |
| // In the recovery scenario, the interface will have already been destroyed and the PHY |
| // will have no record of it and will reply to the destruction request with a |
| // ZX_ERR_NOT_FOUND. In this case, we should verify that the internal accounting is still |
| // updated. |
| responder |
| .send(Err(zx::sys::ZX_ERR_NOT_FOUND)) |
| .expect("failed to send DestroyIfaceResponse"); |
| assert_eq!( |
| Poll::Ready(Err(zx::Status::NOT_FOUND)), |
| exec.run_until_stalled(&mut destroy_fut) |
| ); |
| |
| // Verify iface was removed from available ifaces. |
| assert!(test_values.ifaces.get(&42u16).is_none(), "iface should have been removed."); |
| } |
| |
| #[fuchsia::test] |
| fn destroy_iface_not_found() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let _phy_stream = fake_destroy_iface_env(&test_values.phys, &test_values.ifaces); |
| |
| let fut = super::destroy_iface( |
| &test_values.phys, |
| &test_values.ifaces, |
| test_values.ifaces_node, |
| 43, |
| ); |
| let mut fut = pin!(fut); |
| assert_eq!(Poll::Ready(Err(zx::Status::NOT_FOUND)), exec.run_until_stalled(&mut fut)); |
| } |
| |
| #[fuchsia::test] |
| fn destroy_iface_phy_not_found() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| |
| // Set the PHY ID of the interface to be some non-existent PHY ID. |
| let (proxy, _) = create_proxy::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy"); |
| test_values.ifaces.insert( |
| 1, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 0, phy_assigned_id: 0 }, |
| generic_sme: proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| // Destroy the interface that does not have a parent PHY ID. |
| let fut = super::destroy_iface( |
| &test_values.phys, |
| &test_values.ifaces, |
| test_values.ifaces_node, |
| 1, |
| ); |
| let mut fut = pin!(fut); |
| assert_eq!(Poll::Ready(Err(zx::Status::NOT_FOUND)), exec.run_until_stalled(&mut fut)); |
| |
| assert!(test_values.ifaces.get(&1).is_none()); |
| } |
| |
| #[fuchsia::test] |
| fn get_client_sme() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, _phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let (generic_sme_proxy, mut generic_sme_stream) = |
| create_proxy_and_stream::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy and stream"); |
| |
| test_values.ifaces.insert( |
| 42, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 10, phy_assigned_id: 0 }, |
| generic_sme: generic_sme_proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| let (client_sme_proxy, client_sme_server) = |
| create_proxy::<fidl_sme::ClientSmeMarker>().expect("Failed to create client SME"); |
| |
| let req_fut = super::get_client_sme(&test_values.ifaces, 42, client_sme_server); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Respond to a client SME request with a client endpoint. |
| let sme_server = assert_variant!(exec.run_until_stalled(&mut generic_sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::GenericSmeRequest::GetClientSme { sme_server, responder }))) => { |
| responder.send(Ok(())).expect("Failed to send response"); |
| sme_server |
| } |
| ); |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Ok(()))); |
| |
| // Verify that the correct endpoint is served. |
| let _status_req = client_sme_proxy.status(); |
| |
| let mut sme_stream = sme_server.into_stream().expect("Failed to get client SME stream"); |
| assert_variant!( |
| exec.run_until_stalled(&mut sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { .. }))) |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn get_client_sme_fails() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, _phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let (generic_sme_proxy, generic_sme_server) = create_proxy::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy"); |
| |
| test_values.ifaces.insert( |
| 42, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 10, phy_assigned_id: 0 }, |
| generic_sme: generic_sme_proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| let (_client_sme_proxy, client_sme_server) = |
| create_proxy::<fidl_sme::ClientSmeMarker>().expect("Failed to create client SME"); |
| |
| let req_fut = super::get_client_sme(&test_values.ifaces, 42, client_sme_server); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Respond to a client SME request with an error. |
| let mut generic_sme_stream = |
| generic_sme_server.into_stream().expect("Failed to create generic SME stream"); |
| assert_variant!(exec.run_until_stalled(&mut generic_sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::GenericSmeRequest::GetClientSme { responder, .. }))) => { |
| responder.send(Err(1)).expect("Failed to send response"); |
| } |
| ); |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| |
| #[fuchsia::test] |
| fn get_client_sme_invalid_iface() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, _phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let (generic_sme_proxy, _generic_sme_server) = create_proxy::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy"); |
| |
| test_values.ifaces.insert( |
| 42, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 10, phy_assigned_id: 0 }, |
| generic_sme: generic_sme_proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| let (_client_sme_proxy, client_sme_server) = |
| create_proxy::<fidl_sme::ClientSmeMarker>().expect("Failed to create client SME"); |
| |
| let req_fut = super::get_client_sme(&test_values.ifaces, 1337, client_sme_server); |
| let mut req_fut = pin!(req_fut); |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| |
| #[fuchsia::test] |
| fn get_feature_support() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, _phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let (generic_sme_proxy, mut generic_sme_stream) = |
| create_proxy_and_stream::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy and stream"); |
| |
| let (_feature_support_proxy, feature_support_server) = |
| create_proxy::<fidl_sme::FeatureSupportMarker>().expect("Failed to create client SME"); |
| |
| test_values.ifaces.insert( |
| 42, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 10, phy_assigned_id: 0 }, |
| generic_sme: generic_sme_proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| let req_fut = super::get_feature_support(&test_values.ifaces, 42, feature_support_server); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Respond to a feature support request with a feature support endpoint. |
| assert_variant!(exec.run_until_stalled(&mut generic_sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::GenericSmeRequest::GetFeatureSupport { responder, .. }))) => { |
| responder.send(Ok(())).expect("Failed to send response"); |
| } |
| ); |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Ok(_))); |
| } |
| |
| #[fuchsia::test] |
| fn query_iface() { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let (phy, _phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| let (generic_sme_proxy, mut generic_sme_stream) = |
| create_proxy_and_stream::<fidl_sme::GenericSmeMarker>() |
| .expect("Failed to create generic SME proxy and stream"); |
| |
| test_values.ifaces.insert( |
| 42, |
| device::IfaceDevice { |
| phy_ownership: PhyOwnership { phy_id: 10, phy_assigned_id: 0 }, |
| generic_sme: generic_sme_proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| let req_fut = super::query_iface(&test_values.ifaces, 42); |
| let mut req_fut = pin!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| // Respond to a query with appropriate info. |
| assert_variant!( |
| exec.run_until_stalled(&mut generic_sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::GenericSmeRequest::Query { responder, .. }))) => { |
| responder.send(&fidl_sme::GenericSmeQuery { |
| role: fidl_common::WlanMacRole::Client, |
| sta_addr: [2; 6], |
| }).expect("Failed to send query response"); |
| } |
| ); |
| let resp = |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Ok(resp)) => resp); |
| assert_eq!( |
| resp, |
| fidl_svc::QueryIfaceResponse { |
| role: fidl_fuchsia_wlan_common::WlanMacRole::Client, |
| id: 42, |
| phy_id: 10, |
| phy_assigned_id: 0, |
| sta_addr: [2; 6], |
| } |
| ); |
| } |
| |
| #[test_case(zx::Status::OK, false; "Generic SME with OK epitaph shuts down cleanly")] |
| #[test_case(zx::Status::INTERNAL, true; "Generic SME with error epitaph initiates iface removal")] |
| fn new_iface_stream_epitaph(epitaph: zx::Status, expect_destroy_iface: bool) { |
| let mut exec = fasync::TestExecutor::new(); |
| let test_values = test_setup(); |
| let new_iface_fut = handle_new_iface_stream( |
| test_values.phys.clone(), |
| test_values.ifaces.clone(), |
| test_values.ifaces_node, |
| test_values.new_iface_stream, |
| ); |
| let mut new_iface_fut = pin!(new_iface_fut); |
| |
| let (phy, mut phy_stream) = fake_phy(); |
| let phy_id = 10u16; |
| test_values.phys.insert(phy_id, phy); |
| |
| let id = 42; |
| let phy_ownership = PhyOwnership { phy_id, phy_assigned_id: 0 }; |
| |
| let (generic_sme_proxy, generic_sme_server) = |
| create_proxy::<fidl_sme::GenericSmeMarker>().expect("Failed to make generic SME"); |
| |
| test_values.ifaces.insert( |
| id, |
| device::IfaceDevice { |
| phy_ownership: phy_ownership.clone(), |
| generic_sme: generic_sme_proxy.clone(), |
| inspect_node: None, |
| inspect_vmo: None, |
| }, |
| ); |
| |
| let new_iface = NewIface { |
| id, |
| phy_ownership, |
| generic_sme: generic_sme_proxy, |
| inspect_node: None, |
| inspect_vmo: None, |
| }; |
| test_values.new_iface_sink.unbounded_send(new_iface).expect("Failed to send new iface"); |
| assert_variant!(exec.run_until_stalled(&mut new_iface_fut), Poll::Pending); |
| |
| let (_generic_sme_stream, generic_sme_control) = generic_sme_server |
| .into_stream_and_control_handle() |
| .expect("Failed to get generic SME stream and control handle"); |
| generic_sme_control.shutdown_with_epitaph(epitaph); |
| |
| assert_variant!(exec.run_until_stalled(&mut new_iface_fut), Poll::Pending); |
| |
| if expect_destroy_iface { |
| assert_variant!( |
| exec.run_until_stalled(&mut phy_stream.next()), |
| Poll::Ready(Some(Ok(fidl_dev::PhyRequest::DestroyIface { .. }))) |
| ); |
| } else { |
| assert_variant!(exec.run_until_stalled(&mut phy_stream.next()), Poll::Pending); |
| } |
| } |
| } |