emergency_lib/
bss_cache.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use async_trait::async_trait;
6use fidl::Error as FidlError;
7use fidl_fuchsia_wlan_policy::{
8    Bss as WlanPolicyBss, ScanErrorCode, ScanResult, ScanResultIteratorProxyInterface,
9};
10use futures::future::BoxFuture;
11use futures::task::{Context, Poll};
12use futures::{Future, FutureExt, Stream, StreamExt};
13use std::collections::BTreeMap;
14use std::pin::Pin;
15use thiserror::Error;
16
17#[async_trait(?Send)]
18pub trait BssCache {
19    /// Updates the cache with BSSes from `new_bsses`.
20    async fn update<I: ScanResultIteratorProxyInterface>(
21        &mut self,
22        new_bsses: I,
23    ) -> Result<(), UpdateError>;
24
25    /// Returns an iterator over the known BSSes.
26    fn iter(&self) -> Box<dyn Iterator<Item = (&'_ BssId, &'_ Bss)> + '_>;
27}
28
29/// A cache for WLAN Basic Service Sets, also known as WLAN base-stations.
30#[derive(Default)]
31pub struct RealBssCache {
32    bss_map: BTreeMap<BssId, Bss>,
33}
34
35pub type BssId = [u8; BSS_ADDR_LEN_BYTES];
36
37#[derive(Clone, Copy, Debug, PartialEq)]
38pub struct Bss {
39    pub(crate) rssi: Option<i8>,
40    pub(crate) frequency: Option<u32>,
41}
42
43#[derive(Clone, Copy, Debug, Error, PartialEq)]
44pub enum UpdateError {
45    #[error("found BSSes, but no BSS IDs")]
46    NoBssIds,
47    #[error("found no BSSes")]
48    NoBsses,
49    #[error("connection to iterator failed")]
50    Ipc,
51    #[error("iterator reported error")]
52    Service,
53}
54
55// Length of a BSS ID. Governed by IEEE standards.
56const BSS_ADDR_LEN_BYTES: usize = 6;
57
58// Upper bound on the number of BSSes cached. Our goals in tuning this value are:
59// * retain enough BSSes to give a tight radius of confidence, and
60// * minimize the potential for exhausting memory
61//
62// This value was chosen somewhat arbitrarily, and may be tuned based on field
63// data about the radius of confidence we achieve.
64const MAX_BSSES: usize = 20;
65
66// Upper bound on the number of IPCs to `ScanResultIterator`. Every non-terminal
67// IPC should yield at least one `ScanResult`. And, in normal operation, we expect
68// that a `ScanResult` will have at least one BSS. Hence, we set this limit to
69// equal the limit on BSSes.
70const MAX_IPCS: usize = MAX_BSSES;
71
72impl RealBssCache {
73    pub fn new() -> Self {
74        Default::default()
75    }
76
77    fn prune_to_size(&mut self) {
78        if let Some(first_overflowed_bssid) = self.bss_map.keys().cloned().nth(MAX_BSSES) {
79            self.bss_map.split_off(&first_overflowed_bssid);
80        }
81    }
82}
83
84#[async_trait(?Send)]
85impl BssCache for RealBssCache {
86    async fn update<I: ScanResultIteratorProxyInterface>(
87        &mut self,
88        new_bsses: I,
89    ) -> Result<(), UpdateError> {
90        let mut iterator_service_result = ScanResultStream::new(new_bsses)
91            .take(MAX_IPCS)
92            .collect::<Vec<Result<Vec<ScanResult>, UpdateError>>>()
93            .await
94            .into_iter()
95            .peekable();
96        // If we have no results, report the appropriate error.
97        match iterator_service_result.peek() {
98            None => return Err(UpdateError::NoBsses), // First IPC yielded empty set.
99            Some(Err(e)) => return Err(*e),           // First IPC yielded error.
100            Some(Ok(_)) => (),
101        };
102
103        let mut bss_list = iterator_service_result
104            .filter_map(|res| res.ok()) // Since we have at least one result, ignore errors.
105            .flatten() // Flatten per-IPC `Vec`s into single `Vec`.
106            .flat_map(|network| network.entries) // Project `Vec` of BSSes out of each network.
107            .flatten() // Flatten per-network `Vec`s of BSSes to single `Vec`.
108            .peekable();
109        if bss_list.peek().is_none() {
110            return Err(UpdateError::NoBsses);
111        };
112
113        let mut valid_bss_list = bss_list
114            .filter_map(|bss: WlanPolicyBss| match bss.bssid {
115                Some(id) => Some((id, Bss { rssi: bss.rssi, frequency: bss.frequency })),
116                None => None,
117            })
118            .peekable();
119        if valid_bss_list.peek().is_none() {
120            return Err(UpdateError::NoBssIds);
121        }
122
123        self.bss_map = valid_bss_list.collect();
124        self.prune_to_size();
125        Ok(())
126    }
127
128    fn iter(&self) -> Box<dyn Iterator<Item = (&'_ BssId, &'_ Bss)> + '_> {
129        Box::new(self.bss_map.iter())
130    }
131}
132
133type GetNextResponse = Result<Result<Vec<ScanResult>, ScanErrorCode>, FidlError>;
134
135// ScanResultStream adapts the FIDL `ScanResultIterator` into a Rust
136// `Stream`.  For more details, see the documentation for
137// `ScanResultStream::poll_next()`, below.
138struct ScanResultStream<'a, F, I>
139where
140    F: Future<Output = GetNextResponse> + Send,
141    I: ScanResultIteratorProxyInterface<GetNextResponseFut = F>,
142{
143    iterator_service: Option<I>,
144    pending_ipc: Option<BoxFuture<'a, F::Output>>,
145}
146
147impl<'a, F, I> ScanResultStream<'a, F, I>
148where
149    F: Future<Output = GetNextResponse> + Send,
150    I: ScanResultIteratorProxyInterface<GetNextResponseFut = F>,
151{
152    fn new(iterator_service: I) -> Self {
153        Self { iterator_service: Some(iterator_service), pending_ipc: None }
154    }
155}
156
157impl<'a, F, I> Unpin for ScanResultStream<'a, F, I>
158where
159    F: Future<Output = GetNextResponse> + Send,
160    I: ScanResultIteratorProxyInterface<GetNextResponseFut = F>,
161{
162}
163
164impl<'a, F, I> Stream for ScanResultStream<'a, F, I>
165where
166    F: Future<Output = GetNextResponse> + Send + 'a,
167    I: ScanResultIteratorProxyInterface<GetNextResponseFut = F>,
168{
169    type Item = Result<Vec<ScanResult>, UpdateError>;
170
171    // Calls through to `ScanResultIterator.GetNext()`.
172    // * If the call yields a `FidlError` or a `ScanErrorCode`, returns an `UpdateError`,
173    //   and ensures that subsequent calls yield None.
174    // * If the call yields an empty `Vec` yields None.
175    // * Otherwise, returns the `Vec` from the call to `GetNext()`.
176    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
177        // Note: we `take()` `iterator_service` here, and only replace
178        // `iterator_service` if
179        // a) the IPC yielded a `ScanResult`, or
180        // b) the IPC is still pending.
181        //
182        // By doing so, we ensure that, if the IPC yielded an empty
183        // result, or an error, then subsequent calls to `poll_next()`
184        // a) will _not_ issue an IPC, and
185        // b) will return None.
186        let iterator_service = match self.iterator_service.take() {
187            Some(is) => is,
188            None => return Poll::Ready(None),
189        };
190        let mut fut = match self.pending_ipc.take() {
191            Some(ipc) => ipc,
192            None => iterator_service.get_next().boxed(),
193        };
194        match fut.poll_unpin(cx) {
195            Poll::Pending => {
196                self.pending_ipc = Some(fut);
197                self.iterator_service = Some(iterator_service);
198                Poll::Pending
199            }
200            Poll::Ready(fidl_result) => Poll::Ready(match flatten_get_next_error(fidl_result) {
201                Ok(res) => {
202                    if res.is_empty() {
203                        None
204                    } else {
205                        self.iterator_service = Some(iterator_service);
206                        Some(Ok(res))
207                    }
208                }
209                Err(e) => Some(Err(e)),
210            }),
211        }
212    }
213}
214
215fn flatten_get_next_error(fidl_result: GetNextResponse) -> Result<Vec<ScanResult>, UpdateError> {
216    match fidl_result {
217        Ok(service_result) => match service_result {
218            Ok(scan_results) => Ok(scan_results),
219            Err(_) => Err(UpdateError::Service),
220        },
221        Err(_) => Err(UpdateError::Ipc),
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    mod single_call_success {
228        use super::super::*;
229        use assert_matches::assert_matches;
230        use fidl_fuchsia_wlan_policy::Compatibility::Supported;
231        use fidl_fuchsia_wlan_policy::NetworkIdentifier;
232        use fidl_fuchsia_wlan_policy::SecurityType::Wpa2;
233        use fuchsia_async as fasync;
234        use test_doubles::FakeScanResultIterator;
235
236        #[fasync::run_until_stalled(test)]
237        async fn caches_single_bss_with_just_bss_data() {
238            let mut cache = RealBssCache::new();
239            let result = cache
240                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
241                    id: None,
242                    entries: Some(vec![WlanPolicyBss {
243                        bssid: Some([0, 1, 2, 3, 4, 5]),
244                        rssi: None,
245                        frequency: None,
246                        timestamp_nanos: None,
247                        ..Default::default()
248                    }]),
249                    compatibility: None,
250                    ..Default::default()
251                }]))
252                .await;
253            assert_eq!(result, Ok(()));
254            assert_eq!(
255                cache.iter().next(),
256                Some((&[0, 1, 2, 3, 4, 5], &Bss { rssi: None, frequency: None }))
257            );
258        }
259
260        #[fasync::run_until_stalled(test)]
261        async fn caches_single_bss_with_all_data() {
262            let mut cache = RealBssCache::new();
263            let result = cache
264                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
265                    id: Some(NetworkIdentifier { ssid: vec![b'a'], type_: Wpa2 }),
266                    entries: Some(vec![WlanPolicyBss {
267                        bssid: Some([0, 1, 2, 3, 4, 5]),
268                        rssi: Some(-1),
269                        frequency: Some(2412),
270                        timestamp_nanos: Some(1),
271                        ..Default::default()
272                    }]),
273                    compatibility: Some(Supported),
274                    ..Default::default()
275                }]))
276                .await;
277            assert_eq!(result, Ok(()));
278            assert_eq!(
279                cache.iter().next(),
280                Some((&[0, 1, 2, 3, 4, 5], &Bss { rssi: Some(-1), frequency: Some(2412) }))
281            );
282        }
283
284        #[fasync::run_until_stalled(test)]
285        async fn caches_multiple_bsses_from_single_network() {
286            let mut cache = RealBssCache::new();
287            let result = cache
288                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
289                    id: None,
290                    entries: Some(vec![
291                        WlanPolicyBss {
292                            bssid: Some([0, 0, 0, 0, 0, 0]),
293                            rssi: None,
294                            frequency: None,
295                            timestamp_nanos: None,
296                            ..Default::default()
297                        },
298                        WlanPolicyBss {
299                            bssid: Some([1, 1, 1, 1, 1, 1]),
300                            rssi: None,
301                            frequency: None,
302                            timestamp_nanos: None,
303                            ..Default::default()
304                        },
305                    ]),
306                    compatibility: None,
307                    ..Default::default()
308                }]))
309                .await;
310            assert_eq!(result, Ok(()));
311
312            let bsses: BTreeMap<&BssId, &Bss> = cache.iter().collect();
313            assert_eq!(bsses.get(&[0, 0, 0, 0, 0, 0]), Some(&&Bss { rssi: None, frequency: None }));
314            assert_eq!(bsses.get(&[1, 1, 1, 1, 1, 1]), Some(&&Bss { rssi: None, frequency: None }));
315        }
316
317        #[fasync::run_until_stalled(test)]
318        async fn deduplicates_bsses_from_single_network() {
319            let mut cache = RealBssCache::new();
320            let result = cache
321                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
322                    id: None,
323                    entries: Some(vec![
324                        WlanPolicyBss {
325                            bssid: Some([0, 1, 2, 3, 4, 5]),
326                            rssi: Some(-1),
327                            frequency: Some(2412),
328                            timestamp_nanos: Some(1),
329                            ..Default::default()
330                        },
331                        WlanPolicyBss {
332                            bssid: Some([0, 1, 2, 3, 4, 5]),
333                            rssi: Some(-2),
334                            frequency: Some(2432),
335                            timestamp_nanos: Some(2),
336                            ..Default::default()
337                        },
338                    ]),
339                    compatibility: None,
340                    ..Default::default()
341                }]))
342                .await;
343            assert_eq!(result, Ok(()));
344
345            let mut bsses = cache.iter();
346            assert_matches!(bsses.next(), Some((&[0, 1, 2, 3, 4, 5], _)));
347            assert_eq!(bsses.next(), None);
348        }
349
350        #[fasync::run_until_stalled(test)]
351        async fn caches_multiple_bsses_from_multiple_networks() {
352            let mut cache = RealBssCache::new();
353            let result = cache
354                .update(FakeScanResultIterator::new_single_step(vec![
355                    ScanResult {
356                        id: None,
357                        entries: Some(vec![WlanPolicyBss {
358                            bssid: Some([0, 0, 0, 0, 0, 0]),
359                            rssi: None,
360                            frequency: None,
361                            timestamp_nanos: None,
362                            ..Default::default()
363                        }]),
364                        compatibility: None,
365                        ..Default::default()
366                    },
367                    ScanResult {
368                        id: None,
369                        entries: Some(vec![WlanPolicyBss {
370                            bssid: Some([1, 1, 1, 1, 1, 1]),
371                            rssi: None,
372                            frequency: None,
373                            timestamp_nanos: None,
374                            ..Default::default()
375                        }]),
376                        compatibility: None,
377                        ..Default::default()
378                    },
379                ]))
380                .await;
381            assert_eq!(result, Ok(()));
382
383            let bsses: BTreeMap<&BssId, &Bss> = cache.iter().collect();
384            assert_eq!(bsses.get(&[0, 0, 0, 0, 0, 0]), Some(&&Bss { rssi: None, frequency: None }));
385            assert_eq!(bsses.get(&[1, 1, 1, 1, 1, 1]), Some(&&Bss { rssi: None, frequency: None }));
386        }
387
388        #[fasync::run_until_stalled(test)]
389        async fn deduplicates_bsses_from_multiple_networks() {
390            let mut cache = RealBssCache::new();
391            let result = cache
392                .update(FakeScanResultIterator::new_single_step(vec![
393                    ScanResult {
394                        id: None,
395                        entries: Some(vec![WlanPolicyBss {
396                            bssid: Some([0, 1, 2, 3, 4, 5]),
397                            rssi: Some(-1),
398                            frequency: Some(2412),
399                            timestamp_nanos: Some(1),
400                            ..Default::default()
401                        }]),
402                        compatibility: None,
403                        ..Default::default()
404                    },
405                    ScanResult {
406                        id: None,
407                        entries: Some(vec![WlanPolicyBss {
408                            bssid: Some([0, 1, 2, 3, 4, 5]),
409                            rssi: Some(-2),
410                            frequency: Some(2432),
411                            timestamp_nanos: Some(2),
412                            ..Default::default()
413                        }]),
414                        compatibility: None,
415                        ..Default::default()
416                    },
417                ]))
418                .await;
419            assert_eq!(result, Ok(()));
420
421            let mut bsses = cache.iter();
422            assert_matches!(bsses.next(), Some((&[0, 1, 2, 3, 4, 5], _)));
423            assert_eq!(bsses.next(), None);
424        }
425
426        #[fasync::run_until_stalled(test)]
427        async fn honors_max_bss_limit() {
428            let mut cache = RealBssCache::new();
429            let bsses: Vec<_> = (0..MAX_BSSES + 1)
430                .map(|i| WlanPolicyBss {
431                    bssid: Some(
432                        BssId::try_from(&i.to_le_bytes()[0..BSS_ADDR_LEN_BYTES])
433                            .expect("internal error"),
434                    ),
435                    rssi: None,
436                    frequency: None,
437                    timestamp_nanos: None,
438                    ..Default::default()
439                })
440                .collect();
441            let scan_results = vec![ScanResult {
442                id: None,
443                entries: Some(bsses),
444                compatibility: None,
445                ..Default::default()
446            }];
447            let _ = cache.update(FakeScanResultIterator::new_single_step(scan_results)).await;
448            assert_eq!(cache.iter().count(), MAX_BSSES);
449        }
450
451        #[fasync::run_until_stalled(test)]
452        async fn does_not_count_bad_bsses_toward_max_bss_limit() {
453            let mut cache = RealBssCache::new();
454            let bad_bss = std::iter::once(WlanPolicyBss {
455                bssid: None,
456                rssi: None,
457                frequency: None,
458                timestamp_nanos: None,
459                ..Default::default()
460            });
461            let good_bsses = (0..MAX_BSSES).map(|i| WlanPolicyBss {
462                bssid: Some(
463                    BssId::try_from(&i.to_le_bytes()[0..BSS_ADDR_LEN_BYTES])
464                        .expect("internal error"),
465                ),
466                rssi: None,
467                frequency: None,
468                timestamp_nanos: None,
469                ..Default::default()
470            });
471            let bsses: Vec<_> = bad_bss.chain(good_bsses).collect();
472            let scan_results = vec![ScanResult {
473                id: None,
474                entries: Some(bsses),
475                compatibility: None,
476                ..Default::default()
477            }];
478            let _ = cache.update(FakeScanResultIterator::new_single_step(scan_results)).await;
479            assert_eq!(cache.iter().count(), MAX_BSSES);
480        }
481
482        #[fasync::run_until_stalled(test)]
483        async fn does_not_count_duplicate_bsses_toward_max_bss_limit() {
484            let mut cache = RealBssCache::new();
485            let duplicate_bsses = vec![
486                WlanPolicyBss {
487                    bssid: Some([0, 0, 0, 0, 0, 0]),
488                    rssi: None,
489                    frequency: None,
490                    timestamp_nanos: None,
491                    ..Default::default()
492                },
493                WlanPolicyBss {
494                    bssid: Some([0, 0, 0, 0, 0, 0]),
495                    rssi: None,
496                    frequency: None,
497                    timestamp_nanos: None,
498                    ..Default::default()
499                },
500            ];
501            let unique_bsses = (1..MAX_BSSES).map(|i| WlanPolicyBss {
502                bssid: Some(
503                    BssId::try_from(&i.to_le_bytes()[0..BSS_ADDR_LEN_BYTES])
504                        .expect("internal error"),
505                ),
506                rssi: None,
507                frequency: None,
508                timestamp_nanos: None,
509                ..Default::default()
510            });
511            let bsses: Vec<_> = duplicate_bsses.into_iter().chain(unique_bsses).collect();
512            let scan_results = vec![ScanResult {
513                id: None,
514                entries: Some(bsses),
515                compatibility: None,
516                ..Default::default()
517            }];
518            let _ = cache.update(FakeScanResultIterator::new_single_step(scan_results)).await;
519            assert_eq!(cache.iter().count(), MAX_BSSES);
520        }
521    }
522
523    mod single_call_failure {
524        use super::super::*;
525        use fuchsia_async as fasync;
526        use test_doubles::{FakeScanResultIterator, StubScanResultIterator};
527
528        #[fasync::run_until_stalled(test)]
529        async fn returns_ipc_error_on_fidl_error() {
530            assert_eq!(
531                RealBssCache::new()
532                    .update(StubScanResultIterator::new(|| Err(fidl::Error::InvalidHeader)))
533                    .await,
534                Err(UpdateError::Ipc)
535            );
536        }
537
538        #[fasync::run_until_stalled(test)]
539        async fn returns_service_error_on_general_scan_error() {
540            assert_eq!(
541                RealBssCache::new()
542                    .update(StubScanResultIterator::new(|| Ok(Err(ScanErrorCode::GeneralError))))
543                    .await,
544                Err(UpdateError::Service)
545            );
546        }
547
548        #[fasync::run_until_stalled(test)]
549        async fn returns_no_bsses_error_on_empty_scan_results() {
550            assert_eq!(
551                RealBssCache::new().update(FakeScanResultIterator::new_single_step(vec![])).await,
552                Err(UpdateError::NoBsses)
553            );
554        }
555
556        #[fasync::run_until_stalled(test)]
557        async fn returns_no_bsses_error_on_network_without_entries_vector() {
558            assert_eq!(
559                RealBssCache::new()
560                    .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
561                        id: None,
562                        entries: None,
563                        compatibility: None,
564                        ..Default::default()
565                    }]))
566                    .await,
567                Err(UpdateError::NoBsses)
568            );
569        }
570
571        #[fasync::run_until_stalled(test)]
572        async fn returns_no_bsses_error_on_network_with_empty_entries_vector() {
573            assert_eq!(
574                RealBssCache::new()
575                    .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
576                        id: None,
577                        entries: Some(Vec::new()),
578                        compatibility: None,
579                        ..Default::default()
580                    }]))
581                    .await,
582                Err(UpdateError::NoBsses)
583            );
584        }
585
586        #[fasync::run_until_stalled(test)]
587        async fn returns_no_bss_ids_error_on_bss_without_bssid() {
588            assert_eq!(
589                RealBssCache::new()
590                    .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
591                        id: None,
592                        entries: Some(vec![WlanPolicyBss {
593                            bssid: None,
594                            rssi: Some(-1),
595                            frequency: Some(2414),
596                            timestamp_nanos: Some(1),
597                            ..Default::default()
598                        }]),
599                        compatibility: None,
600                        ..Default::default()
601                    }]))
602                    .await,
603                Err(UpdateError::NoBssIds),
604            );
605        }
606    }
607
608    mod multiple_calls {
609        use super::super::*;
610        use fidl_fuchsia_wlan_policy::Compatibility::Supported;
611        use fidl_fuchsia_wlan_policy::NetworkIdentifier;
612        use fidl_fuchsia_wlan_policy::SecurityType::Wpa2;
613        use fuchsia_async as fasync;
614        use test_doubles::FakeScanResultIterator;
615
616        #[fasync::run_until_stalled(test)]
617        async fn is_non_empty_after_new_non_empty_data() {
618            let mut cache = RealBssCache::new();
619            let _ = cache
620                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
621                    id: None,
622                    entries: Some(vec![WlanPolicyBss {
623                        bssid: Some([0, 0, 0, 0, 0, 0]),
624                        rssi: None,
625                        frequency: None,
626                        timestamp_nanos: None,
627                        ..Default::default()
628                    }]),
629                    compatibility: None,
630                    ..Default::default()
631                }]))
632                .await;
633            let _ = cache
634                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
635                    id: None,
636                    entries: Some(vec![WlanPolicyBss {
637                        bssid: Some([1, 1, 1, 1, 1, 1]),
638                        rssi: None,
639                        frequency: None,
640                        timestamp_nanos: None,
641                        ..Default::default()
642                    }]),
643                    compatibility: None,
644                    ..Default::default()
645                }]))
646                .await;
647
648            // Note: we refrain from making a stronger assertion
649            // (e.g. that only the new BSS is retained), to avoid
650            // needing to revise this test if we change the caching
651            // policy in the future.
652            //
653            // Said differently, we believe that
654            // a) the assertion below will hold true under any reasonable
655            //    caching policy, and
656            // b) there is no pressing need to validate the more specific
657            //    behavior of the current caching policy.
658            assert!(cache.iter().next().is_some());
659        }
660
661        #[fasync::run_until_stalled(test)]
662        async fn is_non_empty_after_new_empty_data() {
663            let mut cache = RealBssCache::new();
664            let _ = cache
665                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
666                    id: None,
667                    entries: Some(vec![WlanPolicyBss {
668                        bssid: Some([0, 0, 0, 0, 0, 0]),
669                        rssi: None,
670                        frequency: None,
671                        timestamp_nanos: None,
672                        ..Default::default()
673                    }]),
674                    compatibility: None,
675                    ..Default::default()
676                }]))
677                .await;
678
679            // Note: we populate everything except `entries.bssid`, to
680            // ensure that the implementation doesn't short-circuit
681            // due to any other data being missing.
682            let _ = cache
683                .update(FakeScanResultIterator::new_single_step(vec![ScanResult {
684                    id: Some(NetworkIdentifier { ssid: vec![b'a'], type_: Wpa2 }),
685                    entries: Some(vec![WlanPolicyBss {
686                        bssid: None,
687                        rssi: Some(-1),
688                        frequency: Some(2412),
689                        timestamp_nanos: Some(1),
690                        ..Default::default()
691                    }]),
692                    compatibility: Some(Supported),
693                    ..Default::default()
694                }]))
695                .await;
696
697            // Note: for now, we make the assumption that having
698            // _some_ location information is better than having none,
699            // even if the data we have is old. If this changes, we
700            // should remove this test.
701            assert!(cache.iter().next().is_some());
702        }
703    }
704
705    mod multi_step_iteration {
706        use super::super::*;
707        use fuchsia_async as fasync;
708        use test_doubles::FakeScanResultIterator;
709
710        #[fasync::run_until_stalled(test)]
711        async fn reads_all_scan_results() {
712            let mut cache = RealBssCache::new();
713            let result = cache
714                .update(FakeScanResultIterator::new_multi_step(vec![
715                    vec![ScanResult {
716                        id: None,
717                        entries: Some(vec![WlanPolicyBss {
718                            bssid: Some([0, 0, 0, 0, 0, 0]),
719                            rssi: None,
720                            frequency: None,
721                            timestamp_nanos: None,
722                            ..Default::default()
723                        }]),
724                        compatibility: None,
725                        ..Default::default()
726                    }],
727                    vec![ScanResult {
728                        id: None,
729                        entries: Some(vec![WlanPolicyBss {
730                            bssid: Some([1, 1, 1, 1, 1, 1]),
731                            rssi: None,
732                            frequency: None,
733                            timestamp_nanos: None,
734                            ..Default::default()
735                        }]),
736                        compatibility: None,
737                        ..Default::default()
738                    }],
739                ]))
740                .await;
741            assert_eq!(result, Ok(()));
742            assert_eq!(2, cache.iter().count());
743        }
744
745        #[fasync::run_until_stalled(test)]
746        async fn finds_later_bsses_even_if_first_iteration_yields_no_bsses() {
747            let mut cache = RealBssCache::new();
748            let result = cache
749                .update(FakeScanResultIterator::new_multi_step(vec![
750                    vec![ScanResult {
751                        id: None,
752                        entries: None,
753                        compatibility: None,
754                        ..Default::default()
755                    }],
756                    vec![ScanResult {
757                        id: None,
758                        entries: Some(vec![WlanPolicyBss {
759                            bssid: Some([0, 0, 0, 0, 0, 0]),
760                            rssi: None,
761                            frequency: None,
762                            timestamp_nanos: None,
763                            ..Default::default()
764                        }]),
765                        compatibility: None,
766                        ..Default::default()
767                    }],
768                ]))
769                .await;
770            assert_eq!(result, Ok(()));
771            assert_eq!(1, cache.iter().count());
772        }
773
774        #[fasync::run_until_stalled(test)]
775        async fn finds_later_bss_ids_even_if_first_iteration_yields_no_bss_ids() {
776            let mut cache = RealBssCache::new();
777            let result = cache
778                .update(FakeScanResultIterator::new_multi_step(vec![
779                    vec![ScanResult {
780                        id: None,
781                        entries: Some(vec![WlanPolicyBss {
782                            bssid: None,
783                            rssi: None,
784                            frequency: None,
785                            timestamp_nanos: None,
786                            ..Default::default()
787                        }]),
788
789                        compatibility: None,
790                        ..Default::default()
791                    }],
792                    vec![ScanResult {
793                        id: None,
794                        entries: Some(vec![WlanPolicyBss {
795                            bssid: Some([0, 0, 0, 0, 0, 0]),
796                            rssi: None,
797                            frequency: None,
798                            timestamp_nanos: None,
799                            ..Default::default()
800                        }]),
801                        compatibility: None,
802                        ..Default::default()
803                    }],
804                ]))
805                .await;
806            assert_eq!(result, Ok(()));
807            assert_eq!(1, cache.iter().count());
808        }
809    }
810
811    mod ipc_interactions {
812        use super::super::*;
813        use fidl_fuchsia_wlan_policy::NetworkIdentifier;
814        use fidl_fuchsia_wlan_policy::SecurityType::Wpa2;
815        use fuchsia_async as fasync;
816        use test_doubles::{RawStubScanResultIterator, StubScanResultIterator};
817
818        #[fasync::run_until_stalled(test)]
819        async fn stops_sending_ipcs_when_get_next_yields_fidl_error() {
820            let mut cache = RealBssCache::new();
821            let mut scan_results = vec![Err(fidl::Error::InvalidHeader)].into_iter();
822            let _ = cache
823                .update(StubScanResultIterator::new(|| {
824                    scan_results.next().expect("already consumed all `scan_results`")
825                }))
826                .await;
827        }
828
829        #[fasync::run_until_stalled(test)]
830        async fn stops_sending_ipcs_when_get_next_yields_scan_error() {
831            let mut cache = RealBssCache::new();
832            let mut scan_results = vec![Ok(Err(ScanErrorCode::GeneralError))].into_iter();
833            let _ = cache
834                .update(StubScanResultIterator::new(|| {
835                    scan_results.next().expect("already consumed all `scan_results`")
836                }))
837                .await;
838        }
839
840        #[fasync::run_until_stalled(test)]
841        async fn stops_sending_ipcs_when_get_next_yields_empty_vec() {
842            let mut cache = RealBssCache::new();
843            let mut scan_results = vec![Ok(Ok(vec![]))].into_iter();
844            let _ = cache
845                .update(StubScanResultIterator::new(|| {
846                    scan_results.next().expect("already consumed all `scan_results`")
847                }))
848                .await;
849        }
850
851        #[fasync::run_until_stalled(test)]
852        async fn drives_pending_ipc_to_completion() {
853            let mut cache = RealBssCache::new();
854            let mut poll_results = vec![Poll::Pending, Poll::Ready(Ok(Ok(vec![])))].into_iter();
855            let mut futures = vec![futures::future::poll_fn(|cx| {
856                let res = poll_results.next().expect("already consumed all `poll_results`");
857                cx.waker().wake_by_ref();
858                res
859            })]
860            .into_iter();
861            let _ = cache
862                .update(RawStubScanResultIterator::new(|| {
863                    futures.next().expect("already consumed all `futures`")
864                }))
865                .await;
866        }
867
868        #[fasync::run_until_stalled(test)]
869        async fn honors_max_ipc_limit() {
870            let mut cache = RealBssCache::new();
871            let mut scan_results = (0..MAX_IPCS)
872                .map(|i| {
873                    Ok(Ok(vec![ScanResult {
874                        id: Some(NetworkIdentifier { ssid: i.to_le_bytes().to_vec(), type_: Wpa2 }),
875                        entries: Some(vec![WlanPolicyBss {
876                            bssid: Some(
877                                BssId::try_from(&i.to_le_bytes()[0..BSS_ADDR_LEN_BYTES])
878                                    .expect("internal error"),
879                            ),
880                            rssi: None,
881                            frequency: None,
882                            timestamp_nanos: None,
883                            ..Default::default()
884                        }]),
885                        compatibility: None,
886                        ..Default::default()
887                    }]))
888                })
889                .collect::<Vec<_>>()
890                .into_iter();
891            let _ = cache
892                .update(StubScanResultIterator::new(|| {
893                    scan_results.next().expect("already consumed all `scan_results`")
894                }))
895                .await;
896        }
897    }
898}
899
900#[cfg(test)]
901mod test_doubles {
902    use super::*;
903    use futures::future::{ready, Ready};
904    use std::sync::RwLock;
905
906    // Test double that returns scan results from initially provided data.
907    // After exhausting the initial data, perpetually returns an empty `Vec`.
908    // Useful for testing success cases.
909    pub(super) struct FakeScanResultIterator {
910        // Why do we need an RwLock here?
911        //
912        // 1) We need to return a `Vec<ScanResult>`
913        // 2) `ScanResult` is not `Copy` or `Clone`
914        // 3) Given 1 and 2, `get_next()` needs to move data
915        //
916        // Given just the constraints above, we might consider `Cell` or `RefCell`. However,
917        // `ScanResultIteratorProxyInterface` is `Sync`, while `Cell` and `RefCell` are not.
918        scan_results: RwLock<Vec<Vec<ScanResult>>>,
919    }
920
921    // Test double that invokes a function which yields a `GetNextResponse`.
922    // Useful for testing error handling.
923    pub(super) struct StubScanResultIterator<F: FnMut() -> GetNextResponse>(RwLock<F>);
924
925    // Test double that invokes a function with yields a `GetNextResponse` `Future`.
926    // Useful for testing interaction with asynchronous IPCs.
927    pub(super) struct RawStubScanResultIterator<F, R>(RwLock<F>)
928    where
929        F: FnMut() -> R,
930        R: Future<Output = GetNextResponse>;
931
932    impl FakeScanResultIterator {
933        // Returns an iterator which yields `scan_results` all at once.
934        pub(super) fn new_single_step(scan_results: Vec<ScanResult>) -> Self {
935            Self::new_multi_step(vec![scan_results])
936        }
937
938        // Returns an iterator which yields one element of `scan_results` at a time.
939        // Note, however, that each element is _itself_ a `Vec<ScanResult>`.
940        pub(super) fn new_multi_step(scan_results: Vec<Vec<ScanResult>>) -> Self {
941            Self { scan_results: RwLock::new(scan_results) }
942        }
943    }
944
945    impl ScanResultIteratorProxyInterface for FakeScanResultIterator {
946        type GetNextResponseFut = Ready<GetNextResponse>;
947
948        fn get_next(&self) -> Self::GetNextResponseFut {
949            let mut scan_results = self.scan_results.write().expect("internal error");
950            ready(Ok(Ok(if scan_results.is_empty() { Vec::new() } else { scan_results.remove(0) })))
951        }
952    }
953
954    impl<F: FnMut() -> GetNextResponse + Send + Sync> StubScanResultIterator<F> {
955        pub(super) fn new(get_next: F) -> Self {
956            Self(RwLock::new(get_next))
957        }
958    }
959
960    impl<F: FnMut() -> GetNextResponse + Send + Sync> ScanResultIteratorProxyInterface
961        for StubScanResultIterator<F>
962    {
963        type GetNextResponseFut = Ready<GetNextResponse>;
964
965        fn get_next(&self) -> Self::GetNextResponseFut {
966            // Note: the `&mut *` here is due to https://github.com/rust-lang/rust/issues/65489
967            let response_func = &mut *self.0.write().expect("internal error");
968            ready(response_func())
969        }
970    }
971
972    impl<F, R> RawStubScanResultIterator<F, R>
973    where
974        F: FnMut() -> R + Send + Sync,
975        R: Future<Output = GetNextResponse> + Send,
976    {
977        pub(super) fn new(get_next: F) -> Self {
978            Self(RwLock::new(get_next))
979        }
980    }
981
982    impl<F, R> ScanResultIteratorProxyInterface for RawStubScanResultIterator<F, R>
983    where
984        F: FnMut() -> R + Send + Sync,
985        R: Future<Output = GetNextResponse> + Send,
986    {
987        type GetNextResponseFut = R;
988
989        fn get_next(&self) -> Self::GetNextResponseFut {
990            // Note: the `&mut *` here is due to https://github.com/rust-lang/rust/issues/65489
991            let response_func = &mut *self.0.write().expect("internal error");
992            response_func()
993        }
994    }
995
996    mod tests {
997        mod fake_scan_result_iterator {
998            use super::super::*;
999            use fuchsia_async as fasync;
1000
1001            #[fasync::run_until_stalled(test)]
1002            async fn single_step_yields_all_scan_results_at_once() {
1003                let iter = FakeScanResultIterator::new_single_step(vec![
1004                    ScanResult {
1005                        id: None,
1006                        entries: None,
1007                        compatibility: None,
1008                        ..Default::default()
1009                    },
1010                    ScanResult {
1011                        id: None,
1012                        entries: None,
1013                        compatibility: None,
1014                        ..Default::default()
1015                    },
1016                ]);
1017                assert_eq!(2, iter.get_next().await.unwrap().unwrap().len());
1018            }
1019
1020            #[fasync::run_until_stalled(test)]
1021            async fn initially_empty_iterator_yields_empty_vec() {
1022                let iter = FakeScanResultIterator::new_single_step(Vec::new());
1023                assert_eq!(Vec::<ScanResult>::new(), iter.get_next().await.unwrap().unwrap());
1024            }
1025
1026            #[fasync::run_until_stalled(test)]
1027            async fn emptied_iterator_yields_empty_vec() {
1028                let iter = FakeScanResultIterator::new_single_step(vec![ScanResult {
1029                    id: None,
1030                    entries: None,
1031                    compatibility: None,
1032                    ..Default::default()
1033                }]);
1034                let _ = iter.get_next().await.unwrap().unwrap();
1035                assert_eq!(Vec::<ScanResult>::new(), iter.get_next().await.unwrap().unwrap());
1036            }
1037
1038            #[fasync::run_until_stalled(test)]
1039            async fn multi_step_yields_scan_results_iteratively() {
1040                let iter = FakeScanResultIterator::new_multi_step(vec![
1041                    vec![ScanResult {
1042                        id: None,
1043                        entries: None,
1044                        compatibility: None,
1045                        ..Default::default()
1046                    }],
1047                    vec![ScanResult {
1048                        id: None,
1049                        entries: None,
1050                        compatibility: None,
1051                        ..Default::default()
1052                    }],
1053                ]);
1054                assert_eq!(1, iter.get_next().await.unwrap().unwrap().len());
1055                assert_eq!(1, iter.get_next().await.unwrap().unwrap().len());
1056                assert_eq!(0, iter.get_next().await.unwrap().unwrap().len());
1057            }
1058        }
1059    }
1060}