1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cmath>
17 #include "gtest/gtest.h"
18 #include "ui_action.h"
19
20 using namespace OHOS::uitest;
21 using namespace std;
22
23 static constexpr uint32_t CUSTOM_CLICK_HOLD_MS = 100;
24 static constexpr uint32_t CUSTOM_LONGCLICK_HOLD_MS = 200;
25 static constexpr uint32_t CUSTOM_DOUBLECLICK_HOLD_MS = 300;
26 static constexpr uint32_t CUSTOM_KEY_HOLD_MS = 400;
27 static constexpr uint32_t CUSTOM_SWIPE_VELOCITY_PPS = 500;
28 static constexpr uint32_t CUSTOM_UI_STEADY_MS = 600;
29 static constexpr uint32_t CUSTOM_WAIT_UI_STEADY_MS = 700;
30
31 class UiActionTest : public testing::Test {
32 public:
33 ~UiActionTest() override = default;
34 protected:
SetUp()35 void SetUp() override
36 {
37 Test::SetUp();
38 // use custom UiOperationOptions
39 customOptions_.clickHoldMs_ = CUSTOM_CLICK_HOLD_MS;
40 customOptions_.longClickHoldMs_ = CUSTOM_LONGCLICK_HOLD_MS;
41 customOptions_.doubleClickIntervalMs_ = CUSTOM_DOUBLECLICK_HOLD_MS;
42 customOptions_.keyHoldMs_ = CUSTOM_KEY_HOLD_MS;
43 customOptions_.swipeVelocityPps_ = CUSTOM_SWIPE_VELOCITY_PPS;
44 customOptions_.uiSteadyThresholdMs_ = CUSTOM_UI_STEADY_MS;
45 customOptions_.waitUiSteadyMaxMs_ = CUSTOM_WAIT_UI_STEADY_MS;
46 }
47 UiDriveOptions customOptions_;
48 };
49
TEST_F(UiActionTest,computeClickAction)50 TEST_F(UiActionTest, computeClickAction)
51 {
52 GenericClick action(PointerOp::CLICK_P);
53 Point point {100, 200};
54 vector<TouchEvent> events;
55 action.Decompose(events, point, customOptions_);
56 ASSERT_EQ(2, events.size()); // up & down
57 auto event1 = *events.begin();
58 auto event2 = *(events.begin() + 1);
59 ASSERT_EQ(point.px_, event1.point_.px_);
60 ASSERT_EQ(point.py_, event1.point_.py_);
61 ASSERT_EQ(ActionStage::DOWN, event1.stage_);
62 ASSERT_EQ(0, event1.downTimeOffsetMs_);
63
64 ASSERT_EQ(point.px_, event2.point_.px_);
65 ASSERT_EQ(point.py_, event2.point_.py_);
66 ASSERT_EQ(ActionStage::UP, event2.stage_);
67 ASSERT_EQ(customOptions_.clickHoldMs_, event2.downTimeOffsetMs_);
68 }
69
TEST_F(UiActionTest,computeLongClickAction)70 TEST_F(UiActionTest, computeLongClickAction)
71 {
72 GenericClick action(PointerOp::LONG_CLICK_P);
73 Point point {100, 200};
74 vector<TouchEvent> events;
75 action.Decompose(events, point, customOptions_);
76 ASSERT_EQ(2, events.size()); // up & down
77 auto event1 = *events.begin();
78 auto event2 = *(events.begin() + 1);
79 ASSERT_EQ(point.px_, event1.point_.px_);
80 ASSERT_EQ(point.py_, event1.point_.py_);
81 ASSERT_EQ(ActionStage::DOWN, event1.stage_);
82 ASSERT_EQ(0, event1.downTimeOffsetMs_);
83 // there should be a proper pause after touch down to make long-click
84 ASSERT_EQ(customOptions_.longClickHoldMs_, event1.holdMs_);
85
86 ASSERT_EQ(point.px_, event2.point_.px_);
87 ASSERT_EQ(point.py_, event2.point_.py_);
88 ASSERT_EQ(ActionStage::UP, event2.stage_);
89 ASSERT_EQ(customOptions_.longClickHoldMs_, event2.downTimeOffsetMs_);
90 }
91
TEST_F(UiActionTest,computeDoubleClickAction)92 TEST_F(UiActionTest, computeDoubleClickAction)
93 {
94 GenericClick action(PointerOp::DOUBLE_CLICK_P);
95 Point point {100, 200};
96 vector<TouchEvent> events;
97 action.Decompose(events, point, customOptions_);
98 ASSERT_EQ(4, events.size()); // up-down-interval-up-down
99 auto event1 = *events.begin();
100 auto event2 = *(events.begin() + 1);
101 auto event3 = *(events.begin() + 2);
102 auto event4 = *(events.begin() + 3);
103 ASSERT_EQ(point.px_, event1.point_.px_);
104 ASSERT_EQ(point.py_, event1.point_.py_);
105 ASSERT_EQ(ActionStage::DOWN, event1.stage_);
106 ASSERT_EQ(0, event1.downTimeOffsetMs_);
107
108 ASSERT_EQ(point.px_, event2.point_.px_);
109 ASSERT_EQ(point.py_, event2.point_.py_);
110 ASSERT_EQ(ActionStage::UP, event2.stage_);
111 ASSERT_EQ(customOptions_.clickHoldMs_, event2.downTimeOffsetMs_);
112 // there should be a proper pause after first click
113 ASSERT_EQ(customOptions_.doubleClickIntervalMs_, event2.holdMs_);
114
115 ASSERT_EQ(point.px_, event3.point_.px_);
116 ASSERT_EQ(point.py_, event3.point_.py_);
117 ASSERT_EQ(ActionStage::DOWN, event3.stage_);
118 ASSERT_EQ(0, event3.downTimeOffsetMs_);
119
120 ASSERT_EQ(point.px_, event4.point_.px_);
121 ASSERT_EQ(point.py_, event4.point_.py_);
122 ASSERT_EQ(ActionStage::UP, event4.stage_);
123 ASSERT_EQ(customOptions_.clickHoldMs_, event4.downTimeOffsetMs_);
124 }
125
TEST_F(UiActionTest,computeSwipeAction)126 TEST_F(UiActionTest, computeSwipeAction)
127 {
128 UiDriveOptions opt {};
129 opt.swipeVelocityPps_ = 50; // specify the swipe velocity
130 Point point0(0, 0);
131 Point point1(100, 200);
132 GenericSwipe action(PointerOp::SWIPE_P);
133 vector<TouchEvent> events;
134 action.Decompose(events, point0, point1, opt);
135 // there should be more than 1 touches
136 const int32_t steps = events.size() - 1;
137 ASSERT_TRUE(steps > 1);
138
139 const int32_t disX = point1.px_ - point0.px_;
140 const int32_t disY = point1.py_ - point0.py_;
141 const uint32_t distance = sqrt(disX * disX + disY * disY);
142 const uint32_t totalCostMs = distance * 1000 / opt.swipeVelocityPps_;
143
144 uint32_t step = 0;
145 // check the TouchEvent of each step
146 for (auto &event:events) {
147 int32_t expectedPointerX = point0.px_ + (disX * step) / steps;
148 int32_t expectedPointerY = point0.py_ + (disY * step) / steps;
149 uint32_t expectedTimeOffset = (totalCostMs * step) / steps;
150 ASSERT_NEAR(expectedPointerX, event.point_.px_, 5);
151 ASSERT_NEAR(expectedPointerY, event.point_.py_, 5);
152 ASSERT_NEAR(expectedTimeOffset, event.downTimeOffsetMs_, 5);
153 if (step == 0) {
154 // should start with Action.DOWN
155 ASSERT_EQ(ActionStage::DOWN, event.stage_);
156 } else if (step == events.size() - 1) {
157 // should end with Action.UP
158 ASSERT_EQ(ActionStage::UP, event.stage_);
159 } else {
160 // middle events should all be action-MOVE
161 ASSERT_EQ(ActionStage::MOVE, event.stage_);
162 }
163 step++;
164 }
165 }
166
TEST_F(UiActionTest,computeDragAction)167 TEST_F(UiActionTest, computeDragAction)
168 {
169 UiDriveOptions opt {};
170 opt.longClickHoldMs_ = 2000; // specify the long-click duration
171 Point point0(0, 0);
172 Point point1(100, 200);
173 GenericSwipe swipeAction(PointerOp::SWIPE_P);
174 GenericSwipe dragAction(PointerOp::DRAG_P);
175 vector<TouchEvent> swipeEvents;
176 vector<TouchEvent> dragEvents;
177 swipeAction.Decompose(swipeEvents, point0, point1, opt);
178 dragAction.Decompose(dragEvents, point0, point1, opt);
179
180 ASSERT_TRUE(swipeEvents.size() > 1);\
181 ASSERT_EQ(swipeEvents.size(), dragEvents.size());
182
183 // check the hold time of each event
184 for (auto step = 0; step < swipeEvents.size(); step++) {
185 auto &swipeEvent = swipeEvents.at(step);
186 auto &dragEvent = dragEvents.at(step);
187 ASSERT_EQ(swipeEvent.stage_, dragEvent.stage_);
188 // drag needs longPressDown firstly, the downOffSet of following event should be delayed
189 if (step == 0) {
190 ASSERT_EQ(swipeEvent.downTimeOffsetMs_, dragEvent.downTimeOffsetMs_);
191 ASSERT_EQ(swipeEvent.holdMs_ + opt.longClickHoldMs_, dragEvent.holdMs_);
192 } else {
193 ASSERT_EQ(swipeEvent.downTimeOffsetMs_ + opt.longClickHoldMs_, dragEvent.downTimeOffsetMs_);
194 ASSERT_EQ(swipeEvent.holdMs_, dragEvent.holdMs_);
195 }
196 }
197 }
198
TEST_F(UiActionTest,computeBackKeyAction)199 TEST_F(UiActionTest, computeBackKeyAction)
200 {
201 Back keyAction;
202 vector<KeyEvent> events;
203 keyAction.ComputeEvents(events, customOptions_);
204 ASSERT_EQ(2, events.size()); // up & down
205 auto event1 = *events.begin();
206 auto event2 = *(events.begin() + 1);
207 ASSERT_EQ(KEYCODE_BACK, event1.code_);
208 ASSERT_EQ(ActionStage::DOWN, event1.stage_);
209
210 ASSERT_EQ(KEYCODE_BACK, event2.code_);
211 ASSERT_EQ(ActionStage::UP, event2.stage_);
212 ASSERT_EQ(KEYNAME_BACK, keyAction.Describe()); // test the description
213 }
214
TEST_F(UiActionTest,computePasteAction)215 TEST_F(UiActionTest, computePasteAction)
216 {
217 Paste keyAction;
218 vector<KeyEvent> events;
219 keyAction.ComputeEvents(events, customOptions_);
220 ASSERT_EQ(4, events.size()); // ctrl_down/key_down/key_up/ctrl_up
221 auto event1 = *events.begin();
222 auto event2 = *(events.begin() + 1);
223 auto event3 = *(events.begin() + 2);
224 auto event4 = *(events.begin() + 3);
225
226 ASSERT_EQ(KEYCODE_CTRL, event1.code_);
227 ASSERT_EQ(ActionStage::DOWN, event1.stage_);
228 ASSERT_EQ(KEYCODE_V, event2.code_);
229 ASSERT_EQ(ActionStage::DOWN, event2.stage_);
230
231 ASSERT_EQ(KEYCODE_V, event3.code_);
232 ASSERT_EQ(ActionStage::UP, event3.stage_);
233 ASSERT_EQ(KEYCODE_CTRL, event4.code_);
234 ASSERT_EQ(ActionStage::UP, event4.stage_);
235 ASSERT_EQ(KEYNAME_PASTE, keyAction.Describe());
236 }
237
TEST_F(UiActionTest,anonymousSignleKey)238 TEST_F(UiActionTest, anonymousSignleKey)
239 {
240 static constexpr uint32_t keyCode = 1234;
241 static const string expectedDesc = "key_" + to_string(keyCode);
242 AnonymousSingleKey anonymousKey(keyCode);
243 vector<KeyEvent> events;
244 anonymousKey.ComputeEvents(events, customOptions_);
245 ASSERT_EQ(2, events.size()); // up & down
246 auto event1 = *events.begin();
247 auto event2 = *(events.begin() + 1);
248 ASSERT_EQ(keyCode, event1.code_);
249 ASSERT_EQ(ActionStage::DOWN, event1.stage_);
250
251 ASSERT_EQ(keyCode, event2.code_);
252 ASSERT_EQ(ActionStage::UP, event2.stage_);
253 ASSERT_EQ(expectedDesc, anonymousKey.Describe());
254 }
255