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_remove_network(
377    client_controller: wlan_policy::ClientControllerProxy,
378    ssid: Vec<u8>,
379    security_type: Option<wlan_policy::SecurityType>,
380    credential: Option<wlan_policy::Credential>,
381) -> Result<(), Error> {
382    let networks_before = handle_get_saved_networks(&client_controller).await.map_err(|e| {
383        format_err!(
384            "The network was not removed because an error occurred getting the list of networks \
385                before removing the requested network: {}",
386            e,
387        )
388    })?;
389
390    // If there is a provided security type and credential, use it to construct the config.
391    // Otherwise get saved networks and find one matching the provided arguments.
392    let config = if security_type.is_some() && credential.is_some() {
393        wlan_policy::NetworkConfig {
394            id: Some(wlan_policy::NetworkIdentifier {
395                ssid: ssid.clone(),
396                type_: security_type.unwrap(),
397            }),
398            credential: credential.clone(),
399            ..Default::default()
400        }
401    } else {
402        // Reuse the saved networks, but don't check for errors until using the data because it
403        // isn't necessary in the case where the exact arguments are provided. The data is needed
404        // below but the error cannot be cloned so the error is manually checked here.
405        let mut matching_networks = networks_before
406            .iter()
407            .filter(|c| config_matches(c, &ssid, &security_type, &credential))
408            .cloned()
409            .collect::<Vec<_>>();
410
411        // If there is one matching saved network, specify this network to remove.
412        if matching_networks.len() == 1 {
413            matching_networks.pop().unwrap()
414        } else if matching_networks.is_empty() {
415            return Err(format_err!(
416                "Failed to find a saved network with the provided arguments. Please check that the arguments are correct if there should be one saved."
417            ));
418        // If there are more than one matching network, do not assume which one to use and
419        // ask the caller to specify.
420        } else {
421            return Err(format_err!(
422                "Multiple saved networks were found matching the provided \
423                arguments, please specify SSID, security, and credential that matches only one \
424                saved network."
425            ));
426        }
427    };
428
429    run_proxy_command(Box::pin(client_controller.remove_network(&config)))
430        .await?
431        .map_err(|e| format_err!("failed to remove network with {:?}", e))?;
432
433    let networks_after = match handle_get_saved_networks(&client_controller).await {
434        Ok(networks) => networks,
435        Err(e) => {
436            println!(
437                "The network provided may or may not have been removed. An error occurred \
438                    getting the list of networks after removing: {}",
439                e
440            );
441            return Ok(());
442        }
443    };
444
445    // Check that there is no matching network after removing it. There should only have been one
446    // config matching the args since if there were multiple, this function would have quit early.
447    if networks_after.iter().any(|c| config_matches(c, &ssid, &security_type, &credential)) {
448        return Err(format_err!(
449            "The network may not have been removed. A network matching the \
450            provided arguments was found after attempting to remove the network."
451        ));
452    }
453
454    // Check whether anything was removed.
455    if networks_before.len() == networks_after.len() {
456        println!(
457            "The number of saved networks is the same after removing the specified network. \
458                  Please check that the arguments provided are correct and whether the intended \
459                  network is saved."
460        );
461    } else {
462        println!("Successfully removed network '{}'", std::str::from_utf8(&ssid).unwrap());
463    }
464    Ok(())
465}
466
467/// Check whether a config matches the provided SSID and optionally security or credential if
468/// provided.
469fn config_matches(
470    config: &wlan_policy::NetworkConfig,
471    ssid: &Vec<u8>,
472    security_type: &Option<wlan_policy::SecurityType>,
473    credential: &Option<wlan_policy::Credential>,
474) -> bool {
475    let config_id = match &config.id {
476        Some(id) => id,
477        None => {
478            return false;
479        }
480    };
481
482    if config_id.ssid != *ssid {
483        return false;
484    }
485
486    // Only check security and credential if not None.
487    if let Some(security) = *security_type {
488        if config_id.type_ != security {
489            return false;
490        }
491    }
492    if credential.is_some() {
493        if config.credential != *credential {
494            return false;
495        }
496    }
497
498    return true;
499}
500
501/// Communicates with the client policy layer to save a network configuration.
502pub async fn handle_save_network(
503    client_controller: wlan_policy::ClientControllerProxy,
504    config: wlan_policy::NetworkConfig,
505) -> Result<(), Error> {
506    save_network(client_controller, config).await
507}
508
509async fn save_network(
510    client_controller: wlan_policy::ClientControllerProxy,
511    network_config: wlan_policy::NetworkConfig,
512) -> Result<(), Error> {
513    run_proxy_command(Box::pin(client_controller.save_network(&network_config)))
514        .await?
515        .map_err(|e| format_err!("failed to save network with {:?}", e))?;
516    println!(
517        "Successfully saved network '{}'",
518        std::str::from_utf8(&network_config.id.unwrap().ssid).unwrap()
519    );
520    Ok(())
521}
522
523/// Issues a scan request to the client policy layer.
524pub async fn handle_scan(
525    client_controller: wlan_policy::ClientControllerProxy,
526) -> Result<Vec<wlan_policy::ScanResult>, Error> {
527    let (client_proxy, server_end) =
528        client_controller.domain().create_proxy::<wlan_policy::ScanResultIteratorMarker>();
529    let fut = async { client_controller.scan_for_networks(server_end) };
530    run_proxy_command(Box::pin(fut)).await?;
531
532    let mut scanned_networks = Vec::<wlan_policy::ScanResult>::new();
533    loop {
534        match run_proxy_command(Box::pin(client_proxy.get_next())).await? {
535            Ok(mut new_networks) => {
536                if new_networks.is_empty() {
537                    break;
538                }
539                scanned_networks.append(&mut new_networks);
540            }
541            Err(e) => return Err(format_err!("Scan failure error: {:?}", e)),
542        }
543    }
544
545    Ok(scanned_networks)
546}
547
548/// Requests that the policy layer start client connections.
549pub async fn handle_start_client_connections(
550    client_controller: wlan_policy::ClientControllerProxy,
551) -> Result<(), Error> {
552    let status = run_proxy_command(Box::pin(client_controller.start_client_connections())).await?;
553    return handle_request_status(status);
554}
555
556/// Asks the client policy layer to stop client connections.
557pub async fn handle_stop_client_connections(
558    client_controller: wlan_policy::ClientControllerProxy,
559) -> Result<(), Error> {
560    let status = run_proxy_command(Box::pin(client_controller.stop_client_connections())).await?;
561    return handle_request_status(status);
562}
563
564/// Asks the policy layer to start an AP with the user's specified network configuration.
565pub async fn handle_start_ap(
566    ap_controller: wlan_policy::AccessPointControllerProxy,
567    mut server_stream: wlan_policy::AccessPointStateUpdatesRequestStream,
568    config: wlan_policy::NetworkConfig,
569) -> Result<(), Error> {
570    let connectivity_mode = wlan_policy::ConnectivityMode::Unrestricted;
571    let operating_band = wlan_policy::OperatingBand::Any;
572    let result = run_proxy_command(Box::pin(ap_controller.start_access_point(
573        &config,
574        connectivity_mode,
575        operating_band,
576    )))
577    .await?;
578    handle_request_status(result)?;
579
580    // Listen for state updates until the service indicates that there is an active AP.
581    while let Some(update_request) = server_stream.try_next().await? {
582        let update = update_request.into_on_access_point_state_update();
583        let (updates, responder) = match update {
584            Some((update, responder)) => (update, responder),
585            None => return Err(format_err!("AP provider produced invalid update.")),
586        };
587        let _ = responder.send();
588
589        for update in updates {
590            match update.state {
591                Some(state) => match state {
592                    wlan_policy::OperatingState::Failed => {
593                        return Err(format_err!("Failed to start AP."));
594                    }
595                    wlan_policy::OperatingState::Starting => {
596                        println!("AP is starting.");
597                        continue;
598                    }
599                    wlan_policy::OperatingState::Active => return Ok(()),
600                },
601                None => continue,
602            }
603        }
604    }
605    Err(format_err!("Status stream terminated before AP start could be verified."))
606}
607
608/// Requests that the policy layer stop the AP associated with the given network configuration.
609pub async fn handle_stop_ap(
610    ap_controller: wlan_policy::AccessPointControllerProxy,
611    config: wlan_policy::NetworkConfig,
612) -> Result<(), Error> {
613    let result = run_proxy_command(Box::pin(ap_controller.stop_access_point(&config))).await?;
614    handle_request_status(result)
615}
616
617/// Requests that the policy layer stop all AP interfaces.
618pub async fn handle_stop_all_aps(
619    ap_controller: wlan_policy::AccessPointControllerProxy,
620) -> Result<(), Error> {
621    let fut = async { ap_controller.stop_all_access_points() };
622    run_proxy_command(Box::pin(fut)).await?;
623    Ok(())
624}
625
626/// Listens for AP state updates and prints each update that is received.
627pub async fn handle_ap_listen(
628    mut server_stream: wlan_policy::AccessPointStateUpdatesRequestStream,
629    single_sample: bool,
630) -> Result<(), Error> {
631    println!(
632        "{:32} | {:4} | {:8} | {:12} | {:6} | {:4} | {:7}",
633        "SSID", "Type", "State", "Mode", "Band", "Freq", "#Clients"
634    );
635
636    while let Some(update_request) = server_stream.try_next().await? {
637        let updates = update_request.into_on_access_point_state_update();
638        let (updates, responder) = match updates {
639            Some((update, responder)) => (update, responder),
640            None => return Err(format_err!("AP provider produced invalid update.")),
641        };
642        let _ = responder.send();
643
644        for update in updates {
645            let (ssid, security_type) = match update.id {
646                Some(network_id) => {
647                    let ssid = network_id.ssid.clone();
648                    let ssid = String::from_utf8(ssid)?;
649                    let security_type = match network_id.type_ {
650                        wlan_policy::SecurityType::None => "none",
651                        wlan_policy::SecurityType::Wep => "wep",
652                        wlan_policy::SecurityType::Wpa => "wpa",
653                        wlan_policy::SecurityType::Wpa2 => "wpa2",
654                        wlan_policy::SecurityType::Wpa3 => "wpa3",
655                    };
656                    (ssid, security_type.to_string())
657                }
658                None => ("".to_string(), "".to_string()),
659            };
660            let state = match update.state {
661                Some(state) => match state {
662                    wlan_policy::OperatingState::Failed => "failed",
663                    wlan_policy::OperatingState::Starting => "starting",
664                    wlan_policy::OperatingState::Active => "active",
665                },
666                None => "",
667            };
668            let mode = match update.mode {
669                Some(mode) => match mode {
670                    wlan_policy::ConnectivityMode::LocalOnly => "local only",
671                    wlan_policy::ConnectivityMode::Unrestricted => "unrestricted",
672                },
673                None => "",
674            };
675            let band = match update.band {
676                Some(band) => match band {
677                    wlan_policy::OperatingBand::Any => "any",
678                    wlan_policy::OperatingBand::Only24Ghz => "2.4Ghz",
679                    wlan_policy::OperatingBand::Only5Ghz => "5Ghz",
680                },
681                None => "",
682            };
683            let frequency = match update.frequency {
684                Some(frequency) => frequency.to_string(),
685                None => "".to_string(),
686            };
687            let client_count = match update.clients {
688                Some(connected_clients) => match connected_clients.count {
689                    Some(count) => count.to_string(),
690                    None => "".to_string(),
691                },
692                None => "".to_string(),
693            };
694
695            println!(
696                "{:32} | {:4} | {:8} | {:12} | {:6} | {:4} | {:7}",
697                ssid, security_type, state, mode, band, frequency, client_count
698            );
699        }
700
701        // If only one sample is desired, break out and return.
702        if single_sample {
703            break;
704        }
705    }
706    Ok(())
707}
708
709pub async fn handle_suggest_ap_mac(
710    configurator: wlan_deprecated::DeprecatedConfiguratorProxy,
711    mac: MacAddress,
712) -> Result<(), Error> {
713    let mac = net::MacAddress { octets: mac.to_array() };
714    let result = configurator.suggest_access_point_mac_address(&mac).await?;
715    result.map_err(|e| format_err!("suggesting MAC failed: {:?}", e))
716}
717
718#[cfg(test)]
719mod tests {
720    use super::*;
721    use assert_matches::assert_matches;
722    use fidl::endpoints;
723    use fuchsia_async::TestExecutor;
724    use futures::stream::StreamExt;
725    use futures::task::Poll;
726    use std::pin::pin;
727    use test_case::test_case;
728    use zx_status;
729
730    static TEST_SSID: &str = "test_ssid";
731    static TEST_PASSWORD: &str = "test_password";
732
733    struct ClientTestValues {
734        client_proxy: wlan_policy::ClientControllerProxy,
735        client_stream: wlan_policy::ClientControllerRequestStream,
736        update_proxy: wlan_policy::ClientStateUpdatesProxy,
737        update_stream: wlan_policy::ClientStateUpdatesRequestStream,
738    }
739
740    fn client_test_setup() -> ClientTestValues {
741        let (client_proxy, client_stream) =
742            endpoints::create_proxy_and_stream::<wlan_policy::ClientControllerMarker>();
743
744        let (listener_proxy, listener_stream) =
745            endpoints::create_proxy_and_stream::<wlan_policy::ClientStateUpdatesMarker>();
746
747        ClientTestValues {
748            client_proxy: client_proxy,
749            client_stream: client_stream,
750            update_proxy: listener_proxy,
751            update_stream: listener_stream,
752        }
753    }
754
755    /// Allows callers to respond to StartClientConnections, StopClientConnections, and Connect
756    /// calls with the RequestStatus response of their choice.
757    fn send_client_request_status(
758        exec: &mut TestExecutor,
759        server: &mut wlan_policy::ClientControllerRequestStream,
760        response: wlan_policy::RequestStatus,
761    ) {
762        let poll = exec.run_until_stalled(&mut server.next());
763        let request = match poll {
764            Poll::Ready(poll_ready) => poll_ready
765                .expect("poll ready result is None")
766                .expect("poll ready result is an Error"),
767            Poll::Pending => panic!("no ClientController request available"),
768        };
769
770        let result = match request {
771            wlan_policy::ClientControllerRequest::StartClientConnections { responder } => {
772                responder.send(response)
773            }
774            wlan_policy::ClientControllerRequest::StopClientConnections { responder } => {
775                responder.send(response)
776            }
777            wlan_policy::ClientControllerRequest::Connect { responder, .. } => {
778                responder.send(response)
779            }
780            _ => panic!("expecting a request that expects a RequestStatus"),
781        };
782
783        if result.is_err() {
784            panic!("could not send request status");
785        }
786    }
787
788    /// Creates a ClientStateSummary to provide as an update to listeners.
789    fn create_client_state_summary(
790        ssid: &str,
791        state: wlan_policy::ConnectionState,
792    ) -> wlan_policy::ClientStateSummary {
793        let network_state = wlan_policy::NetworkState {
794            id: Some(create_network_id(ssid)),
795            state: Some(state),
796            status: None,
797            ..Default::default()
798        };
799        wlan_policy::ClientStateSummary {
800            state: Some(wlan_policy::WlanClientState::ConnectionsEnabled),
801            networks: Some(vec![network_state]),
802            ..Default::default()
803        }
804    }
805
806    /// Creates an AccessPointStateSummary to provide as an update to listeners.
807    fn create_ap_state_summary(
808        state: wlan_policy::OperatingState,
809    ) -> wlan_policy::AccessPointState {
810        wlan_policy::AccessPointState {
811            id: None,
812            state: Some(state),
813            mode: None,
814            band: None,
815            frequency: None,
816            clients: None,
817            ..Default::default()
818        }
819    }
820
821    /// Allows callers to send a response to SaveNetwork and RemoveNetwork calls.
822    #[track_caller]
823    fn send_network_config_response(
824        exec: &mut TestExecutor,
825        server: &mut wlan_policy::ClientControllerRequestStream,
826        success: bool,
827    ) {
828        let poll = exec.run_until_stalled(&mut server.next());
829        let request = match poll {
830            Poll::Ready(poll_ready) => poll_ready
831                .expect("poll ready result is None")
832                .expect("poll ready result is an Error"),
833            Poll::Pending => panic!("no ClientController request available"),
834        };
835
836        let result = match request {
837            wlan_policy::ClientControllerRequest::SaveNetwork { config: _, responder } => {
838                if success {
839                    responder.send(Ok(()))
840                } else {
841                    responder.send(Err(wlan_policy::NetworkConfigChangeError::GeneralError))
842                }
843            }
844            wlan_policy::ClientControllerRequest::RemoveNetwork { config: _, responder } => {
845                if success {
846                    responder.send(Ok(()))
847                } else {
848                    responder.send(Err(wlan_policy::NetworkConfigChangeError::GeneralError))
849                }
850            }
851            _ => panic!("expecting a request that optionally receives a NetworkConfigChangeError"),
852        };
853
854        if result.is_err() {
855            panic!("could not send network config response");
856        }
857    }
858
859    struct ApTestValues {
860        ap_proxy: wlan_policy::AccessPointControllerProxy,
861        ap_stream: wlan_policy::AccessPointControllerRequestStream,
862        update_proxy: wlan_policy::AccessPointStateUpdatesProxy,
863        update_stream: wlan_policy::AccessPointStateUpdatesRequestStream,
864    }
865
866    fn ap_test_setup() -> ApTestValues {
867        let (ap_proxy, ap_stream) =
868            endpoints::create_proxy_and_stream::<wlan_policy::AccessPointControllerMarker>();
869
870        let (listener_proxy, listener_stream) =
871            endpoints::create_proxy_and_stream::<wlan_policy::AccessPointStateUpdatesMarker>();
872
873        ApTestValues {
874            ap_proxy,
875            ap_stream,
876            update_proxy: listener_proxy,
877            update_stream: listener_stream,
878        }
879    }
880
881    /// Allows callers to respond to StartAccessPoint and StopAccessPoint
882    /// calls with the RequestStatus response of their choice.
883    fn send_ap_request_status(
884        exec: &mut TestExecutor,
885        server: &mut wlan_policy::AccessPointControllerRequestStream,
886        response: wlan_policy::RequestStatus,
887    ) {
888        let poll = exec.run_until_stalled(&mut server.next());
889        let request = match poll {
890            Poll::Ready(poll_ready) => poll_ready
891                .expect("poll ready result is None")
892                .expect("poll ready result is an Error"),
893            Poll::Pending => panic!("no AccessPointController request available"),
894        };
895
896        let result = match request {
897            wlan_policy::AccessPointControllerRequest::StartAccessPoint { responder, .. } => {
898                responder.send(response)
899            }
900            wlan_policy::AccessPointControllerRequest::StopAccessPoint { config: _, responder } => {
901                responder.send(response)
902            }
903            _ => panic!("expecting a request that expects a RequestStatus"),
904        };
905
906        if result.is_err() {
907            panic!("could not send request status");
908        }
909    }
910
911    /// Creates a NetworkIdentifier for use in tests.
912    fn create_network_id(ssid: &str) -> wlan_policy::NetworkIdentifier {
913        wlan_policy::NetworkIdentifier {
914            ssid: ssid.as_bytes().to_vec(),
915            type_: wlan_policy::SecurityType::Wpa2,
916        }
917    }
918
919    /// Creates a NetworkConfig for use in tests.
920    fn create_network_config(ssid: &str) -> wlan_policy::NetworkConfig {
921        wlan_policy::NetworkConfig {
922            id: Some(create_network_id(ssid)),
923            credential: Some(create_password(TEST_PASSWORD)),
924            ..Default::default()
925        }
926    }
927
928    fn create_password(val: &str) -> wlan_policy::Credential {
929        wlan_policy::Credential::Password(val.as_bytes().to_vec())
930    }
931
932    /// Creates a NetworkConfig for use in tests.
933    fn create_network_config_with_security(
934        ssid: &str,
935        security: wlan_policy::SecurityType,
936    ) -> wlan_policy::NetworkConfig {
937        let id = wlan_policy::NetworkIdentifier { ssid: ssid.as_bytes().to_vec(), type_: security };
938        wlan_policy::NetworkConfig { id: Some(id), credential: None, ..Default::default() }
939    }
940
941    /// Create a scan result to be sent as a response to a scan request.
942    fn create_scan_result(ssid: &str) -> wlan_policy::ScanResult {
943        wlan_policy::ScanResult {
944            id: Some(create_network_id(ssid)),
945            entries: None,
946            compatibility: None,
947            ..Default::default()
948        }
949    }
950
951    /// Respond to a ScanForNetworks request and provide an iterator so that tests can inject scan
952    /// results.
953    fn get_scan_result_iterator(
954        exec: &mut TestExecutor,
955        mut server: wlan_policy::ClientControllerRequestStream,
956    ) -> wlan_policy::ScanResultIteratorRequestStream {
957        let poll = exec.run_until_stalled(&mut server.next());
958        let request = match poll {
959            Poll::Ready(poll_ready) => poll_ready
960                .expect("poll ready result is None")
961                .expect("poll ready result is an Error"),
962            Poll::Pending => panic!("no ClientController request available"),
963        };
964
965        match request {
966            wlan_policy::ClientControllerRequest::ScanForNetworks {
967                iterator,
968                control_handle: _,
969            } => iterator.into_stream(),
970            _ => panic!("expecting a ScanForNetworks"),
971        }
972    }
973
974    /// Sends scan results back to the client that is requesting a scan.
975    fn send_scan_result(
976        exec: &mut TestExecutor,
977        server: &mut wlan_policy::ScanResultIteratorRequestStream,
978        scan_result: Result<&[wlan_policy::ScanResult], wlan_policy::ScanErrorCode>,
979    ) {
980        assert_matches!(
981            exec.run_until_stalled(&mut server.next()),
982            Poll::Ready(Some(Ok(wlan_policy::ScanResultIteratorRequest::GetNext {
983                responder
984            }))) => {
985                match responder.send(scan_result) {
986                    Ok(()) => {}
987                    Err(e) => panic!("failed to send scan result: {}", e),
988                }
989            }
990        );
991    }
992
993    /// Responds to a GetSavedNetworks request and provide an iterator for sending back saved
994    /// networks.
995    #[track_caller]
996    fn get_saved_networks_iterator(
997        exec: &mut TestExecutor,
998        server: &mut wlan_policy::ClientControllerRequestStream,
999    ) -> wlan_policy::NetworkConfigIteratorRequestStream {
1000        let poll = exec.run_until_stalled(&mut server.next());
1001        let request = match poll {
1002            Poll::Ready(poll_ready) => poll_ready
1003                .expect("poll ready result is None")
1004                .expect("poll ready result is an Error"),
1005            Poll::Pending => panic!("no ClientController request available"),
1006        };
1007
1008        match request {
1009            wlan_policy::ClientControllerRequest::GetSavedNetworks {
1010                iterator,
1011                control_handle: _,
1012            } => iterator.into_stream(),
1013            _ => panic!("expecting a ScanForNetworks"),
1014        }
1015    }
1016
1017    /// Uses the provided iterator and send back saved networks.
1018    fn send_saved_networks(
1019        exec: &mut TestExecutor,
1020        server: &mut wlan_policy::NetworkConfigIteratorRequestStream,
1021        saved_networks_response: Vec<wlan_policy::NetworkConfig>,
1022    ) {
1023        assert_matches!(
1024            exec.run_until_stalled(&mut server.next()),
1025            Poll::Ready(Some(Ok(wlan_policy::NetworkConfigIteratorRequest::GetNext {
1026                responder
1027            }))) => {
1028                match responder.send(&saved_networks_response) {
1029                    Ok(()) => {}
1030                    Err(e) => panic!("failed to send saved networks: {}", e),
1031                }
1032            }
1033        );
1034    }
1035
1036    /// Tests the case where start client connections is called and the operation is successful.
1037    #[fuchsia::test]
1038    fn test_start_client_connections_success() {
1039        let mut exec = TestExecutor::new();
1040        let mut test_values = client_test_setup();
1041        let fut = handle_start_client_connections(test_values.client_proxy);
1042        let mut fut = pin!(fut);
1043
1044        // Wait for the fidl request to go out to start client connections
1045        assert!(exec.run_until_stalled(&mut fut).is_pending());
1046
1047        // Send back an acknowledgement
1048        send_client_request_status(
1049            &mut exec,
1050            &mut test_values.client_stream,
1051            wlan_policy::RequestStatus::Acknowledged,
1052        );
1053
1054        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1055    }
1056
1057    /// Tests the case where starting client connections fails.
1058    #[fuchsia::test]
1059    fn test_start_client_connections_fail() {
1060        let mut exec = TestExecutor::new();
1061        let mut test_values = client_test_setup();
1062        let fut = handle_start_client_connections(test_values.client_proxy);
1063        let mut fut = pin!(fut);
1064
1065        // Wait for the fidl request to go out to start client connections
1066        assert!(exec.run_until_stalled(&mut fut).is_pending());
1067
1068        // Send back an acknowledgement
1069        send_client_request_status(
1070            &mut exec,
1071            &mut test_values.client_stream,
1072            wlan_policy::RequestStatus::RejectedNotSupported,
1073        );
1074
1075        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1076    }
1077
1078    /// Tests the case where starting client connections is successful.
1079    #[fuchsia::test]
1080    fn test_stop_client_connections_success() {
1081        let mut exec = TestExecutor::new();
1082        let mut test_values = client_test_setup();
1083        let fut = handle_stop_client_connections(test_values.client_proxy);
1084        let mut fut = pin!(fut);
1085
1086        // Wait for the fidl request to go out to stop client connections
1087        assert!(exec.run_until_stalled(&mut fut).is_pending());
1088
1089        // Send back an acknowledgement
1090        send_client_request_status(
1091            &mut exec,
1092            &mut test_values.client_stream,
1093            wlan_policy::RequestStatus::Acknowledged,
1094        );
1095
1096        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1097    }
1098
1099    /// Tests the case where starting client connections fails.
1100    #[fuchsia::test]
1101    fn test_stop_client_connections_fail() {
1102        let mut exec = TestExecutor::new();
1103        let mut test_values = client_test_setup();
1104        let fut = handle_stop_client_connections(test_values.client_proxy);
1105        let mut fut = pin!(fut);
1106
1107        // Wait for the fidl request to go out to stop client connections
1108        assert!(exec.run_until_stalled(&mut fut).is_pending());
1109
1110        // Send back an acknowledgement
1111        send_client_request_status(
1112            &mut exec,
1113            &mut test_values.client_stream,
1114            wlan_policy::RequestStatus::RejectedNotSupported,
1115        );
1116
1117        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1118    }
1119
1120    /// Tests the case where a network is successfully saved.
1121    #[fuchsia::test]
1122    fn test_save_network_pass() {
1123        let mut exec = TestExecutor::new();
1124        let mut test_values = client_test_setup();
1125        let config = create_network_config(TEST_SSID);
1126        let fut = handle_save_network(test_values.client_proxy, config);
1127        let mut fut = pin!(fut);
1128
1129        // Wait for the fidl request to go out to save a network
1130        assert!(exec.run_until_stalled(&mut fut).is_pending());
1131
1132        // Drop the remote channel indicating success
1133        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1134
1135        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1136    }
1137
1138    /// Tests the case where a network configuration cannot be saved.
1139    #[fuchsia::test]
1140    fn test_save_network_fail() {
1141        let mut exec = TestExecutor::new();
1142        let mut test_values = client_test_setup();
1143        let config = create_network_config(TEST_SSID);
1144        let fut = handle_save_network(test_values.client_proxy, config);
1145        let mut fut = pin!(fut);
1146
1147        // Wait for the fidl request to go out to save a network
1148        assert!(exec.run_until_stalled(&mut fut).is_pending());
1149
1150        // Send back an error
1151        send_network_config_response(&mut exec, &mut test_values.client_stream, false);
1152
1153        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1154    }
1155
1156    /// Tests the case where a network config can be successfully removed.
1157    #[fuchsia::test]
1158    fn test_remove_network_pass() {
1159        let mut exec = TestExecutor::new();
1160        let mut test_values = client_test_setup();
1161        let security = Some(wlan_policy::SecurityType::Wpa2);
1162        let credential = Some(create_password(TEST_PASSWORD));
1163        let fut =
1164            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1165        let mut fut = pin!(fut);
1166        assert!(exec.run_until_stalled(&mut fut).is_pending());
1167
1168        // Respond to the get saved networks request for checking whether anything will be removed
1169        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1170        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1171        assert!(exec.run_until_stalled(&mut fut).is_pending());
1172        send_saved_networks(&mut exec, &mut iterator, vec![]);
1173
1174        // Wait for the fidl request to go out to remove a network
1175        assert!(exec.run_until_stalled(&mut fut).is_pending());
1176
1177        // Drop the remote channel indicating success
1178        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1179        assert!(exec.run_until_stalled(&mut fut).is_pending());
1180
1181        // Respond to the get saved networks request for checking whether anything was removed
1182        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1183        send_saved_networks(&mut exec, &mut iterator, vec![]);
1184
1185        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1186    }
1187
1188    /// Tests the case where a network config can be successfully removed when only the SSID of the
1189    /// network is provided as an argument. The saved network to remove is found by getting saved
1190    /// networks.
1191    #[fuchsia::test]
1192    fn test_remove_network_ssid_only_pass() {
1193        let mut exec = TestExecutor::new();
1194        let mut test_values = client_test_setup();
1195
1196        // Request to remove a network by only specifying the SSID of the saved network.
1197        let security = None;
1198        let credential = None;
1199        let fut =
1200            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1201        let mut fut = pin!(fut);
1202
1203        // Wait for the request to get saved networks
1204        assert!(exec.run_until_stalled(&mut fut).is_pending());
1205
1206        // Send back a network matching the SSID requested.
1207        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1208        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1209        assert!(exec.run_until_stalled(&mut fut).is_pending());
1210        send_saved_networks(&mut exec, &mut iterator, vec![]);
1211
1212        // Wait for the fidl request to go out to remove a network
1213        assert!(exec.run_until_stalled(&mut fut).is_pending());
1214
1215        // Drop the remote channel indicating success
1216        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1217        assert!(exec.run_until_stalled(&mut fut).is_pending());
1218
1219        // Respond to the get saved networks request for checking whether anything was removed
1220        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1221        send_saved_networks(&mut exec, &mut iterator, vec![]);
1222
1223        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1224    }
1225
1226    /// Test the case where a network config is removed successfully when only the SSID and
1227    /// credential are provided as arguments. The security type to use is determined by getting
1228    /// saved networks and finding a matching config.
1229    #[fuchsia::test]
1230    fn test_remove_network_unspecified_security_pass() {
1231        let mut exec = TestExecutor::new();
1232        let mut test_values = client_test_setup();
1233
1234        // Request to remove a network by only specifying the SSID of the saved network.
1235        let security = None;
1236        let credential = Some(create_password(TEST_PASSWORD));
1237        let fut =
1238            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1239        let mut fut = pin!(fut);
1240
1241        // Wait for the request to get saved networks
1242        assert!(exec.run_until_stalled(&mut fut).is_pending());
1243
1244        // Send back a network matching the SSID requested.
1245        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1246        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1247        assert!(exec.run_until_stalled(&mut fut).is_pending());
1248        send_saved_networks(&mut exec, &mut iterator, vec![]);
1249
1250        // Wait for the fidl request to go out to remove a network
1251        assert!(exec.run_until_stalled(&mut fut).is_pending());
1252
1253        // Drop the remote channel indicating success
1254        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1255        assert!(exec.run_until_stalled(&mut fut).is_pending());
1256
1257        // Respond to the get saved networks request for checking whether anything was 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![]);
1260
1261        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1262    }
1263
1264    /// Test the case where a network config is removed successfully when only the SSID and
1265    /// security type are provided as arguments. The credential to use is determined by getting
1266    /// saved networks and finding a matching config.
1267    #[fuchsia::test]
1268    fn test_remove_network_no_credential_pass() {
1269        let mut exec = TestExecutor::new();
1270        let mut test_values = client_test_setup();
1271
1272        // Request to remove a network by only specifying the SSID of the saved network.
1273        let security = Some(wlan_policy::SecurityType::Wpa2);
1274        let credential = None;
1275        let fut =
1276            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1277        let mut fut = pin!(fut);
1278
1279        // Wait for the request to get saved networks
1280        assert!(exec.run_until_stalled(&mut fut).is_pending());
1281
1282        // Send back a network matching the SSID requested.
1283        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1284        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1285        assert!(exec.run_until_stalled(&mut fut).is_pending());
1286        send_saved_networks(&mut exec, &mut iterator, vec![]);
1287
1288        // Wait for the fidl request to go out to remove a network
1289        assert!(exec.run_until_stalled(&mut fut).is_pending());
1290
1291        // Drop the remote channel indicating success
1292        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1293        assert!(exec.run_until_stalled(&mut fut).is_pending());
1294
1295        // Respond to the get saved networks request for checking whether anything was removed
1296        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1297        send_saved_networks(&mut exec, &mut iterator, vec![]);
1298
1299        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1300    }
1301
1302    /// Tests the case where removing a network config by specifying an SSID fails because there is
1303    /// no matching config.
1304    #[fuchsia::test]
1305    fn test_remove_network_ssid_only_no_match_fails() {
1306        let mut exec = TestExecutor::new();
1307        let mut test_values = client_test_setup();
1308
1309        // Request to remove a network by only specifying the SSID of the saved network.
1310        let security = None;
1311        let credential = None;
1312        let fut =
1313            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1314        let mut fut = pin!(fut);
1315
1316        // Wait for the request to get saved networks
1317        assert!(exec.run_until_stalled(&mut fut).is_pending());
1318
1319        // Send back a network that doesn't match the specified network.
1320        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1321        send_saved_networks(
1322            &mut exec,
1323            &mut iterator,
1324            vec![create_network_config("some-other-ssid")],
1325        );
1326        assert!(exec.run_until_stalled(&mut fut).is_pending());
1327        send_saved_networks(&mut exec, &mut iterator, vec![]);
1328
1329        // Since there is no matching network to remove, an error should be returned.
1330        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1331    }
1332
1333    /// Tests the case where network removal fails.
1334    #[fuchsia::test]
1335    fn test_remove_network_fail() {
1336        let mut exec = TestExecutor::new();
1337        let mut test_values = client_test_setup();
1338        let security = Some(wlan_policy::SecurityType::Wpa2);
1339        let credential = Some(create_password(TEST_PASSWORD));
1340        let fut =
1341            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1342        let mut fut = pin!(fut);
1343        assert!(exec.run_until_stalled(&mut fut).is_pending());
1344
1345        // Respond to the get saved networks request for checking whether anything will be removed
1346        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1347        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1348        assert!(exec.run_until_stalled(&mut fut).is_pending());
1349        send_saved_networks(&mut exec, &mut iterator, vec![]);
1350
1351        // Wait for the fidl request to go out to remove a network
1352        assert!(exec.run_until_stalled(&mut fut).is_pending());
1353
1354        // Send back an error
1355        send_network_config_response(&mut exec, &mut test_values.client_stream, false);
1356
1357        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1358    }
1359
1360    /// Tests the case where network removal returns no error but the network is still present.
1361    #[fuchsia::test]
1362    fn test_remove_network_not_removed_fails() {
1363        let mut exec = TestExecutor::new();
1364        let mut test_values = client_test_setup();
1365        let security = Some(wlan_policy::SecurityType::Wpa2);
1366        let credential = Some(create_password(TEST_PASSWORD));
1367        let fut =
1368            handle_remove_network(test_values.client_proxy, TEST_SSID.into(), security, credential);
1369        let mut fut = pin!(fut);
1370        assert!(exec.run_until_stalled(&mut fut).is_pending());
1371
1372        // Respond to the get saved networks request for checking whether anything will be removed
1373        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1374        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1375        assert!(exec.run_until_stalled(&mut fut).is_pending());
1376        send_saved_networks(&mut exec, &mut iterator, vec![]);
1377
1378        // Wait for the fidl request to go out to remove a network
1379        assert!(exec.run_until_stalled(&mut fut).is_pending());
1380
1381        // Drop the remote channel indicating success
1382        send_network_config_response(&mut exec, &mut test_values.client_stream, true);
1383        assert!(exec.run_until_stalled(&mut fut).is_pending());
1384
1385        // Respond to the get saved networks request for checking whether anything was removed with
1386        // the network that should have been removed.
1387        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1388        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1389        assert!(exec.run_until_stalled(&mut fut).is_pending());
1390        send_saved_networks(&mut exec, &mut iterator, vec![]);
1391
1392        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1393    }
1394
1395    /// Tests the config_matches function which compares a config against optional arguments if
1396    /// argument is provided. Test various combinations of present and non-present dimensions.
1397    /// test variants are compared against the default config from create_config.
1398    #[test_case(
1399        TEST_SSID,
1400        Some(wlan_policy::SecurityType::Wpa2),
1401        Some(create_password(TEST_PASSWORD)),
1402        true
1403    )]
1404    #[test_case(TEST_SSID, Some(wlan_policy::SecurityType::Wpa2), None, true)]
1405    #[test_case(TEST_SSID, None, Some(create_password(TEST_PASSWORD)), true)]
1406    #[test_case(TEST_SSID, None, None, true)]
1407    #[test_case(
1408        TEST_SSID,
1409        Some(wlan_policy::SecurityType::Wpa2),
1410        Some(create_password("otherpassword")),
1411        false
1412    )]
1413    #[test_case(TEST_SSID, None, Some(create_password("otherpassword")), false)]
1414    #[test_case(
1415        TEST_SSID,
1416        Some(wlan_policy::SecurityType::Wpa3),
1417        Some(create_password(TEST_PASSWORD)),
1418        false
1419    )]
1420    #[test_case(TEST_SSID, Some(wlan_policy::SecurityType::Wpa3), None, false)]
1421    #[test_case(
1422        "otherssid",
1423        Some(wlan_policy::SecurityType::Wpa3),
1424        Some(create_password(TEST_PASSWORD)),
1425        false
1426    )]
1427    #[test_case("otherssid", None, None, false)]
1428    #[fuchsia::test(add_test_attr = false)]
1429    fn test_config_matches_config(
1430        ssid: &str,
1431        security: Option<wlan_policy::SecurityType>,
1432        credential: Option<wlan_policy::Credential>,
1433        expected_result: bool,
1434    ) {
1435        let ssid = ssid.as_bytes().to_vec();
1436        let config = create_network_config(TEST_SSID);
1437        let result = config_matches(&config, &ssid, &security, &credential);
1438        assert_eq!(result, expected_result);
1439    }
1440
1441    /// Tests the case where the client successfully connects.
1442    #[fuchsia::test]
1443    fn test_connect_pass() {
1444        let mut exec = TestExecutor::new();
1445        let mut test_values = client_test_setup();
1446
1447        // Start the connect routine.
1448        let ssid = TEST_SSID.to_string();
1449        let security = Some(wlan_policy::SecurityType::Wpa2);
1450        let fut =
1451            handle_connect(test_values.client_proxy, test_values.update_stream, ssid, security);
1452        let mut fut = pin!(fut);
1453
1454        // The function should now stall out waiting on the connect call to go out
1455        assert!(exec.run_until_stalled(&mut fut).is_pending());
1456
1457        // Send back a positive acknowledgement
1458        send_client_request_status(
1459            &mut exec,
1460            &mut test_values.client_stream,
1461            wlan_policy::RequestStatus::Acknowledged,
1462        );
1463
1464        // The client should now wait for events from the listener.  Send a Connecting update.
1465        assert!(exec.run_until_stalled(&mut fut).is_pending());
1466        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1467            TEST_SSID,
1468            wlan_policy::ConnectionState::Connecting,
1469        ));
1470
1471        // The client should stall and then wait for a Connected message.  Send that over.
1472        assert!(exec.run_until_stalled(&mut fut).is_pending());
1473        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1474            TEST_SSID,
1475            wlan_policy::ConnectionState::Connected,
1476        ));
1477
1478        // The connect process should complete after receiving the Connected status response
1479        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1480    }
1481
1482    /// Tests the case where the security type is not specified, but it is automatically chosen
1483    /// because a matching network is already saved. The connection succeeds.
1484    #[fuchsia::test]
1485    fn test_connect_unspecified_security_pass() {
1486        let mut exec = TestExecutor::new();
1487        let mut test_values = client_test_setup();
1488
1489        // Start the connect routine without giving a security type.
1490        let ssid = TEST_SSID.to_string();
1491        let fut = handle_connect(test_values.client_proxy, test_values.update_stream, ssid, None);
1492        let mut fut = pin!(fut);
1493
1494        // The function should now stall out waiting on the get saved networks call to go out.
1495        assert!(exec.run_until_stalled(&mut fut).is_pending());
1496
1497        // Send back a network matching the SSID requested.
1498        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1499        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1500        assert!(exec.run_until_stalled(&mut fut).is_pending());
1501
1502        // Send back an empty set of configs to indicate that the process is complete
1503        send_saved_networks(&mut exec, &mut iterator, vec![]);
1504
1505        // The function should now stall out waiting on the connect call to go out
1506        assert!(exec.run_until_stalled(&mut fut).is_pending());
1507
1508        // Send back a positive acknowledgement
1509        send_client_request_status(
1510            &mut exec,
1511            &mut test_values.client_stream,
1512            wlan_policy::RequestStatus::Acknowledged,
1513        );
1514
1515        // The client should now wait for events from the listener.  Send a Connecting update.
1516        assert!(exec.run_until_stalled(&mut fut).is_pending());
1517        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1518            TEST_SSID,
1519            wlan_policy::ConnectionState::Connecting,
1520        ));
1521
1522        // The client should stall and then wait for a Connected message.  Send that over.
1523        assert!(exec.run_until_stalled(&mut fut).is_pending());
1524        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1525            TEST_SSID,
1526            wlan_policy::ConnectionState::Connected,
1527        ));
1528
1529        // The connect process should complete after receiving the Connected status response
1530        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1531    }
1532
1533    /// Tests the case where a user doesn't provide a security type and nothing matching is saved.
1534    /// In this case there should be no connect request and an error should be returned.
1535    #[fuchsia::test]
1536    fn test_connect_no_security_given_nothing_saved_fails() {
1537        let mut exec = TestExecutor::new();
1538        let mut test_values = client_test_setup();
1539
1540        // Start the connect routine.
1541        let ssid = TEST_SSID.to_string();
1542        let fut = handle_connect(test_values.client_proxy, test_values.update_stream, ssid, None);
1543        let mut fut = pin!(fut);
1544
1545        // Progress future forward until it waits on a get saved networks call.
1546        assert!(exec.run_until_stalled(&mut fut).is_pending());
1547
1548        // Respond to the get saved networks request with no matches
1549        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1550        send_saved_networks(&mut exec, &mut iterator, vec![]);
1551
1552        // The connect routine should return an error.
1553        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1554
1555        // No connect request should have been sent through FIDL.
1556        assert_matches!(
1557            exec.run_until_stalled(&mut test_values.client_stream.next()),
1558            Poll::Ready(None)
1559        );
1560    }
1561
1562    /// Tests the case where a user doesn't provide a security type and multiple matching networks
1563    /// are saved. In this case there should be no connect request and an error should be returned.
1564    #[fuchsia::test]
1565    fn test_connect_no_security_given_multiple_saved_fails() {
1566        let mut exec = TestExecutor::new();
1567        let mut test_values = client_test_setup();
1568
1569        // Start the connect routine.
1570        let ssid = TEST_SSID.to_string();
1571        let fut = handle_connect(test_values.client_proxy, test_values.update_stream, ssid, None);
1572        let mut fut = pin!(fut);
1573
1574        // Progress future forward until it waits on a get saved networks call.
1575        assert!(exec.run_until_stalled(&mut fut).is_pending());
1576
1577        // Send back 2 networks matching the SSID requested.
1578        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1579        let wpa_config =
1580            create_network_config_with_security(TEST_SSID, wlan_policy::SecurityType::Wpa);
1581        let networks = vec![create_network_config(TEST_SSID), wpa_config];
1582
1583        send_saved_networks(&mut exec, &mut iterator, networks);
1584        assert!(exec.run_until_stalled(&mut fut).is_pending());
1585
1586        // Send back an empty set of configs to indicate that the process is complete
1587        send_saved_networks(&mut exec, &mut iterator, vec![]);
1588
1589        // The connect routine should return an error since there are multiple matches.
1590        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1591
1592        // No connect request should have been sent through FIDL.
1593        assert_matches!(
1594            exec.run_until_stalled(&mut test_values.client_stream.next()),
1595            Poll::Ready(None)
1596        );
1597    }
1598
1599    /// Tests the case where a client fails to connect.
1600    #[fuchsia::test]
1601    fn test_connect_fail() {
1602        let mut exec = TestExecutor::new();
1603        let mut test_values = client_test_setup();
1604
1605        // Start the connect routine.
1606        let ssid = TEST_SSID.to_string();
1607        let security = Some(wlan_policy::SecurityType::Wpa2);
1608        let fut =
1609            handle_connect(test_values.client_proxy, test_values.update_stream, ssid, security);
1610        let mut fut = pin!(fut);
1611
1612        // The function should now stall out waiting on the connect call to go out
1613        assert!(exec.run_until_stalled(&mut fut).is_pending());
1614
1615        // Send back a positive acknowledgement
1616        send_client_request_status(
1617            &mut exec,
1618            &mut test_values.client_stream,
1619            wlan_policy::RequestStatus::Acknowledged,
1620        );
1621
1622        // The client should now wait for events from the listener
1623        assert!(exec.run_until_stalled(&mut fut).is_pending());
1624        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1625            TEST_SSID,
1626            wlan_policy::ConnectionState::Failed,
1627        ));
1628
1629        // The connect process should return an error after receiving a Failed status
1630        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1631    }
1632
1633    /// Tests the case where a scan is requested and results are sent back to the requester.
1634    #[fuchsia::test]
1635    fn test_scan_pass() {
1636        let mut exec = TestExecutor::new();
1637        let test_values = client_test_setup();
1638
1639        let fut = handle_scan(test_values.client_proxy);
1640        let mut fut = pin!(fut);
1641
1642        // The function should now stall out waiting on the scan call to go out
1643        assert!(exec.run_until_stalled(&mut fut).is_pending());
1644
1645        // Send back a scan result
1646        let mut iterator = get_scan_result_iterator(&mut exec, test_values.client_stream);
1647        send_scan_result(&mut exec, &mut iterator, Ok(&[create_scan_result(TEST_SSID)]));
1648
1649        // Process the scan result
1650        assert!(exec.run_until_stalled(&mut fut).is_pending());
1651
1652        // Send back an empty scan result
1653        send_scan_result(&mut exec, &mut iterator, Ok(&[]));
1654
1655        // Expect the scan process to complete
1656        assert_matches!(
1657            exec.run_until_stalled(&mut fut),
1658            Poll::Ready(Ok(result)) => {
1659                assert_eq!(result.len(), 1);
1660                assert_eq!(result[0].id.as_ref().unwrap().ssid, TEST_SSID.as_bytes().to_vec());
1661            }
1662        );
1663    }
1664
1665    /// Tests the case where the scan cannot be performed.
1666    #[fuchsia::test]
1667    fn test_scan_fail() {
1668        let mut exec = TestExecutor::new();
1669        let test_values = client_test_setup();
1670
1671        let fut = handle_scan(test_values.client_proxy);
1672        let mut fut = pin!(fut);
1673
1674        // The function should now stall out waiting on the scan call to go out
1675        assert!(exec.run_until_stalled(&mut fut).is_pending());
1676
1677        // Send back a scan error
1678        let mut iterator = get_scan_result_iterator(&mut exec, test_values.client_stream);
1679        send_scan_result(&mut exec, &mut iterator, Err(wlan_policy::ScanErrorCode::GeneralError));
1680
1681        // Process the scan error
1682        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1683    }
1684
1685    /// Tests the ability to get saved networks from the client policy layer.
1686    #[fuchsia::test]
1687    fn test_get_saved_networks() {
1688        let mut exec = TestExecutor::new();
1689        let mut test_values = client_test_setup();
1690
1691        let fut = handle_get_saved_networks(&test_values.client_proxy);
1692        let mut fut = pin!(fut);
1693
1694        // The future should stall out waiting on the get saved networks request
1695        assert!(exec.run_until_stalled(&mut fut).is_pending());
1696
1697        // Send back a saved networks response
1698        let mut iterator = get_saved_networks_iterator(&mut exec, &mut test_values.client_stream);
1699        send_saved_networks(&mut exec, &mut iterator, vec![create_network_config(TEST_SSID)]);
1700        assert!(exec.run_until_stalled(&mut fut).is_pending());
1701
1702        // Send back an empty set of configs to indicate that the process is complete
1703        send_saved_networks(&mut exec, &mut iterator, vec![]);
1704
1705        // Verify that the saved networks response was recorded properly
1706        assert_matches!(
1707            exec.run_until_stalled(&mut fut),
1708            Poll::Ready(Ok(result)) => {
1709                assert_eq!(result.len(), 1);
1710                assert_eq!(result[0].id.as_ref().unwrap().ssid, TEST_SSID.as_bytes().to_vec());
1711            }
1712        );
1713    }
1714
1715    /// Tests to ensure that the listening loop continues to be active after receiving client state updates.
1716    #[fuchsia::test]
1717    fn test_client_listen() {
1718        let mut exec = TestExecutor::new();
1719        let test_values = client_test_setup();
1720
1721        let fut = handle_listen(test_values.update_stream, false);
1722        let mut fut = pin!(fut);
1723
1724        // Listen should stall waiting for updates
1725        assert!(exec.run_until_stalled(&mut fut).is_pending());
1726        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1727            TEST_SSID,
1728            wlan_policy::ConnectionState::Connecting,
1729        ));
1730
1731        // Listen should process the message and stall again
1732        assert!(exec.run_until_stalled(&mut fut).is_pending());
1733        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1734            TEST_SSID,
1735            wlan_policy::ConnectionState::Connected,
1736        ));
1737
1738        // Listener future should continue to run but stall waiting for more updates
1739        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1740    }
1741
1742    /// Tests to ensure that querying client status returns after the first update.
1743    #[fuchsia::test]
1744    fn test_client_status() {
1745        let mut exec = TestExecutor::new();
1746        let test_values = client_test_setup();
1747
1748        let fut = handle_listen(test_values.update_stream, true);
1749        let mut fut = pin!(fut);
1750
1751        // Listen should stall waiting for updates
1752        assert!(exec.run_until_stalled(&mut fut).is_pending());
1753        let _ = test_values.update_proxy.on_client_state_update(&create_client_state_summary(
1754            TEST_SSID,
1755            wlan_policy::ConnectionState::Connecting,
1756        ));
1757
1758        // Listener future should complete.
1759        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1760    }
1761
1762    /// Tests the case where an AP is requested to be stopped and the policy service returns an
1763    /// error.
1764    #[fuchsia::test]
1765    fn test_stop_ap_fail() {
1766        let mut exec = TestExecutor::new();
1767        let mut test_values = ap_test_setup();
1768
1769        let network_config = create_network_config(&TEST_SSID);
1770        let fut = handle_stop_ap(test_values.ap_proxy, network_config);
1771        let mut fut = pin!(fut);
1772
1773        // The request should stall waiting for the service
1774        assert!(exec.run_until_stalled(&mut fut).is_pending());
1775
1776        // Send back a rejection
1777        send_ap_request_status(
1778            &mut exec,
1779            &mut test_values.ap_stream,
1780            wlan_policy::RequestStatus::RejectedNotSupported,
1781        );
1782
1783        // Run the request to completion
1784        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1785    }
1786
1787    /// Tests the case where an AP is successfully stopped.
1788    #[fuchsia::test]
1789    fn test_stop_ap_pass() {
1790        let mut exec = TestExecutor::new();
1791        let mut test_values = ap_test_setup();
1792
1793        let network_config = create_network_config(&TEST_SSID);
1794        let fut = handle_stop_ap(test_values.ap_proxy, network_config);
1795        let mut fut = pin!(fut);
1796
1797        // The request should stall waiting for the service
1798        assert!(exec.run_until_stalled(&mut fut).is_pending());
1799
1800        // Send back an acknowledgement
1801        send_ap_request_status(
1802            &mut exec,
1803            &mut test_values.ap_stream,
1804            wlan_policy::RequestStatus::Acknowledged,
1805        );
1806
1807        // Run the request to completion
1808        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1809    }
1810
1811    /// Tests the case where the request to start an AP results in an error.
1812    #[fuchsia::test]
1813    fn test_start_ap_request_fail() {
1814        let mut exec = TestExecutor::new();
1815        let mut test_values = ap_test_setup();
1816
1817        let network_config = create_network_config(&TEST_SSID);
1818        let fut = handle_start_ap(test_values.ap_proxy, test_values.update_stream, network_config);
1819        let mut fut = pin!(fut);
1820
1821        // The request should stall waiting for the service
1822        assert!(exec.run_until_stalled(&mut fut).is_pending());
1823
1824        // Send back a rejection
1825        send_ap_request_status(
1826            &mut exec,
1827            &mut test_values.ap_stream,
1828            wlan_policy::RequestStatus::RejectedNotSupported,
1829        );
1830
1831        // Run the request to completion
1832        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1833    }
1834
1835    /// Tests the case where the start AP process returns an acknowledgement.  The tool should then
1836    /// wait for an update indicating that the AP is active.
1837    #[fuchsia::test]
1838    fn test_start_ap_pass() {
1839        let mut exec = TestExecutor::new();
1840        let mut test_values = ap_test_setup();
1841
1842        let network_config = create_network_config(&TEST_SSID);
1843        let fut = handle_start_ap(test_values.ap_proxy, test_values.update_stream, network_config);
1844        let mut fut = pin!(fut);
1845
1846        // The request should stall waiting for the service
1847        assert!(exec.run_until_stalled(&mut fut).is_pending());
1848
1849        // Send back an acknowledgement
1850        send_ap_request_status(
1851            &mut exec,
1852            &mut test_values.ap_stream,
1853            wlan_policy::RequestStatus::Acknowledged,
1854        );
1855
1856        // Progress the future so that it waits for AP state updates
1857        assert!(exec.run_until_stalled(&mut fut).is_pending());
1858
1859        // First send a `Starting` status.
1860        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1861            wlan_policy::OperatingState::Starting,
1862        )]);
1863
1864        // Future should still be waiting to see that the AP to be active
1865        assert!(exec.run_until_stalled(&mut fut).is_pending());
1866
1867        // Send the response indicating that the AP is active
1868        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1869            wlan_policy::OperatingState::Active,
1870        )]);
1871
1872        // Run the request to completion
1873        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1874    }
1875
1876    /// Tests the case where the AP start command is successfully sent, but the AP fails during
1877    /// the startup process.
1878    #[fuchsia::test]
1879    fn test_ap_failed_to_start() {
1880        let mut exec = TestExecutor::new();
1881        let mut test_values = ap_test_setup();
1882
1883        let network_config = create_network_config(&TEST_SSID);
1884        let fut = handle_start_ap(test_values.ap_proxy, test_values.update_stream, network_config);
1885        let mut fut = pin!(fut);
1886
1887        // The request should stall waiting for the service
1888        assert!(exec.run_until_stalled(&mut fut).is_pending());
1889
1890        // Send back an acknowledgement
1891        send_ap_request_status(
1892            &mut exec,
1893            &mut test_values.ap_stream,
1894            wlan_policy::RequestStatus::Acknowledged,
1895        );
1896
1897        // Progress the future so that it waits for AP state updates
1898        assert!(exec.run_until_stalled(&mut fut).is_pending());
1899
1900        // Send back a failure
1901        let state_updates = &[create_ap_state_summary(wlan_policy::OperatingState::Failed)];
1902        let _ = test_values.update_proxy.on_access_point_state_update(state_updates);
1903
1904        // Expect that the future returns an error
1905        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
1906    }
1907
1908    /// Tests the case where all APs are requested to be stopped.
1909    #[fuchsia::test]
1910    fn test_stop_all_aps() {
1911        let mut exec = TestExecutor::new();
1912        let mut test_values = ap_test_setup();
1913
1914        let fut = handle_stop_all_aps(test_values.ap_proxy);
1915        let mut fut = pin!(fut);
1916
1917        // The future should finish immediately
1918        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1919
1920        // Make sure that the request is seen on the request stream
1921        assert_matches!(
1922            exec.run_until_stalled(&mut test_values.ap_stream.next()),
1923            Poll::Ready(Some(Ok(
1924                wlan_policy::AccessPointControllerRequest::StopAllAccessPoints { .. }
1925            )))
1926        );
1927    }
1928
1929    /// Tests that the AP listen routine continues listening for new updates.
1930    #[fuchsia::test]
1931    fn test_ap_listen() {
1932        let mut exec = TestExecutor::new();
1933        let test_values = ap_test_setup();
1934
1935        let fut = handle_ap_listen(test_values.update_stream, false);
1936        let mut fut = pin!(fut);
1937
1938        // Listen should stall waiting for updates
1939        assert!(exec.run_until_stalled(&mut fut).is_pending());
1940        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1941            wlan_policy::OperatingState::Starting,
1942        )]);
1943
1944        // Listen should process the message and stall again
1945        assert!(exec.run_until_stalled(&mut fut).is_pending());
1946        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1947            wlan_policy::OperatingState::Active,
1948        )]);
1949
1950        // Process message and stall again
1951        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1952        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1953            wlan_policy::OperatingState::Failed,
1954        )]);
1955
1956        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1957    }
1958
1959    /// Tests that the AP status routine only takes a single update.
1960    #[fuchsia::test]
1961    fn test_ap_status() {
1962        let mut exec = TestExecutor::new();
1963        let test_values = ap_test_setup();
1964
1965        let fut = handle_ap_listen(test_values.update_stream, true);
1966        let mut fut = pin!(fut);
1967
1968        // Listen should stall waiting for updates
1969        assert!(exec.run_until_stalled(&mut fut).is_pending());
1970        let _ = test_values.update_proxy.on_access_point_state_update(&[create_ap_state_summary(
1971            wlan_policy::OperatingState::Starting,
1972        )]);
1973
1974        // The future should complete now that a single update has been processed.
1975        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
1976    }
1977
1978    #[fuchsia::test]
1979    fn test_suggest_ap_mac_succeeds() {
1980        let mut exec = TestExecutor::new();
1981
1982        let (configurator_proxy, mut configurator_stream) =
1983            endpoints::create_proxy_and_stream::<wlan_deprecated::DeprecatedConfiguratorMarker>();
1984        let mac = MacAddress::from_bytes(&[0, 1, 2, 3, 4, 5]).unwrap();
1985        let suggest_fut = handle_suggest_ap_mac(configurator_proxy, mac);
1986        let mut suggest_fut = pin!(suggest_fut);
1987
1988        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Pending);
1989
1990        assert_matches!(
1991            exec.run_until_stalled(&mut configurator_stream.next()),
1992            Poll::Ready(Some(Ok(wlan_deprecated::DeprecatedConfiguratorRequest::SuggestAccessPointMacAddress {
1993                mac: net::MacAddress { octets: [0, 1, 2, 3, 4, 5] }, responder
1994            }))) => {
1995                assert!(responder.send(Ok(())).is_ok());
1996            }
1997        );
1998
1999        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Ready(Ok(())));
2000    }
2001
2002    #[fuchsia::test]
2003    fn test_suggest_ap_mac_fails() {
2004        let mut exec = TestExecutor::new();
2005
2006        let (configurator_proxy, mut configurator_stream) =
2007            endpoints::create_proxy_and_stream::<wlan_deprecated::DeprecatedConfiguratorMarker>();
2008        let mac = MacAddress::from_bytes(&[0, 1, 2, 3, 4, 5]).unwrap();
2009        let suggest_fut = handle_suggest_ap_mac(configurator_proxy, mac);
2010        let mut suggest_fut = pin!(suggest_fut);
2011
2012        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Pending);
2013
2014        assert_matches!(
2015            exec.run_until_stalled(&mut configurator_stream.next()),
2016            Poll::Ready(Some(Ok(wlan_deprecated::DeprecatedConfiguratorRequest::SuggestAccessPointMacAddress {
2017                mac: net::MacAddress { octets: [0, 1, 2, 3, 4, 5] }, responder
2018            }))) => {
2019                assert!(responder.send(Err(wlan_deprecated::SuggestMacAddressError::InvalidArguments)).is_ok());
2020            }
2021        );
2022
2023        assert_matches!(exec.run_until_stalled(&mut suggest_fut), Poll::Ready(Err(_)));
2024    }
2025
2026    #[fuchsia_async::run_singlethreaded(test)]
2027    async fn test_proxy_command_succeeds() {
2028        match run_proxy_command(Box::pin(async { Ok(zx_status::Status::OK) })).await {
2029            Ok(status) => {
2030                assert_eq!(status, zx_status::Status::OK)
2031            }
2032            Err(e) => panic!("Test unexpectedly failed with {}", e),
2033        }
2034    }
2035
2036    #[fuchsia_async::run_singlethreaded(test)]
2037    async fn test_proxy_command_already_bound() {
2038        let result: Result<(), Error> = run_proxy_command(Box::pin(async {
2039            Err(fidl::Error::ClientChannelClosed {
2040                status: zx_status::Status::ALREADY_BOUND,
2041                protocol_name: "test",
2042                epitaph: Some(zx_status::Status::ALREADY_BOUND.into_raw() as u32),
2043            })
2044        }))
2045        .await;
2046        match result {
2047            Ok(status) => panic!("Test unexpectedly succeeded with {:?}", status),
2048            Err(e) => {
2049                assert!(e.to_string().contains("Failed to obtain a WLAN policy controller"));
2050            }
2051        }
2052    }
2053
2054    #[fuchsia_async::run_singlethreaded(test)]
2055    async fn test_proxy_command_generic_failure() {
2056        let result: Result<(), Error> =
2057            run_proxy_command(Box::pin(async { Err(fidl::Error::Invalid) })).await;
2058        match result {
2059            Ok(status) => panic!("Test unexpectedly succeeded with {:?}", status),
2060            Err(e) => {
2061                assert!(!e.to_string().contains("Failed to obtain a WLAN policy controller"));
2062            }
2063        }
2064    }
2065}