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