• 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 <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <input/PointerController.h>
20 #include <input/SpriteController.h>
21 
22 #include <atomic>
23 #include <thread>
24 
25 #include "input/Input.h"
26 #include "mocks/MockSprite.h"
27 #include "mocks/MockSpriteController.h"
28 
29 namespace android {
30 
31 enum TestCursorType {
32     CURSOR_TYPE_DEFAULT = 0,
33     CURSOR_TYPE_HOVER,
34     CURSOR_TYPE_TOUCH,
35     CURSOR_TYPE_ANCHOR,
36     CURSOR_TYPE_ADDITIONAL,
37     CURSOR_TYPE_ADDITIONAL_ANIM,
38     CURSOR_TYPE_STYLUS,
39     CURSOR_TYPE_CUSTOM = -1,
40 };
41 
42 using ::testing::AllOf;
43 using ::testing::Field;
44 using ::testing::NiceMock;
45 using ::testing::Return;
46 using ::testing::Test;
47 
getHotSpotCoordinatesForType(int32_t type)48 std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
49     return std::make_pair(type * 10, type * 10 + 5);
50 }
51 
52 class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
53 public:
54     virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
55     virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
56     virtual void loadAdditionalMouseResources(
57             std::map<PointerIconStyle, SpriteIcon>* outResources,
58             std::map<PointerIconStyle, PointerAnimation>* outAnimationResources,
59             int32_t displayId) override;
60     virtual PointerIconStyle getDefaultPointerIconId() override;
61     virtual PointerIconStyle getDefaultStylusIconId() override;
62     virtual PointerIconStyle getCustomPointerIconId() override;
63     virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override;
64 
65     bool allResourcesAreLoaded();
66     bool noResourcesAreLoaded();
getLastReportedPointerDisplayId()67     std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
68 
69 private:
70     void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
71 
72     bool pointerIconLoaded{false};
73     bool pointerResourcesLoaded{false};
74     bool additionalMouseResourcesLoaded{false};
75     std::optional<int32_t /*displayId*/> latestPointerDisplayId;
76 };
77 
loadPointerIcon(SpriteIcon * icon,int32_t)78 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
79     loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
80     pointerIconLoaded = true;
81 }
82 
loadPointerResources(PointerResources * outResources,int32_t)83 void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
84         int32_t) {
85     loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
86     loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
87     loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
88     pointerResourcesLoaded = true;
89 }
90 
loadAdditionalMouseResources(std::map<PointerIconStyle,SpriteIcon> * outResources,std::map<PointerIconStyle,PointerAnimation> * outAnimationResources,int32_t)91 void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
92         std::map<PointerIconStyle, SpriteIcon>* outResources,
93         std::map<PointerIconStyle, PointerAnimation>* outAnimationResources, int32_t) {
94     SpriteIcon icon;
95     PointerAnimation anim;
96 
97     // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
98     int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
99     loadPointerIconForType(&icon, cursorType);
100     (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
101 
102     // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
103     cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
104     loadPointerIconForType(&icon, cursorType);
105     anim.animationFrames.push_back(icon);
106     anim.durationPerFrame = 10;
107     (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
108     (*outAnimationResources)[static_cast<PointerIconStyle>(cursorType)] = anim;
109 
110     // CURSOR_TYPE_STYLUS doesn't have animation resource.
111     cursorType = CURSOR_TYPE_STYLUS;
112     loadPointerIconForType(&icon, cursorType);
113     (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
114 
115     additionalMouseResourcesLoaded = true;
116 }
117 
getDefaultPointerIconId()118 PointerIconStyle MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
119     return static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT);
120 }
121 
getDefaultStylusIconId()122 PointerIconStyle MockPointerControllerPolicyInterface::getDefaultStylusIconId() {
123     return static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS);
124 }
125 
getCustomPointerIconId()126 PointerIconStyle MockPointerControllerPolicyInterface::getCustomPointerIconId() {
127     return static_cast<PointerIconStyle>(CURSOR_TYPE_CUSTOM);
128 }
129 
allResourcesAreLoaded()130 bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
131     return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
132 }
133 
noResourcesAreLoaded()134 bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
135     return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
136 }
137 
loadPointerIconForType(SpriteIcon * icon,int32_t type)138 void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
139     icon->style = static_cast<PointerIconStyle>(type);
140     std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
141     icon->hotSpotX = hotSpot.first;
142     icon->hotSpotY = hotSpot.second;
143 }
144 
onPointerDisplayIdChanged(int32_t displayId,const FloatPoint &)145 void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
146                                                                      const FloatPoint& /*position*/
147 ) {
148     latestPointerDisplayId = displayId;
149 }
150 
151 class PointerControllerTest : public Test {
152 protected:
153     PointerControllerTest();
154     ~PointerControllerTest();
155 
156     void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
157 
158     sp<MockSprite> mPointerSprite;
159     sp<MockPointerControllerPolicyInterface> mPolicy;
160     sp<MockSpriteController> mSpriteController;
161     std::shared_ptr<PointerController> mPointerController;
162 
163 private:
164     void loopThread();
165 
166     std::atomic<bool> mRunning = true;
167     class MyLooper : public Looper {
168     public:
MyLooper()169         MyLooper() : Looper(false) {}
170         ~MyLooper() = default;
171     };
172     sp<MyLooper> mLooper;
173     std::thread mThread;
174 };
175 
PointerControllerTest()176 PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
177         mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
178 
179     mSpriteController = new NiceMock<MockSpriteController>(mLooper);
180     mPolicy = new MockPointerControllerPolicyInterface();
181 
182     EXPECT_CALL(*mSpriteController, createSprite())
183             .WillOnce(Return(mPointerSprite));
184 
185     mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
186 }
187 
~PointerControllerTest()188 PointerControllerTest::~PointerControllerTest() {
189     mRunning.store(false, std::memory_order_relaxed);
190     mThread.join();
191 }
192 
ensureDisplayViewportIsSet(int32_t displayId)193 void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
194     DisplayViewport viewport;
195     viewport.displayId = displayId;
196     viewport.logicalRight = 1600;
197     viewport.logicalBottom = 1200;
198     viewport.physicalRight = 800;
199     viewport.physicalBottom = 600;
200     viewport.deviceWidth = 400;
201     viewport.deviceHeight = 300;
202     mPointerController->setDisplayViewport(viewport);
203 }
204 
loopThread()205 void PointerControllerTest::loopThread() {
206     Looper::setForThread(mLooper);
207 
208     while (mRunning.load(std::memory_order_relaxed)) {
209         mLooper->pollOnce(100);
210     }
211 }
212 
TEST_F(PointerControllerTest,useDefaultCursorTypeByDefault)213 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
214     ensureDisplayViewportIsSet();
215     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
216 
217     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
218     EXPECT_CALL(*mPointerSprite, setVisible(true));
219     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
220     EXPECT_CALL(*mPointerSprite,
221                 setIcon(AllOf(Field(&SpriteIcon::style,
222                                     static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
223                               Field(&SpriteIcon::hotSpotX, hotspot.first),
224                               Field(&SpriteIcon::hotSpotY, hotspot.second))));
225     mPointerController->reloadPointerResources();
226 }
227 
TEST_F(PointerControllerTest,useStylusTypeForStylusHover)228 TEST_F(PointerControllerTest, useStylusTypeForStylusHover) {
229     ensureDisplayViewportIsSet();
230     mPointerController->setPresentation(PointerController::Presentation::STYLUS_HOVER);
231     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
232     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_STYLUS);
233     EXPECT_CALL(*mPointerSprite, setVisible(true));
234     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
235     EXPECT_CALL(*mPointerSprite,
236                 setIcon(AllOf(Field(&SpriteIcon::style,
237                                     static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS)),
238                               Field(&SpriteIcon::hotSpotX, hotspot.first),
239                               Field(&SpriteIcon::hotSpotY, hotspot.second))));
240     mPointerController->reloadPointerResources();
241 }
242 
TEST_F(PointerControllerTest,updatePointerIcon)243 TEST_F(PointerControllerTest, updatePointerIcon) {
244     ensureDisplayViewportIsSet();
245     mPointerController->setPresentation(PointerController::Presentation::POINTER);
246     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
247 
248     int32_t type = CURSOR_TYPE_ADDITIONAL;
249     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
250     EXPECT_CALL(*mPointerSprite, setVisible(true));
251     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
252     EXPECT_CALL(*mPointerSprite,
253                 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
254                               Field(&SpriteIcon::hotSpotX, hotspot.first),
255                               Field(&SpriteIcon::hotSpotY, hotspot.second))));
256     mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
257 }
258 
TEST_F(PointerControllerTest,setCustomPointerIcon)259 TEST_F(PointerControllerTest, setCustomPointerIcon) {
260     ensureDisplayViewportIsSet();
261     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
262 
263     int32_t style = CURSOR_TYPE_CUSTOM;
264     float hotSpotX = 15;
265     float hotSpotY = 20;
266 
267     SpriteIcon icon;
268     icon.style = static_cast<PointerIconStyle>(style);
269     icon.hotSpotX = hotSpotX;
270     icon.hotSpotY = hotSpotY;
271 
272     EXPECT_CALL(*mPointerSprite, setVisible(true));
273     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
274     EXPECT_CALL(*mPointerSprite,
275                 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(style)),
276                               Field(&SpriteIcon::hotSpotX, hotSpotX),
277                               Field(&SpriteIcon::hotSpotY, hotSpotY))));
278     mPointerController->setCustomPointerIcon(icon);
279 }
280 
TEST_F(PointerControllerTest,doesNotGetResourcesBeforeSettingViewport)281 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
282     mPointerController->setPresentation(PointerController::Presentation::POINTER);
283     mPointerController->setPosition(1.0f, 1.0f);
284     mPointerController->move(1.0f, 1.0f);
285     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
286     mPointerController->fade(PointerController::Transition::IMMEDIATE);
287 
288     EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
289 
290     ensureDisplayViewportIsSet();
291 }
292 
TEST_F(PointerControllerTest,notifiesPolicyWhenPointerDisplayChanges)293 TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
294     EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
295             << "A pointer display change does not occur when PointerController is created.";
296 
297     ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
298 
299     const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
300     ASSERT_TRUE(lastReportedPointerDisplayId)
301             << "The policy is notified of a pointer display change when the viewport is first set.";
302     EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
303             << "Incorrect pointer display notified.";
304 
305     ensureDisplayViewportIsSet(42);
306 
307     EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
308             << "The policy is notified when the pointer display changes.";
309 
310     // Release the PointerController.
311     mPointerController = nullptr;
312 
313     EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
314             << "The pointer display changes to invalid when PointerController is destroyed.";
315 }
316 
317 class PointerControllerWindowInfoListenerTest : public Test {};
318 
319 class TestPointerController : public PointerController {
320 public:
TestPointerController(sp<android::gui::WindowInfosListener> & registeredListener,const sp<Looper> & looper)321     TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
322                           const sp<Looper>& looper)
323           : PointerController(
324                     new MockPointerControllerPolicyInterface(), looper,
325                     new NiceMock<MockSpriteController>(looper),
326                     [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
327                         // Register listener
328                         registeredListener = listener;
329                     },
__anone9b9fb6d0202(const sp<android::gui::WindowInfosListener>& listener) 330                     [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
331                         // Unregister listener
332                         if (registeredListener == listener) registeredListener = nullptr;
333                     }) {}
334 };
335 
TEST_F(PointerControllerWindowInfoListenerTest,doesNotCrashIfListenerCalledAfterPointerControllerDestroyed)336 TEST_F(PointerControllerWindowInfoListenerTest,
337        doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
338     sp<android::gui::WindowInfosListener> registeredListener;
339     sp<android::gui::WindowInfosListener> localListenerCopy;
340     {
341         TestPointerController pointerController(registeredListener, new Looper(false));
342         ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
343         localListenerCopy = registeredListener;
344     }
345     EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
346     localListenerCopy->onWindowInfosChanged({{}, {}, 0, 0});
347 }
348 
349 }  // namespace android
350