blob: a6a4115476df4749f123934afe314c6d9c738e1d [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
Brandon Pollack015f5d92022-06-02 06:59:33 +000017#include <gmock/gmock.h>
18#include <gtest/gtest.h>
Garfield Tanc15eb912019-08-05 16:47:40 -070019#include <input/PointerController.h>
20#include <input/SpriteController.h>
21
22#include <atomic>
Garfield Tanc15eb912019-08-05 16:47:40 -070023#include <thread>
24
Brandon Pollack015f5d92022-06-02 06:59:33 +000025#include "input/Input.h"
26#include "mocks/MockSprite.h"
27#include "mocks/MockSpriteController.h"
28
Garfield Tanc15eb912019-08-05 16:47:40 -070029namespace android {
30
31enum TestCursorType {
32 CURSOR_TYPE_DEFAULT = 0,
33 CURSOR_TYPE_HOVER,
34 CURSOR_TYPE_TOUCH,
35 CURSOR_TYPE_ANCHOR,
Garfield Tane9c61512019-11-21 16:42:13 -080036 CURSOR_TYPE_ADDITIONAL,
37 CURSOR_TYPE_ADDITIONAL_ANIM,
Garfield Tanc15eb912019-08-05 16:47:40 -070038 CURSOR_TYPE_CUSTOM = -1,
39};
40
41using ::testing::AllOf;
42using ::testing::Field;
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;
Brandon Pollack015f5d92022-06-02 06:59:33 +000055 virtual void loadAdditionalMouseResources(
56 std::map<PointerIconStyle, SpriteIcon>* outResources,
57 std::map<PointerIconStyle, PointerAnimation>* outAnimationResources,
58 int32_t displayId) override;
59 virtual PointerIconStyle getDefaultPointerIconId() override;
60 virtual PointerIconStyle getCustomPointerIconId() override;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000061 virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
Garfield Tanc15eb912019-08-05 16:47:40 -070062
Prabir Pradhanca7d7232020-01-31 17:42:34 -080063 bool allResourcesAreLoaded();
64 bool noResourcesAreLoaded();
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000065 std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
Prabir Pradhanca7d7232020-01-31 17:42:34 -080066
Garfield Tanc15eb912019-08-05 16:47:40 -070067private:
68 void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080069
70 bool pointerIconLoaded{false};
71 bool pointerResourcesLoaded{false};
72 bool additionalMouseResourcesLoaded{false};
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000073 std::optional<int32_t /*displayId*/> latestPointerDisplayId;
Garfield Tanc15eb912019-08-05 16:47:40 -070074};
75
76void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
77 loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080078 pointerIconLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070079}
80
81void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
82 int32_t) {
83 loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
84 loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
85 loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080086 pointerResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070087}
88
89void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
Brandon Pollack015f5d92022-06-02 06:59:33 +000090 std::map<PointerIconStyle, SpriteIcon>* outResources,
91 std::map<PointerIconStyle, PointerAnimation>* outAnimationResources, int32_t) {
Garfield Tanc15eb912019-08-05 16:47:40 -070092 SpriteIcon icon;
93 PointerAnimation anim;
94
Garfield Tane9c61512019-11-21 16:42:13 -080095 // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
96 int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
97 loadPointerIconForType(&icon, cursorType);
Brandon Pollack015f5d92022-06-02 06:59:33 +000098 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
Garfield Tane9c61512019-11-21 16:42:13 -080099
100 // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
101 cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
102 loadPointerIconForType(&icon, cursorType);
103 anim.animationFrames.push_back(icon);
104 anim.durationPerFrame = 10;
Brandon Pollack015f5d92022-06-02 06:59:33 +0000105 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
106 (*outAnimationResources)[static_cast<PointerIconStyle>(cursorType)] = anim;
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800107
108 additionalMouseResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -0700109}
110
Brandon Pollack015f5d92022-06-02 06:59:33 +0000111PointerIconStyle MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
112 return static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT);
Garfield Tanc15eb912019-08-05 16:47:40 -0700113}
114
Brandon Pollack015f5d92022-06-02 06:59:33 +0000115PointerIconStyle MockPointerControllerPolicyInterface::getCustomPointerIconId() {
116 return static_cast<PointerIconStyle>(CURSOR_TYPE_CUSTOM);
Garfield Tanc15eb912019-08-05 16:47:40 -0700117}
118
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800119bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
120 return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
121}
122
123bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
124 return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
125}
126
Garfield Tanc15eb912019-08-05 16:47:40 -0700127void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000128 icon->style = static_cast<PointerIconStyle>(type);
Garfield Tanc15eb912019-08-05 16:47:40 -0700129 std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
130 icon->hotSpotX = hotSpot.first;
131 icon->hotSpotY = hotSpot.second;
132}
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000133
134void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
135 float /*xPos*/,
136 float /*yPos*/) {
137 latestPointerDisplayId = displayId;
138}
139
Garfield Tanc15eb912019-08-05 16:47:40 -0700140class PointerControllerTest : public Test {
141protected:
142 PointerControllerTest();
143 ~PointerControllerTest();
144
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000145 void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800146
Garfield Tanc15eb912019-08-05 16:47:40 -0700147 sp<MockSprite> mPointerSprite;
148 sp<MockPointerControllerPolicyInterface> mPolicy;
149 sp<MockSpriteController> mSpriteController;
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100150 std::shared_ptr<PointerController> mPointerController;
Garfield Tanc15eb912019-08-05 16:47:40 -0700151
152private:
153 void loopThread();
154
155 std::atomic<bool> mRunning = true;
156 class MyLooper : public Looper {
157 public:
158 MyLooper() : Looper(false) {}
159 ~MyLooper() = default;
160 };
161 sp<MyLooper> mLooper;
162 std::thread mThread;
163};
164
165PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
166 mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
167
168 mSpriteController = new NiceMock<MockSpriteController>(mLooper);
169 mPolicy = new MockPointerControllerPolicyInterface();
170
171 EXPECT_CALL(*mSpriteController, createSprite())
172 .WillOnce(Return(mPointerSprite));
173
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100174 mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800175}
Garfield Tanc15eb912019-08-05 16:47:40 -0700176
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800177PointerControllerTest::~PointerControllerTest() {
178 mRunning.store(false, std::memory_order_relaxed);
179 mThread.join();
180}
181
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000182void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
Garfield Tanc15eb912019-08-05 16:47:40 -0700183 DisplayViewport viewport;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000184 viewport.displayId = displayId;
Garfield Tanc15eb912019-08-05 16:47:40 -0700185 viewport.logicalRight = 1600;
186 viewport.logicalBottom = 1200;
187 viewport.physicalRight = 800;
188 viewport.physicalBottom = 600;
189 viewport.deviceWidth = 400;
190 viewport.deviceHeight = 300;
191 mPointerController->setDisplayViewport(viewport);
Garfield Tanc15eb912019-08-05 16:47:40 -0700192}
193
194void PointerControllerTest::loopThread() {
195 Looper::setForThread(mLooper);
196
197 while (mRunning.load(std::memory_order_relaxed)) {
198 mLooper->pollOnce(100);
199 }
200}
201
202TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800203 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100204 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700205
206 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
207 EXPECT_CALL(*mPointerSprite, setVisible(true));
208 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000209 EXPECT_CALL(*mPointerSprite,
210 setIcon(AllOf(Field(&SpriteIcon::style,
211 static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
212 Field(&SpriteIcon::hotSpotX, hotspot.first),
213 Field(&SpriteIcon::hotSpotY, hotspot.second))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700214 mPointerController->reloadPointerResources();
215}
216
217TEST_F(PointerControllerTest, updatePointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800218 ensureDisplayViewportIsSet();
Liam Harringtonc782be62020-07-17 19:48:24 +0000219 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Michael Wright6853fe62020-07-02 00:01:38 +0100220 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700221
Garfield Tane9c61512019-11-21 16:42:13 -0800222 int32_t type = CURSOR_TYPE_ADDITIONAL;
Garfield Tanc15eb912019-08-05 16:47:40 -0700223 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
224 EXPECT_CALL(*mPointerSprite, setVisible(true));
225 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000226 EXPECT_CALL(*mPointerSprite,
227 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
228 Field(&SpriteIcon::hotSpotX, hotspot.first),
229 Field(&SpriteIcon::hotSpotY, hotspot.second))));
230 mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
Garfield Tanc15eb912019-08-05 16:47:40 -0700231}
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;
Brandon Pollack015f5d92022-06-02 06:59:33 +0000242 icon.style = static_cast<PointerIconStyle>(style);
Garfield Tanc15eb912019-08-05 16:47:40 -0700243 icon.hotSpotX = hotSpotX;
244 icon.hotSpotY = hotSpotY;
245
246 EXPECT_CALL(*mPointerSprite, setVisible(true));
247 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000248 EXPECT_CALL(*mPointerSprite,
249 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(style)),
250 Field(&SpriteIcon::hotSpotX, hotSpotX),
251 Field(&SpriteIcon::hotSpotY, hotSpotY))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700252 mPointerController->setCustomPointerIcon(icon);
253}
254
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800255TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
Michael Wright6853fe62020-07-02 00:01:38 +0100256 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800257 mPointerController->setPosition(1.0f, 1.0f);
258 mPointerController->move(1.0f, 1.0f);
Michael Wright6853fe62020-07-02 00:01:38 +0100259 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
260 mPointerController->fade(PointerController::Transition::IMMEDIATE);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800261
262 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
263
264 ensureDisplayViewportIsSet();
265}
266
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000267TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
268 EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
269 << "A pointer display change does not occur when PointerController is created.";
270
271 ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
272
273 const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
274 ASSERT_TRUE(lastReportedPointerDisplayId)
275 << "The policy is notified of a pointer display change when the viewport is first set.";
276 EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
277 << "Incorrect pointer display notified.";
278
279 ensureDisplayViewportIsSet(42);
280
281 EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
282 << "The policy is notified when the pointer display changes.";
283
284 // Release the PointerController.
285 mPointerController = nullptr;
286
287 EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
288 << "The pointer display changes to invalid when PointerController is destroyed.";
289}
290
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800291class PointerControllerWindowInfoListenerTest : public Test {};
292
293class TestPointerController : public PointerController {
294public:
295 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
296 const sp<Looper>& looper)
297 : PointerController(
298 new MockPointerControllerPolicyInterface(), looper,
299 new NiceMock<MockSpriteController>(looper),
300 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
301 // Register listener
302 registeredListener = listener;
303 },
304 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
305 // Unregister listener
306 if (registeredListener == listener) registeredListener = nullptr;
307 }) {}
308};
309
310TEST_F(PointerControllerWindowInfoListenerTest,
311 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
312 sp<android::gui::WindowInfosListener> registeredListener;
313 sp<android::gui::WindowInfosListener> localListenerCopy;
314 {
315 TestPointerController pointerController(registeredListener, new Looper(false));
316 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
317 localListenerCopy = registeredListener;
318 }
319 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
320 localListenerCopy->onWindowInfosChanged({}, {});
321}
322
Garfield Tanc15eb912019-08-05 16:47:40 -0700323} // namespace android