| /* |
| * 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 "../InputDeviceMetricsSource.h" |
| |
| #include <NotifyArgsBuilders.h> |
| |
| #include <android/input.h> |
| #include <ftl/enum.h> |
| #include <gtest/gtest.h> |
| #include <input/Input.h> |
| #include <input/InputEventBuilders.h> |
| #include <linux/input.h> |
| |
| #include <set> |
| |
| namespace android { |
| |
| namespace { |
| |
| constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>(); |
| constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN; |
| constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS; |
| constexpr uint32_t KEY_SOURCES = |
| AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD; |
| constexpr int32_t POINTER_1_DOWN = |
| AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); |
| |
| } // namespace |
| |
| // --- InputDeviceMetricsSourceDeviceClassificationTest --- |
| |
| class DeviceClassificationFixture : public ::testing::Test, |
| public ::testing::WithParamInterface<InputDeviceUsageSource> {}; |
| |
| TEST_P(DeviceClassificationFixture, ValidClassifications) { |
| const InputDeviceUsageSource usageSource = GetParam(); |
| |
| // Use a switch to ensure a test is added for all source classifications. |
| switch (usageSource) { |
| case InputDeviceUsageSource::UNKNOWN: { |
| ASSERT_EQ(InputDeviceUsageSource::UNKNOWN, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN) |
| .build())); |
| |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::PALM) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::BUTTONS: { |
| ASSERT_EQ(InputDeviceUsageSource::BUTTONS, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES) |
| .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::KEYBOARD: { |
| ASSERT_EQ(InputDeviceUsageSource::KEYBOARD, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::DPAD: { |
| ASSERT_EQ(InputDeviceUsageSource::DPAD, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES) |
| .keyCode(AKEYCODE_DPAD_CENTER) |
| .build())); |
| |
| ASSERT_EQ(InputDeviceUsageSource::DPAD, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES) |
| .keyCode(AKEYCODE_DPAD_CENTER) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::GAMEPAD: { |
| ASSERT_EQ(InputDeviceUsageSource::GAMEPAD, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES) |
| .keyCode(AKEYCODE_BUTTON_A) |
| .build())); |
| |
| ASSERT_EQ(InputDeviceUsageSource::GAMEPAD, |
| getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC, |
| KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES) |
| .keyCode(AKEYCODE_BUTTON_A) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::JOYSTICK: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN) |
| .axis(AMOTION_EVENT_AXIS_GAS, 1.f)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::MOUSE: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, |
| AINPUT_SOURCE_MOUSE) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::MOUSE_CAPTURED: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, |
| AINPUT_SOURCE_MOUSE_RELATIVE) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE) |
| .x(100) |
| .y(200) |
| .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100) |
| .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::TOUCHPAD: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::TOUCHPAD_CAPTURED: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER) |
| .x(100) |
| .y(200) |
| .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1) |
| .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::ROTARY_ENCODER: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL, |
| AINPUT_SOURCE_ROTARY_ENCODER) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN) |
| .axis(AMOTION_EVENT_AXIS_SCROLL, 10) |
| .axis(AMOTION_EVENT_AXIS_VSCROLL, 10)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::STYLUS_DIRECT: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, |
| STYLUS | TOUCHSCREEN) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::STYLUS_INDIRECT: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, |
| STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::STYLUS_FUSED: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, |
| AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::TOUCH_NAVIGATION: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, |
| AINPUT_SOURCE_TOUCH_NAVIGATION) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER) |
| .x(100) |
| .y(200)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::TOUCHSCREEN: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER) |
| .x(100) |
| .y(200)) |
| .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER) |
| .x(300) |
| .y(400)) |
| .build())); |
| break; |
| } |
| |
| case InputDeviceUsageSource::TRACKBALL: { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL, |
| AINPUT_SOURCE_TRACKBALL) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN) |
| .axis(AMOTION_EVENT_AXIS_VSCROLL, 100) |
| .axis(AMOTION_EVENT_AXIS_HSCROLL, 200)) |
| .build())); |
| break; |
| } |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsSourceDeviceClassificationTest, |
| DeviceClassificationFixture, |
| ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()), |
| [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) { |
| return ftl::enum_string(testParamInfo.param); |
| }); |
| |
| TEST(InputDeviceMetricsSourceDeviceClassificationTest, MixedClassificationTouchscreenStylus) { |
| std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN, |
| InputDeviceUsageSource::STYLUS_DIRECT}; |
| ASSERT_EQ(srcs, |
| getUsageSourcesForMotionArgs( |
| MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS) |
| .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200)) |
| .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400)) |
| .build())); |
| } |
| |
| } // namespace android |