1use anyhow::Error;
6use fidl_fuchsia_hardware_power_statecontrol::{
7 AdminProxy, AdminRequest, AdminRequestStream, AdminShutdownResult, ShutdownOptions,
8};
9use fuchsia_async as fasync;
10use futures::{TryFutureExt, TryStreamExt};
11use std::sync::Arc;
12
13pub struct MockRebootService {
14 call_hook: Box<dyn Fn(ShutdownOptions) -> AdminShutdownResult + Send + Sync>,
15}
16
17impl MockRebootService {
18 pub fn new(
22 call_hook: Box<dyn Fn(ShutdownOptions) -> AdminShutdownResult + Send + Sync>,
23 ) -> Self {
24 Self { call_hook }
25 }
26
27 pub async fn run_reboot_service(
30 self: Arc<Self>,
31 mut stream: AdminRequestStream,
32 ) -> Result<(), Error> {
33 while let Some(event) = stream.try_next().await.expect("received request") {
34 match event {
35 AdminRequest::Shutdown { options, responder } => {
36 let result = if options.action.is_none() {
37 Err(zx::Status::INVALID_ARGS.into_raw())
38 } else {
39 (self.call_hook)(options)
40 };
41 responder.send(result)?;
42 }
43 _ => {
44 panic!("unhandled RebootService method {event:?}");
45 }
46 }
47 }
48 Ok(())
49 }
50
51 pub fn spawn_reboot_service(self: Arc<Self>) -> AdminProxy {
54 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<
55 fidl_fuchsia_hardware_power_statecontrol::AdminMarker,
56 >();
57
58 fasync::Task::spawn(
59 self.run_reboot_service(stream)
60 .unwrap_or_else(|e| panic!("error running reboot service: {e:?}")),
61 )
62 .detach();
63
64 proxy
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use fidl_fuchsia_hardware_power_statecontrol::{ShutdownAction, ShutdownReason};
72 use std::sync::atomic::{AtomicU32, Ordering};
73
74 #[fuchsia::test]
75 async fn test_mock_reboot() {
76 let reboot_service = Arc::new(MockRebootService::new(Box::new(|_| Ok(()))));
77
78 let reboot_service_clone = Arc::clone(&reboot_service);
79 let proxy = reboot_service_clone.spawn_reboot_service();
80
81 proxy
82 .shutdown(&ShutdownOptions {
83 action: Some(ShutdownAction::Reboot),
84 reasons: Some(vec![ShutdownReason::SystemUpdate]),
85 ..Default::default()
86 })
87 .await
88 .expect("made shutdown call")
89 .expect("shutdown call succeeded");
90 }
91
92 #[fuchsia::test]
93 async fn test_mock_reboot_fails() {
94 let reboot_service =
95 Arc::new(MockRebootService::new(Box::new(|_| Err(zx::Status::INTERNAL.into_raw()))));
96
97 let reboot_service_clone = Arc::clone(&reboot_service);
98 let proxy = reboot_service_clone.spawn_reboot_service();
99
100 let shutdown_result = proxy
101 .shutdown(&ShutdownOptions {
102 action: Some(ShutdownAction::Reboot),
103 reasons: Some(vec![ShutdownReason::SystemUpdate]),
104 ..Default::default()
105 })
106 .await
107 .expect("made shutdown call");
108 assert_eq!(shutdown_result, Err(zx::Status::INTERNAL.into_raw()));
109 }
110
111 #[fuchsia::test]
112 async fn test_mock_reboot_fails_on_no_action() {
113 let reboot_service = Arc::new(MockRebootService::new(Box::new(|_| Ok(()))));
114
115 let reboot_service_clone = Arc::clone(&reboot_service);
116 let proxy = reboot_service_clone.spawn_reboot_service();
117
118 let shutdown_result = proxy
119 .shutdown(&ShutdownOptions {
120 reasons: Some(vec![ShutdownReason::SystemUpdate]),
121 ..Default::default()
122 })
123 .await
124 .expect("made shutdown call");
125 assert_eq!(shutdown_result, Err(zx::Status::INVALID_ARGS.into_raw()));
126 }
127
128 #[fuchsia::test]
129 async fn test_mock_reboot_call_hook() {
130 let reboot_service = Arc::new(MockRebootService::new(Box::new(|options| {
131 if let Some(reasons) = options.reasons {
132 match &reasons[..] {
133 [ShutdownReason::DeveloperRequest] => Ok(()),
134 _ => Err(zx::Status::NOT_SUPPORTED.into_raw()),
135 }
136 } else {
137 Err(zx::Status::NOT_SUPPORTED.into_raw())
138 }
139 })));
140
141 let reboot_service_clone = Arc::clone(&reboot_service);
142 let proxy = reboot_service_clone.spawn_reboot_service();
143
144 let () = proxy
146 .shutdown(&ShutdownOptions {
147 action: Some(ShutdownAction::Reboot),
148 reasons: Some(vec![ShutdownReason::DeveloperRequest]),
149 ..Default::default()
150 })
151 .await
152 .expect("made shutdown call")
153 .expect("shutdown call succeeded");
154
155 let error_reboot_result = proxy
157 .shutdown(&ShutdownOptions {
158 action: Some(ShutdownAction::Reboot),
159 reasons: Some(vec![ShutdownReason::SystemUpdate]),
160 ..Default::default()
161 })
162 .await
163 .expect("made shutdown call");
164 assert_eq!(error_reboot_result, Err(zx::Status::NOT_SUPPORTED.into_raw()));
165 }
166
167 #[fuchsia::test]
168 async fn test_mock_reboot_with_external_state() {
169 let called = Arc::new(AtomicU32::new(0));
170 let called_clone = Arc::clone(&called);
171 let reboot_service = Arc::new(MockRebootService::new(Box::new(move |_| {
172 called_clone.fetch_add(1, Ordering::SeqCst);
173 Ok(())
174 })));
175
176 let reboot_service_clone = Arc::clone(&reboot_service);
177 let proxy = reboot_service_clone.spawn_reboot_service();
178
179 proxy
180 .shutdown(&ShutdownOptions {
181 action: Some(ShutdownAction::Reboot),
182 reasons: Some(vec![ShutdownReason::SystemUpdate]),
183 ..Default::default()
184 })
185 .await
186 .expect("made shutdown call")
187 .expect("shutdown call succeeded");
188 assert_eq!(called.load(Ordering::SeqCst), 1);
189 }
190}