1use crate::client::types;
6use anyhow::{Error, format_err};
7use fidl::prelude::*;
8use fidl_fuchsia_wlan_policy as fidl_policy;
9use fidl_fuchsia_wlan_sme as fidl_sme;
10use futures::stream::TryStreamExt;
11use ieee80211::MacAddrBytes;
12use log::{debug, info};
13use measure_tape_for_scan_result::Measurable as _;
14
15const FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE: usize = 56;
18
19fn fidl_security_from_sme_protection(
24 protection: fidl_sme::Protection,
25 wpa3_supported: bool,
26) -> Option<fidl_policy::SecurityType> {
27 use fidl_policy::SecurityType;
28 use fidl_sme::Protection::*;
29 match protection {
30 Wpa3Enterprise | Wpa3Personal | Wpa2Wpa3Personal => {
31 Some(if wpa3_supported { SecurityType::Wpa3 } else { SecurityType::Wpa2 })
32 }
33 Wpa2Enterprise
34 | Wpa2Personal
35 | Wpa1Wpa2Personal
36 | Wpa2PersonalTkipOnly
37 | Wpa1Wpa2PersonalTkipOnly => Some(SecurityType::Wpa2),
38 Wpa1 => Some(SecurityType::Wpa),
39 Wep => Some(SecurityType::Wep),
40 Owe => Some(SecurityType::None),
42 OpenOweTransition => Some(SecurityType::None),
43 Open => Some(SecurityType::None),
44 Unknown => None,
45 }
46}
47
48#[allow(clippy::ptr_arg, reason = "mass allow for https://fxbug.dev/381896734")]
49pub fn scan_result_to_policy_scan_result(
50 internal_results: &Vec<types::ScanResult>,
51) -> Vec<fidl_policy::ScanResult> {
52 let scan_results: Vec<fidl_policy::ScanResult> = internal_results
53 .iter()
54 .filter_map(|internal| {
55 let wpa3_supported = internal.entries.iter().any(|bss| {
57 if let Ok(compatible) = &bss.compatibility {
58 compatible
59 .mutual_security_protocols()
60 .contains(&wlan_common::security::SecurityDescriptor::WPA3_PERSONAL)
61 } else {
62 false
63 }
64 });
65
66 if let Some(security) =
67 fidl_security_from_sme_protection(internal.security_type_detailed, wpa3_supported)
68 {
69 Some(fidl_policy::ScanResult {
70 id: Some(fidl_policy::NetworkIdentifier {
71 ssid: internal.ssid.to_vec(),
72 type_: security,
73 }),
74 entries: Some(
75 internal
76 .entries
77 .iter()
78 .map(|input| {
79 let frequency = input.channel.get_center_freq().unwrap_or(0);
83 fidl_policy::Bss {
84 bssid: Some(input.bssid.to_array()),
85 rssi: Some(input.signal.rssi_dbm),
86 frequency: Some(frequency.into()), timestamp_nanos: Some(input.timestamp.into_nanos()),
88 ..Default::default()
89 }
90 })
91 .collect(),
92 ),
93 compatibility: Some(internal.compatibility),
94 ..Default::default()
95 })
96 } else {
97 debug!(
98 "Unknown security type present in scan results ({} BSSs)",
99 internal.entries.len()
100 );
101 None
102 }
103 })
104 .collect();
105
106 scan_results
107}
108
109pub async fn send_scan_results_over_fidl(
112 output_iterator: fidl::endpoints::ServerEnd<fidl_policy::ScanResultIteratorMarker>,
113 mut scan_results: &[fidl_policy::ScanResult],
114) -> Result<(), Error> {
115 let (mut stream, ctrl) = output_iterator.into_stream_and_control_handle();
117 let mut sent_some_results = false;
118
119 loop {
121 if let Some(fidl_policy::ScanResultIteratorRequest::GetNext { responder }) =
122 stream.try_next().await?
123 {
124 let mut bytes_used = FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE;
125 let mut result_count = 0;
126 for result in scan_results {
127 bytes_used += result.measure().num_bytes;
128 if bytes_used > zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize {
129 if result_count == 0 {
130 return Err(format_err!("Single scan result too large to send via FIDL"));
131 }
132 break;
134 }
135 result_count += 1;
136 }
137 #[expect(clippy::indexing_slicing)]
140 responder.send(Ok(&scan_results[..result_count]))?;
141 #[expect(clippy::indexing_slicing)]
144 let remaining_results = &scan_results[result_count..];
145 scan_results = remaining_results;
146 sent_some_results = true;
147
148 if result_count == 0 {
150 ctrl.shutdown();
151 return Ok(());
152 }
153 } else {
154 if sent_some_results {
157 debug!("Scan result consumer closed channel before consuming all scan results");
160 return Ok(());
161 }
162 return Err(format_err!("Peer closed channel before receiving any scan results"));
163 }
164 }
165}
166
167pub async fn send_scan_error_over_fidl(
170 output_iterator: fidl::endpoints::ServerEnd<fidl_policy::ScanResultIteratorMarker>,
171 error_code: types::ScanError,
172) -> Result<(), fidl::Error> {
173 let (mut stream, ctrl) = output_iterator.into_stream_and_control_handle();
175 if let Some(req) = stream.try_next().await? {
176 let fidl_policy::ScanResultIteratorRequest::GetNext { responder } = req;
177 responder.send(Err(error_code))?;
178 ctrl.shutdown();
179 } else {
180 info!("Peer closed channel for getting scan results unexpectedly");
183 }
184 Ok(())
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use assert_matches::assert_matches;
191 use fidl_fuchsia_wlan_ieee80211::WlanBand::TwoGhz;
192 use fuchsia_async as fasync;
193 use futures::task::Poll;
194 use std::pin::pin;
195 use wlan_common::random_fidl_bss_description;
196 use wlan_common::scan::{Compatible, Incompatible};
197 use wlan_common::security::SecurityDescriptor;
198
199 fn generate_test_fidl_data() -> Vec<fidl_policy::ScanResult> {
200 const CENTER_FREQ_CHAN_1: u32 = 2412;
201 const CENTER_FREQ_CHAN_8: u32 = 2447;
202 const CENTER_FREQ_CHAN_11: u32 = 2462;
203 vec![
204 fidl_policy::ScanResult {
205 id: Some(fidl_policy::NetworkIdentifier {
206 ssid: types::Ssid::try_from("duplicated ssid").unwrap().into(),
207 type_: fidl_policy::SecurityType::Wpa3,
208 }),
209 entries: Some(vec![
210 fidl_policy::Bss {
211 bssid: Some([0, 0, 0, 0, 0, 0]),
212 rssi: Some(0),
213 frequency: Some(CENTER_FREQ_CHAN_1),
214 timestamp_nanos: Some(zx::MonotonicInstant::get().into_nanos()),
215 ..Default::default()
216 },
217 fidl_policy::Bss {
218 bssid: Some([7, 8, 9, 10, 11, 12]),
219 rssi: Some(13),
220 frequency: Some(CENTER_FREQ_CHAN_11),
221 timestamp_nanos: Some(zx::MonotonicInstant::get().into_nanos()),
222 ..Default::default()
223 },
224 ]),
225 compatibility: Some(fidl_policy::Compatibility::Supported),
226 ..Default::default()
227 },
228 fidl_policy::ScanResult {
229 id: Some(fidl_policy::NetworkIdentifier {
230 ssid: types::Ssid::try_from("unique ssid").unwrap().into(),
231 type_: fidl_policy::SecurityType::Wpa2,
232 }),
233 entries: Some(vec![fidl_policy::Bss {
234 bssid: Some([1, 2, 3, 4, 5, 6]),
235 rssi: Some(7),
236 frequency: Some(CENTER_FREQ_CHAN_8),
237 timestamp_nanos: Some(zx::MonotonicInstant::get().into_nanos()),
238 ..Default::default()
239 }]),
240 compatibility: Some(fidl_policy::Compatibility::Supported),
241 ..Default::default()
242 },
243 ]
244 }
245
246 fn create_fidl_scan_results_from_size(
250 result_sizes: Vec<usize>,
251 ) -> Vec<fidl_policy::ScanResult> {
252 let minimal_scan_result = fidl_policy::ScanResult {
254 id: Some(fidl_policy::NetworkIdentifier {
255 ssid: types::Ssid::empty().into(),
256 type_: fidl_policy::SecurityType::None,
257 }),
258 entries: Some(vec![]),
259 ..Default::default()
260 };
261 let minimal_result_size: usize = minimal_scan_result.measure().num_bytes;
262
263 let mut scan_result_with_one_bss = minimal_scan_result.clone();
265 scan_result_with_one_bss.entries = Some(vec![fidl_policy::Bss::default()]);
266
267 let empty_bss_entry_size: usize =
269 scan_result_with_one_bss.measure().num_bytes - minimal_result_size;
270
271 if result_sizes.iter().any(|size| size < &minimal_result_size || !size.is_multiple_of(8)) {
273 panic!(
274 "Invalid size. Requested size must be larger than {minimal_result_size} minimum bytes and divisible into octets (by 8)"
275 );
276 }
277
278 let mut fidl_scan_results = vec![];
279 for size in result_sizes {
280 let mut scan_result = minimal_scan_result.clone();
281
282 let num_bss_for_ap = (size - minimal_result_size) / empty_bss_entry_size;
283 let ssid_length =
285 (size - minimal_result_size) - (num_bss_for_ap * empty_bss_entry_size);
286
287 scan_result.id = Some(fidl_policy::NetworkIdentifier {
288 ssid: (0..ssid_length).map(|_| rand::random::<u8>()).collect(),
289 type_: fidl_policy::SecurityType::None,
290 });
291 scan_result.entries = Some(vec![fidl_policy::Bss::default(); num_bss_for_ap]);
292
293 assert_eq!(scan_result.measure().num_bytes, size);
295
296 fidl_scan_results.push(scan_result);
297 }
298 fidl_scan_results
299 }
300
301 #[fuchsia::test]
302 fn scan_result_generate_from_size() {
303 let scan_results = create_fidl_scan_results_from_size(vec![112; 4]);
304 assert_eq!(scan_results.len(), 4);
305 assert!(scan_results.iter().all(|scan_result| scan_result.measure().num_bytes == 112));
306 }
307
308 #[fuchsia::test]
309 fn sme_protection_converts_to_policy_security() {
310 use super::fidl_policy::SecurityType;
311 use super::fidl_sme::Protection;
312 let wpa3_supported = true;
313 let wpa3_not_supported = false;
314 let test_pairs = vec![
315 (Protection::Wpa3Enterprise, wpa3_supported, Some(SecurityType::Wpa3)),
317 (Protection::Wpa3Personal, wpa3_supported, Some(SecurityType::Wpa3)),
318 (Protection::Wpa2Wpa3Personal, wpa3_supported, Some(SecurityType::Wpa3)),
319 (Protection::Wpa2Enterprise, wpa3_supported, Some(SecurityType::Wpa2)),
320 (Protection::Wpa2Personal, wpa3_supported, Some(SecurityType::Wpa2)),
321 (Protection::Wpa1Wpa2Personal, wpa3_supported, Some(SecurityType::Wpa2)),
322 (Protection::Wpa2PersonalTkipOnly, wpa3_supported, Some(SecurityType::Wpa2)),
323 (Protection::Wpa1Wpa2PersonalTkipOnly, wpa3_supported, Some(SecurityType::Wpa2)),
324 (Protection::Wpa1, wpa3_supported, Some(SecurityType::Wpa)),
325 (Protection::Wep, wpa3_supported, Some(SecurityType::Wep)),
326 (Protection::Owe, wpa3_supported, Some(SecurityType::None)),
329 (Protection::OpenOweTransition, wpa3_supported, Some(SecurityType::None)),
330 (Protection::Open, wpa3_supported, Some(SecurityType::None)),
331 (Protection::Unknown, wpa3_supported, None),
332 (Protection::Wpa3Enterprise, wpa3_not_supported, Some(SecurityType::Wpa2)),
334 (Protection::Wpa3Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
335 (Protection::Wpa2Wpa3Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
336 (Protection::Wpa2Enterprise, wpa3_not_supported, Some(SecurityType::Wpa2)),
337 (Protection::Wpa2Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
338 (Protection::Wpa1Wpa2Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
339 (Protection::Wpa2PersonalTkipOnly, wpa3_not_supported, Some(SecurityType::Wpa2)),
340 (Protection::Wpa1Wpa2PersonalTkipOnly, wpa3_not_supported, Some(SecurityType::Wpa2)),
341 (Protection::Wpa1, wpa3_not_supported, Some(SecurityType::Wpa)),
342 (Protection::Wep, wpa3_not_supported, Some(SecurityType::Wep)),
343 (Protection::Owe, wpa3_not_supported, Some(SecurityType::None)),
346 (Protection::OpenOweTransition, wpa3_not_supported, Some(SecurityType::None)),
347 (Protection::Open, wpa3_not_supported, Some(SecurityType::None)),
348 (Protection::Unknown, wpa3_not_supported, None),
349 ];
350 for (input, wpa3_capable, output) in test_pairs {
351 assert_eq!(fidl_security_from_sme_protection(input, wpa3_capable), output);
352 }
353 }
354
355 #[fuchsia::test]
356 fn scan_results_converted_correctly() {
357 let fidl_aps = generate_test_fidl_data();
358 let internal_aps = vec![
359 types::ScanResult {
360 ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
361 security_type_detailed: types::SecurityTypeDetailed::Wpa3Personal,
362 entries: vec![
363 types::Bss {
364 bssid: types::Bssid::from([0, 0, 0, 0, 0, 0]),
365 signal: types::Signal { rssi_dbm: 0, snr_db: 1 },
366 timestamp: zx::MonotonicInstant::from_nanos(
367 fidl_aps[0].entries.as_ref().unwrap()[0].timestamp_nanos.unwrap(),
368 ),
369 channel: types::WlanChan::new(1, types::Cbw::Cbw20, TwoGhz),
370 observation: types::ScanObservation::Passive,
371 compatibility: Compatible::expect_ok([SecurityDescriptor::WPA3_PERSONAL]),
372 bss_description: random_fidl_bss_description!(
373 Wpa3,
374 bssid: [0, 0, 0, 0, 0, 0],
375 ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
376 rssi_dbm: 0,
377 snr_db: 1,
378 channel: types::WlanChan::new(1, types::Cbw::Cbw20, TwoGhz),
379 )
380 .into(),
381 },
382 types::Bss {
383 bssid: types::Bssid::from([7, 8, 9, 10, 11, 12]),
384 signal: types::Signal { rssi_dbm: 13, snr_db: 3 },
385 timestamp: zx::MonotonicInstant::from_nanos(
386 fidl_aps[0].entries.as_ref().unwrap()[1].timestamp_nanos.unwrap(),
387 ),
388 channel: types::WlanChan::new(11, types::Cbw::Cbw20, TwoGhz),
389 observation: types::ScanObservation::Passive,
390 compatibility: Incompatible::unknown(),
391 bss_description: random_fidl_bss_description!(
392 Wpa3,
393 bssid: [7, 8, 9, 10, 11, 12],
394 ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
395 rssi_dbm: 13,
396 snr_db: 3,
397 channel: types::WlanChan::new(11, types::Cbw::Cbw20, TwoGhz),
398 )
399 .into(),
400 },
401 ],
402 compatibility: types::Compatibility::Supported,
403 },
404 types::ScanResult {
405 ssid: types::Ssid::try_from("unique ssid").unwrap(),
406 security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
407 entries: vec![types::Bss {
408 bssid: types::Bssid::from([1, 2, 3, 4, 5, 6]),
409 signal: types::Signal { rssi_dbm: 7, snr_db: 2 },
410 timestamp: zx::MonotonicInstant::from_nanos(
411 fidl_aps[1].entries.as_ref().unwrap()[0].timestamp_nanos.unwrap(),
412 ),
413 channel: types::WlanChan::new(8, types::Cbw::Cbw20, TwoGhz),
414 observation: types::ScanObservation::Passive,
415 compatibility: Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
416 bss_description: random_fidl_bss_description!(
417 Wpa2,
418 bssid: [1, 2, 3, 4, 5, 6],
419 ssid: types::Ssid::try_from("unique ssid").unwrap(),
420 rssi_dbm: 7,
421 snr_db: 2,
422 channel: types::WlanChan::new(8, types::Cbw::Cbw20, TwoGhz),
423 )
424 .into(),
425 }],
426 compatibility: types::Compatibility::Supported,
427 },
428 ];
429 assert_eq!(fidl_aps, scan_result_to_policy_scan_result(&internal_aps));
430 }
431
432 #[fuchsia::test]
433 fn scan_results_converted_correctly_wpa3_unsupported() {
434 let mut fidl_aps = generate_test_fidl_data();
435 fidl_aps[0].id.as_mut().unwrap().type_ = fidl_policy::SecurityType::Wpa2;
438
439 let internal_aps = vec![
440 types::ScanResult {
441 ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
442 security_type_detailed: types::SecurityTypeDetailed::Wpa3Personal,
443 entries: vec![
444 types::Bss {
445 bssid: types::Bssid::from([0, 0, 0, 0, 0, 0]),
446 signal: types::Signal { rssi_dbm: 0, snr_db: 1 },
447 timestamp: zx::MonotonicInstant::from_nanos(
448 fidl_aps[0].entries.as_ref().unwrap()[0].timestamp_nanos.unwrap(),
449 ),
450 channel: types::WlanChan::new(1, types::Cbw::Cbw20, TwoGhz),
451 observation: types::ScanObservation::Passive,
452 compatibility: Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
454 bss_description: random_fidl_bss_description!(
455 Wpa3,
456 bssid: [0, 0, 0, 0, 0, 0],
457 ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
458 rssi_dbm: 0,
459 snr_db: 1,
460 channel: types::WlanChan::new(1, types::Cbw::Cbw20, TwoGhz),
461 )
462 .into(),
463 },
464 types::Bss {
465 bssid: types::Bssid::from([7, 8, 9, 10, 11, 12]),
466 signal: types::Signal { rssi_dbm: 13, snr_db: 3 },
467 timestamp: zx::MonotonicInstant::from_nanos(
468 fidl_aps[0].entries.as_ref().unwrap()[1].timestamp_nanos.unwrap(),
469 ),
470 channel: types::WlanChan::new(11, types::Cbw::Cbw20, TwoGhz),
471 observation: types::ScanObservation::Passive,
472 compatibility: Incompatible::unknown(),
473 bss_description: random_fidl_bss_description!(
474 Wpa3,
475 bssid: [7, 8, 9, 10, 11, 12],
476 ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
477 rssi_dbm: 13,
478 snr_db: 3,
479 channel: types::WlanChan::new(11, types::Cbw::Cbw20, TwoGhz),
480 )
481 .into(),
482 },
483 ],
484 compatibility: types::Compatibility::Supported,
485 },
486 types::ScanResult {
487 ssid: types::Ssid::try_from("unique ssid").unwrap(),
488 security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
489 entries: vec![types::Bss {
490 bssid: types::Bssid::from([1, 2, 3, 4, 5, 6]),
491 signal: types::Signal { rssi_dbm: 7, snr_db: 2 },
492 timestamp: zx::MonotonicInstant::from_nanos(
493 fidl_aps[1].entries.as_ref().unwrap()[0].timestamp_nanos.unwrap(),
494 ),
495 channel: types::WlanChan::new(8, types::Cbw::Cbw20, TwoGhz),
496 observation: types::ScanObservation::Passive,
497 compatibility: Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
498 bss_description: random_fidl_bss_description!(
499 Wpa2,
500 bssid: [1, 2, 3, 4, 5, 6],
501 ssid: types::Ssid::try_from("unique ssid").unwrap(),
502 rssi_dbm: 7,
503 snr_db: 2,
504 channel: types::WlanChan::new(8, types::Cbw::Cbw20, TwoGhz),
505 )
506 .into(),
507 }],
508 compatibility: types::Compatibility::Supported,
509 },
510 ];
511 assert_eq!(fidl_aps, scan_result_to_policy_scan_result(&internal_aps));
512 }
513
514 #[fuchsia::test]
517 fn partial_scan_result_consumption_has_no_error() {
518 let mut exec = fasync::TestExecutor::new();
519 let scan_results = generate_test_fidl_data();
520
521 let (iter, iter_server) = fidl::endpoints::create_proxy();
523 let send_fut = send_scan_results_over_fidl(iter_server, &scan_results);
524 let mut send_fut = pin!(send_fut);
525
526 let mut output_iter_fut = iter.get_next();
528
529 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
531
532 assert_matches!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
534 let results = result.expect("Failed to get next scan results").unwrap();
535 assert_eq!(results, scan_results);
536 });
537
538 drop(output_iter_fut);
544 drop(iter);
545
546 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Ready(Ok(())));
548 }
549
550 #[fuchsia::test]
551 fn no_scan_result_consumption_has_error() {
552 let mut exec = fasync::TestExecutor::new();
553 let scan_results = generate_test_fidl_data();
554
555 let (iter, iter_server) = fidl::endpoints::create_proxy();
557 let send_fut = send_scan_results_over_fidl(iter_server, &scan_results);
558 let mut send_fut = pin!(send_fut);
559
560 drop(iter);
562
563 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Ready(Err(_)));
565 }
566
567 #[fuchsia::test]
568 fn scan_result_sends_max_message_size() {
569 let mut exec = fasync::TestExecutor::new();
570 let (iter, iter_server) = fidl::endpoints::create_proxy();
571
572 let fidl_scan_results = create_fidl_scan_results_from_size(vec![
575 zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize
576 - FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE,
577 ]);
578
579 let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
580 let mut send_fut = pin!(send_fut);
581
582 let mut output_iter_fut = iter.get_next();
583
584 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
585
586 assert_matches!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
587 let results = result.expect("Failed to get next scan results").unwrap();
588 assert_eq!(results, fidl_scan_results);
589 })
590 }
591
592 #[fuchsia::test]
593 fn scan_result_exceeding_max_size_throws_error() {
594 let mut exec = fasync::TestExecutor::new();
595 let (iter, iter_server) = fidl::endpoints::create_proxy();
596
597 let fidl_scan_results = create_fidl_scan_results_from_size(vec![
600 (zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize
601 - FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE)
602 + 8,
603 ]);
604
605 let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
606 let mut send_fut = pin!(send_fut);
607
608 let mut output_iter_fut = iter.get_next();
609
610 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Ready(Err(_)));
611
612 assert_matches!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(Err(_)));
613 }
614
615 #[fuchsia::test]
616 fn scan_result_sends_single_batch() {
617 let mut exec = fasync::TestExecutor::new();
618 let (iter, iter_server) = fidl::endpoints::create_proxy();
619
620 let fidl_scan_results =
623 create_fidl_scan_results_from_size(vec![
624 zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize / 4;
625 3
626 ]);
627
628 let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
629 let mut send_fut = pin!(send_fut);
630
631 let mut output_iter_fut = iter.get_next();
632
633 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
634
635 assert_matches!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
636 let results = result.expect("Failed to get next scan results").unwrap();
637 assert_eq!(results, fidl_scan_results);
638 });
639 }
640
641 #[fuchsia::test]
642 fn scan_result_sends_multiple_batches() {
643 let mut exec = fasync::TestExecutor::new();
644 let (iter, iter_server) = fidl::endpoints::create_proxy();
645
646 let fidl_scan_results =
649 create_fidl_scan_results_from_size(vec![
650 zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize / 8;
651 8
652 ]);
653
654 let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
655 let mut send_fut = pin!(send_fut);
656
657 let mut output_iter_fut = iter.get_next();
658
659 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
660
661 let mut aggregate_results = vec![];
662 assert_matches!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
663 let results = result.expect("Failed to get next scan results").unwrap();
664 assert_eq!(results.len(), 7);
665 aggregate_results.extend(results);
666 });
667
668 let mut output_iter_fut = iter.get_next();
669 assert_matches!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
670 assert_matches!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
671 let results = result.expect("Failed to get next scan results").unwrap();
672 assert_eq!(results.len(), 1);
673 aggregate_results.extend(results);
674 });
675 assert_eq!(aggregate_results, fidl_scan_results);
676 }
677}