power_manager_integration_test_lib/mocks/
admin.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl::endpoints::ServerEnd;
6use fidl_fuchsia_hardware_power_statecontrol::{self as fpower, ShutdownOptions};
7use fidl_fuchsia_io::DirectoryMarker;
8use fuchsia_async as fasync;
9use fuchsia_component::server::ServiceFs;
10use fuchsia_component_test::LocalComponentHandles;
11use futures::channel::mpsc;
12use futures::lock::Mutex;
13use futures::{StreamExt, TryStreamExt};
14use log::*;
15use std::sync::Arc;
16
17/// Mocks the fuchsia.hardware.power.statecontrol.Admin service to be used in integration tests.
18pub struct MockStateControlAdminService {
19    shutdown_received_sender: Mutex<mpsc::Sender<ShutdownOptions>>,
20    shutdown_received_receiver: Mutex<mpsc::Receiver<ShutdownOptions>>,
21}
22
23impl MockStateControlAdminService {
24    pub fn new() -> Arc<MockStateControlAdminService> {
25        let (sender, receiver) = mpsc::channel(1);
26        Arc::new(Self {
27            shutdown_received_sender: Mutex::new(sender),
28            shutdown_received_receiver: Mutex::new(receiver),
29        })
30    }
31
32    /// Runs the mock using the provided `LocalComponentHandles`.
33    ///
34    /// The mock intentionally does not complete reboot requests in order to prevent further calls
35    /// that may cause extra send.
36    ///
37    /// Expected usage is to call this function from a closure for the
38    /// `local_component_implementation` parameter to `RealmBuilder.add_local_child`.
39    ///
40    /// For example:
41    ///     let mock_admin_service = MockStateControlAdminService::new();
42    ///     let admin_service_child = realm_builder
43    ///         .add_local_child(
44    ///             "admin_service",
45    ///             move |handles| {
46    ///                 Box::pin(mock_admin_service.clone().run(handles))
47    ///             },
48    ///             ChildOptions::new(),
49    ///         )
50    ///         .await
51    ///         .unwrap();
52    ///
53    pub async fn run(self: Arc<Self>, handles: LocalComponentHandles) -> Result<(), anyhow::Error> {
54        self.run_inner(handles.outgoing_dir).await
55    }
56
57    async fn run_inner(
58        self: Arc<Self>,
59        outgoing_dir: ServerEnd<DirectoryMarker>,
60    ) -> Result<(), anyhow::Error> {
61        let mut fs = ServiceFs::new();
62        fs.dir("svc").add_fidl_service(move |mut stream: fpower::AdminRequestStream| {
63            let this = self.clone();
64            fasync::Task::local(async move {
65                info!("MockStateControlAdminService: new connection Admin");
66                while let Some(request) = stream.try_next().await.unwrap() {
67                    match request {
68                        fpower::AdminRequest::Shutdown { options, responder: _ } => {
69                            info!("MockStateControlAdminService: received Shutdown request");
70                            this.shutdown_received_sender
71                                .lock()
72                                .await
73                                .try_send(options)
74                                .expect("Failed to notify shutdown");
75                        }
76                        _ => {
77                            unimplemented!();
78                        }
79                    }
80                }
81            })
82            .detach();
83        });
84
85        fs.serve_connection(outgoing_dir).unwrap();
86        fs.collect::<()>().await;
87
88        Ok(())
89    }
90
91    /// Waits for the mock to receive a fidl.fuchsia.SystemController/Shutdown request.
92    pub async fn wait_for_shutdown_request(&self) -> ShutdownOptions {
93        self.shutdown_received_receiver
94            .lock()
95            .await
96            .next()
97            .await
98            .expect("Failed to wait for shutdown request")
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use fuchsia_component::client::connect_to_protocol_at_dir_svc;
106
107    #[fuchsia::test]
108    async fn test_shutdown() {
109        // Create and serve the mock service
110        let (dir, outgoing_dir) = fidl::endpoints::create_proxy::<DirectoryMarker>();
111        let mock = MockStateControlAdminService::new();
112        let _task = fasync::Task::local(mock.clone().run_inner(outgoing_dir));
113
114        // Connect to the mock server
115        let controller_client =
116            connect_to_protocol_at_dir_svc::<fpower::AdminMarker>(&dir).unwrap();
117
118        // Call the server's `shutdown` method and verify the mock sees the request
119        let _task = fuchsia_async::Task::local(controller_client.shutdown(&ShutdownOptions {
120            action: Some(fpower::ShutdownAction::Reboot),
121            reasons: Some(vec![fpower::ShutdownReason::HighTemperature]),
122            ..Default::default()
123        }));
124        mock.wait_for_shutdown_request().await;
125    }
126}