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