blob: cb38e4e5954c8221b16d75eb2077d4c06baf23b7 [file] [log] [blame]
// Copyright 2022 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Testing the stateful Fuchsia Input3 keyboard interactions. This test case
// is not intended to be exhaustive: it is intended to capture the tests that
// demonstrate how we think Input3 interaction should work, and possibly
// regression tests if we catch some behavior that needs to be guarded long
// term. Pragmatically, this should be enough to ensure no specific bug
// happens twice.
#include <fuchsia/input/cpp/fidl.h>
#include <fuchsia/ui/input/cpp/fidl.h>
#include <fuchsia/ui/input3/cpp/fidl.h>
#include <zircon/time.h>
#include <vector>
#include <gtest/gtest.h>
#include "src/embedder/keyboard.h"
namespace embedder_testing {
namespace {
using fuchsia::input::Key;
using fuchsia::ui::input::kModifierCapsLock;
using fuchsia::ui::input::kModifierLeftAlt;
using fuchsia::ui::input::kModifierLeftControl;
using fuchsia::ui::input::kModifierLeftShift;
using fuchsia::ui::input::kModifierNone;
using fuchsia::ui::input::kModifierRightAlt;
using fuchsia::ui::input::kModifierRightControl;
using fuchsia::ui::input::kModifierRightShift;
using fuchsia::ui::input3::KeyEvent;
using fuchsia::ui::input3::KeyEventType;
using fuchsia::ui::input3::KeyMeaning;
class KeyboardTest : public testing::Test {
protected:
static void SetUpTestCase() { testing::Test::SetUpTestCase(); }
// Creates a new key event for testing.
KeyEvent NewKeyEvent(KeyEventType event_type, Key key) {
KeyEvent event;
// Assume events are delivered with correct timing.
event.set_timestamp(++timestamp_);
event.set_type(event_type);
event.set_key(key);
return event;
}
KeyEvent NewKeyEventWithMeaning(KeyEventType event_type, KeyMeaning key_meaning) {
KeyEvent event;
// Assume events are delivered with correct timing.
event.set_timestamp(++timestamp_);
event.set_type(event_type);
event.set_key_meaning(std::move(key_meaning));
return event;
}
// Makes the keyboard consume all the provided `events`. The end state of
// the keyboard is as if all of the specified events happened between the
// start state of the keyboard and its end state. Returns false if any of
// the event was not consumed.
bool ConsumeEvents(embedder::Keyboard* keyboard, const std::vector<KeyEvent>& events) {
for (const auto& event : events) {
KeyEvent e;
event.Clone(&e);
if (keyboard->ConsumeEvent(std::move(e)) == false) {
return false;
}
}
return true;
}
// Converts a pressed key to usage value.
uint32_t ToUsage(Key key) { return static_cast<uint64_t>(key) & 0xFFFFFFFF; }
private:
zx_time_t timestamp_ = 0;
};
// Checks whether the HID usage, page and ID values are reported correctly.
TEST_F(KeyboardTest, UsageValues) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::SYNC, Key::CAPS_LOCK));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
// Values for Caps Lock.
// See spec at:
// https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.input/keys.fidl;l=177;drc=e3b39f2b57e720770773b857feca4f770ee0619e
EXPECT_EQ(0x07u, keyboard.LastHIDUsagePage());
EXPECT_EQ(0x39u, keyboard.LastHIDUsageID());
EXPECT_EQ(0x70039u, keyboard.LastHIDUsage());
// Try also an usage that is not on page 7. This one is on page 0x0C.
// See spec at:
// https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.input/keys.fidl;l=339;drc=e3b39f2b57e720770773b857feca4f770ee0619e
// Note that Fuchsia does not define constants for every key you may think of,
// rather only those that we had the need for. However it is not an issue
// to add more keys if needed.
keys.clear();
keys.emplace_back(NewKeyEvent(KeyEventType::SYNC, Key::MEDIA_MUTE));
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(0x0Cu, keyboard.LastHIDUsagePage());
EXPECT_EQ(0xE2u, keyboard.LastHIDUsageID());
EXPECT_EQ(0xC00E2u, keyboard.LastHIDUsage());
// Don't crash when a key with only a meaning comes in.
keys.clear();
keys.emplace_back(NewKeyEventWithMeaning(KeyEventType::SYNC, KeyMeaning::WithCodepoint(32)));
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(0x0u, keyboard.LastHIDUsagePage());
EXPECT_EQ(0x0u, keyboard.LastHIDUsageID());
EXPECT_EQ(0x0u, keyboard.LastHIDUsage());
EXPECT_EQ(0x20u, keyboard.LastCodePoint());
keys.clear();
auto key = NewKeyEventWithMeaning(KeyEventType::SYNC, KeyMeaning::WithCodepoint(65));
key.set_key(Key::A);
keys.emplace_back(std::move(key));
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(0x07u, keyboard.LastHIDUsagePage());
EXPECT_EQ(0x04u, keyboard.LastHIDUsageID());
EXPECT_EQ(0x70004u, keyboard.LastHIDUsage());
EXPECT_EQ(65u, keyboard.LastCodePoint());
}
// This test checks that if a caps lock has been pressed when we didn't have
// focus, the effect of caps lock remains. Only this first test case is
// commented to explain how the test case works.
TEST_F(KeyboardTest, CapsLockSync) {
// Place the key events since the beginning of time into `keys`.
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::SYNC, Key::CAPS_LOCK));
// Replay them on the keyboard.
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
// Verify the state of the keyboard's public API:
// - check that the key sync had no code point (it was a caps lock press).
// - check that the registered usage was that of caps lock.
// - check that the net effect is that the caps lock modifier is locked
// active.
EXPECT_EQ(0u, keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::CAPS_LOCK), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierCapsLock, keyboard.Modifiers());
}
TEST_F(KeyboardTest, CapsLockPress) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::CAPS_LOCK));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(0u, keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::CAPS_LOCK), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierCapsLock, keyboard.Modifiers());
}
TEST_F(KeyboardTest, CapsLockPressRelease) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::CAPS_LOCK));
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::CAPS_LOCK));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(0u, keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::CAPS_LOCK), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierCapsLock, keyboard.Modifiers());
}
TEST_F(KeyboardTest, ShiftA) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::LEFT_SHIFT));
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(static_cast<uint32_t>('A'), keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierLeftShift, keyboard.Modifiers());
}
TEST_F(KeyboardTest, ShiftAWithRelease) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::LEFT_SHIFT));
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::A));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(static_cast<uint32_t>('A'), keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierLeftShift, keyboard.Modifiers());
}
TEST_F(KeyboardTest, ShiftAWithReleaseShift) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::LEFT_SHIFT));
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::LEFT_SHIFT));
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::A));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(static_cast<uint32_t>('a'), keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierNone, keyboard.Modifiers());
}
TEST_F(KeyboardTest, LowcaseA) {
std::vector<KeyEvent> keys;
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::A));
embedder::Keyboard keyboard;
ASSERT_TRUE(ConsumeEvents(&keyboard, keys));
EXPECT_EQ(static_cast<uint32_t>('a'), keyboard.LastCodePoint());
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
EXPECT_EQ(kModifierNone, keyboard.Modifiers());
}
} // namespace
} // namespace embedder_testing