blob: a796c49dbeba88a994c4d28afc3158ca56f23c5d [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.
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100141 ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
142 mMapper = createInputMapper<RotaryEncoderInputMapper>(deviceContext, mReaderConfiguration);
143
144 std::list<NotifyArgs> args;
145 // Ensure input events are generated for the secondary display.
146 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
147 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
148 EXPECT_THAT(args,
149 ElementsAre(VariantWith<NotifyMotionArgs>(
150 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
151 WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
152 WithDisplayId(SECONDARY_DISPLAY_ID)))));
153}
154
155TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdNoAssociatedViewport) {
156 // Set up the default display.
157 mFakePolicy->clearViewports();
158 mFakePolicy->addDisplayViewport(createPrimaryViewport());
159
160 // Set up the mapper with no associated viewport.
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100161 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
162
163 // Ensure input events are generated without display ID
164 std::list<NotifyArgs> args;
165 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
166 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
167 EXPECT_THAT(args,
168 ElementsAre(VariantWith<NotifyMotionArgs>(
169 AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
170 WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
171 WithDisplayId(ui::LogicalDisplayId::INVALID)))));
172}
173
Biswarup Palba27d1d2024-07-09 19:57:33 +0000174TEST_F(RotaryEncoderInputMapperTest, ProcessRegularScroll) {
Biswarup Palba27d1d2024-07-09 19:57:33 +0000175 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
176
177 std::list<NotifyArgs> args;
178 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
179 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
180
181 EXPECT_THAT(args,
182 ElementsAre(VariantWith<NotifyMotionArgs>(
183 AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
184 WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f)))));
185}
186
187TEST_F(RotaryEncoderInputMapperTest, ProcessHighResScroll) {
188 vd_flags::high_resolution_scroll(true);
189 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
190 .WillRepeatedly(Return(true));
Biswarup Palba27d1d2024-07-09 19:57:33 +0000191 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
192
193 std::list<NotifyArgs> args;
194 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
195 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
196
197 EXPECT_THAT(args,
198 ElementsAre(VariantWith<NotifyMotionArgs>(
199 AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
200 WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
201}
202
203TEST_F(RotaryEncoderInputMapperTest, HighResScrollIgnoresRegularScroll) {
204 vd_flags::high_resolution_scroll(true);
205 EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
206 .WillRepeatedly(Return(true));
Biswarup Palba27d1d2024-07-09 19:57:33 +0000207 mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
208
209 std::list<NotifyArgs> args;
210 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
211 args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
212 args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
213
214 EXPECT_THAT(args,
215 ElementsAre(VariantWith<NotifyMotionArgs>(
216 AllOf(WithSource(AINPUT_SOURCE_ROTARY_ENCODER),
217 WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
218}
219
Vladimir Komsiyskidd438e22024-02-13 11:47:54 +0100220} // namespace android