blob: 4d61d18ca85afa86f56d12edf794833658eb2fff [file] [log] [blame]
// Copyright 2020 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 {
anyhow::{Context as _, Error, Result},
fidl_fuchsia_input as input, fidl_fuchsia_ui_focus as ui_focus,
fidl_fuchsia_ui_input3 as ui_input3, fidl_fuchsia_ui_shortcut as ui_shortcut,
fidl_fuchsia_ui_views as ui_views,
fuchsia_component::client::connect_to_protocol,
fuchsia_scenic as scenic,
fuchsia_syslog::fx_log_debug,
std::sync::{self, Once},
};
static START: Once = Once::new();
/// A reference-counted value that can be latched to the value of "true" once. Subsequent
/// invocations do not change the latch value, but the latch can be queried as to how many times
/// a valid "set" happened, using `get_num_valid_sets`.
#[derive(Clone, Debug)]
pub struct Latch {
inner: sync::Arc<std::cell::Cell<i32>>,
}
impl Latch {
pub fn new() -> Self {
Latch { inner: sync::Arc::new(std::cell::Cell::new(0)) }
}
/// Returns the current value stored in the [Latch].
pub fn get_value(&self) -> bool {
self.get_num_valid_sets() > 0
}
/// Returns the number of calls that the latch was set.
pub fn get_num_valid_sets(&self) -> i32 {
self.inner.get()
}
/// Latches the `value` if it is "true". Any subsequent calls to [Latch::get_value] will
/// return `true`.
pub fn latch(&self, value: bool) {
if value {
let val = self.get_num_valid_sets();
self.inner.replace(val + 1);
}
}
}
/// Creates instances of FIDL fuchsia.ui.shortcut.Shortcut.
pub struct ShortcutBuilder {
shortcut: ui_shortcut::Shortcut,
}
#[allow(dead_code)]
impl ShortcutBuilder {
pub fn new() -> Self {
Self { shortcut: ui_shortcut::Shortcut { ..ui_shortcut::Shortcut::EMPTY } }
}
/// Creates a new instance of fuchsia.ui.shortcut.Shortcut.
pub fn build(&self) -> ui_shortcut::Shortcut {
ui_shortcut::Shortcut {
id: self.shortcut.id,
use_priority: self.shortcut.use_priority,
trigger: self.shortcut.trigger,
key3: self.shortcut.key3,
keys_required: self.shortcut.keys_required.clone(),
..ui_shortcut::Shortcut::EMPTY
}
}
pub fn set_id(mut self, id: u32) -> Self {
self.shortcut.id = Some(id);
self
}
pub fn set_use_priority(mut self, use_priority: bool) -> Self {
self.shortcut.use_priority = Some(use_priority);
self
}
pub fn set_trigger(mut self, trigger: ui_shortcut::Trigger) -> Self {
self.shortcut.trigger = Some(trigger);
self
}
pub fn set_key3(mut self, key3: input::Key) -> Self {
self.shortcut.key3 = Some(key3);
self
}
pub fn set_keys_required(mut self, keys_required: Vec<input::Key>) -> Self {
self.shortcut.keys_required = Some(keys_required);
self
}
}
/// Test helper for FIDL fuchsia.ui.shortcut.Registry service.
pub struct RegistryService {
pub view_ref: ui_views::ViewRef,
pub registry: ui_shortcut::RegistryProxy,
pub listener: ui_shortcut::ListenerRequestStream,
}
impl RegistryService {
/// Creates the instance of the test helper and connects to shortcut registry service.
pub async fn new() -> Result<Self> {
RegistryService::new_with_view_ref(RegistryService::new_view_ref()).await
}
/// Registers a new shortcut with the shortcut registry service.
/// Returns a future that resolves to the FIDL response.
pub async fn register_shortcut(&self, shortcut: ui_shortcut::Shortcut) -> Result<()> {
self.registry.register_shortcut(shortcut).await.map_err(Into::into)
}
/// Creates a new dummy `ViewRef` for testing.
pub fn new_view_ref() -> fidl_fuchsia_ui_views::ViewRef {
scenic::ViewRefPair::new().expect("could not create ViewRef").view_ref
}
/// Creates a new client with a custom view ref. Use with, for example
/// [RegistryService::new_view_ref].
pub async fn new_with_view_ref(view_ref: fidl_fuchsia_ui_views::ViewRef) -> Result<Self> {
START.call_once(|| {
fuchsia_syslog::init_with_tags(&["shortcut"])
.expect("shortcut syslog init should not fail");
});
let registry = connect_to_protocol::<ui_shortcut::RegistryMarker>()
.context("Failed to connect to Shortcut registry service")?;
let (listener_client_end, listener) =
fidl::endpoints::create_request_stream::<ui_shortcut::ListenerMarker>()?;
// Set listener for the supplied view_ref.
registry
.set_view(&mut fuchsia_scenic::duplicate_view_ref(&view_ref)?, listener_client_end)?;
Ok(Self { registry, listener, view_ref })
}
}
/// Test helper for FIDL fuchsia.ui.shortcut.Manager service.
pub struct ManagerService {
manager: ui_shortcut::ManagerProxy,
}
#[allow(dead_code)]
impl ManagerService {
/// Creates the instance of the test helper and connects to shortcut manager service.
pub async fn new() -> Result<Self, Error> {
START.call_once(|| {
fuchsia_syslog::init_with_tags(&["shortcut"])
.expect("shortcut syslog init should not fail");
});
let manager = connect_to_protocol::<ui_shortcut::ManagerMarker>()
.context("Failed to connect to Shortcut manager service")?;
Ok(Self { manager })
}
/// Emulates a key3 event. Returns `true` if the key was handled, or error.
pub async fn send_key3_event(
&self,
key: input::Key,
event: ui_input3::KeyEventType,
) -> Result<bool> {
// Process key event that triggers a shortcut.
let event = ui_input3::KeyEvent {
timestamp: None,
type_: Some(event),
key: Some(key),
modifiers: None,
..ui_input3::KeyEvent::EMPTY
};
self.manager.handle_key3_event(event).await.map_err(Into::into)
}
/// Emulates sending the specified key events verbatim: can be used to send
/// asymmetric events, which allows testing for specific key chords.
/// Returns `Ok(true)` if at least one of the events in the stream was
/// handled.
pub async fn send_multiple_key3_event(
&self,
events: Vec<(input::Key, ui_input3::KeyEventType)>,
) -> Result<bool> {
let mut was_handled = false;
for (key, event_type) in events.into_iter() {
let event_handled = self.send_key3_event(key, event_type).await?;
was_handled = was_handled || event_handled;
}
Ok(was_handled)
}
/// Emulates a key press event using input3 interface. Returns a a FIDL response from manager
/// service.
pub async fn press_key3(&self, key: input::Key) -> Result<bool> {
self.send_key3_event(key, ui_input3::KeyEventType::Pressed).await
}
/// Emulates a key release event using input3 interface. Returns a FIDL response from manager
/// service.
pub async fn release_key3(&self, key: input::Key) -> Result<bool> {
self.send_key3_event(key, ui_input3::KeyEventType::Released).await
}
/// Emulates multiple key press events sequentially using input3 interface.
/// Returns `Ok(true)` if any of the keys were handled.
pub async fn press_multiple_key3(&self, keys: Vec<input::Key>) -> Result<bool> {
let mut was_handled = false;
let mut iter = keys.into_iter().peekable();
while let Some(key) = iter.next() {
fx_log_debug!("TestCase::press_multiple_keys: processing key: {:?}", &key);
let key_handled = self.press_key3(key).await?;
if key_handled && iter.peek().is_some() {
panic!("Shortcuts activated, but unused keys remained in the sequence!");
}
was_handled = was_handled || key_handled;
fx_log_debug!("TestCase::press_multiple_keys: was handled: {:?}", &was_handled);
}
Ok(was_handled)
}
/// Emulates multiple key release events sequentially using input3 interface.
/// Returns `Ok(true)` if any of the keys were handled.
pub async fn release_multiple_key3(&self, keys: Vec<input::Key>) -> Result<bool> {
let mut was_handled = false;
for key in keys.into_iter() {
let key_handled = self.release_key3(key).await?;
was_handled = was_handled || key_handled;
}
Ok(was_handled)
}
pub async fn set_focus_chain(&self, focus_chain: Vec<&ui_views::ViewRef>) -> Result<()> {
let focus_chain = ui_focus::FocusChain {
focus_chain: Some(
focus_chain
.into_iter()
.map(scenic::duplicate_view_ref)
.collect::<Result<Vec<_>, _>>()?,
),
..ui_focus::FocusChain::EMPTY
};
self.manager.handle_focus_change(focus_chain).await.map_err(Into::into)
}
}