common/
common.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 anyhow::Error;
6use async_trait::async_trait;
7use fidl_fuchsia_settings::*;
8use fidl_fuchsia_ui_brightness::{ControlRequest, ControlRequestStream};
9use fuchsia_async as fasync;
10use fuchsia_component::server::{ServiceFs, ServiceFsDir};
11use fuchsia_component_test::{
12    Capability, ChildOptions, LocalComponentHandles, RealmBuilder, RealmInstance, Ref, Route,
13};
14use futures::channel::mpsc::Sender;
15use futures::lock::Mutex;
16use futures::{SinkExt, StreamExt, TryStreamExt};
17use std::sync::atomic::{AtomicU32, Ordering};
18use std::sync::Arc;
19
20const COMPONENT_URL: &str = "#meta/setui_service.cm";
21
22#[derive(PartialEq, Debug)]
23pub enum Request {
24    SetAutoBrightness,
25    SetManualBrightness,
26}
27
28#[async_trait]
29pub trait Mocks {
30    async fn brightness_service_impl(
31        handles: LocalComponentHandles,
32        manual_brightness: Arc<Mutex<Option<f32>>>,
33        auto_brightness: Arc<Mutex<Option<bool>>>,
34        num_changes: Arc<AtomicU32>,
35        requests_sender: Sender<Request>,
36    ) -> Result<(), Error>;
37}
38
39#[async_trait]
40impl Mocks for DisplayTest {
41    // Mock the brightness dependency and verify the settings service interacts with the brightness
42    // service by checking input value changes through the various requests.
43    async fn brightness_service_impl(
44        handles: LocalComponentHandles,
45        manual_brightness: Arc<Mutex<Option<f32>>>,
46        auto_brightness: Arc<Mutex<Option<bool>>>,
47        num_changes: Arc<AtomicU32>,
48        requests_sender: Sender<Request>,
49    ) -> Result<(), Error> {
50        let mut fs = ServiceFs::new();
51        let _: &mut ServiceFsDir<'_, _> =
52            fs.dir("svc").add_fidl_service(move |mut stream: ControlRequestStream| {
53                let auto_brightness_handle = auto_brightness.clone();
54                let brightness_handle = manual_brightness.clone();
55                let num_changes_handle = num_changes.clone();
56                let mut requests_sender = requests_sender.clone();
57                fasync::Task::spawn(async move {
58                    while let Ok(Some(req)) = stream.try_next().await {
59                        // Support future expansion of FIDL.
60                        #[allow(unreachable_patterns)]
61                        match req {
62                            ControlRequest::WatchCurrentBrightness { responder } => {
63                                responder
64                                    .send(
65                                        brightness_handle
66                                            .lock()
67                                            .await
68                                            .expect("brightness not yet set"),
69                                    )
70                                    .unwrap();
71                            }
72                            ControlRequest::SetAutoBrightness { control_handle: _ } => {
73                                *auto_brightness_handle.lock().await = Some(true);
74                                let current_num = num_changes_handle.load(Ordering::Relaxed);
75                                (*num_changes_handle).store(current_num + 1, Ordering::Relaxed);
76                                requests_sender
77                                    .send(Request::SetAutoBrightness)
78                                    .await
79                                    .expect("Finished processing SetAutoBrightness call");
80                            }
81                            ControlRequest::SetManualBrightness { value, control_handle: _ } => {
82                                *brightness_handle.lock().await = Some(value);
83                                *auto_brightness_handle.lock().await = Some(false);
84                                let current_num = num_changes_handle.load(Ordering::Relaxed);
85                                (*num_changes_handle).store(current_num + 1, Ordering::Relaxed);
86                                requests_sender
87                                    .send(Request::SetManualBrightness)
88                                    .await
89                                    .expect("Finished processing SetManualBrightness call");
90                            }
91                            ControlRequest::WatchAutoBrightness { responder } => {
92                                responder
93                                    .send(auto_brightness_handle.lock().await.unwrap_or(false))
94                                    .unwrap();
95                            }
96                            _ => {}
97                        }
98                    }
99                })
100                .detach();
101            });
102        let _: &mut ServiceFs<_> = fs.serve_connection(handles.outgoing_dir).unwrap();
103        fs.collect::<()>().await;
104        Ok(())
105    }
106}
107
108pub struct DisplayTest {}
109
110impl DisplayTest {
111    // This function creates a realm without external brightness dependency, and is used for tests
112    // in the internal_display_integration_test package.
113    pub async fn create_realm() -> Result<RealmInstance, Error> {
114        let builder = RealmBuilder::new().await?;
115        // Add setui_service as child of the realm builder.
116        let setui_service =
117            builder.add_child("setui_service", COMPONENT_URL, ChildOptions::new()).await?;
118        let info = utils::SettingsRealmInfo {
119            builder,
120            settings: &setui_service,
121            has_config_data: true,
122            capabilities: vec!["fuchsia.settings.Display"],
123        };
124        // Add basic Settings service realm information.
125        utils::create_realm_basic(&info).await?;
126        let instance = info.builder.build().await?;
127        Ok(instance)
128    }
129
130    // This function creates a realm with external brightness dependency, and is used for tests in
131    // the display_integration_test package.
132    pub async fn create_realm_with_brightness_controller(
133        manual_brightness: Arc<Mutex<Option<f32>>>,
134        auto_brightness: Arc<Mutex<Option<bool>>>,
135        num_changes: Arc<AtomicU32>,
136        requests_sender: Sender<Request>,
137    ) -> Result<RealmInstance, Error> {
138        let builder = RealmBuilder::new().await?;
139        // Add setui_service as child of the realm builder.
140        let setui_service =
141            builder.add_child("setui_service", COMPONENT_URL, ChildOptions::new()).await?;
142        let info = utils::SettingsRealmInfo {
143            builder,
144            settings: &setui_service,
145            has_config_data: true,
146            capabilities: vec!["fuchsia.settings.Display"],
147        };
148        // Add basic Settings service realm information.
149        utils::create_realm_basic(&info).await?;
150        // Add mock camera dependency to test Display service with camera.
151        let brightness_service = info
152            .builder
153            .add_local_child(
154                "brightness_service",
155                move |handles: LocalComponentHandles| {
156                    Box::pin(DisplayTest::brightness_service_impl(
157                        handles,
158                        Arc::clone(&manual_brightness),
159                        Arc::clone(&auto_brightness),
160                        Arc::clone(&num_changes),
161                        requests_sender.clone(),
162                    ))
163                },
164                ChildOptions::new().eager(),
165            )
166            .await?;
167        info.builder
168            .add_route(
169                Route::new()
170                    .capability(Capability::protocol_by_name("fuchsia.ui.brightness.Control"))
171                    .from(&brightness_service)
172                    .to(Ref::parent())
173                    .to(&setui_service),
174            )
175            .await?;
176        // Provide LogSink to print out logs of the camera component for debugging purpose.
177        info.builder
178            .add_route(
179                Route::new()
180                    .capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
181                    .from(Ref::parent())
182                    .to(&brightness_service),
183            )
184            .await?;
185        let instance = info.builder.build().await?;
186        Ok(instance)
187    }
188
189    pub fn connect_to_displaymarker(instance: &RealmInstance) -> DisplayProxy {
190        return instance
191            .root
192            .connect_to_protocol_at_exposed_dir::<DisplayMarker>()
193            .expect("connecting to Display");
194    }
195
196    pub fn get_init_manual_brightness() -> Arc<Mutex<Option<f32>>> {
197        Arc::new(Mutex::new(None))
198    }
199
200    pub fn get_init_auto_brightness() -> Arc<Mutex<Option<bool>>> {
201        Arc::new(Mutex::new(None))
202    }
203
204    pub fn get_init_num_changes() -> Arc<AtomicU32> {
205        Arc::new(AtomicU32::new(0))
206    }
207
208    // This is for tests that testing screen enabled fields. It is used for both test packages.
209    pub async fn test_screen_enabled(display_proxy: DisplayProxy) {
210        // Test that if screen is turned off, it is reflected.
211        let mut display_settings = DisplaySettings::default();
212        display_settings.auto_brightness = Some(false);
213        display_proxy.set(&display_settings).await.expect("set completed").expect("set successful");
214
215        let mut display_settings = DisplaySettings::default();
216        display_settings.screen_enabled = Some(false);
217        display_proxy.set(&display_settings).await.expect("set completed").expect("set successful");
218
219        let settings = display_proxy.watch().await.expect("watch completed");
220
221        assert_eq!(settings.screen_enabled, Some(false));
222
223        // Test that if display is turned back on, the display and manual brightness are on.
224        let mut display_settings = DisplaySettings::default();
225        display_settings.screen_enabled = Some(true);
226        display_proxy.set(&display_settings).await.expect("set completed").expect("set successful");
227
228        let settings = display_proxy.watch().await.expect("watch completed");
229
230        assert_eq!(settings.screen_enabled, Some(true));
231        assert_eq!(settings.auto_brightness, Some(false));
232
233        // Test that if auto brightness is turned on, the display and auto brightness are on.
234        let mut display_settings = DisplaySettings::default();
235        display_settings.auto_brightness = Some(true);
236        display_proxy.set(&display_settings).await.expect("set completed").expect("set successful");
237
238        let settings = display_proxy.watch().await.expect("watch completed");
239
240        assert_eq!(settings.auto_brightness, Some(true));
241        assert_eq!(settings.screen_enabled, Some(true));
242    }
243}