Skip to main content

wlancfg_lib/access_point/
state_machine.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::access_point::types;
6use crate::mode_management::iface_manager_api::SmeForApStateMachine;
7use crate::mode_management::{Defect, IfaceFailure};
8use crate::telemetry::{TelemetryEvent, TelemetrySender};
9use crate::util::listener::Message::NotifyListeners;
10use crate::util::listener::{
11    ApListenerMessageSender, ApStateUpdate, ApStatesUpdate, ConnectedClientInformation,
12};
13use crate::util::state_machine::{self, ExitReason, IntoStateExt, StateMachineStatusPublisher};
14use anyhow::format_err;
15use fidl_fuchsia_wlan_sme as fidl_sme;
16use fuchsia_async::{self as fasync, DurationExt};
17use fuchsia_inspect::Node as InspectNode;
18use fuchsia_inspect_contrib::inspect_insert;
19use fuchsia_inspect_contrib::log::WriteInspect;
20use fuchsia_sync::Mutex;
21
22use futures::channel::{mpsc, oneshot};
23use futures::future::FutureExt;
24use futures::select;
25use futures::stream::{self, Fuse, FuturesUnordered, StreamExt, TryStreamExt};
26use log::{info, warn};
27use std::borrow::Cow;
28use std::fmt::Debug;
29use std::pin::pin;
30use std::sync::Arc;
31use wlan_common::RadioConfig;
32use wlan_common::channel::{Cbw, Channel};
33
34const AP_STATUS_INTERVAL_SEC: i64 = 10;
35
36// If a scan is occurring on a PHY and that same PHY is asked to start an AP, the request to start
37// the AP will likely fail.  Scans are allowed a maximum to 10s to complete.  The timeout is
38// defined in //src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h as
39//
40// #define BRCMF_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
41//
42// As such, a minimum of 10s worth of retries should be allowed when starting the soft AP.  Allow
43// 12s worth of retries to ensure adequate time for the scan to finish.
44const AP_START_RETRY_INTERVAL: i64 = 2;
45const AP_START_MAX_RETRIES: u16 = 6;
46
47type State = state_machine::State<ExitReason>;
48type ReqStream = stream::Fuse<mpsc::Receiver<ManualRequest>>;
49
50pub trait AccessPointApi {
51    fn start(
52        &mut self,
53        request: ApConfig,
54        responder: oneshot::Sender<()>,
55    ) -> Result<(), anyhow::Error>;
56    fn stop(&mut self, responder: oneshot::Sender<()>) -> Result<(), anyhow::Error>;
57    fn exit(&mut self, responder: oneshot::Sender<()>) -> Result<(), anyhow::Error>;
58}
59
60pub struct AccessPoint {
61    req_sender: mpsc::Sender<ManualRequest>,
62}
63
64impl AccessPoint {
65    pub fn new(req_sender: mpsc::Sender<ManualRequest>) -> Self {
66        Self { req_sender }
67    }
68}
69
70impl AccessPointApi for AccessPoint {
71    fn start(
72        &mut self,
73        request: ApConfig,
74        responder: oneshot::Sender<()>,
75    ) -> Result<(), anyhow::Error> {
76        self.req_sender
77            .try_send(ManualRequest::Start((request, responder)))
78            .map_err(|e| format_err!("failed to send start request: {:?}", e))
79    }
80
81    fn stop(&mut self, responder: oneshot::Sender<()>) -> Result<(), anyhow::Error> {
82        self.req_sender
83            .try_send(ManualRequest::Stop(responder))
84            .map_err(|e| format_err!("failed to send stop request: {:?}", e))
85    }
86
87    fn exit(&mut self, responder: oneshot::Sender<()>) -> Result<(), anyhow::Error> {
88        self.req_sender
89            .try_send(ManualRequest::Exit(responder))
90            .map_err(|e| format_err!("failed to send exit request: {:?}", e))
91    }
92}
93
94pub enum ManualRequest {
95    Start((ApConfig, oneshot::Sender<()>)),
96    Stop(oneshot::Sender<()>),
97    Exit(oneshot::Sender<()>),
98}
99
100// Status artifact to be reported when recovery occurs.  This status is intended to be updated on
101// state transitions and SME status updates.
102#[derive(Clone, Debug, Default, PartialEq)]
103pub enum Status {
104    Stopping,
105    #[default]
106    Stopped,
107    Starting,
108    Started {
109        band: types::OperatingBand,
110        channel: u8,
111        mode: types::ConnectivityMode,
112        num_clients: u16,
113        security_type: types::SecurityType,
114    },
115}
116
117impl Status {
118    fn started_from_config(config: &ApConfig) -> Self {
119        Status::Started {
120            band: config.band,
121            channel: config.radio_config.channel.primary,
122            mode: config.mode,
123            num_clients: 0,
124            security_type: config.id.security_type,
125        }
126    }
127
128    fn started_from_sme_update(update: &fidl_sme::Ap, config: &ApConfig) -> Self {
129        Status::Started {
130            band: config.band,
131            channel: update.channel,
132            mode: config.mode,
133            num_clients: update.num_clients,
134            security_type: config.id.security_type,
135        }
136    }
137}
138
139impl WriteInspect for Status {
140    fn write_inspect<'a>(&self, writer: &InspectNode, key: impl Into<Cow<'a, str>>) {
141        match self {
142            Status::Started { band, channel, mode, num_clients, security_type } => {
143                inspect_insert!(writer, var key: {
144                    Started: {
145                        band: format!("{:?}", band),
146                        channel: channel,
147                        mode: format!("{:?}", mode),
148                        num_clients: num_clients,
149                        security_type: format!("{:?}", security_type)
150                    }
151                })
152            }
153            other => inspect_insert!(writer, var key: format!("{:?}", other)),
154        }
155    }
156}
157
158// To avoid printing PII, only allow Debug in tests, runtime logging should use Display
159#[cfg_attr(test, derive(Debug))]
160#[derive(Clone, PartialEq)]
161pub struct ApConfig {
162    pub id: types::NetworkIdentifier,
163    pub credential: Vec<u8>,
164    pub radio_config: RadioConfig,
165    pub mode: types::ConnectivityMode,
166    pub band: types::OperatingBand,
167}
168
169impl From<ApConfig> for fidl_sme::ApConfig {
170    fn from(config: ApConfig) -> Self {
171        fidl_sme::ApConfig {
172            ssid: config.id.ssid.to_vec(),
173            password: config.credential,
174            radio_cfg: config.radio_config.into(),
175        }
176    }
177}
178
179struct ApStateTrackerInner {
180    state: Option<ApStateUpdate>,
181    sender: ApListenerMessageSender,
182}
183
184impl ApStateTrackerInner {
185    fn send_update(&mut self) -> Result<(), anyhow::Error> {
186        let updates = match self.state.clone() {
187            Some(state) => ApStatesUpdate { access_points: [state].to_vec() },
188            None => ApStatesUpdate { access_points: [].to_vec() },
189        };
190
191        self.sender
192            .clone()
193            .unbounded_send(NotifyListeners(updates))
194            .map_err(|e| format_err!("failed to send state update: {}", e))
195    }
196}
197
198struct ApStateTracker {
199    inner: Mutex<ApStateTrackerInner>,
200}
201
202impl ApStateTracker {
203    fn new(sender: ApListenerMessageSender) -> Self {
204        ApStateTracker { inner: Mutex::new(ApStateTrackerInner { state: None, sender }) }
205    }
206
207    fn reset_state(&self, state: ApStateUpdate) -> Result<(), anyhow::Error> {
208        let mut inner = self.inner.lock();
209        inner.state = Some(state);
210        inner.send_update()
211    }
212
213    fn consume_sme_status_update(
214        &self,
215        cbw: Cbw,
216        band: fidl_fuchsia_wlan_ieee80211::WlanBand,
217        update: fidl_sme::Ap,
218    ) -> Result<(), anyhow::Error> {
219        let mut inner = self.inner.lock();
220
221        if let Some(ref mut state) = inner.state {
222            let channel = Channel::new(update.channel, cbw, band);
223            let frequency = match channel.get_center_freq() {
224                Ok(frequency) => Some(frequency as u32),
225                Err(e) => {
226                    info!("failed to convert channel to frequency: {}", e);
227                    None
228                }
229            };
230
231            let client_info = Some(ConnectedClientInformation { count: update.num_clients as u8 });
232
233            if frequency != state.frequency || client_info != state.clients {
234                state.frequency = frequency;
235                state.clients = client_info;
236                inner.send_update()?;
237            }
238        }
239
240        Ok(())
241    }
242
243    fn update_operating_state(
244        &self,
245        new_state: types::OperatingState,
246    ) -> Result<(), anyhow::Error> {
247        let mut inner = self.inner.lock();
248
249        // If there is a new operating state, update the existing operating state if present.
250        if let Some(ref mut state) = inner.state {
251            if state.state != new_state {
252                state.state = new_state;
253            }
254            inner.send_update()?;
255        }
256
257        Ok(())
258    }
259
260    fn set_stopped_state(&self) -> Result<(), anyhow::Error> {
261        let mut inner = self.inner.lock();
262        inner.state = None;
263        inner.send_update()
264    }
265}
266
267struct CommonStateDependencies {
268    iface_id: u16,
269    proxy: SmeForApStateMachine,
270    req_stream: ReqStream,
271    state_tracker: Arc<ApStateTracker>,
272    telemetry_sender: TelemetrySender,
273    defect_sender: mpsc::Sender<Defect>,
274    status_publisher: StateMachineStatusPublisher<Status>,
275}
276
277pub async fn serve(
278    iface_id: u16,
279    proxy: SmeForApStateMachine,
280    sme_event_stream: fidl_sme::ApSmeEventStream,
281    req_stream: Fuse<mpsc::Receiver<ManualRequest>>,
282    message_sender: ApListenerMessageSender,
283    telemetry_sender: TelemetrySender,
284    defect_sender: mpsc::Sender<Defect>,
285    status_publisher: StateMachineStatusPublisher<Status>,
286) {
287    let state_tracker = Arc::new(ApStateTracker::new(message_sender));
288    let deps = CommonStateDependencies {
289        iface_id,
290        proxy,
291        req_stream,
292        state_tracker: state_tracker.clone(),
293        telemetry_sender,
294        defect_sender,
295        status_publisher: status_publisher.clone(),
296    };
297    let state_machine = stopped_state(deps).into_state_machine();
298    let removal_watcher = sme_event_stream.map_ok(|_| ()).try_collect::<()>();
299    select! {
300        state_machine = state_machine.fuse() => {
301            match state_machine {
302                Err(ExitReason(Ok(()))) => info!("AP state machine for iface #{} exited", iface_id),
303                Err(ExitReason(Err(e))) => {
304                    info!("AP state machine for iface #{} terminated with an error: {}", iface_id, e)
305                }
306            }
307        },
308        removal_watcher = removal_watcher.fuse() => {
309            match removal_watcher {
310                Ok(()) => info!("AP interface was unexpectedly removed: {}", iface_id),
311                Err(e) => {
312                    info!("Error reading from AP SME channel of iface #{}: {}", iface_id, e);
313                }
314            }
315            let _ = state_tracker.update_operating_state(types::OperatingState::Failed);
316        }
317    }
318
319    status_publisher.publish_status(Status::Stopped);
320}
321
322fn perform_manual_request(
323    deps: CommonStateDependencies,
324    req: Option<ManualRequest>,
325) -> Result<State, ExitReason> {
326    match req {
327        Some(ManualRequest::Start((req, responder))) => {
328            Ok(starting_state(deps, req, AP_START_MAX_RETRIES, Some(responder)).into_state())
329        }
330        Some(ManualRequest::Stop(responder)) => Ok(stopping_state(deps, responder).into_state()),
331        Some(ManualRequest::Exit(responder)) => {
332            responder.send(()).unwrap_or(());
333            Err(ExitReason(Ok(())))
334        }
335        None => {
336            // It is possible that the state machine will be cleaned up before it has the
337            // opportunity to realize that the SME is no longer functional.  In this scenario,
338            // listeners need to be notified of the failure.
339            deps.state_tracker
340                .update_operating_state(types::OperatingState::Failed)
341                .map_err(|e| ExitReason(Err(e)))?;
342
343            Err(ExitReason(Err(format_err!("The stream of user requests ended unexpectedly"))))
344        }
345    }
346}
347
348// This intermediate state supresses a compiler warning on detection of a cycle.
349fn transition_to_starting(
350    deps: CommonStateDependencies,
351    req: ApConfig,
352    remaining_retries: u16,
353    responder: Option<oneshot::Sender<()>>,
354) -> Result<State, ExitReason> {
355    Ok(starting_state(deps, req, remaining_retries, responder).into_state())
356}
357
358/// In the starting state, a request to ApSmeProxy::Start is made.  If the start request fails,
359/// the state machine exits with an error.  On success, the state machine transitions into the
360/// started state to monitor the SME.
361///
362/// The starting state can be entered in the following ways.
363/// 1. When the state machine is stopped and it is asked to start an AP.
364/// 2. When the state machine is started and the AP fails.
365/// 3. When retrying a failed start attempt.
366///
367/// The starting state can be exited in the following ways.
368/// 1. If stopping the AP SME fails, exit the state machine.  The stop operation should be a very
369///    brief interaction with the firmware.  Failure can only occur if the SME layer times out the
370///    operation or the driver crashes.  Either scenario should be considered fatal.
371/// 2. If the start request fails because the AP state machine cannot communicate with the SME,
372///    the state machine will exit with an error.
373/// 3. If the start request fails due to an error reported by the SME, it's possible that a client
374///    interface associated with the same PHY is scanning.  In this case, allow the operation to be
375///    retried by transitioning back through the starting state.  Once the retries are exhausted,
376///    exit the state machine with an error.
377/// 4. When the start request finishes, transition into the started state.
378async fn starting_state(
379    mut deps: CommonStateDependencies,
380    req: ApConfig,
381    remaining_retries: u16,
382    responder: Option<oneshot::Sender<()>>,
383) -> Result<State, ExitReason> {
384    deps.status_publisher.publish_status(Status::Starting);
385
386    // Send a stop request to ensure that the AP begins in an unstarting state.
387    let stop_result = match deps.proxy.stop().await {
388        Ok(fidl_sme::StopApResultCode::Success) => Ok(()),
389        Ok(code) => Err(format_err!("Unexpected StopApResultCode: {:?}", code)),
390        Err(e) => Err(format_err!("Failed to send a stop command to wlanstack: {}", e)),
391    };
392
393    // If the stop operation failed, send a failure update and exit the state machine.
394    if stop_result.is_err() {
395        deps.state_tracker
396            .reset_state(ApStateUpdate::new(
397                req.id.clone(),
398                types::OperatingState::Failed,
399                req.mode,
400                req.band,
401            ))
402            .map_err(|e| ExitReason(Err(e)))?;
403
404        stop_result.map_err(|e| ExitReason(Err(e)))?;
405    }
406
407    // If the stop operation was successful, update all listeners that the AP is stopped.
408    deps.state_tracker.set_stopped_state().map_err(|e| ExitReason(Err(e)))?;
409
410    // Update all listeners that a new AP is starting if this is the first attempt to start the AP.
411    if remaining_retries == AP_START_MAX_RETRIES {
412        deps.state_tracker
413            .reset_state(ApStateUpdate::new(
414                req.id.clone(),
415                types::OperatingState::Starting,
416                req.mode,
417                req.band,
418            ))
419            .map_err(|e| ExitReason(Err(e)))?;
420    }
421
422    let ap_config = fidl_sme::ApConfig::from(req.clone());
423    let start_result = match deps.proxy.start(&ap_config).await {
424        Ok(fidl_sme::StartApResultCode::Success) => {
425            deps.telemetry_sender.send(TelemetryEvent::StartApResult(Ok(())));
426            Ok(())
427        }
428        Ok(code) => {
429            // Log a metric indicating that starting the AP failed.
430            deps.telemetry_sender.send(TelemetryEvent::StartApResult(Err(())));
431            if let Err(e) = deps
432                .defect_sender
433                .try_send(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: deps.iface_id }))
434            {
435                warn!("Failed to log AP start defect: {}", e)
436            }
437
438            // For any non-Success response, attempt to retry the start operation.  A successful
439            // stop operation followed by an unsuccessful start operation likely indicates that the
440            // PHY associated with this AP interface is busy scanning.  A future attempt to start
441            // may succeed.
442            if remaining_retries > 0 {
443                let mut retry_timer = pin!(fasync::Timer::new(
444                    zx::MonotonicDuration::from_seconds(AP_START_RETRY_INTERVAL).after_now(),
445                ));
446
447                // To ensure that the state machine remains responsive, process any incoming
448                // requests while waiting for the timer to expire.
449                select! {
450                    () = retry_timer => {
451                        return transition_to_starting(
452                            deps,
453                            req,
454                            remaining_retries - 1,
455                            responder,
456                        );
457                    },
458                    req = deps.req_stream.next() => {
459                        // If a new request comes in, clear out the current AP state.
460                        deps.state_tracker
461                            .set_stopped_state()
462                            .map_err(|e| ExitReason(Err(e)))?;
463                        return perform_manual_request(deps, req)
464                    }
465                }
466            }
467
468            // Return an error if all retries have been exhausted.
469            Err(format_err!("Failed to start AP: {:?}", code))
470        }
471        Err(e) => {
472            // If communicating with the SME fails, further attempts to start the AP are guaranteed
473            // to fail.
474            Err(format_err!("Failed to send a start command to wlanstack: {}", e))
475        }
476    };
477
478    start_result.map_err(|e| {
479        // Send a failure notification.
480        if let Err(e) = deps.state_tracker.reset_state(ApStateUpdate::new(
481            req.id.clone(),
482            types::OperatingState::Failed,
483            req.mode,
484            req.band,
485        )) {
486            info!("Unable to notify listeners of AP start failure: {:?}", e);
487        }
488        ExitReason(Err(e))
489    })?;
490
491    #[allow(clippy::single_match, reason = "mass allow for https://fxbug.dev/381896734")]
492    match responder {
493        Some(responder) => responder.send(()).unwrap_or(()),
494        None => {}
495    }
496
497    deps.state_tracker
498        .update_operating_state(types::OperatingState::Active)
499        .map_err(|e| ExitReason(Err(e)))?;
500    Ok(started_state(deps, req).into_state())
501}
502
503/// In the stopping state, an ApSmeProxy::Stop is requested.  Once the stop request has been
504/// processed by the ApSmeProxy, all requests to stop the AP are acknowledged.  The state machine
505/// then transitions into the stopped state.
506///
507/// The stopping state can be entered in the following ways.
508/// 1. When a manual stop request is made when the state machine is in the started state.
509///
510/// The stopping state can be exited in the following ways.
511/// 1. When the request to stop the SME completes, the state machine will transition to the stopped
512///    state.
513/// 2. If an SME interaction fails, exits the state machine with an error.
514async fn stopping_state(
515    deps: CommonStateDependencies,
516    responder: oneshot::Sender<()>,
517) -> Result<State, ExitReason> {
518    deps.status_publisher.publish_status(Status::Stopping);
519
520    let result = match deps.proxy.stop().await {
521        Ok(fidl_sme::StopApResultCode::Success) => Ok(()),
522        Ok(code) => Err(format_err!("Unexpected StopApResultCode: {:?}", code)),
523        Err(e) => Err(format_err!("Failed to send a stop command to wlanstack: {}", e)),
524    };
525
526    // If the stop command fails, the SME is probably unusable.  If the state is not updated before
527    // evaluating the stop result code, the AP state updates may end up with a lingering reference
528    // to a started or starting AP.
529    deps.state_tracker.set_stopped_state().map_err(|e| ExitReason(Err(e)))?;
530    result.map_err(|e| ExitReason(Err(e)))?;
531
532    // Ack the request to stop the AP.
533    responder.send(()).unwrap_or(());
534
535    Ok(stopped_state(deps).into_state())
536}
537
538async fn stopped_state(mut deps: CommonStateDependencies) -> Result<State, ExitReason> {
539    deps.status_publisher.publish_status(Status::Stopped);
540
541    // Wait for the next request from the caller
542    loop {
543        let req = deps.req_stream.next().await;
544        match req {
545            // Immediately reply to stop requests indicating that the AP is already stopped
546            Some(ManualRequest::Stop(responder)) => {
547                responder.send(()).unwrap_or(());
548            }
549            // All other requests are handled manually
550            other => return perform_manual_request(deps, other),
551        }
552    }
553}
554
555async fn started_state(
556    mut deps: CommonStateDependencies,
557    req: ApConfig,
558) -> Result<State, ExitReason> {
559    deps.status_publisher.publish_status(Status::started_from_config(&req));
560
561    // Holds a pending status request.  Request status immediately upon entering the started state.
562    let mut pending_status_req = FuturesUnordered::new();
563    let status_proxy = deps.proxy.clone();
564    pending_status_req.push(async move { status_proxy.status().await }.boxed());
565
566    let mut status_timer =
567        fasync::Interval::new(zx::MonotonicDuration::from_seconds(AP_STATUS_INTERVAL_SEC));
568
569    // Channel bandwidth is required for frequency computation when reporting state updates.
570    let cbw = req.radio_config.channel.cbw;
571
572    loop {
573        select! {
574            status_response = pending_status_req.select_next_some() => {
575                let status_response = match status_response {
576                    Ok(status_response) => status_response,
577                    Err(e) => {
578                        // If querying AP status fails, notify listeners and exit the state
579                        // machine.
580                        deps.state_tracker.update_operating_state(types::OperatingState::Failed)
581                            .map_err(|e| { ExitReason(Err(e)) })?;
582
583                        return Err(ExitReason(Err(e)));
584                    }
585                };
586
587                match status_response.running_ap {
588                    Some(sme_state) => {
589                        deps.status_publisher
590                            .publish_status(Status::started_from_sme_update(&sme_state, &req));
591                        deps.state_tracker.consume_sme_status_update(cbw, req.radio_config.channel.band, *sme_state)
592                            .map_err(|e| { ExitReason(Err(e)) })?;
593                    }
594                    None => {
595                        deps.state_tracker.update_operating_state(types::OperatingState::Failed)
596                            .map_err(|e| { ExitReason(Err(e)) })?;
597
598                        return transition_to_starting(
599                            deps,
600                            req,
601                            AP_START_MAX_RETRIES,
602                            None,
603                        );
604                    }
605                }
606            },
607            _ = status_timer.select_next_some() => {
608                if pending_status_req.is_empty() {
609                    let status_proxy = deps.proxy.clone();
610                    pending_status_req.push(async move {
611                        status_proxy.status().await
612                    }.boxed());
613                }
614            },
615            manual_req = deps.req_stream.next() => {
616                return perform_manual_request(deps, manual_req)
617            },
618            complete => {
619                panic!("AP state machine terminated unexpectedly");
620            }
621        }
622    }
623}
624
625#[cfg(test)]
626mod tests {
627    use super::*;
628
629    fn new_radio_config() -> RadioConfig {
630        RadioConfig::new(
631            fidl_fuchsia_wlan_ieee80211::WlanPhyType::Ht,
632            Cbw::Cbw20,
633            6,
634            fidl_fuchsia_wlan_ieee80211::WlanBand::TwoGhz,
635        )
636    }
637    use crate::util::listener;
638    use crate::util::state_machine::{StateMachineStatusReader, status_publisher_and_reader};
639    use assert_matches::assert_matches;
640    use fidl::endpoints::create_proxy;
641    use futures::Future;
642    use futures::stream::StreamFuture;
643    use futures::task::Poll;
644    use std::pin::pin;
645
646    struct TestValues {
647        deps: CommonStateDependencies,
648        sme_req_stream: fidl_sme::ApSmeRequestStream,
649        ap_req_sender: mpsc::Sender<ManualRequest>,
650        update_receiver: mpsc::UnboundedReceiver<listener::ApMessage>,
651        telemetry_receiver: mpsc::Receiver<TelemetryEvent>,
652        defect_receiver: mpsc::Receiver<Defect>,
653        status_reader: StateMachineStatusReader<Status>,
654    }
655
656    fn test_setup() -> TestValues {
657        let (ap_req_sender, ap_req_stream) = mpsc::channel(1);
658        let (update_sender, update_receiver) = mpsc::unbounded();
659        let (telemetry_sender, telemetry_receiver) = mpsc::channel(100);
660        let telemetry_sender = TelemetrySender::new(telemetry_sender);
661        let (defect_sender, defect_receiver) = mpsc::channel(100);
662        let (status_publisher, status_reader) = status_publisher_and_reader::<Status>();
663        let (sme_proxy, sme_server) = create_proxy::<fidl_sme::ApSmeMarker>();
664        let sme_req_stream = sme_server.into_stream();
665        let sme_proxy = SmeForApStateMachine::new(sme_proxy, 123, defect_sender.clone());
666
667        let deps = CommonStateDependencies {
668            iface_id: 123,
669            proxy: sme_proxy,
670            req_stream: ap_req_stream.fuse(),
671            state_tracker: Arc::new(ApStateTracker::new(update_sender)),
672            telemetry_sender,
673            defect_sender,
674            status_publisher,
675        };
676
677        TestValues {
678            deps,
679            sme_req_stream,
680            ap_req_sender,
681            update_receiver,
682            telemetry_receiver,
683            defect_receiver,
684            status_reader,
685        }
686    }
687
688    fn create_network_id() -> types::NetworkIdentifier {
689        types::NetworkIdentifier {
690            ssid: types::Ssid::try_from("test_ssid").unwrap(),
691            security_type: types::SecurityType::None,
692        }
693    }
694
695    fn poll_sme_req(
696        exec: &mut fasync::TestExecutor,
697        next_sme_req: &mut StreamFuture<fidl_sme::ApSmeRequestStream>,
698    ) -> Poll<fidl_sme::ApSmeRequest> {
699        exec.run_until_stalled(next_sme_req).map(|(req, stream)| {
700            *next_sme_req = stream.into_future();
701            req.expect("did not expect the SME request stream to end")
702                .expect("error polling SME request stream")
703        })
704    }
705
706    #[allow(clippy::needless_return, reason = "mass allow for https://fxbug.dev/381896734")]
707    async fn run_state_machine(fut: impl Future<Output = Result<State, ExitReason>> + 'static) {
708        let state_machine = fut.into_state_machine();
709        select! {
710            _state_machine = state_machine.fuse() => return,
711        }
712    }
713
714    #[fuchsia::test]
715    fn test_stop_during_started() {
716        let mut exec = fasync::TestExecutor::new();
717        let test_values = test_setup();
718
719        let radio_config = new_radio_config();
720        let req = ApConfig {
721            id: create_network_id(),
722            credential: vec![],
723            radio_config,
724            mode: types::ConnectivityMode::Unrestricted,
725            band: types::OperatingBand::Any,
726        };
727        {
728            let state = ApStateUpdate::new(
729                create_network_id(),
730                types::OperatingState::Starting,
731                types::ConnectivityMode::Unrestricted,
732                types::OperatingBand::Any,
733            );
734            test_values.deps.state_tracker.inner.lock().state = Some(state);
735        }
736
737        // Run the started state and ignore the status request
738        let fut = started_state(test_values.deps, req);
739        let fut = run_state_machine(fut);
740        let mut fut = pin!(fut);
741
742        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
743
744        let sme_fut = test_values.sme_req_stream.into_future();
745        let mut sme_fut = pin!(sme_fut);
746
747        assert_matches!(
748            poll_sme_req(&mut exec, &mut sme_fut),
749            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
750                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 0, num_clients: 0 };
751                let response = fidl_sme::ApStatusResponse {
752                    running_ap: Some(Box::new(ap_info))
753                };
754                responder.send(&response).expect("could not send AP status response");
755            }
756        );
757
758        // Issue a stop request.
759        let mut ap = AccessPoint::new(test_values.ap_req_sender);
760        let (sender, mut receiver) = oneshot::channel();
761        ap.stop(sender).expect("failed to make stop request");
762
763        // Run the state machine and ensure that a stop request is issued by the SME proxy.
764        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
765        assert_matches!(
766            poll_sme_req(&mut exec, &mut sme_fut),
767            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
768                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send SME stop response");
769            }
770        );
771
772        // Expect the responder to be acknowledged
773        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
774        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
775    }
776
777    #[fuchsia::test]
778    fn test_exit_during_started() {
779        let mut exec = fasync::TestExecutor::new();
780        let test_values = test_setup();
781
782        let radio_config = new_radio_config();
783        let req = ApConfig {
784            id: create_network_id(),
785            credential: vec![],
786            radio_config,
787            mode: types::ConnectivityMode::Unrestricted,
788            band: types::OperatingBand::Any,
789        };
790        {
791            let state = ApStateUpdate::new(
792                create_network_id(),
793                types::OperatingState::Starting,
794                types::ConnectivityMode::Unrestricted,
795                types::OperatingBand::Any,
796            );
797            test_values.deps.state_tracker.inner.lock().state = Some(state);
798        }
799
800        // Run the started state and ignore the status request
801        let fut = started_state(test_values.deps, req);
802        let fut = run_state_machine(fut);
803        let mut fut = pin!(fut);
804
805        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
806
807        let sme_fut = test_values.sme_req_stream.into_future();
808        let mut sme_fut = pin!(sme_fut);
809
810        assert_matches!(
811            poll_sme_req(&mut exec, &mut sme_fut),
812            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
813                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 0, num_clients: 0 };
814                let response = fidl_sme::ApStatusResponse {
815                    running_ap: Some(Box::new(ap_info))
816                };
817                responder.send(&response).expect("could not send AP status response");
818            }
819        );
820
821        // Issue an exit request.
822        let mut ap = AccessPoint::new(test_values.ap_req_sender);
823        let (sender, mut receiver) = oneshot::channel();
824        ap.exit(sender).expect("failed to make stop request");
825
826        // Expect the responder to be acknowledged and the state machine to exit.
827        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
828        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
829    }
830
831    #[fuchsia::test]
832    fn test_start_during_started() {
833        let mut exec = fasync::TestExecutor::new();
834        let test_values = test_setup();
835
836        let radio_config = new_radio_config();
837        let req = ApConfig {
838            id: create_network_id(),
839            credential: vec![],
840            radio_config,
841            mode: types::ConnectivityMode::Unrestricted,
842            band: types::OperatingBand::Any,
843        };
844        {
845            let state = ApStateUpdate::new(
846                create_network_id(),
847                types::OperatingState::Starting,
848                types::ConnectivityMode::Unrestricted,
849                types::OperatingBand::Any,
850            );
851            test_values.deps.state_tracker.inner.lock().state = Some(state);
852        }
853
854        // Run the started state and ignore the status request
855        let fut = started_state(test_values.deps, req);
856        let fut = run_state_machine(fut);
857        let mut fut = pin!(fut);
858
859        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
860
861        let sme_fut = test_values.sme_req_stream.into_future();
862        let mut sme_fut = pin!(sme_fut);
863
864        assert_matches!(
865            poll_sme_req(&mut exec, &mut sme_fut),
866            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
867                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 0, num_clients: 0 };
868                let response = fidl_sme::ApStatusResponse {
869                    running_ap: Some(Box::new(ap_info))
870                };
871                responder.send(&response).expect("could not send AP status response");
872            }
873        );
874
875        // Issue a start request.
876        let mut ap = AccessPoint::new(test_values.ap_req_sender);
877        let (sender, mut receiver) = oneshot::channel();
878        let radio_config = new_radio_config();
879        let req = ApConfig {
880            id: create_network_id(),
881            credential: vec![],
882            radio_config,
883            mode: types::ConnectivityMode::Unrestricted,
884            band: types::OperatingBand::Any,
885        };
886        ap.start(req, sender).expect("failed to make stop request");
887
888        // Expect that the state machine issues a stop request followed by a start request.
889        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
890        assert_matches!(
891            poll_sme_req(&mut exec, &mut sme_fut),
892            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
893                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
894            }
895        );
896
897        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
898        assert_matches!(
899            poll_sme_req(&mut exec, &mut sme_fut),
900            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
901                responder
902                    .send(fidl_sme::StartApResultCode::Success)
903                    .expect("could not send AP stop response");
904            }
905        );
906
907        // Verify that the SME response is plumbed back to the caller.
908        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
909        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
910    }
911
912    #[fuchsia::test]
913    fn test_duplicate_status_during_started() {
914        let mut exec = fasync::TestExecutor::new();
915        let test_values = test_setup();
916
917        let radio_config = new_radio_config();
918        let req = ApConfig {
919            id: create_network_id(),
920            credential: vec![],
921            radio_config,
922            mode: types::ConnectivityMode::Unrestricted,
923            band: types::OperatingBand::Any,
924        };
925        {
926            let mut state = ApStateUpdate::new(
927                create_network_id(),
928                types::OperatingState::Starting,
929                types::ConnectivityMode::Unrestricted,
930                types::OperatingBand::Any,
931            );
932            state.frequency = Some(2437);
933            state.clients = Some(ConnectedClientInformation { count: 0 });
934            test_values.deps.state_tracker.inner.lock().state = Some(state);
935        }
936
937        // Run the started state and send back an identical status.
938        let fut = started_state(test_values.deps, req);
939        let fut = run_state_machine(fut);
940        let mut fut = pin!(fut);
941
942        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
943
944        let sme_fut = test_values.sme_req_stream.into_future();
945        let mut sme_fut = pin!(sme_fut);
946
947        assert_matches!(
948            poll_sme_req(&mut exec, &mut sme_fut),
949            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
950                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 6, num_clients: 0 };
951                let response = fidl_sme::ApStatusResponse {
952                    running_ap: Some(Box::new(ap_info))
953                };
954                responder.send(&response).expect("could not send AP status response");
955            }
956        );
957
958        // Run the state machine and ensure no update has been sent.
959        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
960        assert_matches!(
961            exec.run_until_stalled(&mut test_values.update_receiver.into_future()),
962            Poll::Pending
963        );
964    }
965
966    #[fuchsia::test]
967    fn test_new_status_during_started() {
968        let mut exec = fasync::TestExecutor::new();
969        let test_values = test_setup();
970
971        let radio_config = new_radio_config();
972        let req = ApConfig {
973            id: create_network_id(),
974            credential: vec![],
975            radio_config,
976            mode: types::ConnectivityMode::Unrestricted,
977            band: types::OperatingBand::Any,
978        };
979        {
980            let mut state = ApStateUpdate::new(
981                create_network_id(),
982                types::OperatingState::Starting,
983                types::ConnectivityMode::Unrestricted,
984                types::OperatingBand::Any,
985            );
986            state.frequency = Some(0);
987            state.clients = Some(ConnectedClientInformation { count: 0 });
988            test_values.deps.state_tracker.inner.lock().state = Some(state);
989        }
990
991        // Run the started state.
992        let fut = started_state(test_values.deps, req);
993        let fut = run_state_machine(fut);
994        let mut fut = pin!(fut);
995
996        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
997
998        // Verify the initial status report.
999        assert_matches!(
1000            test_values.status_reader.read_status(),
1001            Ok(Status::Started {
1002                band: types::OperatingBand::Any,
1003                channel: 6,
1004                mode: types::ConnectivityMode::Unrestricted,
1005                num_clients: 0,
1006                security_type: types::SecurityType::None,
1007            })
1008        );
1009
1010        // Send an SME status update.
1011        let sme_fut = test_values.sme_req_stream.into_future();
1012        let mut sme_fut = pin!(sme_fut);
1013
1014        assert_matches!(
1015            poll_sme_req(&mut exec, &mut sme_fut),
1016            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
1017                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 1, num_clients: 1 };
1018                let response = fidl_sme::ApStatusResponse {
1019                    running_ap: Some(Box::new(ap_info))
1020                };
1021                responder.send(&response).expect("could not send AP status response");
1022            }
1023        );
1024
1025        // Run the state machine and ensure an update has been sent.
1026        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1027        assert_matches!(
1028            exec.run_until_stalled(&mut test_values.update_receiver.into_future()),
1029            Poll::Ready((Some(listener::Message::NotifyListeners(updates)), _)) => {
1030                assert!(!updates.access_points.is_empty());
1031        });
1032
1033        // Verify that the status has been updated.
1034        assert_matches!(
1035            test_values.status_reader.read_status(),
1036            Ok(Status::Started {
1037                band: types::OperatingBand::Any,
1038                channel: 1,
1039                mode: types::ConnectivityMode::Unrestricted,
1040                num_clients: 1,
1041                security_type: types::SecurityType::None,
1042            })
1043        );
1044    }
1045
1046    #[fuchsia::test]
1047    fn test_sme_failure_during_started() {
1048        let mut exec = fasync::TestExecutor::new();
1049        let mut test_values = test_setup();
1050
1051        // Drop the serving side of the SME so that a status request will result in an error.
1052        drop(test_values.sme_req_stream);
1053
1054        let radio_config = new_radio_config();
1055        let req = ApConfig {
1056            id: create_network_id(),
1057            credential: vec![],
1058            radio_config,
1059            mode: types::ConnectivityMode::Unrestricted,
1060            band: types::OperatingBand::Any,
1061        };
1062        {
1063            let mut state = ApStateUpdate::new(
1064                create_network_id(),
1065                types::OperatingState::Starting,
1066                types::ConnectivityMode::Unrestricted,
1067                types::OperatingBand::Any,
1068            );
1069            state.frequency = Some(0);
1070            state.clients = Some(ConnectedClientInformation { count: 0 });
1071            test_values.deps.state_tracker.inner.lock().state = Some(state);
1072        }
1073
1074        // Run the started state and send back an identical status.
1075        let fut = started_state(test_values.deps, req);
1076        let fut = run_state_machine(fut);
1077        let mut fut = pin!(fut);
1078
1079        // The state machine should exit when it is unable to query status.
1080        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1081
1082        // Verify that a failure notification is send to listeners.
1083        assert_matches!(
1084            test_values.update_receiver.try_next(),
1085            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1086            let update = updates.access_points.pop().expect("no new updates available.");
1087            assert_eq!(update.state, types::OperatingState::Failed);
1088        });
1089    }
1090
1091    #[fuchsia::test]
1092    fn test_stop_while_stopped() {
1093        let mut exec = fasync::TestExecutor::new();
1094        let test_values = test_setup();
1095
1096        // Run the stopped state.
1097        let fut = stopped_state(test_values.deps);
1098        let fut = run_state_machine(fut);
1099        let mut fut = pin!(fut);
1100
1101        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1102
1103        // Issue a stop request.
1104        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1105        let (sender, mut receiver) = oneshot::channel();
1106        ap.stop(sender).expect("failed to make stop request");
1107
1108        // Expect the responder to be acknowledged immediately.
1109        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1110        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
1111    }
1112
1113    #[fuchsia::test]
1114    fn test_exit_while_stopped() {
1115        let mut exec = fasync::TestExecutor::new();
1116        let test_values = test_setup();
1117
1118        // Run the stopped state.
1119        let fut = stopped_state(test_values.deps);
1120        let fut = run_state_machine(fut);
1121        let mut fut = pin!(fut);
1122
1123        // Issue an exit request.
1124        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1125        let (sender, mut receiver) = oneshot::channel();
1126        ap.exit(sender).expect("failed to make stop request");
1127
1128        // Expect the responder to be acknowledged and the state machine to exit.
1129        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1130        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
1131    }
1132
1133    #[fuchsia::test]
1134    fn test_start_while_stopped() {
1135        let mut exec = fasync::TestExecutor::new();
1136        let mut test_values = test_setup();
1137
1138        // Run the stopped state.
1139        let fut = stopped_state(test_values.deps);
1140        let fut = run_state_machine(fut);
1141        let mut fut = pin!(fut);
1142
1143        // Issue a start request.
1144        let (sender, mut receiver) = oneshot::channel();
1145        let radio_config = new_radio_config();
1146        let req = ApConfig {
1147            id: create_network_id(),
1148            credential: vec![],
1149            radio_config,
1150            mode: types::ConnectivityMode::Unrestricted,
1151            band: types::OperatingBand::Any,
1152        };
1153
1154        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1155        ap.start(req, sender).expect("failed to make stop request");
1156
1157        // Expect that the state machine issues a stop request.
1158        let sme_fut = test_values.sme_req_stream.into_future();
1159        let mut sme_fut = pin!(sme_fut);
1160
1161        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1162        assert_matches!(
1163            poll_sme_req(&mut exec, &mut sme_fut),
1164            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1165                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1166            }
1167        );
1168
1169        // An empty update should be sent after stopping.
1170        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1171        assert_matches!(
1172            test_values.update_receiver.try_next(),
1173            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1174            assert!(updates.access_points.is_empty());
1175        });
1176
1177        // The empty update should be quickly followed by a starting update.
1178        assert_matches!(
1179            test_values.update_receiver.try_next(),
1180            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1181            let update = updates.access_points.pop().expect("no new updates available.");
1182            assert_eq!(update.state, types::OperatingState::Starting);
1183        });
1184
1185        // A start request should have been issues to the SME proxy.
1186        assert_matches!(
1187            poll_sme_req(&mut exec, &mut sme_fut),
1188            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1189                responder
1190                    .send(fidl_sme::StartApResultCode::Success)
1191                    .expect("could not send AP stop response");
1192            }
1193        );
1194
1195        // Verify that the SME response is plumbed back to the caller.
1196        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1197        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
1198
1199        // There should be a pending active state notification
1200        assert_matches!(
1201            test_values.update_receiver.try_next(),
1202            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1203            let update = updates.access_points.pop().expect("no new updates available.");
1204            assert_eq!(update.state, types::OperatingState::Active);
1205        });
1206    }
1207
1208    #[fuchsia::test]
1209    fn test_exit_while_stopping() {
1210        let mut exec = fasync::TestExecutor::new();
1211        let test_values = test_setup();
1212
1213        // Run the stopping state.
1214        let (stop_sender, mut stop_receiver) = oneshot::channel();
1215        let fut = stopping_state(test_values.deps, stop_sender);
1216        let fut = run_state_machine(fut);
1217        let mut fut = pin!(fut);
1218
1219        // Issue an exit request.
1220        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1221        let (exit_sender, mut exit_receiver) = oneshot::channel();
1222        ap.exit(exit_sender).expect("failed to make stop request");
1223
1224        // While stopping is still in progress, exit and stop should not be responded to yet.
1225        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1226        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Pending);
1227        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Pending);
1228
1229        // Once stop AP request is finished, the state machine can terminate
1230        let sme_fut = test_values.sme_req_stream.into_future();
1231        let mut sme_fut = pin!(sme_fut);
1232        assert_matches!(
1233            poll_sme_req(&mut exec, &mut sme_fut),
1234            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1235                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1236            }
1237        );
1238        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1239        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1240        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Ready(Ok(())));
1241    }
1242
1243    #[fuchsia::test]
1244    fn test_stop_while_stopping() {
1245        let mut exec = fasync::TestExecutor::new();
1246        let mut test_values = test_setup();
1247
1248        // Run the stopping state.
1249        let (stop_sender, mut stop_receiver) = oneshot::channel();
1250        let fut = stopping_state(test_values.deps, stop_sender);
1251        let fut = run_state_machine(fut);
1252        let mut fut = pin!(fut);
1253
1254        // Verify that no state update is ready yet.
1255        assert_matches!(&mut test_values.update_receiver.try_next(), Err(_));
1256
1257        // Issue a stop request.
1258        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1259        let (second_stop_sender, mut second_stop_receiver) = oneshot::channel();
1260        ap.stop(second_stop_sender).expect("failed to make stop request");
1261
1262        // Expect the stop request from the SME proxy
1263        let sme_fut = test_values.sme_req_stream.into_future();
1264        let mut sme_fut = pin!(sme_fut);
1265
1266        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1267        assert_matches!(
1268            poll_sme_req(&mut exec, &mut sme_fut),
1269            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1270                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1271            }
1272        );
1273
1274        // Expect both responders to be acknowledged.
1275        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1276        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1277        assert_matches!(exec.run_until_stalled(&mut second_stop_receiver), Poll::Ready(Ok(())));
1278
1279        // There should be a new update indicating that no AP's are active.
1280        assert_matches!(
1281            test_values.update_receiver.try_next(),
1282            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1283            assert!(updates.access_points.is_empty());
1284        });
1285    }
1286
1287    #[fuchsia::test]
1288    fn test_start_while_stopping() {
1289        let mut exec = fasync::TestExecutor::new();
1290        let test_values = test_setup();
1291
1292        // Run the stopping state.
1293        let (stop_sender, mut stop_receiver) = oneshot::channel();
1294        let fut = stopping_state(test_values.deps, stop_sender);
1295        let fut = run_state_machine(fut);
1296        let mut fut = pin!(fut);
1297
1298        // Issue a start request.
1299        let (start_sender, mut start_receiver) = oneshot::channel();
1300        let radio_config = new_radio_config();
1301        let req = ApConfig {
1302            id: create_network_id(),
1303            credential: vec![],
1304            radio_config,
1305            mode: types::ConnectivityMode::Unrestricted,
1306            band: types::OperatingBand::Any,
1307        };
1308
1309        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1310        ap.start(req, start_sender).expect("failed to make stop request");
1311
1312        // The state machine should not respond to the stop request yet until it's finished.
1313        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1314        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Pending);
1315        let sme_fut = test_values.sme_req_stream.into_future();
1316        let mut sme_fut = pin!(sme_fut);
1317        let stop_responder = assert_matches!(
1318            poll_sme_req(&mut exec, &mut sme_fut),
1319            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => responder
1320        );
1321
1322        // The state machine should not send new request yet since stop is still unfinished
1323        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
1324
1325        // After SME sends response, the state machine can proceed
1326        stop_responder
1327            .send(fidl_sme::StopApResultCode::Success)
1328            .expect("could not send AP stop response");
1329        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1330        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1331
1332        // Expect another stop request from the state machine entering the starting state.
1333        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1334        assert_matches!(
1335            poll_sme_req(&mut exec, &mut sme_fut),
1336            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1337                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1338            }
1339        );
1340
1341        // Expect a start request
1342        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1343        assert_matches!(
1344            poll_sme_req(&mut exec, &mut sme_fut),
1345            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1346                responder
1347                    .send(fidl_sme::StartApResultCode::Success)
1348                    .expect("could not send AP stop response");
1349            }
1350        );
1351
1352        // Expect the start responder to be acknowledged
1353        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1354        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1355    }
1356
1357    #[fuchsia::test]
1358    fn test_sme_failure_while_stopping() {
1359        let mut exec = fasync::TestExecutor::new();
1360        let mut test_values = test_setup();
1361
1362        // Drop the serving side of the SME so that the stop request will result in an error.
1363        drop(test_values.sme_req_stream);
1364
1365        // Run the stopping state.
1366        let (stop_sender, mut stop_receiver) = oneshot::channel();
1367        let fut = stopping_state(test_values.deps, stop_sender);
1368        let fut = run_state_machine(fut);
1369        let mut fut = pin!(fut);
1370
1371        // The state machine should exit when it is unable to issue the stop command.
1372        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1373        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Err(_)));
1374
1375        // There should be a new update indicating that no AP's are active.
1376        assert_matches!(
1377            test_values.update_receiver.try_next(),
1378            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1379            assert!(updates.access_points.is_empty());
1380        });
1381    }
1382
1383    #[fuchsia::test]
1384    fn test_failed_result_code_while_stopping() {
1385        let mut exec = fasync::TestExecutor::new();
1386        let mut test_values = test_setup();
1387
1388        // Run the stopping state.
1389        let (stop_sender, mut stop_receiver) = oneshot::channel();
1390        let fut = stopping_state(test_values.deps, stop_sender);
1391        let fut = run_state_machine(fut);
1392        let mut fut = pin!(fut);
1393
1394        // Verify that no state update is ready yet.
1395        assert_matches!(&mut test_values.update_receiver.try_next(), Err(_));
1396
1397        // Expect the stop request from the SME proxy
1398        let sme_fut = test_values.sme_req_stream.into_future();
1399        let mut sme_fut = pin!(sme_fut);
1400
1401        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1402        assert_matches!(
1403            poll_sme_req(&mut exec, &mut sme_fut),
1404            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1405                responder.send(fidl_sme::StopApResultCode::InternalError).expect("could not send AP stop response");
1406            }
1407        );
1408
1409        // The state machine should exit.
1410        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1411        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Err(_)));
1412
1413        // There should be a new update indicating that no AP's are active.
1414        assert_matches!(
1415            test_values.update_receiver.try_next(),
1416            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1417            assert!(updates.access_points.is_empty());
1418        });
1419    }
1420
1421    #[fuchsia::test]
1422    fn test_stop_while_starting() {
1423        let mut exec = fasync::TestExecutor::new();
1424        let mut test_values = test_setup();
1425
1426        let (start_sender, mut start_receiver) = oneshot::channel();
1427        let radio_config = new_radio_config();
1428        let req = ApConfig {
1429            id: create_network_id(),
1430            credential: vec![],
1431            radio_config,
1432            mode: types::ConnectivityMode::Unrestricted,
1433            band: types::OperatingBand::Any,
1434        };
1435
1436        // Start off in the starting state
1437        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1438        let fut = run_state_machine(fut);
1439        let mut fut = pin!(fut);
1440
1441        // Handle the initial disconnect request
1442        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1443
1444        let sme_fut = test_values.sme_req_stream.into_future();
1445        let mut sme_fut = pin!(sme_fut);
1446
1447        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1448        assert_matches!(
1449            poll_sme_req(&mut exec, &mut sme_fut),
1450            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1451                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1452            }
1453        );
1454
1455        // Wait for a start request, but don't reply to it yet.
1456        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1457        let start_responder = assert_matches!(
1458            poll_sme_req(&mut exec, &mut sme_fut),
1459            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1460                responder
1461            }
1462        );
1463
1464        // Issue a stop request.
1465        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1466        let (stop_sender, mut stop_receiver) = oneshot::channel();
1467        ap.stop(stop_sender).expect("failed to make stop request");
1468
1469        // Run the state machine and ensure that a stop request is not issued by the SME proxy yet.
1470        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1471        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
1472
1473        // After SME responds to start request, the state machine can continue
1474        start_responder
1475            .send(fidl_sme::StartApResultCode::Success)
1476            .expect("could not send SME start response");
1477        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1478        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1479
1480        // Stop request should be issued now to SME
1481        assert_matches!(
1482            poll_sme_req(&mut exec, &mut sme_fut),
1483            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1484                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send SME stop response");
1485            }
1486        );
1487
1488        // Expect the responder to be acknowledged
1489        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1490        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1491
1492        // The successful AP start should be logged.
1493        assert_matches!(
1494            test_values.telemetry_receiver.try_next(),
1495            Ok(Some(TelemetryEvent::StartApResult(Ok(()))))
1496        );
1497    }
1498
1499    #[fuchsia::test]
1500    fn test_start_while_starting() {
1501        let mut exec = fasync::TestExecutor::new();
1502        let mut test_values = test_setup();
1503
1504        let (start_sender, mut start_receiver) = oneshot::channel();
1505        let radio_config = new_radio_config();
1506        let req = ApConfig {
1507            id: create_network_id(),
1508            credential: vec![],
1509            radio_config,
1510            mode: types::ConnectivityMode::Unrestricted,
1511            band: types::OperatingBand::Any,
1512        };
1513
1514        // Start off in the starting state
1515        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1516        let fut = run_state_machine(fut);
1517        let mut fut = pin!(fut);
1518
1519        // Handle the initial disconnect request
1520        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1521
1522        let sme_fut = test_values.sme_req_stream.into_future();
1523        let mut sme_fut = pin!(sme_fut);
1524
1525        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1526        assert_matches!(
1527            poll_sme_req(&mut exec, &mut sme_fut),
1528            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1529                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1530            }
1531        );
1532
1533        // Wait for a start request, but don't reply to it.
1534        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1535        let start_responder = assert_matches!(
1536            poll_sme_req(&mut exec, &mut sme_fut),
1537            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1538                responder
1539            }
1540        );
1541
1542        // Issue a second start request.
1543        let (second_start_sender, mut second_start_receiver) = oneshot::channel();
1544        let radio_config = new_radio_config();
1545        let req = ApConfig {
1546            id: create_network_id(),
1547            credential: vec![],
1548            radio_config,
1549            mode: types::ConnectivityMode::Unrestricted,
1550            band: types::OperatingBand::Any,
1551        };
1552        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1553        ap.start(req, second_start_sender).expect("failed to make start request");
1554
1555        // Run the state machine and ensure that the first start request is still pending.
1556        // Furthermore, no new start request is issued yet.
1557        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1558        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Pending);
1559        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
1560
1561        // Respond to the first start request
1562        start_responder
1563            .send(fidl_sme::StartApResultCode::Success)
1564            .expect("failed to send start response");
1565
1566        // The first request should receive the acknowledgement, the second one shouldn't.
1567        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1568        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1569        assert_matches!(exec.run_until_stalled(&mut second_start_receiver), Poll::Pending);
1570
1571        // The state machine should transition back into the starting state and issue a stop
1572        // request, due to the second start request.
1573        assert_matches!(
1574            poll_sme_req(&mut exec, &mut sme_fut),
1575            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1576                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send SME stop response");
1577            }
1578        );
1579
1580        // The state machine should then issue a start request.
1581        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1582        assert_matches!(
1583            poll_sme_req(&mut exec, &mut sme_fut),
1584            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1585                responder
1586                    .send(fidl_sme::StartApResultCode::Success)
1587                    .expect("failed to send start response");
1588            }
1589        );
1590
1591        // The second start request should receive the acknowledgement now.
1592        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1593        assert_matches!(exec.run_until_stalled(&mut second_start_receiver), Poll::Ready(Ok(())));
1594
1595        // The successful AP start event should be logged.
1596        assert_matches!(
1597            test_values.telemetry_receiver.try_next(),
1598            Ok(Some(TelemetryEvent::StartApResult(Ok(()))))
1599        );
1600    }
1601
1602    #[fuchsia::test]
1603    fn test_exit_while_starting() {
1604        let mut exec = fasync::TestExecutor::new();
1605        let mut test_values = test_setup();
1606
1607        let (start_sender, mut start_receiver) = oneshot::channel();
1608        let radio_config = new_radio_config();
1609        let req = ApConfig {
1610            id: create_network_id(),
1611            credential: vec![],
1612            radio_config,
1613            mode: types::ConnectivityMode::Unrestricted,
1614            band: types::OperatingBand::Any,
1615        };
1616
1617        // Start off in the starting state
1618        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1619        let fut = run_state_machine(fut);
1620        let mut fut = pin!(fut);
1621
1622        // Handle the initial disconnect request
1623        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1624
1625        let sme_fut = test_values.sme_req_stream.into_future();
1626        let mut sme_fut = pin!(sme_fut);
1627
1628        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1629        assert_matches!(
1630            poll_sme_req(&mut exec, &mut sme_fut),
1631            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1632                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1633            }
1634        );
1635
1636        // Wait for a start request, but don't reply to it.
1637        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1638        let start_responder = assert_matches!(
1639            poll_sme_req(&mut exec, &mut sme_fut),
1640            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1641                responder
1642            }
1643        );
1644
1645        // Issue an exit request.
1646        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1647        let (exit_sender, mut exit_receiver) = oneshot::channel();
1648        ap.exit(exit_sender).expect("failed to make stop request");
1649
1650        // While starting is still in progress, exit and start should not be responded to yet.
1651        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1652        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Pending);
1653        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Pending);
1654
1655        // Once start AP request is finished, the state machine can terminate
1656        start_responder
1657            .send(fidl_sme::StartApResultCode::Success)
1658            .expect("could not send AP start response");
1659
1660        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1661        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Ready(Ok(())));
1662        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1663
1664        // The AP start success event should be logged to telemetry.
1665        assert_matches!(
1666            test_values.telemetry_receiver.try_next(),
1667            Ok(Some(TelemetryEvent::StartApResult(Ok(()))))
1668        );
1669    }
1670
1671    #[fuchsia::test]
1672    fn test_sme_breaks_while_starting() {
1673        let mut exec = fasync::TestExecutor::new();
1674        let mut test_values = test_setup();
1675
1676        // Drop the serving side of the SME so that client requests fail.
1677        drop(test_values.sme_req_stream);
1678
1679        let (start_sender, _start_receiver) = oneshot::channel();
1680        let radio_config = new_radio_config();
1681        let req = ApConfig {
1682            id: create_network_id(),
1683            credential: vec![],
1684            radio_config,
1685            mode: types::ConnectivityMode::Unrestricted,
1686            band: types::OperatingBand::Any,
1687        };
1688
1689        // Start off in the starting state
1690        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1691        let fut = run_state_machine(fut);
1692        let mut fut = pin!(fut);
1693
1694        // Run the state machine and expect it to exit
1695        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1696
1697        // No metric should be logged in this case and the sender should have been dropped.
1698        assert_matches!(test_values.telemetry_receiver.try_next(), Ok(None));
1699    }
1700
1701    #[fuchsia::test]
1702    fn test_sme_fails_to_stop_while_starting() {
1703        let mut exec = fasync::TestExecutor::new();
1704        let mut test_values = test_setup();
1705
1706        let (start_sender, _start_receiver) = oneshot::channel();
1707        let radio_config = new_radio_config();
1708        let req = ApConfig {
1709            id: create_network_id(),
1710            credential: vec![],
1711            radio_config,
1712            mode: types::ConnectivityMode::Unrestricted,
1713            band: types::OperatingBand::Any,
1714        };
1715
1716        // Start off in the starting state
1717        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1718        let fut = run_state_machine(fut);
1719        let mut fut = pin!(fut);
1720
1721        // Handle the initial disconnect request and send back a failure.
1722        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1723
1724        let sme_fut = test_values.sme_req_stream.into_future();
1725        let mut sme_fut = pin!(sme_fut);
1726
1727        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1728        assert_matches!(
1729            poll_sme_req(&mut exec, &mut sme_fut),
1730            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1731                responder
1732                    .send(fidl_sme::StopApResultCode::TimedOut)
1733                    .expect("could not send AP stop response");
1734            }
1735        );
1736
1737        // The future should complete.
1738        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1739
1740        // There should also be a failed state update.
1741        assert_matches!(
1742            test_values.update_receiver.try_next(),
1743            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1744            let update = updates.access_points.pop().expect("no new updates available.");
1745            assert_eq!(update.state, types::OperatingState::Failed);
1746        });
1747
1748        // No metric should be logged in this case and the sender should have been dropped.
1749        assert_matches!(test_values.telemetry_receiver.try_next(), Ok(None));
1750    }
1751
1752    #[fuchsia::test]
1753    fn test_sme_fails_to_start_while_starting() {
1754        let mut exec = fasync::TestExecutor::new();
1755        let mut test_values = test_setup();
1756
1757        let (start_sender, mut start_receiver) = oneshot::channel();
1758        let radio_config = new_radio_config();
1759        let req = ApConfig {
1760            id: create_network_id(),
1761            credential: vec![],
1762            radio_config,
1763            mode: types::ConnectivityMode::Unrestricted,
1764            band: types::OperatingBand::Any,
1765        };
1766
1767        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
1768        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
1769        let fut = run_state_machine(fut);
1770        let mut fut = pin!(fut);
1771
1772        // We'll need to inject some SME responses.
1773        let sme_fut = test_values.sme_req_stream.into_future();
1774        let mut sme_fut = pin!(sme_fut);
1775
1776        for retry_number in 0..(AP_START_MAX_RETRIES + 1) {
1777            // Handle the initial stop request.
1778            assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1779            assert_matches!(
1780                poll_sme_req(&mut exec, &mut sme_fut),
1781                Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1782                    responder
1783                        .send(fidl_sme::StopApResultCode::Success)
1784                        .expect("could not send AP stop response");
1785                }
1786            );
1787
1788            // There should also be a stopped state update.
1789            assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1790            assert_matches!(
1791                test_values.update_receiver.try_next(),
1792                Ok(Some(listener::Message::NotifyListeners(_)))
1793            );
1794
1795            // If this is the first attempt, there should be a starting notification, otherwise
1796            // there should be no update.
1797            if retry_number == 0 {
1798                assert_matches!(
1799                    test_values.update_receiver.try_next(),
1800                    Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1801                    let update = updates.access_points.pop().expect("no new updates available.");
1802                    assert_eq!(update.state, types::OperatingState::Starting);
1803                });
1804            } else {
1805                assert_matches!(test_values.update_receiver.try_next(), Err(_));
1806            }
1807
1808            // Wait for a start request and send back a timeout.
1809            assert_matches!(
1810                poll_sme_req(&mut exec, &mut sme_fut),
1811                Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1812                    responder
1813                        .send(fidl_sme::StartApResultCode::TimedOut)
1814                        .expect("could not send AP stop response");
1815                }
1816            );
1817
1818            if retry_number < AP_START_MAX_RETRIES {
1819                // The future should still be running.
1820                assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1821
1822                // Verify that no new message has been reported yet.
1823                assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Pending);
1824
1825                // The state machine should then retry following the retry interval.
1826                assert_matches!(exec.wake_next_timer(), Some(_));
1827            }
1828        }
1829
1830        // The future should complete.
1831        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1832
1833        // Verify that the start receiver got an error.
1834        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Err(_)));
1835
1836        // There should be a failure notification at the end of the retries.
1837        assert_matches!(
1838            test_values.update_receiver.try_next(),
1839            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1840            let update = updates.access_points.pop().expect("no new updates available.");
1841            assert_eq!(update.state, types::OperatingState::Failed);
1842        });
1843
1844        // A metric should be logged for the failure to start the AP.
1845        assert_matches!(
1846            test_values.telemetry_receiver.try_next(),
1847            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
1848        );
1849
1850        // A defect should be sent as well.
1851        assert_matches!(
1852            test_values.defect_receiver.try_next(),
1853            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
1854        );
1855    }
1856
1857    #[fuchsia::test]
1858    fn test_stop_after_start_failure() {
1859        let mut exec = fasync::TestExecutor::new();
1860        let mut test_values = test_setup();
1861
1862        let (start_sender, mut start_receiver) = oneshot::channel();
1863        let radio_config = new_radio_config();
1864        let req = ApConfig {
1865            id: create_network_id(),
1866            credential: vec![],
1867            radio_config,
1868            mode: types::ConnectivityMode::Unrestricted,
1869            band: types::OperatingBand::Any,
1870        };
1871
1872        // Insert a stop request to be processed after starting the AP fails.
1873        let (stop_sender, mut stop_receiver) = oneshot::channel();
1874        test_values
1875            .ap_req_sender
1876            .try_send(ManualRequest::Stop(stop_sender))
1877            .expect("failed to request AP stop");
1878
1879        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
1880        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
1881        let fut = run_state_machine(fut);
1882        let mut fut = pin!(fut);
1883
1884        // We'll need to inject some SME responses.
1885        let sme_fut = test_values.sme_req_stream.into_future();
1886        let mut sme_fut = pin!(sme_fut);
1887
1888        // Handle the initial stop request.
1889        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1890        assert_matches!(
1891            poll_sme_req(&mut exec, &mut sme_fut),
1892            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1893                responder
1894                    .send(fidl_sme::StopApResultCode::Success)
1895                    .expect("could not send AP stop response");
1896            }
1897        );
1898
1899        // There should also be a stopped state update.
1900        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1901        assert_matches!(
1902            test_values.update_receiver.try_next(),
1903            Ok(Some(listener::Message::NotifyListeners(_)))
1904        );
1905
1906        // Followed by a starting update.
1907        assert_matches!(
1908            test_values.update_receiver.try_next(),
1909            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1910            let update = updates.access_points.pop().expect("no new updates available.");
1911            assert_eq!(update.state, types::OperatingState::Starting);
1912        });
1913
1914        // Wait for a start request and send back a timeout.
1915        assert_matches!(
1916            poll_sme_req(&mut exec, &mut sme_fut),
1917            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1918                responder
1919                    .send(fidl_sme::StartApResultCode::TimedOut)
1920                    .expect("could not send AP stop response");
1921            }
1922        );
1923
1924        // At this point, the state machine should pause before retrying the start request.  It
1925        // should also check to see if there are any incoming AP commands and find the initial stop
1926        // request.
1927        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1928
1929        // A metric should be logged for the failure to start the AP.
1930        assert_matches!(
1931            test_values.telemetry_receiver.try_next(),
1932            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
1933        );
1934
1935        // A defect should be sent as well.
1936        assert_matches!(
1937            test_values.defect_receiver.try_next(),
1938            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
1939        );
1940
1941        // The start sender will be dropped in this transition.
1942        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Err(_)));
1943
1944        // There should be a pending AP stop request.
1945        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1946        assert_matches!(
1947            poll_sme_req(&mut exec, &mut sme_fut),
1948            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1949                responder
1950                    .send(fidl_sme::StopApResultCode::Success)
1951                    .expect("could not send AP stop response");
1952            }
1953        );
1954
1955        // The future should be parked in the stopped state.
1956        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1957
1958        // Verify that the stop receiver is acknowledged.
1959        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1960
1961        // There should be a new update indicating that no AP's are active.
1962        assert_matches!(
1963            test_values.update_receiver.try_next(),
1964            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1965            assert!(updates.access_points.is_empty());
1966        });
1967    }
1968
1969    #[fuchsia::test]
1970    fn test_start_after_start_failure() {
1971        let mut exec = fasync::TestExecutor::new();
1972        let mut test_values = test_setup();
1973
1974        let (start_sender, mut start_receiver) = oneshot::channel();
1975        let radio_config = new_radio_config();
1976        let req = ApConfig {
1977            id: create_network_id(),
1978            credential: vec![],
1979            radio_config: radio_config.clone(),
1980            mode: types::ConnectivityMode::Unrestricted,
1981            band: types::OperatingBand::Any,
1982        };
1983
1984        // Insert a stop request to be processed after starting the AP fails.
1985        let mut requested_id = create_network_id();
1986        requested_id.ssid = types::Ssid::try_from("second_test_ssid").unwrap();
1987
1988        let requested_config = ApConfig {
1989            id: requested_id.clone(),
1990            credential: vec![],
1991            radio_config,
1992            mode: types::ConnectivityMode::Unrestricted,
1993            band: types::OperatingBand::Any,
1994        };
1995
1996        let (start_response_sender, _) = oneshot::channel();
1997        test_values
1998            .ap_req_sender
1999            .try_send(ManualRequest::Start((requested_config, start_response_sender)))
2000            .expect("failed to request AP stop");
2001
2002        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
2003        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
2004        let fut = run_state_machine(fut);
2005        let mut fut = pin!(fut);
2006
2007        // We'll need to inject some SME responses.
2008        let sme_fut = test_values.sme_req_stream.into_future();
2009        let mut sme_fut = pin!(sme_fut);
2010
2011        // Handle the initial stop request.
2012        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2013        assert_matches!(
2014            poll_sme_req(&mut exec, &mut sme_fut),
2015            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2016                responder
2017                    .send(fidl_sme::StopApResultCode::Success)
2018                    .expect("could not send AP stop response");
2019            }
2020        );
2021
2022        // There should also be a stopped state update.
2023        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2024        assert_matches!(
2025            test_values.update_receiver.try_next(),
2026            Ok(Some(listener::Message::NotifyListeners(_)))
2027        );
2028
2029        // Followed by a starting update.
2030        assert_matches!(
2031            test_values.update_receiver.try_next(),
2032            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
2033            let update = updates.access_points.pop().expect("no new updates available.");
2034            assert_eq!(update.state, types::OperatingState::Starting);
2035        });
2036
2037        // Wait for a start request and send back a timeout.
2038        assert_matches!(
2039            poll_sme_req(&mut exec, &mut sme_fut),
2040            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
2041                responder
2042                    .send(fidl_sme::StartApResultCode::TimedOut)
2043                    .expect("could not send AP stop response");
2044            }
2045        );
2046
2047        // At this point, the state machine should pause before retrying the start request.  It
2048        // should also check to see if there are any incoming AP commands and find the initial
2049        // start request.
2050        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2051
2052        // A metric should be logged for the failure to start the AP.
2053        assert_matches!(
2054            test_values.telemetry_receiver.try_next(),
2055            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
2056        );
2057
2058        // A defect should be sent as well.
2059        assert_matches!(
2060            test_values.defect_receiver.try_next(),
2061            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
2062        );
2063
2064        // The original start sender will be dropped in this transition.
2065        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Err(_)));
2066
2067        // There should be a pending AP stop request.
2068        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2069        assert_matches!(
2070            poll_sme_req(&mut exec, &mut sme_fut),
2071            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2072                responder
2073                    .send(fidl_sme::StopApResultCode::Success)
2074                    .expect("could not send AP stop response");
2075            }
2076        );
2077
2078        // This should be followed by another start request that matches the requested config.
2079        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2080        assert_matches!(
2081            poll_sme_req(&mut exec, &mut sme_fut),
2082            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config, responder: _ }) => {
2083                assert_eq!(config.ssid, requested_id.ssid);
2084            }
2085        );
2086    }
2087
2088    #[fuchsia::test]
2089    fn test_exit_after_start_failure() {
2090        let mut exec = fasync::TestExecutor::new();
2091        let mut test_values = test_setup();
2092
2093        let (start_sender, _) = oneshot::channel();
2094        let radio_config = new_radio_config();
2095        let req = ApConfig {
2096            id: create_network_id(),
2097            credential: vec![],
2098            radio_config,
2099            mode: types::ConnectivityMode::Unrestricted,
2100            band: types::OperatingBand::Any,
2101        };
2102
2103        // Insert a stop request to be processed after starting the AP fails.
2104        let (exit_sender, mut exit_receiver) = oneshot::channel();
2105        test_values
2106            .ap_req_sender
2107            .try_send(ManualRequest::Exit(exit_sender))
2108            .expect("failed to request AP stop");
2109
2110        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
2111        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
2112        let fut = run_state_machine(fut);
2113        let mut fut = pin!(fut);
2114
2115        // We'll need to inject some SME responses.
2116        let sme_fut = test_values.sme_req_stream.into_future();
2117        let mut sme_fut = pin!(sme_fut);
2118
2119        // Handle the initial stop request.
2120        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2121        assert_matches!(
2122            poll_sme_req(&mut exec, &mut sme_fut),
2123            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2124                responder
2125                    .send(fidl_sme::StopApResultCode::Success)
2126                    .expect("could not send AP stop response");
2127            }
2128        );
2129
2130        // There should also be a stopped state update.
2131        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2132        assert_matches!(
2133            test_values.update_receiver.try_next(),
2134            Ok(Some(listener::Message::NotifyListeners(_)))
2135        );
2136
2137        // Followed by a starting update.
2138        assert_matches!(
2139            test_values.update_receiver.try_next(),
2140            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
2141            let update = updates.access_points.pop().expect("no new updates available.");
2142            assert_eq!(update.state, types::OperatingState::Starting);
2143        });
2144
2145        // Wait for a start request and send back a timeout.
2146        assert_matches!(
2147            poll_sme_req(&mut exec, &mut sme_fut),
2148            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
2149                responder
2150                    .send(fidl_sme::StartApResultCode::TimedOut)
2151                    .expect("could not send AP stop response");
2152            }
2153        );
2154
2155        // At this point, the state machine should pause before retrying the start request.  It
2156        // should also check to see if there are any incoming AP commands and find the initial exit
2157        // request at which point it should exit.
2158        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
2159        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Ready(Ok(())));
2160
2161        // A metric should be logged for the failure to start the AP.
2162        assert_matches!(
2163            test_values.telemetry_receiver.try_next(),
2164            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
2165        );
2166
2167        // A defect should be sent as well.
2168        assert_matches!(
2169            test_values.defect_receiver.try_next(),
2170            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
2171        );
2172    }
2173
2174    #[fuchsia::test]
2175    fn test_manual_start_causes_starting_notification() {
2176        let mut exec = fasync::TestExecutor::new();
2177        let mut test_values = test_setup();
2178
2179        // Create a start request and enter the state machine with a manual start request.
2180        let radio_config = new_radio_config();
2181        let requested_config = ApConfig {
2182            id: create_network_id(),
2183            credential: vec![],
2184            radio_config,
2185            mode: types::ConnectivityMode::Unrestricted,
2186            band: types::OperatingBand::Any,
2187        };
2188
2189        let (start_response_sender, _) = oneshot::channel();
2190        let manual_request = ManualRequest::Start((requested_config, start_response_sender));
2191
2192        let fut = perform_manual_request(test_values.deps, Some(manual_request));
2193        let fut = run_state_machine(async move { fut });
2194        let mut fut = pin!(fut);
2195        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2196
2197        // We should get a stop request
2198        let sme_fut = test_values.sme_req_stream.into_future();
2199        let mut sme_fut = pin!(sme_fut);
2200
2201        assert_matches!(
2202            poll_sme_req(&mut exec, &mut sme_fut),
2203            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2204                responder
2205                    .send(fidl_sme::StopApResultCode::Success)
2206                    .expect("could not send SME stop response");
2207            }
2208        );
2209
2210        // We should then get a notification that the AP is inactive followed by a new starting
2211        // notification.
2212        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2213        assert_matches!(
2214            test_values.update_receiver.try_next(),
2215            Ok(Some(listener::Message::NotifyListeners(updates))) => {
2216                assert!(updates.access_points.is_empty());
2217        });
2218
2219        assert_matches!(
2220            test_values.update_receiver.try_next(),
2221            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
2222            let update = updates.access_points.pop().expect("no new updates available.");
2223            assert_eq!(update.state, types::OperatingState::Starting);
2224        });
2225    }
2226
2227    #[fuchsia::test]
2228    fn test_serve_does_not_terminate_right_away() {
2229        let mut exec = fasync::TestExecutor::new();
2230        let test_values = test_setup();
2231        let sme_event_stream = test_values.deps.proxy.take_event_stream();
2232        let sme_fut = test_values.sme_req_stream.into_future();
2233        let mut sme_fut = pin!(sme_fut);
2234
2235        let update_sender = test_values.deps.state_tracker.inner.lock().sender.clone();
2236
2237        let fut = serve(
2238            0,
2239            test_values.deps.proxy,
2240            sme_event_stream,
2241            test_values.deps.req_stream,
2242            update_sender,
2243            test_values.deps.telemetry_sender,
2244            test_values.deps.defect_sender,
2245            test_values.deps.status_publisher,
2246        );
2247        let mut fut = pin!(fut);
2248
2249        // Run the state machine. No request is made initially.
2250        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2251        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
2252    }
2253
2254    #[fuchsia::test]
2255    fn test_no_notification_when_sme_fails_while_stopped() {
2256        let mut exec = fasync::TestExecutor::new();
2257        let test_values = test_setup();
2258        let sme_event_stream = test_values.deps.proxy.take_event_stream();
2259        let update_sender = test_values.deps.state_tracker.inner.lock().sender.clone();
2260
2261        // Set the initial state to Starting to verify that it is changed on exit.
2262        test_values.deps.status_publisher.publish_status(Status::Starting);
2263
2264        let fut = serve(
2265            0,
2266            test_values.deps.proxy,
2267            sme_event_stream,
2268            test_values.deps.req_stream,
2269            update_sender,
2270            test_values.deps.telemetry_sender,
2271            test_values.deps.defect_sender,
2272            test_values.deps.status_publisher.clone(),
2273        );
2274        let mut fut = pin!(fut);
2275
2276        // Cause the SME event stream to terminate.
2277        drop(test_values.sme_req_stream);
2278
2279        // Run the state machine and observe that it has terminated.
2280        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
2281
2282        // There should be no notification of failure since no AP is actively running.
2283        assert_matches!(
2284            exec.run_until_stalled(&mut test_values.update_receiver.into_future()),
2285            Poll::Pending
2286        );
2287
2288        // Verify that the state has been set to stopped on exit.
2289        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Stopped));
2290    }
2291
2292    #[fuchsia::test]
2293    fn test_failure_notification_when_configured() {
2294        let mut exec = fasync::TestExecutor::new();
2295        let mut test_values = test_setup();
2296        let sme_event_stream = test_values.deps.proxy.take_event_stream();
2297        let mut sme_fut = Box::pin(test_values.sme_req_stream.into_future());
2298
2299        let update_sender = test_values.deps.state_tracker.inner.lock().sender.clone();
2300        let fut = serve(
2301            0,
2302            test_values.deps.proxy,
2303            sme_event_stream,
2304            test_values.deps.req_stream,
2305            update_sender,
2306            test_values.deps.telemetry_sender,
2307            test_values.deps.defect_sender,
2308            test_values.deps.status_publisher,
2309        );
2310        let mut fut = pin!(fut);
2311
2312        // Make a request to start the access point.
2313        let mut ap = AccessPoint::new(test_values.ap_req_sender);
2314        let (sender, _receiver) = oneshot::channel();
2315        let radio_config = new_radio_config();
2316        let config = ApConfig {
2317            id: create_network_id(),
2318            credential: vec![],
2319            radio_config,
2320            mode: types::ConnectivityMode::Unrestricted,
2321            band: types::OperatingBand::Any,
2322        };
2323        ap.start(config, sender).expect("failed to make start request");
2324
2325        // Expect that the state machine issues a stop request followed by a start request.
2326        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2327        assert_matches!(
2328            poll_sme_req(&mut exec, &mut sme_fut),
2329            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2330                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
2331            }
2332        );
2333
2334        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2335
2336        // At this point, the state machine will have sent an empty notification and a starting
2337        // notification.
2338        assert_matches!(
2339            test_values.update_receiver.try_next(),
2340            Ok(Some(listener::Message::NotifyListeners(update))) => {
2341                assert!(update.access_points.is_empty());
2342            }
2343        );
2344        assert_matches!(
2345            test_values.update_receiver.try_next(),
2346            Ok(Some(listener::Message::NotifyListeners(update))) => {
2347                assert_eq!(update.access_points.len(), 1);
2348                assert_eq!(update.access_points[0].state, types::OperatingState::Starting);
2349            }
2350        );
2351
2352        // Cause the SME event stream to terminate.
2353        drop(sme_fut);
2354
2355        // Run the state machine and observe that it has terminated.
2356        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
2357
2358        // There should be a failure notification.
2359        assert_matches!(
2360            test_values.update_receiver.try_next(),
2361            Ok(Some(listener::Message::NotifyListeners(update))) => {
2362                assert_eq!(update.access_points.len(), 1);
2363                assert_eq!(update.access_points[0].state, types::OperatingState::Failed);
2364            }
2365        );
2366    }
2367
2368    #[fuchsia::test]
2369    fn test_state_tracker_reset() {
2370        let _exec = fasync::TestExecutor::new();
2371        let (sender, mut receiver) = mpsc::unbounded();
2372
2373        // A new state tracker should initially have no state.
2374        let state = ApStateTracker::new(sender);
2375        {
2376            assert!(state.inner.lock().state.is_none());
2377        }
2378
2379        // And there should be no updates.
2380        assert_matches!(receiver.try_next(), Err(_));
2381
2382        // Reset the state to starting and verify that the internal state has been updated.
2383        let new_state = ApStateUpdate::new(
2384            create_network_id(),
2385            types::OperatingState::Starting,
2386            types::ConnectivityMode::Unrestricted,
2387            types::OperatingBand::Any,
2388        );
2389        state.reset_state(new_state).expect("failed to reset state");
2390        assert_matches!(state.inner.lock().state.as_ref(), Some(ApStateUpdate {
2391                id: types::NetworkIdentifier {
2392                    ssid,
2393                    security_type: types::SecurityType::None,
2394                },
2395                state: types::OperatingState::Starting,
2396                mode: Some(types::ConnectivityMode::Unrestricted),
2397                band: Some(types::OperatingBand::Any),
2398                frequency: None,
2399                clients: None,
2400        }) => {
2401            let expected_ssid = types::Ssid::try_from("test_ssid").unwrap();
2402            assert_eq!(ssid, &expected_ssid);
2403        });
2404
2405        // Resetting the state should result in an update.
2406        assert_matches!(
2407            receiver.try_next(),
2408            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))) => {
2409            assert_eq!(access_points.len(), 1);
2410
2411            let expected_id = types::NetworkIdentifier {
2412                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2413                security_type: types::SecurityType::None,
2414            };
2415            assert_eq!(access_points[0].id, expected_id);
2416            assert_eq!(access_points[0].state, types::OperatingState::Starting);
2417            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2418            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2419            assert_eq!(access_points[0].frequency, None);
2420            assert_eq!(access_points[0].clients, None);
2421            }
2422        );
2423    }
2424
2425    #[fuchsia::test]
2426    fn test_state_tracker_consume_sme_update() {
2427        let _exec = fasync::TestExecutor::new();
2428        let (sender, mut receiver) = mpsc::unbounded();
2429        let state = ApStateTracker::new(sender);
2430
2431        // Reset the state to started and send an update.
2432        let new_state = ApStateUpdate::new(
2433            create_network_id(),
2434            types::OperatingState::Active,
2435            types::ConnectivityMode::Unrestricted,
2436            types::OperatingBand::Any,
2437        );
2438        state.reset_state(new_state).expect("failed to reset state");
2439
2440        // The update should note that the AP is active.
2441        assert_matches!(
2442            receiver.try_next(),
2443            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2444        ) => {
2445            assert_eq!(access_points.len(), 1);
2446
2447            let expected_id = types::NetworkIdentifier {
2448                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2449                security_type: types::SecurityType::None,
2450            };
2451            assert_eq!(access_points[0].id, expected_id);
2452            assert_eq!(access_points[0].state, types::OperatingState::Active);
2453            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2454            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2455            assert_eq!(access_points[0].frequency, None);
2456            assert_eq!(access_points[0].clients, None);
2457        });
2458
2459        // Consume a status update and expect a new notification to be generated.
2460        let ap_info = fidl_sme::Ap {
2461            ssid: types::Ssid::try_from("test_ssid").unwrap().to_vec(),
2462            channel: 6,
2463            num_clients: 123,
2464        };
2465        state
2466            .consume_sme_status_update(
2467                Cbw::Cbw20,
2468                fidl_fuchsia_wlan_ieee80211::WlanBand::TwoGhz,
2469                ap_info,
2470            )
2471            .expect("failure while updating SME status");
2472
2473        assert_matches!(
2474            receiver.try_next(),
2475            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2476        ) => {
2477            assert_eq!(access_points.len(), 1);
2478
2479            let expected_id = types::NetworkIdentifier {
2480                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2481                security_type: types::SecurityType::None,
2482            };
2483            assert_eq!(access_points[0].id, expected_id);
2484            assert_eq!(access_points[0].state, types::OperatingState::Active);
2485            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2486            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2487            assert_eq!(access_points[0].frequency, Some(2437));
2488            assert_eq!(access_points[0].clients, Some(ConnectedClientInformation { count: 123 }));
2489        });
2490    }
2491
2492    #[fuchsia::test]
2493    fn test_state_tracker_update_operating_state() {
2494        let _exec = fasync::TestExecutor::new();
2495        let (sender, mut receiver) = mpsc::unbounded();
2496        let state = ApStateTracker::new(sender);
2497
2498        // Reset the state to started and send an update.
2499        let new_state = ApStateUpdate::new(
2500            create_network_id(),
2501            types::OperatingState::Starting,
2502            types::ConnectivityMode::Unrestricted,
2503            types::OperatingBand::Any,
2504        );
2505        state.reset_state(new_state).expect("failed to reset state");
2506
2507        // The update should note that the AP is starting.
2508        assert_matches!(
2509            receiver.try_next(),
2510            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2511        ) => {
2512            assert_eq!(access_points.len(), 1);
2513
2514            let expected_id = types::NetworkIdentifier {
2515                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2516                security_type: types::SecurityType::None,
2517            };
2518            assert_eq!(access_points[0].id, expected_id);
2519            assert_eq!(access_points[0].state, types::OperatingState::Starting);
2520            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2521            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2522            assert_eq!(access_points[0].frequency, None);
2523            assert_eq!(access_points[0].clients, None);
2524        });
2525
2526        // Give another update that the state is starting and ensure that a notification is sent.
2527        state
2528            .update_operating_state(types::OperatingState::Starting)
2529            .expect("failed to send duplicate update.");
2530        assert_matches!(
2531            receiver.try_next(),
2532            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2533        ) => {
2534            assert_eq!(access_points.len(), 1);
2535
2536            let expected_id = types::NetworkIdentifier {
2537                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2538                security_type: types::SecurityType::None,
2539            };
2540            assert_eq!(access_points[0].id, expected_id);
2541            assert_eq!(access_points[0].state, types::OperatingState::Starting);
2542            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2543            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2544            assert_eq!(access_points[0].frequency, None);
2545            assert_eq!(access_points[0].clients, None);
2546        });
2547
2548        // Now update that the state is active and expect a notification to be generated.
2549        state
2550            .update_operating_state(types::OperatingState::Active)
2551            .expect("failed to send active update.");
2552        assert_matches!(
2553            receiver.try_next(),
2554            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2555        ) => {
2556            assert_eq!(access_points.len(), 1);
2557
2558            let expected_id = types::NetworkIdentifier {
2559                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2560                security_type: types::SecurityType::None,
2561            };
2562            assert_eq!(access_points[0].id, expected_id);
2563            assert_eq!(access_points[0].state, types::OperatingState::Active);
2564            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2565            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2566            assert_eq!(access_points[0].frequency, None);
2567            assert_eq!(access_points[0].clients, None);
2568        });
2569    }
2570
2571    #[fuchsia::test]
2572    fn test_state_tracker_set_stopped_state() {
2573        let _exec = fasync::TestExecutor::new();
2574        let (sender, mut receiver) = mpsc::unbounded();
2575        let state = ApStateTracker::new(sender);
2576
2577        // Set up some initial state.
2578        {
2579            let new_state = ApStateUpdate::new(
2580                create_network_id(),
2581                types::OperatingState::Active,
2582                types::ConnectivityMode::Unrestricted,
2583                types::OperatingBand::Any,
2584            );
2585            state.inner.lock().state = Some(new_state);
2586        }
2587
2588        // Set the state to stopped and verify that the internal state information has been
2589        // removed.
2590        state.set_stopped_state().expect("failed to send stopped notification");
2591        {
2592            assert!(state.inner.lock().state.is_none());
2593        }
2594
2595        // Verify that an empty update has arrived.
2596        assert_matches!(
2597            receiver.try_next(),
2598            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2599        ) => {
2600            assert!(access_points.is_empty());
2601        });
2602    }
2603
2604    #[fuchsia::test]
2605    fn test_state_tracker_failure_modes() {
2606        let _exec = fasync::TestExecutor::new();
2607        let (sender, receiver) = mpsc::unbounded();
2608        let state = ApStateTracker::new(sender);
2609        {
2610            let new_state = ApStateUpdate::new(
2611                create_network_id(),
2612                types::OperatingState::Active,
2613                types::ConnectivityMode::Unrestricted,
2614                types::OperatingBand::Any,
2615            );
2616            state.inner.lock().state = Some(new_state);
2617        }
2618
2619        // Currently, the only reason any of the state tracker methods might fail is because of a
2620        // failure to enqueue a state change notification.  Drop the receiving end to trigger this
2621        // condition.
2622        drop(receiver);
2623
2624        let _ = state
2625            .update_operating_state(types::OperatingState::Failed)
2626            .expect_err("unexpectedly able to set operating state");
2627        let _ = state
2628            .consume_sme_status_update(
2629                Cbw::Cbw20,
2630                fidl_fuchsia_wlan_ieee80211::WlanBand::TwoGhz,
2631                fidl_sme::Ap {
2632                    ssid: types::Ssid::try_from("test_ssid").unwrap().to_vec(),
2633                    channel: 6,
2634                    num_clients: 123,
2635                },
2636            )
2637            .expect_err("unexpectedly able to update SME status");
2638        let _ = state.set_stopped_state().expect_err("unexpectedly able to set stopped state");
2639    }
2640
2641    #[fuchsia::test]
2642    fn test_state_when_stopping() {
2643        let mut exec = fasync::TestExecutor::new();
2644        let test_values = test_setup();
2645
2646        // Run the stopping state.
2647        let (stop_sender, _) = oneshot::channel();
2648        let fut = stopping_state(test_values.deps, stop_sender);
2649        let fut = run_state_machine(fut);
2650        let mut fut = pin!(fut);
2651        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2652
2653        // Verify that the state has been set to Stopping.
2654        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Stopping));
2655    }
2656
2657    #[fuchsia::test]
2658    fn test_state_when_stopped() {
2659        let mut exec = fasync::TestExecutor::new();
2660        let test_values = test_setup();
2661
2662        // Set the initial state to Starting to verify that it is changed in the stopped state.
2663        test_values.deps.status_publisher.publish_status(Status::Starting);
2664
2665        // Run the stopping state.
2666        let fut = stopped_state(test_values.deps);
2667        let fut = run_state_machine(fut);
2668        let mut fut = pin!(fut);
2669        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2670
2671        // Verify that the state has been set to Stopped.
2672        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Stopped));
2673    }
2674
2675    #[fuchsia::test]
2676    fn test_state_when_starting() {
2677        let mut exec = fasync::TestExecutor::new();
2678        let test_values = test_setup();
2679
2680        // Run the starting state.
2681        let (start_sender, _) = oneshot::channel();
2682        let radio_config = new_radio_config();
2683        let req = ApConfig {
2684            id: create_network_id(),
2685            credential: vec![],
2686            radio_config,
2687            mode: types::ConnectivityMode::Unrestricted,
2688            band: types::OperatingBand::Any,
2689        };
2690        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
2691        let fut = run_state_machine(fut);
2692        let mut fut = pin!(fut);
2693        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2694
2695        // Verify that the state has been set to Starting.
2696        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Starting));
2697    }
2698
2699    #[fuchsia::test]
2700    fn test_state_when_started() {
2701        let mut exec = fasync::TestExecutor::new();
2702        let test_values = test_setup();
2703
2704        // Run the started state.
2705        let radio_config = new_radio_config();
2706        let req = ApConfig {
2707            id: create_network_id(),
2708            credential: vec![],
2709            radio_config,
2710            mode: types::ConnectivityMode::Unrestricted,
2711            band: types::OperatingBand::Any,
2712        };
2713        let fut = started_state(test_values.deps, req);
2714        let fut = run_state_machine(fut);
2715        let mut fut = pin!(fut);
2716        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2717
2718        // Verify that the state has been set to Started.
2719        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Started { .. }));
2720    }
2721
2722    struct InspectTestValues {
2723        exec: fasync::TestExecutor,
2724        inspector: fuchsia_inspect::Inspector,
2725        _node: fuchsia_inspect::Node,
2726        status_node: fuchsia_inspect_contrib::nodes::BoundedListNode,
2727    }
2728
2729    impl InspectTestValues {
2730        fn new(exec: fasync::TestExecutor) -> Self {
2731            let inspector = fuchsia_inspect::Inspector::default();
2732            let _node = inspector.root().create_child("node");
2733            let status_node =
2734                fuchsia_inspect_contrib::nodes::BoundedListNode::new(_node.clone_weak(), 1);
2735
2736            Self { exec, inspector, _node, status_node }
2737        }
2738
2739        fn log_status(&mut self, status: Status) -> fuchsia_inspect::reader::DiagnosticsHierarchy {
2740            fuchsia_inspect_contrib::inspect_log!(self.status_node, "status" => status);
2741            let read_fut = fuchsia_inspect::reader::read(&self.inspector);
2742            let mut read_fut = pin!(read_fut);
2743            assert_matches!(
2744                self.exec.run_until_stalled(&mut read_fut),
2745                Poll::Ready(Ok(hierarchy)) => hierarchy
2746            )
2747        }
2748    }
2749
2750    #[fuchsia::test]
2751    fn test_stopping_status_inspect_log() {
2752        let exec = fasync::TestExecutor::new_with_fake_time();
2753        let mut test_values = InspectTestValues::new(exec);
2754        let hierarchy = test_values.log_status(Status::Stopping);
2755        diagnostics_assertions::assert_data_tree!(
2756            @executor test_values.exec,
2757            hierarchy,
2758            root: contains {
2759                node: contains {
2760                    "0": contains {
2761                        status: "Stopping"
2762                    }
2763                }
2764        });
2765    }
2766
2767    #[fuchsia::test]
2768    fn test_stopped_status_inspect_log() {
2769        let exec = fasync::TestExecutor::new_with_fake_time();
2770        let mut test_values = InspectTestValues::new(exec);
2771        let hierarchy = test_values.log_status(Status::Stopped);
2772        diagnostics_assertions::assert_data_tree!(
2773            @executor test_values.exec,
2774            hierarchy,
2775            root: contains {
2776                node: contains {
2777                    "0": contains {
2778                        status: "Stopped"
2779                    }
2780                }
2781        });
2782    }
2783
2784    #[fuchsia::test]
2785    fn test_starting_status_inspect_log() {
2786        let exec = fasync::TestExecutor::new_with_fake_time();
2787        let mut test_values = InspectTestValues::new(exec);
2788        let hierarchy = test_values.log_status(Status::Starting);
2789        diagnostics_assertions::assert_data_tree!(
2790            @executor test_values.exec,
2791            hierarchy,
2792            root: contains {
2793                node: contains {
2794                    "0": contains {
2795                        status: "Starting"
2796                    }
2797                }
2798        });
2799    }
2800
2801    #[fuchsia::test]
2802    fn test_started_status_inspect_log() {
2803        let exec = fasync::TestExecutor::new_with_fake_time();
2804        let mut test_values = InspectTestValues::new(exec);
2805        let hierarchy = test_values.log_status(Status::Started {
2806            band: types::OperatingBand::Any,
2807            channel: 1,
2808            mode: types::ConnectivityMode::Unrestricted,
2809            num_clients: 2,
2810            security_type: types::SecurityType::None,
2811        });
2812        diagnostics_assertions::assert_data_tree!(
2813            @executor test_values.exec,
2814            hierarchy,
2815            root: contains {
2816                node: contains {
2817                    "0": contains {
2818                        status: contains {
2819                            Started: {
2820                                band: "Any",
2821                                channel: 1_u64,
2822                                mode: "Unrestricted",
2823                                num_clients: 2_u64,
2824                                security_type: "None"
2825                            }
2826                        }
2827                    }
2828                }
2829        });
2830    }
2831}