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 
60     bool allResourcesAreLoaded();
61     bool noResourcesAreLoaded();
62 
63 private:
64     void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
65 
66     bool pointerIconLoaded{false};
67     bool pointerResourcesLoaded{false};
68     bool additionalMouseResourcesLoaded{false};
69 };
70 
loadPointerIcon(SpriteIcon * icon,int32_t)71 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
72     loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
73     pointerIconLoaded = true;
74 }
75 
loadPointerResources(PointerResources * outResources,int32_t)76 void 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);
81     pointerResourcesLoaded = true;
82 }
83 
loadAdditionalMouseResources(std::map<int32_t,SpriteIcon> * outResources,std::map<int32_t,PointerAnimation> * outAnimationResources,int32_t)84 void 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 
91     // 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;
103 
104     additionalMouseResourcesLoaded = true;
105 }
106 
getDefaultPointerIconId()107 int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
108     return CURSOR_TYPE_DEFAULT;
109 }
110 
getCustomPointerIconId()111 int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
112     return CURSOR_TYPE_CUSTOM;
113 }
114 
allResourcesAreLoaded()115 bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
116     return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
117 }
118 
noResourcesAreLoaded()119 bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
120     return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
121 }
122 
loadPointerIconForType(SpriteIcon * icon,int32_t type)123 void 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 }
129 class PointerControllerTest : public Test {
130 protected:
131     PointerControllerTest();
132     ~PointerControllerTest();
133 
134     void ensureDisplayViewportIsSet();
135 
136     sp<MockSprite> mPointerSprite;
137     sp<MockPointerControllerPolicyInterface> mPolicy;
138     sp<MockSpriteController> mSpriteController;
139     std::shared_ptr<PointerController> mPointerController;
140 
141 private:
142     void loopThread();
143 
144     std::atomic<bool> mRunning = true;
145     class MyLooper : public Looper {
146     public:
MyLooper()147         MyLooper() : Looper(false) {}
148         ~MyLooper() = default;
149     };
150     sp<MyLooper> mLooper;
151     std::thread mThread;
152 };
153 
PointerControllerTest()154 PointerControllerTest::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 
163     mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
164 }
165 
~PointerControllerTest()166 PointerControllerTest::~PointerControllerTest() {
167     mRunning.store(false, std::memory_order_relaxed);
168     mThread.join();
169 }
170 
ensureDisplayViewportIsSet()171 void PointerControllerTest::ensureDisplayViewportIsSet() {
172     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);
181 
182     // The first call to setDisplayViewport should trigger the loading of the necessary resources.
183     EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
184 }
185 
loopThread()186 void PointerControllerTest::loopThread() {
187     Looper::setForThread(mLooper);
188 
189     while (mRunning.load(std::memory_order_relaxed)) {
190         mLooper->pollOnce(100);
191     }
192 }
193 
TEST_F(PointerControllerTest,useDefaultCursorTypeByDefault)194 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
195     ensureDisplayViewportIsSet();
196     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
197 
198     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
199     EXPECT_CALL(*mPointerSprite, setVisible(true));
200     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
201     EXPECT_CALL(*mPointerSprite, setIcon(
202             AllOf(
203                     Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
204                     Field(&SpriteIcon::hotSpotX, hotspot.first),
205                     Field(&SpriteIcon::hotSpotY, hotspot.second))));
206     mPointerController->reloadPointerResources();
207 }
208 
TEST_F(PointerControllerTest,updatePointerIcon)209 TEST_F(PointerControllerTest, updatePointerIcon) {
210     ensureDisplayViewportIsSet();
211     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
212 
213     int32_t type = CURSOR_TYPE_ADDITIONAL;
214     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
215     EXPECT_CALL(*mPointerSprite, setVisible(true));
216     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
217     EXPECT_CALL(*mPointerSprite, setIcon(
218             AllOf(
219                     Field(&SpriteIcon::style, type),
220                     Field(&SpriteIcon::hotSpotX, hotspot.first),
221                     Field(&SpriteIcon::hotSpotY, hotspot.second))));
222     mPointerController->updatePointerIcon(type);
223 }
224 
TEST_F(PointerControllerTest,setCustomPointerIcon)225 TEST_F(PointerControllerTest, setCustomPointerIcon) {
226     ensureDisplayViewportIsSet();
227     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
228 
229     int32_t style = CURSOR_TYPE_CUSTOM;
230     float hotSpotX = 15;
231     float hotSpotY = 20;
232 
233     SpriteIcon icon;
234     icon.style = style;
235     icon.hotSpotX = hotSpotX;
236     icon.hotSpotY = hotSpotY;
237 
238     EXPECT_CALL(*mPointerSprite, setVisible(true));
239     EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
240     EXPECT_CALL(*mPointerSprite, setIcon(
241             AllOf(
242                     Field(&SpriteIcon::style, style),
243                     Field(&SpriteIcon::hotSpotX, hotSpotX),
244                     Field(&SpriteIcon::hotSpotY, hotSpotY))));
245     mPointerController->setCustomPointerIcon(icon);
246 }
247 
TEST_F(PointerControllerTest,doesNotGetResourcesBeforeSettingViewport)248 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
249     mPointerController->setPresentation(PointerController::Presentation::POINTER);
250     mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
251     mPointerController->clearSpots();
252     mPointerController->setPosition(1.0f, 1.0f);
253     mPointerController->move(1.0f, 1.0f);
254     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
255     mPointerController->fade(PointerController::Transition::IMMEDIATE);
256 
257     EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
258 
259     ensureDisplayViewportIsSet();
260 }
261 
262 }  // namespace android
263