1use 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 async fn update<I: ScanResultIteratorProxyInterface>(
21 &mut self,
22 new_bsses: I,
23 ) -> Result<(), UpdateError>;
24
25 fn iter(&self) -> Box<dyn Iterator<Item = (&'_ BssId, &'_ Bss)> + '_>;
27}
28
29#[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
55const BSS_ADDR_LEN_BYTES: usize = 6;
57
58const MAX_BSSES: usize = 20;
65
66const 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 match iterator_service_result.peek() {
98 None => return Err(UpdateError::NoBsses), Some(Err(e)) => return Err(*e), Some(Ok(_)) => (),
101 };
102
103 let mut bss_list = iterator_service_result
104 .filter_map(|res| res.ok()) .flatten() .flat_map(|network| network.entries) .flatten() .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
135struct 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 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
177 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 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 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 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 pub(super) struct FakeScanResultIterator {
910 scan_results: RwLock<Vec<Vec<ScanResult>>>,
919 }
920
921 pub(super) struct StubScanResultIterator<F: FnMut() -> GetNextResponse>(RwLock<F>);
924
925 pub(super) struct RawStubScanResultIterator<F, R>(RwLock<F>)
928 where
929 F: FnMut() -> R,
930 R: Future<Output = GetNextResponse>;
931
932 impl FakeScanResultIterator {
933 pub(super) fn new_single_step(scan_results: Vec<ScanResult>) -> Self {
935 Self::new_multi_step(vec![scan_results])
936 }
937
938 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 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 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}