blob: dae1fccec8043eca96d8d8c33c75f7e270cf4876 [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;
59
Prabir Pradhanca7d7232020-01-31 17:42:34 -080060 bool allResourcesAreLoaded();
61 bool noResourcesAreLoaded();
62
Garfield Tanc15eb912019-08-05 16:47:40 -070063private:
64 void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080065
66 bool pointerIconLoaded{false};
67 bool pointerResourcesLoaded{false};
68 bool additionalMouseResourcesLoaded{false};
Garfield Tanc15eb912019-08-05 16:47:40 -070069};
70
71void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
72 loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080073 pointerIconLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070074}
75
76void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
77 int32_t) {
78 loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
79 loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
80 loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080081 pointerResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070082}
83
84void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
85 std::map<int32_t, SpriteIcon>* outResources,
86 std::map<int32_t, PointerAnimation>* outAnimationResources,
87 int32_t) {
88 SpriteIcon icon;
89 PointerAnimation anim;
90
Garfield Tane9c61512019-11-21 16:42:13 -080091 // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
92 int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
93 loadPointerIconForType(&icon, cursorType);
94 (*outResources)[cursorType] = icon;
95
96 // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
97 cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
98 loadPointerIconForType(&icon, cursorType);
99 anim.animationFrames.push_back(icon);
100 anim.durationPerFrame = 10;
101 (*outResources)[cursorType] = icon;
102 (*outAnimationResources)[cursorType] = anim;
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800103
104 additionalMouseResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -0700105}
106
107int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
108 return CURSOR_TYPE_DEFAULT;
109}
110
111int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
112 return CURSOR_TYPE_CUSTOM;
113}
114
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800115bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
116 return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
117}
118
119bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
120 return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
121}
122
Garfield Tanc15eb912019-08-05 16:47:40 -0700123void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
124 icon->style = type;
125 std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
126 icon->hotSpotX = hotSpot.first;
127 icon->hotSpotY = hotSpot.second;
128}
Garfield Tanc15eb912019-08-05 16:47:40 -0700129class PointerControllerTest : public Test {
130protected:
131 PointerControllerTest();
132 ~PointerControllerTest();
133
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800134 void ensureDisplayViewportIsSet();
135
Garfield Tanc15eb912019-08-05 16:47:40 -0700136 sp<MockSprite> mPointerSprite;
137 sp<MockPointerControllerPolicyInterface> mPolicy;
138 sp<MockSpriteController> mSpriteController;
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100139 std::shared_ptr<PointerController> mPointerController;
Garfield Tanc15eb912019-08-05 16:47:40 -0700140
141private:
142 void loopThread();
143
144 std::atomic<bool> mRunning = true;
145 class MyLooper : public Looper {
146 public:
147 MyLooper() : Looper(false) {}
148 ~MyLooper() = default;
149 };
150 sp<MyLooper> mLooper;
151 std::thread mThread;
152};
153
154PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
155 mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
156
157 mSpriteController = new NiceMock<MockSpriteController>(mLooper);
158 mPolicy = new MockPointerControllerPolicyInterface();
159
160 EXPECT_CALL(*mSpriteController, createSprite())
161 .WillOnce(Return(mPointerSprite));
162
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100163 mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800164}
Garfield Tanc15eb912019-08-05 16:47:40 -0700165
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800166PointerControllerTest::~PointerControllerTest() {
167 mRunning.store(false, std::memory_order_relaxed);
168 mThread.join();
169}
170
171void PointerControllerTest::ensureDisplayViewportIsSet() {
Garfield Tanc15eb912019-08-05 16:47:40 -0700172 DisplayViewport viewport;
173 viewport.displayId = ADISPLAY_ID_DEFAULT;
174 viewport.logicalRight = 1600;
175 viewport.logicalBottom = 1200;
176 viewport.physicalRight = 800;
177 viewport.physicalBottom = 600;
178 viewport.deviceWidth = 400;
179 viewport.deviceHeight = 300;
180 mPointerController->setDisplayViewport(viewport);
Garfield Tanc15eb912019-08-05 16:47:40 -0700181}
182
183void PointerControllerTest::loopThread() {
184 Looper::setForThread(mLooper);
185
186 while (mRunning.load(std::memory_order_relaxed)) {
187 mLooper->pollOnce(100);
188 }
189}
190
191TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800192 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100193 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700194
195 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
196 EXPECT_CALL(*mPointerSprite, setVisible(true));
197 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
198 EXPECT_CALL(*mPointerSprite, setIcon(
199 AllOf(
200 Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
201 Field(&SpriteIcon::hotSpotX, hotspot.first),
202 Field(&SpriteIcon::hotSpotY, hotspot.second))));
203 mPointerController->reloadPointerResources();
204}
205
206TEST_F(PointerControllerTest, updatePointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800207 ensureDisplayViewportIsSet();
Liam Harringtonc782be62020-07-17 19:48:24 +0000208 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Michael Wright6853fe62020-07-02 00:01:38 +0100209 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700210
Garfield Tane9c61512019-11-21 16:42:13 -0800211 int32_t type = CURSOR_TYPE_ADDITIONAL;
Garfield Tanc15eb912019-08-05 16:47:40 -0700212 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
213 EXPECT_CALL(*mPointerSprite, setVisible(true));
214 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
215 EXPECT_CALL(*mPointerSprite, setIcon(
216 AllOf(
217 Field(&SpriteIcon::style, type),
218 Field(&SpriteIcon::hotSpotX, hotspot.first),
219 Field(&SpriteIcon::hotSpotY, hotspot.second))));
220 mPointerController->updatePointerIcon(type);
221}
222
223TEST_F(PointerControllerTest, setCustomPointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800224 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100225 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700226
227 int32_t style = CURSOR_TYPE_CUSTOM;
228 float hotSpotX = 15;
229 float hotSpotY = 20;
230
231 SpriteIcon icon;
232 icon.style = style;
233 icon.hotSpotX = hotSpotX;
234 icon.hotSpotY = hotSpotY;
235
236 EXPECT_CALL(*mPointerSprite, setVisible(true));
237 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
238 EXPECT_CALL(*mPointerSprite, setIcon(
239 AllOf(
240 Field(&SpriteIcon::style, style),
241 Field(&SpriteIcon::hotSpotX, hotSpotX),
242 Field(&SpriteIcon::hotSpotY, hotSpotY))));
243 mPointerController->setCustomPointerIcon(icon);
244}
245
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800246TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
Michael Wright6853fe62020-07-02 00:01:38 +0100247 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800248 mPointerController->setPosition(1.0f, 1.0f);
249 mPointerController->move(1.0f, 1.0f);
Michael Wright6853fe62020-07-02 00:01:38 +0100250 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
251 mPointerController->fade(PointerController::Transition::IMMEDIATE);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800252
253 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
254
255 ensureDisplayViewportIsSet();
256}
257
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800258class PointerControllerWindowInfoListenerTest : public Test {};
259
260class TestPointerController : public PointerController {
261public:
262 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
263 const sp<Looper>& looper)
264 : PointerController(
265 new MockPointerControllerPolicyInterface(), looper,
266 new NiceMock<MockSpriteController>(looper),
267 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
268 // Register listener
269 registeredListener = listener;
270 },
271 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
272 // Unregister listener
273 if (registeredListener == listener) registeredListener = nullptr;
274 }) {}
275};
276
277TEST_F(PointerControllerWindowInfoListenerTest,
278 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
279 sp<android::gui::WindowInfosListener> registeredListener;
280 sp<android::gui::WindowInfosListener> localListenerCopy;
281 {
282 TestPointerController pointerController(registeredListener, new Looper(false));
283 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
284 localListenerCopy = registeredListener;
285 }
286 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
287 localListenerCopy->onWindowInfosChanged({}, {});
288}
289
Garfield Tanc15eb912019-08-05 16:47:40 -0700290} // namespace android