| // Copyright 2017 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. |
| |
| #include <fbl/vector.h> |
| #include <hid/hid.h> |
| #include <hid/usages.h> |
| |
| #include "garnet/lib/machina/hid_event_source.h" |
| #include "gtest/gtest.h" |
| |
| // Yanked from system/ulib/hid/hid.c |
| #define KEYSET(bitmap, n) (bitmap[(n) >> 5] |= (1 << ((n)&31))) |
| #define KEYCLR(bitmap, n) (bitmap[(n) >> 5] &= ~(1 << ((n)&31))) |
| |
| namespace machina { |
| namespace { |
| |
| static constexpr size_t kInputQueueSize = 8; |
| |
| static constexpr hid_keys_t kAllKeysUp = {}; |
| |
| // Utility class for performing verifications on the state of an |
| // InputDispatcher. |
| class InputDispatcherVerifier { |
| public: |
| InputDispatcherVerifier(InputDispatcher* dispatcher) { Reset(dispatcher); } |
| |
| void Reset(InputDispatcher* dispatcher) { |
| queued_events_.reset(); |
| while (dispatcher->Keyboard()->size() > 0) { |
| InputEvent event = dispatcher->Keyboard()->Wait(); |
| queued_events_.push_back(event); |
| } |
| } |
| |
| size_t events() { return queued_events_.size(); } |
| |
| // Check for an evdev event between |min| and |max| inclusive in the |
| // output stream. |
| bool HasKeyEvent(size_t min, size_t max, uint32_t hid_usage, KeyState state) { |
| for (size_t i = min; i < max + 1 && i < queued_events_.size(); ++i) { |
| auto event = queued_events_[i]; |
| if (event.type == InputEventType::KEYBOARD && |
| event.key.hid_usage == hid_usage && event.key.state == state) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool HasKeyPress(size_t min, size_t max, uint32_t usage) { |
| return HasKeyEvent(min, max, usage, KeyState::PRESSED); |
| } |
| |
| bool HasKeyRelease(size_t min, size_t max, uint32_t usage) { |
| return HasKeyEvent(min, max, usage, KeyState::RELEASED); |
| } |
| |
| bool HasBarrier(size_t i) { |
| auto event = queued_events_[i]; |
| return event.type == InputEventType::BARRIER; |
| } |
| |
| private: |
| fbl::Vector<InputEvent> queued_events_; |
| }; |
| |
| TEST(HidEventSourceTest, HandleKeyPress) { |
| InputDispatcher dispatcher(kInputQueueSize); |
| HidInputDevice hid_device(&dispatcher); |
| |
| // Set 'A' as pressed. |
| hid_keys_t keys = {}; |
| KEYSET(keys.keymask, HID_USAGE_KEY_A); |
| |
| ASSERT_EQ(hid_device.HandleHidKeys(keys), ZX_OK); |
| |
| InputDispatcherVerifier verifier(&dispatcher); |
| ASSERT_EQ(verifier.events(), 2u); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 0, HID_USAGE_KEY_A)); |
| EXPECT_TRUE(verifier.HasBarrier(1)); |
| } |
| |
| TEST(HidEventSourceTest, HandleMultipleKeyPress) { |
| InputDispatcher dispatcher(kInputQueueSize); |
| HidInputDevice hid_device(&dispatcher); |
| |
| // Set 'ABCD' as pressed. |
| hid_keys_t keys = {}; |
| KEYSET(keys.keymask, HID_USAGE_KEY_A); |
| KEYSET(keys.keymask, HID_USAGE_KEY_B); |
| KEYSET(keys.keymask, HID_USAGE_KEY_C); |
| KEYSET(keys.keymask, HID_USAGE_KEY_D); |
| |
| ASSERT_EQ(hid_device.HandleHidKeys(keys), ZX_OK); |
| |
| InputDispatcherVerifier verifier(&dispatcher); |
| ASSERT_EQ(verifier.events(), 5u); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 3, HID_USAGE_KEY_A)); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 3, HID_USAGE_KEY_B)); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 3, HID_USAGE_KEY_C)); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 3, HID_USAGE_KEY_D)); |
| EXPECT_TRUE(verifier.HasBarrier(4)); |
| } |
| |
| TEST(HidEventSourceTest, HandleKeyRelease) { |
| InputDispatcher dispatcher(kInputQueueSize); |
| HidInputDevice hid_device(&dispatcher); |
| |
| // Initialize with 'A' key pressed. |
| hid_keys_t key_pressed_keys = {}; |
| KEYSET(key_pressed_keys.keymask, HID_USAGE_KEY_A); |
| ASSERT_EQ(hid_device.HandleHidKeys(key_pressed_keys), ZX_OK); |
| |
| // Release all keys. |
| InputDispatcherVerifier verifier(&dispatcher); |
| ASSERT_EQ(hid_device.HandleHidKeys(kAllKeysUp), ZX_OK); |
| verifier.Reset(&dispatcher); |
| |
| ASSERT_EQ(verifier.events(), 2u); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 0, HID_USAGE_KEY_A)); |
| EXPECT_TRUE(verifier.HasBarrier(1)); |
| } |
| |
| TEST(HidEventSourceTest, HandleMultipleKeyRelease) { |
| InputDispatcher dispatcher(kInputQueueSize); |
| HidInputDevice hid_device(&dispatcher); |
| |
| // Set 'ABCD' as pressed. |
| hid_keys_t keys = {}; |
| KEYSET(keys.keymask, HID_USAGE_KEY_A); |
| KEYSET(keys.keymask, HID_USAGE_KEY_B); |
| KEYSET(keys.keymask, HID_USAGE_KEY_C); |
| KEYSET(keys.keymask, HID_USAGE_KEY_D); |
| ASSERT_EQ(hid_device.HandleHidKeys(keys), ZX_OK); |
| |
| // Release all keys. |
| InputDispatcherVerifier verifier(&dispatcher); |
| ASSERT_EQ(hid_device.HandleHidKeys(kAllKeysUp), ZX_OK); |
| verifier.Reset(&dispatcher); |
| |
| ASSERT_EQ(verifier.events(), 5u); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 3, HID_USAGE_KEY_A)); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 3, HID_USAGE_KEY_B)); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 3, HID_USAGE_KEY_C)); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 3, HID_USAGE_KEY_D)); |
| EXPECT_TRUE(verifier.HasBarrier(4)); |
| } |
| |
| // Test keys both being pressed and released in a single HID report. |
| TEST(HidEventSourceTest, HandleKeyPressAndRelease) { |
| InputDispatcher dispatcher(kInputQueueSize); |
| HidInputDevice hid_device(&dispatcher); |
| |
| // Set 'AB' as pressed. |
| hid_keys_t keys_ab = {}; |
| KEYSET(keys_ab.keymask, HID_USAGE_KEY_A); |
| KEYSET(keys_ab.keymask, HID_USAGE_KEY_B); |
| ASSERT_EQ(hid_device.HandleHidKeys(keys_ab), ZX_OK); |
| |
| // Release 'AB' and press 'CD'. |
| hid_keys_t keys_cd = {}; |
| KEYSET(keys_cd.keymask, HID_USAGE_KEY_C); |
| KEYSET(keys_cd.keymask, HID_USAGE_KEY_D); |
| InputDispatcherVerifier verifier(&dispatcher); |
| ASSERT_EQ(hid_device.HandleHidKeys(keys_cd), ZX_OK); |
| verifier.Reset(&dispatcher); |
| |
| ASSERT_EQ(verifier.events(), 5u); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 3, HID_USAGE_KEY_C)); |
| EXPECT_TRUE(verifier.HasKeyPress(0, 3, HID_USAGE_KEY_D)); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 3, HID_USAGE_KEY_A)); |
| EXPECT_TRUE(verifier.HasKeyRelease(0, 3, HID_USAGE_KEY_B)); |
| EXPECT_TRUE(verifier.HasBarrier(4)); |
| } |
| |
| } // namespace |
| } // namespace machina |