blob: cdef2c1f1fbe631ca6f7b49b594d4dce695182fb [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.
#![cfg(test)]
use fidl_fuchsia_ui_keyboard_focus as fidl_focus;
use fuchsia_async as fasync;
use fuchsia_syslog::fx_log_debug;
use {
anyhow::{Context as _, Result},
fidl_fuchsia_input as input, fidl_fuchsia_ui_input3 as ui_input3,
fidl_fuchsia_ui_views as ui_views,
fuchsia_component::client::connect_to_service,
fuchsia_scenic as scenic,
futures::{
future,
stream::{FusedStream, StreamExt},
},
matches::assert_matches,
};
mod test_helpers;
fn create_key_down_event(key: input::Key, modifiers: ui_input3::Modifiers) -> ui_input3::KeyEvent {
ui_input3::KeyEvent {
key: Some(key),
modifiers: Some(modifiers),
type_: Some(ui_input3::KeyEventType::Pressed),
..ui_input3::KeyEvent::EMPTY
}
}
fn create_key_up_event(key: input::Key, modifiers: ui_input3::Modifiers) -> ui_input3::KeyEvent {
ui_input3::KeyEvent {
key: Some(key),
modifiers: Some(modifiers),
type_: Some(ui_input3::KeyEventType::Released),
..ui_input3::KeyEvent::EMPTY
}
}
async fn expect_key_event(
listener: &mut ui_input3::KeyboardListenerRequestStream,
) -> ui_input3::KeyEvent {
let listener_request = listener.next().await;
if let Some(Ok(ui_input3::KeyboardListenerRequest::OnKeyEvent { event, responder, .. })) =
listener_request
{
responder.send(ui_input3::KeyEventStatus::Handled).expect("responding from key listener");
event
} else {
panic!("Expected key event, got {:?}", listener_request);
}
}
async fn dispatch_and_expect_key_event<'a>(
key_dispatcher: &'a test_helpers::KeySimulator<'a>,
listener: &mut ui_input3::KeyboardListenerRequestStream,
event: ui_input3::KeyEvent,
) -> Result<()> {
let (was_handled, event_result) =
future::join(key_dispatcher.dispatch(event.clone()), expect_key_event(listener)).await;
assert_eq!(was_handled?, true);
assert_eq!(event_result.key, event.key);
assert_eq!(event_result.type_, event.type_);
Ok(())
}
async fn focus_and_expect_key_event(
focus_ctl: &fidl_focus::ControllerProxy,
listener: &mut ui_input3::KeyboardListenerRequestStream,
view_ref: &mut ui_views::ViewRef,
event: ui_input3::KeyEvent,
) -> Result<()> {
let (event_result, focus_result) =
future::join(expect_key_event(listener), focus_ctl.notify(view_ref)).await;
focus_result?;
assert_eq!(event, event_result);
Ok(())
}
async fn expect_key_and_modifiers(
listener: &mut ui_input3::KeyboardListenerRequestStream,
key: input::Key,
modifiers: ui_input3::Modifiers,
) {
let event = expect_key_event(listener).await;
assert_eq!(event.key, Some(key));
assert_eq!(event.modifiers, Some(modifiers));
}
async fn test_disconnecting_keyboard_client_disconnects_listener_with_connections(
focus_ctl: fidl_focus::ControllerProxy,
key_simulator: &'_ test_helpers::KeySimulator<'_>,
keyboard_service_client: ui_input3::KeyboardProxy,
keyboard_service_other_client: &ui_input3::KeyboardProxy,
) -> Result<()> {
fx_log_debug!("test_disconnecting_keyboard_client_disconnects_listener_with_connections");
// Create fake client.
let (listener_client_end, mut listener) =
fidl::endpoints::create_request_stream::<ui_input3::KeyboardListenerMarker>()?;
let view_ref = scenic::ViewRefPair::new()?.view_ref;
keyboard_service_client
.add_listener(&mut scenic::duplicate_view_ref(&view_ref)?, listener_client_end)
.await
.expect("add_listener for first client");
// Create another fake client.
let (other_listener_client_end, mut other_listener) =
fidl::endpoints::create_request_stream::<ui_input3::KeyboardListenerMarker>()?;
let other_view_ref = scenic::ViewRefPair::new()?.view_ref;
keyboard_service_other_client
.add_listener(&mut scenic::duplicate_view_ref(&other_view_ref)?, other_listener_client_end)
.await
.expect("add_listener for another client");
// Focus second client.
focus_ctl.notify(&mut scenic::duplicate_view_ref(&other_view_ref)?).await?;
// Drop proxy, emulating first client disconnecting from it.
std::mem::drop(keyboard_service_client);
// Expect disconnected client key event listener to be disconnected as well.
assert_matches!(listener.next().await, None);
assert_matches!(listener.is_terminated(), true);
// Ensure that the other client is still connected.
let (key, modifiers) = (input::Key::A, ui_input3::Modifiers::CapsLock);
let dispatched_event = create_key_down_event(key, modifiers);
let (was_handled, _) = future::join(
key_simulator.dispatch(dispatched_event),
expect_key_and_modifiers(&mut other_listener, key, modifiers),
)
.await;
assert_eq!(was_handled?, true);
let dispatched_event = create_key_up_event(key, modifiers);
let (was_handled, _) = future::join(
key_simulator.dispatch(dispatched_event),
expect_key_and_modifiers(&mut other_listener, key, modifiers),
)
.await;
assert_eq!(was_handled?, true);
Ok(())
}
fn connect_to_focus_controller() -> Result<fidl_focus::ControllerProxy> {
connect_to_service::<fidl_focus::ControllerMarker>()
.context("Failed to connect to fuchsia.ui.keyboard.focus.Controller")
}
#[fasync::run_singlethreaded(test)]
async fn test_disconnecting_keyboard_client_disconnects_listener_via_key_event_injector(
) -> Result<()> {
fuchsia_syslog::init_with_tags(&["keyboard3_integration_test"])
.expect("syslog init should not fail");
let key_event_injector = connect_to_service::<ui_input3::KeyEventInjectorMarker>()
.context("Failed to connect to fuchsia.ui.input3.KeyEventInjector")?;
let key_dispatcher =
test_helpers::KeyEventInjectorDispatcher { key_event_injector: &key_event_injector };
let key_simulator = test_helpers::KeySimulator::new(&key_dispatcher);
let keyboard_service_client = connect_to_service::<ui_input3::KeyboardMarker>()
.context("Failed to connect to input3 Keyboard service")?;
let keyboard_service_other_client = connect_to_service::<ui_input3::KeyboardMarker>()
.context("Failed to establish another connection to input3 Keyboard service")?;
test_disconnecting_keyboard_client_disconnects_listener_with_connections(
connect_to_focus_controller()?,
&key_simulator,
// This one will be dropped as part of the test, so needs to be moved.
keyboard_service_client,
&keyboard_service_other_client,
)
.await
}
async fn test_sync_cancel_with_connections(
focus_ctl: fidl_focus::ControllerProxy,
key_simulator: &'_ test_helpers::KeySimulator<'_>,
keyboard_service_client: &ui_input3::KeyboardProxy,
keyboard_service_other_client: &ui_input3::KeyboardProxy,
) -> Result<()> {
// Create fake client.
let (listener_client_end, mut listener) =
fidl::endpoints::create_request_stream::<ui_input3::KeyboardListenerMarker>()?;
let view_ref = scenic::ViewRefPair::new()?.view_ref;
keyboard_service_client
.add_listener(&mut scenic::duplicate_view_ref(&view_ref)?, listener_client_end)
.await
.expect("add_listener for first client");
// Create another fake client.
let (other_listener_client_end, mut other_listener) =
fidl::endpoints::create_request_stream::<ui_input3::KeyboardListenerMarker>()?;
let other_view_ref = scenic::ViewRefPair::new()?.view_ref;
keyboard_service_other_client
.add_listener(&mut scenic::duplicate_view_ref(&other_view_ref)?, other_listener_client_end)
.await
.expect("add_listener for another client");
let key1 = input::Key::A;
let event1_press = ui_input3::KeyEvent {
key: Some(key1),
type_: Some(ui_input3::KeyEventType::Pressed),
..ui_input3::KeyEvent::EMPTY
};
let event1_release = ui_input3::KeyEvent {
key: Some(key1),
type_: Some(ui_input3::KeyEventType::Released),
..ui_input3::KeyEvent::EMPTY
};
// Focus first client.
focus_ctl.notify(&mut scenic::duplicate_view_ref(&view_ref)?).await?;
// Press the key.
dispatch_and_expect_key_event(&key_simulator, &mut listener, event1_press).await?;
// Focus second client, expect sync event.
focus_and_expect_key_event(
&focus_ctl,
&mut other_listener,
&mut scenic::duplicate_view_ref(&other_view_ref)?,
ui_input3::KeyEvent {
key: Some(input::Key::A),
type_: Some(ui_input3::KeyEventType::Sync),
..ui_input3::KeyEvent::EMPTY
},
)
.await?;
// Release the key.
dispatch_and_expect_key_event(&key_simulator, &mut other_listener, event1_release).await?;
// Focus first client again, expect CANCEL for the first key.
focus_and_expect_key_event(
&focus_ctl,
&mut listener,
&mut scenic::duplicate_view_ref(&view_ref)?,
ui_input3::KeyEvent {
key: Some(input::Key::A),
type_: Some(ui_input3::KeyEventType::Cancel),
..ui_input3::KeyEvent::EMPTY
},
)
.await?;
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_sync_cancel_via_key_event_injector() -> Result<()> {
fuchsia_syslog::init_with_tags(&["keyboard3_integration_test"])
.expect("syslog init should not fail");
// This test dispatches keys via KeyEventInjector.
let key_event_injector = connect_to_service::<ui_input3::KeyEventInjectorMarker>()
.context("Failed to connect to fuchsia.ui.input3.KeyEventInjector")?;
let key_dispatcher =
test_helpers::KeyEventInjectorDispatcher { key_event_injector: &key_event_injector };
let key_simulator = test_helpers::KeySimulator::new(&key_dispatcher);
let keyboard_service_client = connect_to_service::<ui_input3::KeyboardMarker>()
.context("Failed to connect to input3 Keyboard service")?;
let keyboard_service_other_client = connect_to_service::<ui_input3::KeyboardMarker>()
.context("Failed to establish another connection to input3 Keyboard service")?;
test_sync_cancel_with_connections(
connect_to_focus_controller()?,
&key_simulator,
&keyboard_service_client,
&keyboard_service_other_client,
)
.await
}