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