/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "widget_selector.h" namespace OHOS::uitest { using namespace std; using namespace nlohmann; static constexpr auto NEST_USAGE_ERROR = "Nesting By usage like 'BY.before(BY.after(...))' is not supported"; WidgetSelector::WidgetSelector(bool addVisibleMatcher) { if (!addVisibleMatcher) { return; } auto visibleMatcher = WidgetAttrMatcher(ATTR_NAMES[UiAttr::VISIBLE], "true", EQ); selfMatchers_.emplace_back(visibleMatcher); } void WidgetSelector::AddMatcher(const WidgetAttrMatcher &matcher) { selfMatchers_.emplace_back(matcher); } void WidgetSelector::AddFrontLocator(const WidgetSelector &selector, ApiCallErr &error) { if (!selector.rearLocators_.empty() || !selector.frontLocators_.empty()) { error = ApiCallErr(ERR_INVALID_INPUT, NEST_USAGE_ERROR); return; } frontLocators_.emplace_back(selector); } void WidgetSelector::AddRearLocator(const WidgetSelector &selector, ApiCallErr &error) { if (!selector.rearLocators_.empty() || !selector.frontLocators_.empty()) { error = ApiCallErr(ERR_INVALID_INPUT, NEST_USAGE_ERROR); return; } rearLocators_.emplace_back(selector); } void WidgetSelector::AddParentLocator(const WidgetSelector &selector, ApiCallErr &error) { if (!selector.rearLocators_.empty() || !selector.frontLocators_.empty()) { error = ApiCallErr(ERR_INVALID_INPUT, NEST_USAGE_ERROR); return; } parentLocators_.emplace_back(selector); } void WidgetSelector::AddAppLocator(string app) { appLocator_ = app; } string WidgetSelector::GetAppLocator() const { return appLocator_; } static bool CheckHasLocator(const WidgetTree &tree, const Widget &widget, const WidgetMatcher &matcher, size_t idx) { vector> locators; locators.clear(); MatchedWidgetCollector collector(matcher, locators); switch (idx) { case INDEX_ZERO: tree.DfsTraverseFronts(collector, widget); break; case INDEX_ONE: tree.DfsTraverseRears(collector, widget); break; case INDEX_TWO: tree.DfsTraverseParents(collector, widget); break; default: break; } return !locators.empty(); } void WidgetSelector::Select(const WidgetTree &tree, vector> &results) const { auto allSelfMatcher = All(selfMatchers_); MatchedWidgetCollector selfCollector(allSelfMatcher, results); tree.DfsTraverse(selfCollector); if (results.empty()) { LOG_W("Self node not found matching:%{public}s", allSelfMatcher.Describe().c_str()); return; } vector discardWidgetOffsets; // check locators at each direction and filter out unsatisfied widgets std::vector> allLocators = {frontLocators_, rearLocators_, parentLocators_}; for (size_t idx = 0; idx < INDEX_THREE; idx++) { discardWidgetOffsets.clear(); uint32_t offset = 0; for (auto &result:results) { const auto &locators = allLocators[idx]; for (auto &locator:locators) { auto locatorMatcher = All(locator.selfMatchers_); if (!CheckHasLocator(tree, result.get(), locatorMatcher, idx)) { // this means not all the required front locator are found, exclude this candidate discardWidgetOffsets.emplace_back(offset); break; } } offset++; } // remove unsatisfied candidates, remove from last to first reverse(discardWidgetOffsets.begin(), discardWidgetOffsets.end()); for (auto &off:discardWidgetOffsets) { results.erase(results.begin() + off); } if (results.empty()) { break; } } } string WidgetSelector::Describe() const { stringstream ss; auto allSelfMatcher = All(selfMatchers_); ss << "{"; ss << "selfMatcher=[" << allSelfMatcher.Describe() << "]"; if (!frontLocators_.empty()) { ss << "; frontMatcher="; for (auto &locator:frontLocators_) { ss << "[" << locator.Describe() << "]"; } } if (!rearLocators_.empty()) { ss << "; rearMatcher="; for (auto &locator:rearLocators_) { ss << "[" << locator.Describe() << "]"; } } ss << "}"; return ss.str(); } }