wlan_service_util/
client.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{format_err, Context as _, Error};
6use fidl::endpoints;
7use fidl_fuchsia_wlan_common::WlanMacRole;
8use fidl_fuchsia_wlan_device_service::DeviceMonitorProxy;
9use futures::stream::TryStreamExt;
10use ieee80211::Ssid;
11use wlan_common::bss::{BssDescription, Protection};
12use wlan_common::security::wep::WepKey;
13use wlan_common::security::wpa::credential::{Passphrase, Psk};
14use wlan_common::security::SecurityError;
15use {
16    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_common_security as fidl_security,
17    fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_sme as fidl_sme,
18};
19
20type WlanService = DeviceMonitorProxy;
21
22/// Context for negotiating an `Authentication` (security protocol and credentials).
23///
24/// This ephemeral type joins a BSS description with credential data to negotiate an
25/// `Authentication`. See the `TryFrom` implementation below.
26#[derive(Clone, Debug)]
27struct SecurityContext {
28    pub bss: BssDescription,
29    pub unparsed_credential_bytes: Vec<u8>,
30}
31
32/// Negotiates an `Authentication` from security information (given credentials and a BSS
33/// description). The security protocol is based on the protection information described by the BSS
34/// description. This is used to parse and validate the given credentials.
35///
36/// This is necessary, because the service utilities communicate directly with SME, which requires
37/// more detailed information than the Policy layer.
38impl TryFrom<SecurityContext> for fidl_security::Authentication {
39    type Error = SecurityError;
40
41    fn try_from(context: SecurityContext) -> Result<Self, SecurityError> {
42        let SecurityContext { bss, unparsed_credential_bytes } = context;
43        fn parse_wpa_credentials(
44            unparsed_credential_bytes: Vec<u8>,
45        ) -> Result<fidl_security::Credentials, SecurityError> {
46            // Interpret credentials as a PSK first. This format is more specific.
47            if let Ok(psk) = Psk::try_from(unparsed_credential_bytes.as_slice()) {
48                Ok(fidl_security::Credentials::Wpa(fidl_security::WpaCredentials::Psk(psk.into())))
49            } else {
50                let passphrase = Passphrase::try_from(unparsed_credential_bytes)?;
51                Ok(fidl_security::Credentials::Wpa(fidl_security::WpaCredentials::Passphrase(
52                    passphrase.into(),
53                )))
54            }
55        }
56
57        match bss.protection() {
58            // Unsupported.
59            // TODO(https://fxbug.dev/42174395): Implement conversions for WPA Enterprise.
60            Protection::Unknown | Protection::Wpa2Enterprise | Protection::Wpa3Enterprise => {
61                Err(SecurityError::Unsupported)
62            }
63            Protection::Open => {
64                if unparsed_credential_bytes.len() == 0 {
65                    Ok(fidl_security::Authentication {
66                        protocol: fidl_security::Protocol::Open,
67                        credentials: None,
68                    })
69                } else {
70                    Err(SecurityError::Incompatible)
71                }
72            }
73            Protection::Wep => WepKey::parse(unparsed_credential_bytes.as_slice())
74                .map(|key| fidl_security::Authentication {
75                    protocol: fidl_security::Protocol::Wep,
76                    credentials: Some(Box::new(fidl_security::Credentials::Wep(
77                        fidl_security::WepCredentials { key: key.into() },
78                    ))),
79                })
80                .map_err(From::from),
81            Protection::Wpa1 => {
82                parse_wpa_credentials(unparsed_credential_bytes).map(|credentials| {
83                    fidl_security::Authentication {
84                        protocol: fidl_security::Protocol::Wpa1,
85                        credentials: Some(Box::new(credentials)),
86                    }
87                })
88            }
89            Protection::Wpa1Wpa2PersonalTkipOnly
90            | Protection::Wpa1Wpa2Personal
91            | Protection::Wpa2PersonalTkipOnly
92            | Protection::Wpa2Personal => {
93                parse_wpa_credentials(unparsed_credential_bytes).map(|credentials| {
94                    fidl_security::Authentication {
95                        protocol: fidl_security::Protocol::Wpa2Personal,
96                        credentials: Some(Box::new(credentials)),
97                    }
98                })
99            }
100            Protection::Wpa2Wpa3Personal => {
101                // Use WPA2 for transitional networks when a PSK is supplied.
102                if let Ok(psk) = Psk::try_from(unparsed_credential_bytes.as_slice()) {
103                    Ok(fidl_security::Authentication {
104                        protocol: fidl_security::Protocol::Wpa2Personal,
105                        credentials: Some(Box::new(fidl_security::Credentials::Wpa(
106                            fidl_security::WpaCredentials::Psk(psk.into()),
107                        ))),
108                    })
109                } else {
110                    Passphrase::try_from(unparsed_credential_bytes)
111                        .map(|passphrase| fidl_security::Authentication {
112                            protocol: fidl_security::Protocol::Wpa3Personal,
113                            credentials: Some(Box::new(fidl_security::Credentials::Wpa(
114                                fidl_security::WpaCredentials::Passphrase(passphrase.into()),
115                            ))),
116                        })
117                        .map_err(From::from)
118                }
119            }
120            Protection::Wpa3Personal => Passphrase::try_from(unparsed_credential_bytes)
121                .map(|passphrase| fidl_security::Authentication {
122                    protocol: fidl_security::Protocol::Wpa3Personal,
123                    credentials: Some(Box::new(fidl_security::Credentials::Wpa(
124                        fidl_security::WpaCredentials::Passphrase(passphrase.into()),
125                    ))),
126                })
127                .map_err(From::from),
128        }
129    }
130}
131
132pub async fn get_sme_proxy(
133    wlan_svc: &WlanService,
134    iface_id: u16,
135) -> Result<fidl_sme::ClientSmeProxy, Error> {
136    let (sme_proxy, sme_remote) = endpoints::create_proxy();
137    let result = wlan_svc
138        .get_client_sme(iface_id, sme_remote)
139        .await
140        .context("error sending GetClientSme request")?;
141    match result {
142        Ok(()) => Ok(sme_proxy),
143        Err(e) => Err(format_err!(
144            "Failed to get client sme proxy for interface id {} with error {}",
145            iface_id,
146            e
147        )),
148    }
149}
150
151pub async fn get_first_sme(wlan_svc: &WlanService) -> Result<fidl_sme::ClientSmeProxy, Error> {
152    let iface_id = super::get_first_iface(wlan_svc, WlanMacRole::Client)
153        .await
154        .context("failed to get iface")?;
155    get_sme_proxy(&wlan_svc, iface_id).await
156}
157
158pub async fn connect(
159    iface_sme_proxy: &fidl_sme::ClientSmeProxy,
160    target_ssid: Ssid,
161    target_pwd: Vec<u8>,
162    target_bss_desc: fidl_common::BssDescription,
163) -> Result<bool, Error> {
164    let (connection_proxy, connection_remote) = endpoints::create_proxy();
165
166    // Create a `ConnectRequest` using the given network information.
167    // Negotiate a security protocol based on the given credential and BSS metadata. Note that the
168    // resulting `Authentication` may be invalid.
169    let authentication = fidl_security::Authentication::try_from(SecurityContext {
170        bss: BssDescription::try_from(target_bss_desc.clone())?,
171        unparsed_credential_bytes: target_pwd,
172    })?;
173    let req = fidl_sme::ConnectRequest {
174        ssid: target_ssid.clone().into(),
175        bss_description: target_bss_desc,
176        authentication,
177        deprecated_scan_type: fidl_common::ScanType::Passive,
178        multiple_bss_candidates: false, // only used for metrics, select an arbitrary value
179    };
180
181    let _result = iface_sme_proxy.connect(&req, Some(connection_remote))?;
182
183    let connection_result_code = handle_connect_transaction(connection_proxy).await?;
184
185    if !matches!(connection_result_code, fidl_ieee80211::StatusCode::Success) {
186        log::error!("Failed to connect to network: {:?}", connection_result_code);
187        return Ok(false);
188    }
189
190    let client_status_response =
191        iface_sme_proxy.status().await.context("failed to check status from sme_proxy")?;
192    Ok(match client_status_response {
193        fidl_sme::ClientStatusResponse::Connected(serving_ap_info) => {
194            if serving_ap_info.ssid != target_ssid {
195                log::error!(
196                    "Connected to wrong network: {:?}. Expected: {:?}.",
197                    serving_ap_info.ssid.as_slice(),
198                    target_ssid
199                );
200                false
201            } else {
202                true
203            }
204        }
205        fidl_sme::ClientStatusResponse::Connecting(_)
206        | fidl_sme::ClientStatusResponse::Roaming(_)
207        | fidl_sme::ClientStatusResponse::Idle(_) => {
208            log::error!(
209                "Unexpected status {:?} after {:?}",
210                client_status_response,
211                connection_result_code
212            );
213            false
214        }
215    })
216}
217
218async fn handle_connect_transaction(
219    connect_transaction: fidl_sme::ConnectTransactionProxy,
220) -> Result<fidl_ieee80211::StatusCode, Error> {
221    let mut event_stream = connect_transaction.take_event_stream();
222    let mut result_code = fidl_ieee80211::StatusCode::RefusedReasonUnspecified;
223
224    if let Some(evt) = event_stream
225        .try_next()
226        .await
227        .context("failed to receive connect result before the channel was closed")?
228    {
229        match evt {
230            fidl_sme::ConnectTransactionEvent::OnConnectResult { result } => {
231                result_code = result.code;
232            }
233            other => {
234                return Err(format_err!(
235                    "Expected ConnectTransactionEvent::OnConnectResult event, got {:?}",
236                    other
237                ))
238            }
239        }
240    }
241
242    Ok(result_code)
243}
244
245pub async fn disconnect(iface_sme_proxy: &fidl_sme::ClientSmeProxy) -> Result<(), Error> {
246    iface_sme_proxy
247        .disconnect(fidl_sme::UserDisconnectReason::WlanServiceUtilTesting)
248        .await
249        .context("failed to trigger disconnect")?;
250
251    // check the status and ensure we are not connected to or connecting to anything
252    let client_status_response =
253        iface_sme_proxy.status().await.context("failed to check status from sme_proxy")?;
254    match client_status_response {
255        fidl_sme::ClientStatusResponse::Connected(_)
256        | fidl_sme::ClientStatusResponse::Connecting(_)
257        | fidl_sme::ClientStatusResponse::Roaming(_) => {
258            Err(format_err!("Disconnect confirmation failed: {:?}", client_status_response))
259        }
260        fidl_sme::ClientStatusResponse::Idle(_) => Ok(()),
261    }
262}
263
264pub async fn disconnect_all(wlan_svc: &WlanService) -> Result<(), Error> {
265    let wlan_iface_ids =
266        super::get_iface_list(wlan_svc).await.context("Connect: failed to get wlan iface list")?;
267
268    let mut error_msg = format!("");
269    for iface_id in wlan_iface_ids {
270        let result = wlan_svc.query_iface(iface_id).await.context("querying iface info")?;
271
272        match result {
273            Ok(query_info) => {
274                if query_info.role == WlanMacRole::Client {
275                    let sme_proxy = get_sme_proxy(&wlan_svc, iface_id)
276                        .await
277                        .context("Disconnect all: failed to get iface sme proxy")?;
278                    if let Err(e) = disconnect(&sme_proxy).await {
279                        error_msg =
280                            format!("{}Error disconnecting iface {}: {}\n", error_msg, iface_id, e);
281                        log::error!("disconnect_all: disconnect err on iface {}: {}", iface_id, e);
282                    }
283                }
284            }
285            Err(zx::sys::ZX_ERR_NOT_FOUND) => {
286                error_msg = format!("{}no query response on iface {}\n", error_msg, iface_id);
287                log::error!("disconnect_all: iface query empty on iface {}", iface_id);
288            }
289            Err(e) => {
290                error_msg = format!("{}failed querying iface {}: {}\n", error_msg, iface_id, e);
291                log::error!("disconnect_all: query err on iface {}: {}", iface_id, e);
292            }
293        }
294    }
295    if error_msg.is_empty() {
296        Ok(())
297    } else {
298        Err(format_err!("{}", error_msg))
299    }
300}
301
302pub async fn passive_scan(
303    iface_sme_proxy: &fidl_sme::ClientSmeProxy,
304) -> Result<Vec<fidl_sme::ScanResult>, Error> {
305    let vmo = iface_sme_proxy
306        .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {}))
307        .await
308        .context("error sending scan request")?
309        .map_err(|scan_error_code| format_err!("Scan error: {:?}", scan_error_code))?;
310    wlan_common::scan::read_vmo(vmo)
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::*;
317    use fidl::endpoints::RequestStream;
318    use fidl_fuchsia_wlan_device_service::{
319        self as wlan_service, DeviceMonitorMarker, DeviceMonitorProxy, DeviceMonitorRequest,
320        DeviceMonitorRequestStream,
321    };
322    use fidl_fuchsia_wlan_sme::{
323        ClientSmeMarker, ClientSmeRequest, ClientSmeRequestStream, Protection,
324    };
325    use fuchsia_async::TestExecutor;
326    use futures::stream::{StreamExt, StreamFuture};
327    use futures::task::Poll;
328    use ieee80211::Ssid;
329    use rand::Rng as _;
330    use std::convert::{TryFrom, TryInto};
331    use std::pin::pin;
332    use wlan_common::channel::{Cbw, Channel};
333    use wlan_common::scan::write_vmo;
334    use wlan_common::{assert_variant, fake_fidl_bss_description};
335    use {
336        fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_common_security as fidl_security,
337    };
338
339    fn generate_random_wpa2_bss_description() -> fidl_fuchsia_wlan_internal::BssDescription {
340        let mut rng = rand::thread_rng();
341        fidl_fuchsia_wlan_internal::BssDescription {
342            bssid: (0..6).map(|_| rng.gen::<u8>()).collect::<Vec<u8>>().try_into().unwrap(),
343            beacon_period: rng.gen::<u16>(),
344            rssi_dbm: rng.gen::<i8>(),
345            channel: fidl_common::WlanChannel {
346                primary: rng.gen::<u8>(),
347                cbw: match rng.gen_range(0..5) {
348                    0 => fidl_common::ChannelBandwidth::Cbw20,
349                    1 => fidl_common::ChannelBandwidth::Cbw40,
350                    2 => fidl_common::ChannelBandwidth::Cbw40Below,
351                    3 => fidl_common::ChannelBandwidth::Cbw80,
352                    4 => fidl_common::ChannelBandwidth::Cbw160,
353                    5 => fidl_common::ChannelBandwidth::Cbw80P80,
354                    _ => panic!(),
355                },
356                secondary80: rng.gen::<u8>(),
357            },
358            snr_db: rng.gen::<i8>(),
359            ..fake_fidl_bss_description!(Wpa2)
360        }
361    }
362
363    fn extract_sme_server_from_get_client_sme_req_and_respond(
364        exec: &mut TestExecutor,
365        req_stream: &mut DeviceMonitorRequestStream,
366        result: Result<(), zx::Status>,
367    ) -> fidl_sme::ClientSmeRequestStream {
368        let req = exec.run_until_stalled(&mut req_stream.next());
369
370        let (responder, fake_sme_server) = assert_variant !(
371            req,
372            Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme{ iface_id:_, sme_server, responder})))
373            => (responder, sme_server));
374
375        // now send the response back
376        responder
377            .send(result.map_err(|e| e.into_raw()))
378            .expect("fake sme proxy response: send failed");
379
380        // and return the stream
381        // let sme_stream = fake_sme_server.into_stream();
382        // sme_stream
383        fake_sme_server.into_stream()
384    }
385
386    fn respond_to_get_client_sme_request(
387        exec: &mut TestExecutor,
388        req_stream: &mut DeviceMonitorRequestStream,
389        result: Result<(), zx::Status>,
390    ) {
391        let req = exec.run_until_stalled(&mut req_stream.next());
392
393        let responder = assert_variant !(
394            req,
395            Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme{ responder, ..})))
396            => responder);
397
398        // now send the response back
399        responder
400            .send(result.map_err(|e| e.into_raw()))
401            .expect("fake sme proxy response: send failed")
402    }
403
404    fn respond_to_client_sme_disconnect_request(
405        exec: &mut TestExecutor,
406        req_stream: &mut ClientSmeRequestStream,
407    ) {
408        let req = exec.run_until_stalled(&mut req_stream.next());
409        let responder = assert_variant !(
410            req,
411            Poll::Ready(Some(Ok(ClientSmeRequest::Disconnect{ responder, .. })))
412            => responder);
413
414        // now send the response back
415        responder.send().expect("fake disconnect response: send failed")
416    }
417
418    fn respond_to_client_sme_status_request(
419        exec: &mut TestExecutor,
420        req_stream: &mut ClientSmeRequestStream,
421        status: &StatusResponse,
422    ) {
423        let req = exec.run_until_stalled(&mut req_stream.next());
424        let responder = assert_variant !(
425            req,
426            Poll::Ready(Some(Ok(ClientSmeRequest::Status{ responder})))
427            => responder);
428
429        // Send appropriate status response
430        match status {
431            StatusResponse::Idle => {
432                let response = fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {});
433                responder.send(&response).expect("Failed to send StatusResponse.");
434            }
435            StatusResponse::Connected => {
436                let serving_ap_info =
437                    create_serving_ap_info_using_ssid(Ssid::try_from([1, 2, 3, 4]).unwrap());
438                let response = fidl_sme::ClientStatusResponse::Connected(serving_ap_info);
439                responder.send(&response).expect("Failed to send StatusResponse.");
440            }
441            StatusResponse::Connecting => {
442                let response = fidl_sme::ClientStatusResponse::Connecting(vec![1, 2, 3, 4]);
443                responder.send(&response).expect("Failed to send StatusResponse.");
444            }
445        }
446    }
447
448    fn test_get_first_sme(iface_list: &[WlanMacRole]) -> Result<(), Error> {
449        let (mut exec, proxy, mut req_stream) =
450            crate::tests::setup_fake_service::<DeviceMonitorMarker>();
451        let fut = get_first_sme(&proxy);
452        let mut fut = pin!(fut);
453
454        let ifaces = (0..iface_list.len() as u16).collect();
455
456        assert!(exec.run_until_stalled(&mut fut).is_pending());
457        crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
458
459        for mac_role in iface_list {
460            // iface query response
461            assert!(exec.run_until_stalled(&mut fut).is_pending());
462
463            crate::tests::respond_to_query_iface_request(
464                &mut exec,
465                &mut req_stream,
466                *mac_role,
467                Some([1, 2, 3, 4, 5, 6]),
468            );
469
470            if *mac_role == WlanMacRole::Client {
471                // client sme proxy
472                assert!(exec.run_until_stalled(&mut fut).is_pending());
473                respond_to_get_client_sme_request(&mut exec, &mut req_stream, Ok(()));
474                break;
475            }
476        }
477
478        let _proxy = exec.run_singlethreaded(&mut fut)?;
479        Ok(())
480    }
481
482    fn test_disconnect_all(iface_list: &[(WlanMacRole, StatusResponse)]) -> Result<(), Error> {
483        let (mut exec, proxy, mut req_stream) =
484            crate::tests::setup_fake_service::<DeviceMonitorMarker>();
485        let fut = disconnect_all(&proxy);
486        let mut fut = pin!(fut);
487
488        let ifaces = (0..iface_list.len() as u16).collect();
489
490        assert!(exec.run_until_stalled(&mut fut).is_pending());
491        crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
492
493        for (mac_role, status) in iface_list {
494            // iface query response
495            assert!(exec.run_until_stalled(&mut fut).is_pending());
496            crate::tests::respond_to_query_iface_request(
497                &mut exec,
498                &mut req_stream,
499                *mac_role,
500                Some([1, 2, 3, 4, 5, 6]),
501            );
502
503            if *mac_role == WlanMacRole::Client {
504                // Get the Client SME server (to send the responses for the following 2 SME requests)
505                assert!(exec.run_until_stalled(&mut fut).is_pending());
506                let mut fake_sme_server_stream =
507                    extract_sme_server_from_get_client_sme_req_and_respond(
508                        &mut exec,
509                        &mut req_stream,
510                        Ok(()),
511                    );
512
513                // Disconnect
514                assert!(exec.run_until_stalled(&mut fut).is_pending());
515                respond_to_client_sme_disconnect_request(&mut exec, &mut fake_sme_server_stream);
516
517                assert!(exec.run_until_stalled(&mut fut).is_pending());
518
519                // Send appropriate status response
520                respond_to_client_sme_status_request(
521                    &mut exec,
522                    &mut fake_sme_server_stream,
523                    status,
524                );
525            }
526        }
527        exec.run_singlethreaded(&mut fut)
528    }
529
530    // iface list contains an AP and a client. Test should pass
531    #[test]
532    fn check_get_client_sme_success() {
533        let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Client];
534        test_get_first_sme(&iface_list).expect("expect success but failed");
535    }
536
537    // iface list is empty. Test should fail
538    #[test]
539    fn check_get_client_sme_no_devices() {
540        let iface_list: Vec<WlanMacRole> = Vec::new();
541        test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
542    }
543
544    // iface list does not contain a client. Test should fail
545    #[test]
546    fn check_get_client_sme_no_clients() {
547        let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Ap];
548        test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
549    }
550
551    // test disconnect_all with a Client and an AP. Test should pass
552    // as AP IF will be ignored and Client IF delete should succeed.
553    #[test]
554    fn check_disconnect_all_client_and_ap_success() {
555        let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
556            (WlanMacRole::Ap, StatusResponse::Idle),
557            (WlanMacRole::Client, StatusResponse::Idle),
558        ];
559        test_disconnect_all(&iface_list).expect("Expect success but failed")
560    }
561
562    // test disconnect_all with 2 Clients. Test should pass as both the
563    // IFs are clients and both deletes should succeed.
564    #[test]
565    fn check_disconnect_all_all_clients_success() {
566        let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
567            (WlanMacRole::Client, StatusResponse::Idle),
568            (WlanMacRole::Client, StatusResponse::Idle),
569        ];
570        test_disconnect_all(&iface_list).expect("Expect success but failed");
571    }
572
573    // test disconnect_all with 2 Clients, one disconnect failure
574    #[test]
575    fn check_disconnect_all_all_clients_fail() {
576        let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
577            (WlanMacRole::Ap, StatusResponse::Connected),
578            (WlanMacRole::Client, StatusResponse::Connected),
579        ];
580        test_disconnect_all(&iface_list).expect_err("Expect fail but succeeded");
581    }
582
583    // test disconnect_all with no Clients
584    #[test]
585    fn check_disconnect_all_no_clients_success() {
586        let iface_list: Vec<(WlanMacRole, StatusResponse)> =
587            vec![(WlanMacRole::Ap, StatusResponse::Idle), (WlanMacRole::Ap, StatusResponse::Idle)];
588        test_disconnect_all(&iface_list).expect("Expect success but failed");
589    }
590
591    #[test]
592    fn list_ifaces_returns_iface_id_vector() {
593        let mut exec = TestExecutor::new();
594        let (wlan_monitor, server) = create_wlan_monitor_util();
595        let mut next_device_monitor_req = server.into_future();
596
597        let ifaces: Vec<u16> = vec![0, 1, 35, 36];
598
599        let fut = get_iface_list(&wlan_monitor);
600        let mut fut = pin!(fut);
601        assert!(exec.run_until_stalled(&mut fut).is_pending());
602
603        send_iface_list_response(&mut exec, &mut next_device_monitor_req, ifaces.clone());
604
605        let complete = exec.run_until_stalled(&mut fut);
606
607        let list_response = match complete {
608            Poll::Ready(result) => result,
609            _ => panic!("Expected an iface list response"),
610        };
611
612        let response = match list_response {
613            Ok(response) => response,
614            Err(_) => panic!("Expected a valid list response"),
615        };
616
617        // now verify the response
618        assert_eq!(response, ifaces)
619    }
620
621    #[test]
622    fn list_ifaces_properly_handles_zero_ifaces() {
623        let mut exec = TestExecutor::new();
624        let (wlan_monitor, server) = create_wlan_monitor_util();
625        let mut next_device_monitor_req = server.into_future();
626
627        // create the data to use in the response
628        let iface_id_list: Vec<u16> = vec![];
629        let iface_list_vec = vec![];
630
631        let fut = get_iface_list(&wlan_monitor);
632        let mut fut = pin!(fut);
633        assert!(exec.run_until_stalled(&mut fut).is_pending());
634
635        send_iface_list_response(&mut exec, &mut next_device_monitor_req, iface_list_vec);
636
637        let complete = exec.run_until_stalled(&mut fut);
638
639        let list_response = match complete {
640            Poll::Ready(result) => result,
641            _ => panic!("Expected an iface list response"),
642        };
643
644        let response = match list_response {
645            Ok(response) => response,
646            Err(_) => panic!("Expected a valid list response"),
647        };
648
649        // now verify the response
650        assert_eq!(response, iface_id_list)
651    }
652
653    #[test]
654    fn list_phys_returns_iface_id_vector() {
655        let mut exec = TestExecutor::new();
656        let (monitor_service, server) = create_wlan_monitor_util();
657        let mut next_device_service_req = server.into_future();
658
659        // create the data to use in the response
660        let phy_id_list: Vec<u16> = vec![0, 1, 35, 36];
661        let mut phy_list_vec = vec![];
662        for id in &phy_id_list {
663            phy_list_vec.push(*id);
664        }
665
666        let fut = get_phy_list(&monitor_service);
667        let mut fut = pin!(fut);
668        assert!(exec.run_until_stalled(&mut fut).is_pending());
669
670        send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
671
672        let complete = exec.run_until_stalled(&mut fut);
673
674        let list_response = match complete {
675            Poll::Ready(result) => result,
676            _ => panic!("Expected an phy list response"),
677        };
678
679        let response = match list_response {
680            Ok(response) => response,
681            Err(_) => panic!("Expected a valid list response"),
682        };
683
684        // now verify the response
685        assert_eq!(response, phy_id_list)
686    }
687
688    #[test]
689    fn list_phys_properly_handles_zero_phys() {
690        let mut exec = TestExecutor::new();
691        let (monitor_service, server) = create_wlan_monitor_util();
692        let mut next_device_service_req = server.into_future();
693
694        // create the data to use in the response
695        let phy_id_list: Vec<u16> = vec![];
696        let phy_list_vec = vec![];
697
698        let fut = get_phy_list(&monitor_service);
699        let mut fut = pin!(fut);
700        assert!(exec.run_until_stalled(&mut fut).is_pending());
701
702        send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
703
704        let complete = exec.run_until_stalled(&mut fut);
705
706        let list_response = match complete {
707            Poll::Ready(result) => result,
708            _ => panic!("Expected an phy list response"),
709        };
710
711        let response = match list_response {
712            Ok(response) => response,
713            Err(_) => panic!("Expected a valid list response"),
714        };
715
716        // now verify the response
717        assert_eq!(response, phy_id_list)
718    }
719
720    fn poll_device_monitor_req(
721        exec: &mut TestExecutor,
722        next_device_monitor_req: &mut StreamFuture<DeviceMonitorRequestStream>,
723    ) -> Poll<DeviceMonitorRequest> {
724        exec.run_until_stalled(next_device_monitor_req).map(|(req, stream)| {
725            *next_device_monitor_req = stream.into_future();
726            req.expect("did not expect the DeviceMonitorRequestStream to end")
727                .expect("error polling device service request stream")
728        })
729    }
730
731    fn send_iface_list_response(
732        exec: &mut TestExecutor,
733        server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
734        ifaces: Vec<u16>,
735    ) {
736        let responder = match poll_device_monitor_req(exec, server) {
737            Poll::Ready(DeviceMonitorRequest::ListIfaces { responder }) => responder,
738            Poll::Pending => panic!("expected a request to be available"),
739            _ => panic!("expected a ListIfaces request"),
740        };
741
742        // now send the response back
743        let _result = responder.send(&ifaces[..]);
744    }
745
746    fn send_phy_list_response(
747        exec: &mut TestExecutor,
748        server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
749        phy_list_vec: Vec<u16>,
750    ) {
751        let responder = match poll_device_monitor_req(exec, server) {
752            Poll::Ready(DeviceMonitorRequest::ListPhys { responder }) => responder,
753            Poll::Pending => panic!("expected a request to be available"),
754            _ => panic!("expected a ListPhys request"),
755        };
756
757        // now send the response back
758        let _result = responder.send(&phy_list_vec);
759    }
760
761    #[test]
762    fn get_client_sme_valid_iface() {
763        let mut exec = TestExecutor::new();
764        let (wlan_monitor, server) = create_wlan_monitor_util();
765        let mut next_device_monitor_req = server.into_future();
766
767        let fut = get_sme_proxy(&wlan_monitor, 1);
768        let mut fut = pin!(fut);
769        assert!(exec.run_until_stalled(&mut fut).is_pending());
770
771        // pass in that we expect this to succeed
772        send_sme_proxy_response(&mut exec, &mut next_device_monitor_req, Ok(()));
773
774        let () = match exec.run_until_stalled(&mut fut) {
775            Poll::Ready(Ok(_)) => (),
776            _ => panic!("Expected a status response"),
777        };
778    }
779
780    fn send_sme_proxy_response(
781        exec: &mut TestExecutor,
782        server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
783        result: Result<(), zx::Status>,
784    ) {
785        let responder = match poll_device_monitor_req(exec, server) {
786            Poll::Ready(DeviceMonitorRequest::GetClientSme { responder, .. }) => responder,
787            Poll::Pending => panic!("expected a request to be available"),
788            _ => panic!("expected a GetClientSme request"),
789        };
790
791        // now send the response back
792        let _result = responder.send(result.map_err(|e| e.into_raw()));
793    }
794
795    #[test]
796    fn get_client_sme_invalid_iface() {
797        let mut exec = TestExecutor::new();
798        let (wlan_monitor, server) = create_wlan_monitor_util();
799        let mut next_device_monitor_req = server.into_future();
800
801        let fut = get_sme_proxy(&wlan_monitor, 1);
802        let mut fut = pin!(fut);
803        assert!(exec.run_until_stalled(&mut fut).is_pending());
804
805        // pass in that we expect this to fail with zx::Status::NOT_FOUND
806        send_sme_proxy_response(
807            &mut exec,
808            &mut next_device_monitor_req,
809            Err(zx::Status::NOT_FOUND),
810        );
811
812        let complete = exec.run_until_stalled(&mut fut);
813
814        match complete {
815            Poll::Ready(Err(_)) => (),
816            _ => panic!("Expected a status response"),
817        };
818    }
819
820    #[test]
821    fn connect_success_returns_true() {
822        let connect_result =
823            test_wpa2_connect("TestAp", "password", "TestAp", fidl_ieee80211::StatusCode::Success);
824        assert!(connect_result);
825    }
826
827    #[test]
828    fn connect_failed_returns_false() {
829        let connect_result = test_wpa2_connect(
830            "TestAp",
831            "password",
832            "",
833            fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
834        );
835        assert!(!connect_result);
836    }
837
838    #[test]
839    fn connect_different_ssid_returns_false() {
840        let connect_result = test_wpa2_connect(
841            "TestAp1",
842            "password",
843            "TestAp2",
844            fidl_ieee80211::StatusCode::Success,
845        );
846        assert!(!connect_result);
847    }
848
849    fn test_wpa2_connect(
850        target_ssid: &str,
851        password: &str,
852        connected_to_ssid: &str,
853        result_code: fidl_ieee80211::StatusCode,
854    ) -> bool {
855        let target_ssid = Ssid::try_from(target_ssid).unwrap();
856        let connected_to_ssid = Ssid::try_from(connected_to_ssid).unwrap();
857
858        let mut exec = TestExecutor::new();
859        let (client_sme, server) = create_client_sme_proxy();
860        let mut next_client_sme_req = server.into_future();
861
862        let target_password = password.as_bytes();
863        let target_bss_desc = generate_random_wpa2_bss_description();
864
865        let fut = connect(
866            &client_sme,
867            target_ssid.clone(),
868            target_password.to_vec(),
869            target_bss_desc.clone(),
870        );
871        let mut fut = pin!(fut);
872        assert!(exec.run_until_stalled(&mut fut).is_pending());
873
874        // have the request, need to send a response
875        send_connect_request_response(
876            &mut exec,
877            &mut next_client_sme_req,
878            &target_ssid,
879            fidl_security::Authentication::try_from(SecurityContext {
880                bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
881                unparsed_credential_bytes: target_password.to_vec(),
882            })
883            .unwrap(),
884            result_code,
885        );
886
887        // if connection is successful, status is requested to extract ssid
888        if result_code == fidl_ieee80211::StatusCode::Success {
889            assert!(exec.run_until_stalled(&mut fut).is_pending());
890            send_status_response(
891                &mut exec,
892                &mut next_client_sme_req,
893                Some(connected_to_ssid),
894                None,
895            );
896        }
897
898        let complete = exec.run_until_stalled(&mut fut);
899
900        let connection_result = match complete {
901            Poll::Ready(result) => result,
902            _ => panic!("Expected a connect response"),
903        };
904
905        let returned_bool = match connection_result {
906            Ok(response) => response,
907            _ => panic!("Expected a valid connection result"),
908        };
909
910        returned_bool
911    }
912
913    #[test]
914    fn connect_properly_passes_network_info_with_password() {
915        let mut exec = TestExecutor::new();
916        let (client_sme, server) = create_client_sme_proxy();
917        let mut next_client_sme_req = server.into_future();
918
919        let target_ssid = Ssid::try_from("TestAp").unwrap();
920        let target_password = "password".as_bytes();
921        let target_bss_desc = generate_random_wpa2_bss_description();
922
923        let fut = connect(
924            &client_sme,
925            target_ssid.clone(),
926            target_password.to_vec(),
927            target_bss_desc.clone(),
928        );
929        let mut fut = pin!(fut);
930        assert!(exec.run_until_stalled(&mut fut).is_pending());
931
932        // verify the connect request info
933        verify_connect_request_info(
934            &mut exec,
935            &mut next_client_sme_req,
936            &target_ssid,
937            fidl_security::Authentication::try_from(SecurityContext {
938                bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
939                unparsed_credential_bytes: target_password.to_vec(),
940            })
941            .unwrap(),
942            target_bss_desc,
943        );
944    }
945
946    #[test]
947    fn connect_properly_passes_network_info_open() {
948        let mut exec = TestExecutor::new();
949        let (client_sme, server) = create_client_sme_proxy();
950        let mut next_client_sme_req = server.into_future();
951
952        let target_ssid = Ssid::try_from("TestAp").unwrap();
953        let target_password = "".as_bytes();
954        let target_bss_desc = fake_fidl_bss_description!(Open);
955
956        let fut = connect(
957            &client_sme,
958            target_ssid.clone(),
959            target_password.to_vec(),
960            target_bss_desc.clone(),
961        );
962        let mut fut = pin!(fut);
963        assert!(exec.run_until_stalled(&mut fut).is_pending());
964
965        // verify the connect request info
966        verify_connect_request_info(
967            &mut exec,
968            &mut next_client_sme_req,
969            &target_ssid,
970            fidl_security::Authentication::try_from(SecurityContext {
971                bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
972                unparsed_credential_bytes: vec![],
973            })
974            .unwrap(),
975            target_bss_desc,
976        );
977    }
978
979    fn verify_connect_request_info(
980        exec: &mut TestExecutor,
981        server: &mut StreamFuture<ClientSmeRequestStream>,
982        expected_ssid: &Ssid,
983        expected_authentication: fidl_security::Authentication,
984        expected_bss_desc: fidl_common::BssDescription,
985    ) {
986        match poll_client_sme_request(exec, server) {
987            Poll::Ready(ClientSmeRequest::Connect { req, .. }) => {
988                assert_eq!(expected_ssid, &req.ssid);
989                assert_eq!(req.authentication, expected_authentication);
990                assert_eq!(req.bss_description, expected_bss_desc);
991            }
992            _ => panic!("expected a Connect request"),
993        }
994    }
995
996    fn send_connect_request_response(
997        exec: &mut TestExecutor,
998        server: &mut StreamFuture<ClientSmeRequestStream>,
999        expected_ssid: &Ssid,
1000        expected_authentication: fidl_security::Authentication,
1001        connect_result: fidl_ieee80211::StatusCode,
1002    ) {
1003        let responder = match poll_client_sme_request(exec, server) {
1004            Poll::Ready(ClientSmeRequest::Connect { req, txn, .. }) => {
1005                assert_eq!(expected_ssid, &req.ssid[..]);
1006                assert_eq!(req.authentication, expected_authentication);
1007                txn.expect("expected a Connect transaction channel")
1008            }
1009            Poll::Pending => panic!("expected a request to be available"),
1010            _ => panic!("expected a Connect request"),
1011        };
1012        let connect_transaction = responder
1013            .into_stream()
1014            .expect("failed to create a connect transaction stream")
1015            .control_handle();
1016        connect_transaction
1017            .send_on_connect_result(&fidl_sme::ConnectResult {
1018                code: connect_result,
1019                is_credential_rejected: false,
1020                is_reconnect: false,
1021            })
1022            .expect("failed to send OnConnectResult to ConnectTransaction");
1023    }
1024
1025    fn poll_client_sme_request(
1026        exec: &mut TestExecutor,
1027        next_client_sme_req: &mut StreamFuture<ClientSmeRequestStream>,
1028    ) -> Poll<ClientSmeRequest> {
1029        exec.run_until_stalled(next_client_sme_req).map(|(req, stream)| {
1030            *next_client_sme_req = stream.into_future();
1031            req.expect("did not expect the ClientSmeRequestStream to end")
1032                .expect("error polling client sme request stream")
1033        })
1034    }
1035
1036    fn create_client_sme_proxy() -> (fidl_sme::ClientSmeProxy, ClientSmeRequestStream) {
1037        let (proxy, server) = endpoints::create_proxy::<ClientSmeMarker>();
1038        let server = server.into_stream();
1039        (proxy, server)
1040    }
1041
1042    fn create_wlan_monitor_util() -> (DeviceMonitorProxy, DeviceMonitorRequestStream) {
1043        let (proxy, server) = endpoints::create_proxy::<DeviceMonitorMarker>();
1044        let server = server.into_stream();
1045        (proxy, server)
1046    }
1047
1048    enum StatusResponse {
1049        Idle,
1050        Connected,
1051        Connecting,
1052    }
1053
1054    #[test]
1055    fn disconnect_with_empty_status_response() {
1056        if let Poll::Ready(result) = test_disconnect(StatusResponse::Idle) {
1057            return assert!(result.is_ok());
1058        }
1059        panic!("disconnect did not return a Poll::Ready")
1060    }
1061
1062    #[test]
1063    fn disconnect_fail_because_connected() {
1064        if let Poll::Ready(result) = test_disconnect(StatusResponse::Connected) {
1065            return assert!(result.is_err());
1066        }
1067        panic!("disconnect did not return a Poll::Ready")
1068    }
1069
1070    #[test]
1071    fn disconnect_fail_because_connecting() {
1072        if let Poll::Ready(result) = test_disconnect(StatusResponse::Connecting) {
1073            return assert!(result.is_err());
1074        }
1075        panic!("disconnect did not return a Poll::Ready")
1076    }
1077
1078    fn test_disconnect(status: StatusResponse) -> Poll<Result<(), Error>> {
1079        let mut exec = TestExecutor::new();
1080        let (client_sme, server) = create_client_sme_proxy();
1081        let mut client_sme_req = server.into_future();
1082
1083        let fut = disconnect(&client_sme);
1084        let mut fut = pin!(fut);
1085        assert!(exec.run_until_stalled(&mut fut).is_pending());
1086
1087        send_disconnect_request_response(&mut exec, &mut client_sme_req);
1088
1089        assert!(exec.run_until_stalled(&mut fut).is_pending());
1090
1091        match status {
1092            StatusResponse::Idle => {
1093                send_status_response(&mut exec, &mut client_sme_req, None, None)
1094            }
1095            StatusResponse::Connected => send_status_response(
1096                &mut exec,
1097                &mut client_sme_req,
1098                Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1099                None,
1100            ),
1101            StatusResponse::Connecting => send_status_response(
1102                &mut exec,
1103                &mut client_sme_req,
1104                None,
1105                Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1106            ),
1107        }
1108
1109        exec.run_until_stalled(&mut fut)
1110    }
1111
1112    fn send_disconnect_request_response(
1113        exec: &mut TestExecutor,
1114        server: &mut StreamFuture<ClientSmeRequestStream>,
1115    ) {
1116        let rsp = match poll_client_sme_request(exec, server) {
1117            Poll::Ready(ClientSmeRequest::Disconnect { responder, .. }) => responder,
1118            Poll::Pending => panic!("Expected a DisconnectRequest"),
1119            _ => panic!("Expected a DisconnectRequest"),
1120        };
1121        rsp.send().expect("Failed to send DisconnectResponse.");
1122    }
1123
1124    fn create_serving_ap_info_using_ssid(ssid: Ssid) -> fidl_sme::ServingApInfo {
1125        fidl_sme::ServingApInfo {
1126            bssid: [0, 1, 2, 3, 4, 5],
1127            ssid: ssid.into(),
1128            rssi_dbm: -30,
1129            snr_db: 10,
1130            channel: fidl_common::WlanChannel {
1131                primary: 1,
1132                cbw: fidl_common::ChannelBandwidth::Cbw20,
1133                secondary80: 0,
1134            },
1135            protection: Protection::Wpa2Personal,
1136        }
1137    }
1138
1139    fn send_status_response(
1140        exec: &mut TestExecutor,
1141        server: &mut StreamFuture<ClientSmeRequestStream>,
1142        connected_to_ssid: Option<Ssid>,
1143        connecting_to_ssid: Option<Ssid>,
1144    ) {
1145        let rsp = match poll_client_sme_request(exec, server) {
1146            Poll::Ready(ClientSmeRequest::Status { responder }) => responder,
1147            Poll::Pending => panic!("Expected a StatusRequest"),
1148            _ => panic!("Expected a StatusRequest"),
1149        };
1150
1151        let response = match (connected_to_ssid, connecting_to_ssid) {
1152            (Some(_), Some(_)) => panic!("SME cannot simultaneously be Connected and Connecting."),
1153            (Some(ssid), None) => {
1154                let serving_ap_info = create_serving_ap_info_using_ssid(ssid);
1155                fidl_sme::ClientStatusResponse::Connected(serving_ap_info)
1156            }
1157            (None, Some(ssid)) => fidl_sme::ClientStatusResponse::Connecting(ssid.to_vec()),
1158            (None, None) => fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {}),
1159        };
1160
1161        rsp.send(&response).expect("Failed to send StatusResponse.");
1162    }
1163
1164    #[test]
1165    fn scan_success_returns_empty_results() {
1166        assert_eq!(test_scan(&[]), &[]);
1167    }
1168
1169    #[test]
1170    fn scan_success_returns_results() {
1171        let scan_results = &[
1172            create_scan_result(
1173                [0, 1, 2, 3, 4, 5],
1174                Ssid::try_from("foo").unwrap(),
1175                -30,
1176                20,
1177                Channel::new(1, Cbw::Cbw20),
1178                Protection::Wpa2Personal,
1179                Some(fidl_sme::Compatibility {
1180                    mutual_security_protocols: vec![fidl_security::Protocol::Wpa2Personal],
1181                }),
1182            ),
1183            create_scan_result(
1184                [1, 2, 3, 4, 5, 6],
1185                Ssid::try_from("hello").unwrap(),
1186                -60,
1187                10,
1188                Channel::new(2, Cbw::Cbw20),
1189                Protection::Wpa2Personal,
1190                None,
1191            ),
1192        ];
1193
1194        assert_eq!(test_scan(scan_results), scan_results);
1195    }
1196
1197    #[test]
1198    fn scan_error_correctly_handled() {
1199        // need to expect an error
1200        assert!(test_scan_error().is_err())
1201    }
1202
1203    fn test_scan(scan_results: &[fidl_sme::ScanResult]) -> Vec<fidl_sme::ScanResult> {
1204        let mut exec = TestExecutor::new();
1205        let (client_sme, server) = create_client_sme_proxy();
1206        let mut client_sme_req = server.into_future();
1207
1208        let fut = passive_scan(&client_sme);
1209        let mut fut = pin!(fut);
1210        assert!(exec.run_until_stalled(&mut fut).is_pending());
1211
1212        send_scan_result_response(&mut exec, &mut client_sme_req, scan_results);
1213
1214        let complete = exec.run_until_stalled(&mut fut);
1215        let request_result = match complete {
1216            Poll::Ready(result) => result,
1217            _ => panic!("Expected a scan request result"),
1218        };
1219        let returned_scan_results = request_result.expect("failed to get scan results");
1220
1221        returned_scan_results
1222    }
1223
1224    fn send_scan_result_response(
1225        exec: &mut TestExecutor,
1226        server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1227        scan_results: &[fidl_sme::ScanResult],
1228    ) {
1229        match poll_client_sme_request(exec, server) {
1230            Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => {
1231                let vmo = write_vmo(scan_results.to_vec()).expect("failed to write VMO");
1232                responder.send(Ok(vmo)).expect("failed to send scan results")
1233            }
1234            Poll::Pending => panic!("expected a request to be available"),
1235            _ => panic!("expected a scan request"),
1236        }
1237    }
1238
1239    fn test_scan_error() -> Result<(), Error> {
1240        let mut exec = TestExecutor::new();
1241        let (client_sme, server) = create_client_sme_proxy();
1242        let mut client_sme_req = server.into_future();
1243
1244        let fut = passive_scan(&client_sme);
1245        let mut fut = pin!(fut);
1246        assert!(exec.run_until_stalled(&mut fut).is_pending());
1247
1248        send_scan_error_response(&mut exec, &mut client_sme_req);
1249        let _ = exec.run_until_stalled(&mut fut)?;
1250        Ok(())
1251    }
1252
1253    fn send_scan_error_response(
1254        exec: &mut TestExecutor,
1255        server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1256    ) {
1257        match poll_client_sme_request(exec, server) {
1258            Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => responder
1259                .send(Err(fidl_sme::ScanErrorCode::InternalError))
1260                .expect("failed to send ScanError"),
1261            Poll::Pending => panic!("expected a request to be available"),
1262            _ => panic!("expected a scan request"),
1263        };
1264    }
1265
1266    fn create_scan_result(
1267        bssid: [u8; 6],
1268        ssid: Ssid,
1269        rssi_dbm: i8,
1270        snr_db: i8,
1271        channel: Channel,
1272        protection: Protection,
1273        compatibility: Option<fidl_sme::Compatibility>,
1274    ) -> fidl_sme::ScanResult {
1275        fidl_sme::ScanResult {
1276            compatibility: compatibility.map(Box::new),
1277            timestamp_nanos: zx::MonotonicInstant::get().into_nanos(),
1278            bss_description: fake_fidl_bss_description!(
1279                protection => protection,
1280                bssid: bssid,
1281                ssid: ssid,
1282                rssi_dbm: rssi_dbm,
1283                snr_db: snr_db,
1284                channel: channel,
1285            ),
1286        }
1287    }
1288
1289    fn send_destroy_iface_response(
1290        exec: &mut TestExecutor,
1291        server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
1292        status: zx::Status,
1293    ) {
1294        let responder = match poll_device_monitor_req(exec, server) {
1295            Poll::Ready(DeviceMonitorRequest::DestroyIface { responder, .. }) => responder,
1296            Poll::Pending => panic!("expected a request to be available"),
1297            _ => panic!("expected a destroy iface request"),
1298        };
1299
1300        // now send the response back
1301        let _result = responder.send(status.into_raw());
1302    }
1303
1304    #[test]
1305    fn test_destroy_single_iface_ok() {
1306        let mut exec = TestExecutor::new();
1307        let (monitor_service, server) = create_wlan_monitor_util();
1308        let mut next_device_service_req = server.into_future();
1309
1310        let fut = destroy_iface(&monitor_service, 0);
1311        let mut fut = pin!(fut);
1312        assert!(exec.run_until_stalled(&mut fut).is_pending());
1313
1314        send_destroy_iface_response(&mut exec, &mut next_device_service_req, zx::Status::OK);
1315
1316        match exec.run_until_stalled(&mut fut) {
1317            Poll::Ready(Ok(_)) => (),
1318            _ => panic!("Expected a status response"),
1319        };
1320    }
1321}