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