• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 #include "gtest/gtest.h"
16 #include "widget_operator.h"
17 
18 using namespace OHOS::uitest;
19 using namespace std;
20 
21 static constexpr auto ATTR_TEXT = "text";
22 // record the triggered touch events.
23 
24 static std::unique_ptr<PointerMatrix> touch_event_records = nullptr;
25 
26 class MockController2 : public UiController {
27 public:
MockController2()28     explicit MockController2() : UiController("mock_controller") {}
29 
30     ~MockController2() = default;
31 
SetDomFrame(string_view domFrame)32     void SetDomFrame(string_view domFrame)
33     {
34         mockDomFrames_.clear();
35         mockDomFrames_.emplace_back(string(domFrame));
36         frameIndex_ = 0;
37     }
38 
SetDomFrames(vector<string> domFrames)39     void SetDomFrames(vector<string> domFrames)
40     {
41         mockDomFrames_.clear();
42         mockDomFrames_ = move(domFrames);
43         frameIndex_ = 0;
44     }
45 
GetConsumedDomFrameCount() const46     uint32_t GetConsumedDomFrameCount() const
47     {
48         return frameIndex_;
49     }
50 
GetUiHierarchy(vector<pair<Window,nlohmann::json>> & out)51     void GetUiHierarchy(vector<pair<Window, nlohmann::json>>& out) override
52     {
53         uint32_t newIndex = frameIndex_;
54         frameIndex_++;
55         auto winInfo = Window(0);
56         auto dom = nlohmann::json();
57         if (newIndex >= mockDomFrames_.size()) {
58             dom = nlohmann::json::parse(mockDomFrames_.at(mockDomFrames_.size() - 1));
59         } else {
60             dom = nlohmann::json::parse(mockDomFrames_.at((newIndex)));
61         }
62         out.push_back(make_pair(move(winInfo), move(dom)));
63     }
64 
IsWorkable() const65     bool IsWorkable() const override
66     {
67         return true;
68     }
69 
InjectTouchEventSequence(const PointerMatrix & events) const70     void InjectTouchEventSequence(const PointerMatrix &events) const override
71     {
72         touch_event_records = std::make_unique<PointerMatrix>(events.GetFingers(), events.GetSteps());
73         for (uint32_t step = 0; step < events.GetSteps(); step++) {
74             for (uint32_t finger = 0; finger < events.GetFingers(); finger++) {
75                 touch_event_records->PushAction(events.At(finger, step));
76             }
77         }
78     }
79 
80 private:
81     vector<string> mockDomFrames_;
82     mutable uint32_t frameIndex_ = 0;
83 };
84 
85 // test fixture
86 class WidgetOperatorTest : public testing::Test {
87 protected:
SetUp()88     void SetUp() override
89     {
90         touch_event_records.reset(nullptr);
91         auto mockController = make_unique<MockController2>();
92         controller_ = mockController.get();
93         UiController::RegisterController(move(mockController), Priority::MEDIUM);
94         driver_ = make_unique<UiDriver>();
95     }
96 
TearDown()97     void TearDown() override
98     {
99         controller_ = nullptr;
100         UiController::RemoveAllControllers();
101     }
102 
103     MockController2 *controller_ = nullptr;
104     unique_ptr<UiDriver> driver_ = nullptr;
105     UiOpArgs opt_;
106 
107     ~WidgetOperatorTest() override = default;
108 };
109 
TEST_F(WidgetOperatorTest,scrollSearchRetrieveSubjectWidgetFailed)110 TEST_F(WidgetOperatorTest, scrollSearchRetrieveSubjectWidgetFailed)
111 {
112     constexpr auto mockDom0 = R"({
113 "attributes": {
114 "index": "0",
115 "resource-id": "id1",
116 "bounds": "[0,0][100,100]",
117 "text": ""
118 },
119 "children": [
120 {
121 "attributes": {
122 "index": "0",
123 "resource-id": "id4",
124 "bounds": "[0,0][50,50]",
125 "text": "USB"
126 },
127 "children": []
128 }
129 ]
130 })";
131     constexpr auto mockDom1 = R"({"attributes":{"bounds":"[0,0][100,100]"},"children":[]})";
132     controller_->SetDomFrame(mockDom0);
133 
134     auto error = ApiCallErr(NO_ERROR);
135     auto scrollWidgetSelector = WidgetSelector();
136     auto matcher = WidgetAttrMatcher(ATTR_TEXT, "USB", EQ);
137     scrollWidgetSelector.AddMatcher(matcher);
138     vector<unique_ptr<Widget>> widgets;
139     driver_->FindWidgets(scrollWidgetSelector, widgets, error);
140 
141     ASSERT_EQ(1, widgets.size());
142 
143     // mock another dom on which the scroll-widget is missing, and perform scroll-search
144     controller_->SetDomFrame(mockDom1);
145     error = ApiCallErr(NO_ERROR);
146     auto targetWidgetSelector = WidgetSelector();
147     auto wOp = WidgetOperator(*driver_, *widgets.at(0), opt_);
148     ASSERT_EQ(nullptr, wOp.ScrollFindWidget(targetWidgetSelector, error));
149     // retrieve scroll widget failed should be marked as exception
150     ASSERT_EQ(ERR_COMPONENT_LOST, error.code_);
151     ASSERT_TRUE(error.message_.find(scrollWidgetSelector.Describe()) != string::npos)
152                                 << "Error message should contains the scroll-widget selection description";
153 }
154 
TEST_F(WidgetOperatorTest,scrollSearchTargetWidgetNotExist)155 TEST_F(WidgetOperatorTest, scrollSearchTargetWidgetNotExist)
156 {
157     constexpr auto mockDom = R"({
158 "attributes": {
159 "index": "0",
160 "resource-id": "id1",
161 "bounds": "[0,0][100,100]",
162 "text": ""
163 },
164 "children": [
165 {
166 "attributes": {
167 "index": "0",
168 "resource-id": "id4",
169 "bounds": "[0,0][50,50]",
170 "text": "USB"
171 },
172 "children": []
173 }
174 ]
175 }
176 )";
177     controller_->SetDomFrame(mockDom);
178 
179     auto error = ApiCallErr(NO_ERROR);
180     auto scrollWidgetSelector = WidgetSelector();
181     auto matcher = WidgetAttrMatcher(ATTR_TEXT, "USB", EQ);
182     scrollWidgetSelector.AddMatcher(matcher);
183     vector<unique_ptr<Widget>> widgets;
184     driver_->FindWidgets(scrollWidgetSelector, widgets, error);
185 
186     ASSERT_EQ(1, widgets.size());
187 
188     error = ApiCallErr(NO_ERROR);
189     auto targetWidgetMatcher = WidgetAttrMatcher(ATTR_TEXT, "wyz", EQ);
190     auto targetWidgetSelector = WidgetSelector();
191     targetWidgetSelector.AddMatcher(targetWidgetMatcher);
192     auto wOp = WidgetOperator(*driver_, *widgets.at(0), opt_);
193     ASSERT_EQ(nullptr, wOp.ScrollFindWidget(targetWidgetSelector, error));
194 }
195 
TEST_F(WidgetOperatorTest,scrollSearchCheckSubjectWidget)196 TEST_F(WidgetOperatorTest, scrollSearchCheckSubjectWidget)
197 {
198     constexpr auto mockDom = R"({
199 "attributes": {
200 "bounds": "[0,0][1200,2000]",
201 "text": ""
202 },
203 "children": [
204 {
205 "attributes": {
206 "bounds": "[0,200][600,1000]",
207 "text": "USB",
208 "type": "List"
209 },
210 "children": []
211 }
212 ]
213 }
214 )";
215     controller_->SetDomFrame(mockDom);
216 
217     auto error = ApiCallErr(NO_ERROR);
218     auto scrollWidgetSelector = WidgetSelector();
219     auto matcher = WidgetAttrMatcher(ATTR_TEXT, "USB", EQ);
220     scrollWidgetSelector.AddMatcher(matcher);
221     vector<unique_ptr<Widget>> images;
222     driver_->FindWidgets(scrollWidgetSelector, images, error);
223 
224     ASSERT_EQ(1, images.size());
225 
226     error = ApiCallErr(NO_ERROR);
227     auto targetWidgetMatcher = WidgetAttrMatcher(ATTR_TEXT, "wyz", EQ);
228     auto targetWidgetSelector = WidgetSelector();
229     targetWidgetSelector.AddMatcher(targetWidgetMatcher);
230     opt_.scrollWidgetDeadZone_ = 0; // set deadzone to 0 for easy computation
231     auto wOp = WidgetOperator(*driver_, *images.at(0), opt_);
232     ASSERT_EQ(nullptr, wOp.ScrollFindWidget(targetWidgetSelector, error));
233     // check the scroll action events, should be acted on the subject node specified by WidgetMatcher
234     ASSERT_TRUE(!touch_event_records->Empty());
235     auto &firstEvent = touch_event_records->At(0, 0);
236     auto fin = touch_event_records->GetFingers() - 1;
237     auto ste = touch_event_records->GetSteps() - 1;
238     auto &lastEvent = touch_event_records->At(fin, ste);
239     // check scroll event pointer_x
240     int32_t subjectCx = (0 + 600) / 2;
241     ASSERT_NEAR(firstEvent.point_.px_, subjectCx, 5);
242     ASSERT_NEAR(lastEvent.point_.px_, subjectCx, 5);
243 
244     // check scroll event pointer_y
245     constexpr int32_t subjectWidgetHeight = 1000 - 200;
246     int32_t maxCy = 0;
247     int32_t minCy = 1E5;
248     for (uint32_t finger = 0; finger < touch_event_records->GetFingers(); finger++) {
249         for (uint32_t step = 0; step < touch_event_records->GetSteps(); step++) {
250             if (touch_event_records->At(finger, step).point_.py_ > maxCy) {
251             maxCy = touch_event_records->At(finger, step).point_.py_;
252             }
253             if (touch_event_records->At(finger, step).point_.py_ < minCy) {
254             minCy = touch_event_records->At(finger, step).point_.py_;
255             }
256         }
257     }
258 
259     int32_t scrollDistanceY = maxCy - minCy;
260     ASSERT_TRUE(abs(scrollDistanceY - subjectWidgetHeight) < 5);
261 }
262 
TEST_F(WidgetOperatorTest,scrollSearchCheckDirection)263 TEST_F(WidgetOperatorTest, scrollSearchCheckDirection)
264 {
265     constexpr auto mockDom = R"({
266 "attributes": {
267 "bounds": "[0,0][100,100]",
268 "text": ""
269 },
270 "children": [
271 {
272 "attributes": {
273 "bounds": "[0,0][50,50]",
274 "text": "USB",
275 "type": "List"
276 },
277 "children": []
278 }]})";
279     controller_->SetDomFrame(mockDom);
280 
281     auto error = ApiCallErr(NO_ERROR);
282     auto scrollWidgetSelector = WidgetSelector();
283     auto matcher = WidgetAttrMatcher(ATTR_TEXT, "USB", EQ);
284     scrollWidgetSelector.AddMatcher(matcher);
285     vector<unique_ptr<Widget>> widgets;
286     driver_->FindWidgets(scrollWidgetSelector, widgets, error);
287     ASSERT_EQ(1, widgets.size());
288 
289     error = ApiCallErr(NO_ERROR);
290     auto targetWidgetMatcher = WidgetAttrMatcher(ATTR_TEXT, "wyz", EQ);
291     auto targetWidgetSelector = WidgetSelector();
292     targetWidgetSelector.AddMatcher(targetWidgetMatcher);
293     opt_.scrollWidgetDeadZone_ = 0; // set deadzone to 0 for easy computation
294     auto wOp = WidgetOperator(*driver_, *widgets.at(0), opt_);
295     ASSERT_EQ(nullptr, wOp.ScrollFindWidget(targetWidgetSelector, error));
296     // check the scroll action events, should be acted on the specified node
297     ASSERT_TRUE(!touch_event_records->Empty());
298     // should scroll-search upward (cy_from<cy_to) then downward (cy_from>cy_to)
299     uint32_t maxCyEventIndex = 0;
300     uint32_t index = 0;
301     for (uint32_t event = 0; event < touch_event_records->GetSize() - 1; event++) {
302         if (touch_event_records->At(0, event).point_.py_ > touch_event_records->At(0, maxCyEventIndex).point_.py_) {
303             maxCyEventIndex = index;
304         }
305         index++;
306     }
307 
308     for (uint32_t idx = 0; idx < touch_event_records->GetSize() - 1; idx++) {
309         if (idx < maxCyEventIndex) {
310             ASSERT_LT(touch_event_records->At(0, idx).point_.py_, touch_event_records->At(0, idx + 1).point_.py_);
311         } else if (idx > maxCyEventIndex) {
312             ASSERT_GT(touch_event_records->At(0, idx).point_.py_, touch_event_records->At(0, idx + 1).point_.py_);
313         }
314     }
315 }
316 
317 /**
318  * The expected scroll-search count. The search starts upward till the target widget
319  * is found or reach the top (DOM snapshot becomes frozen); then search downward till
320  * the target widget is found or reach the bottom (DOM snapshot becomes frozen); */
TEST_F(WidgetOperatorTest,scrollSearchCheckCount_targetNotExist)321 TEST_F(WidgetOperatorTest, scrollSearchCheckCount_targetNotExist)
322 {
323     auto error = ApiCallErr(NO_ERROR);
324     // mocked widget text
325     const vector<string> domFrameSet[4] = {
326         {
327             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
328             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
329             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
330             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
331             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})"
332         },
333         {
334             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
335             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
336             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
337             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
338             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})"
339         },
340         {
341             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
342             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
343             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WLJ"},"children":[]})",
344             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
345             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})"
346         },
347         {
348             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
349             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WLJ"},"children":[]})",
350             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WLJ"},"children":[]})",
351             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
352             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})"
353         }
354     };
355 
356     controller_->SetDomFrames(domFrameSet[0]); // set frame, let the scroll-widget be found firstly
357     auto scrollWidgetSelector = WidgetSelector();
358     auto matcher = WidgetAttrMatcher(ATTR_TEXT, "USB", EQ);
359     scrollWidgetSelector.AddMatcher(matcher);
360     vector<unique_ptr<Widget>> widgets;
361     driver_->FindWidgets(scrollWidgetSelector, widgets, error);
362 
363     ASSERT_EQ(1, widgets.size());
364 
365     auto targetWidgetMatcher = WidgetAttrMatcher(ATTR_TEXT, "xyz", EQ); // widget that will never be found
366     auto targetWidgetSelector = WidgetSelector();
367     targetWidgetSelector.AddMatcher(targetWidgetMatcher);
368 
369     const uint32_t expectedSearchCount[] = {3, 4, 5, 5};
370     opt_.scrollWidgetDeadZone_ = 0; // set deadzone to 0 for easy computation
371     for (size_t index = 0; index < 4; index++) {
372         controller_->SetDomFrames(domFrameSet[index]);
373         // check search result
374         auto wOp = WidgetOperator(*driver_, *widgets.at(0), opt_);
375         ASSERT_EQ(nullptr, wOp.ScrollFindWidget(targetWidgetSelector, error));
376         // check scroll-search count
377         ASSERT_EQ(expectedSearchCount[index], controller_->GetConsumedDomFrameCount()) << index;
378     }
379 }
380 
TEST_F(WidgetOperatorTest,scrollSearchCheckCount_targetExist)381 TEST_F(WidgetOperatorTest, scrollSearchCheckCount_targetExist)
382 {
383     auto error = ApiCallErr(NO_ERROR);
384     const vector<string> domFrameSet[4] = {
385         {
386             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
387             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
388             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
389             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
390             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})"
391         },
392         {
393             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
394             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
395             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WLJ"},"children":[]})",
396             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"XYZ"},"children":[]})",
397             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})"
398         },
399         {
400             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
401             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
402             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
403             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
404             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})"
405         },
406         {
407             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"USB"},"children":[]})",
408             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"XYZ"},"children":[]})",
409             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WLJ"},"children":[]})",
410             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})",
411             R"({"attributes":{"bounds":"[0,0][100,100]","hashcode":"123","type":"List","text":"WYZ"},"children":[]})"
412         }
413     };
414 
415     controller_->SetDomFrames(domFrameSet[1]); // set frame, let the scroll-widget be found firstly
416     auto scrollWidgetSelector = WidgetSelector();
417     auto matcher = WidgetAttrMatcher(ATTR_TEXT, "USB", EQ);
418     scrollWidgetSelector.AddMatcher(matcher);
419     vector<unique_ptr<Widget>> widgets;
420     driver_->FindWidgets(scrollWidgetSelector, widgets, error);
421     ASSERT_EQ(1, widgets.size());
422 
423     auto targetWidgetMatcher = WidgetAttrMatcher(ATTR_TEXT, "WYZ", EQ);
424     auto targetWidgetSelector = WidgetSelector();
425     targetWidgetSelector.AddMatcher(targetWidgetMatcher);
426 
427     const uint32_t expectedSearchCount[] = {1, 2, 3, 4};
428     for (size_t index = 0; index < 4; index++) {
429         controller_->SetDomFrames(domFrameSet[index]);
430         // check search result
431         auto wOp = WidgetOperator(*driver_, *widgets.at(0), opt_);
432         ASSERT_NE(nullptr, wOp.ScrollFindWidget(targetWidgetSelector, error));
433         ASSERT_EQ("NONE", widgets.at(0)->GetHostTreeId()); // should return dettached widget
434         // check scroll-search count
435         ASSERT_EQ(expectedSearchCount[index], controller_->GetConsumedDomFrameCount()) << index;
436     }
437 }