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 "widget_selector.h" 17 18 namespace OHOS::uitest { 19 using namespace std; 20 using namespace nlohmann; 21 22 static constexpr auto NEST_USAGE_ERROR = "Nesting By usage like 'BY.before(BY.after(...))' is not supported"; 23 WidgetSelector(bool addVisibleMatcher)24 WidgetSelector::WidgetSelector(bool addVisibleMatcher) 25 { 26 if (!addVisibleMatcher) { 27 return; 28 } 29 auto visibleMatcher = WidgetAttrMatcher(ATTR_NAMES[UiAttr::VISIBLE], "true", EQ); 30 selfMatchers_.emplace_back(visibleMatcher); 31 } 32 AddMatcher(const WidgetAttrMatcher & matcher)33 void WidgetSelector::AddMatcher(const WidgetAttrMatcher &matcher) 34 { 35 selfMatchers_.emplace_back(matcher); 36 } 37 AddFrontLocator(const WidgetSelector & selector,ApiCallErr & error)38 void WidgetSelector::AddFrontLocator(const WidgetSelector &selector, ApiCallErr &error) 39 { 40 if (!selector.rearLocators_.empty() || !selector.frontLocators_.empty()) { 41 error = ApiCallErr(ERR_INVALID_INPUT, NEST_USAGE_ERROR); 42 return; 43 } 44 frontLocators_.emplace_back(selector); 45 } 46 AddRearLocator(const WidgetSelector & selector,ApiCallErr & error)47 void WidgetSelector::AddRearLocator(const WidgetSelector &selector, ApiCallErr &error) 48 { 49 if (!selector.rearLocators_.empty() || !selector.frontLocators_.empty()) { 50 error = ApiCallErr(ERR_INVALID_INPUT, NEST_USAGE_ERROR); 51 return; 52 } 53 rearLocators_.emplace_back(selector); 54 } 55 AddParentLocator(const WidgetSelector & selector,ApiCallErr & error)56 void WidgetSelector::AddParentLocator(const WidgetSelector &selector, ApiCallErr &error) 57 { 58 if (!selector.rearLocators_.empty() || !selector.frontLocators_.empty()) { 59 error = ApiCallErr(ERR_INVALID_INPUT, NEST_USAGE_ERROR); 60 return; 61 } 62 parentLocators_.emplace_back(selector); 63 } 64 AddAppLocator(string app)65 void WidgetSelector::AddAppLocator(string app) 66 { 67 appLocator_ = app; 68 } 69 GetAppLocator() const70 string WidgetSelector::GetAppLocator() const 71 { 72 return appLocator_; 73 } 74 CheckHasLocator(const WidgetTree & tree,const Widget & widget,const WidgetMatcher & matcher,size_t idx)75 static bool CheckHasLocator(const WidgetTree &tree, const Widget &widget, const WidgetMatcher &matcher, size_t idx) 76 { 77 vector<reference_wrapper<const Widget>> locators; 78 locators.clear(); 79 MatchedWidgetCollector collector(matcher, locators); 80 switch (idx) { 81 case INDEX_ZERO: 82 tree.DfsTraverseFronts(collector, widget); 83 break; 84 case INDEX_ONE: 85 tree.DfsTraverseRears(collector, widget); 86 break; 87 case INDEX_TWO: 88 tree.DfsTraverseParents(collector, widget); 89 break; 90 default: 91 break; 92 } 93 return !locators.empty(); 94 } 95 Select(const WidgetTree & tree,vector<std::reference_wrapper<const Widget>> & results) const96 void WidgetSelector::Select(const WidgetTree &tree, vector<std::reference_wrapper<const Widget>> &results) const 97 { 98 auto allSelfMatcher = All(selfMatchers_); 99 MatchedWidgetCollector selfCollector(allSelfMatcher, results); 100 tree.DfsTraverse(selfCollector); 101 if (results.empty()) { 102 LOG_W("Self node not found matching:%{public}s", allSelfMatcher.Describe().c_str()); 103 return; 104 } 105 106 vector<uint32_t> discardWidgetOffsets; 107 // check locators at each direction and filter out unsatisfied widgets 108 std::vector<std::vector<WidgetSelector>> allLocators = {frontLocators_, rearLocators_, parentLocators_}; 109 for (size_t idx = 0; idx < INDEX_THREE; idx++) { 110 discardWidgetOffsets.clear(); 111 uint32_t offset = 0; 112 for (auto &result:results) { 113 const auto &locators = allLocators[idx]; 114 for (auto &locator:locators) { 115 auto locatorMatcher = All(locator.selfMatchers_); 116 if (!CheckHasLocator(tree, result.get(), locatorMatcher, idx)) { 117 // this means not all the required front locator are found, exclude this candidate 118 discardWidgetOffsets.emplace_back(offset); 119 break; 120 } 121 } 122 offset++; 123 } 124 // remove unsatisfied candidates, remove from last to first 125 reverse(discardWidgetOffsets.begin(), discardWidgetOffsets.end()); 126 for (auto &off:discardWidgetOffsets) { 127 results.erase(results.begin() + off); 128 } 129 if (results.empty()) { 130 break; 131 } 132 } 133 } 134 Describe() const135 string WidgetSelector::Describe() const 136 { 137 stringstream ss; 138 auto allSelfMatcher = All(selfMatchers_); 139 ss << "{"; 140 ss << "selfMatcher=[" << allSelfMatcher.Describe() << "]"; 141 if (!frontLocators_.empty()) { 142 ss << "; frontMatcher="; 143 for (auto &locator:frontLocators_) { 144 ss << "[" << locator.Describe() << "]"; 145 } 146 } 147 if (!rearLocators_.empty()) { 148 ss << "; rearMatcher="; 149 for (auto &locator:rearLocators_) { 150 ss << "[" << locator.Describe() << "]"; 151 } 152 } 153 ss << "}"; 154 return ss.str(); 155 } 156 }