Allows finding object by name in batch mode 57/164557/39
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Tue, 19 Dec 2017 15:46:04 +0000 (16:46 +0100)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Thu, 18 Jan 2018 10:22:44 +0000 (11:22 +0100)
Adds convertion from string to UIElement, which works by searching for
a atspi object with reqested name. Must be found only one or batch mode
will fail.

Change-Id: Ie09fc2370c59e24953cdfefbe6734f19d4864f04

src/batch/BatchRunner.cpp

index d1d5e51..dbba98b 100644 (file)
@@ -24,6 +24,7 @@
 #include "../NavigationInterface.hpp"
 
 #include <mutex>
+#include <future>
 #include <chrono>
 #include <thread>
 #include <fstream>
@@ -124,6 +125,138 @@ struct TestExecutor : ExecutorInterface {
                });
                return std::move(result);
        }
+       void findByName(const std::vector<AtspiAccessiblePtr> &elems, std::string requestedName, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback)
+       {
+               struct Exec {
+                       std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback;
+                       std::string requestedName;
+                       std::vector<AtspiAccessiblePtr> foundElements;
+                       Optional<DBus::Error> error;
+
+                       ~Exec()
+                       {
+                               if (error) callback(*error);
+                               else callback(std::move(foundElements));
+                       }
+               };
+               auto exec = std::make_shared<Exec>();
+               exec->callback = std::move(callback);
+               exec->requestedName = std::move(requestedName);
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+               for (auto &e : elems) {
+                       atspi->getName(e, [e, exec](DBus::ValueOrError<std::string> name) {
+                               if (!name) {
+                                       if (!exec->error)
+                                               exec->error = name.getError();
+                               } else if (std::get<0>(name) == exec->requestedName) {
+                                       exec->foundElements.push_back(e);
+                               }
+                       });
+               }
+       }
+       void getAllObjects(AtspiAccessiblePtr root, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback,
+                                          std::vector<int> roles = {}, std::vector<int> states = {})
+       {
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+               auto col = atspi->getCollectionInterface(root);
+               if (!col) {
+                       callback(DBus::Error{ "root '" + Atspi::getUniqueId(root) + "' doesn't have collection interface" });
+               } else {
+                       atspi->getMatchedElements(col, ATSPI_Collection_SORT_ORDER_CANONICAL, 0,
+                                                                         Atspi::Matcher().states(states.begin(), states.end(), ATSPI_Collection_MATCH_ALL)
+                                                                         .roles(roles.begin(), roles.end(), ATSPI_Collection_MATCH_ANY)
+                                                                         , false, std::move(callback));
+               }
+       }
+       void makeUIElement(AtspiAccessiblePtr src, std::function<void(DBus::ValueOrError<std::shared_ptr<UIElement>>)> callback)
+       {
+               auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+               atspi->getComponentInterface(src, [src, callback = std::move(callback)](DBus::ValueOrError<AtspiComponentPtr> comp) {
+                       if (!comp) {
+                               callback(comp.getError());
+                               return;
+                       }
+                       auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
+                       atspi->getScreenPosition(std::get<0>(comp), [src, callback = std::move(callback)](DBus::ValueOrError<Rectangle> pos) {
+                               if (!pos) {
+                                       callback(pos.getError());
+                                       return;
+                               }
+                               callback(std::make_shared<UIElement>(src, std::get<0>(pos).getCenterPoint(), UIElement::ApplicationCategory::OTHER));
+                       });
+               });
+       }
+       struct PromiseObject {
+               std::promise<std::shared_ptr<UIElement>> promise;
+               bool set = false;
+
+               void setValue(std::shared_ptr<UIElement> v)
+               {
+                       ASSERT(!set);
+                       promise.set_value(std::move(v));
+                       set = true;
+               }
+               template <typename T> void setException(T &&e)
+               {
+                       ASSERT(!set);
+                       promise.set_exception(std::make_exception_ptr(std::move(e)));
+                       set = true;
+               }
+
+               ~PromiseObject()
+               {
+                       if (!set) promise.set_value(nullptr);
+               }
+       };
+       std::shared_ptr<UIElement> convert(const std::string &requestedName) override
+       {
+               auto promiseObject = std::make_shared<PromiseObject>();
+               auto future = promiseObject->promise.get_future();
+
+               executeOnMainThread([promiseObject = std::move(promiseObject), this, &requestedName]() {
+                       auto uiRoot = getVisibleRoot();
+                       if (!uiRoot) {
+                               promiseObject->setException(EvaluationFailure{} << "no visible root (context changed didn't happen)");
+                               return;
+                       }
+                       auto root = uiRoot->getObject();
+                       ASSERT(root);
+                       getAllObjects(root, [promiseObject, &requestedName, this](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elems) {
+                               if (!promiseObject->set) {
+                                       if (!elems) {
+                                               promiseObject->setException(EvaluationFailure{} << elems.getError().message);
+                                               return;
+                                       }
+                                       findByName(std::get<0>(elems), requestedName,
+                                       [&requestedName, promiseObject, this](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
+                                               if (!elements) {
+                                                       promiseObject->setException(EvaluationFailure{} << elements.getError().message);
+                                               } else {
+                                                       auto &e = std::get<0>(elements);
+                                                       if (e.empty()) {
+                                                               promiseObject->setException(EvaluationFailure{} << "no at-spi object found with name '" << requestedName << "'");
+                                                       } else if (e.size() > 1) {
+                                                               std::ostringstream names;
+                                                               for (auto &elem : e) names << " " << Atspi::getUniqueId(elem);
+                                                               promiseObject->setException(EvaluationFailure{} <<
+                                                                                                                       "found more, than one at-spi object with name '" << requestedName << "' (see" << names.str() << ")");
+                                                       } else {
+                                                               makeUIElement(e[0], [promiseObject](DBus::ValueOrError<std::shared_ptr<UIElement>> elem) {
+                                                                       if (!elem)
+                                                                               promiseObject->setException(EvaluationFailure{} << elem.getError().message);
+                                                                       else
+                                                                               promiseObject->setValue(std::move(std::get<0>(elem)));
+                                                               });
+                                                       }
+                                               }
+                                       });
+                               }
+                       });
+               });
+               future.wait();
+               return std::move(future.get());
+       }
+
        void insertStateConstants()
        {
                // from at-spi2-core v. 2.16.0