1#![deny(missing_docs)]
6
7use display_types::INVALID_DISP_ID;
10use fidl::endpoints::{ClientEnd, ServerEnd};
11use fidl_fuchsia_hardware_display::{
12 self as display, CoordinatorListenerMarker, CoordinatorListenerProxy, CoordinatorMarker,
13 CoordinatorRequestStream, VsyncAckCookie,
14};
15use fidl_fuchsia_hardware_display_types as display_types;
16use itertools::Itertools;
17use std::collections::HashMap;
18use thiserror::Error;
19
20#[derive(Error, Debug)]
22pub enum MockCoordinatorError {
23 #[error("duplicate IDs provided")]
26 DuplicateIds,
27
28 #[error("FIDL error: {0}")]
30 FidlError(#[from] fidl::Error),
31}
32
33pub type Result<T> = std::result::Result<T, MockCoordinatorError>;
35
36pub struct MockCoordinator {
40 #[allow(unused)]
41 coordinator_stream: CoordinatorRequestStream,
42 listener_proxy: CoordinatorListenerProxy,
43
44 displays: HashMap<DisplayId, display::Info>,
45}
46
47#[derive(Eq, Hash, Ord, PartialOrd, PartialEq)]
50struct DisplayId(u64);
51
52impl MockCoordinator {
53 pub fn new(
55 coordinator_server_end: ServerEnd<CoordinatorMarker>,
56 listener_client_end: ClientEnd<CoordinatorListenerMarker>,
57 ) -> Result<MockCoordinator> {
58 let coordinator_stream = coordinator_server_end.into_stream();
59 let listener_proxy = listener_client_end.into_proxy();
60 Ok(MockCoordinator { coordinator_stream, listener_proxy, displays: HashMap::new() })
61 }
62
63 pub fn assign_displays(&mut self, displays: Vec<display::Info>) -> Result<()> {
71 let mut map = HashMap::new();
72 if !displays.into_iter().all(|info| map.insert(DisplayId(info.id.value), info).is_none()) {
73 return Err(MockCoordinatorError::DuplicateIds);
74 }
75
76 let added: Vec<_> = map
77 .iter()
78 .sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
79 .map(|(_, info)| info.clone())
80 .collect();
81 let removed: Vec<display_types::DisplayId> =
82 self.displays.iter().map(|(_, info)| info.id).collect();
83 self.displays = map;
84 self.listener_proxy.on_displays_changed(&added, &removed)?;
85 Ok(())
86 }
87
88 pub fn emit_vsync_event(
95 &self,
96 display_id_value: u64,
97 stamp: display::ConfigStamp,
98 ) -> Result<()> {
99 self.listener_proxy
100 .on_vsync(
101 &display_types::DisplayId { value: display_id_value },
102 zx::MonotonicInstant::get().into_nanos(),
103 &stamp,
104 &VsyncAckCookie { value: INVALID_DISP_ID },
105 )
106 .map_err(MockCoordinatorError::from)
107 }
108}
109
110pub fn create_proxy_and_mock(
116) -> Result<(display::CoordinatorProxy, display::CoordinatorListenerRequestStream, MockCoordinator)>
117{
118 let (coordinator_proxy, coordinator_server) =
119 fidl::endpoints::create_proxy::<CoordinatorMarker>();
120 let (listener_client, listener_requests) =
121 fidl::endpoints::create_request_stream::<CoordinatorListenerMarker>();
122 Ok((
123 coordinator_proxy,
124 listener_requests,
125 MockCoordinator::new(coordinator_server, listener_client)?,
126 ))
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use anyhow::{Context, Result};
133 use fidl_fuchsia_hardware_display as display;
134 use futures::{future, TryStreamExt};
135
136 async fn wait_for_displays_changed_event(
137 listener_requests: &mut display::CoordinatorListenerRequestStream,
138 ) -> Result<(Vec<display::Info>, Vec<display_types::DisplayId>)> {
139 let mut stream = listener_requests.try_filter_map(|event| match event {
140 display::CoordinatorListenerRequest::OnDisplaysChanged {
141 added,
142 removed,
143 control_handle: _,
144 } => future::ok(Some((added, removed))),
145 _ => future::ok(None),
146 });
147 stream.try_next().await?.context("failed to listen to coordinator listener requests")
148 }
149
150 #[fuchsia::test]
151 async fn assign_displays_fails_with_duplicate_display_ids() {
152 let displays = vec![
153 display::Info {
154 id: display_types::DisplayId { value: 1 },
155 modes: Vec::new(),
156 pixel_format: Vec::new(),
157 manufacturer_name: "Foo".to_string(),
158 monitor_name: "what".to_string(),
159 monitor_serial: "".to_string(),
160 horizontal_size_mm: 0,
161 vertical_size_mm: 0,
162 using_fallback_size: false,
163 },
164 display::Info {
165 id: display_types::DisplayId { value: 1 },
166 modes: Vec::new(),
167 pixel_format: Vec::new(),
168 manufacturer_name: "Bar".to_string(),
169 monitor_name: "who".to_string(),
170 monitor_serial: "".to_string(),
171 horizontal_size_mm: 0,
172 vertical_size_mm: 0,
173 using_fallback_size: false,
174 },
175 ];
176
177 let (_proxy, _listener_requests, mut mock) =
178 create_proxy_and_mock().expect("failed to create MockCoordinator");
179 let result = mock.assign_displays(displays);
180 assert!(result.is_err());
181 }
182
183 #[fuchsia::test]
184 async fn assign_displays_displays_added() -> Result<()> {
185 let displays = vec![
186 display::Info {
187 id: display_types::DisplayId { value: 1 },
188 modes: Vec::new(),
189 pixel_format: Vec::new(),
190 manufacturer_name: "Foo".to_string(),
191 monitor_name: "what".to_string(),
192 monitor_serial: "".to_string(),
193 horizontal_size_mm: 0,
194 vertical_size_mm: 0,
195 using_fallback_size: false,
196 },
197 display::Info {
198 id: display_types::DisplayId { value: 2 },
199 modes: Vec::new(),
200 pixel_format: Vec::new(),
201 manufacturer_name: "Bar".to_string(),
202 monitor_name: "who".to_string(),
203 monitor_serial: "".to_string(),
204 horizontal_size_mm: 0,
205 vertical_size_mm: 0,
206 using_fallback_size: false,
207 },
208 ];
209
210 let (_proxy, mut listener_requests, mut mock) =
211 create_proxy_and_mock().expect("failed to create MockCoordinator");
212 mock.assign_displays(displays.clone())?;
213
214 let (added, removed) = wait_for_displays_changed_event(&mut listener_requests).await?;
215 assert_eq!(added, displays);
216 assert_eq!(removed, vec![]);
217
218 Ok(())
219 }
220
221 #[fuchsia::test]
222 async fn assign_displays_displays_removed() -> Result<()> {
223 let displays = vec![display::Info {
224 id: display_types::DisplayId { value: 1 },
225 modes: Vec::new(),
226 pixel_format: Vec::new(),
227 manufacturer_name: "Foo".to_string(),
228 monitor_name: "what".to_string(),
229 monitor_serial: "".to_string(),
230 horizontal_size_mm: 0,
231 vertical_size_mm: 0,
232 using_fallback_size: false,
233 }];
234
235 let (_proxy, mut listener_requests, mut mock) =
236 create_proxy_and_mock().expect("failed to create MockCoordinator");
237 mock.assign_displays(displays)?;
238
239 let _ = wait_for_displays_changed_event(&mut listener_requests).await?;
240
241 mock.assign_displays(vec![])?;
243 let (added, removed) = wait_for_displays_changed_event(&mut listener_requests).await?;
244 assert_eq!(added, vec![]);
245 assert_eq!(removed, vec![display_types::DisplayId { value: 1 }]);
246
247 Ok(())
248 }
249}