From 3e0535e2110538fdf2376b1aae3edd19551daf37 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 5 Oct 2021 15:38:24 -0700 Subject: [PATCH] [XCB] Add tests to cover text events from key presses Signed-off-by: Chris Burel --- .../Tests/Platform/Common/Xcb/Matchers.h | 17 ++ .../Platform/Common/Xcb/MockXcbInterface.cpp | 12 + .../Platform/Common/Xcb/MockXcbInterface.h | 12 +- .../Xcb/XcbInputDeviceKeyboardTests.cpp | 237 +++++++++++++++++- .../Xcb/azframework_xcb_tests_files.cmake | 1 + 5 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h new file mode 100644 index 0000000000..77acb1c3e4 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Matchers.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +inline testing::PolymorphicMatcher> StrEq(const AZStd::string& str) +{ + return ::testing::MakePolymorphicMatcher(testing::internal::StrEqualityMatcher(str, true, true)); +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp index 4797f1133d..b15809a4c6 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -103,5 +103,17 @@ int xkb_state_key_get_utf8(xkb_state* state, xkb_keycode_t key, char* buffer, si { return MockXcbInterface::Instance()->xkb_state_key_get_utf8(state, key, buffer, size); } +xkb_state_component xkb_state_update_mask( + xkb_state* state, + xkb_mod_mask_t depressed_mods, + xkb_mod_mask_t latched_mods, + xkb_mod_mask_t locked_mods, + xkb_layout_index_t depressed_layout, + xkb_layout_index_t latched_layout, + xkb_layout_index_t locked_layout) +{ + return MockXcbInterface::Instance()->xkb_state_update_mask( + state, depressed_mods, latched_mods, locked_mods, depressed_layout, latched_layout, locked_layout); +} } diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h index 4adb73caeb..b57751344e 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -36,14 +36,7 @@ struct xkb_keymap struct xkb_state { - enum class Modifier - { - None = 0, - Shift = 1, - Ctrl = 1 << 1, - Alt = 1 << 2, - }; - Modifier m_modifiers; + xkb_mod_mask_t m_modifiers{}; }; class MockXcbInterface @@ -87,7 +80,8 @@ public: MOCK_CONST_METHOD1(xkb_keymap_unref, void(xkb_keymap* keymap)); MOCK_CONST_METHOD1(xkb_state_unref, void(xkb_state* state)); MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state* state, xkb_keycode_t key)); - MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(struct xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)); + MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)); + MOCK_CONST_METHOD7(xkb_state_update_mask, xkb_state_component(xkb_state* state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout)); private: static inline MockXcbInterface* self = nullptr; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp index 9eb93fb4ae..c01cf1e7cd 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -14,7 +14,9 @@ #include #include +#include #include "MockXcbInterface.h" +#include "Matchers.h" #include "Actions.h" #include "XcbBaseTestFixture.h" @@ -58,7 +60,7 @@ namespace AzFramework Return(1) )); - constexpr unsigned int xcbXkbSelectEventsSequence = 1; + constexpr unsigned int xcbXkbSelectEventsSequence = 342; ON_CALL(m_interface, xcb_xkb_select_events(&m_connection, _, _, _, _, _, _, _)) .WillByDefault(Return(xcb_void_cookie_t{/*.sequence = */ xcbXkbSelectEventsSequence})); ON_CALL(m_interface, xcb_request_check(&m_connection, testing::Field(&xcb_void_cookie_t::sequence, testing::Eq(xcbXkbSelectEventsSequence)))) @@ -70,15 +72,64 @@ namespace AzFramework ON_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey)) .WillByDefault(Return(XKB_KEY_a)) ; + ON_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForShiftLKey)) + .WillByDefault(Return(XKB_KEY_Shift_L)) + ; + + ON_CALL(m_interface, xkb_state_update_mask(&m_xkbState, _, _, _, _, _, _)) + .WillByDefault(testing::Invoke(this, &XcbInputDeviceKeyboardTests::UpdateStateMask)); + + ON_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForAKey, nullptr, 0)) + .WillByDefault(Return(1)); + ON_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithoutShift, s_keycodeForAKey, _, 2)) + .WillByDefault(DoAll( + SetArgPointee<2>('a'), + Return(1) + )); + ON_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithShift, s_keycodeForAKey, _, 2)) + .WillByDefault(DoAll( + SetArgPointee<2>('A'), + Return(1) + )); + } + + private: + xkb_state_component UpdateStateMask( + xkb_state* state, + xkb_mod_mask_t depressed_mods, + xkb_mod_mask_t latched_mods, + xkb_mod_mask_t locked_mods, + xkb_layout_index_t depressed_layout, + xkb_layout_index_t latched_layout, + xkb_layout_index_t locked_layout) + { + state->m_modifiers = depressed_mods | locked_mods; + return {}; } protected: xkb_context m_xkbContext{}; xkb_keymap m_xkbKeymap{}; xkb_state m_xkbState{}; + const testing::Matcher m_matchesStateWithoutShift = testing::AllOf(&m_xkbState, testing::Field(&xkb_state::m_modifiers, 0)); + const testing::Matcher m_matchesStateWithShift = testing::AllOf(&m_xkbState, testing::Field(&xkb_state::m_modifiers, XCB_MOD_MASK_SHIFT)); + static constexpr int32_t s_coreDeviceId{1}; static constexpr uint8_t s_xkbEventCode{85}; + static constexpr xcb_keycode_t s_keycodeForAKey{38}; + static constexpr xcb_keycode_t s_keycodeForShiftLKey{50}; + }; + + class InputTextNotificationListener + : public InputTextNotificationBus::Handler + { + public: + InputTextNotificationListener() + { + BusConnect(); + } + MOCK_METHOD2(OnInputTextEvent, void(const AZStd::string& /*textUTF8*/, bool& /*o_hasBeenConsumed*/)); }; TEST_F(XcbInputDeviceKeyboardTests, InputChannelsUpdateStateFromXcbEvents) @@ -140,14 +191,6 @@ namespace AzFramework EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey)) .Times(2); - EXPECT_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForAKey, nullptr, 0)) - .WillRepeatedly(Return(1)); - EXPECT_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForAKey, _, 2)) - .WillRepeatedly(DoAll( - SetArgPointee<2>('a'), - Return(1) - )); - Application application; application.Start({}, {}); @@ -169,4 +212,180 @@ namespace AzFramework application.Stop(); } + + TEST_F(XcbInputDeviceKeyboardTests, TextEnteredFromXcbKeyPressEvents) + { + using testing::DoAll; + using testing::Eq; + using testing::Return; + using testing::SetArgPointee; + using testing::_; + + // press a + // release a + // press shift + // press a + // release a + // release shift + const AZStd::array events + { + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_xkb_state_notify_event_t{ + /*.response_type = */ s_xkbEventCode, + /*.xkbType = */ XCB_XKB_STATE_NOTIFY, + /*.sequence = */ 0, + /*.time = */ 0, + /*.deviceID = */ s_coreDeviceId, + /*.mods = */ XCB_MOD_MASK_SHIFT, + /*.baseMods = */ XCB_MOD_MASK_SHIFT, + /*.latchedMods = */ 0, + /*.lockedMods = */ 0, + /*.group = */ 0, + /*.baseGroup = */ 0, + /*.latchedGroup = */ 0, + /*.lockedGroup = */ 0, + /*.compatState = */ XCB_MOD_MASK_SHIFT, + /*.grabMods = */ XCB_MOD_MASK_SHIFT, + /*.compatGrabMods = */ XCB_MOD_MASK_SHIFT, + /*.lookupMods = */ XCB_MOD_MASK_SHIFT, + /*.compatLoockupMods = */ XCB_MOD_MASK_SHIFT, + /*.ptrBtnState = */ 0, + /*.changed = */ 0, + /*.keycode = */ s_keycodeForShiftLKey, + /*.eventType = */ XCB_KEY_PRESS, + /*.requestMajor = */ 0, + /*.requestMinor = */ 0, + }), + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ s_keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_xkb_state_notify_event_t{ + /*.response_type = */ s_xkbEventCode, + /*.xkbType = */ XCB_XKB_STATE_NOTIFY, + /*.sequence = */ 0, + /*.time = */ 0, + /*.deviceID = */ s_coreDeviceId, + /*.mods = */ 0, + /*.baseMods = */ 0, + /*.latchedMods = */ 0, + /*.lockedMods = */ 0, + /*.group = */ 0, + /*.baseGroup = */ 0, + /*.latchedGroup = */ 0, + /*.lockedGroup = */ 0, + /*.compatState = */ 0, + /*.grabMods = */ 0, + /*.compatGrabMods = */ 0, + /*.lookupMods = */ 0, + /*.compatLoockupMods = */ 0, + /*.ptrBtnState = */ 0, + /*.changed = */ 0, + /*.keycode = */ s_keycodeForShiftLKey, + /*.eventType = */ XCB_KEY_RELEASE, + /*.requestMajor = */ 0, + /*.requestMinor = */ 0, + }), + }; + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // event pointers are freed by the calling code, so we malloc new copies + // here + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) + .WillOnce(ReturnMalloc(events[0])) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[1])) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[2])) + .WillOnce(ReturnMalloc(events[3])) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[4])) + .WillOnce(ReturnMalloc(events[5])) + .WillRepeatedly(Return(nullptr)) + ; + + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(&m_xkbState, s_keycodeForAKey, nullptr, 0)) + .Times(2); + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithoutShift, s_keycodeForAKey, _, 2)) + .Times(1); + EXPECT_CALL(m_interface, xkb_state_key_get_utf8(m_matchesStateWithShift, s_keycodeForAKey, _, 2)) + .Times(1); + + InputTextNotificationListener textListener; + EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1); + EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1); + + Application application; + application.Start({}, {}); + + for (int i = 0; i < 4; ++i) + { + application.PumpSystemEventLoopUntilEmpty(); + application.TickSystem(); + application.Tick(); + } + + application.Stop(); + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake index 0ba1ece9c7..762d5d74fe 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -9,6 +9,7 @@ set(FILES Actions.h Main.cpp + Matchers.h MockXcbInterface.cpp MockXcbInterface.h Printers.cpp