diff options
| author | 2023-05-26 16:32:06 +0000 | |
|---|---|---|
| committer | 2023-05-26 16:32:06 +0000 | |
| commit | 2feca2d9c81c5c71dccca59cab04864d5dbcf101 (patch) | |
| tree | db24538c6d629a1208bf53b22fac528474200b18 | |
| parent | 9afb1bcafb311c9381e509b7de958386478a83ab (diff) | |
| parent | 1930f60ddfe3f2e3e7b5e7137772e1be84cc78e2 (diff) | |
Merge "Add a unit test for TouchpadInputMapper" into udc-dev am: 1930f60ddf
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/23267663
Change-Id: I3c3d4b54c17f0c82fbdfe6e6ea0000cba8b684c5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
| -rw-r--r-- | include/input/Input.h | 13 | ||||
| -rw-r--r-- | services/inputflinger/tests/Android.bp | 2 | ||||
| -rw-r--r-- | services/inputflinger/tests/CursorInputMapper_test.cpp | 105 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputMapperTest.cpp | 68 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputMapperTest.h | 32 | ||||
| -rw-r--r-- | services/inputflinger/tests/InterfaceMocks.h | 146 | ||||
| -rw-r--r-- | services/inputflinger/tests/TouchpadInputMapper_test.cpp | 155 |
7 files changed, 521 insertions, 0 deletions
diff --git a/include/input/Input.h b/include/input/Input.h index fe0c775fd3..527a47741c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -242,6 +242,19 @@ enum class ToolType { ftl_last = PALM, }; +/** + * The state of the key. This should have 1:1 correspondence with the values of anonymous enum + * defined in input.h + */ +enum class KeyState { + UNKNOWN = AKEY_STATE_UNKNOWN, + UP = AKEY_STATE_UP, + DOWN = AKEY_STATE_DOWN, + VIRTUAL = AKEY_STATE_VIRTUAL, + ftl_first = UNKNOWN, + ftl_last = VIRTUAL, +}; + bool isStylusToolType(ToolType toolType); /* diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 52277ff078..569690ab78 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -40,6 +40,7 @@ cc_test { "AnrTracker_test.cpp", "BlockingQueue_test.cpp", "CapturedTouchpadEventConverter_test.cpp", + "CursorInputMapper_test.cpp", "EventHub_test.cpp", "FakeEventHub.cpp", "FakeInputReaderPolicy.cpp", @@ -58,6 +59,7 @@ cc_test { "PreferStylusOverTouch_test.cpp", "PropertyProvider_test.cpp", "TestInputListener.cpp", + "TouchpadInputMapper_test.cpp", "UinputDevice.cpp", "UnwantedInteractionBlocker_test.cpp", ], diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp new file mode 100644 index 0000000000..6774b1793f --- /dev/null +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorInputMapper.h" + +#include <android-base/logging.h> +#include <gtest/gtest.h> + +#include "FakePointerController.h" +#include "InputMapperTest.h" +#include "InterfaceMocks.h" +#include "TestInputListenerMatchers.h" + +#define TAG "CursorInputMapper_test" + +namespace android { + +using testing::Return; +using testing::VariantWith; +constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr auto ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE; +constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP; +constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; +constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; +constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; + +/** + * Unit tests for CursorInputMapper. + * This class is named 'CursorInputMapperUnitTest' to avoid name collision with the existing + * 'CursorInputMapperTest'. If all of the CursorInputMapper tests are migrated here, the name + * can be simplified to 'CursorInputMapperTest'. + * TODO(b/283812079): move CursorInputMapper tests here. + */ +class CursorInputMapperUnitTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + + // Current scan code state - all keys are UP by default + setScanCodeState(KeyState::UP, + {BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, BTN_BACK, BTN_SIDE, BTN_FORWARD, + BTN_EXTRA, BTN_TASK}); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL)) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(mMockInputReaderContext, bumpGeneration()).WillRepeatedly(Return(1)); + + mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); + } +}; + +/** + * Move the mouse and then click the button. Check whether HOVER_EXIT is generated when hovering + * ends. Currently, it is not. + */ +TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) { + std::list<NotifyArgs> args; + + // Move the cursor a little + args += process(EV_REL, REL_X, 10); + args += process(EV_REL, REL_Y, 20); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + + // Now click the mouse button + args.clear(); + args += process(EV_KEY, BTN_LEFT, 1); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)))); + + // Move some more. + args.clear(); + args += process(EV_REL, REL_X, 10); + args += process(EV_REL, REL_Y, 20); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_MOVE)))); + + // Release the button + args.clear(); + args += process(EV_KEY, BTN_LEFT, 0); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); +} + +} // namespace android diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index ad48a79731..0eee2b9be4 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -22,6 +22,74 @@ namespace android { +using testing::Return; + +void InputMapperUnitTest::SetUp() { + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(400, 240); + + EXPECT_CALL(mMockInputReaderContext, getPointerController(DEVICE_ID)) + .WillRepeatedly(Return(mFakePointerController)); + + EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub)); + InputDeviceIdentifier identifier; + identifier.name = "device"; + identifier.location = "USB1"; + identifier.bus = 0; + + EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)).WillRepeatedly(Return(identifier)); + mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID, + /*generation=*/2, identifier); + mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID); +} + +void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, + int32_t resolution) { + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) + .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { + outAxisInfo->valid = valid; + outAxisInfo->minValue = min; + outAxisInfo->maxValue = max; + outAxisInfo->flat = 0; + outAxisInfo->fuzz = 0; + outAxisInfo->resolution = resolution; + return valid ? OK : -1; + }); +} + +void InputMapperUnitTest::expectScanCodes(bool present, std::set<int> scanCodes) { + for (const auto& scanCode : scanCodes) { + EXPECT_CALL(mMockEventHub, hasScanCode(EVENTHUB_ID, scanCode)) + .WillRepeatedly(testing::Return(present)); + } +} + +void InputMapperUnitTest::setScanCodeState(KeyState state, std::set<int> scanCodes) { + for (const auto& scanCode : scanCodes) { + EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, scanCode)) + .WillRepeatedly(testing::Return(static_cast<int>(state))); + } +} + +void InputMapperUnitTest::setKeyCodeState(KeyState state, std::set<int> keyCodes) { + for (const auto& keyCode : keyCodes) { + EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, keyCode)) + .WillRepeatedly(testing::Return(static_cast<int>(state))); + } +} + +std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) { + RawEvent event; + event.when = systemTime(SYSTEM_TIME_MONOTONIC); + event.readTime = event.when; + event.deviceId = mMapper->getDeviceContext().getEventHubId(); + event.type = type; + event.code = code; + event.value = value; + return mMapper->process(&event); +} + const char* InputMapperTest::DEVICE_NAME = "device"; const char* InputMapperTest::DEVICE_LOCATION = "USB1"; const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES = diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index 2b6655c45e..909bd9c056 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -23,16 +23,48 @@ #include <InputMapper.h> #include <NotifyArgs.h> #include <ftl/flags.h> +#include <gmock/gmock.h> #include <utils/StrongPointer.h> #include "FakeEventHub.h" #include "FakeInputReaderPolicy.h" #include "InstrumentedInputReader.h" +#include "InterfaceMocks.h" #include "TestConstants.h" #include "TestInputListener.h" namespace android { +class InputMapperUnitTest : public testing::Test { +protected: + static constexpr int32_t EVENTHUB_ID = 1; + static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; + virtual void SetUp() override; + + void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution); + + void expectScanCodes(bool present, std::set<int> scanCodes); + + void setScanCodeState(KeyState state, std::set<int> scanCodes); + + void setKeyCodeState(KeyState state, std::set<int> keyCodes); + + std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); + + MockEventHubInterface mMockEventHub; + std::shared_ptr<FakePointerController> mFakePointerController; + MockInputReaderContext mMockInputReaderContext; + std::unique_ptr<InputDevice> mDevice; + + std::unique_ptr<InputDeviceContext> mDeviceContext; + InputReaderConfiguration mReaderConfiguration; + // The mapper should be created by the subclasses. + std::unique_ptr<InputMapper> mMapper; +}; + +/** + * Deprecated - use InputMapperUnitTest instead. + */ class InputMapperTest : public testing::Test { protected: static const char* DEVICE_NAME; diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h new file mode 100644 index 0000000000..d720a902dc --- /dev/null +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -0,0 +1,146 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/logging.h> +#include <gmock/gmock.h> + +namespace android { + +class MockInputReaderContext : public InputReaderContext { +public: + MOCK_METHOD(void, updateGlobalMetaState, (), (override)); + int32_t getGlobalMetaState() override { return 0; }; + + MOCK_METHOD(void, disableVirtualKeysUntil, (nsecs_t time), (override)); + MOCK_METHOD(bool, shouldDropVirtualKey, (nsecs_t now, int32_t keyCode, int32_t scanCode), + (override)); + + MOCK_METHOD(void, fadePointer, (), (override)); + MOCK_METHOD(std::shared_ptr<PointerControllerInterface>, getPointerController, + (int32_t deviceId), (override)); + + MOCK_METHOD(void, requestTimeoutAtTime, (nsecs_t when), (override)); + MOCK_METHOD(int32_t, bumpGeneration, (), (override)); + + MOCK_METHOD(void, getExternalStylusDevices, (std::vector<InputDeviceInfo> & outDevices), + (override)); + MOCK_METHOD(std::list<NotifyArgs>, dispatchExternalStylusState, (const StylusState& outState), + (override)); + + MOCK_METHOD(InputReaderPolicyInterface*, getPolicy, (), (override)); + MOCK_METHOD(EventHubInterface*, getEventHub, (), (override)); + + int32_t getNextId() override { return 1; }; + + MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override)); + MOCK_METHOD(int32_t, getLedMetaState, (), (override)); +}; + +class MockEventHubInterface : public EventHubInterface { +public: + MOCK_METHOD(ftl::Flags<InputDeviceClass>, getDeviceClasses, (int32_t deviceId), (const)); + MOCK_METHOD(InputDeviceIdentifier, getDeviceIdentifier, (int32_t deviceId), (const)); + MOCK_METHOD(int32_t, getDeviceControllerNumber, (int32_t deviceId), (const)); + MOCK_METHOD(std::optional<PropertyMap>, getConfiguration, (int32_t deviceId), (const)); + MOCK_METHOD(status_t, getAbsoluteAxisInfo, + (int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo), (const)); + MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const)); + MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const)); + MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const)); + MOCK_METHOD(void, addKeyRemapping, (int32_t deviceId, int fromKeyCode, int toKeyCode), (const)); + MOCK_METHOD(status_t, mapKey, + (int32_t deviceId, int scanCode, int usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags), + (const)); + MOCK_METHOD(status_t, mapAxis, (int32_t deviceId, int scanCode, AxisInfo* outAxisInfo), + (const)); + MOCK_METHOD(void, setExcludedDevices, (const std::vector<std::string>& devices)); + MOCK_METHOD(std::vector<RawEvent>, getEvents, (int timeoutMillis)); + MOCK_METHOD(std::vector<TouchVideoFrame>, getVideoFrames, (int32_t deviceId)); + MOCK_METHOD((base::Result<std::pair<InputDeviceSensorType, int32_t>>), mapSensor, + (int32_t deviceId, int32_t absCode), (const, override)); + MOCK_METHOD(std::vector<int32_t>, getRawBatteryIds, (int32_t deviceId), (const, override)); + MOCK_METHOD(std::optional<RawBatteryInfo>, getRawBatteryInfo, + (int32_t deviceId, int32_t BatteryId), (const, override)); + MOCK_METHOD(std::vector<int32_t>, getRawLightIds, (int32_t deviceId), (const, override)); + MOCK_METHOD(std::optional<RawLightInfo>, getRawLightInfo, (int32_t deviceId, int32_t lightId), + (const, override)); + MOCK_METHOD(std::optional<int32_t>, getLightBrightness, (int32_t deviceId, int32_t lightId), + (const, override)); + MOCK_METHOD(void, setLightBrightness, (int32_t deviceId, int32_t lightId, int32_t brightness), + (override)); + MOCK_METHOD((std::optional<std::unordered_map<LightColor, int32_t>>), getLightIntensities, + (int32_t deviceId, int32_t lightId), (const, override)); + MOCK_METHOD(void, setLightIntensities, + (int32_t deviceId, int32_t lightId, + (std::unordered_map<LightColor, int32_t>)intensities), + (override)); + + MOCK_METHOD(std::optional<RawLayoutInfo>, getRawLayoutInfo, (int32_t deviceId), + (const, override)); + MOCK_METHOD(int32_t, getScanCodeState, (int32_t deviceId, int32_t scanCode), (const, override)); + MOCK_METHOD(int32_t, getKeyCodeState, (int32_t deviceId, int32_t keyCode), (const, override)); + MOCK_METHOD(int32_t, getSwitchState, (int32_t deviceId, int32_t sw), (const, override)); + + MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue), + (const, override)); + MOCK_METHOD(int32_t, getKeyCodeForKeyLocation, (int32_t deviceId, int32_t locationKeyCode), + (const, override)); + MOCK_METHOD(bool, markSupportedKeyCodes, + (int32_t deviceId, const std::vector<int32_t>& keyCodes, uint8_t* outFlags), + (const, override)); + + MOCK_METHOD(bool, hasScanCode, (int32_t deviceId, int32_t scanCode), (const, override)); + + MOCK_METHOD(bool, hasKeyCode, (int32_t deviceId, int32_t keyCode), (const, override)); + + MOCK_METHOD(bool, hasLed, (int32_t deviceId, int32_t led), (const, override)); + + MOCK_METHOD(void, setLedState, (int32_t deviceId, int32_t led, bool on), (override)); + + MOCK_METHOD(void, getVirtualKeyDefinitions, + (int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys), + (const, override)); + + MOCK_METHOD(const std::shared_ptr<KeyCharacterMap>, getKeyCharacterMap, (int32_t deviceId), + (const, override)); + + MOCK_METHOD(bool, setKeyboardLayoutOverlay, + (int32_t deviceId, std::shared_ptr<KeyCharacterMap> map), (override)); + + MOCK_METHOD(void, vibrate, (int32_t deviceId, const VibrationElement& effect), (override)); + MOCK_METHOD(void, cancelVibrate, (int32_t deviceId), (override)); + + MOCK_METHOD(std::vector<int32_t>, getVibratorIds, (int32_t deviceId), (const, override)); + MOCK_METHOD(std::optional<int32_t>, getBatteryCapacity, (int32_t deviceId, int32_t batteryId), + (const, override)); + + MOCK_METHOD(std::optional<int32_t>, getBatteryStatus, (int32_t deviceId, int32_t batteryId), + (const, override)); + MOCK_METHOD(void, requestReopenDevices, (), (override)); + MOCK_METHOD(void, wake, (), (override)); + + MOCK_METHOD(void, dump, (std::string & dump), (const, override)); + MOCK_METHOD(void, monitor, (), (const, override)); + MOCK_METHOD(bool, isDeviceEnabled, (int32_t deviceId), (const, override)); + MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override)); + MOCK_METHOD(status_t, disableDevice, (int32_t deviceId), (override)); + MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override)); +}; + +} // namespace android diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp new file mode 100644 index 0000000000..92cd462c9a --- /dev/null +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TouchpadInputMapper.h" + +#include <android-base/logging.h> +#include <gtest/gtest.h> + +#include <thread> +#include "FakePointerController.h" +#include "InputMapperTest.h" +#include "InterfaceMocks.h" +#include "TestInputListenerMatchers.h" + +#define TAG "TouchpadInputMapper_test" + +namespace android { + +using testing::Return; +using testing::VariantWith; +constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP; +constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; +constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; +constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; + +/** + * Unit tests for TouchpadInputMapper. + */ +class TouchpadInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + + // Present scan codes: BTN_TOUCH and BTN_TOOL_FINGER + expectScanCodes(/*present=*/true, + {BTN_LEFT, BTN_RIGHT, BTN_TOOL_FINGER, BTN_TOOL_QUINTTAP, BTN_TOUCH, + BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP}); + // Missing scan codes that the mapper checks for. + expectScanCodes(/*present=*/false, + {BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, BTN_TOOL_PENCIL, + BTN_TOOL_AIRBRUSH}); + + // Current scan code state - all keys are UP by default + setScanCodeState(KeyState::UP, {BTN_TOUCH, BTN_STYLUS, + BTN_STYLUS2, BTN_0, + BTN_TOOL_FINGER, BTN_TOOL_PEN, + BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, + BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, + BTN_TOOL_MOUSE, BTN_TOOL_LENS, + BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, + BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP, + BTN_LEFT, BTN_RIGHT, + BTN_MIDDLE, BTN_BACK, + BTN_SIDE, BTN_FORWARD, + BTN_EXTRA, BTN_TASK}); + + setKeyCodeState(KeyState::UP, + {AKEYCODE_STYLUS_BUTTON_PRIMARY, AKEYCODE_STYLUS_BUTTON_SECONDARY}); + + // Key mappings + EXPECT_CALL(mMockEventHub, + mapKey(EVENTHUB_ID, BTN_LEFT, /*usageCode=*/0, /*metaState=*/0, testing::_, + testing::_, testing::_)) + .WillRepeatedly(Return(NAME_NOT_FOUND)); + + // Input properties - only INPUT_PROP_BUTTONPAD + EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_BUTTONPAD)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_SEMI_MT)) + .WillRepeatedly(Return(false)); + + // Axes that the device has + setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/4, /*resolution=*/0); + setupAxis(ABS_MT_POSITION_X, /*valid=*/true, /*min=*/0, /*max=*/2000, /*resolution=*/24); + setupAxis(ABS_MT_POSITION_Y, /*valid=*/true, /*min=*/0, /*max=*/1000, /*resolution=*/24); + setupAxis(ABS_MT_PRESSURE, /*valid=*/true, /*min*/ 0, /*max=*/255, /*resolution=*/0); + // Axes that the device does not have + setupAxis(ABS_MT_ORIENTATION, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TOUCH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0); + + EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_)) + .WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) { + *outValue = 0; + return OK; + }); + mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration); + } +}; + +/** + * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is + * generated when hovering stops. Currently, it is not. + * In the current implementation, HOVER_MOVE and ACTION_DOWN events are not sent out right away, + * but only after the button is released. + */ +TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { + std::list<NotifyArgs> args; + + args += process(EV_ABS, ABS_MT_TRACKING_ID, 1); + args += process(EV_KEY, BTN_TOUCH, 1); + setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER}); + args += process(EV_KEY, BTN_TOOL_FINGER, 1); + args += process(EV_ABS, ABS_MT_POSITION_X, 50); + args += process(EV_ABS, ABS_MT_POSITION_Y, 50); + args += process(EV_ABS, ABS_MT_PRESSURE, 1); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, testing::IsEmpty()); + + // Without this sleep, the test fails. + // TODO(b/284133337): Figure out whether this can be removed + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + args += process(EV_KEY, BTN_LEFT, 1); + setScanCodeState(KeyState::DOWN, {BTN_LEFT}); + args += process(EV_SYN, SYN_REPORT, 0); + + args += process(EV_KEY, BTN_LEFT, 0); + setScanCodeState(KeyState::UP, {BTN_LEFT}); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)))); + + // Liftoff + args.clear(); + args += process(EV_ABS, ABS_MT_PRESSURE, 0); + args += process(EV_ABS, ABS_MT_TRACKING_ID, -1); + args += process(EV_KEY, BTN_TOUCH, 0); + setScanCodeState(KeyState::UP, {BTN_TOOL_FINGER}); + args += process(EV_KEY, BTN_TOOL_FINGER, 0); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, testing::IsEmpty()); +} + +} // namespace android |