1#![allow(clippy::let_unit_value)]
6
7use anyhow::{anyhow, Error};
8use async_trait::async_trait;
9use fidl_fuchsia_mem::Buffer;
10use fuchsia_sync::Mutex;
11use futures::channel::mpsc;
12use futures::lock::Mutex as AsyncMutex;
13use futures::prelude::*;
14use std::sync::Arc;
15use zx::{Status, Vmo, VmoOptions};
16use {fidl_fuchsia_paver as paver, fuchsia_async as fasync};
17
18fn verify_buffer(buffer: &mut Buffer) {
19 let size = buffer.vmo.get_size().expect("vmo size query to succeed");
22 buffer.vmo.set_size(size * 2).expect("vmo must be resizable");
23}
24
25fn read_mem_buffer(buffer: &Buffer) -> Vec<u8> {
26 let mut res = vec![0; buffer.size.try_into().expect("usize")];
27 buffer.vmo.read(&mut res[..], 0).expect("vmo read to succeed");
28 res
29}
30
31fn write_mem_buffer(payload: Vec<u8>) -> Buffer {
32 let vmo =
33 Vmo::create_with_opts(VmoOptions::RESIZABLE, payload.len() as u64).expect("Creating VMO");
34 vmo.write(&payload, 0).expect("writing to VMO");
35 Buffer { vmo, size: payload.len() as u64 }
36}
37
38fn write_mem_buffer_custom_buffer_size((payload, size): (Vec<u8>, u64)) -> Buffer {
39 let vmo =
40 Vmo::create_with_opts(VmoOptions::RESIZABLE, payload.len() as u64).expect("Creating VMO");
41 let () = vmo.write(&payload, 0).expect("writing to VMO");
42 Buffer { vmo, size }
43}
44
45#[derive(Debug, PartialEq, Eq, Clone)]
46pub enum PaverEvent {
47 ReadAsset { configuration: paver::Configuration, asset: paver::Asset },
48 WriteAsset { configuration: paver::Configuration, asset: paver::Asset, payload: Vec<u8> },
49 ReadFirmware { configuration: paver::Configuration, firmware_type: String },
50 WriteFirmware { configuration: paver::Configuration, firmware_type: String, payload: Vec<u8> },
51 QueryActiveConfiguration,
52 QueryConfigurationLastSetActive,
53 QueryCurrentConfiguration,
54 QueryConfigurationStatus { configuration: paver::Configuration },
55 QueryConfigurationStatusAndBootAttempts { configuration: paver::Configuration },
56 SetConfigurationHealthy { configuration: paver::Configuration },
57 SetConfigurationActive { configuration: paver::Configuration },
58 SetConfigurationUnbootable { configuration: paver::Configuration },
59 SetOneShotRecovery,
60 BootManagerFlush,
61 DataSinkFlush,
62}
63
64impl PaverEvent {
65 pub fn from_data_sink_request(request: &paver::DataSinkRequest) -> PaverEvent {
66 match request {
67 paver::DataSinkRequest::WriteAsset { configuration, asset, payload, .. } => {
68 PaverEvent::WriteAsset {
69 configuration: configuration.to_owned(),
70 asset: asset.to_owned(),
71 payload: read_mem_buffer(payload),
72 }
73 }
74 paver::DataSinkRequest::WriteFirmware {
75 configuration,
76 type_: firmware_type,
77 payload,
78 ..
79 } => PaverEvent::WriteFirmware {
80 configuration: configuration.to_owned(),
81 firmware_type: firmware_type.to_owned(),
82 payload: read_mem_buffer(payload),
83 },
84 paver::DataSinkRequest::Flush { .. } => PaverEvent::DataSinkFlush {},
85 paver::DataSinkRequest::ReadAsset { configuration, asset, .. } => {
86 PaverEvent::ReadAsset {
87 configuration: configuration.to_owned(),
88 asset: asset.to_owned(),
89 }
90 }
91 paver::DataSinkRequest::ReadFirmware { configuration, type_, .. } => {
92 PaverEvent::ReadFirmware {
93 configuration: configuration.to_owned(),
94 firmware_type: type_.to_owned(),
95 }
96 }
97 request => panic!("Unhandled method Paver::{}", request.method_name()),
98 }
99 }
100
101 pub fn from_boot_manager_request(request: &paver::BootManagerRequest) -> PaverEvent {
102 match request {
103 paver::BootManagerRequest::QueryActiveConfiguration { .. } => {
104 PaverEvent::QueryActiveConfiguration
105 }
106 paver::BootManagerRequest::QueryConfigurationLastSetActive { .. } => {
107 PaverEvent::QueryConfigurationLastSetActive
108 }
109 paver::BootManagerRequest::QueryCurrentConfiguration { .. } => {
110 PaverEvent::QueryCurrentConfiguration
111 }
112 paver::BootManagerRequest::QueryConfigurationStatus { configuration, .. } => {
113 PaverEvent::QueryConfigurationStatus { configuration: configuration.to_owned() }
114 }
115 paver::BootManagerRequest::QueryConfigurationStatusAndBootAttempts {
116 configuration,
117 ..
118 } => PaverEvent::QueryConfigurationStatusAndBootAttempts {
119 configuration: configuration.to_owned(),
120 },
121 paver::BootManagerRequest::SetConfigurationHealthy { configuration, .. } => {
122 PaverEvent::SetConfigurationHealthy { configuration: configuration.to_owned() }
123 }
124 paver::BootManagerRequest::SetConfigurationActive { configuration, .. } => {
125 PaverEvent::SetConfigurationActive { configuration: configuration.to_owned() }
126 }
127 paver::BootManagerRequest::SetConfigurationUnbootable { configuration, .. } => {
128 PaverEvent::SetConfigurationUnbootable { configuration: configuration.to_owned() }
129 }
130 paver::BootManagerRequest::SetOneShotRecovery { .. } => PaverEvent::SetOneShotRecovery,
131 paver::BootManagerRequest::Flush { .. } => PaverEvent::BootManagerFlush,
132 }
133 }
134}
135
136#[async_trait]
148pub trait Hook: Sync {
149 async fn boot_manager(
150 &self,
151 request: paver::BootManagerRequest,
152 ) -> Option<paver::BootManagerRequest> {
153 Some(request)
154 }
155
156 async fn data_sink(&self, request: paver::DataSinkRequest) -> Option<paver::DataSinkRequest> {
157 Some(request)
158 }
159}
160
161pub mod hooks {
162 use super::*;
163
164 pub fn return_error<F>(callback: F) -> ReturnError<F>
170 where
171 F: Fn(&PaverEvent) -> Status,
172 {
173 ReturnError(callback)
174 }
175
176 pub struct ReturnError<F>(F);
177
178 #[async_trait]
179 impl<F> Hook for ReturnError<F>
180 where
181 F: Fn(&PaverEvent) -> Status + Sync,
182 {
183 async fn boot_manager(
184 &self,
185 request: paver::BootManagerRequest,
186 ) -> Option<paver::BootManagerRequest> {
187 let status = (self.0)(&PaverEvent::from_boot_manager_request(&request));
188 if status == Status::OK {
189 Some(request)
190 } else {
191 let _ = match request {
193 paver::BootManagerRequest::QueryActiveConfiguration { responder, .. } => {
194 responder.send(Err(status.into_raw()))
195 }
196 paver::BootManagerRequest::QueryConfigurationLastSetActive {
197 responder,
198 ..
199 } => responder.send(Err(status.into_raw())),
200 paver::BootManagerRequest::QueryCurrentConfiguration { responder, .. } => {
201 responder.send(Err(status.into_raw()))
202 }
203 paver::BootManagerRequest::QueryConfigurationStatus { responder, .. } => {
204 responder.send(Err(status.into_raw()))
205 }
206 paver::BootManagerRequest::QueryConfigurationStatusAndBootAttempts {
207 responder,
208 ..
209 } => responder.send(Err(status.into_raw())),
210 paver::BootManagerRequest::SetConfigurationHealthy { responder, .. } => {
211 responder.send(status.into_raw())
212 }
213 paver::BootManagerRequest::SetConfigurationActive { responder, .. } => {
214 responder.send(status.into_raw())
215 }
216 paver::BootManagerRequest::SetConfigurationUnbootable { responder, .. } => {
217 responder.send(status.into_raw())
218 }
219 paver::BootManagerRequest::SetOneShotRecovery { responder, .. } => {
220 responder.send(Ok(()))
221 }
222 paver::BootManagerRequest::Flush { responder } => {
223 responder.send(status.into_raw())
224 }
225 };
226 None
227 }
228 }
229
230 async fn data_sink(
231 &self,
232 request: paver::DataSinkRequest,
233 ) -> Option<paver::DataSinkRequest> {
234 let status = (self.0)(&PaverEvent::from_data_sink_request(&request));
235 if status == Status::OK {
236 Some(request)
237 } else {
238 let _ = match request {
240 paver::DataSinkRequest::WriteFirmware { responder, .. } => {
241 responder.send(&paver::WriteFirmwareResult::Status(status.into_raw()))
242 }
243 paver::DataSinkRequest::ReadAsset { responder, .. } => {
244 responder.send(Err(status.into_raw()))
245 }
246 paver::DataSinkRequest::WriteAsset { responder, .. } => {
247 responder.send(status.into_raw())
248 }
249 paver::DataSinkRequest::Flush { responder, .. } => {
250 responder.send(status.into_raw())
251 }
252 request => panic!("Unhandled method Paver::{}", request.method_name()),
253 };
254 None
255 }
256 }
257 }
258
259 pub fn config_status<F>(callback: F) -> ConfigStatus<F>
261 where
262 F: Fn(paver::Configuration) -> Result<paver::ConfigurationStatus, Status>,
263 {
264 ConfigStatus(callback)
265 }
266
267 pub struct ConfigStatus<F>(F);
268
269 #[async_trait]
270 impl<F> Hook for ConfigStatus<F>
271 where
272 F: Fn(paver::Configuration) -> Result<paver::ConfigurationStatus, Status> + Sync,
273 {
274 async fn boot_manager(
275 &self,
276 request: paver::BootManagerRequest,
277 ) -> Option<paver::BootManagerRequest> {
278 match request {
279 paver::BootManagerRequest::QueryConfigurationStatus {
280 configuration,
281 responder,
282 } => {
283 let result = (self.0)(configuration).map_err(Status::into_raw);
284 let _ = responder.send(result);
286 None
287 }
288 request => Some(request),
289 }
290 }
291 }
292
293 pub fn config_status_and_boot_attempts<F>(callback: F) -> ConfigStatusAndBootAttempts<F>
295 where
296 F: Fn(paver::Configuration) -> Result<(paver::ConfigurationStatus, Option<u8>), Status>,
297 {
298 ConfigStatusAndBootAttempts(callback)
299 }
300
301 pub struct ConfigStatusAndBootAttempts<F>(F);
302
303 #[async_trait]
304 impl<F> Hook for ConfigStatusAndBootAttempts<F>
305 where
306 F: Fn(paver::Configuration) -> Result<(paver::ConfigurationStatus, Option<u8>), Status>
307 + Sync,
308 {
309 async fn boot_manager(
310 &self,
311 request: paver::BootManagerRequest,
312 ) -> Option<paver::BootManagerRequest> {
313 let mut response_storage;
314 match request {
315 paver::BootManagerRequest::QueryConfigurationStatusAndBootAttempts {
316 configuration,
317 responder,
318 } => {
319 let raw_values = (self.0)(configuration).map_err(Status::into_raw);
320 let result = match raw_values {
321 Ok((status, boot_attempts)) => {
322 response_storage = paver::BootManagerQueryConfigurationStatusAndBootAttemptsResponse::default();
323 response_storage.status = Some(status);
324 response_storage.boot_attempts = boot_attempts;
325 Ok(&response_storage)
326 }
327 Err(e) => Err(e),
328 };
329
330 let _ = responder.send(result);
332 None
333 }
334 request => Some(request),
335 }
336 }
337 }
338
339 pub fn write_firmware<F>(callback: F) -> WriteFirmware<F>
341 where
342 F: Fn(
343 paver::Configuration,
344 String,
345 Vec<u8>,
346 ) -> paver::WriteFirmwareResult,
347 {
348 WriteFirmware(callback)
349 }
350
351 pub struct WriteFirmware<F>(F);
352
353 #[async_trait]
354 impl<F> Hook for WriteFirmware<F>
355 where
356 F: Fn(
357 paver::Configuration,
358 String,
359 Vec<u8>,
360 ) -> paver::WriteFirmwareResult
361 + Sync,
362 {
363 async fn data_sink(
364 &self,
365 request: paver::DataSinkRequest,
366 ) -> Option<paver::DataSinkRequest> {
367 match request {
368 paver::DataSinkRequest::WriteFirmware {
369 configuration,
370 type_: firmware_type,
371 payload,
372 responder,
373 } => {
374 let result = (self.0)(configuration, firmware_type, read_mem_buffer(&payload));
375 let _ = responder.send(&result);
377 None
378 }
379 request => Some(request),
380 }
381 }
382 }
383
384 pub fn read_firmware<F>(callback: F) -> ReadFirmware<F>
386 where
387 F: Fn(paver::Configuration, String) -> Result<Vec<u8>, Status>,
388 {
389 ReadFirmware(callback)
390 }
391
392 pub struct ReadFirmware<F>(F);
393
394 #[async_trait]
395 impl<F> Hook for ReadFirmware<F>
396 where
397 F: Fn(paver::Configuration, String) -> Result<Vec<u8>, Status> + Sync,
398 {
399 async fn data_sink(
400 &self,
401 request: paver::DataSinkRequest,
402 ) -> Option<paver::DataSinkRequest> {
403 match request {
404 paver::DataSinkRequest::ReadFirmware { configuration, type_, responder } => {
405 let result = (self.0)(configuration, type_)
406 .map(write_mem_buffer)
407 .map_err(Status::into_raw);
408 let _ = responder.send(result);
410 None
411 }
412 request => Some(request),
413 }
414 }
415 }
416
417 pub fn read_firmware_custom_buffer_size<F>(callback: F) -> ReadFirmwareCustomBufferSize<F>
420 where
421 F: Fn(paver::Configuration, String) -> Result<(Vec<u8>, u64), Status>,
422 {
423 ReadFirmwareCustomBufferSize(callback)
424 }
425
426 pub struct ReadFirmwareCustomBufferSize<F>(F);
427
428 #[async_trait]
429 impl<F> Hook for ReadFirmwareCustomBufferSize<F>
430 where
431 F: Fn(paver::Configuration, String) -> Result<(Vec<u8>, u64), Status> + Sync,
432 {
433 async fn data_sink(
434 &self,
435 request: paver::DataSinkRequest,
436 ) -> Option<paver::DataSinkRequest> {
437 match request {
438 paver::DataSinkRequest::ReadFirmware { configuration, type_, responder } => {
439 let result = (self.0)(configuration, type_)
440 .map(write_mem_buffer_custom_buffer_size)
441 .map_err(Status::into_raw);
442 let _ = responder.send(result);
444 None
445 }
446 request => Some(request),
447 }
448 }
449 }
450
451 pub fn read_asset<F>(callback: F) -> ReadAsset<F>
453 where
454 F: Fn(paver::Configuration, paver::Asset) -> Result<Vec<u8>, Status>,
455 {
456 ReadAsset(callback)
457 }
458
459 pub struct ReadAsset<F>(F);
460
461 #[async_trait]
462 impl<F> Hook for ReadAsset<F>
463 where
464 F: Fn(paver::Configuration, paver::Asset) -> Result<Vec<u8>, Status> + Sync,
465 {
466 async fn data_sink(
467 &self,
468 request: paver::DataSinkRequest,
469 ) -> Option<paver::DataSinkRequest> {
470 match request {
471 paver::DataSinkRequest::ReadAsset { configuration, asset, responder } => {
472 let result = (self.0)(configuration, asset)
473 .map(write_mem_buffer)
474 .map_err(Status::into_raw);
475 let _ = responder.send(result);
477 None
478 }
479 request => Some(request),
480 }
481 }
482 }
483
484 pub fn read_asset_custom_buffer_size<F>(callback: F) -> ReadAssetCustomBufferSize<F>
487 where
488 F: Fn(paver::Configuration, paver::Asset) -> Result<(Vec<u8>, u64), Status>,
489 {
490 ReadAssetCustomBufferSize(callback)
491 }
492
493 pub struct ReadAssetCustomBufferSize<F>(F);
494
495 #[async_trait]
496 impl<F> Hook for ReadAssetCustomBufferSize<F>
497 where
498 F: Fn(paver::Configuration, paver::Asset) -> Result<(Vec<u8>, u64), Status> + Sync,
499 {
500 async fn data_sink(
501 &self,
502 request: paver::DataSinkRequest,
503 ) -> Option<paver::DataSinkRequest> {
504 match request {
505 paver::DataSinkRequest::ReadAsset { configuration, asset, responder } => {
506 let result = (self.0)(configuration, asset)
507 .map(write_mem_buffer_custom_buffer_size)
508 .map_err(Status::into_raw);
509 let _ = responder.send(result);
511 None
512 }
513 request => Some(request),
514 }
515 }
516 }
517
518 pub fn throttle() -> (ThrottleHook, Throttle) {
520 let (send, recv) = mpsc::unbounded();
521 (ThrottleHook(AsyncMutex::new(Some(recv))), Throttle(send))
522 }
523
524 pub struct Throttle(mpsc::UnboundedSender<PaverEvent>);
527
528 impl Throttle {
529 pub fn emit_next_paver_event(&self, expected_event: &PaverEvent) {
530 self.0.unbounded_send(expected_event.clone()).expect("emit paver event");
531 }
532
533 pub fn emit_next_paver_events(&self, expected_events: &[PaverEvent]) {
534 for event in expected_events.iter() {
535 self.emit_next_paver_event(event);
536 }
537 }
538 }
539
540 pub struct ThrottleHook(AsyncMutex<Option<mpsc::UnboundedReceiver<PaverEvent>>>);
541
542 #[async_trait]
543 impl Hook for ThrottleHook {
544 async fn boot_manager(
545 &self,
546 request: paver::BootManagerRequest,
547 ) -> Option<paver::BootManagerRequest> {
548 let mut optional_recv = self.0.lock().await;
549 if let Some(recv) = optional_recv.as_mut() {
550 if let Some(expected_request) = recv.next().await {
551 assert_eq!(PaverEvent::from_boot_manager_request(&request), expected_request);
552 } else {
553 assert!(optional_recv.take().is_some());
554 }
555 }
556 Some(request)
557 }
558
559 async fn data_sink(
560 &self,
561 request: paver::DataSinkRequest,
562 ) -> Option<paver::DataSinkRequest> {
563 let mut optional_recv = self.0.lock().await;
564 if let Some(recv) = optional_recv.as_mut() {
565 if let Some(expected_request) = recv.next().await {
566 assert_eq!(PaverEvent::from_data_sink_request(&request), expected_request);
567 } else {
568 assert!(optional_recv.take().is_some());
569 }
570 }
571 Some(request)
572 }
573 }
574}
575
576#[allow(clippy::type_complexity)]
577pub struct MockPaverServiceBuilder {
578 hooks: Vec<Box<dyn Hook + Send + Sync>>,
579 event_hook: Option<Box<dyn Fn(&PaverEvent) + Send + Sync>>,
580 active_config: paver::Configuration,
581 current_config: paver::Configuration,
582 boot_manager_close_with_epitaph: Option<Status>,
583}
584
585impl MockPaverServiceBuilder {
586 #[allow(clippy::new_without_default)]
587 pub fn new() -> Self {
588 Self {
589 hooks: vec![],
590 event_hook: None,
591 active_config: paver::Configuration::A,
592 current_config: paver::Configuration::A,
593 boot_manager_close_with_epitaph: None,
594 }
595 }
596
597 pub fn insert_hook(mut self, hook: impl Hook + Send + 'static) -> Self {
599 self.hooks.push(Box::new(hook));
600 self
601 }
602
603 pub fn event_hook<F>(mut self, event_hook: F) -> Self
606 where
607 F: Fn(&PaverEvent) + Send + Sync + 'static,
608 {
609 self.event_hook = Some(Box::new(event_hook));
610 self
611 }
612
613 pub fn active_config(mut self, active_config: paver::Configuration) -> Self {
614 self.active_config = active_config;
615 self
616 }
617
618 pub fn current_config(mut self, current_config: paver::Configuration) -> Self {
619 self.current_config = current_config;
620 self
621 }
622
623 pub fn boot_manager_close_with_epitaph(mut self, status: Status) -> Self {
624 self.boot_manager_close_with_epitaph = Some(status);
625 self
626 }
627
628 pub fn build(self) -> MockPaverService {
629 MockPaverService {
630 hooks: self.hooks,
631 events: Mutex::new(vec![]),
632 event_hook: self.event_hook.unwrap_or_else(|| Box::new(|_| ())),
633 active_config: self.active_config,
634 current_config: self.current_config,
635 boot_manager_close_with_epitaph: self.boot_manager_close_with_epitaph,
636 }
637 }
638}
639
640pub struct MockPaverService {
641 hooks: Vec<Box<dyn Hook + Send + Sync>>,
642 events: Mutex<Vec<PaverEvent>>,
643 event_hook: Box<dyn Fn(&PaverEvent) + Send + Sync>,
644 active_config: paver::Configuration,
645 current_config: paver::Configuration,
646 boot_manager_close_with_epitaph: Option<Status>,
647}
648
649impl MockPaverService {
650 pub fn take_events(&self) -> Vec<PaverEvent> {
651 std::mem::take(&mut *self.events.lock())
652 }
653
654 pub fn spawn_data_sink_service(self: &Arc<Self>) -> paver::DataSinkProxy {
656 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<paver::DataSinkMarker>();
657
658 fasync::Task::spawn(
659 Arc::clone(self)
660 .run_data_sink_service(stream)
661 .unwrap_or_else(|e| panic!("error running data sink service: {:#}", anyhow!(e))),
662 )
663 .detach();
664
665 proxy
666 }
667
668 pub fn spawn_boot_manager_service(self: &Arc<Self>) -> paver::BootManagerProxy {
670 let (proxy, server_end) = fidl::endpoints::create_proxy::<paver::BootManagerMarker>();
671
672 fasync::Task::spawn(
673 Arc::clone(self)
674 .run_boot_manager_service(server_end)
675 .unwrap_or_else(|e| panic!("error running boot manager service: {:#}", anyhow!(e))),
676 )
677 .detach();
678
679 proxy
680 }
681
682 pub fn spawn_paver_service(self: &Arc<Self>) -> paver::PaverProxy {
684 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<paver::PaverMarker>();
685
686 fasync::Task::spawn(
687 Arc::clone(self)
688 .run_paver_service(stream)
689 .unwrap_or_else(|e| panic!("error running paver service: {:#}", anyhow!(e))),
690 )
691 .detach();
692
693 proxy
694 }
695
696 fn push_event(self: &Arc<Self>, event: PaverEvent) {
697 (*self.event_hook)(&event);
698 self.events.lock().push(event);
699 }
700
701 async fn run_data_sink_service(
702 self: Arc<Self>,
703 mut stream: paver::DataSinkRequestStream,
704 ) -> Result<(), Error> {
705 'req_stream: while let Some(mut request) = stream.try_next().await? {
706 self.push_event(PaverEvent::from_data_sink_request(&request));
707
708 for hook in self.hooks.iter() {
709 match hook.data_sink(request).await {
710 Some(r) => request = r,
711 None => continue 'req_stream,
712 }
713 }
714
715 let _ = match request {
717 paver::DataSinkRequest::WriteAsset { mut payload, responder, .. } => {
718 verify_buffer(&mut payload);
719 responder.send(Status::OK.into_raw())
720 }
721 paver::DataSinkRequest::WriteFirmware { mut payload, responder, .. } => {
722 verify_buffer(&mut payload);
723 responder.send(&paver::WriteFirmwareResult::Status(Status::OK.into_raw()))
724 }
725 paver::DataSinkRequest::Flush { responder } => {
726 responder.send(Status::OK.into_raw())
727 }
728 paver::DataSinkRequest::ReadAsset { responder, .. } => {
729 responder.send(Ok(write_mem_buffer(vec![0u8; 4096])))
733 }
734 paver::DataSinkRequest::ReadFirmware { responder, .. } => {
735 responder.send(Ok(write_mem_buffer(vec![0u8; 4096])))
739 }
740 request => panic!("Unhandled method Paver::{}", request.method_name()),
741 };
742 }
743
744 Ok(())
745 }
746
747 async fn run_boot_manager_service(
748 self: Arc<Self>,
749 boot_manager: fidl::endpoints::ServerEnd<paver::BootManagerMarker>,
750 ) -> Result<(), Error> {
751 if let Some(status) = self.boot_manager_close_with_epitaph {
752 boot_manager.close_with_epitaph(status)?;
753 return Ok(());
754 };
755
756 let mut stream = boot_manager.into_stream();
757
758 'req_stream: while let Some(mut request) = stream.try_next().await? {
759 self.push_event(PaverEvent::from_boot_manager_request(&request));
760
761 for hook in self.hooks.iter() {
762 match hook.boot_manager(request).await {
763 Some(r) => request = r,
764 None => continue 'req_stream,
765 }
766 }
767
768 let _ = match request {
770 paver::BootManagerRequest::QueryActiveConfiguration { responder } => {
771 let result = if self.active_config == paver::Configuration::Recovery {
772 Err(Status::NOT_SUPPORTED.into_raw())
773 } else {
774 Ok(self.active_config)
775 };
776 responder.send(result)
777 }
778 paver::BootManagerRequest::QueryConfigurationLastSetActive { responder } => {
779 responder.send(Err(Status::NOT_SUPPORTED.into_raw()))
781 }
782 paver::BootManagerRequest::QueryCurrentConfiguration { responder } => {
783 responder.send(Ok(self.current_config))
784 }
785 paver::BootManagerRequest::QueryConfigurationStatus { responder, .. } => {
786 responder.send(Ok(paver::ConfigurationStatus::Healthy))
787 }
788 paver::BootManagerRequest::QueryConfigurationStatusAndBootAttempts {
789 responder,
790 ..
791 } => responder.send(Ok(
792 &paver::BootManagerQueryConfigurationStatusAndBootAttemptsResponse {
793 status: Some(paver::ConfigurationStatus::Healthy),
794 ..Default::default()
795 },
796 )),
797 paver::BootManagerRequest::SetConfigurationHealthy {
798 configuration,
799 responder,
800 ..
801 } => {
802 let status = if configuration == paver::Configuration::Recovery {
804 Status::INVALID_ARGS
805 } else {
806 Status::OK
807 };
808 responder.send(status.into_raw())
809 }
810 paver::BootManagerRequest::SetConfigurationActive { responder, .. } => {
811 responder.send(Status::OK.into_raw())
812 }
813 paver::BootManagerRequest::SetConfigurationUnbootable { responder, .. } => {
814 responder.send(Status::OK.into_raw())
815 }
816 paver::BootManagerRequest::SetOneShotRecovery { responder, .. } => {
817 responder.send(Ok(()))
818 }
819 paver::BootManagerRequest::Flush { responder } => {
820 responder.send(Status::OK.into_raw())
821 }
822 };
823 }
824
825 Ok(())
826 }
827
828 pub async fn run_paver_service(
829 self: Arc<Self>,
830 mut stream: paver::PaverRequestStream,
831 ) -> Result<(), Error> {
832 while let Some(request) = stream.try_next().await? {
833 match request {
834 paver::PaverRequest::FindDataSink { data_sink, .. } => {
835 let paver_service_clone = self.clone();
836 fasync::Task::spawn(
837 paver_service_clone
838 .run_data_sink_service(data_sink.into_stream())
839 .unwrap_or_else(|e| panic!("error running data sink service: {e:?}")),
840 )
841 .detach();
842 }
843 paver::PaverRequest::FindBootManager { boot_manager, .. } => {
844 let paver_service_clone = self.clone();
845 fasync::Task::spawn(
846 paver_service_clone.run_boot_manager_service(boot_manager).unwrap_or_else(
847 |e| panic!("error running boot manager service: {e:?}"),
848 ),
849 )
850 .detach();
851 }
852 request => panic!("Unhandled method Paver::{}", request.method_name()),
853 }
854 }
855
856 Ok(())
857 }
858}
859
860#[cfg(test)]
861pub mod tests {
862 use super::*;
863 use assert_matches::assert_matches;
864 use fidl_fuchsia_paver as paver;
865
866 use futures::task::Poll;
867
868 struct MockPaverForTest {
869 pub paver: Arc<MockPaverService>,
870 pub data_sink: paver::DataSinkProxy,
871 pub boot_manager: paver::BootManagerProxy,
872 }
873
874 impl MockPaverForTest {
875 pub fn new<F>(f: F) -> Self
876 where
877 F: FnOnce(MockPaverServiceBuilder) -> MockPaverServiceBuilder,
878 {
879 let paver = f(MockPaverServiceBuilder::new());
880 let paver = Arc::new(paver.build());
881 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<paver::PaverMarker>();
882
883 fasync::Task::spawn(
884 Arc::clone(&paver)
885 .run_paver_service(stream)
886 .unwrap_or_else(|_| panic!("Failed to run paver")),
887 )
888 .detach();
889
890 let (data_sink, server) = fidl::endpoints::create_proxy::<paver::DataSinkMarker>();
891 proxy.find_data_sink(server).expect("Finding data sink");
892 let (boot_manager, server) =
893 fidl::endpoints::create_proxy::<paver::BootManagerMarker>();
894 proxy.find_boot_manager(server).expect("Finding boot manager");
895
896 MockPaverForTest { paver, data_sink, boot_manager }
897 }
898 }
899
900 #[fasync::run_singlethreaded(test)]
901 pub async fn test_events() -> Result<(), Error> {
902 let paver = MockPaverForTest::new(|p| p);
903 let data = "hello there".as_bytes();
904 let vmo =
905 Vmo::create_with_opts(VmoOptions::RESIZABLE, data.len() as u64).expect("Creating VMO");
906 vmo.write(data, 0).expect("writing to VMO");
907 paver
908 .data_sink
909 .write_asset(
910 paver::Configuration::A,
911 paver::Asset::Kernel,
912 Buffer { vmo, size: data.len() as u64 },
913 )
914 .await
915 .expect("Writing asset");
916
917 let result = paver
918 .boot_manager
919 .query_active_configuration()
920 .await
921 .expect("Querying active configuration")
922 .expect("Querying active configuration (2)");
923 assert_eq!(result, paver::Configuration::A);
924 paver
925 .boot_manager
926 .set_configuration_active(paver::Configuration::B)
927 .await
928 .expect("Setting active configuration");
929
930 assert_eq!(
931 paver.paver.take_events(),
932 vec![
933 PaverEvent::WriteAsset {
934 configuration: paver::Configuration::A,
935 asset: paver::Asset::Kernel,
936 payload: data.to_vec()
937 },
938 PaverEvent::QueryActiveConfiguration,
939 PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B },
940 ]
941 );
942
943 Ok(())
944 }
945
946 #[fasync::run_singlethreaded(test)]
947 pub async fn test_hook() -> Result<(), Error> {
948 let hook = |_: &PaverEvent| zx::Status::NOT_SUPPORTED;
949 let paver = MockPaverForTest::new(|p| p.insert_hook(hooks::return_error(hook)));
950
951 assert_eq!(
952 Err(zx::Status::NOT_SUPPORTED.into_raw()),
953 paver.boot_manager.query_active_configuration().await?
954 );
955
956 assert_eq!(paver.paver.take_events(), vec![PaverEvent::QueryActiveConfiguration]);
957
958 Ok(())
959 }
960
961 #[fasync::run_singlethreaded(test)]
962 pub async fn test_config_status_hook() -> Result<(), Error> {
963 let config_status_hook =
964 |_: paver::Configuration| Ok(paver::ConfigurationStatus::Unbootable);
965 let paver =
966 MockPaverForTest::new(|p| p.insert_hook(hooks::config_status(config_status_hook)));
967
968 assert_eq!(
969 Ok(paver::ConfigurationStatus::Unbootable),
970 paver.boot_manager.query_configuration_status(paver::Configuration::A).await?
971 );
972
973 assert_eq!(
974 paver.paver.take_events(),
975 vec![PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }]
976 );
977
978 Ok(())
979 }
980
981 #[test]
982 pub fn test_throttle_hook() -> Result<(), Error> {
983 let mut executor = fasync::TestExecutor::new();
984
985 let (throttle_hook, throttler) = hooks::throttle();
986 let paver = MockPaverForTest::new(|p| p.insert_hook(throttle_hook));
987
988 let mut fut0 = paver.boot_manager.query_configuration_status(paver::Configuration::A);
990 assert_eq!(executor.run_until_stalled(&mut fut0).map(|fidl| fidl.unwrap()), Poll::Pending);
991 let mut fut1 = paver.data_sink.flush();
992 assert_eq!(executor.run_until_stalled(&mut fut1).map(|fidl| fidl.unwrap()), Poll::Pending);
993
994 let () = throttler.emit_next_paver_event(&PaverEvent::QueryConfigurationStatus {
998 configuration: paver::Configuration::A,
999 });
1000 assert_eq!(
1001 executor.run_until_stalled(&mut fut0).map(|fidl| fidl.unwrap()),
1002 Poll::Ready(Ok(paver::ConfigurationStatus::Healthy))
1003 );
1004 assert_eq!(executor.run_until_stalled(&mut fut1).map(|fidl| fidl.unwrap()), Poll::Pending);
1005
1006 let () = throttler.emit_next_paver_event(&PaverEvent::DataSinkFlush);
1008 assert_eq!(
1009 executor.run_until_stalled(&mut fut1).map(|fidl| fidl.unwrap()),
1010 Poll::Ready(Status::OK.into_raw())
1011 );
1012
1013 drop(throttler);
1015 executor.run_singlethreaded(async {
1016 assert_eq!(
1017 paver.boot_manager.query_current_configuration().await.unwrap(),
1018 Ok(paver::Configuration::A)
1019 );
1020 assert_matches!(
1021 paver
1022 .data_sink
1023 .read_asset(paver::Configuration::A, paver::Asset::Kernel)
1024 .await
1025 .unwrap(),
1026 Ok(_)
1027 );
1028 });
1029
1030 Ok(())
1031 }
1032
1033 #[fasync::run_singlethreaded(test)]
1034 pub async fn test_active_config() -> Result<(), Error> {
1035 let paver = MockPaverForTest::new(|p| p.active_config(paver::Configuration::B));
1036 assert_eq!(
1037 Ok(paver::Configuration::B),
1038 paver.boot_manager.query_active_configuration().await?
1039 );
1040 assert_eq!(paver.paver.take_events(), vec![PaverEvent::QueryActiveConfiguration]);
1041 Ok(())
1042 }
1043
1044 #[fasync::run_singlethreaded(test)]
1045 pub async fn test_active_config_when_recovery() -> Result<(), Error> {
1046 let paver = MockPaverForTest::new(|p| p.active_config(paver::Configuration::Recovery));
1047 assert_eq!(
1048 Err(Status::NOT_SUPPORTED.into_raw()),
1049 paver.boot_manager.query_active_configuration().await?
1050 );
1051 assert_eq!(paver.paver.take_events(), vec![PaverEvent::QueryActiveConfiguration]);
1052 Ok(())
1053 }
1054
1055 #[fasync::run_singlethreaded(test)]
1056 pub async fn test_boot_manager_epitaph() -> Result<(), Error> {
1057 let paver =
1058 MockPaverForTest::new(|p| p.boot_manager_close_with_epitaph(zx::Status::NOT_SUPPORTED));
1059
1060 let result = paver.boot_manager.query_active_configuration().await;
1061 assert_matches!(
1062 result,
1063 Err(fidl::Error::ClientChannelClosed { status: zx::Status::NOT_SUPPORTED, .. })
1064 );
1065 Ok(())
1066 }
1067
1068 #[fasync::run_singlethreaded(test)]
1069 pub async fn test_set_config_a_healthy() -> Result<(), Error> {
1070 let paver = MockPaverForTest::new(|p| p);
1071 assert_eq!(
1072 Status::OK.into_raw(),
1073 paver.boot_manager.set_configuration_healthy(paver::Configuration::A).await?
1074 );
1075 assert_eq!(
1076 paver.paver.take_events(),
1077 vec![PaverEvent::SetConfigurationHealthy { configuration: paver::Configuration::A }]
1078 );
1079 Ok(())
1080 }
1081
1082 #[fasync::run_singlethreaded(test)]
1083 pub async fn test_set_recovery_config_healthy() -> Result<(), Error> {
1084 let paver = MockPaverForTest::new(|p| p);
1085 assert_eq!(
1086 Status::INVALID_ARGS.into_raw(),
1087 paver.boot_manager.set_configuration_healthy(paver::Configuration::Recovery).await?
1088 );
1089 assert_eq!(
1090 paver.paver.take_events(),
1091 vec![PaverEvent::SetConfigurationHealthy {
1092 configuration: paver::Configuration::Recovery
1093 }]
1094 );
1095 Ok(())
1096 }
1097}