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::channel::{Cbw, Channel};
32use wlan_common::RadioConfig;
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::{status_publisher_and_reader, StateMachineStatusReader};
629    use assert_matches::assert_matches;
630    use fidl::endpoints::create_proxy;
631    use fidl_fuchsia_wlan_common as fidl_common;
632    use futures::stream::StreamFuture;
633    use futures::task::Poll;
634    use futures::Future;
635    use std::pin::pin;
636
637    struct TestValues {
638        deps: CommonStateDependencies,
639        sme_req_stream: fidl_sme::ApSmeRequestStream,
640        ap_req_sender: mpsc::Sender<ManualRequest>,
641        update_receiver: mpsc::UnboundedReceiver<listener::ApMessage>,
642        telemetry_receiver: mpsc::Receiver<TelemetryEvent>,
643        defect_receiver: mpsc::Receiver<Defect>,
644        status_reader: StateMachineStatusReader<Status>,
645    }
646
647    fn test_setup() -> TestValues {
648        let (ap_req_sender, ap_req_stream) = mpsc::channel(1);
649        let (update_sender, update_receiver) = mpsc::unbounded();
650        let (telemetry_sender, telemetry_receiver) = mpsc::channel(100);
651        let telemetry_sender = TelemetrySender::new(telemetry_sender);
652        let (defect_sender, defect_receiver) = mpsc::channel(100);
653        let (status_publisher, status_reader) = status_publisher_and_reader::<Status>();
654        let (sme_proxy, sme_server) = create_proxy::<fidl_sme::ApSmeMarker>();
655        let sme_req_stream = sme_server.into_stream();
656        let sme_proxy = SmeForApStateMachine::new(sme_proxy, 123, defect_sender.clone());
657
658        let deps = CommonStateDependencies {
659            iface_id: 123,
660            proxy: sme_proxy,
661            req_stream: ap_req_stream.fuse(),
662            state_tracker: Arc::new(ApStateTracker::new(update_sender)),
663            telemetry_sender,
664            defect_sender,
665            status_publisher,
666        };
667
668        TestValues {
669            deps,
670            sme_req_stream,
671            ap_req_sender,
672            update_receiver,
673            telemetry_receiver,
674            defect_receiver,
675            status_reader,
676        }
677    }
678
679    fn create_network_id() -> types::NetworkIdentifier {
680        types::NetworkIdentifier {
681            ssid: types::Ssid::try_from("test_ssid").unwrap(),
682            security_type: types::SecurityType::None,
683        }
684    }
685
686    fn poll_sme_req(
687        exec: &mut fasync::TestExecutor,
688        next_sme_req: &mut StreamFuture<fidl_sme::ApSmeRequestStream>,
689    ) -> Poll<fidl_sme::ApSmeRequest> {
690        exec.run_until_stalled(next_sme_req).map(|(req, stream)| {
691            *next_sme_req = stream.into_future();
692            req.expect("did not expect the SME request stream to end")
693                .expect("error polling SME request stream")
694        })
695    }
696
697    #[allow(clippy::needless_return, reason = "mass allow for https://fxbug.dev/381896734")]
698    async fn run_state_machine(fut: impl Future<Output = Result<State, ExitReason>> + 'static) {
699        let state_machine = fut.into_state_machine();
700        select! {
701            _state_machine = state_machine.fuse() => return,
702        }
703    }
704
705    #[fuchsia::test]
706    fn test_stop_during_started() {
707        let mut exec = fasync::TestExecutor::new();
708        let test_values = test_setup();
709
710        let radio_config = RadioConfig::new(fidl_common::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 = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
774        let req = ApConfig {
775            id: create_network_id(),
776            credential: vec![],
777            radio_config,
778            mode: types::ConnectivityMode::Unrestricted,
779            band: types::OperatingBand::Any,
780        };
781        {
782            let state = ApStateUpdate::new(
783                create_network_id(),
784                types::OperatingState::Starting,
785                types::ConnectivityMode::Unrestricted,
786                types::OperatingBand::Any,
787            );
788            test_values.deps.state_tracker.inner.lock().state = Some(state);
789        }
790
791        // Run the started state and ignore the status request
792        let fut = started_state(test_values.deps, req);
793        let fut = run_state_machine(fut);
794        let mut fut = pin!(fut);
795
796        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
797
798        let sme_fut = test_values.sme_req_stream.into_future();
799        let mut sme_fut = pin!(sme_fut);
800
801        assert_matches!(
802            poll_sme_req(&mut exec, &mut sme_fut),
803            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
804                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 0, num_clients: 0 };
805                let response = fidl_sme::ApStatusResponse {
806                    running_ap: Some(Box::new(ap_info))
807                };
808                responder.send(&response).expect("could not send AP status response");
809            }
810        );
811
812        // Issue an exit request.
813        let mut ap = AccessPoint::new(test_values.ap_req_sender);
814        let (sender, mut receiver) = oneshot::channel();
815        ap.exit(sender).expect("failed to make stop request");
816
817        // Expect the responder to be acknowledged and the state machine to exit.
818        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
819        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
820    }
821
822    #[fuchsia::test]
823    fn test_start_during_started() {
824        let mut exec = fasync::TestExecutor::new();
825        let test_values = test_setup();
826
827        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
828        let req = ApConfig {
829            id: create_network_id(),
830            credential: vec![],
831            radio_config,
832            mode: types::ConnectivityMode::Unrestricted,
833            band: types::OperatingBand::Any,
834        };
835        {
836            let state = ApStateUpdate::new(
837                create_network_id(),
838                types::OperatingState::Starting,
839                types::ConnectivityMode::Unrestricted,
840                types::OperatingBand::Any,
841            );
842            test_values.deps.state_tracker.inner.lock().state = Some(state);
843        }
844
845        // Run the started state and ignore the status request
846        let fut = started_state(test_values.deps, req);
847        let fut = run_state_machine(fut);
848        let mut fut = pin!(fut);
849
850        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
851
852        let sme_fut = test_values.sme_req_stream.into_future();
853        let mut sme_fut = pin!(sme_fut);
854
855        assert_matches!(
856            poll_sme_req(&mut exec, &mut sme_fut),
857            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
858                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 0, num_clients: 0 };
859                let response = fidl_sme::ApStatusResponse {
860                    running_ap: Some(Box::new(ap_info))
861                };
862                responder.send(&response).expect("could not send AP status response");
863            }
864        );
865
866        // Issue a start request.
867        let mut ap = AccessPoint::new(test_values.ap_req_sender);
868        let (sender, mut receiver) = oneshot::channel();
869        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
870        let req = ApConfig {
871            id: create_network_id(),
872            credential: vec![],
873            radio_config,
874            mode: types::ConnectivityMode::Unrestricted,
875            band: types::OperatingBand::Any,
876        };
877        ap.start(req, sender).expect("failed to make stop request");
878
879        // Expect that the state machine issues a stop request followed by a start request.
880        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
881        assert_matches!(
882            poll_sme_req(&mut exec, &mut sme_fut),
883            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
884                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
885            }
886        );
887
888        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
889        assert_matches!(
890            poll_sme_req(&mut exec, &mut sme_fut),
891            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
892                responder
893                    .send(fidl_sme::StartApResultCode::Success)
894                    .expect("could not send AP stop response");
895            }
896        );
897
898        // Verify that the SME response is plumbed back to the caller.
899        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
900        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
901    }
902
903    #[fuchsia::test]
904    fn test_duplicate_status_during_started() {
905        let mut exec = fasync::TestExecutor::new();
906        let test_values = test_setup();
907
908        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
909        let req = ApConfig {
910            id: create_network_id(),
911            credential: vec![],
912            radio_config,
913            mode: types::ConnectivityMode::Unrestricted,
914            band: types::OperatingBand::Any,
915        };
916        {
917            let mut state = ApStateUpdate::new(
918                create_network_id(),
919                types::OperatingState::Starting,
920                types::ConnectivityMode::Unrestricted,
921                types::OperatingBand::Any,
922            );
923            state.frequency = Some(2437);
924            state.clients = Some(ConnectedClientInformation { count: 0 });
925            test_values.deps.state_tracker.inner.lock().state = Some(state);
926        }
927
928        // Run the started state and send back an identical status.
929        let fut = started_state(test_values.deps, req);
930        let fut = run_state_machine(fut);
931        let mut fut = pin!(fut);
932
933        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
934
935        let sme_fut = test_values.sme_req_stream.into_future();
936        let mut sme_fut = pin!(sme_fut);
937
938        assert_matches!(
939            poll_sme_req(&mut exec, &mut sme_fut),
940            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
941                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 6, num_clients: 0 };
942                let response = fidl_sme::ApStatusResponse {
943                    running_ap: Some(Box::new(ap_info))
944                };
945                responder.send(&response).expect("could not send AP status response");
946            }
947        );
948
949        // Run the state machine and ensure no update has been sent.
950        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
951        assert_matches!(
952            exec.run_until_stalled(&mut test_values.update_receiver.into_future()),
953            Poll::Pending
954        );
955    }
956
957    #[fuchsia::test]
958    fn test_new_status_during_started() {
959        let mut exec = fasync::TestExecutor::new();
960        let test_values = test_setup();
961
962        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
963        let req = ApConfig {
964            id: create_network_id(),
965            credential: vec![],
966            radio_config,
967            mode: types::ConnectivityMode::Unrestricted,
968            band: types::OperatingBand::Any,
969        };
970        {
971            let mut state = ApStateUpdate::new(
972                create_network_id(),
973                types::OperatingState::Starting,
974                types::ConnectivityMode::Unrestricted,
975                types::OperatingBand::Any,
976            );
977            state.frequency = Some(0);
978            state.clients = Some(ConnectedClientInformation { count: 0 });
979            test_values.deps.state_tracker.inner.lock().state = Some(state);
980        }
981
982        // Run the started state.
983        let fut = started_state(test_values.deps, req);
984        let fut = run_state_machine(fut);
985        let mut fut = pin!(fut);
986
987        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
988
989        // Verify the initial status report.
990        assert_matches!(
991            test_values.status_reader.read_status(),
992            Ok(Status::Started {
993                band: types::OperatingBand::Any,
994                channel: 6,
995                mode: types::ConnectivityMode::Unrestricted,
996                num_clients: 0,
997                security_type: types::SecurityType::None,
998            })
999        );
1000
1001        // Send an SME status update.
1002        let sme_fut = test_values.sme_req_stream.into_future();
1003        let mut sme_fut = pin!(sme_fut);
1004
1005        assert_matches!(
1006            poll_sme_req(&mut exec, &mut sme_fut),
1007            Poll::Ready(fidl_sme::ApSmeRequest::Status{ responder }) => {
1008                let ap_info = fidl_sme::Ap { ssid: vec![], channel: 1, num_clients: 1 };
1009                let response = fidl_sme::ApStatusResponse {
1010                    running_ap: Some(Box::new(ap_info))
1011                };
1012                responder.send(&response).expect("could not send AP status response");
1013            }
1014        );
1015
1016        // Run the state machine and ensure an update has been sent.
1017        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1018        assert_matches!(
1019            exec.run_until_stalled(&mut test_values.update_receiver.into_future()),
1020            Poll::Ready((Some(listener::Message::NotifyListeners(updates)), _)) => {
1021                assert!(!updates.access_points.is_empty());
1022        });
1023
1024        // Verify that the status has been updated.
1025        assert_matches!(
1026            test_values.status_reader.read_status(),
1027            Ok(Status::Started {
1028                band: types::OperatingBand::Any,
1029                channel: 1,
1030                mode: types::ConnectivityMode::Unrestricted,
1031                num_clients: 1,
1032                security_type: types::SecurityType::None,
1033            })
1034        );
1035    }
1036
1037    #[fuchsia::test]
1038    fn test_sme_failure_during_started() {
1039        let mut exec = fasync::TestExecutor::new();
1040        let mut test_values = test_setup();
1041
1042        // Drop the serving side of the SME so that a status request will result in an error.
1043        drop(test_values.sme_req_stream);
1044
1045        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1046        let req = ApConfig {
1047            id: create_network_id(),
1048            credential: vec![],
1049            radio_config,
1050            mode: types::ConnectivityMode::Unrestricted,
1051            band: types::OperatingBand::Any,
1052        };
1053        {
1054            let mut state = ApStateUpdate::new(
1055                create_network_id(),
1056                types::OperatingState::Starting,
1057                types::ConnectivityMode::Unrestricted,
1058                types::OperatingBand::Any,
1059            );
1060            state.frequency = Some(0);
1061            state.clients = Some(ConnectedClientInformation { count: 0 });
1062            test_values.deps.state_tracker.inner.lock().state = Some(state);
1063        }
1064
1065        // Run the started state and send back an identical status.
1066        let fut = started_state(test_values.deps, req);
1067        let fut = run_state_machine(fut);
1068        let mut fut = pin!(fut);
1069
1070        // The state machine should exit when it is unable to query status.
1071        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1072
1073        // Verify that a failure notification is send to listeners.
1074        assert_matches!(
1075            test_values.update_receiver.try_next(),
1076            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1077            let update = updates.access_points.pop().expect("no new updates available.");
1078            assert_eq!(update.state, types::OperatingState::Failed);
1079        });
1080    }
1081
1082    #[fuchsia::test]
1083    fn test_stop_while_stopped() {
1084        let mut exec = fasync::TestExecutor::new();
1085        let test_values = test_setup();
1086
1087        // Run the stopped state.
1088        let fut = stopped_state(test_values.deps);
1089        let fut = run_state_machine(fut);
1090        let mut fut = pin!(fut);
1091
1092        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1093
1094        // Issue a stop request.
1095        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1096        let (sender, mut receiver) = oneshot::channel();
1097        ap.stop(sender).expect("failed to make stop request");
1098
1099        // Expect the responder to be acknowledged immediately.
1100        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1101        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
1102    }
1103
1104    #[fuchsia::test]
1105    fn test_exit_while_stopped() {
1106        let mut exec = fasync::TestExecutor::new();
1107        let test_values = test_setup();
1108
1109        // Run the stopped state.
1110        let fut = stopped_state(test_values.deps);
1111        let fut = run_state_machine(fut);
1112        let mut fut = pin!(fut);
1113
1114        // Issue an exit request.
1115        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1116        let (sender, mut receiver) = oneshot::channel();
1117        ap.exit(sender).expect("failed to make stop request");
1118
1119        // Expect the responder to be acknowledged and the state machine to exit.
1120        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1121        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
1122    }
1123
1124    #[fuchsia::test]
1125    fn test_start_while_stopped() {
1126        let mut exec = fasync::TestExecutor::new();
1127        let mut test_values = test_setup();
1128
1129        // Run the stopped state.
1130        let fut = stopped_state(test_values.deps);
1131        let fut = run_state_machine(fut);
1132        let mut fut = pin!(fut);
1133
1134        // Issue a start request.
1135        let (sender, mut receiver) = oneshot::channel();
1136        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1137        let req = ApConfig {
1138            id: create_network_id(),
1139            credential: vec![],
1140            radio_config,
1141            mode: types::ConnectivityMode::Unrestricted,
1142            band: types::OperatingBand::Any,
1143        };
1144
1145        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1146        ap.start(req, sender).expect("failed to make stop request");
1147
1148        // Expect that the state machine issues a stop request.
1149        let sme_fut = test_values.sme_req_stream.into_future();
1150        let mut sme_fut = pin!(sme_fut);
1151
1152        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1153        assert_matches!(
1154            poll_sme_req(&mut exec, &mut sme_fut),
1155            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1156                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1157            }
1158        );
1159
1160        // An empty update should be sent after stopping.
1161        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1162        assert_matches!(
1163            test_values.update_receiver.try_next(),
1164            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1165            assert!(updates.access_points.is_empty());
1166        });
1167
1168        // The empty update should be quickly followed by a starting update.
1169        assert_matches!(
1170            test_values.update_receiver.try_next(),
1171            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1172            let update = updates.access_points.pop().expect("no new updates available.");
1173            assert_eq!(update.state, types::OperatingState::Starting);
1174        });
1175
1176        // A start request should have been issues to the SME proxy.
1177        assert_matches!(
1178            poll_sme_req(&mut exec, &mut sme_fut),
1179            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1180                responder
1181                    .send(fidl_sme::StartApResultCode::Success)
1182                    .expect("could not send AP stop response");
1183            }
1184        );
1185
1186        // Verify that the SME response is plumbed back to the caller.
1187        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1188        assert_matches!(exec.run_until_stalled(&mut receiver), Poll::Ready(Ok(())));
1189
1190        // There should be a pending active state notification
1191        assert_matches!(
1192            test_values.update_receiver.try_next(),
1193            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1194            let update = updates.access_points.pop().expect("no new updates available.");
1195            assert_eq!(update.state, types::OperatingState::Active);
1196        });
1197    }
1198
1199    #[fuchsia::test]
1200    fn test_exit_while_stopping() {
1201        let mut exec = fasync::TestExecutor::new();
1202        let test_values = test_setup();
1203
1204        // Run the stopping state.
1205        let (stop_sender, mut stop_receiver) = oneshot::channel();
1206        let fut = stopping_state(test_values.deps, stop_sender);
1207        let fut = run_state_machine(fut);
1208        let mut fut = pin!(fut);
1209
1210        // Issue an exit request.
1211        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1212        let (exit_sender, mut exit_receiver) = oneshot::channel();
1213        ap.exit(exit_sender).expect("failed to make stop request");
1214
1215        // While stopping is still in progress, exit and stop should not be responded to yet.
1216        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1217        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Pending);
1218        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Pending);
1219
1220        // Once stop AP request is finished, the state machine can terminate
1221        let sme_fut = test_values.sme_req_stream.into_future();
1222        let mut sme_fut = pin!(sme_fut);
1223        assert_matches!(
1224            poll_sme_req(&mut exec, &mut sme_fut),
1225            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1226                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1227            }
1228        );
1229        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1230        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1231        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Ready(Ok(())));
1232    }
1233
1234    #[fuchsia::test]
1235    fn test_stop_while_stopping() {
1236        let mut exec = fasync::TestExecutor::new();
1237        let mut test_values = test_setup();
1238
1239        // Run the stopping state.
1240        let (stop_sender, mut stop_receiver) = oneshot::channel();
1241        let fut = stopping_state(test_values.deps, stop_sender);
1242        let fut = run_state_machine(fut);
1243        let mut fut = pin!(fut);
1244
1245        // Verify that no state update is ready yet.
1246        assert_matches!(&mut test_values.update_receiver.try_next(), Err(_));
1247
1248        // Issue a stop request.
1249        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1250        let (second_stop_sender, mut second_stop_receiver) = oneshot::channel();
1251        ap.stop(second_stop_sender).expect("failed to make stop request");
1252
1253        // Expect the stop request from the SME proxy
1254        let sme_fut = test_values.sme_req_stream.into_future();
1255        let mut sme_fut = pin!(sme_fut);
1256
1257        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1258        assert_matches!(
1259            poll_sme_req(&mut exec, &mut sme_fut),
1260            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1261                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1262            }
1263        );
1264
1265        // Expect both responders to be acknowledged.
1266        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1267        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1268        assert_matches!(exec.run_until_stalled(&mut second_stop_receiver), Poll::Ready(Ok(())));
1269
1270        // There should be a new update indicating that no AP's are active.
1271        assert_matches!(
1272            test_values.update_receiver.try_next(),
1273            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1274            assert!(updates.access_points.is_empty());
1275        });
1276    }
1277
1278    #[fuchsia::test]
1279    fn test_start_while_stopping() {
1280        let mut exec = fasync::TestExecutor::new();
1281        let test_values = test_setup();
1282
1283        // Run the stopping state.
1284        let (stop_sender, mut stop_receiver) = oneshot::channel();
1285        let fut = stopping_state(test_values.deps, stop_sender);
1286        let fut = run_state_machine(fut);
1287        let mut fut = pin!(fut);
1288
1289        // Issue a start request.
1290        let (start_sender, mut start_receiver) = oneshot::channel();
1291        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1292        let req = ApConfig {
1293            id: create_network_id(),
1294            credential: vec![],
1295            radio_config,
1296            mode: types::ConnectivityMode::Unrestricted,
1297            band: types::OperatingBand::Any,
1298        };
1299
1300        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1301        ap.start(req, start_sender).expect("failed to make stop request");
1302
1303        // The state machine should not respond to the stop request yet until it's finished.
1304        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1305        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Pending);
1306        let sme_fut = test_values.sme_req_stream.into_future();
1307        let mut sme_fut = pin!(sme_fut);
1308        let stop_responder = assert_matches!(
1309            poll_sme_req(&mut exec, &mut sme_fut),
1310            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => responder
1311        );
1312
1313        // The state machine should not send new request yet since stop is still unfinished
1314        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
1315
1316        // After SME sends response, the state machine can proceed
1317        stop_responder
1318            .send(fidl_sme::StopApResultCode::Success)
1319            .expect("could not send AP stop response");
1320        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1321        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1322
1323        // Expect another stop request from the state machine entering the starting state.
1324        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1325        assert_matches!(
1326            poll_sme_req(&mut exec, &mut sme_fut),
1327            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1328                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1329            }
1330        );
1331
1332        // Expect a start request
1333        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1334        assert_matches!(
1335            poll_sme_req(&mut exec, &mut sme_fut),
1336            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1337                responder
1338                    .send(fidl_sme::StartApResultCode::Success)
1339                    .expect("could not send AP stop response");
1340            }
1341        );
1342
1343        // Expect the start responder to be acknowledged
1344        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1345        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1346    }
1347
1348    #[fuchsia::test]
1349    fn test_sme_failure_while_stopping() {
1350        let mut exec = fasync::TestExecutor::new();
1351        let mut test_values = test_setup();
1352
1353        // Drop the serving side of the SME so that the stop request will result in an error.
1354        drop(test_values.sme_req_stream);
1355
1356        // Run the stopping state.
1357        let (stop_sender, mut stop_receiver) = oneshot::channel();
1358        let fut = stopping_state(test_values.deps, stop_sender);
1359        let fut = run_state_machine(fut);
1360        let mut fut = pin!(fut);
1361
1362        // The state machine should exit when it is unable to issue the stop command.
1363        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1364        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Err(_)));
1365
1366        // There should be a new update indicating that no AP's are active.
1367        assert_matches!(
1368            test_values.update_receiver.try_next(),
1369            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1370            assert!(updates.access_points.is_empty());
1371        });
1372    }
1373
1374    #[fuchsia::test]
1375    fn test_failed_result_code_while_stopping() {
1376        let mut exec = fasync::TestExecutor::new();
1377        let mut test_values = test_setup();
1378
1379        // Run the stopping state.
1380        let (stop_sender, mut stop_receiver) = oneshot::channel();
1381        let fut = stopping_state(test_values.deps, stop_sender);
1382        let fut = run_state_machine(fut);
1383        let mut fut = pin!(fut);
1384
1385        // Verify that no state update is ready yet.
1386        assert_matches!(&mut test_values.update_receiver.try_next(), Err(_));
1387
1388        // Expect the stop request from the SME proxy
1389        let sme_fut = test_values.sme_req_stream.into_future();
1390        let mut sme_fut = pin!(sme_fut);
1391
1392        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1393        assert_matches!(
1394            poll_sme_req(&mut exec, &mut sme_fut),
1395            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1396                responder.send(fidl_sme::StopApResultCode::InternalError).expect("could not send AP stop response");
1397            }
1398        );
1399
1400        // The state machine should exit.
1401        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1402        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Err(_)));
1403
1404        // There should be a new update indicating that no AP's are active.
1405        assert_matches!(
1406            test_values.update_receiver.try_next(),
1407            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1408            assert!(updates.access_points.is_empty());
1409        });
1410    }
1411
1412    #[fuchsia::test]
1413    fn test_stop_while_starting() {
1414        let mut exec = fasync::TestExecutor::new();
1415        let mut test_values = test_setup();
1416
1417        let (start_sender, mut start_receiver) = oneshot::channel();
1418        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1419        let req = ApConfig {
1420            id: create_network_id(),
1421            credential: vec![],
1422            radio_config,
1423            mode: types::ConnectivityMode::Unrestricted,
1424            band: types::OperatingBand::Any,
1425        };
1426
1427        // Start off in the starting state
1428        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1429        let fut = run_state_machine(fut);
1430        let mut fut = pin!(fut);
1431
1432        // Handle the initial disconnect request
1433        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1434
1435        let sme_fut = test_values.sme_req_stream.into_future();
1436        let mut sme_fut = pin!(sme_fut);
1437
1438        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1439        assert_matches!(
1440            poll_sme_req(&mut exec, &mut sme_fut),
1441            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1442                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1443            }
1444        );
1445
1446        // Wait for a start request, but don't reply to it yet.
1447        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1448        let start_responder = assert_matches!(
1449            poll_sme_req(&mut exec, &mut sme_fut),
1450            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1451                responder
1452            }
1453        );
1454
1455        // Issue a stop request.
1456        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1457        let (stop_sender, mut stop_receiver) = oneshot::channel();
1458        ap.stop(stop_sender).expect("failed to make stop request");
1459
1460        // Run the state machine and ensure that a stop request is not issued by the SME proxy yet.
1461        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1462        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
1463
1464        // After SME responds to start request, the state machine can continue
1465        start_responder
1466            .send(fidl_sme::StartApResultCode::Success)
1467            .expect("could not send SME start response");
1468        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1469        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1470
1471        // Stop request should be issued now to SME
1472        assert_matches!(
1473            poll_sme_req(&mut exec, &mut sme_fut),
1474            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1475                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send SME stop response");
1476            }
1477        );
1478
1479        // Expect the responder to be acknowledged
1480        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1481        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1482
1483        // The successful AP start should be logged.
1484        assert_matches!(
1485            test_values.telemetry_receiver.try_next(),
1486            Ok(Some(TelemetryEvent::StartApResult(Ok(()))))
1487        );
1488    }
1489
1490    #[fuchsia::test]
1491    fn test_start_while_starting() {
1492        let mut exec = fasync::TestExecutor::new();
1493        let mut test_values = test_setup();
1494
1495        let (start_sender, mut start_receiver) = oneshot::channel();
1496        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1497        let req = ApConfig {
1498            id: create_network_id(),
1499            credential: vec![],
1500            radio_config,
1501            mode: types::ConnectivityMode::Unrestricted,
1502            band: types::OperatingBand::Any,
1503        };
1504
1505        // Start off in the starting state
1506        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1507        let fut = run_state_machine(fut);
1508        let mut fut = pin!(fut);
1509
1510        // Handle the initial disconnect request
1511        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1512
1513        let sme_fut = test_values.sme_req_stream.into_future();
1514        let mut sme_fut = pin!(sme_fut);
1515
1516        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1517        assert_matches!(
1518            poll_sme_req(&mut exec, &mut sme_fut),
1519            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1520                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1521            }
1522        );
1523
1524        // Wait for a start request, but don't reply to it.
1525        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1526        let start_responder = assert_matches!(
1527            poll_sme_req(&mut exec, &mut sme_fut),
1528            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1529                responder
1530            }
1531        );
1532
1533        // Issue a second start request.
1534        let (second_start_sender, mut second_start_receiver) = oneshot::channel();
1535        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1536        let req = ApConfig {
1537            id: create_network_id(),
1538            credential: vec![],
1539            radio_config,
1540            mode: types::ConnectivityMode::Unrestricted,
1541            band: types::OperatingBand::Any,
1542        };
1543        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1544        ap.start(req, second_start_sender).expect("failed to make start request");
1545
1546        // Run the state machine and ensure that the first start request is still pending.
1547        // Furthermore, no new start request is issued yet.
1548        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1549        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Pending);
1550        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
1551
1552        // Respond to the first start request
1553        start_responder
1554            .send(fidl_sme::StartApResultCode::Success)
1555            .expect("failed to send start response");
1556
1557        // The first request should receive the acknowledgement, the second one shouldn't.
1558        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1559        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1560        assert_matches!(exec.run_until_stalled(&mut second_start_receiver), Poll::Pending);
1561
1562        // The state machine should transition back into the starting state and issue a stop
1563        // request, due to the second start request.
1564        assert_matches!(
1565            poll_sme_req(&mut exec, &mut sme_fut),
1566            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1567                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send SME stop response");
1568            }
1569        );
1570
1571        // The state machine should then issue a start request.
1572        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1573        assert_matches!(
1574            poll_sme_req(&mut exec, &mut sme_fut),
1575            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1576                responder
1577                    .send(fidl_sme::StartApResultCode::Success)
1578                    .expect("failed to send start response");
1579            }
1580        );
1581
1582        // The second start request should receive the acknowledgement now.
1583        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1584        assert_matches!(exec.run_until_stalled(&mut second_start_receiver), Poll::Ready(Ok(())));
1585
1586        // The successful AP start event should be logged.
1587        assert_matches!(
1588            test_values.telemetry_receiver.try_next(),
1589            Ok(Some(TelemetryEvent::StartApResult(Ok(()))))
1590        );
1591    }
1592
1593    #[fuchsia::test]
1594    fn test_exit_while_starting() {
1595        let mut exec = fasync::TestExecutor::new();
1596        let mut test_values = test_setup();
1597
1598        let (start_sender, mut start_receiver) = oneshot::channel();
1599        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1600        let req = ApConfig {
1601            id: create_network_id(),
1602            credential: vec![],
1603            radio_config,
1604            mode: types::ConnectivityMode::Unrestricted,
1605            band: types::OperatingBand::Any,
1606        };
1607
1608        // Start off in the starting state
1609        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1610        let fut = run_state_machine(fut);
1611        let mut fut = pin!(fut);
1612
1613        // Handle the initial disconnect request
1614        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1615
1616        let sme_fut = test_values.sme_req_stream.into_future();
1617        let mut sme_fut = pin!(sme_fut);
1618
1619        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1620        assert_matches!(
1621            poll_sme_req(&mut exec, &mut sme_fut),
1622            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1623                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
1624            }
1625        );
1626
1627        // Wait for a start request, but don't reply to it.
1628        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1629        let start_responder = assert_matches!(
1630            poll_sme_req(&mut exec, &mut sme_fut),
1631            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1632                responder
1633            }
1634        );
1635
1636        // Issue an exit request.
1637        let mut ap = AccessPoint::new(test_values.ap_req_sender);
1638        let (exit_sender, mut exit_receiver) = oneshot::channel();
1639        ap.exit(exit_sender).expect("failed to make stop request");
1640
1641        // While starting is still in progress, exit and start should not be responded to yet.
1642        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1643        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Pending);
1644        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Pending);
1645
1646        // Once start AP request is finished, the state machine can terminate
1647        start_responder
1648            .send(fidl_sme::StartApResultCode::Success)
1649            .expect("could not send AP start response");
1650
1651        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1652        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Ready(Ok(())));
1653        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Ok(())));
1654
1655        // The AP start success event should be logged to telemetry.
1656        assert_matches!(
1657            test_values.telemetry_receiver.try_next(),
1658            Ok(Some(TelemetryEvent::StartApResult(Ok(()))))
1659        );
1660    }
1661
1662    #[fuchsia::test]
1663    fn test_sme_breaks_while_starting() {
1664        let mut exec = fasync::TestExecutor::new();
1665        let mut test_values = test_setup();
1666
1667        // Drop the serving side of the SME so that client requests fail.
1668        drop(test_values.sme_req_stream);
1669
1670        let (start_sender, _start_receiver) = oneshot::channel();
1671        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1672        let req = ApConfig {
1673            id: create_network_id(),
1674            credential: vec![],
1675            radio_config,
1676            mode: types::ConnectivityMode::Unrestricted,
1677            band: types::OperatingBand::Any,
1678        };
1679
1680        // Start off in the starting state
1681        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1682        let fut = run_state_machine(fut);
1683        let mut fut = pin!(fut);
1684
1685        // Run the state machine and expect it to exit
1686        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1687
1688        // No metric should be logged in this case and the sender should have been dropped.
1689        assert_matches!(test_values.telemetry_receiver.try_next(), Ok(None));
1690    }
1691
1692    #[fuchsia::test]
1693    fn test_sme_fails_to_stop_while_starting() {
1694        let mut exec = fasync::TestExecutor::new();
1695        let mut test_values = test_setup();
1696
1697        let (start_sender, _start_receiver) = oneshot::channel();
1698        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1699        let req = ApConfig {
1700            id: create_network_id(),
1701            credential: vec![],
1702            radio_config,
1703            mode: types::ConnectivityMode::Unrestricted,
1704            band: types::OperatingBand::Any,
1705        };
1706
1707        // Start off in the starting state
1708        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
1709        let fut = run_state_machine(fut);
1710        let mut fut = pin!(fut);
1711
1712        // Handle the initial disconnect request and send back a failure.
1713        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1714
1715        let sme_fut = test_values.sme_req_stream.into_future();
1716        let mut sme_fut = pin!(sme_fut);
1717
1718        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1719        assert_matches!(
1720            poll_sme_req(&mut exec, &mut sme_fut),
1721            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1722                responder
1723                    .send(fidl_sme::StopApResultCode::TimedOut)
1724                    .expect("could not send AP stop response");
1725            }
1726        );
1727
1728        // The future should complete.
1729        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1730
1731        // There should also be a failed state update.
1732        assert_matches!(
1733            test_values.update_receiver.try_next(),
1734            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1735            let update = updates.access_points.pop().expect("no new updates available.");
1736            assert_eq!(update.state, types::OperatingState::Failed);
1737        });
1738
1739        // No metric should be logged in this case and the sender should have been dropped.
1740        assert_matches!(test_values.telemetry_receiver.try_next(), Ok(None));
1741    }
1742
1743    #[fuchsia::test]
1744    fn test_sme_fails_to_start_while_starting() {
1745        let mut exec = fasync::TestExecutor::new();
1746        let mut test_values = test_setup();
1747
1748        let (start_sender, mut start_receiver) = oneshot::channel();
1749        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1750        let req = ApConfig {
1751            id: create_network_id(),
1752            credential: vec![],
1753            radio_config,
1754            mode: types::ConnectivityMode::Unrestricted,
1755            band: types::OperatingBand::Any,
1756        };
1757
1758        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
1759        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
1760        let fut = run_state_machine(fut);
1761        let mut fut = pin!(fut);
1762
1763        // We'll need to inject some SME responses.
1764        let sme_fut = test_values.sme_req_stream.into_future();
1765        let mut sme_fut = pin!(sme_fut);
1766
1767        for retry_number in 0..(AP_START_MAX_RETRIES + 1) {
1768            // Handle the initial stop request.
1769            assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1770            assert_matches!(
1771                poll_sme_req(&mut exec, &mut sme_fut),
1772                Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1773                    responder
1774                        .send(fidl_sme::StopApResultCode::Success)
1775                        .expect("could not send AP stop response");
1776                }
1777            );
1778
1779            // There should also be a stopped state update.
1780            assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1781            assert_matches!(
1782                test_values.update_receiver.try_next(),
1783                Ok(Some(listener::Message::NotifyListeners(_)))
1784            );
1785
1786            // If this is the first attempt, there should be a starting notification, otherwise
1787            // there should be no update.
1788            if retry_number == 0 {
1789                assert_matches!(
1790                    test_values.update_receiver.try_next(),
1791                    Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1792                    let update = updates.access_points.pop().expect("no new updates available.");
1793                    assert_eq!(update.state, types::OperatingState::Starting);
1794                });
1795            } else {
1796                assert_matches!(test_values.update_receiver.try_next(), Err(_));
1797            }
1798
1799            // Wait for a start request and send back a timeout.
1800            assert_matches!(
1801                poll_sme_req(&mut exec, &mut sme_fut),
1802                Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1803                    responder
1804                        .send(fidl_sme::StartApResultCode::TimedOut)
1805                        .expect("could not send AP stop response");
1806                }
1807            );
1808
1809            if retry_number < AP_START_MAX_RETRIES {
1810                // The future should still be running.
1811                assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1812
1813                // Verify that no new message has been reported yet.
1814                assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Pending);
1815
1816                // The state machine should then retry following the retry interval.
1817                assert_matches!(exec.wake_next_timer(), Some(_));
1818            }
1819        }
1820
1821        // The future should complete.
1822        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
1823
1824        // Verify that the start receiver got an error.
1825        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Err(_)));
1826
1827        // There should be a failure notification at the end of the retries.
1828        assert_matches!(
1829            test_values.update_receiver.try_next(),
1830            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1831            let update = updates.access_points.pop().expect("no new updates available.");
1832            assert_eq!(update.state, types::OperatingState::Failed);
1833        });
1834
1835        // A metric should be logged for the failure to start the AP.
1836        assert_matches!(
1837            test_values.telemetry_receiver.try_next(),
1838            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
1839        );
1840
1841        // A defect should be sent as well.
1842        assert_matches!(
1843            test_values.defect_receiver.try_next(),
1844            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
1845        );
1846    }
1847
1848    #[fuchsia::test]
1849    fn test_stop_after_start_failure() {
1850        let mut exec = fasync::TestExecutor::new();
1851        let mut test_values = test_setup();
1852
1853        let (start_sender, mut start_receiver) = oneshot::channel();
1854        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1855        let req = ApConfig {
1856            id: create_network_id(),
1857            credential: vec![],
1858            radio_config,
1859            mode: types::ConnectivityMode::Unrestricted,
1860            band: types::OperatingBand::Any,
1861        };
1862
1863        // Insert a stop request to be processed after starting the AP fails.
1864        let (stop_sender, mut stop_receiver) = oneshot::channel();
1865        test_values
1866            .ap_req_sender
1867            .try_send(ManualRequest::Stop(stop_sender))
1868            .expect("failed to request AP stop");
1869
1870        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
1871        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
1872        let fut = run_state_machine(fut);
1873        let mut fut = pin!(fut);
1874
1875        // We'll need to inject some SME responses.
1876        let sme_fut = test_values.sme_req_stream.into_future();
1877        let mut sme_fut = pin!(sme_fut);
1878
1879        // Handle the initial stop request.
1880        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1881        assert_matches!(
1882            poll_sme_req(&mut exec, &mut sme_fut),
1883            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1884                responder
1885                    .send(fidl_sme::StopApResultCode::Success)
1886                    .expect("could not send AP stop response");
1887            }
1888        );
1889
1890        // There should also be a stopped state update.
1891        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1892        assert_matches!(
1893            test_values.update_receiver.try_next(),
1894            Ok(Some(listener::Message::NotifyListeners(_)))
1895        );
1896
1897        // Followed by a starting update.
1898        assert_matches!(
1899            test_values.update_receiver.try_next(),
1900            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
1901            let update = updates.access_points.pop().expect("no new updates available.");
1902            assert_eq!(update.state, types::OperatingState::Starting);
1903        });
1904
1905        // Wait for a start request and send back a timeout.
1906        assert_matches!(
1907            poll_sme_req(&mut exec, &mut sme_fut),
1908            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
1909                responder
1910                    .send(fidl_sme::StartApResultCode::TimedOut)
1911                    .expect("could not send AP stop response");
1912            }
1913        );
1914
1915        // At this point, the state machine should pause before retrying the start request.  It
1916        // should also check to see if there are any incoming AP commands and find the initial stop
1917        // request.
1918        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1919
1920        // A metric should be logged for the failure to start the AP.
1921        assert_matches!(
1922            test_values.telemetry_receiver.try_next(),
1923            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
1924        );
1925
1926        // A defect should be sent as well.
1927        assert_matches!(
1928            test_values.defect_receiver.try_next(),
1929            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
1930        );
1931
1932        // The start sender will be dropped in this transition.
1933        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Err(_)));
1934
1935        // There should be a pending AP stop request.
1936        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1937        assert_matches!(
1938            poll_sme_req(&mut exec, &mut sme_fut),
1939            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
1940                responder
1941                    .send(fidl_sme::StopApResultCode::Success)
1942                    .expect("could not send AP stop response");
1943            }
1944        );
1945
1946        // The future should be parked in the stopped state.
1947        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
1948
1949        // Verify that the stop receiver is acknowledged.
1950        assert_matches!(exec.run_until_stalled(&mut stop_receiver), Poll::Ready(Ok(())));
1951
1952        // There should be a new update indicating that no AP's are active.
1953        assert_matches!(
1954            test_values.update_receiver.try_next(),
1955            Ok(Some(listener::Message::NotifyListeners(updates))) => {
1956            assert!(updates.access_points.is_empty());
1957        });
1958    }
1959
1960    #[fuchsia::test]
1961    fn test_start_after_start_failure() {
1962        let mut exec = fasync::TestExecutor::new();
1963        let mut test_values = test_setup();
1964
1965        let (start_sender, mut start_receiver) = oneshot::channel();
1966        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
1967        let req = ApConfig {
1968            id: create_network_id(),
1969            credential: vec![],
1970            radio_config: radio_config.clone(),
1971            mode: types::ConnectivityMode::Unrestricted,
1972            band: types::OperatingBand::Any,
1973        };
1974
1975        // Insert a stop request to be processed after starting the AP fails.
1976        let mut requested_id = create_network_id();
1977        requested_id.ssid = types::Ssid::try_from("second_test_ssid").unwrap();
1978
1979        let requested_config = ApConfig {
1980            id: requested_id.clone(),
1981            credential: vec![],
1982            radio_config,
1983            mode: types::ConnectivityMode::Unrestricted,
1984            band: types::OperatingBand::Any,
1985        };
1986
1987        let (start_response_sender, _) = oneshot::channel();
1988        test_values
1989            .ap_req_sender
1990            .try_send(ManualRequest::Start((requested_config, start_response_sender)))
1991            .expect("failed to request AP stop");
1992
1993        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
1994        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
1995        let fut = run_state_machine(fut);
1996        let mut fut = pin!(fut);
1997
1998        // We'll need to inject some SME responses.
1999        let sme_fut = test_values.sme_req_stream.into_future();
2000        let mut sme_fut = pin!(sme_fut);
2001
2002        // Handle the initial stop request.
2003        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2004        assert_matches!(
2005            poll_sme_req(&mut exec, &mut sme_fut),
2006            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2007                responder
2008                    .send(fidl_sme::StopApResultCode::Success)
2009                    .expect("could not send AP stop response");
2010            }
2011        );
2012
2013        // There should also be a stopped state update.
2014        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2015        assert_matches!(
2016            test_values.update_receiver.try_next(),
2017            Ok(Some(listener::Message::NotifyListeners(_)))
2018        );
2019
2020        // Followed by a starting update.
2021        assert_matches!(
2022            test_values.update_receiver.try_next(),
2023            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
2024            let update = updates.access_points.pop().expect("no new updates available.");
2025            assert_eq!(update.state, types::OperatingState::Starting);
2026        });
2027
2028        // Wait for a start request and send back a timeout.
2029        assert_matches!(
2030            poll_sme_req(&mut exec, &mut sme_fut),
2031            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
2032                responder
2033                    .send(fidl_sme::StartApResultCode::TimedOut)
2034                    .expect("could not send AP stop response");
2035            }
2036        );
2037
2038        // At this point, the state machine should pause before retrying the start request.  It
2039        // should also check to see if there are any incoming AP commands and find the initial
2040        // start request.
2041        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2042
2043        // A metric should be logged for the failure to start the AP.
2044        assert_matches!(
2045            test_values.telemetry_receiver.try_next(),
2046            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
2047        );
2048
2049        // A defect should be sent as well.
2050        assert_matches!(
2051            test_values.defect_receiver.try_next(),
2052            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
2053        );
2054
2055        // The original start sender will be dropped in this transition.
2056        assert_matches!(exec.run_until_stalled(&mut start_receiver), Poll::Ready(Err(_)));
2057
2058        // There should be a pending AP stop request.
2059        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2060        assert_matches!(
2061            poll_sme_req(&mut exec, &mut sme_fut),
2062            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2063                responder
2064                    .send(fidl_sme::StopApResultCode::Success)
2065                    .expect("could not send AP stop response");
2066            }
2067        );
2068
2069        // This should be followed by another start request that matches the requested config.
2070        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2071        assert_matches!(
2072            poll_sme_req(&mut exec, &mut sme_fut),
2073            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config, responder: _ }) => {
2074                assert_eq!(config.ssid, requested_id.ssid);
2075            }
2076        );
2077    }
2078
2079    #[fuchsia::test]
2080    fn test_exit_after_start_failure() {
2081        let mut exec = fasync::TestExecutor::new();
2082        let mut test_values = test_setup();
2083
2084        let (start_sender, _) = oneshot::channel();
2085        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
2086        let req = ApConfig {
2087            id: create_network_id(),
2088            credential: vec![],
2089            radio_config,
2090            mode: types::ConnectivityMode::Unrestricted,
2091            band: types::OperatingBand::Any,
2092        };
2093
2094        // Insert a stop request to be processed after starting the AP fails.
2095        let (exit_sender, mut exit_receiver) = oneshot::channel();
2096        test_values
2097            .ap_req_sender
2098            .try_send(ManualRequest::Exit(exit_sender))
2099            .expect("failed to request AP stop");
2100
2101        // Start off in the starting state with AP_START_MAX_RETRIES retry attempts.
2102        let fut = starting_state(test_values.deps, req, AP_START_MAX_RETRIES, Some(start_sender));
2103        let fut = run_state_machine(fut);
2104        let mut fut = pin!(fut);
2105
2106        // We'll need to inject some SME responses.
2107        let sme_fut = test_values.sme_req_stream.into_future();
2108        let mut sme_fut = pin!(sme_fut);
2109
2110        // Handle the initial stop request.
2111        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2112        assert_matches!(
2113            poll_sme_req(&mut exec, &mut sme_fut),
2114            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2115                responder
2116                    .send(fidl_sme::StopApResultCode::Success)
2117                    .expect("could not send AP stop response");
2118            }
2119        );
2120
2121        // There should also be a stopped state update.
2122        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2123        assert_matches!(
2124            test_values.update_receiver.try_next(),
2125            Ok(Some(listener::Message::NotifyListeners(_)))
2126        );
2127
2128        // Followed by a starting update.
2129        assert_matches!(
2130            test_values.update_receiver.try_next(),
2131            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
2132            let update = updates.access_points.pop().expect("no new updates available.");
2133            assert_eq!(update.state, types::OperatingState::Starting);
2134        });
2135
2136        // Wait for a start request and send back a timeout.
2137        assert_matches!(
2138            poll_sme_req(&mut exec, &mut sme_fut),
2139            Poll::Ready(fidl_sme::ApSmeRequest::Start{ config: _, responder }) => {
2140                responder
2141                    .send(fidl_sme::StartApResultCode::TimedOut)
2142                    .expect("could not send AP stop response");
2143            }
2144        );
2145
2146        // At this point, the state machine should pause before retrying the start request.  It
2147        // should also check to see if there are any incoming AP commands and find the initial exit
2148        // request at which point it should exit.
2149        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
2150        assert_matches!(exec.run_until_stalled(&mut exit_receiver), Poll::Ready(Ok(())));
2151
2152        // A metric should be logged for the failure to start the AP.
2153        assert_matches!(
2154            test_values.telemetry_receiver.try_next(),
2155            Ok(Some(TelemetryEvent::StartApResult(Err(()))))
2156        );
2157
2158        // A defect should be sent as well.
2159        assert_matches!(
2160            test_values.defect_receiver.try_next(),
2161            Ok(Some(Defect::Iface(IfaceFailure::ApStartFailure { .. })))
2162        );
2163    }
2164
2165    #[fuchsia::test]
2166    fn test_manual_start_causes_starting_notification() {
2167        let mut exec = fasync::TestExecutor::new();
2168        let mut test_values = test_setup();
2169
2170        // Create a start request and enter the state machine with a manual start request.
2171        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
2172        let requested_config = ApConfig {
2173            id: create_network_id(),
2174            credential: vec![],
2175            radio_config,
2176            mode: types::ConnectivityMode::Unrestricted,
2177            band: types::OperatingBand::Any,
2178        };
2179
2180        let (start_response_sender, _) = oneshot::channel();
2181        let manual_request = ManualRequest::Start((requested_config, start_response_sender));
2182
2183        let fut = perform_manual_request(test_values.deps, Some(manual_request));
2184        let fut = run_state_machine(async move { fut });
2185        let mut fut = pin!(fut);
2186        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2187
2188        // We should get a stop request
2189        let sme_fut = test_values.sme_req_stream.into_future();
2190        let mut sme_fut = pin!(sme_fut);
2191
2192        assert_matches!(
2193            poll_sme_req(&mut exec, &mut sme_fut),
2194            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2195                responder
2196                    .send(fidl_sme::StopApResultCode::Success)
2197                    .expect("could not send SME stop response");
2198            }
2199        );
2200
2201        // We should then get a notification that the AP is inactive followed by a new starting
2202        // notification.
2203        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2204        assert_matches!(
2205            test_values.update_receiver.try_next(),
2206            Ok(Some(listener::Message::NotifyListeners(updates))) => {
2207                assert!(updates.access_points.is_empty());
2208        });
2209
2210        assert_matches!(
2211            test_values.update_receiver.try_next(),
2212            Ok(Some(listener::Message::NotifyListeners(mut updates))) => {
2213            let update = updates.access_points.pop().expect("no new updates available.");
2214            assert_eq!(update.state, types::OperatingState::Starting);
2215        });
2216    }
2217
2218    #[fuchsia::test]
2219    fn test_serve_does_not_terminate_right_away() {
2220        let mut exec = fasync::TestExecutor::new();
2221        let test_values = test_setup();
2222        let sme_event_stream = test_values.deps.proxy.take_event_stream();
2223        let sme_fut = test_values.sme_req_stream.into_future();
2224        let mut sme_fut = pin!(sme_fut);
2225
2226        let update_sender = test_values.deps.state_tracker.inner.lock().sender.clone();
2227
2228        let fut = serve(
2229            0,
2230            test_values.deps.proxy,
2231            sme_event_stream,
2232            test_values.deps.req_stream,
2233            update_sender,
2234            test_values.deps.telemetry_sender,
2235            test_values.deps.defect_sender,
2236            test_values.deps.status_publisher,
2237        );
2238        let mut fut = pin!(fut);
2239
2240        // Run the state machine. No request is made initially.
2241        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2242        assert_matches!(poll_sme_req(&mut exec, &mut sme_fut), Poll::Pending);
2243    }
2244
2245    #[fuchsia::test]
2246    fn test_no_notification_when_sme_fails_while_stopped() {
2247        let mut exec = fasync::TestExecutor::new();
2248        let test_values = test_setup();
2249        let sme_event_stream = test_values.deps.proxy.take_event_stream();
2250        let update_sender = test_values.deps.state_tracker.inner.lock().sender.clone();
2251
2252        // Set the initial state to Starting to verify that it is changed on exit.
2253        test_values.deps.status_publisher.publish_status(Status::Starting);
2254
2255        let fut = serve(
2256            0,
2257            test_values.deps.proxy,
2258            sme_event_stream,
2259            test_values.deps.req_stream,
2260            update_sender,
2261            test_values.deps.telemetry_sender,
2262            test_values.deps.defect_sender,
2263            test_values.deps.status_publisher.clone(),
2264        );
2265        let mut fut = pin!(fut);
2266
2267        // Cause the SME event stream to terminate.
2268        drop(test_values.sme_req_stream);
2269
2270        // Run the state machine and observe that it has terminated.
2271        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
2272
2273        // There should be no notification of failure since no AP is actively running.
2274        assert_matches!(
2275            exec.run_until_stalled(&mut test_values.update_receiver.into_future()),
2276            Poll::Pending
2277        );
2278
2279        // Verify that the state has been set to stopped on exit.
2280        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Stopped));
2281    }
2282
2283    #[fuchsia::test]
2284    fn test_failure_notification_when_configured() {
2285        let mut exec = fasync::TestExecutor::new();
2286        let mut test_values = test_setup();
2287        let sme_event_stream = test_values.deps.proxy.take_event_stream();
2288        let mut sme_fut = Box::pin(test_values.sme_req_stream.into_future());
2289
2290        let update_sender = test_values.deps.state_tracker.inner.lock().sender.clone();
2291        let fut = serve(
2292            0,
2293            test_values.deps.proxy,
2294            sme_event_stream,
2295            test_values.deps.req_stream,
2296            update_sender,
2297            test_values.deps.telemetry_sender,
2298            test_values.deps.defect_sender,
2299            test_values.deps.status_publisher,
2300        );
2301        let mut fut = pin!(fut);
2302
2303        // Make a request to start the access point.
2304        let mut ap = AccessPoint::new(test_values.ap_req_sender);
2305        let (sender, _receiver) = oneshot::channel();
2306        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
2307        let config = ApConfig {
2308            id: create_network_id(),
2309            credential: vec![],
2310            radio_config,
2311            mode: types::ConnectivityMode::Unrestricted,
2312            band: types::OperatingBand::Any,
2313        };
2314        ap.start(config, sender).expect("failed to make start request");
2315
2316        // Expect that the state machine issues a stop request followed by a start request.
2317        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2318        assert_matches!(
2319            poll_sme_req(&mut exec, &mut sme_fut),
2320            Poll::Ready(fidl_sme::ApSmeRequest::Stop{ responder }) => {
2321                responder.send(fidl_sme::StopApResultCode::Success).expect("could not send AP stop response");
2322            }
2323        );
2324
2325        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2326
2327        // At this point, the state machine will have sent an empty notification and a starting
2328        // notification.
2329        assert_matches!(
2330            test_values.update_receiver.try_next(),
2331            Ok(Some(listener::Message::NotifyListeners(update))) => {
2332                assert!(update.access_points.is_empty());
2333            }
2334        );
2335        assert_matches!(
2336            test_values.update_receiver.try_next(),
2337            Ok(Some(listener::Message::NotifyListeners(update))) => {
2338                assert_eq!(update.access_points.len(), 1);
2339                assert_eq!(update.access_points[0].state, types::OperatingState::Starting);
2340            }
2341        );
2342
2343        // Cause the SME event stream to terminate.
2344        drop(sme_fut);
2345
2346        // Run the state machine and observe that it has terminated.
2347        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
2348
2349        // There should be a failure notification.
2350        assert_matches!(
2351            test_values.update_receiver.try_next(),
2352            Ok(Some(listener::Message::NotifyListeners(update))) => {
2353                assert_eq!(update.access_points.len(), 1);
2354                assert_eq!(update.access_points[0].state, types::OperatingState::Failed);
2355            }
2356        );
2357    }
2358
2359    #[fuchsia::test]
2360    fn test_state_tracker_reset() {
2361        let _exec = fasync::TestExecutor::new();
2362        let (sender, mut receiver) = mpsc::unbounded();
2363
2364        // A new state tracker should initially have no state.
2365        let state = ApStateTracker::new(sender);
2366        {
2367            assert!(state.inner.lock().state.is_none());
2368        }
2369
2370        // And there should be no updates.
2371        assert_matches!(receiver.try_next(), Err(_));
2372
2373        // Reset the state to starting and verify that the internal state has been updated.
2374        let new_state = ApStateUpdate::new(
2375            create_network_id(),
2376            types::OperatingState::Starting,
2377            types::ConnectivityMode::Unrestricted,
2378            types::OperatingBand::Any,
2379        );
2380        state.reset_state(new_state).expect("failed to reset state");
2381        assert_matches!(state.inner.lock().state.as_ref(), Some(ApStateUpdate {
2382                id: types::NetworkIdentifier {
2383                    ssid,
2384                    security_type: types::SecurityType::None,
2385                },
2386                state: types::OperatingState::Starting,
2387                mode: Some(types::ConnectivityMode::Unrestricted),
2388                band: Some(types::OperatingBand::Any),
2389                frequency: None,
2390                clients: None,
2391        }) => {
2392            let expected_ssid = types::Ssid::try_from("test_ssid").unwrap();
2393            assert_eq!(ssid, &expected_ssid);
2394        });
2395
2396        // Resetting the state should result in an update.
2397        assert_matches!(
2398            receiver.try_next(),
2399            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))) => {
2400            assert_eq!(access_points.len(), 1);
2401
2402            let expected_id = types::NetworkIdentifier {
2403                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2404                security_type: types::SecurityType::None,
2405            };
2406            assert_eq!(access_points[0].id, expected_id);
2407            assert_eq!(access_points[0].state, types::OperatingState::Starting);
2408            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2409            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2410            assert_eq!(access_points[0].frequency, None);
2411            assert_eq!(access_points[0].clients, None);
2412            }
2413        );
2414    }
2415
2416    #[fuchsia::test]
2417    fn test_state_tracker_consume_sme_update() {
2418        let _exec = fasync::TestExecutor::new();
2419        let (sender, mut receiver) = mpsc::unbounded();
2420        let state = ApStateTracker::new(sender);
2421
2422        // Reset the state to started and send an update.
2423        let new_state = ApStateUpdate::new(
2424            create_network_id(),
2425            types::OperatingState::Active,
2426            types::ConnectivityMode::Unrestricted,
2427            types::OperatingBand::Any,
2428        );
2429        state.reset_state(new_state).expect("failed to reset state");
2430
2431        // The update should note that the AP is active.
2432        assert_matches!(
2433            receiver.try_next(),
2434            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2435        ) => {
2436            assert_eq!(access_points.len(), 1);
2437
2438            let expected_id = types::NetworkIdentifier {
2439                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2440                security_type: types::SecurityType::None,
2441            };
2442            assert_eq!(access_points[0].id, expected_id);
2443            assert_eq!(access_points[0].state, types::OperatingState::Active);
2444            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2445            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2446            assert_eq!(access_points[0].frequency, None);
2447            assert_eq!(access_points[0].clients, None);
2448        });
2449
2450        // Consume a status update and expect a new notification to be generated.
2451        let ap_info = fidl_sme::Ap {
2452            ssid: types::Ssid::try_from("test_ssid").unwrap().to_vec(),
2453            channel: 6,
2454            num_clients: 123,
2455        };
2456        state
2457            .consume_sme_status_update(Cbw::Cbw20, ap_info)
2458            .expect("failure while updating SME status");
2459
2460        assert_matches!(
2461            receiver.try_next(),
2462            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2463        ) => {
2464            assert_eq!(access_points.len(), 1);
2465
2466            let expected_id = types::NetworkIdentifier {
2467                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2468                security_type: types::SecurityType::None,
2469            };
2470            assert_eq!(access_points[0].id, expected_id);
2471            assert_eq!(access_points[0].state, types::OperatingState::Active);
2472            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2473            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2474            assert_eq!(access_points[0].frequency, Some(2437));
2475            assert_eq!(access_points[0].clients, Some(ConnectedClientInformation { count: 123 }));
2476        });
2477    }
2478
2479    #[fuchsia::test]
2480    fn test_state_tracker_update_operating_state() {
2481        let _exec = fasync::TestExecutor::new();
2482        let (sender, mut receiver) = mpsc::unbounded();
2483        let state = ApStateTracker::new(sender);
2484
2485        // Reset the state to started and send an update.
2486        let new_state = ApStateUpdate::new(
2487            create_network_id(),
2488            types::OperatingState::Starting,
2489            types::ConnectivityMode::Unrestricted,
2490            types::OperatingBand::Any,
2491        );
2492        state.reset_state(new_state).expect("failed to reset state");
2493
2494        // The update should note that the AP is starting.
2495        assert_matches!(
2496            receiver.try_next(),
2497            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2498        ) => {
2499            assert_eq!(access_points.len(), 1);
2500
2501            let expected_id = types::NetworkIdentifier {
2502                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2503                security_type: types::SecurityType::None,
2504            };
2505            assert_eq!(access_points[0].id, expected_id);
2506            assert_eq!(access_points[0].state, types::OperatingState::Starting);
2507            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2508            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2509            assert_eq!(access_points[0].frequency, None);
2510            assert_eq!(access_points[0].clients, None);
2511        });
2512
2513        // Give another update that the state is starting and ensure that a notification is sent.
2514        state
2515            .update_operating_state(types::OperatingState::Starting)
2516            .expect("failed to send duplicate update.");
2517        assert_matches!(
2518            receiver.try_next(),
2519            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2520        ) => {
2521            assert_eq!(access_points.len(), 1);
2522
2523            let expected_id = types::NetworkIdentifier {
2524                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2525                security_type: types::SecurityType::None,
2526            };
2527            assert_eq!(access_points[0].id, expected_id);
2528            assert_eq!(access_points[0].state, types::OperatingState::Starting);
2529            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2530            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2531            assert_eq!(access_points[0].frequency, None);
2532            assert_eq!(access_points[0].clients, None);
2533        });
2534
2535        // Now update that the state is active and expect a notification to be generated.
2536        state
2537            .update_operating_state(types::OperatingState::Active)
2538            .expect("failed to send active update.");
2539        assert_matches!(
2540            receiver.try_next(),
2541            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2542        ) => {
2543            assert_eq!(access_points.len(), 1);
2544
2545            let expected_id = types::NetworkIdentifier {
2546                ssid: types::Ssid::try_from("test_ssid").unwrap(),
2547                security_type: types::SecurityType::None,
2548            };
2549            assert_eq!(access_points[0].id, expected_id);
2550            assert_eq!(access_points[0].state, types::OperatingState::Active);
2551            assert_eq!(access_points[0].mode, Some(types::ConnectivityMode::Unrestricted));
2552            assert_eq!(access_points[0].band, Some(types::OperatingBand::Any));
2553            assert_eq!(access_points[0].frequency, None);
2554            assert_eq!(access_points[0].clients, None);
2555        });
2556    }
2557
2558    #[fuchsia::test]
2559    fn test_state_tracker_set_stopped_state() {
2560        let _exec = fasync::TestExecutor::new();
2561        let (sender, mut receiver) = mpsc::unbounded();
2562        let state = ApStateTracker::new(sender);
2563
2564        // Set up some initial state.
2565        {
2566            let new_state = ApStateUpdate::new(
2567                create_network_id(),
2568                types::OperatingState::Active,
2569                types::ConnectivityMode::Unrestricted,
2570                types::OperatingBand::Any,
2571            );
2572            state.inner.lock().state = Some(new_state);
2573        }
2574
2575        // Set the state to stopped and verify that the internal state information has been
2576        // removed.
2577        state.set_stopped_state().expect("failed to send stopped notification");
2578        {
2579            assert!(state.inner.lock().state.is_none());
2580        }
2581
2582        // Verify that an empty update has arrived.
2583        assert_matches!(
2584            receiver.try_next(),
2585            Ok(Some(listener::Message::NotifyListeners(ApStatesUpdate { access_points }))
2586        ) => {
2587            assert!(access_points.is_empty());
2588        });
2589    }
2590
2591    #[fuchsia::test]
2592    fn test_state_tracker_failure_modes() {
2593        let _exec = fasync::TestExecutor::new();
2594        let (sender, receiver) = mpsc::unbounded();
2595        let state = ApStateTracker::new(sender);
2596        {
2597            let new_state = ApStateUpdate::new(
2598                create_network_id(),
2599                types::OperatingState::Active,
2600                types::ConnectivityMode::Unrestricted,
2601                types::OperatingBand::Any,
2602            );
2603            state.inner.lock().state = Some(new_state);
2604        }
2605
2606        // Currently, the only reason any of the state tracker methods might fail is because of a
2607        // failure to enqueue a state change notification.  Drop the receiving end to trigger this
2608        // condition.
2609        drop(receiver);
2610
2611        let _ = state
2612            .update_operating_state(types::OperatingState::Failed)
2613            .expect_err("unexpectedly able to set operating state");
2614        let _ = state
2615            .consume_sme_status_update(
2616                Cbw::Cbw20,
2617                fidl_sme::Ap {
2618                    ssid: types::Ssid::try_from("test_ssid").unwrap().to_vec(),
2619                    channel: 6,
2620                    num_clients: 123,
2621                },
2622            )
2623            .expect_err("unexpectedly able to update SME status");
2624        let _ = state.set_stopped_state().expect_err("unexpectedly able to set stopped state");
2625    }
2626
2627    #[fuchsia::test]
2628    fn test_state_when_stopping() {
2629        let mut exec = fasync::TestExecutor::new();
2630        let test_values = test_setup();
2631
2632        // Run the stopping state.
2633        let (stop_sender, _) = oneshot::channel();
2634        let fut = stopping_state(test_values.deps, stop_sender);
2635        let fut = run_state_machine(fut);
2636        let mut fut = pin!(fut);
2637        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2638
2639        // Verify that the state has been set to Stopping.
2640        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Stopping));
2641    }
2642
2643    #[fuchsia::test]
2644    fn test_state_when_stopped() {
2645        let mut exec = fasync::TestExecutor::new();
2646        let test_values = test_setup();
2647
2648        // Set the initial state to Starting to verify that it is changed in the stopped state.
2649        test_values.deps.status_publisher.publish_status(Status::Starting);
2650
2651        // Run the stopping state.
2652        let fut = stopped_state(test_values.deps);
2653        let fut = run_state_machine(fut);
2654        let mut fut = pin!(fut);
2655        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2656
2657        // Verify that the state has been set to Stopped.
2658        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Stopped));
2659    }
2660
2661    #[fuchsia::test]
2662    fn test_state_when_starting() {
2663        let mut exec = fasync::TestExecutor::new();
2664        let test_values = test_setup();
2665
2666        // Run the starting state.
2667        let (start_sender, _) = oneshot::channel();
2668        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
2669        let req = ApConfig {
2670            id: create_network_id(),
2671            credential: vec![],
2672            radio_config,
2673            mode: types::ConnectivityMode::Unrestricted,
2674            band: types::OperatingBand::Any,
2675        };
2676        let fut = starting_state(test_values.deps, req, 0, Some(start_sender));
2677        let fut = run_state_machine(fut);
2678        let mut fut = pin!(fut);
2679        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2680
2681        // Verify that the state has been set to Starting.
2682        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Starting));
2683    }
2684
2685    #[fuchsia::test]
2686    fn test_state_when_started() {
2687        let mut exec = fasync::TestExecutor::new();
2688        let test_values = test_setup();
2689
2690        // Run the started state.
2691        let radio_config = RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6);
2692        let req = ApConfig {
2693            id: create_network_id(),
2694            credential: vec![],
2695            radio_config,
2696            mode: types::ConnectivityMode::Unrestricted,
2697            band: types::OperatingBand::Any,
2698        };
2699        let fut = started_state(test_values.deps, req);
2700        let fut = run_state_machine(fut);
2701        let mut fut = pin!(fut);
2702        assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
2703
2704        // Verify that the state has been set to Started.
2705        assert_matches!(test_values.status_reader.read_status(), Ok(Status::Started { .. }));
2706    }
2707
2708    struct InspectTestValues {
2709        exec: fasync::TestExecutor,
2710        inspector: fuchsia_inspect::Inspector,
2711        _node: fuchsia_inspect::Node,
2712        status_node: fuchsia_inspect_contrib::nodes::BoundedListNode,
2713    }
2714
2715    impl InspectTestValues {
2716        fn new(exec: fasync::TestExecutor) -> Self {
2717            let inspector = fuchsia_inspect::Inspector::default();
2718            let _node = inspector.root().create_child("node");
2719            let status_node =
2720                fuchsia_inspect_contrib::nodes::BoundedListNode::new(_node.clone_weak(), 1);
2721
2722            Self { exec, inspector, _node, status_node }
2723        }
2724
2725        fn log_status(&mut self, status: Status) -> fuchsia_inspect::reader::DiagnosticsHierarchy {
2726            fuchsia_inspect_contrib::inspect_log!(self.status_node, "status" => status);
2727            let read_fut = fuchsia_inspect::reader::read(&self.inspector);
2728            let mut read_fut = pin!(read_fut);
2729            assert_matches!(
2730                self.exec.run_until_stalled(&mut read_fut),
2731                Poll::Ready(Ok(hierarchy)) => hierarchy
2732            )
2733        }
2734    }
2735
2736    #[fuchsia::test]
2737    fn test_stopping_status_inspect_log() {
2738        let exec = fasync::TestExecutor::new_with_fake_time();
2739        let mut test_values = InspectTestValues::new(exec);
2740        let hierarchy = test_values.log_status(Status::Stopping);
2741        diagnostics_assertions::assert_data_tree!(
2742            @executor test_values.exec,
2743            hierarchy,
2744            root: contains {
2745                node: contains {
2746                    "0": contains {
2747                        status: "Stopping"
2748                    }
2749                }
2750        });
2751    }
2752
2753    #[fuchsia::test]
2754    fn test_stopped_status_inspect_log() {
2755        let exec = fasync::TestExecutor::new_with_fake_time();
2756        let mut test_values = InspectTestValues::new(exec);
2757        let hierarchy = test_values.log_status(Status::Stopped);
2758        diagnostics_assertions::assert_data_tree!(
2759            @executor test_values.exec,
2760            hierarchy,
2761            root: contains {
2762                node: contains {
2763                    "0": contains {
2764                        status: "Stopped"
2765                    }
2766                }
2767        });
2768    }
2769
2770    #[fuchsia::test]
2771    fn test_starting_status_inspect_log() {
2772        let exec = fasync::TestExecutor::new_with_fake_time();
2773        let mut test_values = InspectTestValues::new(exec);
2774        let hierarchy = test_values.log_status(Status::Starting);
2775        diagnostics_assertions::assert_data_tree!(
2776            @executor test_values.exec,
2777            hierarchy,
2778            root: contains {
2779                node: contains {
2780                    "0": contains {
2781                        status: "Starting"
2782                    }
2783                }
2784        });
2785    }
2786
2787    #[fuchsia::test]
2788    fn test_started_status_inspect_log() {
2789        let exec = fasync::TestExecutor::new_with_fake_time();
2790        let mut test_values = InspectTestValues::new(exec);
2791        let hierarchy = test_values.log_status(Status::Started {
2792            band: types::OperatingBand::Any,
2793            channel: 1,
2794            mode: types::ConnectivityMode::Unrestricted,
2795            num_clients: 2,
2796            security_type: types::SecurityType::None,
2797        });
2798        diagnostics_assertions::assert_data_tree!(
2799            @executor test_values.exec,
2800            hierarchy,
2801            root: contains {
2802                node: contains {
2803                    "0": contains {
2804                        status: contains {
2805                            Started: {
2806                                band: "Any",
2807                                channel: 1_u64,
2808                                mode: "Unrestricted",
2809                                num_clients: 2_u64,
2810                                security_type: "None"
2811                            }
2812                        }
2813                    }
2814                }
2815        });
2816    }
2817}