power_manager_integration_test_lib/
lib.rs1pub mod client_connectors;
6mod mocks;
7
8use crate::mocks::activity_service::MockActivityService;
9use crate::mocks::admin::MockStateControlAdminService;
10use crate::mocks::input_settings_service::MockInputSettingsService;
11use crate::mocks::kernel_service::MockKernelService;
12use fidl::endpoints::{DiscoverableProtocolMarker, ProtocolMarker, ServiceMarker};
13use fuchsia_component::client::Service;
14use fuchsia_component_test::{
15 Capability, ChildOptions, RealmBuilder, RealmBuilderParams, RealmInstance, Ref, Route,
16};
17use fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance};
18use log::*;
19use std::sync::Arc;
20use std::sync::atomic::{AtomicU64, Ordering};
21use {
22 fidl_fuchsia_driver_test as fdt, fidl_fuchsia_hardware_cpu_ctrl as fcpu_ctrl,
23 fidl_fuchsia_hardware_power_statecontrol as fpower, fidl_fuchsia_io as fio,
24 fidl_fuchsia_kernel as fkernel,
25 fidl_fuchsia_powermanager_driver_temperaturecontrol as ftemperaturecontrol,
26 fidl_fuchsia_sys2 as fsys2, fidl_fuchsia_testing as ftesting,
27};
28
29const POWER_MANAGER_URL: &str = "#meta/power-manager.cm";
30const CPU_MANAGER_URL: &str = "#meta/cpu-manager.cm";
31const FAKE_COBALT_URL: &str = "#meta/fake_cobalt.cm";
32const FAKE_CLOCK_URL: &str = "#meta/fake_clock.cm";
33
34const FAKE_TIME_SCALE: u32 = 100;
36
37static UNIQUE_REALM_NUMBER: AtomicU64 = AtomicU64::new(0);
39
40pub struct TestEnvBuilder {
41 power_manager_node_config_path: Option<String>,
42 cpu_manager_node_config_path: Option<String>,
43}
44
45impl TestEnvBuilder {
46 pub fn new() -> Self {
47 Self { power_manager_node_config_path: None, cpu_manager_node_config_path: None }
48 }
49
50 pub fn power_manager_node_config_path(mut self, path: &str) -> Self {
52 self.power_manager_node_config_path = Some(path.into());
53 self
54 }
55
56 pub fn cpu_manager_node_config_path(mut self, path: &str) -> Self {
58 self.cpu_manager_node_config_path = Some(path.into());
59 self
60 }
61
62 pub async fn build(self) -> TestEnv {
63 let realm_name = format!(
66 "{}-{}",
67 fuchsia_runtime::process_self().koid().unwrap().raw_koid(),
68 UNIQUE_REALM_NUMBER.fetch_add(1, Ordering::Relaxed)
69 );
70
71 let realm_builder =
72 RealmBuilder::with_params(RealmBuilderParams::new().realm_name(realm_name))
73 .await
74 .expect("Failed to create RealmBuilder");
75
76 realm_builder.driver_test_realm_setup().await.expect("Failed to setup driver test realm");
77
78 let expose =
79 fuchsia_component_test::Capability::service::<fcpu_ctrl::ServiceMarker>().into();
80 let dtr_exposes = vec![expose];
81
82 realm_builder.driver_test_realm_add_dtr_exposes(&dtr_exposes).await.unwrap();
83
84 let power_manager = realm_builder
85 .add_child("power_manager", POWER_MANAGER_URL, ChildOptions::new())
86 .await
87 .expect("Failed to add child: power_manager");
88
89 let cpu_manager = realm_builder
90 .add_child("cpu_manager", CPU_MANAGER_URL, ChildOptions::new())
91 .await
92 .expect("Failed to add child: cpu_manager");
93
94 let fake_cobalt = realm_builder
95 .add_child("fake_cobalt", FAKE_COBALT_URL, ChildOptions::new())
96 .await
97 .expect("Failed to add child: fake_cobalt");
98
99 let fake_clock = realm_builder
100 .add_child("fake_clock", FAKE_CLOCK_URL, ChildOptions::new())
101 .await
102 .expect("Failed to add child: fake_clock");
103
104 let activity_service = MockActivityService::new();
105 let activity_service_clone = activity_service.clone();
106 let activity_service_child = realm_builder
107 .add_local_child(
108 "activity_service",
109 move |handles| Box::pin(activity_service_clone.clone().run(handles)),
110 ChildOptions::new(),
111 )
112 .await
113 .expect("Failed to add child: activity_service");
114
115 let input_settings_service = MockInputSettingsService::new();
116 let input_settings_service_clone = input_settings_service.clone();
117 let input_settings_service_child = realm_builder
118 .add_local_child(
119 "input_settings_service",
120 move |handles| Box::pin(input_settings_service_clone.clone().run(handles)),
121 ChildOptions::new(),
122 )
123 .await
124 .expect("Failed to add child: input_settings_service");
125
126 let admin_service = MockStateControlAdminService::new();
127 let admin_service_clone = admin_service.clone();
128 let admin_service_child = realm_builder
129 .add_local_child(
130 "admin_service",
131 move |handles| Box::pin(admin_service_clone.clone().run(handles)),
132 ChildOptions::new(),
133 )
134 .await
135 .expect("Failed to add child: admin_service");
136
137 let kernel_service = MockKernelService::new();
138 let kernel_service_clone = kernel_service.clone();
139 let kernel_service_child = realm_builder
140 .add_local_child(
141 "kernel_service",
142 move |handles| Box::pin(kernel_service_clone.clone().run(handles)),
143 ChildOptions::new(),
144 )
145 .await
146 .expect("Failed to add child: kernel_service");
147
148 let parent_to_power_manager_routes = Route::new()
150 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
151 .capability(Capability::protocol_by_name("fuchsia.tracing.provider.Registry"))
152 .capability(Capability::protocol_by_name("fuchsia.boot.WriteOnlyLog"));
153 realm_builder
154 .add_route(parent_to_power_manager_routes.from(Ref::parent()).to(&power_manager))
155 .await
156 .unwrap();
157
158 let parent_to_cobalt_routes =
159 Route::new().capability(Capability::protocol_by_name("fuchsia.logger.LogSink"));
160 realm_builder
161 .add_route(parent_to_cobalt_routes.from(Ref::parent()).to(&fake_cobalt))
162 .await
163 .unwrap();
164
165 let parent_to_fake_clock_routes =
166 Route::new().capability(Capability::protocol_by_name("fuchsia.logger.LogSink"));
167 realm_builder
168 .add_route(parent_to_fake_clock_routes.from(Ref::parent()).to(&fake_clock))
169 .await
170 .unwrap();
171
172 let fake_clock_to_power_manager_routes =
173 Route::new().capability(Capability::protocol_by_name("fuchsia.testing.FakeClock"));
174 realm_builder
175 .add_route(fake_clock_to_power_manager_routes.from(&fake_clock).to(&power_manager))
176 .await
177 .unwrap();
178
179 let fake_clock_to_cpu_manager_routes =
180 Route::new().capability(Capability::protocol_by_name("fuchsia.testing.FakeClock"));
181 realm_builder
182 .add_route(fake_clock_to_cpu_manager_routes.from(&fake_clock).to(&cpu_manager))
183 .await
184 .unwrap();
185
186 let fake_clock_to_parent_routes = Route::new()
187 .capability(Capability::protocol_by_name("fuchsia.testing.FakeClockControl"));
188 realm_builder
189 .add_route(fake_clock_to_parent_routes.from(&fake_clock).to(Ref::parent()))
190 .await
191 .unwrap();
192
193 let cobalt_to_power_manager_routes = Route::new()
194 .capability(Capability::protocol_by_name("fuchsia.metrics.MetricEventLoggerFactory"));
195 realm_builder
196 .add_route(cobalt_to_power_manager_routes.from(&fake_cobalt).to(&power_manager))
197 .await
198 .unwrap();
199
200 let activity_service_to_power_manager_routes =
201 Route::new().capability(Capability::protocol_by_name("fuchsia.ui.activity.Provider"));
202 realm_builder
203 .add_route(
204 activity_service_to_power_manager_routes
205 .from(&activity_service_child)
206 .to(&power_manager),
207 )
208 .await
209 .unwrap();
210
211 let input_settings_service_to_power_manager_routes =
212 Route::new().capability(Capability::protocol_by_name("fuchsia.settings.Input"));
213 realm_builder
214 .add_route(
215 input_settings_service_to_power_manager_routes
216 .from(&input_settings_service_child)
217 .to(&power_manager),
218 )
219 .await
220 .unwrap();
221
222 let shutdown_shim_to_power_manager_routes = Route::new()
223 .capability(Capability::protocol_by_name("fuchsia.hardware.power.statecontrol.Admin"));
224
225 realm_builder
226 .add_route(
227 shutdown_shim_to_power_manager_routes.from(&admin_service_child).to(&power_manager),
228 )
229 .await
230 .unwrap();
231
232 let kernel_service_to_cpu_manager_routes =
233 Route::new().capability(Capability::protocol_by_name("fuchsia.kernel.Stats"));
234 realm_builder
235 .add_route(
236 kernel_service_to_cpu_manager_routes.from(&kernel_service_child).to(&cpu_manager),
237 )
238 .await
239 .unwrap();
240
241 realm_builder
242 .add_route(
243 Route::new()
244 .capability(
245 Capability::directory("pkg")
246 .subdir("config/power_manager")
247 .as_("config")
248 .path("/config")
249 .rights(fio::R_STAR_DIR),
250 )
251 .from(Ref::framework())
252 .to(&power_manager),
253 )
254 .await
255 .unwrap();
256
257 realm_builder
258 .add_route(
259 Route::new()
260 .capability(
261 Capability::directory("pkg")
262 .subdir("config/cpu_manager")
263 .as_("config")
264 .path("/config")
265 .rights(fio::R_STAR_DIR),
266 )
267 .from(Ref::framework())
268 .to(&cpu_manager),
269 )
270 .await
271 .unwrap();
272
273 realm_builder
274 .add_route(
275 Route::new()
276 .capability(Capability::protocol::<fsys2::LifecycleControllerMarker>())
277 .from(Ref::framework())
278 .to(Ref::parent()),
279 )
280 .await
281 .unwrap();
282
283 let power_manager_to_parent_routes = Route::new()
284 .capability(Capability::protocol_by_name("fuchsia.power.clientlevel.Connector"))
285 .capability(Capability::protocol_by_name("fuchsia.power.profile.Watcher"))
286 .capability(Capability::protocol_by_name("fuchsia.thermal.ClientStateConnector"))
287 .capability(Capability::protocol_by_name("fuchsia.thermal.SensorManager"));
288
289 realm_builder
290 .add_route(power_manager_to_parent_routes.from(&power_manager).to(Ref::parent()))
291 .await
292 .unwrap();
293
294 let parent_to_cpu_manager_routes = Route::new()
296 .capability(Capability::protocol_by_name("fuchsia.kernel.CpuResource"))
297 .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
298 .capability(Capability::protocol_by_name("fuchsia.tracing.provider.Registry"));
299 realm_builder
300 .add_route(parent_to_cpu_manager_routes.from(Ref::parent()).to(&cpu_manager))
301 .await
302 .unwrap();
303
304 let power_manager_to_cpu_manager_routes = Route::new()
305 .capability(Capability::protocol_by_name("fuchsia.thermal.ClientStateConnector"));
306 realm_builder
307 .add_route(power_manager_to_cpu_manager_routes.from(&power_manager).to(&cpu_manager))
308 .await
309 .unwrap();
310
311 let cpu_manager_to_parent_routes = Route::new()
312 .capability(Capability::protocol_by_name("fuchsia.power.cpu.DomainController"));
313 realm_builder
314 .add_route(cpu_manager_to_parent_routes.from(&cpu_manager).to(Ref::parent()))
315 .await
316 .unwrap();
317
318 realm_builder
319 .add_route(
320 Route::new()
321 .capability(Capability::directory("dev-topological"))
322 .from(Ref::child("driver_test_realm"))
323 .to(&power_manager),
324 )
325 .await
326 .unwrap();
327
328 realm_builder
329 .add_route(
330 Route::new()
331 .capability(Capability::service::<fcpu_ctrl::ServiceMarker>())
332 .from(Ref::child("driver_test_realm"))
333 .to(&cpu_manager),
334 )
335 .await
336 .unwrap();
337
338 realm_builder.init_mutable_config_from_package(&power_manager).await.unwrap();
340 realm_builder
341 .set_config_value(
342 &power_manager,
343 "node_config_path",
344 self.power_manager_node_config_path
345 .expect("power_manager_node_config_path not set")
346 .into(),
347 )
348 .await
349 .unwrap();
350
351 let cpu_node_config_path = self.cpu_manager_node_config_path.unwrap_or_default();
353 realm_builder
354 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
355 name: "fuchsia.power.cpu.BoostEnabled".parse().unwrap(),
356 value: false.into(),
357 }))
358 .await
359 .unwrap();
360 realm_builder
361 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
362 name: "fuchsia.power.cpu.NodeConfigPath".parse().unwrap(),
363 value: cpu_node_config_path.into(),
364 }))
365 .await
366 .unwrap();
367 realm_builder
368 .add_route(
369 Route::new()
370 .capability(Capability::configuration("fuchsia.power.cpu.BoostEnabled"))
371 .capability(Capability::configuration("fuchsia.power.cpu.NodeConfigPath"))
372 .from(Ref::self_())
373 .to(&cpu_manager),
374 )
375 .await
376 .unwrap();
377
378 let realm_instance = realm_builder.build().await.expect("Failed to build RealmInstance");
380
381 let args = fdt::RealmArgs {
383 root_driver: Some("#meta/root.cm".to_string()),
384 dtr_exposes: Some(dtr_exposes),
385 ..Default::default()
386 };
387
388 realm_instance
389 .driver_test_realm_start(args)
390 .await
391 .expect("Failed to start driver test realm");
392
393 set_fake_time_scale(&realm_instance, FAKE_TIME_SCALE).await;
396
397 TestEnv {
398 realm_instance: Some(realm_instance),
399 mocks: Mocks {
400 activity_service,
401 input_settings_service,
402 admin_service,
403 kernel_service,
404 },
405 }
406 }
407}
408
409pub struct TestEnv {
410 realm_instance: Option<RealmInstance>,
411 pub mocks: Mocks,
412}
413
414impl TestEnv {
415 pub fn connect_to_protocol<P: DiscoverableProtocolMarker>(&self) -> P::Proxy {
417 self.realm_instance.as_ref().unwrap().root.connect_to_protocol_at_exposed_dir().unwrap()
418 }
419
420 pub fn connect_to_device<P: ProtocolMarker>(&self, driver_path: &str) -> P::Proxy {
421 let dev = self.realm_instance.as_ref().unwrap().driver_test_realm_connect_to_dev().unwrap();
422 let path = driver_path.strip_prefix("/dev/").unwrap();
423
424 fuchsia_component::client::connect_to_named_protocol_at_dir_root::<P>(&dev, path).unwrap()
425 }
426
427 pub async fn connect_to_first_service_instance<S: ServiceMarker>(&self, marker: S) -> S::Proxy {
429 Service::open_from_dir(self.realm_instance.as_ref().unwrap().root.get_exposed_dir(), marker)
430 .unwrap()
431 .watch_for_any()
432 .await
433 .unwrap()
434 }
435
436 pub async fn destroy(&mut self) {
440 info!("Destroying TestEnv");
441 self.realm_instance
442 .take()
443 .expect("Missing realm instance")
444 .destroy()
445 .await
446 .expect("Failed to destroy realm instance");
447 }
448
449 pub async fn set_temperature(&self, driver_path: &str, temperature: f32) {
451 let dev = self.realm_instance.as_ref().unwrap().driver_test_realm_connect_to_dev().unwrap();
452
453 let control_path = driver_path.strip_prefix("/dev").unwrap().to_owned() + "/control";
454
455 let fake_temperature_control =
456 fuchsia_component::client::connect_to_named_protocol_at_dir_root::<
457 ftemperaturecontrol::DeviceMarker,
458 >(&dev, &control_path)
459 .unwrap();
460
461 let _status = fake_temperature_control.set_temperature_celsius(temperature).await.unwrap();
462 }
463
464 pub async fn set_cpu_stats(&self, cpu_stats: fkernel::CpuStats) {
465 self.mocks.kernel_service.set_cpu_stats(cpu_stats).await;
466 }
467
468 pub async fn wait_for_shutdown_request(&self) {
469 self.mocks.admin_service.wait_for_shutdown_request().await;
470 }
471
472 pub async fn wait_for_device(&self, driver_path: &str) {
474 let dev = self.realm_instance.as_ref().unwrap().driver_test_realm_connect_to_dev().unwrap();
475
476 let path = driver_path.strip_prefix("/dev").unwrap().to_owned();
477
478 device_watcher::recursive_wait(&dev, &path).await.unwrap();
479 }
480}
481
482impl Drop for TestEnv {
485 fn drop(&mut self) {
486 assert!(self.realm_instance.is_none(), "Must call destroy() to tear down test environment");
487 }
488}
489
490async fn set_fake_time_scale(realm_instance: &RealmInstance, scale: u32) {
492 let fake_clock_control: ftesting::FakeClockControlProxy =
493 realm_instance.root.connect_to_protocol_at_exposed_dir().unwrap();
494
495 fake_clock_control.pause().await.expect("failed to pause fake time: FIDL error");
496 fake_clock_control
497 .resume_with_increments(
498 zx::MonotonicDuration::from_millis(1).into_nanos(),
499 &ftesting::Increment::Determined(
500 zx::MonotonicDuration::from_millis(scale.into()).into_nanos(),
501 ),
502 )
503 .await
504 .expect("failed to set fake time scale: FIDL error")
505 .expect("failed to set fake time scale: protocol error");
506}
507
508pub struct Mocks {
510 pub activity_service: Arc<MockActivityService>,
511 pub input_settings_service: Arc<MockInputSettingsService>,
512 pub admin_service: Arc<MockStateControlAdminService>,
513 pub kernel_service: Arc<MockKernelService>,
514}
515
516pub async fn test_thermal_reboot(mut env: TestEnv, sensor_path: &str, temperature: f32) {
520 let _client = client_connectors::ThermalClient::new(&env, "audio");
522
523 env.set_temperature(sensor_path, temperature).await;
526 let result = env.mocks.admin_service.wait_for_shutdown_request().await;
527 assert_eq!(result.action.unwrap(), fpower::ShutdownAction::Reboot);
528 assert_eq!(result.reasons.unwrap(), vec![fpower::ShutdownReason::HighTemperature]);
529
530 env.destroy().await;
531}