#![deny(missing_docs)]
use {
fidl::endpoints::{RequestStream, ServerEnd},
fidl_fuchsia_hardware_display::{self as display, CoordinatorMarker, CoordinatorRequestStream},
fidl_fuchsia_hardware_display_types as display_types, fuchsia_zircon as zx,
itertools::Itertools,
std::collections::HashMap,
thiserror::Error,
};
#[derive(Error, Debug)]
pub enum MockCoordinatorError {
#[error("duplicate IDs provided")]
DuplicateIds,
#[error("FIDL error: {0}")]
FidlError(#[from] fidl::Error),
}
pub type Result<T> = std::result::Result<T, MockCoordinatorError>;
pub struct MockCoordinator {
#[allow(unused)]
stream: CoordinatorRequestStream,
control_handle: <CoordinatorRequestStream as RequestStream>::ControlHandle,
displays: HashMap<DisplayId, display::Info>,
}
#[derive(Eq, Hash, Ord, PartialOrd, PartialEq)]
struct DisplayId(u64);
impl MockCoordinator {
pub fn new(server_end: ServerEnd<CoordinatorMarker>) -> Result<MockCoordinator> {
let (stream, control_handle) = server_end.into_stream_and_control_handle()?;
Ok(MockCoordinator { stream, control_handle, displays: HashMap::new() })
}
pub fn assign_displays(&mut self, displays: Vec<display::Info>) -> Result<()> {
let mut map = HashMap::new();
if !displays.into_iter().all(|info| map.insert(DisplayId(info.id.value), info).is_none()) {
return Err(MockCoordinatorError::DuplicateIds);
}
let added: Vec<_> = map
.iter()
.sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
.map(|(_, info)| info.clone())
.collect();
let removed: Vec<display_types::DisplayId> =
self.displays.iter().map(|(_, info)| info.id).collect();
self.displays = map;
self.control_handle.send_on_displays_changed(&added, &removed)?;
Ok(())
}
pub fn emit_vsync_event(
&self,
display_id_value: u64,
stamp: display_types::ConfigStamp,
) -> Result<()> {
self.control_handle
.send_on_vsync(
&display_types::DisplayId { value: display_id_value },
zx::Time::get_monotonic().into_nanos() as u64,
&stamp,
0,
)
.map_err(MockCoordinatorError::from)
}
}
pub fn create_proxy_and_mock() -> Result<(display::CoordinatorProxy, MockCoordinator)> {
let (proxy, server) = fidl::endpoints::create_proxy::<CoordinatorMarker>()?;
Ok((proxy, MockCoordinator::new(server)?))
}
#[cfg(test)]
mod tests {
use super::*;
use {
anyhow::{Context, Result},
fidl_fuchsia_hardware_display as display,
futures::{future, TryStreamExt},
};
async fn wait_for_displays_changed_event(
events: &mut display::CoordinatorEventStream,
) -> Result<(Vec<display::Info>, Vec<display_types::DisplayId>)> {
let mut stream = events.try_filter_map(|event| match event {
display::CoordinatorEvent::OnDisplaysChanged { added, removed } => {
future::ok(Some((added, removed)))
}
_ => future::ok(None),
});
stream.try_next().await?.context("failed to listen to coordinator events")
}
#[fuchsia::test]
async fn assign_displays_fails_with_duplicate_display_ids() {
let displays = vec![
display::Info {
id: display_types::DisplayId { value: 1 },
modes: Vec::new(),
pixel_format: Vec::new(),
manufacturer_name: "Foo".to_string(),
monitor_name: "what".to_string(),
monitor_serial: "".to_string(),
horizontal_size_mm: 0,
vertical_size_mm: 0,
using_fallback_size: false,
},
display::Info {
id: display_types::DisplayId { value: 1 },
modes: Vec::new(),
pixel_format: Vec::new(),
manufacturer_name: "Bar".to_string(),
monitor_name: "who".to_string(),
monitor_serial: "".to_string(),
horizontal_size_mm: 0,
vertical_size_mm: 0,
using_fallback_size: false,
},
];
let (_proxy, mut mock) = create_proxy_and_mock().expect("failed to create MockCoordinator");
let result = mock.assign_displays(displays);
assert!(result.is_err());
}
#[fuchsia::test]
async fn assign_displays_displays_added() -> Result<()> {
let displays = vec![
display::Info {
id: display_types::DisplayId { value: 1 },
modes: Vec::new(),
pixel_format: Vec::new(),
manufacturer_name: "Foo".to_string(),
monitor_name: "what".to_string(),
monitor_serial: "".to_string(),
horizontal_size_mm: 0,
vertical_size_mm: 0,
using_fallback_size: false,
},
display::Info {
id: display_types::DisplayId { value: 2 },
modes: Vec::new(),
pixel_format: Vec::new(),
manufacturer_name: "Bar".to_string(),
monitor_name: "who".to_string(),
monitor_serial: "".to_string(),
horizontal_size_mm: 0,
vertical_size_mm: 0,
using_fallback_size: false,
},
];
let (proxy, mut mock) = create_proxy_and_mock().expect("failed to create MockCoordinator");
mock.assign_displays(displays.clone())?;
let mut events = proxy.take_event_stream();
let (added, removed) = wait_for_displays_changed_event(&mut events).await?;
assert_eq!(added, displays);
assert_eq!(removed, vec![]);
Ok(())
}
#[fuchsia::test]
async fn assign_displays_displays_removed() -> Result<()> {
let displays = vec![display::Info {
id: display_types::DisplayId { value: 1 },
modes: Vec::new(),
pixel_format: Vec::new(),
manufacturer_name: "Foo".to_string(),
monitor_name: "what".to_string(),
monitor_serial: "".to_string(),
horizontal_size_mm: 0,
vertical_size_mm: 0,
using_fallback_size: false,
}];
let (proxy, mut mock) = create_proxy_and_mock().expect("failed to create MockCoordinator");
mock.assign_displays(displays)?;
let mut events = proxy.take_event_stream();
let _ = wait_for_displays_changed_event(&mut events).await?;
mock.assign_displays(vec![])?;
let (added, removed) = wait_for_displays_changed_event(&mut events).await?;
assert_eq!(added, vec![]);
assert_eq!(removed, vec![display_types::DisplayId { value: 1 }]);
Ok(())
}
}