[XCB] Add test fixtures for xcb connection and keyboard setup

Signed-off-by: Chris Burel <burelc@amazon.com>
This commit is contained in:
Chris Burel
2021-10-05 12:12:40 -07:00
parent ba48426750
commit b1b52c201f
6 changed files with 177 additions and 55 deletions
@@ -28,6 +28,10 @@ xcb_generic_event_t* xcb_poll_for_event(xcb_connection_t* c)
{
return MockXcbInterface::Instance()->xcb_poll_for_event(c);
}
xcb_generic_error_t* xcb_request_check(xcb_connection_t* c, xcb_void_cookie_t cookie)
{
return MockXcbInterface::Instance()->xcb_request_check(c, cookie);
}
// ----------------------------------------------------------------------------
// xcb-xkb
@@ -39,6 +43,10 @@ xcb_xkb_use_extension_reply_t* xcb_xkb_use_extension_reply(xcb_connection_t* c,
{
return MockXcbInterface::Instance()->xcb_xkb_use_extension_reply(c, cookie, e);
}
xcb_void_cookie_t xcb_xkb_select_events(xcb_connection_t* c, xcb_xkb_device_spec_t deviceSpec, uint16_t affectWhich, uint16_t clear, uint16_t selectAll, uint16_t affectMap, uint16_t map, const void* details)
{
return MockXcbInterface::Instance()->xcb_xkb_select_events(c, deviceSpec, affectWhich, clear, selectAll, affectMap, map, details);
}
// ----------------------------------------------------------------------------
// xkb-x11
@@ -46,7 +54,7 @@ int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t* connection)
{
return MockXcbInterface::Instance()->xkb_x11_get_core_keyboard_device_id(connection);
}
struct xkb_keymap* xkb_x11_keymap_new_from_device(struct xkb_context* context, xcb_connection_t* connection, int32_t device_id, enum xkb_keymap_compile_flags flags)
xkb_keymap* xkb_x11_keymap_new_from_device(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags)
{
return MockXcbInterface::Instance()->xkb_x11_keymap_new_from_device(context, connection, device_id, flags);
}
@@ -54,28 +62,46 @@ xkb_state* xkb_x11_state_new_from_device(xkb_keymap* keymap, xcb_connection_t* c
{
return MockXcbInterface::Instance()->xkb_x11_state_new_from_device(keymap, connection, device_id);
}
int xkb_x11_setup_xkb_extension(
xcb_connection_t* connection,
uint16_t major_xkb_version,
uint16_t minor_xkb_version,
xkb_x11_setup_xkb_extension_flags flags,
uint16_t* major_xkb_version_out,
uint16_t* minor_xkb_version_out,
uint8_t* base_event_out,
uint8_t* base_error_out)
{
return MockXcbInterface::Instance()->xkb_x11_setup_xkb_extension(
connection, major_xkb_version, minor_xkb_version, flags, major_xkb_version_out, minor_xkb_version_out, base_event_out,
base_error_out);
}
// ----------------------------------------------------------------------------
// xkbcommon
xkb_context* xkb_context_new(enum xkb_context_flags flags)
xkb_context* xkb_context_new(xkb_context_flags flags)
{
return MockXcbInterface::Instance()->xkb_context_new(flags);
}
void xkb_context_unref(xkb_context *context)
void xkb_context_unref(xkb_context* context)
{
return MockXcbInterface::Instance()->xkb_context_unref(context);
}
void xkb_keymap_unref(xkb_keymap *keymap)
void xkb_keymap_unref(xkb_keymap* keymap)
{
return MockXcbInterface::Instance()->xkb_keymap_unref(keymap);
}
void xkb_state_unref(xkb_state *state)
void xkb_state_unref(xkb_state* state)
{
return MockXcbInterface::Instance()->xkb_state_unref(state);
}
xkb_keysym_t xkb_state_key_get_one_sym(xkb_state *state, xkb_keycode_t key)
xkb_keysym_t xkb_state_key_get_one_sym(xkb_state* state, xkb_keycode_t key)
{
return MockXcbInterface::Instance()->xkb_state_key_get_one_sym(state, key);
}
int xkb_state_key_get_utf8(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)
{
return MockXcbInterface::Instance()->xkb_state_key_get_utf8(state, key, buffer, size);
}
}
@@ -17,6 +17,7 @@
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#include "Printers.h"
@@ -35,6 +36,14 @@ struct xkb_keymap
struct xkb_state
{
enum class Modifier
{
None = 0,
Shift = 1,
Ctrl = 1 << 1,
Alt = 1 << 2,
};
Modifier m_modifiers;
};
class MockXcbInterface
@@ -48,7 +57,7 @@ public:
MockXcbInterface(MockXcbInterface&&) = delete;
MockXcbInterface& operator=(const MockXcbInterface&) = delete;
MockXcbInterface& operator=(MockXcbInterface&&) = delete;
~MockXcbInterface()
virtual ~MockXcbInterface()
{
self = nullptr;
}
@@ -59,22 +68,26 @@ public:
MOCK_CONST_METHOD2(xcb_connect, xcb_connection_t*(const char* displayname, int* screenp));
MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c));
MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c));
MOCK_CONST_METHOD2(xcb_request_check, xcb_generic_error_t*(xcb_connection_t* c, xcb_void_cookie_t cookie));
// xcb-xkb
MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor));
MOCK_CONST_METHOD3(xcb_xkb_use_extension_reply, xcb_xkb_use_extension_reply_t*(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e));
MOCK_CONST_METHOD8(xcb_xkb_select_events, xcb_void_cookie_t(xcb_connection_t* c, xcb_xkb_device_spec_t deviceSpec, uint16_t affectWhich, uint16_t clear, uint16_t selectAll, uint16_t affectMap, uint16_t map, const void* details));
// xkb-x11
MOCK_CONST_METHOD1(xkb_x11_get_core_keyboard_device_id, int32_t(xcb_connection_t* connection));
MOCK_CONST_METHOD4(xkb_x11_keymap_new_from_device, xkb_keymap*(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags));
MOCK_CONST_METHOD3(xkb_x11_state_new_from_device, xkb_state*(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id));
MOCK_CONST_METHOD8(xkb_x11_setup_xkb_extension, int(xcb_connection_t* connection, uint16_t major_xkb_version, uint16_t minor_xkb_version, xkb_x11_setup_xkb_extension_flags flags, uint16_t* major_xkb_version_out, uint16_t* minor_xkb_version_out, uint8_t* base_event_out, uint8_t* base_error_out));
// xkbcommon
MOCK_CONST_METHOD1(xkb_context_new, xkb_context*(xkb_context_flags flags));
MOCK_CONST_METHOD1(xkb_context_unref, void(xkb_context* context));
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_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));
private:
static inline MockXcbInterface* self = nullptr;
@@ -0,0 +1,25 @@
/*
* 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
*
*/
#include "XcbBaseTestFixture.h"
namespace AzFramework
{
void XcbBaseTestFixture::SetUp()
{
using testing::Return;
using testing::_;
testing::Test::SetUp();
EXPECT_CALL(m_interface, xcb_connect(_, _))
.WillOnce(Return(&m_connection));
EXPECT_CALL(m_interface, xcb_disconnect(&m_connection))
.Times(1);
}
} // namespace AzFramework
@@ -0,0 +1,29 @@
/*
* 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 <gtest/gtest.h>
#include <xcb/xcb.h>
#include "MockXcbInterface.h"
namespace AzFramework
{
// Sets up mock behavior for the xcb library, providing an xcb_connection_t that is returned from a call to xcb_connect
class XcbBaseTestFixture
: public testing::Test
{
public:
void SetUp() override;
protected:
testing::NiceMock<MockXcbInterface> m_interface;
xcb_connection_t m_connection{};
};
} // namespace AzFramework
@@ -6,6 +6,7 @@
*
*/
#include <gmock/gmock-actions.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
@@ -15,6 +16,7 @@
#include <AzFramework/XcbInputDeviceKeyboard.h>
#include "MockXcbInterface.h"
#include "Actions.h"
#include "XcbBaseTestFixture.h"
template<typename T>
xcb_generic_event_t MakeEvent(T event)
@@ -24,26 +26,74 @@ xcb_generic_event_t MakeEvent(T event)
namespace AzFramework
{
TEST(XcbInputDeviceKeyboard, InputChannelsUpdateStateFromXcbEvents)
// Sets up default behavior for mock keyboard responses to xcb methods
class XcbInputDeviceKeyboardTests
: public XcbBaseTestFixture
{
using testing::Return;
void SetUp() override
{
using testing::Return;
using testing::SetArgPointee;
using testing::_;
XcbBaseTestFixture::SetUp();
EXPECT_CALL(m_interface, xkb_context_new(XKB_CONTEXT_NO_FLAGS))
.WillOnce(Return(&m_xkbContext));
EXPECT_CALL(m_interface, xkb_context_unref(&m_xkbContext))
.Times(1);
EXPECT_CALL(m_interface, xkb_x11_keymap_new_from_device(&m_xkbContext, &m_connection, s_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS))
.WillOnce(Return(&m_xkbKeymap));
EXPECT_CALL(m_interface, xkb_keymap_unref(&m_xkbKeymap))
.Times(1);
EXPECT_CALL(m_interface, xkb_x11_state_new_from_device(&m_xkbKeymap, &m_connection, s_coreDeviceId))
.WillOnce(Return(&m_xkbState));
EXPECT_CALL(m_interface, xkb_state_unref(&m_xkbState))
.Times(1);
ON_CALL(m_interface, xkb_x11_setup_xkb_extension(&m_connection, 1, 0, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, _, _, _, _))
.WillByDefault(DoAll(
SetArgPointee<6>(s_xkbEventCode), // Set the "base_event_out" argument to the xkbEventCode, the value to identify XKB events
Return(1)
));
constexpr unsigned int xcbXkbSelectEventsSequence = 1;
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))))
.WillByDefault(Return(nullptr)); // indicates success
ON_CALL(m_interface, xkb_x11_get_core_keyboard_device_id(&m_connection))
.WillByDefault(Return(s_coreDeviceId));
ON_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey))
.WillByDefault(Return(XKB_KEY_a))
;
}
protected:
xkb_context m_xkbContext{};
xkb_keymap m_xkbKeymap{};
xkb_state m_xkbState{};
static constexpr int32_t s_coreDeviceId{1};
static constexpr uint8_t s_xkbEventCode{85};
static constexpr xcb_keycode_t s_keycodeForAKey{38};
};
TEST_F(XcbInputDeviceKeyboardTests, InputChannelsUpdateStateFromXcbEvents)
{
using testing::DoAll;
using testing::Eq;
using testing::Return;
using testing::SetArgPointee;
using testing::_;
MockXcbInterface interface;
xcb_connection_t connection{};
xkb_context xkbContext{};
xkb_keymap xkbKeymap{};
xkb_state xkbState{};
const int32_t coreDeviceId{1};
constexpr xcb_keycode_t keycodeForAKey = 38;
const AZStd::array events
{
MakeEvent(xcb_key_press_event_t{
/*.response_type = */ XCB_KEY_PRESS,
/*.detail = */ keycodeForAKey,
/*.detail = */ s_keycodeForAKey,
/*.sequence = */ 0,
/*.time = */ 0,
/*.root = */ 0,
@@ -59,7 +109,7 @@ namespace AzFramework
}),
MakeEvent(xcb_key_release_event_t{
/*.response_type = */ XCB_KEY_RELEASE,
/*.detail = */ keycodeForAKey,
/*.detail = */ s_keycodeForAKey,
/*.sequence = */ 0,
/*.time = */ 0,
/*.root = */ 0,
@@ -75,51 +125,28 @@ namespace AzFramework
}),
};
EXPECT_CALL(interface, xcb_connect(_, _))
.WillOnce(Return(&connection));
EXPECT_CALL(interface, xcb_disconnect(&connection))
.Times(1);
EXPECT_CALL(interface, xkb_context_new(XKB_CONTEXT_NO_FLAGS))
.WillOnce(Return(&xkbContext));
EXPECT_CALL(interface, xkb_context_unref(&xkbContext))
.Times(1);
EXPECT_CALL(interface, xkb_x11_keymap_new_from_device(&xkbContext, &connection, coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS))
.WillOnce(Return(&xkbKeymap));
EXPECT_CALL(interface, xkb_keymap_unref(&xkbKeymap))
.Times(1);
EXPECT_CALL(interface, xkb_x11_state_new_from_device(&xkbKeymap, &connection, coreDeviceId))
.WillOnce(Return(&xkbState));
EXPECT_CALL(interface, xkb_state_unref(&xkbState))
.Times(1);
EXPECT_CALL(interface, xcb_xkb_use_extension(&connection, 1, 0));
EXPECT_CALL(interface, xcb_xkb_use_extension_reply(&connection, _, _))
.WillOnce(ReturnMalloc<xcb_xkb_use_extension_reply_t>(
/* .response_type =*/static_cast<uint8_t>(XCB_XKB_USE_EXTENSION),
/* .supported =*/ static_cast<uint8_t>(1))
);
EXPECT_CALL(interface, xkb_x11_get_core_keyboard_device_id(&connection))
.WillRepeatedly(Return(coreDeviceId));
// 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(interface, xcb_poll_for_event(&connection))
EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection))
.WillOnce(ReturnMalloc<xcb_generic_event_t>(events[0]))
.WillOnce(Return(nullptr))
.WillOnce(ReturnMalloc<xcb_generic_event_t>(events[1]))
.WillOnce(Return(nullptr))
;
EXPECT_CALL(interface, xkb_state_key_get_one_sym(&xkbState, keycodeForAKey))
.WillOnce(Return(XKB_KEY_a))
.WillOnce(Return(XKB_KEY_a))
;
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({}, {});
@@ -13,5 +13,7 @@ set(FILES
MockXcbInterface.h
Printers.cpp
Printers.h
XcbBaseTestFixture.cpp
XcbBaseTestFixture.h
XcbInputDeviceKeyboardTests.cpp
)