• 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 
UpdateUi(bool updateUiTree,ApiCallErr & error)25     void UiDriver::UpdateUi(bool updateUiTree, ApiCallErr &error)
26     {
27         UiController::InstallFromProvider();
28         uiController_ = UiController::GetController();
29         if (uiController_ == nullptr) {
30             LOG_E("%{public}s", "No available UiController currently");
31             error = ApiCallErr(ERR_INTERNAL, "No available UiController currently");
32             return;
33         }
34         if (!updateUiTree) {
35             return;
36         }
37         windows_.clear();
38         widgetTree_ = make_unique<WidgetTree>("");
39         vector<pair<Window, nlohmann::json>> hierarchies;
40         uiController_->GetUiHierarchy(hierarchies);
41         if (hierarchies.empty()) {
42             LOG_E("%{public}s", "Get windows failed");
43             error = ApiCallErr(ERR_INTERNAL, "Get window nodes failed");
44             return;
45         }
46         vector<unique_ptr<WidgetTree>> trees;
47         for (auto &hierarchy : hierarchies) {
48             auto tree = make_unique<WidgetTree>("");
49             tree->ConstructFromDom(hierarchy.second, true);
50             auto &window = hierarchy.first;
51             windows_.push_back(move(window));
52             trees.push_back(move(tree));
53         }
54         WidgetTree::MergeTrees(trees, *widgetTree_);
55     }
56 
57     /** Get current UI tree.*/
GetWidgetTree() const58     const WidgetTree *UiDriver::GetWidgetTree() const
59     {
60         return widgetTree_ == nullptr? nullptr : widgetTree_.get();
61     }
62 
63     /** Get current UI controller.*/
GetUiController(ApiCallErr & error)64     const UiController *UiDriver::GetUiController(ApiCallErr &error)
65     {
66         this->UpdateUi(false, error);
67         return uiController_;
68     }
69 
DumpUiHierarchy(nlohmann::json & out,ApiCallErr & error)70     void UiDriver::DumpUiHierarchy(nlohmann::json &out, ApiCallErr &error)
71     {
72         UpdateUi(true, error);
73         if (error.code_ != NO_ERROR || widgetTree_ == nullptr) {
74             return;
75         }
76         widgetTree_->MarshalIntoDom(out);
77     }
78 
CloneFreeWidget(const Widget & from,const WidgetSelector & selector)79     static unique_ptr<Widget> CloneFreeWidget(const Widget &from, const WidgetSelector &selector)
80     {
81         auto clone = from.Clone("NONE", from.GetHierarchy());
82         // save the selection desc as dummy attribute
83         clone->SetAttr(DUMMY_ATTRNAME_SELECTION, selector.Describe());
84         return clone;
85     }
86 
RetrieveWidget(const Widget & widget,ApiCallErr & err,bool updateUi)87     const Widget *UiDriver::RetrieveWidget(const Widget &widget, ApiCallErr &err, bool updateUi)
88     {
89         if (updateUi) {
90             UpdateUi(true, err);
91             if (err.code_ != NO_ERROR) {
92                 return nullptr;
93             }
94         }
95         // retrieve widget by hashcode or by hierarchy
96         constexpr auto attrHashCode = ATTR_NAMES[UiAttr::HASHCODE];
97         constexpr auto attrHierarchy = ATTR_NAMES[UiAttr::HIERARCHY];
98         auto hashcodeMatcher = WidgetAttrMatcher(attrHashCode, widget.GetAttr(attrHashCode, "NA"), EQ);
99         auto hierarchyMatcher = WidgetAttrMatcher(attrHierarchy, widget.GetHierarchy(), EQ);
100         auto anyMatcher = Any(hashcodeMatcher, hierarchyMatcher);
101         vector<reference_wrapper<const Widget>> recv;
102         auto visitor = MatchedWidgetCollector(anyMatcher, recv);
103         widgetTree_->DfsTraverse(visitor);
104         stringstream msg;
105         msg << "Widget: " << widget.GetAttr(DUMMY_ATTRNAME_SELECTION, "");
106         msg << "dose not exist on current UI! Check if the UI has changed after you got the widget object";
107         if (recv.empty()) {
108             msg << "(NoCandidates)";
109             err = ApiCallErr(ERR_COMPONENT_LOST, msg.str());
110             LOG_W("%{public}s", err.message_.c_str());
111             return nullptr;
112         }
113         DCHECK(recv.size() == 1);
114         auto &retrieved = recv.at(0).get();
115         // confirm type
116         constexpr auto attrType = ATTR_NAMES[UiAttr::TYPE];
117         if (widget.GetAttr(attrType, "A").compare(retrieved.GetAttr(attrType, "B")) != 0) {
118             msg << " (CompareEqualsFailed)";
119             err = ApiCallErr(ERR_COMPONENT_LOST, msg.str());
120             LOG_W("%{public}s", err.message_.c_str());
121             return nullptr;
122         }
123         return &retrieved;
124     }
125 
TriggerKey(const KeyAction & key,const UiOpArgs & opt,ApiCallErr & error)126     void UiDriver::TriggerKey(const KeyAction &key, const UiOpArgs &opt, ApiCallErr &error)
127     {
128         UpdateUi(false, error);
129         if (error.code_ != NO_ERROR) {
130             return;
131         }
132         vector<KeyEvent> events;
133         key.ComputeEvents(events, opt);
134         if (events.empty()) {
135             return;
136         }
137         uiController_->InjectKeyEventSequence(events);
138         uiController_->WaitForUiSteady(opt.uiSteadyThresholdMs_, opt.waitUiSteadyMaxMs_);
139     }
140 
FindWidgets(const WidgetSelector & select,vector<unique_ptr<Widget>> & rev,ApiCallErr & err,bool updateUi)141     void UiDriver::FindWidgets(const WidgetSelector &select, vector<unique_ptr<Widget>> &rev,
142         ApiCallErr &err, bool updateUi)
143     {
144         if (updateUi) {
145             UpdateUi(true, err);
146             if (err.code_ != NO_ERROR) {
147                 return;
148             }
149         }
150         vector<reference_wrapper<const Widget>> widgets;
151         select.Select(*widgetTree_, widgets);
152         // covert widgets to images as return value
153         uint32_t index = 0;
154         for (auto &ref : widgets) {
155             auto image = CloneFreeWidget(ref.get(), select);
156             // at sometime, more than one widgets are found, add the node index to the description
157             auto selectionDesc = select.Describe() + "(index=" + to_string(index) + ")";
158             image->SetAttr(DUMMY_ATTRNAME_SELECTION, selectionDesc);
159             rev.emplace_back(move(image));
160             index++;
161         }
162     }
163 
WaitForWidget(const WidgetSelector & select,const UiOpArgs & opt,ApiCallErr & err)164     unique_ptr<Widget> UiDriver::WaitForWidget(const WidgetSelector &select, const UiOpArgs &opt, ApiCallErr &err)
165     {
166         const uint32_t sliceMs = 20;
167         const auto startMs = GetCurrentMillisecond();
168         vector<unique_ptr<Widget>> receiver;
169         do {
170             FindWidgets(select, receiver, err);
171             if (err.code_ != NO_ERROR) { // abort on error
172                 return nullptr;
173             }
174             if (!receiver.empty()) {
175                 return move(receiver.at(0));
176             }
177             DelayMs(sliceMs);
178         } while (GetCurrentMillisecond() - startMs < opt.waitWidgetMaxMs_);
179         return nullptr;
180     }
181 
DelayMs(uint32_t ms)182     void UiDriver::DelayMs(uint32_t ms)
183     {
184         if (ms > 0) {
185             this_thread::sleep_for(chrono::milliseconds(ms));
186         }
187     }
188 
PerformTouch(const TouchAction & touch,const UiOpArgs & opt,ApiCallErr & err)189     void UiDriver::PerformTouch(const TouchAction &touch, const UiOpArgs &opt, ApiCallErr &err)
190     {
191         UpdateUi(false, err);
192         if (err.code_ != NO_ERROR) {
193             return;
194         }
195         PointerMatrix events;
196         touch.Decompose(events, opt);
197         if (events.Empty()) {
198             return;
199         }
200         uiController_->InjectTouchEventSequence(events);
201         uiController_->WaitForUiSteady(opt.uiSteadyThresholdMs_, opt.waitUiSteadyMaxMs_);
202     }
203 
TakeScreenCap(string_view savePath,ApiCallErr & err)204     void UiDriver::TakeScreenCap(string_view savePath, ApiCallErr &err)
205     {
206         UpdateUi(false, err);
207         if (err.code_ != NO_ERROR) {
208             return;
209         }
210         stringstream errorRecv;
211         if (!uiController_->TakeScreenCap(savePath, errorRecv)) {
212             string errStr = errorRecv.str();
213             LOG_W("ScreenCap failed: %{public}s", errStr.c_str());
214             if (errStr.find("File opening failed") == 0) {
215                 err = ApiCallErr(ERR_INVALID_INPUT, "Invalid save path or permission denied");
216             } else {
217                 err = ApiCallErr(ERR_INTERNAL, errStr);
218             }
219             LOG_W("ScreenCap failed: %{public}s", errorRecv.str().c_str());
220         } else {
221             LOG_D("ScreenCap saved to %{public}s", savePath.data());
222         }
223     }
224 
FindWindow(function<bool (const Window &)> matcher,ApiCallErr & err)225     unique_ptr<Window> UiDriver::FindWindow(function<bool(const Window &)> matcher, ApiCallErr &err)
226     {
227         UpdateUi(true, err);
228         if (err.code_ != NO_ERROR) {
229             return nullptr;
230         }
231         for (const auto &window : windows_) {
232             if (matcher(window)) {
233                 auto clone = make_unique<Window>(0);
234                 *clone = window; // copy construct
235                 return clone;
236             }
237         }
238         return nullptr;
239     }
240 
RetrieveWindow(const Window & window,ApiCallErr & err)241     const Window *UiDriver::RetrieveWindow(const Window &window, ApiCallErr &err)
242     {
243         UpdateUi(true, err);
244         if (err.code_ != NO_ERROR) {
245             return nullptr;
246         }
247         for (const auto &win : windows_) {
248             if (win.id_ == window.id_) {
249                 return &win;
250             }
251         }
252         stringstream msg;
253         msg << "Window " << window.id_;
254         msg << "dose not exist on current UI! Check if the UI has changed after you got the window object";
255         err = ApiCallErr(ERR_COMPONENT_LOST, msg.str());
256         LOG_W("%{public}s", err.message_.c_str());
257         return nullptr;
258     }
259 } // namespace OHOS::uitest
260