• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <future>
17 #include "ui_driver.h"
18 
19 namespace OHOS::uitest {
20     using namespace std;
21     using namespace nlohmann;
22 
23     static constexpr string_view DUMMY_ATTRNAME_SELECTION = "selectionDesc";
24 
25     std::unique_ptr<UiController> UiDriver::uiController_;
26 
RegisterController(std::unique_ptr<UiController> controller)27     void UiDriver::RegisterController(std::unique_ptr<UiController> controller)
28     {
29         uiController_ = move(controller);
30     }
31 
RegisterUiEventListener(std::shared_ptr<UiEventListener> listener)32     void UiDriver::RegisterUiEventListener(std::shared_ptr<UiEventListener> listener)
33     {
34         uiController_->RegisterUiEventListener(listener);
35     }
36 
CheckStatus(bool isConnected,ApiCallErr & error)37     bool UiDriver::CheckStatus(bool isConnected, ApiCallErr &error)
38     {
39         DCHECK(uiController_);
40         if (isConnected && !uiController_->IsWorkable()) {
41             LOG_I("Not connect to AAMS, try to reconnect");
42             if (!uiController_->Initialize()) {
43                 error = ApiCallErr(ERR_INITIALIZE_FAILED, "Can not connect to AAMS");
44                 return false;
45             }
46         }
47         return true;
48     }
49 
UpdateUi(bool updateUiTree,ApiCallErr & error,bool getWidgetNodes,string targetWin)50     void UiDriver::UpdateUi(bool updateUiTree, ApiCallErr &error, bool getWidgetNodes, string targetWin)
51     {
52         if (!updateUiTree) {
53             return;
54         }
55         if (!CheckStatus(true, error)) {
56             return;
57         }
58         windows_.clear();
59         widgetTree_ = make_unique<WidgetTree>("");
60         vector<pair<Window, nlohmann::json>> hierarchies;
61         uiController_->GetUiHierarchy(hierarchies, getWidgetNodes, targetWin);
62         if (hierarchies.empty()) {
63             LOG_E("%{public}s", "Get windows failed");
64             error = ApiCallErr(ERR_INTERNAL, "Get window nodes failed");
65             return;
66         }
67         vector<unique_ptr<WidgetTree>> trees;
68         for (auto &hierarchy : hierarchies) {
69             auto tree = make_unique<WidgetTree>("");
70             tree->ConstructFromDom(hierarchy.second, true);
71             trees.push_back(move(tree));
72         }
73         vector<int32_t> mergedOrdres;
74         WidgetTree::MergeTrees(trees, *widgetTree_, mergedOrdres);
75         auto virtualRoot = widgetTree_->GetRootWidget();
76         for (size_t index = 0; index < mergedOrdres.size(); index++) {
77             auto root = widgetTree_->GetChildWidget(*virtualRoot, index);
78             DCHECK(root != nullptr);
79             DCHECK(hierarchies.size() > mergedOrdres[index]);
80             auto &window = hierarchies[mergedOrdres[index]].first;
81             window.visibleBounds_ = root->GetBounds();
82             windows_.push_back(move(window));
83         }
84     }
85 
DumpUiHierarchy(nlohmann::json & out,bool listWindows,ApiCallErr & error)86     void UiDriver::DumpUiHierarchy(nlohmann::json &out, bool listWindows, ApiCallErr &error)
87     {
88         if (listWindows) {
89             if (!CheckStatus(true, error)) {
90                 return;
91             }
92             vector<pair<Window, nlohmann::json>> datas;
93             uiController_->GetUiHierarchy(datas, true);
94             out = nlohmann::json::array();
95             for (auto& data : datas) {
96                 out.push_back(data.second);
97             }
98         } else {
99             UpdateUi(true, error, true);
100             if (error.code_ != NO_ERROR || widgetTree_ == nullptr) {
101                 return;
102             }
103             widgetTree_->MarshalIntoDom(out);
104         }
105     }
106 
CloneFreeWidget(const Widget & from,const WidgetSelector & selector)107     static unique_ptr<Widget> CloneFreeWidget(const Widget &from, const WidgetSelector &selector)
108     {
109         auto clone = from.Clone("NONE", from.GetHierarchy());
110         // save the selection desc as dummy attribute
111         clone->SetAttr(DUMMY_ATTRNAME_SELECTION, selector.Describe());
112         return clone;
113     }
114 
GetHostApp(const Widget & widget)115     string UiDriver::GetHostApp(const Widget &widget)
116     {
117         auto winId = widget.GetAttr(ATTR_NAMES[UiAttr::HOST_WINDOW_ID], "0");
118         auto id = atoi(winId.c_str());
119         for (auto window: windows_) {
120             if (id == window.id_) {
121                 // If not a actived window, get all.
122                 if (window.actived_ == false) {
123                     return "";
124                 }
125                 return window.bundleName_;
126             }
127         }
128         return "";
129     }
130 
RetrieveWidget(const Widget & widget,ApiCallErr & err,bool updateUi)131     const Widget *UiDriver::RetrieveWidget(const Widget &widget, ApiCallErr &err, bool updateUi)
132     {
133         if (updateUi) {
134             auto hostApp = this->GetHostApp(widget);
135             UpdateUi(true, err, true, hostApp);
136             if (err.code_ != NO_ERROR) {
137                 return nullptr;
138             }
139         }
140         // retrieve widget by hashcode or by hierarchy
141         constexpr auto attrHashCode = ATTR_NAMES[UiAttr::HASHCODE];
142         constexpr auto attrHierarchy = ATTR_NAMES[UiAttr::HIERARCHY];
143         auto hashcodeMatcher = WidgetAttrMatcher(attrHashCode, widget.GetAttr(attrHashCode, "NA"), EQ);
144         auto hierarchyMatcher = WidgetAttrMatcher(attrHierarchy, widget.GetHierarchy(), EQ);
145         auto anyMatcher = Any(hashcodeMatcher, hierarchyMatcher);
146         vector<reference_wrapper<const Widget>> recv;
147         auto visitor = MatchedWidgetCollector(anyMatcher, recv);
148         widgetTree_->DfsTraverse(visitor);
149         stringstream msg;
150         msg << "Widget: " << widget.GetAttr(DUMMY_ATTRNAME_SELECTION, "");
151         msg << "dose not exist on current UI! Check if the UI has changed after you got the widget object";
152         if (recv.empty()) {
153             msg << "(NoCandidates)";
154             err = ApiCallErr(ERR_COMPONENT_LOST, msg.str());
155             LOG_W("%{public}s", err.message_.c_str());
156             return nullptr;
157         }
158         DCHECK(recv.size() == 1);
159         auto &retrieved = recv.at(0).get();
160         // confirm type
161         constexpr auto attrType = ATTR_NAMES[UiAttr::TYPE];
162         if (widget.GetAttr(attrType, "A").compare(retrieved.GetAttr(attrType, "B")) != 0) {
163             msg << " (CompareEqualsFailed)";
164             err = ApiCallErr(ERR_COMPONENT_LOST, msg.str());
165             LOG_W("%{public}s", err.message_.c_str());
166             return nullptr;
167         }
168         return &retrieved;
169     }
170 
TriggerKey(const KeyAction & key,const UiOpArgs & opt,ApiCallErr & error)171     void UiDriver::TriggerKey(const KeyAction &key, const UiOpArgs &opt, ApiCallErr &error)
172     {
173         if (!CheckStatus(false, error)) {
174             return;
175         }
176         vector<KeyEvent> events;
177         key.ComputeEvents(events, opt);
178         if (events.empty()) {
179             return;
180         }
181         uiController_->InjectKeyEventSequence(events);
182         uiController_->WaitForUiSteady(opt.uiSteadyThresholdMs_, opt.waitUiSteadyMaxMs_);
183     }
184 
FindWidgets(const WidgetSelector & select,vector<unique_ptr<Widget>> & rev,ApiCallErr & err,bool updateUi)185     void UiDriver::FindWidgets(const WidgetSelector &select, vector<unique_ptr<Widget>> &rev,
186         ApiCallErr &err, bool updateUi)
187     {
188         if (updateUi) {
189             auto hostApp = select.GetAppLocator();
190             UpdateUi(true, err, true, hostApp);
191             if (err.code_ != NO_ERROR) {
192                 return;
193             }
194         }
195         vector<reference_wrapper<const Widget>> widgets;
196         select.Select(*widgetTree_, widgets);
197         // covert widgets to images as return value
198         uint32_t index = 0;
199         for (auto &ref : widgets) {
200             auto image = CloneFreeWidget(ref.get(), select);
201             // at sometime, more than one widgets are found, add the node index to the description
202             auto selectionDesc = select.Describe() + "(index=" + to_string(index) + ")";
203             image->SetAttr(DUMMY_ATTRNAME_SELECTION, selectionDesc);
204             rev.emplace_back(move(image));
205             index++;
206         }
207     }
208 
WaitForWidget(const WidgetSelector & select,const UiOpArgs & opt,ApiCallErr & err)209     unique_ptr<Widget> UiDriver::WaitForWidget(const WidgetSelector &select, const UiOpArgs &opt, ApiCallErr &err)
210     {
211         const uint32_t sliceMs = 20;
212         const auto startMs = GetCurrentMillisecond();
213         vector<unique_ptr<Widget>> receiver;
214         do {
215             FindWidgets(select, receiver, err);
216             if (err.code_ != NO_ERROR) { // abort on error
217                 return nullptr;
218             }
219             if (!receiver.empty()) {
220                 return move(receiver.at(0));
221             }
222             DelayMs(sliceMs);
223         } while (GetCurrentMillisecond() - startMs < opt.waitWidgetMaxMs_);
224         return nullptr;
225     }
226 
DelayMs(uint32_t ms)227     void UiDriver::DelayMs(uint32_t ms)
228     {
229         if (ms > 0) {
230             this_thread::sleep_for(chrono::milliseconds(ms));
231         }
232     }
233 
PerformTouch(const TouchAction & touch,const UiOpArgs & opt,ApiCallErr & err)234     void UiDriver::PerformTouch(const TouchAction &touch, const UiOpArgs &opt, ApiCallErr &err)
235     {
236         if (!CheckStatus(false, err)) {
237             return;
238         }
239         PointerMatrix events;
240         touch.Decompose(events, opt);
241         if (events.Empty()) {
242             return;
243         }
244         uiController_->InjectTouchEventSequence(events);
245         uiController_->WaitForUiSteady(opt.uiSteadyThresholdMs_, opt.waitUiSteadyMaxMs_);
246     }
247 
TakeScreenCap(int32_t fd,ApiCallErr & err,Rect rect)248     void UiDriver::TakeScreenCap(int32_t fd, ApiCallErr &err, Rect rect)
249     {
250         if (!CheckStatus(false, err)) {
251             return;
252         }
253         stringstream errorRecv;
254         if (!uiController_->TakeScreenCap(fd, errorRecv, rect)) {
255             string errStr = errorRecv.str();
256             LOG_W("ScreenCap failed: %{public}s", errStr.c_str());
257             if (errStr.find("File opening failed") == 0) {
258                 err = ApiCallErr(ERR_INVALID_INPUT, "Invalid save path or permission denied");
259             } else {
260                 err = ApiCallErr(ERR_INTERNAL, errStr);
261             }
262             LOG_W("ScreenCap failed: %{public}s", errorRecv.str().c_str());
263         } else {
264             LOG_D("ScreenCap successed");
265         }
266     }
267 
FindWindow(function<bool (const Window &)> matcher,ApiCallErr & err)268     unique_ptr<Window> UiDriver::FindWindow(function<bool(const Window &)> matcher, ApiCallErr &err)
269     {
270         UpdateUi(true, err, false);
271         if (err.code_ != NO_ERROR) {
272             return nullptr;
273         }
274         for (const auto &window : windows_) {
275             if (matcher(window)) {
276                 auto clone = make_unique<Window>(0);
277                 *clone = window; // copy construct
278                 return clone;
279             }
280         }
281         return nullptr;
282     }
283 
RetrieveWindow(const Window & window,ApiCallErr & err)284     const Window *UiDriver::RetrieveWindow(const Window &window, ApiCallErr &err)
285     {
286         UpdateUi(true, err, false);
287         if (err.code_ != NO_ERROR) {
288             return nullptr;
289         }
290         for (const auto &win : windows_) {
291             if (win.id_ == window.id_) {
292                 return &win;
293             }
294         }
295         stringstream msg;
296         msg << "Window " << window.id_;
297         msg << "dose not exist on current UI! Check if the UI has changed after you got the window object";
298         err = ApiCallErr(ERR_COMPONENT_LOST, msg.str());
299         LOG_W("%{public}s", err.message_.c_str());
300         return nullptr;
301     }
302 
SetDisplayRotation(DisplayRotation rotation,ApiCallErr & error)303     void UiDriver::SetDisplayRotation(DisplayRotation rotation, ApiCallErr &error)
304     {
305         if (!CheckStatus(false, error)) {
306             return;
307         }
308         uiController_->SetDisplayRotation(rotation);
309     }
310 
GetDisplayRotation(ApiCallErr & error)311     DisplayRotation UiDriver::GetDisplayRotation(ApiCallErr &error)
312     {
313         if (!CheckStatus(false, error)) {
314             return ROTATION_0;
315         }
316         return uiController_->GetDisplayRotation();
317     }
318 
SetDisplayRotationEnabled(bool enabled,ApiCallErr & error)319     void UiDriver::SetDisplayRotationEnabled(bool enabled, ApiCallErr &error)
320     {
321         if (!CheckStatus(false, error)) {
322             return;
323         }
324         uiController_->SetDisplayRotationEnabled(enabled);
325     }
326 
WaitForUiSteady(uint32_t idleThresholdMs,uint32_t timeoutSec,ApiCallErr & error)327     bool UiDriver::WaitForUiSteady(uint32_t idleThresholdMs, uint32_t timeoutSec, ApiCallErr &error)
328     {
329         if (!CheckStatus(false, error)) {
330             return false;
331         }
332         return uiController_->WaitForUiSteady(idleThresholdMs, timeoutSec);
333     }
334 
WakeUpDisplay(ApiCallErr & error)335     void UiDriver::WakeUpDisplay(ApiCallErr &error)
336     {
337         if (!CheckStatus(false, error)) {
338             return;
339         }
340         if (uiController_->IsScreenOn()) {
341             return;
342         } else {
343             LOG_I("screen is off, turn it on");
344             UiOpArgs uiOpArgs;
345             this->TriggerKey(Power(), uiOpArgs, error);
346         }
347     }
348 
GetDisplaySize(ApiCallErr & error)349     Point UiDriver::GetDisplaySize(ApiCallErr &error)
350     {
351         if (!CheckStatus(false, error)) {
352             return Point(0, 0);
353         }
354         return uiController_->GetDisplaySize();
355     }
356 
GetDisplayDensity(ApiCallErr & error)357     Point UiDriver::GetDisplayDensity(ApiCallErr &error)
358     {
359         if (!CheckStatus(false, error)) {
360             return Point(0, 0);
361         }
362         return uiController_->GetDisplayDensity();
363     }
364 
GetCharKeyCode(char ch,int32_t & code,int32_t & ctrlCode,ApiCallErr & error)365     bool UiDriver::GetCharKeyCode(char ch, int32_t &code, int32_t &ctrlCode, ApiCallErr &error)
366     {
367         if (!CheckStatus(false, error)) {
368             return false;
369         }
370         return uiController_->GetCharKeyCode(ch, code, ctrlCode);
371     }
372 
DfsTraverseTree(WidgetVisitor & visitor,const Widget * widget)373     void UiDriver::DfsTraverseTree(WidgetVisitor &visitor, const Widget *widget)
374     {
375         if (widgetTree_ == nullptr) {
376             return;
377         }
378         if (widget == nullptr) {
379             widgetTree_->DfsTraverse(visitor);
380         } else {
381             widgetTree_->DfsTraverseDescendants(visitor, *widget);
382         }
383     }
384 
InjectMouseAction(MouseOpArgs mouseOpArgs,ApiCallErr & error)385     void UiDriver::InjectMouseAction(MouseOpArgs mouseOpArgs, ApiCallErr &error)
386     {
387         if (error.code_ != NO_ERROR) {
388             return;
389         }
390         switch (mouseOpArgs.action_) {
391             case MouseOp::M_MOVETO:
392                 uiController_->InjectMouseMove(mouseOpArgs);
393                 break;
394             case MouseOp::M_CLICK:
395                 uiController_->InjectMouseClick(mouseOpArgs);
396                 break;
397             case MouseOp::M_SCROLL:
398                 uiController_->InjectMouseScroll(mouseOpArgs);
399                 break;
400             default:
401                 return;
402         }
403     }
404 
GetLayoutJson(nlohmann::json & dom)405     void UiDriver::GetLayoutJson(nlohmann::json &dom)
406     {
407         widgetTree_->MarshalIntoDom(dom);
408     }
409 } // namespace OHOS::uitest
410