blob: 33937fade026f5370da2ca12e5ef05c56721ea3a [file] [log] [blame]
// Copyright 2019 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 anyhow::{Context as _, Error};
use fidl::client::QueryResponseFut;
use fidl_fuchsia_ui_input as ui_input;
use fidl_fuchsia_ui_input2 as ui_input2;
use fidl_fuchsia_ui_views as ui_views;
use fuchsia_async as fasync;
use fuchsia_component::client::connect_to_service;
use fuchsia_zircon as zx;
use futures::lock::Mutex;
use futures::stream::StreamExt;
use serde::{Deserialize, Deserializer};
use serde_json::{self as json};
use std::fs;
use std::sync::{Arc, Once};
static START: Once = Once::new();
const DEFAULT_GOLDEN_PATH: &'static str = "/pkg/data/goldens/en-us.json";
fn clone_semantic_key(semantic_key: &ui_input2::SemanticKey) -> ui_input2::SemanticKey {
match semantic_key {
ui_input2::SemanticKey::Symbol(symbol) => {
ui_input2::SemanticKey::Symbol(symbol.to_string())
}
ui_input2::SemanticKey::Action(action) => ui_input2::SemanticKey::Action(*action),
_ => panic!("UnknownVariant"),
}
}
#[derive(Deserialize, Debug)]
struct GoldenTestSuite {
test_cases: Vec<TestCase>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
enum SemanticKey {
Symbol(String),
#[serde(with = "hex_serde")]
Action(u32),
}
impl From<SemanticKey> for ui_input2::SemanticKey {
fn from(def: SemanticKey) -> ui_input2::SemanticKey {
match def {
SemanticKey::Symbol(symbol) => ui_input2::SemanticKey::Symbol(symbol.to_string()),
SemanticKey::Action(action) => ui_input2::SemanticKey::Action(
ui_input2::SemanticKeyAction::from_primitive(action)
.unwrap_or_else(|| panic!("Unable to parse semantic key action {:?}", def)),
),
}
}
}
#[derive(Deserialize, Debug, Copy, Clone)]
enum Modifiers {
Shift = 0x00000001,
LeftShift = 0x00000002,
RightShift = 0x00000004,
Control = 0x00000008,
LeftControl = 0x00000010,
RightControl = 0x00000020,
Alt = 0x00000040,
LeftAlt = 0x00000080,
RightAlt = 0x00000100,
Meta = 0x00000200,
LeftMeta = 0x00000400,
RightMeta = 0x00000800,
CapsLock = 0x00001000,
NumLock = 0x00002000,
ScrollLock = 0x00004000,
}
impl From<Modifiers> for ui_input2::Modifiers {
fn from(def: Modifiers) -> ui_input2::Modifiers {
ui_input2::Modifiers::from_bits(def as u32)
.unwrap_or_else(|| panic!("Unable to parse modifiers {:?}", def))
}
}
#[derive(Deserialize, Debug)]
struct TestCase {
#[serde(deserialize_with = "deserialize_key")]
key: ui_input2::Key,
#[serde(deserialize_with = "deserialize_modifiers")]
modifiers: Option<ui_input2::Modifiers>,
#[serde(deserialize_with = "deserialize_semantic_key")]
semantic_key: ui_input2::SemanticKey,
}
fn deserialize_key<'de, D>(deserializer: D) -> Result<ui_input2::Key, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let s_lower = s.to_lowercase();
let without_prefix = s_lower.trim_start_matches("0x");
let index = u32::from_str_radix(&without_prefix, 16).map_err(serde::de::Error::custom);
index.map(|i| {
let key = ui_input2::Key::from_primitive(i);
key.unwrap()
})
}
fn deserialize_modifiers<'de, D>(deserializer: D) -> Result<Option<ui_input2::Modifiers>, D::Error>
where
D: Deserializer<'de>,
{
let s: Vec<Modifiers> =
Deserialize::deserialize(deserializer).map_err(serde::de::Error::custom)?;
if s.len() == 0 {
Ok(None)
} else {
Ok(Some(s.iter().fold(ui_input2::Modifiers::empty(), |acc, &m| acc | m.into())))
}
}
fn deserialize_semantic_key<'de, D>(deserializer: D) -> Result<ui_input2::SemanticKey, D::Error>
where
D: Deserializer<'de>,
{
let s: SemanticKey =
Deserialize::deserialize(deserializer).map_err(serde::de::Error::custom)?;
Ok(s.into())
}
mod hex_serde {
use serde::Deserialize;
pub fn deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let s_lower = s.to_lowercase();
let without_prefix = s_lower.trim_start_matches("0x");
u32::from_str_radix(&without_prefix, 16).map_err(serde::de::Error::custom)
}
}
struct KeyboardService {
_keyboard: ui_input2::KeyboardProxy,
ime_service: ui_input::ImeServiceProxy,
listener: ui_input2::KeyListenerRequestStream,
}
impl KeyboardService {
async fn new() -> Result<KeyboardService, Error> {
START.call_once(|| {
fuchsia_syslog::init_with_tags(&["keyboard_test"])
.expect("keyboard test syslog init should not fail");
});
let keyboard = connect_to_service::<ui_input2::KeyboardMarker>()
.context("Failed to connect to Keyboard service")?;
let ime_service = connect_to_service::<ui_input::ImeServiceMarker>()
.context("Failed to connect to IME service")?;
let (listener_client_end, listener) =
fidl::endpoints::create_request_stream::<ui_input2::KeyListenerMarker>()?;
// Set listener and view ref.
let (raw_event_pair, _) = zx::EventPair::create()?;
let view_ref = &mut ui_views::ViewRef { reference: raw_event_pair };
keyboard.set_listener(view_ref, listener_client_end).await.expect("set_listener");
Ok(KeyboardService { ime_service, listener, _keyboard: keyboard })
}
fn press_key_low_level(
&self,
key: ui_input2::Key,
modifiers: Option<ui_input2::Modifiers>,
) -> QueryResponseFut<bool> {
// Process key event that triggers a shortcut.
let event = ui_input2::KeyEvent {
key: Some(key),
modifiers: modifiers,
phase: Some(ui_input2::KeyEventPhase::Pressed),
physical_key: None,
semantic_key: None,
..ui_input2::KeyEvent::empty()
};
self.ime_service.dispatch_key(event)
}
async fn press_key(
&mut self,
key: ui_input2::Key,
modifiers: Option<ui_input2::Modifiers>,
) -> ui_input2::KeyEvent {
let was_handled_fut = self.press_key_low_level(key, modifiers);
let event = match self.listener.next().await {
Some(Ok(ui_input2::KeyListenerRequest::OnKeyEvent { event, responder, .. })) => {
responder.send(ui_input2::Status::Handled).expect("responding from key listener");
Some(event)
}
None => None,
_ => panic!("Error from listener.next() while pressing {:?}", (key, modifiers)),
};
let was_handled = was_handled_fut.await.expect("press_key");
assert_eq!(true, was_handled);
event.unwrap()
}
}
/// Generates test cases with variations for left and right modifiers (e.g. Shift, Alt, etc)
fn test_case_to_variations(test: TestCase) -> Vec<TestCase> {
let TestCase { key, modifiers: test_modifiers, semantic_key } = test;
let test_modifiers = match test_modifiers {
Some(m) => m,
None => return vec![TestCase { key, modifiers: test_modifiers, semantic_key }],
};
let modifier_variations = [
(
ui_input2::Modifiers::Shift,
[ui_input2::Modifiers::LeftShift, ui_input2::Modifiers::RightShift],
),
(
ui_input2::Modifiers::Alt,
[ui_input2::Modifiers::LeftAlt, ui_input2::Modifiers::RightAlt],
),
(
ui_input2::Modifiers::Control,
[ui_input2::Modifiers::LeftControl, ui_input2::Modifiers::RightControl],
),
(
ui_input2::Modifiers::Meta,
[ui_input2::Modifiers::LeftMeta, ui_input2::Modifiers::RightMeta],
),
];
let test_variations = modifier_variations
.iter()
.flat_map(|(modifiers, variants)| {
if test_modifiers.contains(*modifiers) {
variants
.iter()
.map(|modifier_variant| TestCase {
key,
modifiers: Some(test_modifiers | *modifier_variant),
semantic_key: clone_semantic_key(&semantic_key),
})
.collect()
} else {
vec![]
}
})
.collect::<Vec<TestCase>>();
if test_variations.is_empty() {
vec![TestCase { key, modifiers: Some(test_modifiers), semantic_key }]
} else {
test_variations
}
}
#[fasync::run_singlethreaded(test)]
async fn test_goldens() -> Result<(), Error> {
let data = fs::read_to_string(DEFAULT_GOLDEN_PATH)?;
let goldens: GoldenTestSuite = json::from_str(&data)?;
let service = Arc::new(Mutex::new(KeyboardService::new().await?));
let with_variations = goldens.test_cases.into_iter().flat_map(test_case_to_variations);
futures::stream::iter(with_variations.into_iter())
.then(|TestCase { key, modifiers, semantic_key }| {
let service = service.clone();
async move {
let mut service = service.lock().await;
let event = service.press_key(key, modifiers).await;
let pressed_semantic_key = clone_semantic_key(
&event
.semantic_key
.unwrap_or_else(|| panic!("no semantic_key for {:?}", (key, modifiers))),
);
assert_eq!(
semantic_key,
pressed_semantic_key,
"Pressed key {:?} expected semantic key {:?}",
(key, modifiers),
semantic_key
);
}
})
.collect::<Vec<_>>()
.await;
Ok(())
}