• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
28 namespace android {
29 
30 enum TestCursorType {
31     CURSOR_TYPE_DEFAULT = 0,
32     CURSOR_TYPE_HOVER,
33     CURSOR_TYPE_TOUCH,
34     CURSOR_TYPE_ANCHOR,
35     CURSOR_TYPE_ADDITIONAL,
36     CURSOR_TYPE_ADDITIONAL_ANIM,
37     CURSOR_TYPE_CUSTOM = -1,
38 };
39 
40 using ::testing::AllOf;
41 using ::testing::Field;
42 using ::testing::Mock;
43 using ::testing::NiceMock;
44 using ::testing::Return;
45 using ::testing::Test;
46 
getHotSpotCoordinatesForType(int32_t type)47 std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
48     return std::make_pair(type * 10, type * 10 + 5);
49 }
50 
51 class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
52 public:
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     virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
60 
61     bool allResourcesAreLoaded();
62     bool noResourcesAreLoaded();
getLastReportedPointerDisplayId()63     std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
64 
65 private:
66     void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
67 
68     bool pointerIconLoaded{false};
69     bool pointerResourcesLoaded{false};
70     bool additionalMouseResourcesLoaded{false};
71     std::optional<int32_t /*displayId*/> latestPointerDisplayId;
72 };
73 
loadPointerIcon(SpriteIcon * icon,int32_t)74 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
75     loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
76     pointerIconLoaded = true;
77 }
78 
loadPointerResources(PointerResources * outResources,int32_t)79 void 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);
84     pointerResourcesLoaded = true;
85 }
86 
loadAdditionalMouseResources(std::map<int32_t,SpriteIcon> * outResources,std::map<int32_t,PointerAnimation> * outAnimationResources,int32_t)87 void 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 
94     // 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;
106 
107     additionalMouseResourcesLoaded = true;
108 }
109 
getDefaultPointerIconId()110 int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
111     return CURSOR_TYPE_DEFAULT;
112 }
113 
getCustomPointerIconId()114 int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
115     return CURSOR_TYPE_CUSTOM;
116 }
117 
allResourcesAreLoaded()118 bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
119     return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
120 }
121 
noResourcesAreLoaded()122 bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
123     return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
124 }
125 
loadPointerIconForType(SpriteIcon * icon,int32_t type)126 void 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 }
132 
onPointerDisplayIdChanged(int32_t displayId,float,float)133 void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
134                                                                      float /*xPos*/,
135                                                                      float /*yPos*/) {
136     latestPointerDisplayId = displayId;
137 }
138 
139 class PointerControllerTest : public Test {
140 protected:
141     PointerControllerTest();
142     ~PointerControllerTest();
143 
144     void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
145 
146     sp<MockSprite> mPointerSprite;
147     sp<MockPointerControllerPolicyInterface> mPolicy;
148     sp<MockSpriteController> mSpriteController;
149     std::shared_ptr<PointerController> mPointerController;
150 
151 private:
152     void loopThread();
153 
154     std::atomic<bool> mRunning = true;
155     class MyLooper : public Looper {
156     public:
MyLooper()157         MyLooper() : Looper(false) {}
158         ~MyLooper() = default;
159     };
160     sp<MyLooper> mLooper;
161     std::thread mThread;
162 };
163 
PointerControllerTest()164 PointerControllerTest::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 
173     mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
174 }
175 
~PointerControllerTest()176 PointerControllerTest::~PointerControllerTest() {
177     mRunning.store(false, std::memory_order_relaxed);
178     mThread.join();
179 }
180 
ensureDisplayViewportIsSet(int32_t displayId)181 void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
182     DisplayViewport viewport;
183     viewport.displayId = displayId;
184     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);
191 }
192 
loopThread()193 void PointerControllerTest::loopThread() {
194     Looper::setForThread(mLooper);
195 
196     while (mRunning.load(std::memory_order_relaxed)) {
197         mLooper->pollOnce(100);
198     }
199 }
200 
TEST_F(PointerControllerTest,useDefaultCursorTypeByDefault)201 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
202     ensureDisplayViewportIsSet();
203     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
204 
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 
TEST_F(PointerControllerTest,updatePointerIcon)216 TEST_F(PointerControllerTest, updatePointerIcon) {
217     ensureDisplayViewportIsSet();
218     mPointerController->setPresentation(PointerController::Presentation::POINTER);
219     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
220 
221     int32_t type = CURSOR_TYPE_ADDITIONAL;
222     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 
TEST_F(PointerControllerTest,setCustomPointerIcon)233 TEST_F(PointerControllerTest, setCustomPointerIcon) {
234     ensureDisplayViewportIsSet();
235     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
236 
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 
TEST_F(PointerControllerTest,doesNotGetResourcesBeforeSettingViewport)256 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
257     mPointerController->setPresentation(PointerController::Presentation::POINTER);
258     mPointerController->setPosition(1.0f, 1.0f);
259     mPointerController->move(1.0f, 1.0f);
260     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
261     mPointerController->fade(PointerController::Transition::IMMEDIATE);
262 
263     EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
264 
265     ensureDisplayViewportIsSet();
266 }
267 
TEST_F(PointerControllerTest,notifiesPolicyWhenPointerDisplayChanges)268 TEST_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 
292 class PointerControllerWindowInfoListenerTest : public Test {};
293 
294 class TestPointerController : public PointerController {
295 public:
TestPointerController(sp<android::gui::WindowInfosListener> & registeredListener,const sp<Looper> & looper)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                     },
__anon4ef577b40202(const sp<android::gui::WindowInfosListener>& listener) 305                     [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
306                         // Unregister listener
307                         if (registeredListener == listener) registeredListener = nullptr;
308                     }) {}
309 };
310 
TEST_F(PointerControllerWindowInfoListenerTest,doesNotCrashIfListenerCalledAfterPointerControllerDestroyed)311 TEST_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 
324 }  // namespace android
325