| // 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}, |
| fidl::endpoints::create_endpoints, |
| 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_mlme as fidl_mlme, fidl_fuchsia_wlan_sme as fidl_sme, fuchsia_zircon as zx, |
| futures::TryStreamExt, |
| log::{error, info}, |
| std::sync::Arc, |
| }; |
| |
| 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>, |
| dev_svc: fidl_svc::DeviceServiceProxy, |
| ) -> Result<(), Error> { |
| while let Some(req) = req_stream.try_next().await.context("error running DeviceService")? { |
| match req { |
| DeviceMonitorRequest::ListPhys { responder } => responder.send(&mut list_phys(&phys)), |
| DeviceMonitorRequest::GetDevPath { phy_id, responder } => { |
| responder.send(get_dev_path(&phys, phy_id).as_deref()) |
| } |
| DeviceMonitorRequest::GetSupportedMacRoles { phy_id, responder } => { |
| responder.send(&mut get_supported_mac_roles(&phys, phy_id).await) |
| } |
| 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(&mut get_country(&phys, phy_id).await.map_err(|status| status.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::SetPsMode { req, responder } => { |
| let status = set_ps_mode(&phys, req).await; |
| responder.send(status.into_raw()) |
| } |
| DeviceMonitorRequest::GetPsMode { phy_id, responder } => responder |
| .send(&mut get_ps_mode(&phys, phy_id).await.map_err(|status| status.into_raw())), |
| DeviceMonitorRequest::CreateIface { req, responder } => { |
| match create_iface(&dev_svc, &phys, req).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, |
| }, |
| ); |
| |
| let resp = fidl_svc::CreateIfaceResponse { iface_id: new_iface.id }; |
| responder.send(zx::sys::ZX_OK, Some(resp).as_mut()) |
| } |
| Err(status) => responder.send(status.into_raw(), None), |
| } |
| } |
| DeviceMonitorRequest::DestroyIface { req, responder } => { |
| let result = destroy_iface(&phys, &ifaces, req.iface_id).await; |
| let status = into_status_and_opt(result).0; |
| responder.send(status.into_raw()) |
| } |
| DeviceMonitorRequest::GetClientSme { iface_id, responder } => { |
| let result = get_client_sme(&ifaces, iface_id).await; |
| responder.send(&mut result.map_err(|e| e.into_raw())) |
| } |
| DeviceMonitorRequest::GetApSme { iface_id, responder } => { |
| let result = get_ap_sme(&ifaces, iface_id).await; |
| responder.send(&mut result.map_err(|e| e.into_raw())) |
| } |
| DeviceMonitorRequest::GetSmeTelemetry { iface_id, responder } => { |
| let result = get_sme_telemetry(&ifaces, iface_id).await; |
| responder.send(&mut result.map_err(|e| e.into_raw())) |
| } |
| }?; |
| } |
| |
| Ok(()) |
| } |
| |
| fn list_phys(phys: &PhyMap) -> Vec<u16> { |
| phys.get_snapshot().iter().map(|(phy_id, _)| *phy_id).collect() |
| } |
| |
| fn get_dev_path(phys: &PhyMap, phy_id: u16) -> Option<String> { |
| let phy = phys.get(&phy_id)?; |
| Some(phy.device.path().to_string_lossy().to_string()) |
| } |
| |
| 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 mut phy_req = fidl_dev::CountryCode { alpha2: req.alpha2 }; |
| match phy.proxy.set_country(&mut 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_ps_mode(phys: &PhyMap, req: fidl_svc::SetPsModeRequest) -> 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_ps_mode(phy_req).await { |
| Ok(status) => zx::Status::from_raw(status), |
| Err(e) => { |
| error!("Error sending SetPsModeRequest request to phy #{}: {}", phy_id, e); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| async fn get_ps_mode( |
| phys: &PhyMap, |
| phy_id: u16, |
| ) -> Result<fidl_svc::GetPsModeResponse, zx::Status> { |
| let phy = phys.get(&phy_id).ok_or(Err(zx::Status::NOT_FOUND))?; |
| match phy.proxy.get_ps_mode().await { |
| Ok(result) => match result { |
| Ok(resp) => Ok(fidl_svc::GetPsModeResponse { ps_mode: resp }), |
| Err(status) => Err(zx::Status::from_raw(status)), |
| }, |
| Err(e) => { |
| error!("Error sending 'GetPsMode' 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( |
| dev_svc: &fidl_svc::DeviceServiceProxy, |
| phys: &PhyMap, |
| req: fidl_svc::CreateIfaceRequest, |
| ) -> Result<NewIface, zx::Status> { |
| let phy_id = req.phy_id; |
| let phy = phys.get(&req.phy_id).ok_or(zx::Status::NOT_FOUND)?; |
| |
| let (mlme_client, mlme_server) = create_endpoints::<fidl_mlme::MlmeMarker>() |
| .map_err(|e| { |
| error!("failed to create MlmeProxy: {}", e); |
| zx::Status::INTERNAL |
| }) |
| .map(|(p, c)| (p, Some(c.into_channel())))?; |
| |
| let mut phy_req = fidl_dev::CreateIfaceRequest { |
| role: req.role, |
| mlme_channel: mlme_server, |
| init_sta_addr: req.sta_addr, |
| }; |
| let r = phy.proxy.create_iface(&mut phy_req).await.map_err(move |e| { |
| error!("Error sending 'CreateIface' request to phy #{}: {}", phy_id, e); |
| zx::Status::INTERNAL |
| })?; |
| zx::Status::ok(r.status)?; |
| |
| let (generic_sme_client, generic_sme_server) = create_endpoints::<fidl_sme::GenericSmeMarker>() |
| .map_err(|e| { |
| error!("failed to create GenericSmeProxy: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| let generic_sme_proxy = generic_sme_client.into_proxy().map_err(|e| { |
| error!("Error creating GenericSmeProxy: {}", e); |
| zx::Status::INTERNAL |
| })?; |
| |
| let (status, iface_id) = dev_svc |
| .add_iface(&mut fidl_svc::AddIfaceRequest { |
| phy_id, |
| assigned_iface_id: r.iface_id, |
| iface: mlme_client, |
| generic_sme: generic_sme_server, |
| }) |
| .await |
| .map_err(|e| { |
| error!("failed to add interfaces to DeviceService: {:?}", e); |
| zx::Status::INTERNAL |
| })?; |
| zx::Status::ok(status)?; |
| let added_iface = iface_id.ok_or(zx::Status::INTERNAL)?; |
| |
| Ok(NewIface { |
| id: added_iface.iface_id, |
| phy_ownership: device::PhyOwnership { phy_id, phy_assigned_id: r.iface_id }, |
| generic_sme: generic_sme_proxy, |
| }) |
| } |
| |
| async fn destroy_iface(phys: &PhyMap, ifaces: &IfaceMap, 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 = phys.get(&phy_ownership.phy_id).ok_or(zx::Status::NOT_FOUND)?; |
| let mut phy_req = fidl_dev::DestroyIfaceRequest { id: phy_ownership.phy_assigned_id }; |
| let r = phy.proxy.destroy_iface(&mut 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 r.status { |
| zx::sys::ZX_OK => ifaces.remove(&id), |
| 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(r.status) |
| } |
| |
| async fn get_client_sme( |
| ifaces: &IfaceMap, |
| id: u16, |
| ) -> Result<fidl::endpoints::ClientEnd<fidl_sme::ClientSmeMarker>, 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().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, |
| ) -> Result<fidl::endpoints::ClientEnd<fidl_sme::ApSmeMarker>, 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().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, |
| ) -> Result<fidl::endpoints::ClientEnd<fidl_sme::TelemetryMarker>, 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().await.map_err(|e| { |
| error!("Failed to request SME telemetry: {}", 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}, |
| fidl_fuchsia_wlan_common as fidl_wlan_common, fuchsia_async as fasync, |
| futures::{future::BoxFuture, task::Poll, StreamExt}, |
| ieee80211::NULL_MAC_ADDR, |
| pin_utils::pin_mut, |
| std::fs::File, |
| tempfile, |
| test_case::test_case, |
| void::Void, |
| wlan_common::assert_variant, |
| }; |
| |
| struct TestValues { |
| monitor_proxy: fidl_svc::DeviceMonitorProxy, |
| monitor_req_stream: fidl_svc::DeviceMonitorRequestStream, |
| dev_proxy: fidl_svc::DeviceServiceProxy, |
| #[allow(unused)] |
| dev_req_stream: fidl_svc::DeviceServiceRequestStream, |
| phys: Arc<PhyMap>, |
| ifaces: Arc<IfaceMap>, |
| watcher_service: watcher_service::WatcherService<PhyDevice, IfaceDevice>, |
| watcher_fut: BoxFuture<'static, Result<Void, Error>>, |
| } |
| |
| 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 (dev_proxy, requests) = create_proxy::<fidl_svc::DeviceServiceMarker>() |
| .expect("failed to create DeviceMonitor proxy"); |
| let dev_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); |
| |
| TestValues { |
| monitor_proxy, |
| monitor_req_stream, |
| dev_proxy, |
| dev_req_stream, |
| phys, |
| ifaces, |
| watcher_service, |
| watcher_fut: Box::pin(watcher_fut), |
| } |
| } |
| |
| fn fake_phy() -> (PhyDevice, fidl_dev::PhyRequestStream) { |
| let (proxy, server) = |
| create_proxy::<fidl_dev::PhyMarker>().expect("fake_phy: create_proxy() failed"); |
| let temp_dir = tempfile::TempDir::new().expect("failed to create temp dir"); |
| let test_path = temp_dir.path().join("test"); |
| let file = File::create(test_path.clone()).expect("failed to open file"); |
| let device = wlan_dev::Device { node: file, path: test_path }; |
| let stream = server.into_stream().expect("fake_phy: failed to create stream"); |
| (PhyDevice { proxy, device }, stream) |
| } |
| |
| fn fake_alpha2() -> [u8; 2] { |
| let mut alpha2: [u8; 2] = [0, 0]; |
| alpha2.copy_from_slice("MX".as_bytes()); |
| alpha2 |
| } |
| |
| #[test] |
| fn test_list_phys() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| 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.dev_proxy, |
| ); |
| pin_mut!(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(); |
| pin_mut!(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(); |
| pin_mut!(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(); |
| pin_mut!(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()) |
| }); |
| } |
| |
| #[test] |
| fn test_get_dev_path_success() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let test_values = test_setup(); |
| let (phy, _) = fake_phy(); |
| |
| let expected_path = phy |
| .device |
| .path |
| .as_path() |
| .as_os_str() |
| .to_os_string() |
| .into_string() |
| .expect("could not convert path to string."); |
| |
| 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.dev_proxy, |
| ); |
| pin_mut!(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); |
| pin_mut!(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); |
| } |
| ); |
| } |
| |
| #[test] |
| fn test_get_dev_path_phy_not_found() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| 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.dev_proxy, |
| ); |
| pin_mut!(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); |
| pin_mut!(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))); |
| } |
| |
| #[test] |
| fn test_get_mac_roles_success() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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.dev_proxy, |
| ); |
| pin_mut!(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); |
| pin_mut!(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(&mut Ok(vec![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]); |
| } |
| ); |
| } |
| |
| #[test] |
| fn test_get_mac_roles_phy_not_found() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| 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.dev_proxy, |
| ); |
| pin_mut!(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); |
| pin_mut!(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))) |
| ); |
| } |
| |
| #[test] |
| fn test_watch_devices_add_remove_phy() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| pin_mut!(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.dev_proxy, |
| ); |
| pin_mut!(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(); |
| pin_mut!(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 }))) |
| ); |
| } |
| |
| #[test] |
| fn test_watch_devices_remove_existing_phy() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| pin_mut!(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.dev_proxy, |
| ); |
| pin_mut!(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(); |
| pin_mut!(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 }))) |
| ); |
| } |
| |
| #[test] |
| fn test_watch_devices_add_remove_iface() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| pin_mut!(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.dev_proxy, |
| ); |
| pin_mut!(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(); |
| pin_mut!(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, |
| }; |
| 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 }))) |
| ); |
| } |
| |
| #[test] |
| fn test_watch_devices_remove_existing_iface() { |
| let mut exec = fasync::TestExecutor::new().expect("failed to create an executor"); |
| let test_values = test_setup(); |
| let watcher_fut = test_values.watcher_fut; |
| pin_mut!(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.dev_proxy, |
| ); |
| pin_mut!(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, |
| }; |
| 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(); |
| pin_mut!(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 }))) |
| ); |
| } |
| |
| #[test] |
| fn test_set_country_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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); |
| pin_mut!(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)); |
| } |
| |
| #[test] |
| fn test_set_country_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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); |
| pin_mut!(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)); |
| } |
| |
| #[test] |
| fn test_get_country_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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); |
| pin_mut!(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( |
| &mut 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 })) |
| ); |
| } |
| |
| #[test] |
| fn test_get_country_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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); |
| pin_mut!(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(&mut 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(_))); |
| } |
| |
| #[test] |
| fn test_clear_country_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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); |
| pin_mut!(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)); |
| } |
| |
| #[test] |
| fn test_clear_country_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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); |
| pin_mut!(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)); |
| } |
| |
| #[test] |
| fn test_set_ps_mode_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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::SetPsModeRequest() |
| let req_msg = fidl_svc::SetPsModeRequest { |
| phy_id, |
| ps_mode: fidl_wlan_common::PowerSaveType::PsModeBalanced, |
| }; |
| let req_fut = super::set_ps_mode(&test_values.phys, req_msg); |
| pin_mut!(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::SetPsMode { 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 SetPsModeRequest"); |
| } |
| ); |
| |
| // req_fut should have completed by now. Test the result. |
| assert_eq!(exec.run_until_stalled(&mut req_fut), Poll::Ready(zx::Status::OK)); |
| } |
| |
| #[test] |
| fn test_set_ps_mode_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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::SetPsModeRequest() |
| let req_msg = fidl_svc::SetPsModeRequest { |
| phy_id, |
| ps_mode: fidl_wlan_common::PowerSaveType::PsModeLowPower, |
| }; |
| let req_fut = super::set_ps_mode(&test_values.phys, req_msg); |
| pin_mut!(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::SetPsMode { 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 SetPsMode"); |
| assert_eq!(Poll::Ready(zx::Status::NOT_SUPPORTED), exec.run_until_stalled(&mut req_fut)); |
| } |
| |
| #[test] |
| fn test_get_ps_mode_succeeds() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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_ps_mode(&test_values.phys, phy_id); |
| pin_mut!(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::GetPsMode { responder }))) => { |
| // Pretend to be a WLAN PHY to return the result. |
| responder.send( |
| &mut Ok(fidl_wlan_common::PowerSaveType::PsModePerformance) |
| ).expect("failed to send the response to SetPsMode"); |
| } |
| ); |
| |
| assert_eq!( |
| exec.run_until_stalled(&mut req_fut), |
| Poll::Ready(Ok(fidl_svc::GetPsModeResponse { |
| ps_mode: fidl_wlan_common::PowerSaveType::PsModePerformance |
| })) |
| ); |
| } |
| |
| #[test] |
| fn test_get_ps_mode_fails() { |
| // Setup environment |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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_ps_mode(&test_values.phys, phy_id); |
| pin_mut!(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::GetPsMode { 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(&mut Err(zx::Status::NOT_SUPPORTED.into_raw())) |
| .expect("failed to send the response to GetPsMode"); |
| } |
| ); |
| |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| |
| #[test_case(false, false, false; "CreateIface without MACsucceeds")] |
| #[test_case(true, false, false; "CreateIface with MAC succeeds")] |
| #[test_case(false, true, false; "CreateIface fails on interface creation")] |
| #[test_case(false, false, true; "CreateIface fails on interface addition")] |
| fn test_create_iface(with_mac: bool, create_iface_fails: bool, add_iface_fails: bool) { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let mut test_values = test_setup(); |
| |
| let (phy, mut phy_stream) = fake_phy(); |
| test_values.phys.insert(10, phy); |
| |
| // Initiate a CreateIface request. The returned future should not be able |
| // to produce a result immediately |
| let create_fut = super::create_iface( |
| &test_values.dev_proxy, |
| &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_MAC_ADDR }, |
| }, |
| ); |
| pin_mut!(create_fut); |
| assert_variant!(exec.run_until_stalled(&mut create_fut), Poll::Pending); |
| |
| // Validate the PHY request |
| 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]); |
| } |
| |
| let mut response = if create_iface_fails { |
| fidl_dev::CreateIfaceResponse { status: zx::sys::ZX_ERR_NOT_SUPPORTED, iface_id: 0 } |
| } else { |
| fidl_dev::CreateIfaceResponse { status: zx::sys::ZX_OK, iface_id: 123 } |
| }; |
| |
| responder.send(&mut response).expect("failed to send CreateIfaceResponse"); |
| } |
| ); |
| |
| // 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; |
| } |
| |
| // Progress the interface creation process so that the new MLME channel is passed to the |
| // device service. |
| assert_variant!(exec.run_until_stalled(&mut create_fut), Poll::Pending); |
| assert_variant!( |
| exec.run_until_stalled(&mut test_values.dev_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_svc::DeviceServiceRequest::AddIface { req, responder}))) => { |
| assert_eq!(req.phy_id, 10); |
| assert_eq!(req.assigned_iface_id, 123); |
| |
| let (status, mut response) = if add_iface_fails { |
| (zx::Status::NOT_SUPPORTED.into_raw(), None) |
| } else { |
| (zx::Status::OK.into_raw(), Some(fidl_svc::AddIfaceResponse { iface_id: 5 })) |
| }; |
| responder.send(status, response.as_mut()).expect("failed to send AddIface response"); |
| } |
| ); |
| |
| // The original future should resolve into a response. |
| assert_variant!(exec.run_until_stalled(&mut create_fut), |
| Poll::Ready(response) => { |
| if add_iface_fails { |
| assert_variant!(response, Err(zx::Status::NOT_SUPPORTED)); |
| } else { |
| let response = response.expect("CreateIface failed unexpectedly"); |
| assert_eq!(5, response.id); |
| assert_eq!( |
| device::PhyOwnership { phy_id: 10, phy_assigned_id: 123 }, |
| response.phy_ownership |
| ); |
| } |
| } |
| ); |
| } |
| |
| #[test] |
| fn create_iface_on_invalid_phy_id() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let test_values = test_setup(); |
| |
| let fut = super::create_iface( |
| &test_values.dev_proxy, |
| &test_values.phys, |
| fidl_svc::CreateIfaceRequest { |
| phy_id: 10, |
| role: fidl_wlan_common::WlanMacRole::Client, |
| sta_addr: NULL_MAC_ADDR, |
| }, |
| ); |
| pin_mut!(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, |
| }, |
| ); |
| phy_stream |
| } |
| |
| #[test] |
| fn destroy_iface_success() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, 42); |
| pin_mut!(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(&mut fidl_dev::DestroyIfaceResponse { status: zx::sys::ZX_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"); |
| } |
| |
| #[test] |
| fn destroy_iface_failure() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, 42); |
| pin_mut!(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(&mut fidl_dev::DestroyIfaceResponse { status: 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"); |
| } |
| |
| #[test] |
| fn destroy_iface_recovery() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, 42); |
| pin_mut!(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(&mut fidl_dev::DestroyIfaceResponse { status: 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."); |
| } |
| |
| #[test] |
| fn destroy_iface_not_found() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, 43); |
| pin_mut!(fut); |
| assert_eq!(Poll::Ready(Err(zx::Status::NOT_FOUND)), exec.run_until_stalled(&mut fut)); |
| } |
| |
| #[test] |
| fn get_client_sme() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, |
| }, |
| ); |
| |
| let req_fut = super::get_client_sme(&test_values.ifaces, 42); |
| pin_mut!(req_fut); |
| assert_eq!(Poll::Pending, exec.run_until_stalled(&mut req_fut)); |
| |
| let (client_sme_client, client_sme_server) = |
| create_endpoints::<fidl_sme::ClientSmeMarker>().expect("Failed to create client SME"); |
| |
| // Respond to a client SME request with a client endpoint. |
| assert_variant!(exec.run_until_stalled(&mut generic_sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::GenericSmeRequest::GetClientSme { responder }))) => { |
| responder.send(&mut Ok(client_sme_client)).expect("Failed to send response"); |
| } |
| ); |
| let client_sme = assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Ok(client_sme)) => client_sme); |
| |
| // Verify that the correct endpoint was returned. |
| let client_sme_proxy = client_sme.into_proxy().expect("Failed to get client SME proxy"); |
| let _status_req = client_sme_proxy.status(); |
| |
| let mut client_sme_stream = |
| client_sme_server.into_stream().expect("Failed to get client SME stream"); |
| assert_variant!( |
| exec.run_until_stalled(&mut client_sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { .. }))) |
| ); |
| } |
| |
| #[test] |
| fn get_client_sme_fails() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, |
| }, |
| ); |
| |
| let req_fut = super::get_client_sme(&test_values.ifaces, 42); |
| pin_mut!(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(&mut Err(1)).expect("Failed to send response"); |
| } |
| ); |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| |
| #[test] |
| fn get_client_sme_invalid_iface() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| 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, |
| }, |
| ); |
| |
| let req_fut = super::get_client_sme(&test_values.ifaces, 1337); |
| pin_mut!(req_fut); |
| assert_variant!(exec.run_until_stalled(&mut req_fut), Poll::Ready(Err(_))); |
| } |
| } |