Skip to main content

donut_lib/
lib.rs

1// Copyright 2020 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 ::fidl as _;
6use anyhow::{Error, format_err};
7use eui48::MacAddress;
8use flex_client::ProxyHasDomain;
9use flex_fuchsia_net as net;
10use flex_fuchsia_wlan_policy as wlan_policy;
11use flex_fuchsia_wlan_product_deprecatedconfiguration as wlan_deprecated;
12use futures::TryStreamExt;
13use futures::future::LocalBoxFuture;
14
15pub mod opts;
16pub mod serialize;
17
18// String formatting, printing, and general boilerplate helpers.
19
20/// Returns the SSID and security type of a network identifier as strings.
21fn extract_network_id(
22    network_id: Option<wlan_policy::NetworkIdentifier>,
23) -> Result<(String, String), Error> {
24    match network_id {
25        Some(id) => {
26            let ssid = std::string::String::from_utf8(id.ssid).unwrap();
27            let security_type = match id.type_ {
28                wlan_policy::SecurityType::None => "",
29                wlan_policy::SecurityType::Wep => "wep",
30                wlan_policy::SecurityType::Wpa => "wpa",
31                wlan_policy::SecurityType::Wpa2 => "wpa2",
32                wlan_policy::SecurityType::Wpa3 => "wpa3",
33            };
34            return Ok((ssid, security_type.to_string()));
35        }
36        None => return Ok(("".to_string(), "".to_string())),
37    };
38}
39
40/// Returns a BSS's BSSID as a string.
41fn extract_bss_details(bss: wlan_policy::Bss) -> (String, i8, u32) {
42    let bssid = match bss.bssid {
43        Some(bssid) => hex::encode(bssid),
44        None => "".to_string(),
45    };
46    let rssi = match bss.rssi {
47        Some(rssi) => rssi,
48        None => 0,
49    };
50    let frequency = match bss.frequency {
51        Some(frequency) => frequency,
52        None => 0,
53    };
54
55    (bssid, rssi, frequency)
56}
57
58/// Returns WLAN compatilibity information as a string.
59fn extract_compatibility(compatibility: Option<wlan_policy::Compatibility>) -> String {
60    if compatibility.is_some() {
61        match compatibility.unwrap() {
62            wlan_policy::Compatibility::Supported => return "supported".to_string(),
63            wlan_policy::Compatibility::DisallowedInsecure => {
64                return "disallowed, insecure".to_string();
65            }
66            wlan_policy::Compatibility::DisallowedNotSupported => {
67                return "disallowed, not supported".to_string();
68            }
69        }
70    }
71
72    return "compatilibity unknown".to_string();
73}
74
75/// Iterates through a vector of network configurations and prints their contents.
76pub fn print_saved_networks(saved_networks: Vec<wlan_policy::NetworkConfig>) -> Result<(), Error> {
77    for config in saved_networks {
78        let (ssid, security_type) = extract_network_id(config.id)?;
79        let password = match config.credential {
80            Some(credential) => match credential {
81                wlan_policy::Credential::None(wlan_policy::Empty) => String::from(""),
82                wlan_policy::Credential::Password(bytes) => {
83                    let password = std::string::String::from_utf8(bytes);
84                    password.unwrap()
85                }
86                wlan_policy::Credential::Psk(bytes) => {
87                    // PSK is stored as bytes but is displayed as hex to prevent UTF-8 errors
88                    hex::encode(bytes)
89                }
90                _ => return Err(format_err!("unknown credential variant detected")),
91            },
92            None => String::from(""),
93        };
94        println!("{:32} | {:4} | {}", ssid, security_type, password);
95    }
96    Ok(())
97}
98
99/// Prints a serialized version of saved networks
100pub fn print_serialized_saved_networks(
101    saved_networks: Vec<wlan_policy::NetworkConfig>,
102) -> Result<(), Error> {
103    let serialized = serialize::serialize_saved_networks(saved_networks)?;
104    println!("{}", serialized);
105    Ok(())
106}
107
108/// Deserializes the output of serialize_saved_networks and saves them
109pub async fn restore_serialized_config(
110    client_controller: wlan_policy::ClientControllerProxy,
111    serialized_config: String,
112) -> Result<(), Error> {
113    let network_configs = serialize::deserialize_saved_networks(serialized_config)?;
114    for network in network_configs {
115        save_network(client_controller.clone(), network).await?;
116    }
117    Ok(())
118}
119
120/// Iterates through a vector of scan results and prints each one.
121pub fn print_scan_results(scan_results: Vec<wlan_policy::ScanResult>) -> Result<(), Error> {
122    for network in scan_results {
123        let (ssid, security_type) = extract_network_id(network.id)?;
124        let compatibility = extract_compatibility(network.compatibility);
125        println!("{:32} | {:3} ({})", ssid, security_type, compatibility);
126
127        if network.entries.is_some() {
128            for entry in network.entries.unwrap() {
129                let (bssid, rssi, frequency) = extract_bss_details(entry);
130                println!("\t0x{:12} | {:3} | {:5}", bssid, rssi, frequency);
131            }
132        }
133    }
134    Ok(())
135}
136
137/// Parses the return value from policy layer FIDL operations and prints a human-readable
138/// representation of the result.
139fn handle_request_status(status: wlan_policy::RequestStatus) -> Result<(), Error> {
140    match status {
141        wlan_policy::RequestStatus::Acknowledged => Ok(()),
142        wlan_policy::RequestStatus::RejectedNotSupported => {
143            Err(format_err!("request failed: not supported"))
144        }
145        wlan_policy::RequestStatus::RejectedIncompatibleMode => {
146            Err(format_err!("request failed: incompatible mode"))
147        }
148        wlan_policy::RequestStatus::RejectedAlreadyInUse => {
149            Err(format_err!("request failed: already in use"))
150        }
151        wlan_policy::RequestStatus::RejectedDuplicateRequest => {
152            Err(format_err!("request failed: duplicate request."))
153        }
154    }
155}
156
157/// When a client or AP controller is created, the policy layer may close the serving end with an
158/// epitaph if another component already holds a controller.  This macro wraps proxy API calls
159/// and provides context to the caller.
160async fn run_proxy_command<'a, T>(
161    fut: LocalBoxFuture<'a, Result<T, ::fidl::Error>>,
162) -> Result<T, Error> {
163    fut.await.map_err(|e| {
164        match e {
165            ::fidl::Error::ClientChannelClosed { .. } => format_err!(
166                "Failed to obtain a WLAN policy controller. Your command was not executed.\n\n\
167                Help: Only one component may hold a policy controller at once. You can try killing\n\
168                other holders with:\n\
169                * ffx component destroy /core/session-manager/session:session\n"
170            ),
171            e => format_err!("{}", e),
172        }
173    })
174}
175
176// Policy client helper functions
177
178/// Issues a connect call to the client policy layer and waits for the connection process to
179/// complete.
180pub async fn handle_connect(
181    client_controller: wlan_policy::ClientControllerProxy,
182    mut server_stream: wlan_policy::ClientStateUpdatesRequestStream,
183    ssid: String,
184    security_type: Option<wlan_policy::SecurityType>,
185) -> Result<(), Error> {
186    // Use the exact security type if provided, or try to find the intended NetworkIdentifier
187    let network_id = if let Some(type_) = security_type {
188        wlan_policy::NetworkIdentifier { ssid: ssid.into_bytes(), type_ }
189    } else {
190        let mut saved_networks = handle_get_saved_networks(&client_controller)
191            .await
192            .map_err(|e| format_err!("failed to look up matching saved networks: {:?}", e))?;
193        saved_networks.retain(|config| config.id.as_ref().unwrap().ssid == ssid.as_bytes());
194        // If there is one matching saved network, use it to connect
195        if saved_networks.len() == 1 {
196            saved_networks.pop().unwrap().id.unwrap()
197        } else if saved_networks.is_empty() {
198            return Err(format_err!(
199                "Failed to find a saved network with the provided name. Please check that the name is correct or make sure the network with the provided name is saved."
200            ));
201        // If there are more than one matching network, do not assume which one to use and
202        // ask the caller to specify.
203        } else {
204            return Err(format_err!(
205                "Multiple saved networks were found matching the provided name, please specify the security type of the network to connect to."
206            ));
207        }
208    };
209
210    let result = run_proxy_command(Box::pin(client_controller.connect(&network_id))).await?;
211    handle_request_status(result)?;
212
213    while let Some(update_request) = server_stream.try_next().await? {
214        let update = update_request.into_on_client_state_update();
215        let (update, responder) = match update {
216            Some((update, responder)) => (update, responder),
217            None => return Err(format_err!("Client provider produced invalid update.")),
218        };
219        let _ = responder.send();
220
221        match update.state {
222            Some(state) => {
223                if state == wlan_policy::WlanClientState::ConnectionsDisabled {
224                    return Err(format_err!("Connections disabled while trying to conncet."));
225                }
226            }
227            None => continue,
228        }
229
230        let networks = match update.networks {
231            Some(networks) => networks,
232            None => continue,
233        };
234
235        for net_state in networks {
236            if net_state.id.is_none() || net_state.state.is_none() {
237                continue;
238            }
239            if net_state.id.unwrap().ssid == network_id.ssid {
240                match net_state.state.unwrap() {
241                    wlan_policy::ConnectionState::Failed => {
242                        return Err(format_err!(
243                            "Failed to connect with reason {:?}",
244                            net_state.status
245                        ));
246                    }
247                    wlan_policy::ConnectionState::Disconnected => {
248                        return Err(format_err!("Disconnect with reason {:?}", net_state.status));
249                    }
250                    wlan_policy::ConnectionState::Connecting => continue,
251                    wlan_policy::ConnectionState::Connected => {
252                        println!("Successfully connected");
253                        return Ok(());
254                    }
255                }
256            }
257        }
258    }
259    Err(format_err!("Status stream terminated before connection could be verified."))
260}
261
262/// Issues a call to the client policy layer to get saved networks and prints all saved network
263/// configurations.
264pub async fn handle_get_saved_networks(
265    client_controller: &wlan_policy::ClientControllerProxy,
266) -> Result<Vec<wlan_policy::NetworkConfig>, Error> {
267    let (client_proxy, server_end) =
268        client_controller.domain().create_proxy::<wlan_policy::NetworkConfigIteratorMarker>();
269    let fut = async { client_controller.get_saved_networks(server_end) };
270    run_proxy_command(Box::pin(fut)).await?;
271
272    let mut saved_networks = Vec::new();
273    loop {
274        let mut new_configs = run_proxy_command(Box::pin(client_proxy.get_next())).await?;
275        if new_configs.is_empty() {
276            break;
277        }
278        saved_networks.append(&mut new_configs);
279    }
280    Ok(saved_networks)
281}
282
283/// Listens for client state updates and prints each update that is received. Updates are printed
284/// in the following format:
285///
286/// Update:
287///     <SSIDA>:                  <state> - <status_if_any>,
288///     <SSIDB_LONGER>:           <state> - <status_if_any>,
289pub async fn handle_listen(
290    mut server_stream: wlan_policy::ClientStateUpdatesRequestStream,
291    single_sample: bool,
292) -> Result<(), Error> {
293    let mut last_known_connection_state = None;
294    while let Some(update_request) = server_stream.try_next().await? {
295        let update = update_request.into_on_client_state_update();
296        let (update, responder) = match update {
297            Some((update, responder)) => (update, responder),
298            None => return Err(format_err!("Client provider produced invalid update.")),
299        };
300        let _ = responder.send();
301
302        match update.state {
303            Some(state) => {
304                if last_known_connection_state != Some(state) {
305                    last_known_connection_state = Some(state);
306                    match state {
307                        wlan_policy::WlanClientState::ConnectionsEnabled => {
308                            println!("Client connections are enabled");
309                        }
310                        wlan_policy::WlanClientState::ConnectionsDisabled => {
311                            println!("Client connections are disabled");
312                        }
313                    }
314                };
315            }
316            None => {
317                println!("Unexpected client connection state 'None'");
318            }
319        }
320
321        let networks = match update.networks {
322            Some(networks) => networks,
323            None => continue,
324        };
325
326        let mut updates = vec![];
327        // Create update string for each network. Pad the SSID so the updates align
328        for net_state in networks {
329            if net_state.id.is_none() || net_state.state.is_none() {
330                continue;
331            }
332            let id = net_state.id.unwrap();
333            let ssid = std::str::from_utf8(&id.ssid).unwrap();
334            match net_state.state.unwrap() {
335                wlan_policy::ConnectionState::Failed => {
336                    updates.push(format!(
337                        "\t{:32} connection failed - {:?}",
338                        format!("{}:", ssid),
339                        net_state.status
340                    ));
341                }
342                wlan_policy::ConnectionState::Disconnected => {
343                    updates.push(format!(
344                        "\t{:32} connection disconnected - {:?}",
345                        format!("{}:", ssid),
346                        net_state.status
347                    ));
348                }
349                wlan_policy::ConnectionState::Connecting => {
350                    updates.push(format!("\t{:32} connecting", format!("{}:", ssid)));
351                }
352                wlan_policy::ConnectionState::Connected => {
353                    updates.push(format!("\t{:32} connected", format!("{}:", ssid)))
354                }
355            }
356        }
357
358        // Sort updates by SSID
359        updates.sort_by_key(|s| s.to_lowercase());
360        println!("Update:");
361        for update in updates {
362            println!("{}", update);
363        }
364
365        // If only the current status is desired, break out and return.
366        if single_sample {
367            break;
368        }
369    }
370    Ok(())
371}
372
373/// Communicates with the client policy layer to remove a network. This will also get the list of
374/// saved networks before and after to indicate whether anything was removed, since there is no
375/// error if the specified network was never saved.
376pub async fn handle_forget_network(
377    client_controller: wlan_policy::ClientControllerProxy,
378    ssid: Vec<u8>,
379    security_type: Option<wlan_policy::SecurityType>,
380) -> Result<(), Error> {
381    let networks_before = handle_get_saved_networks(&client_controller).await.map_err(|e| {
382        format_err!(
383            "The network was not removed because an error occurred getting the list of networks \
384                before removing the requested network: {}",
385            e,
386        )
387    })?;
388
389    // If there is a provided security type, use it to construct the config.
390    // Otherwise get saved networks and find one matching the provided arguments.
391    let network_id = if security_type.is_some() {
392        wlan_policy::NetworkIdentifier { ssid: ssid.clone(), type_: security_type.unwrap() }
393    } else {
394        // Reuse the saved networks, but don't check for errors until using the data because it
395        // isn't necessary in the case where the exact arguments are provided. The data is needed
396        // below but the error cannot be cloned so the error is manually checked here.
397        let mut matching_networks = networks_before
398            .iter()
399            .filter(|c| config_matches(c, &ssid, &security_type))
400            .cloned()
401            .collect::<Vec<_>>();
402
403        // If there is one matching saved network, specify this network to remove.
404        if matching_networks.len() == 1 {
405            let config = matching_networks.pop().unwrap();
406            config.id.ok_or_else(|| format_err!("missing network ID in config"))?
407        } else if matching_networks.is_empty() {
408            return Err(format_err!(
409                "Failed to find a saved network with the provided arguments. Please check that the arguments are correct if there should be one saved."
410            ));
411        // If there are more than one matching network, do not assume which one to use and
412        // ask the caller to specify.
413        } else {
414            return Err(format_err!(
415                "Multiple saved networks were found matching the provided \
416                arguments, please specify SSID and security that matches only one \
417                saved network."
418            ));
419        }
420    };
421
422    run_proxy_command(Box::pin(client_controller.forget_network(&network_id)))
423        .await?
424        .map_err(|e| format_err!("failed to forget network with {:?}", e))?;
425
426    let networks_after = match handle_get_saved_networks(&client_controller).await {
427        Ok(networks) => networks,
428        Err(e) => {
429            println!(
430                "The network provided may or may not have been removed. An error occurred \
431                    getting the list of networks after removing: {}",
432                e
433            );
434            return Ok(());
435        }
436    };
437
438    // Check that there is no matching network after removing it. There should only have been one
439    // config matching the args since if there were multiple, this function would have quit early.
440    if networks_after.iter().any(|c| config_matches(c, &ssid, &security_type)) {
441        return Err(format_err!(
442            "The network may not have been removed. A network matching the \
443            provided arguments was found after attempting to remove the network."
444        ));
445    }
446
447    // Check whether anything was removed.
448    if networks_before.len() == networks_after.len() {
449        println!(
450            "The number of saved networks is the same after removing the specified network. \
451                  Please check that the arguments provided are correct and whether the intended \
452                  network is saved."
453        );
454    } else {
455        println!("Successfully removed network '{}'", std::str::from_utf8(&ssid).unwrap());
456    }
457    Ok(())
458}
459
460/// Check whether a config matches the provided SSID and optionally security if provided.
461fn config_matches(
462    config: &wlan_policy::NetworkConfig,
463    ssid: &Vec<u8>,
464    security_type: &Option<wlan_policy::SecurityType>,
465) -> bool {
466    let config_id = match &config.id {
467        Some(id) => id,
468        None => {
469            return false;
470        }
471    };
472
473    if config_id.ssid != *ssid {
474        return false;
475    }
476
477    // Only check security and credential if not None.
478    if let Some(security) = *security_type {
479        if config_id.type_ != security {
480            return false;
481        }
482    }
483
484    return true;
485}
486
487/// Communicates with the client policy layer to save a network configuration.
488pub async fn handle_save_network(
489    client_controller: wlan_policy::ClientControllerProxy,
490    config: wlan_policy::NetworkConfig,
491) -> Result<(), Error> {
492    save_network(client_controller, config).await
493}
494
495async fn save_network(
496    client_controller: wlan_policy::ClientControllerProxy,
497    network_config: wlan_policy::NetworkConfig,
498) -> Result<(), Error> {
499    run_proxy_command(Box::pin(client_controller.save_network(&network_config)))
500        .await?
501        .map_err(|e| format_err!("failed to save network with {:?}", e))?;
502    println!(
503        "Successfully saved network '{}'",
504        std::str::from_utf8(&network_config.id.unwrap().ssid).unwrap()
505    );
506    Ok(())
507}
508
509/// Issues a scan request to the client policy layer.
510pub async fn handle_scan(
511    client_controller: wlan_policy::ClientControllerProxy,
512) -> Result<Vec<wlan_policy::ScanResult>, Error> {
513    let (client_proxy, server_end) =
514        client_controller.domain().create_proxy::<wlan_policy::ScanResultIteratorMarker>();
515    let fut = async { client_controller.scan_for_networks(server_end) };
516    run_proxy_command(Box::pin(fut)).await?;
517
518    let mut scanned_networks = Vec::<wlan_policy::ScanResult>::new();
519    loop {
520        match run_proxy_command(Box::pin(client_proxy.get_next())).await? {
521            Ok(mut new_networks) => {
522                if new_networks.is_empty() {
523                    break;
524                }
525                scanned_networks.append(&mut new_networks);
526            }
527            Err(e) => return Err(format_err!("Scan failure error: {:?}", e)),
528        }
529    }
530
531    Ok(scanned_networks)
532}
533
534/// Requests that the policy layer start client connections.
535pub async fn handle_start_client_connections(
536    client_controller: wlan_policy::ClientControllerProxy,
537) -> Result<(), Error> {
538    let status = run_proxy_command(Box::pin(client_controller.start_client_connections())).await?;
539    return handle_request_status(status);
540}
541
542/// Asks the client policy layer to stop client connections.
543pub async fn handle_stop_client_connections(
544    client_controller: wlan_policy::ClientControllerProxy,
545) -> Result<(), Error> {
546    let status = run_proxy_command(Box::pin(client_controller.stop_client_connections())).await?;
547    return handle_request_status(status);
548}
549
550/// Asks the policy layer to start an AP with the user's specified network configuration.
551pub async fn handle_start_ap(
552    ap_controller: wlan_policy::AccessPointControllerProxy,
553    mut server_stream: wlan_policy::AccessPointStateUpdatesRequestStream,
554    config: wlan_policy::NetworkConfig,
555) -> Result<(), Error> {
556    let connectivity_mode = wlan_policy::ConnectivityMode::Unrestricted;
557    let operating_band = wlan_policy::OperatingBand::Any;
558    let result = run_proxy_command(Box::pin(ap_controller.start_access_point(
559        &config,
560        connectivity_mode,
561        operating_band,
562    )))
563    .await?;
564    handle_request_status(result)?;
565
566    // Listen for state updates until the service indicates that there is an active AP.
567    while let Some(update_request) = server_stream.try_next().await? {
568        let update = update_request.into_on_access_point_state_update();
569        let (updates, responder) = match update {
570            Some((update, responder)) => (update, responder),
571            None => return Err(format_err!("AP provider produced invalid update.")),
572        };
573        let _ = responder.send();
574
575        for update in updates {
576            match update.state {
577                Some(state) => match state {
578                    wlan_policy::OperatingState::Failed => {
579                        return Err(format_err!("Failed to start AP."));
580                    }
581                    wlan_policy::OperatingState::Starting => {
582                        println!("AP is starting.");
583                        continue;
584                    }
585                    wlan_policy::OperatingState::Active => return Ok(()),
586                },
587                None => continue,
588            }
589        }
590    }
591    Err(format_err!("Status stream terminated before AP start could be verified."))
592}
593
594/// Requests that the policy layer stop the AP associated with the given network configuration.
595pub async fn handle_stop_ap(
596    ap_controller: wlan_policy::AccessPointControllerProxy,
597    config: wlan_policy::NetworkConfig,
598) -> Result<(), Error> {
599    let result = run_proxy_command(Box::pin(ap_controller.stop_access_point(&config))).await?;
600    handle_request_status(result)
601}
602
603/// Requests that the policy layer stop all AP interfaces.
604pub async fn handle_stop_all_aps(
605    ap_controller: wlan_policy::AccessPointControllerProxy,
606) -> Result<(), Error> {
607    let fut = async { ap_controller.stop_all_access_points() };
608    run_proxy_command(Box::pin(fut)).await?;
609    Ok(())
610}
611
612/// Listens for AP state updates and prints each update that is received.
613pub async fn handle_ap_listen(
614    mut server_stream: wlan_policy::AccessPointStateUpdatesRequestStream,
615    single_sample: bool,
616) -> Result<(), Error> {
617    println!(
618        "{:32} | {:4} | {:8} | {:12} | {:6} | {:4} | {:7}",
619        "SSID", "Type", "State", "Mode", "Band", "Freq", "#Clients"
620    );
621
622    while let Some(update_request) = server_stream.try_next().await? {
623        let updates = update_request.into_on_access_point_state_update();
624        let (updates, responder) = match updates {
625            Some((update, responder)) => (update, responder),
626            None => return Err(format_err!("AP provider produced invalid update.")),
627        };
628        let _ = responder.send();
629
630        for update in updates {
631            let (ssid, security_type) = match update.id {
632                Some(network_id) => {
633                    let ssid = network_id.ssid.clone();
634                    let ssid = String::from_utf8(ssid)?;
635                    let security_type = match network_id.type_ {
636                        wlan_policy::SecurityType::None => "none",
637                        wlan_policy::SecurityType::Wep => "wep",
638                        wlan_policy::SecurityType::Wpa => "wpa",
639                        wlan_policy::SecurityType::Wpa2 => "wpa2",
640                        wlan_policy::SecurityType::Wpa3 => "wpa3",
641                    };
642                    (ssid, security_type.to_string())
643                }
644                None => ("".to_string(), "".to_string()),
645            };
646            let state = match update.state {
647                Some(state) => match state {
648                    wlan_policy::OperatingState::Failed => "failed",
649                    wlan_policy::OperatingState::Starting => "starting",
650                    wlan_policy::OperatingState::Active => "active",
651                },
652                None => "",
653            };
654            let mode = match update.mode {
655                Some(mode) => match mode {
656                    wlan_policy::ConnectivityMode::LocalOnly => "local only",
657                    wlan_policy::ConnectivityMode::Unrestricted => "unrestricted",
658                },
659                None => "",
660            };
661            let band = match update.band {
662                Some(band) => match band {
663                    wlan_policy::OperatingBand::Any => "any",
664                    wlan_policy::OperatingBand::Only24Ghz => "2.4Ghz",
665                    wlan_policy::OperatingBand::Only5Ghz => "5Ghz",
666                },
667                None => "",
668            };
669            let frequency = match update.frequency {
670                Some(frequency) => frequency.to_string(),
671                None => "".to_string(),
672            };
673            let client_count = match update.clients {
674                Some(connected_clients) => match connected_clients.count {
675                    Some(count) => count.to_string(),
676                    None => "".to_string(),
677                },
678                None => "".to_string(),
679            };
680
681            println!(
682                "{:32} | {:4} | {:8} | {:12} | {:6} | {:4} | {:7}",
683                ssid, security_type, state, mode, band, frequency, client_count
684            );
685        }
686
687        // If only one sample is desired, break out and return.
688        if single_sample {
689            break;
690        }
691    }
692    Ok(())
693}
694
695pub async fn handle_suggest_ap_mac(
696    configurator: wlan_deprecated::DeprecatedConfiguratorProxy,
697    mac: MacAddress,
698) -> Result<(), Error> {
699    let mac = net::MacAddress { octets: mac.to_array() };
700    let result = configurator.suggest_access_point_mac_address(&mac).await?;
701    result.map_err(|e| format_err!("suggesting MAC failed: {:?}", e))
702}
703
704#[cfg(test)]
705mod tests {
706    use super::*;
707    use assert_matches::assert_matches;
708    use fidl::endpoints;
709    use fuchsia_async::TestExecutor;
710    use futures::stream::StreamExt;
711    use futures::task::Poll;
712    use std::pin::pin;
713    use test_case::test_case;
714    use zx_status;
715
716    static TEST_SSID: &str = "test_ssid";
717    static TEST_PASSWORD: &str = "test_password";
718
719    struct ClientTestValues {
720        client_proxy: wlan_policy::ClientControllerProxy,
721        client_stream: wlan_policy::ClientControllerRequestStream,
722        update_proxy: wlan_policy::ClientStateUpdatesProxy,
723        update_stream: wlan_policy::ClientStateUpdatesRequestStream,
724    }
725
726    fn client_test_setup() -> ClientTestValues {
727        let (client_proxy, client_stream) =
728            endpoints::create_proxy_and_stream::<wlan_policy::ClientControllerMarker>();
729
730        let (listener_proxy, listener_stream) =
731            endpoints::create_proxy_and_stream::<wlan_policy::ClientStateUpdatesMarker>();
732
733        ClientTestValues {
734            client_proxy: client_proxy,
735            client_stream: client_stream,
736            update_proxy: listener_proxy,
737            update_stream: listener_stream,
738        }
739    }
740
741    /// Allows callers to respond to StartClientConnections, StopClientConnections, and Connect
742    /// calls with the RequestStatus response of their choice.
743    fn send_client_request_status(
744        exec: &mut TestExecutor,
745        server: &mut wlan_policy::ClientControllerRequestStream,
746        response: wlan_policy::RequestStatus,
747    ) {
748        let poll = exec.run_until_stalled(&mut server.next());
749        let request = match poll {
750            Poll::Ready(poll_ready) => poll_ready
751                .expect("poll ready result is None")
752                .expect("poll ready result is an Error"),
753            Poll::Pending => panic!("no ClientController request available"),
754        };
755
756        let result = match request {
757            wlan_policy::ClientControllerRequest::StartClientConnections { responder } => {
758                responder.send(response)
759            }
760            wlan_policy::ClientControllerRequest::StopClientConnections { responder } => {
761                responder.send(response)
762            }
763            wlan_policy::ClientControllerRequest::Connect { responder, .. } => {
764                responder.send(response)
765            }
766            _ => panic!("expecting a request that expects a RequestStatus"),
767        };
768
769        if result.is_err() {
770            panic!("could not send request status");
771        }
772    }
773
774    /// Creates a ClientStateSummary to provide as an update to listeners.
775    fn create_client_state_summary(
776        ssid: &str,
777        state: wlan_policy::ConnectionState,
778    ) -> wlan_policy::ClientStateSummary {
779        let network_state = wlan_policy::NetworkState {
780            id: Some(create_network_id(ssid)),
781            state: Some(state),
782            status: None,
783            ..Default::default()
784        };
785        wlan_policy::ClientStateSummary {
786            state: Some(wlan_policy::WlanClientState::ConnectionsEnabled),
787            networks: Some(vec![network_state]),
788            ..Default::default()
789        }
790    }
791
792    /// Creates an AccessPointStateSummary to provide as an update to listeners.
793    fn create_ap_state_summary(
794        state: wlan_policy::OperatingState,
795    ) -> wlan_policy::AccessPointState {
796        wlan_policy::AccessPointState {
797            id: None,
798            state: Some(state),
799            mode: None,
800            band: None,
801            frequency: None,
802            clients: None,
803            ..Default::default()
804        }
805    }
806
807    /// Allows callers to send a response to SaveNetwork and ForgetNetwork calls.
808    #[track_caller]
809    fn send_network_config_response(
810        exec: &mut TestExecutor,
811        server: &mut wlan_policy::ClientControllerRequestStream,
812        success: bool,
813    ) {
814        let poll = exec.run_until_stalled(&mut server.next());
815        let request = match poll {
816            Poll::Ready(poll_ready) => poll_ready
817                .expect("poll ready result is None")
818                .expect("poll ready result is an Error"),
819            Poll::Pending => panic!("no ClientController request available"),
820        };
821
822        let result = match request {
823            wlan_policy::ClientControllerRequest::SaveNetwork { config: _, responder } => {
824                if success {
825                    responder.send(Ok(()))
826                } else {
827                    responder.send(Err(wlan_policy::NetworkConfigChangeError::GeneralError))
828                }
829            }
830            wlan_policy::ClientControllerRequest::ForgetNetwork { id: _, responder } => {
831                if success {
832                    responder.send(Ok(()))
833                } else {
834                    responder.send(Err(wlan_policy::NetworkConfigChangeError::GeneralError))
835                }
836            }
837            _ => panic!("expecting a request that optionally receives a NetworkConfigChangeError"),
838        };
839
840        if result.is_err() {
841            panic!("could not send network config response");
842        }
843    }
844
845    struct ApTestValues {
846        ap_proxy: wlan_policy::AccessPointControllerProxy,
847        ap_stream: wlan_policy::AccessPointControllerRequestStream,
848        update_proxy: wlan_policy::AccessPointStateUpdatesProxy,
849        update_stream: wlan_policy::AccessPointStateUpdatesRequestStream,
850    }
851
852    fn ap_test_setup() -> ApTestValues {
853        let (ap_proxy, ap_stream) =
854            endpoints::create_proxy_and_stream::<wlan_policy::AccessPointControllerMarker>();
855
856        let (listener_proxy, listener_stream) =
857            endpoints::create_proxy_and_stream::<wlan_policy::AccessPointStateUpdatesMarker>();
858
859        ApTestValues {
860            ap_proxy,
861            ap_stream,
862            update_proxy: listener_proxy,
863            update_stream: listener_stream,
864        }
865    }
866
867    /// Allows callers to respond to StartAccessPoint and StopAccessPoint
868    /// calls with the RequestStatus response of their choice.
869    fn send_ap_request_status(
870        exec: &mut TestExecutor,
871        server: &mut wlan_policy::AccessPointControllerRequestStream,
872        response: wlan_policy::RequestStatus,
873    ) {
874        let poll = exec.run_until_stalled(&mut server.next());
875        let request = match poll {
876            Poll::Ready(poll_ready) => poll_ready
877                .expect("poll ready result is None")
878                .expect("poll ready result is an Error"),
879            Poll::Pending => panic!("no AccessPointController request available"),
880        };
881
882        let result = match request {
883            wlan_policy::AccessPointControllerRequest::StartAccessPoint { responder, .. } => {
884                responder.send(response)
885            }
886            wlan_policy::AccessPointControllerRequest::StopAccessPoint { config: _, responder } => {
887                responder.send(response)
888            }
889            _ => panic!("expecting a request that expects a RequestStatus"),
890        };
891
892        if result.is_err() {
893            panic!("could not send request status");
894        }
895    }
896
897    /// Creates a NetworkIdentifier for use in tests.
898    fn create_network_id(ssid: &str) -> wlan_policy::NetworkIdentifier {
899        wlan_policy::NetworkIdentifier {
900            ssid: ssid.as_bytes().to_vec(),
901            type_: wlan_policy::SecurityType::Wpa2,
902        }
903    }
904
905    /// Creates a NetworkConfig for use in tests.
906    fn create_network_config(ssid: &str) -> wlan_policy::NetworkConfig {
907        wlan_policy::NetworkConfig {
908            id: Some(create_network_id(ssid)),
909            credential: Some(create_password(TEST_PASSWORD)),
910            ..Default::default()
911        }
912    }
913
914    fn create_password(val: &str) -> wlan_policy::Credential {
915        wlan_policy::Credential::Password(val.as_bytes().to_vec())
916    }
917
918    /// Creates a NetworkConfig for use in tests.
919    fn create_network_config_with_security(
920        ssid: &str,
921        security: wlan_policy::SecurityType,
922    ) -> wlan_policy::NetworkConfig {
923        let id = wlan_policy::NetworkIdentifier { ssid: ssid.as_bytes().to_vec(), type_: security };
924        wlan_policy::NetworkConfig { id: Some(id), credential: None, ..Default::default() }
925    }
926
927    /// Create a scan result to be sent as a response to a scan request.
928    fn create_scan_result(ssid: &str) -> wlan_policy::ScanResult {
929        wlan_policy::ScanResult {
930            id: Some(create_network_id(ssid)),
931            entries: None,
932            compatibility: None,
933            ..Default::default()
934        }
935    }
936
937    /// Respond to a ScanForNetworks request and provide an iterator so that tests can inject scan
938    /// results.
939    fn get_scan_result_iterator(
940        exec: &mut TestExecutor,
941        mut server: wlan_policy::ClientControllerRequestStream,
942    ) -> wlan_policy::ScanResultIteratorRequestStream {
943        let poll = exec.run_until_stalled(&mut server.next());
944        let request = match poll {
945            Poll::Ready(poll_ready) => poll_ready
946                .expect("poll ready result is None")
947                .expect("poll ready result is an Error"),
948            Poll::Pending => panic!("no ClientController request available"),
949        };
950
951        match request {
952            wlan_policy::ClientControllerRequest::ScanForNetworks {
953                iterator,
954                control_handle: _,
955            } => iterator.into_stream(),
956            _ => panic!("expecting a ScanForNetworks"),
957        }
958    }
959
960    /// Sends scan results back to the client that is requesting a scan.
961    fn send_scan_result(
962        exec: &mut TestExecutor,
963        server: &mut wlan_policy::ScanResultIteratorRequestStream,
964        scan_result: Result<&[wlan_policy::ScanResult], wlan_policy::ScanErrorCode>,
965    ) {
966        assert_matches!(
967            exec.run_until_stalled(&mut server.next()),
968            Poll::Ready(Some(Ok(wlan_policy::ScanResultIteratorRequest::GetNext {
969                responder
970            }))) => {
971                match responder.send(scan_result) {
972                    Ok(()) => {}
973                    Err(e) => panic!("failed to send scan result: {}", e),
974                }
975            }
976        );
977    }
978
979    /// Responds to a GetSavedNetworks request and provide an iterator for sending back saved
980    /// networks.
981    #[track_caller]
982    fn get_saved_networks_iterator(
983        exec: &mut TestExecutor,
984        server: &mut wlan_policy::ClientControllerRequestStream,
985    ) -> wlan_policy::NetworkConfigIteratorRequestStream {
986        let poll = exec.run_until_stalled(&mut server.next());
987        let request = match poll {
988            Poll::Ready(poll_ready) => poll_ready
989                .expect("poll ready result is None")
990                .expect("poll ready result is an Error"),
991            Poll::Pending => panic!("no ClientController request available"),
992        };
993
994        match request {
995            wlan_policy::ClientControllerRequest::GetSavedNetworks {
996                iterator,
997                control_handle: _,
998            } => iterator.into_stream(),
999            _ => panic!("expecting a ScanForNetworks"),
1000        }
1001    }
1002
1003    /// Uses the provided iterator and send back saved networks.
1004    fn send_saved_networks(
1005        exec: &mut TestExecutor,
1006        server: &mut wlan_policy::NetworkConfigIteratorRequestStream,
1007        saved_networks_response: Vec<wlan_policy::NetworkConfig>,
1008    ) {
1009        assert_matches!(
1010            exec.run_until_stalled(&mut server.next()),
1011            Poll::Ready(Some(Ok(wlan_policy::NetworkConfigIteratorRequest::GetNext {
1012                responder
1013            }))) => {
1014                match responder.send(&saved_networks_response) {
1015                    Ok(()) => {}
1016                    Err(e) => panic!("failed to send saved networks: {}", e),
1017                }
1018            }
1019        );
1020    }
1021
1022    /// Tests the case where start client connections is called and the operation is successful.
1023    #[fuchsia::test]
1024    fn test_start_client_connections_success() {
1025        let mut exec = TestExecutor::new();
1026        let mut test_values = client_test_setup();
1027        let fut = handle_start_client_connections(test_values.client_proxy);
1028        let mut fut = pin!(fut);
1029
1030        // Wait for the fidl request to go out to start client connections
1031        assert!(exec.run_until_stalled(&mut fut).is_pending());
1032
1033        // Send back an acknowledgement
1034        send_client_request_status(
1035            &mut exec,
1036            &mut test_values.client_stream,
1037            wlan_policy::RequestStatus::Acknowledged,
1038        );
1039
1040        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1041    }
1042
1043    /// Tests the case where starting client connections fails.
1044    #[fuchsia::test]
1045    fn test_start_client_connections_fail() {
1046        let mut exec = TestExecutor::new();
1047        let mut test_values = client_test_setup();
1048        let fut = handle_start_client_connections(test_values.client_proxy);
1049        let mut fut = pin!(fut);
1050
1051        // Wait for the fidl request to go out to start client connections
1052        assert!(exec.run_until_stalled(&mut fut).is_pending());
1053
1054        // Send back an acknowledgement
1055        send_client_request_status(
1056            &mut exec,
1057            &mut test_values.client_stream,
1058            wlan_policy::RequestStatus::RejectedNotSupported,
1059        );
1060
1061        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1062    }
1063
1064    /// Tests the case where starting client connections is successful.
1065    #[fuchsia::test]
1066    fn test_stop_client_connections_success() {
1067        let mut exec = TestExecutor::new();
1068        let mut test_values = client_test_setup();
1069        let fut = handle_stop_client_connections(test_values.client_proxy);
1070        let mut fut = pin!(fut);
1071
1072        // Wait for the fidl request to go out to stop client connections
1073        assert!(exec.run_until_stalled(&mut fut).is_pending());
1074
1075        // Send back an acknowledgement
1076        send_client_request_status(
1077            &mut exec,
1078            &mut test_values.client_stream,
1079            wlan_policy::RequestStatus::Acknowledged,
1080        );
1081
1082        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1083    }
1084
1085    /// Tests the case where starting client connections fails.
1086    #[fuchsia::test]
1087    fn test_stop_client_connections_fail() {
1088        let mut exec = TestExecutor::new();
1089        let mut test_values = client_test_setup();
1090        let fut = handle_stop_client_connections(test_values.client_proxy);
1091        let mut fut = pin!(fut);
1092
1093        // Wait for the fidl request to go out to stop client connections
1094        assert!(exec.run_until_stalled(&mut fut).is_pending());
1095
1096        // Send back an acknowledgement
1097        send_client_request_status(
1098            &mut exec,
1099            &mut test_values.client_stream,
1100            wlan_policy::RequestStatus::RejectedNotSupported,
1101        );
1102
1103        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1104    }
1105
1106    /// Tests the case where a network is successfully saved.
1107    #[fuchsia::test]
1108    fn test_save_network_pass() {
1109        let mut exec = TestExecutor::new();
1110        let mut test_values = client_test_setup();
1111        let config = create_network_config(TEST_SSID);
1112        let fut = handle_save_network(test_values.client_proxy, config);
1113        let mut fut = pin!(fut);
1114
1115        // Wait for the fidl request to go out to save a network
1116        assert!(exec.run_until_stalled(&mut fut).is_pending());
1117
1118        // Drop the remote channel indicating success
1119        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1120
1121        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1122    }
1123
1124    /// Tests the case where a network configuration cannot be saved.
1125    #[fuchsia::test]
1126    fn test_save_network_fail() {
1127        let mut exec = TestExecutor::new();
1128        let mut test_values = client_test_setup();
1129        let config = create_network_config(TEST_SSID);
1130        let fut = handle_save_network(test_values.client_proxy, config);
1131        let mut fut = pin!(fut);
1132
1133        // Wait for the fidl request to go out to save a network
1134        assert!(exec.run_until_stalled(&mut fut).is_pending());
1135
1136        // Send back an error
1137        send_network_config_response(&mut exec, &mut test_values.client_stream, false);
1138
1139        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1140    }
1141
1142    /// Tests the case where a network config can be successfully removed.
1143    #[fuchsia::test]
1144    fn test_forget_network_pass() {
1145        let mut exec = TestExecutor::new();
1146        let mut test_values = client_test_setup();
1147        let security = Some(wlan_policy::SecurityType::Wpa2);
1148        let fut = handle_forget_network(test_values.client_proxy, TEST_SSID.into(), security);
1149        let mut fut = pin!(fut);
1150        assert!(exec.run_until_stalled(&mut fut).is_pending());
1151
1152        // Respond to the get saved networks request for checking whether anything will be removed
1153        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1154        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1155        assert!(exec.run_until_stalled(&mut fut).is_pending());
1156        send_saved_networks(&mut exec, &mut iterator, vec![]);
1157
1158        // Wait for the fidl request to go out to remove a network
1159        assert!(exec.run_until_stalled(&mut fut).is_pending());
1160
1161        // Drop the remote channel indicating success
1162        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1163        assert!(exec.run_until_stalled(&mut fut).is_pending());
1164
1165        // Respond to the get saved networks request for checking whether anything was removed
1166        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1167        send_saved_networks(&mut exec, &mut iterator, vec![]);
1168
1169        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1170    }
1171
1172    /// Tests the case where a network config can be successfully removed when only the SSID of the
1173    /// network is provided as an argument. The saved network to remove is found by getting saved
1174    /// networks.
1175    #[fuchsia::test]
1176    fn test_forget_network_ssid_only_pass() {
1177        let mut exec = TestExecutor::new();
1178        let mut test_values = client_test_setup();
1179
1180        // Request to remove a network by only specifying the SSID of the saved network.
1181        let security = None;
1182        let fut = handle_forget_network(test_values.client_proxy, TEST_SSID.into(), security);
1183        let mut fut = pin!(fut);
1184
1185        // Wait for the request to get saved networks
1186        assert!(exec.run_until_stalled(&mut fut).is_pending());
1187
1188        // Send back a network matching the SSID requested.
1189        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1190        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1191        assert!(exec.run_until_stalled(&mut fut).is_pending());
1192        send_saved_networks(&mut exec, &mut iterator, vec![]);
1193
1194        // Wait for the fidl request to go out to remove a network
1195        assert!(exec.run_until_stalled(&mut fut).is_pending());
1196
1197        // Drop the remote channel indicating success
1198        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1199        assert!(exec.run_until_stalled(&mut fut).is_pending());
1200
1201        // Respond to the get saved networks request for checking whether anything was removed
1202        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1203        send_saved_networks(&mut exec, &mut iterator, vec![]);
1204
1205        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1206    }
1207
1208    /// Tests the case where network removal fails.
1209    #[fuchsia::test]
1210    fn test_forget_network_fail() {
1211        let mut exec = TestExecutor::new();
1212        let mut test_values = client_test_setup();
1213        let security = Some(wlan_policy::SecurityType::Wpa2);
1214        let fut = handle_forget_network(test_values.client_proxy, TEST_SSID.into(), security);
1215        let mut fut = pin!(fut);
1216        assert!(exec.run_until_stalled(&mut fut).is_pending());
1217
1218        // Respond to the get saved networks request for checking whether anything will be removed
1219        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1220        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1221        assert!(exec.run_until_stalled(&mut fut).is_pending());
1222        send_saved_networks(&mut exec, &mut iterator, vec![]);
1223
1224        // Wait for the fidl request to go out to remove a network
1225        assert!(exec.run_until_stalled(&mut fut).is_pending());
1226
1227        // Send back an error
1228        send_network_config_response(&mut exec, &mut test_values.client_stream, false);
1229
1230        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1231    }
1232
1233    /// Tests the case where network removal returns no error but the network is still present.
1234    #[fuchsia::test]
1235    fn test_forget_network_not_removed_fails() {
1236        let mut exec = TestExecutor::new();
1237        let mut test_values = client_test_setup();
1238        let security = Some(wlan_policy::SecurityType::Wpa2);
1239        let fut = handle_forget_network(test_values.client_proxy, TEST_SSID.into(), security);
1240        let mut fut = pin!(fut);
1241        assert!(exec.run_until_stalled(&mut fut).is_pending());
1242
1243        // Respond to the get saved networks request for checking whether anything will be removed
1244        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1245        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1246        assert!(exec.run_until_stalled(&mut fut).is_pending());
1247        send_saved_networks(&mut exec, &mut iterator, vec![]);
1248
1249        // Wait for the fidl request to go out to remove a network
1250        assert!(exec.run_until_stalled(&mut fut).is_pending());
1251
1252        // Drop the remote channel indicating success
1253        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1254        assert!(exec.run_until_stalled(&mut fut).is_pending());
1255
1256        // Respond to the get saved networks request for checking whether anything was removed with
1257        // the network that should have been removed.
1258        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1259        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1260        assert!(exec.run_until_stalled(&mut fut).is_pending());
1261        send_saved_networks(&mut exec, &mut iterator, vec![]);
1262
1263        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1264    }
1265
1266    /// Tests the config_matches function which compares a config against optional arguments if
1267    /// argument is provided. Test various combinations of present and non-present dimensions.
1268    /// test variants are compared against the default config from create_config.
1269    #[test_case(TEST_SSID, Some(wlan_policy::SecurityType::Wpa2), true)]
1270    #[test_case(TEST_SSID, None, true)]
1271    #[test_case(TEST_SSID, Some(wlan_policy::SecurityType::Wpa3), false)]
1272    #[test_case("otherssid", Some(wlan_policy::SecurityType::Wpa2), false)]
1273    #[fuchsia::test(add_test_attr = false)]
1274    fn test_config_matches_config(
1275        ssid: &str,
1276        security: Option<wlan_policy::SecurityType>,
1277        expected_result: bool,
1278    ) {
1279        let ssid = ssid.as_bytes().to_vec();
1280        let config = create_network_config(TEST_SSID);
1281        let result = config_matches(&config, &ssid, &security);
1282        assert_eq!(result, expected_result);
1283    }
1284
1285    /// Tests the case where the client successfully connects.
1286    #[fuchsia::test]
1287    fn test_connect_pass() {
1288        let mut exec = TestExecutor::new();
1289        let mut test_values = client_test_setup();
1290
1291        // Start the connect routine.
1292        let ssid = TEST_SSID.to_string();
1293        let security = Some(wlan_policy::SecurityType::Wpa2);
1294        let fut =
1295            handle_connect(test_values.client_proxy, test_values.update_stream, ssid, security);
1296        let mut fut = pin!(fut);
1297
1298        // The function should now stall out waiting on the connect call to go out
1299        assert!(exec.run_until_stalled(&mut fut).is_pending());
1300
1301        // Send back a positive acknowledgement
1302        send_client_request_status(
1303            &mut exec,
1304            &mut test_values.client_stream,
1305            wlan_policy::RequestStatus::Acknowledged,
1306        );
1307
1308        // The client should now wait for events from the listener.  Send a Connecting update.
1309        assert!(exec.run_until_stalled(&mut fut).is_pending());
1310        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1311            TEST_SSID,
1312            wlan_policy::ConnectionState::Connecting,
1313        ));
1314
1315        // The client should stall and then wait for a Connected message.  Send that over.
1316        assert!(exec.run_until_stalled(&mut fut).is_pending());
1317        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1318            TEST_SSID,
1319            wlan_policy::ConnectionState::Connected,
1320        ));
1321
1322        // The connect process should complete after receiving the Connected status response
1323        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1324    }
1325
1326    /// Tests the case where the security type is not specified, but it is automatically chosen
1327    /// because a matching network is already saved. The connection succeeds.
1328    #[fuchsia::test]
1329    fn test_connect_unspecified_security_pass() {
1330        let mut exec = TestExecutor::new();
1331        let mut test_values = client_test_setup();
1332
1333        // Start the connect routine without giving a security type.
1334        let ssid = TEST_SSID.to_string();
1335        let fut = handle_connect(test_values.client_proxy, test_values.update_stream, ssid, None);
1336        let mut fut = pin!(fut);
1337
1338        // The function should now stall out waiting on the get saved networks call to go out.
1339        assert!(exec.run_until_stalled(&mut fut).is_pending());
1340
1341        // Send back a network matching the SSID requested.
1342        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1343        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1344        assert!(exec.run_until_stalled(&mut fut).is_pending());
1345
1346        // Send back an empty set of configs to indicate that the process is complete
1347        send_saved_networks(&mut exec, &mut iterator, vec![]);
1348
1349        // The function should now stall out waiting on the connect call to go out
1350        assert!(exec.run_until_stalled(&mut fut).is_pending());
1351
1352        // Send back a positive acknowledgement
1353        send_client_request_status(
1354            &mut exec,
1355            &mut test_values.client_stream,
1356            wlan_policy::RequestStatus::Acknowledged,
1357        );
1358
1359        // The client should now wait for events from the listener.  Send a Connecting update.
1360        assert!(exec.run_until_stalled(&mut fut).is_pending());
1361        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1362            TEST_SSID,
1363            wlan_policy::ConnectionState::Connecting,
1364        ));
1365
1366        // The client should stall and then wait for a Connected message.  Send that over.
1367        assert!(exec.run_until_stalled(&mut fut).is_pending());
1368        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1369            TEST_SSID,
1370            wlan_policy::ConnectionState::Connected,
1371        ));
1372
1373        // The connect process should complete after receiving the Connected status response
1374        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1375    }
1376
1377    /// Tests the case where a user doesn't provide a security type and nothing matching is saved.
1378    /// In this case there should be no connect request and an error should be returned.
1379    #[fuchsia::test]
1380    fn test_connect_no_security_given_nothing_saved_fails() {
1381        let mut exec = TestExecutor::new();
1382        let mut test_values = client_test_setup();
1383
1384        // Start the connect routine.
1385        let ssid = TEST_SSID.to_string();
1386        let fut = handle_connect(test_values.client_proxy, test_values.update_stream, ssid, None);
1387        let mut fut = pin!(fut);
1388
1389        // Progress future forward until it waits on a get saved networks call.
1390        assert!(exec.run_until_stalled(&mut fut).is_pending());
1391
1392        // Respond to the get saved networks request with no matches
1393        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1394        send_saved_networks(&mut exec, &mut iterator, vec![]);
1395
1396        // The connect routine should return an error.
1397        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1398
1399        // No connect request should have been sent through FIDL.
1400        assert_matches!(
1401            exec.run_until_stalled(&mut test_values.client_stream.next()),
1402            Poll::Ready(None)
1403        );
1404    }
1405
1406    /// Tests the case where a user doesn't provide a security type and multiple matching networks
1407    /// are saved. In this case there should be no connect request and an error should be returned.
1408    #[fuchsia::test]
1409    fn test_connect_no_security_given_multiple_saved_fails() {
1410        let mut exec = TestExecutor::new();
1411        let mut test_values = client_test_setup();
1412
1413        // Start the connect routine.
1414        let ssid = TEST_SSID.to_string();
1415        let fut = handle_connect(test_values.client_proxy, test_values.update_stream, ssid, None);
1416        let mut fut = pin!(fut);
1417
1418        // Progress future forward until it waits on a get saved networks call.
1419        assert!(exec.run_until_stalled(&mut fut).is_pending());
1420
1421        // Send back 2 networks matching the SSID requested.
1422        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1423        let wpa_config =
1424            create_network_config_with_security(TEST_SSID, wlan_policy::SecurityType::Wpa);
1425        let networks = vec![create_network_config(TEST_SSID), wpa_config];
1426
1427        send_saved_networks(&mut exec, &mut iterator, networks);
1428        assert!(exec.run_until_stalled(&mut fut).is_pending());
1429
1430        // Send back an empty set of configs to indicate that the process is complete
1431        send_saved_networks(&mut exec, &mut iterator, vec![]);
1432
1433        // The connect routine should return an error since there are multiple matches.
1434        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1435
1436        // No connect request should have been sent through FIDL.
1437        assert_matches!(
1438            exec.run_until_stalled(&mut test_values.client_stream.next()),
1439            Poll::Ready(None)
1440        );
1441    }
1442
1443    /// Tests the case where a client fails to connect.
1444    #[fuchsia::test]
1445    fn test_connect_fail() {
1446        let mut exec = TestExecutor::new();
1447        let mut test_values = client_test_setup();
1448
1449        // Start the connect routine.
1450        let ssid = TEST_SSID.to_string();
1451        let security = Some(wlan_policy::SecurityType::Wpa2);
1452        let fut =
1453            handle_connect(test_values.client_proxy, test_values.update_stream, ssid, security);
1454        let mut fut = pin!(fut);
1455
1456        // The function should now stall out waiting on the connect call to go out
1457        assert!(exec.run_until_stalled(&mut fut).is_pending());
1458
1459        // Send back a positive acknowledgement
1460        send_client_request_status(
1461            &mut exec,
1462            &mut test_values.client_stream,
1463            wlan_policy::RequestStatus::Acknowledged,
1464        );
1465
1466        // The client should now wait for events from the listener
1467        assert!(exec.run_until_stalled(&mut fut).is_pending());
1468        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1469            TEST_SSID,
1470            wlan_policy::ConnectionState::Failed,
1471        ));
1472
1473        // The connect process should return an error after receiving a Failed status
1474        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1475    }
1476
1477    /// Tests the case where a scan is requested and results are sent back to the requester.
1478    #[fuchsia::test]
1479    fn test_scan_pass() {
1480        let mut exec = TestExecutor::new();
1481        let test_values = client_test_setup();
1482
1483        let fut = handle_scan(test_values.client_proxy);
1484        let mut fut = pin!(fut);
1485
1486        // The function should now stall out waiting on the scan call to go out
1487        assert!(exec.run_until_stalled(&mut fut).is_pending());
1488
1489        // Send back a scan result
1490        let mut iterator = get_scan_result_iterator(&mut exec, test_values.client_stream);
1491        send_scan_result(&mut exec, &mut iterator, Ok(&[create_scan_result(TEST_SSID)]));
1492
1493        // Process the scan result
1494        assert!(exec.run_until_stalled(&mut fut).is_pending());
1495
1496        // Send back an empty scan result
1497        send_scan_result(&mut exec, &mut iterator, Ok(&[]));
1498
1499        // Expect the scan process to complete
1500        assert_matches!(
1501            exec.run_until_stalled(&mut fut),
1502            Poll::Ready(Ok(result)) => {
1503                assert_eq!(result.len(), 1);
1504                assert_eq!(result[0].id.as_ref().unwrap().ssid, TEST_SSID.as_bytes().to_vec());
1505            }
1506        );
1507    }
1508
1509    /// Tests the case where the scan cannot be performed.
1510    #[fuchsia::test]
1511    fn test_scan_fail() {
1512        let mut exec = TestExecutor::new();
1513        let test_values = client_test_setup();
1514
1515        let fut = handle_scan(test_values.client_proxy);
1516        let mut fut = pin!(fut);
1517
1518        // The function should now stall out waiting on the scan call to go out
1519        assert!(exec.run_until_stalled(&mut fut).is_pending());
1520
1521        // Send back a scan error
1522        let mut iterator = get_scan_result_iterator(&mut exec, test_values.client_stream);
1523        send_scan_result(&mut exec, &mut iterator, Err(wlan_policy::ScanErrorCode::GeneralError));
1524
1525        // Process the scan error
1526        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1527    }
1528
1529    /// Tests the ability to get saved networks from the client policy layer.
1530    #[fuchsia::test]
1531    fn test_get_saved_networks() {
1532        let mut exec = TestExecutor::new();
1533        let mut test_values = client_test_setup();
1534
1535        let fut = handle_get_saved_networks(&test_values.client_proxy);
1536        let mut fut = pin!(fut);
1537
1538        // The future should stall out waiting on the get saved networks request
1539        assert!(exec.run_until_stalled(&mut fut).is_pending());
1540
1541        // Send back a saved networks response
1542        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1543        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1544        assert!(exec.run_until_stalled(&mut fut).is_pending());
1545
1546        // Send back an empty set of configs to indicate that the process is complete
1547        send_saved_networks(&mut exec, &mut iterator, vec![]);
1548
1549        // Verify that the saved networks response was recorded properly
1550        assert_matches!(
1551            exec.run_until_stalled(&mut fut),
1552            Poll::Ready(Ok(result)) => {
1553                assert_eq!(result.len(), 1);
1554                assert_eq!(result[0].id.as_ref().unwrap().ssid, TEST_SSID.as_bytes().to_vec());
1555            }
1556        );
1557    }
1558
1559    /// Tests to ensure that the listening loop continues to be active after receiving client state updates.
1560    #[fuchsia::test]
1561    fn test_client_listen() {
1562        let mut exec = TestExecutor::new();
1563        let test_values = client_test_setup();
1564
1565        let fut = handle_listen(test_values.update_stream, false);
1566        let mut fut = pin!(fut);
1567
1568        // Listen should stall waiting for updates
1569        assert!(exec.run_until_stalled(&mut fut).is_pending());
1570        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1571            TEST_SSID,
1572            wlan_policy::ConnectionState::Connecting,
1573        ));
1574
1575        // Listen should process the message and stall again
1576        assert!(exec.run_until_stalled(&mut fut).is_pending());
1577        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1578            TEST_SSID,
1579            wlan_policy::ConnectionState::Connected,
1580        ));
1581
1582        // Listener future should continue to run but stall waiting for more updates
1583        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1584    }
1585
1586    /// Tests to ensure that querying client status returns after the first update.
1587    #[fuchsia::test]
1588    fn test_client_status() {
1589        let mut exec = TestExecutor::new();
1590        let test_values = client_test_setup();
1591
1592        let fut = handle_listen(test_values.update_stream, true);
1593        let mut fut = pin!(fut);
1594
1595        // Listen should stall waiting for updates
1596        assert!(exec.run_until_stalled(&mut fut).is_pending());
1597        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1598            TEST_SSID,
1599            wlan_policy::ConnectionState::Connecting,
1600        ));
1601
1602        // Listener future should complete.
1603        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1604    }
1605
1606    /// Tests the case where an AP is requested to be stopped and the policy service returns an
1607    /// error.
1608    #[fuchsia::test]
1609    fn test_stop_ap_fail() {
1610        let mut exec = TestExecutor::new();
1611        let mut test_values = ap_test_setup();
1612
1613        let network_config = create_network_config(&TEST_SSID);
1614        let fut = handle_stop_ap(test_values.ap_proxy, network_config);
1615        let mut fut = pin!(fut);
1616
1617        // The request should stall waiting for the service
1618        assert!(exec.run_until_stalled(&mut fut).is_pending());
1619
1620        // Send back a rejection
1621        send_ap_request_status(
1622            &mut exec,
1623            &mut test_values.ap_stream,
1624            wlan_policy::RequestStatus::RejectedNotSupported,
1625        );
1626
1627        // Run the request to completion
1628        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1629    }
1630
1631    /// Tests the case where an AP is successfully stopped.
1632    #[fuchsia::test]
1633    fn test_stop_ap_pass() {
1634        let mut exec = TestExecutor::new();
1635        let mut test_values = ap_test_setup();
1636
1637        let network_config = create_network_config(&TEST_SSID);
1638        let fut = handle_stop_ap(test_values.ap_proxy, network_config);
1639        let mut fut = pin!(fut);
1640
1641        // The request should stall waiting for the service
1642        assert!(exec.run_until_stalled(&mut fut).is_pending());
1643
1644        // Send back an acknowledgement
1645        send_ap_request_status(
1646            &mut exec,
1647            &mut test_values.ap_stream,
1648            wlan_policy::RequestStatus::Acknowledged,
1649        );
1650
1651        // Run the request to completion
1652        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1653    }
1654
1655    /// Tests the case where the request to start an AP results in an error.
1656    #[fuchsia::test]
1657    fn test_start_ap_request_fail() {
1658        let mut exec = TestExecutor::new();
1659        let mut test_values = ap_test_setup();
1660
1661        let network_config = create_network_config(&TEST_SSID);
1662        let fut = handle_start_ap(test_values.ap_proxy, test_values.update_stream, network_config);
1663        let mut fut = pin!(fut);
1664
1665        // The request should stall waiting for the service
1666        assert!(exec.run_until_stalled(&mut fut).is_pending());
1667
1668        // Send back a rejection
1669        send_ap_request_status(
1670            &mut exec,
1671            &mut test_values.ap_stream,
1672            wlan_policy::RequestStatus::RejectedNotSupported,
1673        );
1674
1675        // Run the request to completion
1676        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1677    }
1678
1679    /// Tests the case where the start AP process returns an acknowledgement.  The tool should then
1680    /// wait for an update indicating that the AP is active.
1681    #[fuchsia::test]
1682    fn test_start_ap_pass() {
1683        let mut exec = TestExecutor::new();
1684        let mut test_values = ap_test_setup();
1685
1686        let network_config = create_network_config(&TEST_SSID);
1687        let fut = handle_start_ap(test_values.ap_proxy, test_values.update_stream, network_config);
1688        let mut fut = pin!(fut);
1689
1690        // The request should stall waiting for the service
1691        assert!(exec.run_until_stalled(&mut fut).is_pending());
1692
1693        // Send back an acknowledgement
1694        send_ap_request_status(
1695            &mut exec,
1696            &mut test_values.ap_stream,
1697            wlan_policy::RequestStatus::Acknowledged,
1698        );
1699
1700        // Progress the future so that it waits for AP state updates
1701        assert!(exec.run_until_stalled(&mut fut).is_pending());
1702
1703        // First send a `Starting` status.
1704        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1705            wlan_policy::OperatingState::Starting,
1706        )]);
1707
1708        // Future should still be waiting to see that the AP to be active
1709        assert!(exec.run_until_stalled(&mut fut).is_pending());
1710
1711        // Send the response indicating that the AP is active
1712        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1713            wlan_policy::OperatingState::Active,
1714        )]);
1715
1716        // Run the request to completion
1717        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1718    }
1719
1720    /// Tests the case where the AP start command is successfully sent, but the AP fails during
1721    /// the startup process.
1722    #[fuchsia::test]
1723    fn test_ap_failed_to_start() {
1724        let mut exec = TestExecutor::new();
1725        let mut test_values = ap_test_setup();
1726
1727        let network_config = create_network_config(&TEST_SSID);
1728        let fut = handle_start_ap(test_values.ap_proxy, test_values.update_stream, network_config);
1729        let mut fut = pin!(fut);
1730
1731        // The request should stall waiting for the service
1732        assert!(exec.run_until_stalled(&mut fut).is_pending());
1733
1734        // Send back an acknowledgement
1735        send_ap_request_status(
1736            &mut exec,
1737            &mut test_values.ap_stream,
1738            wlan_policy::RequestStatus::Acknowledged,
1739        );
1740
1741        // Progress the future so that it waits for AP state updates
1742        assert!(exec.run_until_stalled(&mut fut).is_pending());
1743
1744        // Send back a failure
1745        let state_updates = &[create_ap_state_summary(wlan_policy::OperatingState::Failed)];
1746        let _ = test_values.update_proxy.on_access_point_state_update(state_updates);
1747
1748        // Expect that the future returns an error
1749        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1750    }
1751
1752    /// Tests the case where all APs are requested to be stopped.
1753    #[fuchsia::test]
1754    fn test_stop_all_aps() {
1755        let mut exec = TestExecutor::new();
1756        let mut test_values = ap_test_setup();
1757
1758        let fut = handle_stop_all_aps(test_values.ap_proxy);
1759        let mut fut = pin!(fut);
1760
1761        // The future should finish immediately
1762        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1763
1764        // Make sure that the request is seen on the request stream
1765        assert_matches!(
1766            exec.run_until_stalled(&mut test_values.ap_stream.next()),
1767            Poll::Ready(Some(Ok(
1768                wlan_policy::AccessPointControllerRequest::StopAllAccessPoints { .. }
1769            )))
1770        );
1771    }
1772
1773    /// Tests that the AP listen routine continues listening for new updates.
1774    #[fuchsia::test]
1775    fn test_ap_listen() {
1776        let mut exec = TestExecutor::new();
1777        let test_values = ap_test_setup();
1778
1779        let fut = handle_ap_listen(test_values.update_stream, false);
1780        let mut fut = pin!(fut);
1781
1782        // Listen should stall waiting for updates
1783        assert!(exec.run_until_stalled(&mut fut).is_pending());
1784        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1785            wlan_policy::OperatingState::Starting,
1786        )]);
1787
1788        // Listen should process the message and stall again
1789        assert!(exec.run_until_stalled(&mut fut).is_pending());
1790        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1791            wlan_policy::OperatingState::Active,
1792        )]);
1793
1794        // Process message and stall again
1795        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1796        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1797            wlan_policy::OperatingState::Failed,
1798        )]);
1799
1800        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1801    }
1802
1803    /// Tests that the AP status routine only takes a single update.
1804    #[fuchsia::test]
1805    fn test_ap_status() {
1806        let mut exec = TestExecutor::new();
1807        let test_values = ap_test_setup();
1808
1809        let fut = handle_ap_listen(test_values.update_stream, true);
1810        let mut fut = pin!(fut);
1811
1812        // Listen should stall waiting for updates
1813        assert!(exec.run_until_stalled(&mut fut).is_pending());
1814        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1815            wlan_policy::OperatingState::Starting,
1816        )]);
1817
1818        // The future should complete now that a single update has been processed.
1819        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1820    }
1821
1822    #[fuchsia::test]
1823    fn test_suggest_ap_mac_succeeds() {
1824        let mut exec = TestExecutor::new();
1825
1826        let (configurator_proxy, mut configurator_stream) =
1827            endpoints::create_proxy_and_stream::<wlan_deprecated::DeprecatedConfiguratorMarker>();
1828        let mac = MacAddress::from_bytes(&[0, 1, 2, 3, 4, 5]).unwrap();
1829        let suggest_fut = handle_suggest_ap_mac(configurator_proxy, mac);
1830        let mut suggest_fut = pin!(suggest_fut);
1831
1832        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Pending);
1833
1834        assert_matches!(
1835            exec.run_until_stalled(&mut configurator_stream.next()),
1836            Poll::Ready(Some(Ok(wlan_deprecated::DeprecatedConfiguratorRequest::SuggestAccessPointMacAddress {
1837                mac: net::MacAddress { octets: [0, 1, 2, 3, 4, 5] }, responder
1838            }))) => {
1839                assert!(responder.send(Ok(())).is_ok());
1840            }
1841        );
1842
1843        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Ready(Ok(())));
1844    }
1845
1846    #[fuchsia::test]
1847    fn test_suggest_ap_mac_fails() {
1848        let mut exec = TestExecutor::new();
1849
1850        let (configurator_proxy, mut configurator_stream) =
1851            endpoints::create_proxy_and_stream::<wlan_deprecated::DeprecatedConfiguratorMarker>();
1852        let mac = MacAddress::from_bytes(&[0, 1, 2, 3, 4, 5]).unwrap();
1853        let suggest_fut = handle_suggest_ap_mac(configurator_proxy, mac);
1854        let mut suggest_fut = pin!(suggest_fut);
1855
1856        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Pending);
1857
1858        assert_matches!(
1859            exec.run_until_stalled(&mut configurator_stream.next()),
1860            Poll::Ready(Some(Ok(wlan_deprecated::DeprecatedConfiguratorRequest::SuggestAccessPointMacAddress {
1861                mac: net::MacAddress { octets: [0, 1, 2, 3, 4, 5] }, responder
1862            }))) => {
1863                assert!(responder.send(Err(wlan_deprecated::SuggestMacAddressError::InvalidArguments)).is_ok());
1864            }
1865        );
1866
1867        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Ready(Err(_)));
1868    }
1869
1870    #[fuchsia::test]
1871    async fn test_proxy_command_succeeds() {
1872        match run_proxy_command(Box::pin(async { Ok(zx_status::Status::OK) })).await {
1873            Ok(status) => {
1874                assert_eq!(status, zx_status::Status::OK)
1875            }
1876            Err(e) => panic!("Test unexpectedly failed with {}", e),
1877        }
1878    }
1879
1880    #[fuchsia::test]
1881    async fn test_proxy_command_already_bound() {
1882        let result: Result<(), Error> = run_proxy_command(Box::pin(async {
1883            Err(fidl::Error::ClientChannelClosed {
1884                epitaph: fidl::Epitaph::Explicit(Err(zx_status::Status::ALREADY_BOUND)),
1885                protocol_name: "test",
1886            })
1887        }))
1888        .await;
1889        match result {
1890            Ok(status) => panic!("Test unexpectedly succeeded with {:?}", status),
1891            Err(e) => {
1892                assert!(e.to_string().contains("Failed to obtain a WLAN policy controller"));
1893            }
1894        }
1895    }
1896
1897    #[fuchsia::test]
1898    async fn test_proxy_command_generic_failure() {
1899        let result: Result<(), Error> =
1900            run_proxy_command(Box::pin(async { Err(fidl::Error::Invalid) })).await;
1901        match result {
1902            Ok(status) => panic!("Test unexpectedly succeeded with {:?}", status),
1903            Err(e) => {
1904                assert!(!e.to_string().contains("Failed to obtain a WLAN policy controller"));
1905            }
1906        }
1907    }
1908}