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;
    }
}