blob: f9752ed155dffc906ab4cdec3c51db5c3b8e1bd9 [file] [log] [blame]
Garfield Tanc15eb912019-08-05 16:47:40 -07001/*
2 * Copyright (C) 2019 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 "mocks/MockSprite.h"
18#include "mocks/MockSpriteController.h"
19
20#include <input/PointerController.h>
21#include <input/SpriteController.h>
22
23#include <atomic>
24#include <gmock/gmock.h>
25#include <gtest/gtest.h>
26#include <thread>
27
28namespace android {
29
30enum TestCursorType {
31 CURSOR_TYPE_DEFAULT = 0,
32 CURSOR_TYPE_HOVER,
33 CURSOR_TYPE_TOUCH,
34 CURSOR_TYPE_ANCHOR,
Garfield Tane9c61512019-11-21 16:42:13 -080035 CURSOR_TYPE_ADDITIONAL,
36 CURSOR_TYPE_ADDITIONAL_ANIM,
Garfield Tanc15eb912019-08-05 16:47:40 -070037 CURSOR_TYPE_CUSTOM = -1,
38};
39
40using ::testing::AllOf;
41using ::testing::Field;
Garfield Tanc15eb912019-08-05 16:47:40 -070042using ::testing::Mock;
Prabir Pradhanca7d7232020-01-31 17:42:34 -080043using ::testing::NiceMock;
Garfield Tanc15eb912019-08-05 16:47:40 -070044using ::testing::Return;
45using ::testing::Test;
46
47std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
48 return std::make_pair(type * 10, type * 10 + 5);
49}
50
51class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
52public:
53 virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
54 virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
55 virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
56 std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
57 virtual int32_t getDefaultPointerIconId() override;
58 virtual int32_t getCustomPointerIconId() override;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000059 virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
Garfield Tanc15eb912019-08-05 16:47:40 -070060
Prabir Pradhanca7d7232020-01-31 17:42:34 -080061 bool allResourcesAreLoaded();
62 bool noResourcesAreLoaded();
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000063 std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
Prabir Pradhanca7d7232020-01-31 17:42:34 -080064
Garfield Tanc15eb912019-08-05 16:47:40 -070065private:
66 void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080067
68 bool pointerIconLoaded{false};
69 bool pointerResourcesLoaded{false};
70 bool additionalMouseResourcesLoaded{false};
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000071 std::optional<int32_t /*displayId*/> latestPointerDisplayId;
Garfield Tanc15eb912019-08-05 16:47:40 -070072};
73
74void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
75 loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080076 pointerIconLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070077}
78
79void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
80 int32_t) {
81 loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
82 loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
83 loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080084 pointerResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070085}
86
87void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
88 std::map<int32_t, SpriteIcon>* outResources,
89 std::map<int32_t, PointerAnimation>* outAnimationResources,
90 int32_t) {
91 SpriteIcon icon;
92 PointerAnimation anim;
93
Garfield Tane9c61512019-11-21 16:42:13 -080094 // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
95 int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
96 loadPointerIconForType(&icon, cursorType);
97 (*outResources)[cursorType] = icon;
98
99 // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
100 cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
101 loadPointerIconForType(&icon, cursorType);
102 anim.animationFrames.push_back(icon);
103 anim.durationPerFrame = 10;
104 (*outResources)[cursorType] = icon;
105 (*outAnimationResources)[cursorType] = anim;
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800106
107 additionalMouseResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -0700108}
109
110int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
111 return CURSOR_TYPE_DEFAULT;
112}
113
114int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
115 return CURSOR_TYPE_CUSTOM;
116}
117
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800118bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
119 return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
120}
121
122bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
123 return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
124}
125
Garfield Tanc15eb912019-08-05 16:47:40 -0700126void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
127 icon->style = type;
128 std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
129 icon->hotSpotX = hotSpot.first;
130 icon->hotSpotY = hotSpot.second;
131}
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000132
133void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
134 float /*xPos*/,
135 float /*yPos*/) {
136 latestPointerDisplayId = displayId;
137}
138
Garfield Tanc15eb912019-08-05 16:47:40 -0700139class PointerControllerTest : public Test {
140protected:
141 PointerControllerTest();
142 ~PointerControllerTest();
143
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000144 void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800145
Garfield Tanc15eb912019-08-05 16:47:40 -0700146 sp<MockSprite> mPointerSprite;
147 sp<MockPointerControllerPolicyInterface> mPolicy;
148 sp<MockSpriteController> mSpriteController;
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100149 std::shared_ptr<PointerController> mPointerController;
Garfield Tanc15eb912019-08-05 16:47:40 -0700150
151private:
152 void loopThread();
153
154 std::atomic<bool> mRunning = true;
155 class MyLooper : public Looper {
156 public:
157 MyLooper() : Looper(false) {}
158 ~MyLooper() = default;
159 };
160 sp<MyLooper> mLooper;
161 std::thread mThread;
162};
163
164PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
165 mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
166
167 mSpriteController = new NiceMock<MockSpriteController>(mLooper);
168 mPolicy = new MockPointerControllerPolicyInterface();
169
170 EXPECT_CALL(*mSpriteController, createSprite())
171 .WillOnce(Return(mPointerSprite));
172
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100173 mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800174}
Garfield Tanc15eb912019-08-05 16:47:40 -0700175
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800176PointerControllerTest::~PointerControllerTest() {
177 mRunning.store(false, std::memory_order_relaxed);
178 mThread.join();
179}
180
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000181void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
Garfield Tanc15eb912019-08-05 16:47:40 -0700182 DisplayViewport viewport;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000183 viewport.displayId = displayId;
Garfield Tanc15eb912019-08-05 16:47:40 -0700184 viewport.logicalRight = 1600;
185 viewport.logicalBottom = 1200;
186 viewport.physicalRight = 800;
187 viewport.physicalBottom = 600;
188 viewport.deviceWidth = 400;
189 viewport.deviceHeight = 300;
190 mPointerController->setDisplayViewport(viewport);
Garfield Tanc15eb912019-08-05 16:47:40 -0700191}
192
193void PointerControllerTest::loopThread() {
194 Looper::setForThread(mLooper);
195
196 while (mRunning.load(std::memory_order_relaxed)) {
197 mLooper->pollOnce(100);
198 }
199}
200
201TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800202 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100203 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700204
205 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
206 EXPECT_CALL(*mPointerSprite, setVisible(true));
207 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
208 EXPECT_CALL(*mPointerSprite, setIcon(
209 AllOf(
210 Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
211 Field(&SpriteIcon::hotSpotX, hotspot.first),
212 Field(&SpriteIcon::hotSpotY, hotspot.second))));
213 mPointerController->reloadPointerResources();
214}
215
216TEST_F(PointerControllerTest, updatePointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800217 ensureDisplayViewportIsSet();
Liam Harringtonc782be62020-07-17 19:48:24 +0000218 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Michael Wright6853fe62020-07-02 00:01:38 +0100219 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700220
Garfield Tane9c61512019-11-21 16:42:13 -0800221 int32_t type = CURSOR_TYPE_ADDITIONAL;
Garfield Tanc15eb912019-08-05 16:47:40 -0700222 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
223 EXPECT_CALL(*mPointerSprite, setVisible(true));
224 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
225 EXPECT_CALL(*mPointerSprite, setIcon(
226 AllOf(
227 Field(&SpriteIcon::style, type),
228 Field(&SpriteIcon::hotSpotX, hotspot.first),
229 Field(&SpriteIcon::hotSpotY, hotspot.second))));
230 mPointerController->updatePointerIcon(type);
231}
232
233TEST_F(PointerControllerTest, setCustomPointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800234 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100235 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700236
237 int32_t style = CURSOR_TYPE_CUSTOM;
238 float hotSpotX = 15;
239 float hotSpotY = 20;
240
241 SpriteIcon icon;
242 icon.style = style;
243 icon.hotSpotX = hotSpotX;
244 icon.hotSpotY = hotSpotY;
245
246 EXPECT_CALL(*mPointerSprite, setVisible(true));
247 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
248 EXPECT_CALL(*mPointerSprite, setIcon(
249 AllOf(
250 Field(&SpriteIcon::style, style),
251 Field(&SpriteIcon::hotSpotX, hotSpotX),
252 Field(&SpriteIcon::hotSpotY, hotSpotY))));
253 mPointerController->setCustomPointerIcon(icon);
254}
255
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800256TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
Michael Wright6853fe62020-07-02 00:01:38 +0100257 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800258 mPointerController->setPosition(1.0f, 1.0f);
259 mPointerController->move(1.0f, 1.0f);
Michael Wright6853fe62020-07-02 00:01:38 +0100260 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
261 mPointerController->fade(PointerController::Transition::IMMEDIATE);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800262
263 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
264
265 ensureDisplayViewportIsSet();
266}
267
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000268TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
269 EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
270 << "A pointer display change does not occur when PointerController is created.";
271
272 ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
273
274 const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
275 ASSERT_TRUE(lastReportedPointerDisplayId)
276 << "The policy is notified of a pointer display change when the viewport is first set.";
277 EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
278 << "Incorrect pointer display notified.";
279
280 ensureDisplayViewportIsSet(42);
281
282 EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
283 << "The policy is notified when the pointer display changes.";
284
285 // Release the PointerController.
286 mPointerController = nullptr;
287
288 EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
289 << "The pointer display changes to invalid when PointerController is destroyed.";
290}
291
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800292class PointerControllerWindowInfoListenerTest : public Test {};
293
294class TestPointerController : public PointerController {
295public:
296 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
297 const sp<Looper>& looper)
298 : PointerController(
299 new MockPointerControllerPolicyInterface(), looper,
300 new NiceMock<MockSpriteController>(looper),
301 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
302 // Register listener
303 registeredListener = listener;
304 },
305 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
306 // Unregister listener
307 if (registeredListener == listener) registeredListener = nullptr;
308 }) {}
309};
310
311TEST_F(PointerControllerWindowInfoListenerTest,
312 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
313 sp<android::gui::WindowInfosListener> registeredListener;
314 sp<android::gui::WindowInfosListener> localListenerCopy;
315 {
316 TestPointerController pointerController(registeredListener, new Looper(false));
317 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
318 localListenerCopy = registeredListener;
319 }
320 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
321 localListenerCopy->onWindowInfosChanged({}, {});
322}
323
Garfield Tanc15eb912019-08-05 16:47:40 -0700324} // namespace android