power_manager_integration_test_lib/mocks/
activity_service.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
117
118
119
120
121
122
123
124
125
126
127
// 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_ui_activity as factivity, fuchsia_async as fasync};

/// Mocks the fuchsia.ui.activity.Provider service to be used in integration tests.
pub struct MockActivityService {
    /// Sends a new activity state to the mock server. The expected usage is that the test holds the
    /// sender end to communicate new activity state values to the server on the receiver end.
    state_sender: Mutex<mpsc::Sender<factivity::State>>,

    /// Receiver end for activity state changes. When the server reads the new state from the
    /// receiver end, it will send that new state out to any listener clients that have previously
    /// called `WatchState`.
    state_receiver: Mutex<mpsc::Receiver<factivity::State>>,
}

impl MockActivityService {
    pub fn new() -> Arc<MockActivityService> {
        let (state_sender, state_receiver) = mpsc::channel(1);
        Arc::new(Self {
            state_sender: Mutex::new(state_sender),
            state_receiver: Mutex::new(state_receiver),
        })
    }

    /// Runs the mock using the provided `LocalComponentHandles`.
    ///
    /// 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_activity_service = MockActivityService::new();
    ///     let activity_service_child = realm_builder
    ///         .add_local_child(
    ///             "activity_service",
    ///             move |handles| Box::pin(mock_activity_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: factivity::ProviderRequestStream| {
            let this = self.clone();
            fasync::Task::local(async move {
                info!("MockActivityService: new connection");
                let factivity::ProviderRequest::WatchState { listener, .. } =
                    stream.try_next().await.unwrap().unwrap();
                info!("MockActivityService: received WatchState request");
                let listener = listener.into_proxy();
                while let Some(state) = this.state_receiver.lock().await.next().await {
                    info!("MockActivityService: sending activity state: {:?}", state);
                    let _ = listener.on_state_changed(state, 0).await;
                }
                info!("MockActivityService: closing connection")
            })
            .detach();
        });

        fs.serve_connection(outgoing_dir).unwrap();
        fs.collect::<()>().await;

        Ok(())
    }

    pub async fn set_activity_state(&self, state: factivity::State) {
        info!("MockActivityService: set activity state: {:?}", state);
        self.state_sender.lock().await.try_send(state).expect("try_send() failed");
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use assert_matches::assert_matches;
    use fuchsia_component::client::connect_to_protocol_at_dir_svc;

    #[fuchsia::test]
    async fn test_set_activity_state() {
        // Create and serve the mock service
        let (dir, outgoing_dir) = fidl::endpoints::create_proxy::<DirectoryMarker>();
        let mock = MockActivityService::new();
        let _task = fasync::Task::local(mock.clone().run_inner(outgoing_dir));

        // Connect to the mock server
        let provider_client =
            connect_to_protocol_at_dir_svc::<factivity::ProviderMarker>(&dir).unwrap();

        // Call the server's `watch_state` method, providing a Listener client end
        let (listener_client, mut listener_stream) =
            fidl::endpoints::create_request_stream::<factivity::ListenerMarker>();
        provider_client.watch_state(listener_client).unwrap();

        // Set `Active` state on the mock and verify the listener sees the correct state
        mock.set_activity_state(factivity::State::Active).await;
        let factivity::ListenerRequest::OnStateChanged { state, responder, .. } =
            listener_stream.next().await.unwrap().unwrap();
        assert_matches!(responder.send(), Ok(()));
        assert_eq!(state, factivity::State::Active);

        // Set `Idle` state on the mock and verify the listener sees the correct state
        mock.set_activity_state(factivity::State::Idle).await;
        let factivity::ListenerRequest::OnStateChanged { state, responder, .. } =
            listener_stream.next().await.unwrap().unwrap();
        assert_matches!(responder.send(), Ok(()));
        assert_eq!(state, factivity::State::Idle);
    }
}