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