blob: 366b3dcdd0e0c19a2180380d4b034eb2c0ed8fa2 [file] [log] [blame]
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +01001/*
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "RotaryEncoderInputMapper.h"
18
19#include <list>
20#include <string>
21#include <tuple>
22#include <variant>
23
24#include <android-base/logging.h>
Biswarup Palba27d1d2024-07-09 19:57:33 +000025#include <android_companion_virtualdevice_flags.h>
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +010026#include <gtest/gtest.h>
27#include <input/DisplayViewport.h>
28#include <linux/input-event-codes.h>
29#include <linux/input.h>
30#include <utils/Timers.h>
31
32#include "InputMapperTest.h"
33#include "InputReaderBase.h"
34#include "InterfaceMocks.h"
35#include "NotifyArgs.h"
36#include "TestEventMatchers.h"
37#include "ui/Rotation.h"
38
39#define TAG "RotaryEncoderInputMapper_test"
40
41namespace android {
42
43using testing::AllOf;
44using testing::Return;
45using testing::VariantWith;
46constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
47constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
48constexpr int32_t DISPLAY_WIDTH = 480;
49constexpr int32_t DISPLAY_HEIGHT = 800;
50
51namespace {
52
53DisplayViewport createViewport() {
54 DisplayViewport v;
55 v.orientation = ui::Rotation::Rotation0;
56 v.logicalRight = DISPLAY_HEIGHT;
57 v.logicalBottom = DISPLAY_WIDTH;
58 v.physicalRight = DISPLAY_HEIGHT;
59 v.physicalBottom = DISPLAY_WIDTH;
60 v.deviceWidth = DISPLAY_HEIGHT;
61 v.deviceHeight = DISPLAY_WIDTH;
62 v.isActive = true;
63 return v;
64}
65
66DisplayViewport createPrimaryViewport() {
67 DisplayViewport v = createViewport();
68 v.displayId = DISPLAY_ID;
69 v.uniqueId = "local:1";
70 return v;
71}
72
73DisplayViewport createSecondaryViewport() {
74 DisplayViewport v = createViewport();
75 v.displayId = SECONDARY_DISPLAY_ID;
76 v.uniqueId = "local:2";
77 v.type = ViewportType::EXTERNAL;
78 return v;
79}
80
81/**
82 * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
83 *
84 * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
85 * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
86 * device doesn't set its associated viewport when it's configured.
87 *
88 * TODO(b/319217713): work out a way to avoid this fake.
89 */
90class ViewportFakingInputDeviceContext : public InputDeviceContext {
91public:
92 ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
93 std::optional<DisplayViewport> viewport)
94 : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
95
96 ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId)
97 : ViewportFakingInputDeviceContext(device, eventHubId, createPrimaryViewport()) {}
98
99 std::optional<DisplayViewport> getAssociatedViewport() const override {
100 return mAssociatedViewport;
101 }
102
103 void setViewport(const std::optional<DisplayViewport>& viewport) {
104 mAssociatedViewport = viewport;
105 }
106
107private:
108 std::optional<DisplayViewport> mAssociatedViewport;
109};
110
111} // namespace
112
Biswarup Palba27d1d2024-07-09 19:57:33 +0000113namespace vd_flags = android::companion::virtualdevice::flags;
114
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100115/**
116 * Unit tests for RotaryEncoderInputMapper.
117 */
118class RotaryEncoderInputMapperTest : public InputMapperUnitTest {
119protected:
120 void SetUp() override { SetUpWithBus(BUS_USB); }
121 void SetUpWithBus(int bus) override {
122 InputMapperUnitTest::SetUpWithBus(bus);
123
124 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL))
125 .WillRepeatedly(Return(true));
126 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
127 .WillRepeatedly(Return(false));
Biswarup Pal8ff5e5e2024-06-15 12:58:20 +0000128 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
129 .WillRepeatedly(Return(false));
130 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
131 .WillRepeatedly(Return(false));
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100132 }
133};
134
135TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdWithAssociatedViewport) {
136 DisplayViewport primaryViewport = createPrimaryViewport();
137 DisplayViewport secondaryViewport = createSecondaryViewport();
138 mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
139
140 // Set up the secondary display as the associated viewport of the mapper.
141 createDevice();
142 ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
143 mMapper = createInputMapper<RotaryEncoderInputMapper>(deviceContext, mReaderConfiguration);
144
145 std::list<NotifyArgs> args;
146 // Ensure input events are generated for the secondary display.
147 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
148 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
149 EXPECT_THAT(args,
150 ElementsAre(VariantWith<NotifyMotionArgs>(
151 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
152 WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
153 WithDisplayId(SECONDARY_DISPLAY_ID)))));
154}
155
156TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdNoAssociatedViewport) {
157 // Set up the default display.
158 mFakePolicy->clearViewports();
159 mFakePolicy->addDisplayViewport(createPrimaryViewport());
160
161 // Set up the mapper with no associated viewport.
162 createDevice();
163 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
164
165 // Ensure input events are generated without display ID
166 std::list<NotifyArgs> args;
167 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
168 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
169 EXPECT_THAT(args,
170 ElementsAre(VariantWith<NotifyMotionArgs>(
171 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
172 WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
173 WithDisplayId(ui::LogicalDisplayId::INVALID)))));
174}
175
Biswarup Palba27d1d2024-07-09 19:57:33 +0000176TEST_F(RotaryEncoderInputMapperTest, ProcessRegularScroll) {
177 createDevice();
178 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
179
180 std::list<NotifyArgs> args;
181 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
182 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
183
184 EXPECT_THAT(args,
185 ElementsAre(VariantWith<NotifyMotionArgs>(
186 AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
187 WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f)))));
188}
189
190TEST_F(RotaryEncoderInputMapperTest, ProcessHighResScroll) {
191 vd_flags::high_resolution_scroll(true);
192 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
193 .WillRepeatedly(Return(true));
194 createDevice();
195 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
196
197 std::list<NotifyArgs> args;
198 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
199 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
200
201 EXPECT_THAT(args,
202 ElementsAre(VariantWith<NotifyMotionArgs>(
203 AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
204 WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
205}
206
207TEST_F(RotaryEncoderInputMapperTest, HighResScrollIgnoresRegularScroll) {
208 vd_flags::high_resolution_scroll(true);
209 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
210 .WillRepeatedly(Return(true));
211 createDevice();
212 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
213
214 std::list<NotifyArgs> args;
215 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
216 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
217 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
218
219 EXPECT_THAT(args,
220 ElementsAre(VariantWith<NotifyMotionArgs>(
221 AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
222 WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
223}
224
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100225} // namespace android