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