2 * Copyright 2017 Samsung Electronics Co., Ltd
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "BatchRunner.hpp"
18 #include "../UniversalSwitch.hpp"
19 #include "../Window.hpp"
20 #include "../UniversalSwitchLog.hpp"
23 #include "EvaluationValue.hpp"
24 #include "../ActivityFactory.hpp"
25 #include "../Observer.hpp"
26 #include "../UIElement.hpp"
27 #include "../UIActivity.hpp"
28 #include "Monitor.hpp"
29 #include "../DoneCallback.hpp"
30 #include "../DBus.hpp"
32 #include "ReturnValue.hpp"
33 #include "EvaluationValueBase.hpp"
40 #include <type_traits>
41 #include <app_manager.h>
42 #include <app_manager_extension.h>
46 template <typename MONITORED_TYPE, typename ... ARGS>
47 static std::function<void(DBus::ValueOrError<ARGS...>)> wrapImpl(
48 std::function<void(DBus::ValueOrError<ARGS...>)> func,
49 Monitor<BatchValueOrError<MONITORED_TYPE>> monitor)
51 return [monitor = std::move(monitor), func = std::move(func)](DBus::ValueOrError<ARGS...> val) -> void {
55 auto h = monitor.lock();
56 h->setError(EvaluationFailure{} << val.getError().message);
60 } catch (EvaluationFailure &ev)
62 auto h = monitor.lock();
63 h->setError(std::move(ev));
64 } catch (std::exception &ev)
66 auto h = monitor.lock();
67 h->setError(EvaluationFailure{} << "unhandled exception (" << ev.what() << ")");
70 auto h = monitor.lock();
71 h->setError(EvaluationFailure{} << "unhandled unknown exception");
76 // (template magic) proxy function to discover arguments of the callable,
77 // called in wrap(...) function below. Typical usage is:
78 // wrap(monitor, [](A a) { ... })
79 // at implementation of function wrap lambda is turned into type F, for which
80 // we want to know return type and it's arguments (we already know, it's callable,
81 // as it's required). So (in function wrap) we take F's operator () - it will become
82 // pointer to member. Now we call WrapHelper<F's operator ()>::call.
83 // This class has two specializations, both expect pointer to member (our operator () ).
84 // Template deduction allows compiler to discover both return type (which we know must be void)
85 // and it's arguments (we know in this case, that it must be single DBus::ValueOrError<ARGS...>).
86 // But now the compiler can tells us, which ARGS... exactly are, which will allow wrapImpl to create
87 // functor with the required arguments and return type.
89 template <typename T> struct WrapHelper;
90 template <typename C, typename ... ARGS>
91 struct WrapHelper<void(C::*)(DBus::ValueOrError<ARGS...>) const> {
92 template <typename MONITORED_TYPE>
93 static std::function<void(DBus::ValueOrError<ARGS...>)> call(
94 std::function<void(DBus::ValueOrError<ARGS...>)> func,
95 Monitor<BatchValueOrError<MONITORED_TYPE>> monitor)
97 return wrapImpl(std::move(func), std::move(monitor));
100 template <typename C, typename ... ARGS>
101 struct WrapHelper<void(C::*)(DBus::ValueOrError<ARGS...>)> {
102 template <typename MONITORED_TYPE>
103 static std::function<void(DBus::ValueOrError<ARGS...>)> call(
104 std::function<void(DBus::ValueOrError<ARGS...>)> func,
105 Monitor<BatchValueOrError<MONITORED_TYPE>> monitor)
107 return wrapImpl(std::move(func), std::move(monitor));
112 template <typename MONITORED_TYPE, typename F>
113 auto wrap(Monitor<BatchValueOrError<MONITORED_TYPE>> monitor, F &&func)
115 return WrapDetails::WrapHelper<decltype(&std::remove_reference<F>::type::operator())>::
116 call(std::forward<F>(func), std::move(monitor));
119 class ExecutorOnMainThread
122 template < typename F, typename = typename
123 std::enable_if < !std::is_same<typename std::result_of<F()>::type, void>::value >::type >
124 static auto execute(F && function)
126 typename std::remove_reference<decltype(function())>::type ret;
127 ExecutorOnMainThread tmp{ [&]()
131 ecore_main_loop_thread_safe_call_sync(executeOnMainThreadCb, &tmp);
132 ASSERT(!eina_main_loop_is());
133 if (tmp.failure) throw *tmp.failure;
136 template <typename F, typename = typename
137 std::enable_if<std::is_same<typename std::result_of<F()>::type, void>::value>::type>
138 static void execute(F && function)
140 ExecutorOnMainThread tmp{ [&]()
144 ecore_main_loop_thread_safe_call_sync(executeOnMainThreadCb, &tmp);
145 ASSERT(!eina_main_loop_is());
146 if (tmp.failure) throw *tmp.failure;
150 ExecutorOnMainThread() = delete;
151 ExecutorOnMainThread(std::function<void()> function) : func(std::move(function)) { }
152 ExecutorOnMainThread(const ExecutorOnMainThread &) = delete;
153 ExecutorOnMainThread(ExecutorOnMainThread &&) = delete;
155 ExecutorOnMainThread &operator = (const ExecutorOnMainThread &) = delete;
156 ExecutorOnMainThread &operator = (ExecutorOnMainThread &&) = delete;
158 std::function<void()> func;
159 Optional<EvaluationFailure> failure;
161 static void *executeOnMainThreadCb(void *d)
163 auto helper = static_cast<ExecutorOnMainThread *>(d);
166 } catch (EvaluationFailure &e) {
167 helper->failure = std::move(e);
168 } catch (std::exception &e) {
169 helper->failure = EvaluationFailure{} << "unhandled expection (" << e.what() << ")";
171 helper->failure = EvaluationFailure{} << "unhandled expection";
177 template <typename T> auto executeOnMainThread(T &&fnc)
179 return ExecutorOnMainThread::execute(std::forward<T>(fnc));
182 template <typename T, typename MONITORED_TYPE> MONITORED_TYPE executeOnMainThread(T &&fnc,
183 Monitor<BatchValueOrError<MONITORED_TYPE>> monitor,
184 std::chrono::microseconds timeout = 3s)
186 auto untilMoment = std::chrono::high_resolution_clock::now() + timeout;
187 ExecutorOnMainThread::execute(std::forward<T>(fnc));
188 auto h = monitor.lock();
189 bool success = h.waitForCondition(untilMoment, [&]() {
193 throw EvaluationFailure{} << "evaluation timeouted";
195 return h->takeValue();
198 BatchExecutor::BatchExecutor(std::ostream &output) : output(output)
200 insertRoleConstants();
201 insertStateConstants();
202 insertCollectionConstants();
206 registerContextChangeCallback();
209 void BatchExecutor::registerContextChangeCallback()
211 auto nav = Singleton<UniversalSwitch>::instance().getNavigationInterface();
213 DEBUG("no navigation interface found, context changed callback not registered");
216 auto updateContextInfo = [this](std::shared_ptr<UIElement> root, std::shared_ptr<UIElement> keyboardRoot, std::shared_ptr<NavigationElement> navigationContext) {
218 if (root && root->getObject()) {
219 auto r = Singleton<UniversalSwitch>::instance().getAtspi()->getName(root->getObject());
221 name = std::move(*r);
223 DEBUG("context changed to root %s (%s) [%s]", root ? Atspi::getUniqueId(root->getObject()).c_str() : "", name.c_str(),
224 keyboardRoot ? Atspi::getUniqueId(keyboardRoot->getObject()).c_str() : "");
225 auto h = contextInfo.lock();
226 h->navigation = std::move(navigationContext);
227 h->root = std::move(root);
228 h->keyboardRoot = std::move(keyboardRoot);
229 h->rootName = std::move(name);
230 h->rootAddress = h->root ? Atspi::getUniqueId(h->root->getObject()) : "";
232 updateContextInfo(nav->getCurrentVisibleRoot(), nav->getCurrentKeyboardVisibleRoot(), nav->getCurrentNavigationContext());
233 contextChangedHandle = nav->registerCb<NavigationCallbackType::ContextChanged>(std::move(updateContextInfo));
236 EvaluationValue BatchExecutor::getVariableByName(const std::string &name)
238 auto it = variables.find(name);
239 if (it != variables.end())
241 throw EvaluationFailure{} << "unknown variable '" << name << "'";
244 std::shared_ptr<UIElement> BatchExecutor::getVisibleRoot()
246 auto h = contextInfo.lock();
250 std::shared_ptr<UIElement> BatchExecutor::getKeyboardRoot()
252 auto h = contextInfo.lock();
253 return h->keyboardRoot;
256 std::ostream &BatchExecutor::outputStream()
261 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(Point pt)
263 return executeOnMainThread([&]() {
264 auto root = getVisibleRoot();
265 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
266 auto found = atspi->getAtPoint(pt, Atspi::CoordType::Screen, root->getObject());
267 if (!found) throw EvaluationFailure{} << "no at-spi object found at position <" <<
268 pt.x << ", " << pt.y << ">";
269 return std::make_shared<UIElement>(std::move(found), pt, root->getApplicationCategory());
273 std::unordered_map<std::string, std::string> BatchExecutor::getUIElementAttributes(const std::shared_ptr<UIElement> &uiElem)
275 return executeOnMainThread([&]() {
276 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
277 auto found = atspi->getAttributes(uiElem->getObject());
278 std::unordered_map<std::string, std::string> res;
279 for (auto &it : found)
280 res.insert(std::move(it));
281 return std::move(res);
285 unsigned int BatchExecutor::getUIElementRole(const std::shared_ptr<UIElement> &uiElem)
287 return executeOnMainThread([&]() {
288 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
289 auto found = atspi->getRole(uiElem->getObject());
291 return static_cast<unsigned int>(*found);
292 throw EvaluationFailure{} << "failed to get at-spi object's role (use dlogutil to get at-spi error message)";
296 Atspi::StateSet BatchExecutor::getUIElementStates(const std::shared_ptr<UIElement> &uiElem)
298 return executeOnMainThread([&]() {
299 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
300 auto states = atspi->getStateSet(uiElem->getObject());
302 return std::move(*states);
304 throw EvaluationFailure{} << "failed to get at-spi state set (use dlogutil to get at-spi error message)";
308 Rectangle BatchExecutor::getUIElementPosition(const std::shared_ptr<UIElement> &uiElem)
310 return executeOnMainThread([&]() {
311 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
312 auto component = atspi->getComponentInterface(uiElem->getObject());
314 auto found = atspi->getScreenPosition(component);
316 return std::move(*found);
318 throw EvaluationFailure{} << "failed to get at-spi object's position (use dlogutil to get at-spi error message)";
322 std::string BatchExecutor::getUIElementName(const std::shared_ptr<UIElement> &uiElem)
324 return executeOnMainThread([&]() {
325 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
326 auto found = atspi->getName(uiElem->getObject());
328 return std::move(*found);
329 throw EvaluationFailure{} << "failed to get at-spi object's name (use dlogutil to get at-spi error message)";
333 std::string BatchExecutor::getUIElementUniqueId(const std::shared_ptr<UIElement> &uiElem)
335 return executeOnMainThread([&]() {
336 return Atspi::getUniqueId(uiElem->getObject());
340 void BatchExecutor::findByName(const std::vector<AtspiAccessiblePtr> &elems, std::string requestedName, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback)
343 std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback;
344 std::string requestedName;
345 std::vector<AtspiAccessiblePtr> foundElements;
346 Optional<DBus::Error> error;
350 if (error) callback(*error);
351 else callback(std::move(foundElements));
354 auto exec = std::make_shared<Exec>();
355 exec->callback = std::move(callback);
356 exec->requestedName = std::move(requestedName);
357 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
358 for (auto &e : elems) {
359 atspi->getName(e, [e, exec](DBus::ValueOrError<std::string> name) {
362 exec->error = name.getError();
364 if (std::get<0>(name) == exec->requestedName) {
365 exec->foundElements.push_back(e);
372 void BatchExecutor::getAllObjects(AtspiAccessiblePtr root, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback,
373 const std::vector<int> &roles, int roleMode, const std::vector<int> &states, int stateMode)
375 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
376 auto col = atspi->getCollectionInterface(root);
378 callback(DBus::Error{ "root '" + Atspi::getUniqueId(root) + "' doesn't have collection interface" });
380 auto m = Atspi::Matcher();
381 if (!states.empty() && stateMode < 0) stateMode = ATSPI_Collection_MATCH_ALL;
382 if (stateMode >= 0) m.states(states.begin(), states.end(), static_cast<AtspiCollectionMatchType>(stateMode));
384 if (!roles.empty() && roleMode < 0) roleMode = ATSPI_Collection_MATCH_ANY;
385 if (roleMode >= 0) m.roles(roles.begin(), roles.end(), static_cast<AtspiCollectionMatchType>(roleMode));
386 atspi->getMatchedElements(col, ATSPI_Collection_SORT_ORDER_CANONICAL, 0,
387 m, false, std::move(callback));
391 void BatchExecutor::makeUIElement(AtspiAccessiblePtr src, std::function<void(DBus::ValueOrError<std::shared_ptr<UIElement>>)> callback)
393 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
394 atspi->getComponentInterface(src, [src, callback = std::move(callback)](DBus::ValueOrError<AtspiComponentPtr> comp) {
395 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
397 callback(comp.getError());
400 atspi->getScreenPosition(std::get<0>(comp), [src, callback = std::move(callback)](DBus::ValueOrError<Rectangle> pos) {
402 callback(pos.getError());
405 callback(std::make_shared<UIElement>(src, std::get<0>(pos).getCenterPoint(), UIElement::ApplicationCategory::OTHER));
410 void BatchExecutor::makeUIElements(std::vector<AtspiAccessiblePtr> sources,
411 std::function<void(DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>>)> callback)
414 std::function<void(DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>>)> callback;
415 std::vector<std::shared_ptr<UIElement>> results;
416 Optional<DBus::Error> error;
423 for (auto e : results)
424 ASSERT(e && e->getObject());
425 callback(std::move(results));
429 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
430 auto exec = std::make_shared<Exec>();
431 exec->callback = std::move(callback);
432 exec->results.resize(sources.size());
433 for (auto i = 0u; i < sources.size(); ++i) {
434 makeUIElement(sources[i], [i, exec](DBus::ValueOrError<std::shared_ptr<UIElement>> ui) {
437 exec->error = std::move(ui.getError());
441 exec->results[i] = std::move(std::get<0>(ui));
447 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(const std::string &requestedName)
450 getVisibleRoot(), getKeyboardRoot()
452 if (root && root->getObject()) {
453 Monitor<BatchValueOrError<std::vector<std::shared_ptr<UIElement>>>> monitor;
454 auto r = executeOnMainThread([&]() {
455 getAllObjects(root->getObject(), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> allElements) {
456 findByName(std::get<0>(allElements), requestedName, wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
457 auto &elems = std::get<0>(elements);
458 makeUIElements(std::move(elems), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>> uiElems) {
459 auto h = monitor.lock();
460 h->setValue(std::move(std::get<0>(uiElems)));
467 std::ostringstream names;
468 for (auto &elem : r) names << " " << Atspi::getUniqueId(elem->getObject());
469 throw EvaluationFailure{} << "found more, than one at-spi object with name '" <<
470 requestedName << "' (see" << names.str() << ")";
472 return std::move(r[0]);
476 throw EvaluationFailure{} << "found no at-spi object with name '" << requestedName << "'";
479 std::string BatchExecutor::getFileContent(const std::string &filename)
481 auto source = std::ifstream{ filename, std::ios_base::in };
482 if (!source.is_open())
483 throw EvaluationFailure{} << "failed to open file '" << filename << "'\n";
485 source.seekg(0, std::ios_base::end);
486 auto total = source.tellg();
488 std::vector<char> bufor;
490 source.read(bufor.data(), total);
491 if ((size_t) source.gcount() != total)
492 throw EvaluationFailure{} << "error while reading file '" << filename << "'\n";
494 return std::string(bufor.data(), (size_t)total);
497 //TODO: when switching to c++17 we should use std::filename::path
498 std::string getPathRelativeToFile(const std::string &relativePath, const std::string &file)
500 if (!relativePath.empty() && relativePath[0] == '/') {
501 //not a relative path
504 auto pos = file.rfind('/');
505 if (pos == std::string::npos) {
508 return file.substr(0, pos + 1) + relativePath;
511 std::string errorToStr(int err)
513 std::ostringstream tmp;
517 if (first) first = false; \
522 Q(TIZEN_ERROR_NOT_PERMITTED)
523 Q(TIZEN_ERROR_NO_SUCH_FILE)
524 Q(TIZEN_ERROR_NO_SUCH_PROCESS)
525 Q(TIZEN_ERROR_INTERRUPTED_SYS_CALL)
526 Q(TIZEN_ERROR_IO_ERROR)
527 Q(TIZEN_ERROR_NO_SUCH_DEVICE)
528 Q(TIZEN_ERROR_ARGUMENT_LIST_TOO_LONG)
529 Q(TIZEN_ERROR_EXEC_FORMAT_ERROR)
530 Q(TIZEN_ERROR_BAD_FILE_NUMBER)
531 Q(TIZEN_ERROR_TRY_AGAIN)
532 Q(TIZEN_ERROR_OUT_OF_MEMORY)
533 Q(TIZEN_ERROR_PERMISSION_DENIED)
534 Q(TIZEN_ERROR_BAD_ADDRESS)
535 Q(TIZEN_ERROR_BLOCK_DEVICE_REQUIRED)
536 Q(TIZEN_ERROR_RESOURCE_BUSY)
537 Q(TIZEN_ERROR_FILE_EXISTS)
538 Q(TIZEN_ERROR_CROSS_DEVICE_LINK)
539 Q(TIZEN_ERROR_NOT_A_DIRECTORY)
540 Q(TIZEN_ERROR_IS_A_DIRECTORY)
541 Q(TIZEN_ERROR_INVALID_PARAMETER)
542 Q(TIZEN_ERROR_FILE_TABLE_OVERFLOW)
543 Q(TIZEN_ERROR_TOO_MANY_OPEN_FILES)
544 Q(TIZEN_ERROR_TOO_NOT_A_TERMINAL)
545 Q(TIZEN_ERROR_TOO_TEXT_FILE_BUSY)
546 Q(TIZEN_ERROR_FILE_TOO_LARGE)
547 Q(TIZEN_ERROR_FILE_NO_SPACE_ON_DEVICE)
548 Q(TIZEN_ERROR_ILLEGAL_SEEK)
549 Q(TIZEN_ERROR_READ_ONLY_FILESYSTEM)
550 Q(TIZEN_ERROR_NO_DATA)
551 Q(TIZEN_ERROR_TOO_MANY_LINKS)
552 Q(TIZEN_ERROR_BROKEN_PIPE)
553 Q(TIZEN_ERROR_ARGUMENT_OUT_OF_DOMAIN)
554 Q(TIZEN_ERROR_RESULT_OUT_OF_RANGE)
555 Q(TIZEN_ERROR_WOULD_CAUSE_DEADLOCK)
556 Q(TIZEN_ERROR_FILE_NAME_TOO_LONG)
557 Q(TIZEN_ERROR_FILE_NO_LOCKS_AVAILABLE)
558 Q(TIZEN_ERROR_INVALID_OPERATION)
559 Q(TIZEN_ERROR_DIR_NOT_EMPTY)
560 Q(TIZEN_ERROR_TOO_MANY_SYMBOLIC_LINKS)
561 Q(TIZEN_ERROR_WOULD_BLOCK)
562 Q(TIZEN_ERROR_CORRUPTED_SHARED_LIB)
563 Q(TIZEN_ERROR_LIB_SECTION_CORRUPTED)
564 Q(TIZEN_ERROR_LINK_TOO_MANY_SHARED_LIB)
565 Q(TIZEN_ERROR_SHARED_LIB_EXEC)
566 Q(TIZEN_ERROR_ILLEGAL_BYTE_SEQ)
567 Q(TIZEN_ERROR_SYSTEM_CALL_RESTART)
568 Q(TIZEN_ERROR_STREAMS_PIPE)
569 Q(TIZEN_ERROR_TOO_MANY_USERS)
570 Q(TIZEN_ERROR_NON_SOCKET)
571 Q(TIZEN_ERROR_NO_DEST_ADDRESS)
572 Q(TIZEN_ERROR_MSG_TOO_LONG)
573 Q(TIZEN_ERROR_PROTOCOL_WRONG_TYPE)
574 Q(TIZEN_ERROR_PROTOCOL_NOT_AVALIABLE)
575 Q(TIZEN_ERROR_PROTOCOL_NOT_SUPPORTED)
576 Q(TIZEN_ERROR_SOCKET_TYPE_NOT_SUPPORTED)
577 Q(TIZEN_ERROR_ENDPOINT_OPERATIN_NOT_SUPPORTED)
578 Q(TIZEN_ERROR_PROTOCOL_FAMILY_NOT_SUPPORTED)
579 Q(TIZEN_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED)
580 Q(TIZEN_ERROR_ADDRES_IN_USE)
581 Q(TIZEN_ERROR_CANNOT_ASSIGN_ADDRESS)
582 Q(TIZEN_ERROR_NETWORK_DOWN)
583 Q(TIZEN_ERROR_NETWORK_UNREACHABLE)
584 Q(TIZEN_ERROR_NETWORK_RESET)
585 Q(TIZEN_ERROR_CONNECTION_ABORTED)
586 Q(TIZEN_ERROR_CONNECTION_RESET_BY_PEER)
587 Q(TIZEN_ERROR_BUFFER_SPACE)
588 Q(TIZEN_ERROR_ENDPOINT_CONNECTED)
589 Q(TIZEN_ERROR_ENDPOINT_NOT_CONNECTED)
590 Q(TIZEN_ERROR_ENDPOINT_SHUTDOWN)
591 Q(TIZEN_ERROR_TOO_MANY_REFERENCES)
592 Q(TIZEN_ERROR_CONNECTION_TIME_OUT)
593 Q(TIZEN_ERROR_CONNECTION_REFUSED)
594 Q(TIZEN_ERROR_HOST_DOWN)
595 Q(TIZEN_ERROR_NO_ROUTE_TO_HOST)
596 Q(TIZEN_ERROR_ALREADY_IN_PROGRESS)
597 Q(TIZEN_ERROR_NOW_IN_PROGRESS)
598 Q(TIZEN_ERROR_STALE_NFS_FILE_HANDLE)
599 Q(TIZEN_ERROR_STRUCTURE_UNCLEAN)
600 Q(TIZEN_ERROR_NOT_XENIX_NAMED_TYPE_FILE)
601 Q(TIZEN_ERROR_NO_XENIX_SEMAPHORES_AVAILABLE)
602 Q(TIZEN_ERROR_IS_NAMED_TYPE_FILE)
603 Q(TIZEN_ERROR_REMOTE_IO)
604 Q(TIZEN_ERROR_QUOTA_EXCEEDED)
605 Q(TIZEN_ERROR_NO_MEDIUM)
606 Q(TIZEN_ERROR_WRONG_MEDIUM_TYPE)
607 Q(TIZEN_ERROR_CANCELED)
608 Q(TIZEN_ERROR_KEY_NOT_AVAILABLE)
609 Q(TIZEN_ERROR_KEY_EXPIRED)
610 Q(TIZEN_ERROR_KEY_REVOKED)
611 Q(TIZEN_ERROR_KEY_REJECTED)
612 Q(TIZEN_ERROR_OWNER_DEAD)
613 Q(TIZEN_ERROR_UNKNOWN)
614 Q(TIZEN_ERROR_TIMED_OUT)
615 Q(TIZEN_ERROR_NOT_SUPPORTED)
616 Q(TIZEN_ERROR_USER_NOT_CONSENTED)
617 Q(TIZEN_ERROR_DEVICE_POLICY_RESTRICTION)
618 Q(TIZEN_ERROR_END_OF_COLLECTION)
623 void BatchExecutor::killApplication(const std::pair<std::string, std::string> &appInstance)
625 auto &appid = appInstance.first;
626 auto &instance_id = appInstance.second;
627 app_context_h context = NULL;
630 if (appid == "org.tizen.tts-engine-default-sr")
633 output << "killing application " << appInstance.first << (appid.empty() ? "" : ":") << instance_id << "\n";
634 if (!instance_id.empty()) {
635 ret = app_manager_get_app_context_by_instance_id(appid.c_str(), instance_id.c_str(), &context);
637 bool running = false;
638 ret = app_manager_is_running(appid.c_str(), &running);
639 if (ret != APP_MANAGER_ERROR_NONE) {
640 throw EvaluationFailure{} << "fail to app_manager_is_running[" << ret << "]: " << get_error_message(ret);
646 ret = app_manager_get_app_context(appid.c_str(), &context);
649 if (ret == APP_MANAGER_ERROR_APP_NO_RUNNING) {
650 DebugEvaluator{} << "Application " << appInstance.first << " not found";
654 if (ret != APP_MANAGER_ERROR_NONE) {
655 DebugEvaluator{} << "warning: fail to app_manager_get_app_context failed[" << ret << "]: " << get_error_message(ret);
659 ret = app_manager_terminate_app(context);
660 app_context_destroy(context);
662 if (ret != APP_MANAGER_ERROR_NONE) {
663 throw EvaluationFailure{} << "fail to terminate_app '" << appid << ":" << instance_id << "'" <<
664 "[" << ret << "]: " << get_error_message(ret);
668 std::pair<std::string, std::string> BatchExecutor::getApplicationInfo(app_info_h app_info, struct rua_rec *record)
674 char *appid = const_cast<char *>("");
675 char *instanceid = const_cast<char *>("");
677 int ret = app_info_get_app_id(app_info, &appid);
678 if (ret != APP_MANAGER_ERROR_NONE) {
679 throw EvaluationFailure{} << "app_info_get_app_id failed[" << ret << "]: " << get_error_message(ret);
682 if (record->instance_id) {
683 instanceid = record->instance_id;
685 return { appid, instanceid };
688 using ApplicationInfoIterCbDataType =
689 std::tuple<struct rua_rec *, std::vector<std::pair<std::string, std::string>>, BatchExecutor *, std::string>;
691 bool BatchExecutor::applicationInfoIterCb(app_info_h app_info, void *user_data)
693 auto &data = *(ApplicationInfoIterCbDataType *)user_data;
694 return (std::get<2>(data))->applicationInfoIterCbImpl(app_info, user_data);
697 bool BatchExecutor::applicationInfoIterCbImpl(app_info_h app_info, void *user_data)
699 auto &data = *(ApplicationInfoIterCbDataType *)user_data;
701 struct rua_rec *record = std::get<0>(data);
704 std::get<1>(data).push_back(std::get<2>(data)->getApplicationInfo(app_info, record));
705 } catch (EvaluationFailure &ev) {
706 std::get<3>(data) = ev.message();
712 void BatchExecutor::clearApplications()
714 char **table = nullptr;
715 int rows = 0, cols = 0;
716 struct rua_rec record;
717 app_info_filter_h filter = nullptr;
720 throw EvaluationFailure{} << "rua_init failed";
723 DefferedCall rua_init_cb{ [&]()
728 if (rua_history_load_db(&table, &rows, &cols) || !table) {
729 throw EvaluationFailure{} << "rua_history_load_db failed";
731 DefferedCall rua_history_load_db_cb{ [&]()
733 rua_history_unload_db(&table);
736 int ret = app_info_filter_create(&filter);
737 if (ret != APP_MANAGER_ERROR_NONE) {
738 throw EvaluationFailure{} << "filter_create failed";
740 DefferedCall app_info_filter_create_cb{ [&]()
742 app_info_filter_destroy(filter);
745 ret = app_info_filter_add_bool(filter, PACKAGE_INFO_PROP_APP_TASKMANAGE, true);
746 if (ret != APP_MANAGER_ERROR_NONE) {
747 throw EvaluationFailure{} << "app_info_filer_add_bool failed[" << ret << "]: " << get_error_message(ret);
750 auto data = ApplicationInfoIterCbDataType{ nullptr, {}, this, "" };
752 for (auto row = 0; row < rows; ++row) {
753 rua_history_get_rec(&record, table, rows, cols, row);
754 std::get<0>(data) = &record;
756 int ret = app_info_filter_add_string(filter, PACKAGE_INFO_PROP_APP_ID, record.pkg_name);
757 if (ret != APP_MANAGER_ERROR_NONE) {
758 throw EvaluationFailure{} << "app_info_filter_add_string failed[" << ret << "]: " << get_error_message(ret);
761 ret = app_info_filter_foreach_appinfo(filter, applicationInfoIterCb, &data);
762 if (ret != APP_MANAGER_ERROR_NONE) {
763 throw EvaluationFailure{} << "app_info_filter_foreach_app_info failed[" << ret << "]: " << get_error_message(ret);
765 if (!std::get<3>(data).empty()) {
766 DebugEvaluator{} << std::get<3>(data);
767 throw EvaluationFailure{} << std::get<3>(data);
771 for (auto &r : std::get<1>(data)) {
776 void BatchExecutor::insertMethods()
778 variables["get_batch_file"] = [&]() -> EvaluationValue {
779 // this function is called through Evaluator.cpp:CallEvaluator::evaluate()
780 // which pushes it's own location to path stack, so path stack's top will be valid here.
781 return EvaluationContext::getCurrentEvaluationContext().topBatchPath();
784 variables["import"] = EvaluationValueFunction{ [&](std::string filename) -> EvaluationValue {
787 auto currentRdlFile = EvaluationContext::getCurrentEvaluationContext().topBatchPath();
788 auto filenameFullPath = getPathRelativeToFile(filename, currentRdlFile);
789 auto it = imports.insert({ filenameFullPath, {} });
792 result = it.first->second;
794 throw EvaluationFailure{} << "nested import found in " << filenameFullPath;
800 auto content = getFileContent(filenameFullPath);
802 auto tokens = lexTest(error, filenameFullPath, content);
805 throw EvaluationFailure{} << "Lexing failed\n" << error;
807 std::vector<std::string> errors;
808 result = parseTokens(errors, tokens);
812 auto error = EvaluationFailure{};
813 error << "Parsing failed\n";
814 for (auto &e : errors) {
817 throw std::move(error);
820 CallOnExit _tmp{ [&]()
822 it.first->second = std::move(result);
827 } catch (ReturnValue)
831 throw EvaluationFailure{} << "break used on global scope in file " << filename;
832 } catch (ContinueValue)
834 throw EvaluationFailure{} << "continue used on global scope in file " << filename;
837 }, { {"filename"} } };
839 variables["sleep"] = EvaluationValueFunction{ [&](double seconds) -> EvaluationValue {
842 auto sleepTime = std::chrono::milliseconds{ static_cast<int>(std::floor(1000.0 * seconds + 0.5)) };
843 std::this_thread::sleep_for(sleepTime);
846 }, { {"seconds"} } };
848 variables["print"] = EvaluationValueFunction{ [&](EvaluationValue text) -> EvaluationValue {
849 DebugEvaluator{} << text;
850 outputStream() << text << "\n";
854 variables["range"] = EvaluationValueFunction{ [&](EvaluationValue min, EvaluationValue max, EvaluationValue step) -> EvaluationValue {
855 if (max.isEmpty() && step.isEmpty())
861 auto mn = min.convertToInteger();
862 auto mx = max.convertToInteger();
863 if (step.isEmpty()) step = mn <= mx ? 1 : -1;
864 auto st = step.convertToInteger();
865 struct Iterator : public EvaluationValueIteratorInterface {
866 EvaluationValueInteger min, max, step;
868 Iterator(EvaluationValueInteger min, EvaluationValueInteger max, EvaluationValueInteger step) :
869 min(min), max(max), step(step) { }
871 Optional<EvaluationValue> get() const override {
872 if ((step > 0 && min < max) || (step < 0 && min > max)) return min;
875 void next() override {
876 if ((step > 0 && min < max) || (step < 0 && min > max)) min += step;
879 return EvaluationValueBase::create(std::make_shared<Iterator>(mn, mx, st));
880 }, { {"min"}, { "max", EvaluationValue{} }, { "step", EvaluationValue{} } } };
882 variables["str"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
883 std::ostringstream tmp;
888 variables["is_point_on_screen"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
889 auto dims = executeOnMainThread([&]()
891 return Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions();
893 return dims.contains(point);
896 variables["highlight"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
897 AtspiAccessiblePtr obj;
900 auto root = getVisibleRoot();
901 obj = Singleton<UniversalSwitch>::instance().getAtspi()->getAtPoint(e.asPoint(), Atspi::CoordType::Screen, root->getObject());
904 if (e.isString()) e = convertToUIElement(e.asString());
905 auto v = e.convertToUIElement();
906 obj = v->getObject();
908 auto comp = Singleton<UniversalSwitch>::instance().getAtspi()->getComponentInterface(obj);
909 if (!comp) throw EvaluationFailure{} << "atspi object '" << Atspi::getUniqueId(obj) << "' doesn't have a component interface";
910 auto res = Singleton<UniversalSwitch>::instance().getAtspi()->grabHighlight(comp);
911 if (!res) throw EvaluationFailure{} << "grabHighlight call failed (see dlog log for more details)";
915 variables["len"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
916 if (e.isString()) return static_cast<int>(e.asString().size());
917 if (e.isVector()) return static_cast<int>(e.asVector().size());
918 if (e.isSet()) return static_cast<int>(e.asSet().size());
919 if (e.isDict()) return static_cast<int>(e.asDict().size());
920 throw EvaluationFailure{} << "value of type " << e.typeName() << " doesn't have size property";
923 variables["get_at_point"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
924 return convertToUIElement(point);
927 variables["assert"] = EvaluationValueFunction{ [&](bool condition) -> EvaluationValue {
928 if (!condition) throw EvaluationFailure{} << "assertion failed";
930 }, { {"condition"} } };
932 variables["clear_applications"] = EvaluationValueFunction{
933 [&]() -> EvaluationValue {
935 std::this_thread::sleep_for(std::chrono::milliseconds{ 1000 });
939 variables["find_by_criteria"] = EvaluationValueFunction{
940 [&](std::vector<int> roles, int roleMode, std::vector<int> states, int stateMode) -> EvaluationValue {
941 auto root = getVisibleRoot();
942 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
943 ASSERT(root->getObject());
944 Monitor<BatchValueOrError<bool>> monitor;
945 std::vector<EvaluationValue> result;
947 executeOnMainThread([&]()
949 getAllObjects(root->getObject(), wrap(monitor, [ =, result = &result ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
950 auto &e = std::get<0>(elements);
952 auto h = monitor.lock();
955 result->reserve(e.size());
957 makeUIElement(f, wrap(monitor, [ = ](DBus::ValueOrError<std::shared_ptr<UIElement>> elem) {
958 result->push_back(std::move(std::get<0>(elem)));
959 if (result->size() == result->capacity()) {
960 auto h = monitor.lock();
966 }), roles, roleMode, states, stateMode);
968 return std::move(result);
969 }, { { "roles", EvaluationValueSet() }, { "roleMode", -1 },
970 { "states", EvaluationValueSet() }, { "stateMode", -1 }
973 variables["find_by_name"] = EvaluationValueFunction{ [&](std::string name) -> EvaluationValue {
974 for (auto root : { getVisibleRoot(), getKeyboardRoot() })
976 if (root && root->getObject()) {
977 Monitor<BatchValueOrError<std::vector<std::shared_ptr<UIElement>>>> monitor;
978 auto r = executeOnMainThread([&]() {
979 getAllObjects(root->getObject(), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> allElements) {
980 findByName(std::get<0>(allElements), name, wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
981 auto &elems = std::get<0>(elements);
982 makeUIElements(std::move(elems), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>> uiElems) {
983 auto h = monitor.lock();
984 h->setValue(std::move(std::get<0>(uiElems)));
989 if (!r.empty()) return std::move(r);
992 return std::vector<std::shared_ptr<UIElement>>{};
995 variables["launch_application"] = EvaluationValueFunction{ [&](std::string name) -> EvaluationValue {
996 executeOnMainThread([&]()
998 bool ret = utils::generateLaunchRequest(name);
999 if (!ret) throw EvaluationFailure{} << "failed to launch application " << name;
1001 return EvaluationValue{};
1002 }, { { "name" } } };
1004 auto generateTapFunction = [&](size_t tapCount) {
1006 return EvaluationValueFunction{ [ &, tapCount](EvaluationValue target, EvaluationValue fingers) -> EvaluationValue {
1008 auto root = getVisibleRoot();
1009 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
1010 ASSERT(root->getObject());
1012 const auto fingerCount = fingers.convertToInteger();
1013 if (fingerCount <= 0 || fingerCount > 3)
1014 throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
1016 auto coord = Point{ 200, 200 };
1017 if (!target.isEmpty())
1019 if (target.isString()) {
1020 target = this->convertToUIElement(target.convertToString());
1022 auto dest = target.convertToUIElement();
1023 coord = getUIElementPosition(dest).getCenterPoint();
1026 if (tapCount == 1 && fingers == 1)
1027 throw EvaluationFailure{} << "no target value passed (you need target to do one finger single tap)";
1030 auto sleep_until = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds{ 400 };
1031 for (auto i = 0u; i < tapCount; ++i)
1033 executeOnMainThread([&]() {
1034 auto res = utils::generateTapGesture(coord.x, coord.y, 0.0f, fingerCount);
1036 throw EvaluationFailure{} << "failed to execute " << tapCount << " tap gesture, " << res.getError().message;
1039 std::this_thread::sleep_until(sleep_until);
1041 return EvaluationValue{};
1042 }, { { "target", EvaluationValue{} }, { "fingers", 1 } } };
1044 variables["TAP"] = generateTapFunction(1);
1045 variables["DOUBLE_TAP"] = generateTapFunction(2);
1046 variables["TRIPLE_TAP"] = generateTapFunction(3);
1048 enum class FlickKind {
1049 down, up, left, right
1051 auto generateFlickFunction = [&](FlickKind kind, int steps) {
1052 return EvaluationValueFunction{ [ &, kind, steps](EvaluationValue fingers) -> EvaluationValue {
1054 auto size = executeOnMainThread([&]()
1056 return Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions().size;
1059 // 280 = 360 - 2 * 40 ( 40 - left margin to emulate regular flick but not flick from side of the screen )
1060 // 250 = 360 - 2 * 55 ( 55 - top margin to emulate regular flick but not flick from side of the screen )
1061 // 360 x 360 - dimensions of wearable screen
1062 auto width = size.width * 280 / 360;
1063 auto height = size.height * 250 / 360;
1067 case FlickKind::right:
1071 case FlickKind::left:
1079 case FlickKind::down:
1084 auto root = getVisibleRoot();
1085 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
1086 ASSERT(root->getObject());
1088 auto fingerCount = fingers.convertToInteger();
1089 if (fingerCount <= 0 || fingerCount > 3)
1090 throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
1092 executeOnMainThread([&]()
1094 auto dims = Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions();
1095 auto mx = dims.getCenterPoint();
1096 auto x0 = mx.x - dx / 2;
1098 auto y0 = mx.y - dy / 2;
1100 utils::generateDragGesture(x0, y0, x1, y1, steps, 0.0, fingerCount);
1102 std::this_thread::sleep_for(std::chrono::milliseconds{ 600 });
1103 return EvaluationValue{};
1104 }, { { "fingers", 1 } } };
1107 // For flick gesture we set the step value to 20.
1108 variables["FLICK_RIGHT"] = generateFlickFunction(FlickKind::right, 20);
1109 variables["FLICK_LEFT"] = generateFlickFunction(FlickKind::left, 20);
1110 variables["FLICK_UP"] = generateFlickFunction(FlickKind::up, 20);
1111 variables["FLICK_DOWN"] = generateFlickFunction(FlickKind::down, 20);
1113 // For drag gesture we set the step value to 60.
1114 variables["DRAG_RIGHT"] = generateFlickFunction(FlickKind::right, 60);
1115 variables["DRAG_LEFT"] = generateFlickFunction(FlickKind::left, 60);
1116 variables["DRAG_UP"] = generateFlickFunction(FlickKind::up, 60);
1117 variables["DRAG_DOWN"] = generateFlickFunction(FlickKind::down, 60);
1119 auto generateWheelTurnFunction = [&]() {
1120 return EvaluationValueFunction{ [&](bool clockwise, int multiplicity) -> EvaluationValue {
1122 if (multiplicity <= 0)
1123 throw EvaluationFailure{} << "Invalid multiplicity (must be more that 0)";
1125 auto delay = multiplicity > 1 ? 0.75 : 0;
1127 executeOnMainThread([&]()
1129 utils::generateWheelTurn(clockwise, multiplicity, delay);
1132 int await = 1000 * (multiplicity * delay);
1133 std::this_thread::sleep_for(std::chrono::milliseconds{ await });
1135 return EvaluationValue{};
1136 }, { {"clockwise", true}, {"multiplicity", 1} } };
1138 variables["TURN_WHEEL"] = generateWheelTurnFunction();
1140 auto generateKeyPressFunction = [&]() {
1141 return EvaluationValueFunction{ [&](std::string keyId, unsigned multiplicity, double hold_time) -> EvaluationValue {
1144 throw EvaluationFailure{} << "Invalid keyId";
1146 if (multiplicity <= 0)
1147 throw EvaluationFailure{} << "Invalid multiplicity (must be more that 0)";
1150 throw EvaluationFailure{} << "Invalid hold time (must be more or equal 0)";
1152 executeOnMainThread([&]()
1154 utils::generateKeyPress(keyId, multiplicity, hold_time);
1157 std::this_thread::sleep_for(std::chrono::milliseconds{ 800 });
1159 return EvaluationValue{};
1160 }, { {"keyId" }, {"multiplicity", 1 }, {"holdTime", 0} } };
1162 variables["PRESS_KEY"] = generateKeyPressFunction();
1165 class PredicateWaitInterface : public EvaluationValueWaitInterface
1168 PredicateWaitInterface(BatchExecutor *executor, std::chrono::milliseconds timeout)
1169 : self(executor), delay(timeout)
1172 void join() override
1174 timeout = std::chrono::high_resolution_clock::now() + delay;
1179 virtual void joinImpl() = 0;
1181 BatchExecutor *self;
1182 std::chrono::high_resolution_clock::time_point timeout;
1183 std::chrono::milliseconds delay;
1186 class WaitDlog : public PredicateWaitInterface
1189 WaitDlog(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern)
1190 : PredicateWaitInterface(executor, timeout)
1195 void prepare() override
1197 auto h = self->dlogInfo.lock();
1198 h->stack.push_back({ std::move(pattern) });
1199 h->stack.back().found = &found;
1201 void joinImpl() override
1203 auto h = self->dlogInfo.lock();
1208 assert(!h->stack.empty());
1209 assert(h->stack.back().found == &found);
1210 h->stack.pop_back();
1214 auto res = h.waitForCondition(timeout, [&]() {
1219 throw EvaluationFailure{} << "wait for dlog ('" << pattern << "'): operation timeouted";
1222 std::string pattern;
1226 class WaitTTS : public PredicateWaitInterface
1229 WaitTTS(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern, bool exact)
1230 : PredicateWaitInterface(executor, timeout), exact(exact)
1232 this->pattern = pattern;
1236 void prepare() override
1238 auto h = self->ttsInfo.lock();
1239 h->stack.push_back({});
1240 auto &w = h->stack.back();
1241 if (!pattern.empty())
1242 w.searchLine = pattern;
1246 void joinImpl() override
1248 auto h = self->ttsInfo.lock();
1253 assert(!h->stack.empty());
1254 assert(h->stack.back().found == &found);
1255 h->stack.pop_back();
1259 auto res = h.waitForCondition(timeout, [&]() {
1264 throw EvaluationFailure{} << "wait for tts ('" << pattern << "'): operation timeouted";
1267 std::string pattern;
1268 bool exact = false, found = false;
1271 class WaitGui : public PredicateWaitInterface
1274 WaitGui(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string name)
1275 : PredicateWaitInterface(executor, timeout), name(std::move(name))
1277 auto h = self->contextInfo.lock();
1278 this->currentContextName = h->rootName;
1279 this->currentContextAddress = h->rootAddress;
1282 void prepare() override
1284 auto h = self->contextInfo.lock();
1285 currentContextName = h->rootName;
1286 currentContextAddress = h->rootAddress;
1288 void joinImpl() override
1290 auto h = self->contextInfo.lock();
1292 if (currentContextAddress != h->rootAddress) {
1293 self->outputStream() << "context name changed from " <<
1294 "'" << currentContextName << "' (" << currentContextAddress << "), to " <<
1295 "'" << h->rootName << "' (" << h->rootAddress << "), while looking for '" << name << "'\n";
1296 currentContextName = h->rootName;
1297 currentContextAddress = h->rootAddress;
1301 return !name.empty() && name == h->rootName;
1303 auto res = h.waitForCondition(timeout, pred);
1306 throw EvaluationFailure{} << "wait for gui: operation timeouted, context change never came";
1307 throw EvaluationFailure{} << "wait for gui ('" << name << "'): operation timeouted, " <<
1308 "current root name is '" << h->rootName << "'";
1312 std::string name, currentContextName;
1313 std::string currentContextAddress;
1316 void BatchExecutor::insertWaits()
1318 auto dlogTTS = [&](std::string pattern, double timeout) -> EvaluationValue {
1319 auto impl = std::make_shared<WaitDlog>(this, std::chrono::milliseconds{ static_cast<size_t>(timeout * 1000) }, std::move(pattern));
1320 return EvaluationValue{ impl };
1322 variables["dlog"] = EvaluationValueFunction{ std::move(dlogTTS), { { "pattern" }, { "timeout", 5.0 } } };
1324 auto waitTTS = [&](std::string pattern, double timeout, bool exact) -> EvaluationValue {
1325 auto impl = std::make_shared<WaitTTS>(this, std::chrono::milliseconds{ static_cast<size_t>(timeout * 1000) }, std::move(pattern), exact);
1326 return EvaluationValue{ impl };
1328 variables["tts"] = EvaluationValueFunction{ std::move(waitTTS), { { "pattern", "" }, { "timeout", 5.0 }, { "exact", true } } };
1330 auto waitGui = [&](std::string name, double timeout) -> EvaluationValue {
1331 auto impl = std::make_shared<WaitGui>(this, std::chrono::milliseconds{ static_cast<size_t>(timeout * 1000) }, std::move(name));
1332 return EvaluationValue{ impl };
1334 variables["gui"] = EvaluationValueFunction{ std::move(waitGui), { { "name", "" }, { "timeout", 5.0 } } };
1337 void BatchExecutor::callActivity(const std::string &activityName, const EvaluationValueFunction::Args &args)
1339 auto activity = executeOnMainThread([&]() {
1340 return ActivityFactory::getInstance()->createActivity(activityName);
1343 throw EvaluationFailure{} << "failed to construct '" << activityName << "' activity";
1344 auto numOfArgs = activity->getRequiredNumberOfArgumentsIfAllowedInBatchProcessing();
1346 throw EvaluationFailure{} << "activity '" << activityName << "' is not supported";
1347 if (*numOfArgs != args.size())
1348 throw EvaluationFailure{} << "invalid number of arguments for activity '" << activityName <<
1349 "', got " << args.size() << ", expected " << *numOfArgs;
1350 auto uiActivity = std::dynamic_pointer_cast<UIActivity>(activity);
1352 // activity must inherit from UIActivity if it returns non zero expected arguments
1353 ASSERT(args.empty() || uiActivity);
1355 std::vector<std::shared_ptr<UIElement>> uiArgs(args.size());
1356 for (size_t i = 0; i < args.size(); ++i) {
1358 uiArgs[i] = args[i].convertToUIElement();
1360 throw EvaluationFailure{} << "can't convert argument " << (i + 1) <<
1361 " of kind " << args[i].typeName() << " to UIElement";
1363 Monitor<BatchValueOrError<bool>> monitor;
1365 executeOnMainThread([&]() {
1366 DEBUG("calling activity %s", activityName.c_str());
1367 for (auto &arg : uiArgs) {
1369 uiActivity->update(std::move(arg));
1371 activity->process(DoneCallback{ [ = ] {
1372 auto h = monitor.lock();
1375 DEBUG("calling activity %s done", activityName.c_str());
1377 if (!activity->isCompleted())
1378 throw EvaluationFailure{} << "activity '" << activityName << "' is not marked as completed!";
1379 auto h = monitor.lock();
1380 ASSERT(*h); // sanity check, must be set, otherwise an exception was thrown
1384 void BatchExecutor::insertActivities()
1386 for (auto activityName : ActivityFactory::getInstance()->getAllActivityTypes()) {
1387 if (variables.find(activityName) != variables.end())
1389 variables[activityName] = [ = ](EvaluationValueFunction::Args args) -> EvaluationValue {
1390 callActivity(activityName, args);
1396 void BatchExecutor::insertStateConstants()
1398 // from at-spi2-core v. 2.16.0
1399 for (auto pair : std::initializer_list<std::pair<std::string, int>> {
1400 #define Q(a) { #a, a }
1401 Q(ATSPI_STATE_INVALID),
1402 Q(ATSPI_STATE_ACTIVE),
1403 Q(ATSPI_STATE_ARMED),
1404 Q(ATSPI_STATE_BUSY),
1405 Q(ATSPI_STATE_CHECKED),
1406 Q(ATSPI_STATE_COLLAPSED),
1407 Q(ATSPI_STATE_DEFUNCT),
1408 Q(ATSPI_STATE_EDITABLE),
1409 Q(ATSPI_STATE_ENABLED),
1410 Q(ATSPI_STATE_EXPANDABLE),
1411 Q(ATSPI_STATE_EXPANDED),
1412 Q(ATSPI_STATE_FOCUSABLE),
1413 Q(ATSPI_STATE_FOCUSED),
1414 Q(ATSPI_STATE_HAS_TOOLTIP),
1415 Q(ATSPI_STATE_HORIZONTAL),
1416 Q(ATSPI_STATE_ICONIFIED),
1417 Q(ATSPI_STATE_MODAL),
1418 Q(ATSPI_STATE_MULTI_LINE),
1419 Q(ATSPI_STATE_MULTISELECTABLE),
1420 Q(ATSPI_STATE_OPAQUE),
1421 Q(ATSPI_STATE_PRESSED),
1422 Q(ATSPI_STATE_RESIZABLE),
1423 Q(ATSPI_STATE_SELECTABLE),
1424 Q(ATSPI_STATE_SELECTED),
1425 Q(ATSPI_STATE_SENSITIVE),
1426 Q(ATSPI_STATE_SHOWING),
1427 Q(ATSPI_STATE_SINGLE_LINE),
1428 Q(ATSPI_STATE_STALE),
1429 Q(ATSPI_STATE_TRANSIENT),
1430 Q(ATSPI_STATE_VERTICAL),
1431 Q(ATSPI_STATE_VISIBLE),
1432 Q(ATSPI_STATE_MANAGES_DESCENDANTS),
1433 Q(ATSPI_STATE_INDETERMINATE),
1434 Q(ATSPI_STATE_REQUIRED),
1435 Q(ATSPI_STATE_TRUNCATED),
1436 Q(ATSPI_STATE_ANIMATED),
1437 Q(ATSPI_STATE_INVALID_ENTRY),
1438 Q(ATSPI_STATE_SUPPORTS_AUTOCOMPLETION),
1439 Q(ATSPI_STATE_SELECTABLE_TEXT),
1440 Q(ATSPI_STATE_IS_DEFAULT),
1441 Q(ATSPI_STATE_VISITED),
1442 Q(ATSPI_STATE_CHECKABLE),
1443 Q(ATSPI_STATE_HAS_POPUP),
1444 Q(ATSPI_STATE_READ_ONLY),
1445 Q(ATSPI_STATE_HIGHLIGHTED),
1446 Q(ATSPI_STATE_HIGHLIGHTABLE),
1447 Q(ATSPI_STATE_LAST_DEFINED),
1450 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1451 variables[pair.first.substr(6)] = pair.second;
1455 void BatchExecutor::insertCollectionConstants()
1457 for (auto pair : std::initializer_list<std::pair<std::string, int>> {
1458 #define Q(a) { #a, a }
1459 Q(ATSPI_Collection_MATCH_ALL),
1460 Q(ATSPI_Collection_MATCH_ANY),
1461 Q(ATSPI_Collection_MATCH_NONE),
1462 Q(ATSPI_Collection_MATCH_EMPTY),
1464 ASSERT(pair.first.substr(0, 6 + 11) == "ATSPI_Collection_");
1465 variables[pair.first.substr(6 + 11)] = pair.second;
1468 void BatchExecutor::insertRoleConstants()
1470 // from at-spi2-core v. 2.16.0
1471 for (auto pair : std::initializer_list<std::pair<std::string, int>> {
1472 #define Q(a) { #a, a }
1473 Q(ATSPI_ROLE_INVALID),
1474 Q(ATSPI_ROLE_ACCELERATOR_LABEL),
1475 Q(ATSPI_ROLE_ALERT),
1476 Q(ATSPI_ROLE_ANIMATION),
1477 Q(ATSPI_ROLE_ARROW),
1478 Q(ATSPI_ROLE_CALENDAR),
1479 Q(ATSPI_ROLE_CANVAS),
1480 Q(ATSPI_ROLE_CHECK_BOX),
1481 Q(ATSPI_ROLE_CHECK_MENU_ITEM),
1482 Q(ATSPI_ROLE_COLOR_CHOOSER),
1483 Q(ATSPI_ROLE_COLUMN_HEADER),
1484 Q(ATSPI_ROLE_COMBO_BOX),
1485 Q(ATSPI_ROLE_DATE_EDITOR),
1486 Q(ATSPI_ROLE_DESKTOP_ICON),
1487 Q(ATSPI_ROLE_DESKTOP_FRAME),
1489 Q(ATSPI_ROLE_DIALOG),
1490 Q(ATSPI_ROLE_DIRECTORY_PANE),
1491 Q(ATSPI_ROLE_DRAWING_AREA),
1492 Q(ATSPI_ROLE_FILE_CHOOSER),
1493 Q(ATSPI_ROLE_FILLER),
1494 Q(ATSPI_ROLE_FOCUS_TRAVERSABLE),
1495 Q(ATSPI_ROLE_FONT_CHOOSER),
1496 Q(ATSPI_ROLE_FRAME),
1497 Q(ATSPI_ROLE_GLASS_PANE),
1498 Q(ATSPI_ROLE_HTML_CONTAINER),
1500 Q(ATSPI_ROLE_IMAGE),
1501 Q(ATSPI_ROLE_INTERNAL_FRAME),
1502 Q(ATSPI_ROLE_LABEL),
1503 Q(ATSPI_ROLE_LAYERED_PANE),
1505 Q(ATSPI_ROLE_LIST_ITEM),
1507 Q(ATSPI_ROLE_MENU_BAR),
1508 Q(ATSPI_ROLE_MENU_ITEM),
1509 Q(ATSPI_ROLE_OPTION_PANE),
1510 Q(ATSPI_ROLE_PAGE_TAB),
1511 Q(ATSPI_ROLE_PAGE_TAB_LIST),
1512 Q(ATSPI_ROLE_PANEL),
1513 Q(ATSPI_ROLE_PASSWORD_TEXT),
1514 Q(ATSPI_ROLE_POPUP_MENU),
1515 Q(ATSPI_ROLE_PROGRESS_BAR),
1516 Q(ATSPI_ROLE_PUSH_BUTTON),
1517 Q(ATSPI_ROLE_RADIO_BUTTON),
1518 Q(ATSPI_ROLE_RADIO_MENU_ITEM),
1519 Q(ATSPI_ROLE_ROOT_PANE),
1520 Q(ATSPI_ROLE_ROW_HEADER),
1521 Q(ATSPI_ROLE_SCROLL_BAR),
1522 Q(ATSPI_ROLE_SCROLL_PANE),
1523 Q(ATSPI_ROLE_SEPARATOR),
1524 Q(ATSPI_ROLE_SLIDER),
1525 Q(ATSPI_ROLE_SPIN_BUTTON),
1526 Q(ATSPI_ROLE_SPLIT_PANE),
1527 Q(ATSPI_ROLE_STATUS_BAR),
1528 Q(ATSPI_ROLE_TABLE),
1529 Q(ATSPI_ROLE_TABLE_CELL),
1530 Q(ATSPI_ROLE_TABLE_COLUMN_HEADER),
1531 Q(ATSPI_ROLE_TABLE_ROW_HEADER),
1532 Q(ATSPI_ROLE_TEAROFF_MENU_ITEM),
1533 Q(ATSPI_ROLE_TERMINAL),
1535 Q(ATSPI_ROLE_TOGGLE_BUTTON),
1536 Q(ATSPI_ROLE_TOOL_BAR),
1537 Q(ATSPI_ROLE_TOOL_TIP),
1539 Q(ATSPI_ROLE_TREE_TABLE),
1540 Q(ATSPI_ROLE_UNKNOWN),
1541 Q(ATSPI_ROLE_VIEWPORT),
1542 Q(ATSPI_ROLE_WINDOW),
1543 Q(ATSPI_ROLE_EXTENDED),
1544 Q(ATSPI_ROLE_HEADER),
1545 Q(ATSPI_ROLE_FOOTER),
1546 Q(ATSPI_ROLE_PARAGRAPH),
1547 Q(ATSPI_ROLE_RULER),
1548 Q(ATSPI_ROLE_APPLICATION),
1549 Q(ATSPI_ROLE_AUTOCOMPLETE),
1550 Q(ATSPI_ROLE_EDITBAR),
1551 Q(ATSPI_ROLE_EMBEDDED),
1552 Q(ATSPI_ROLE_ENTRY),
1553 Q(ATSPI_ROLE_CHART),
1554 Q(ATSPI_ROLE_CAPTION),
1555 Q(ATSPI_ROLE_DOCUMENT_FRAME),
1556 Q(ATSPI_ROLE_HEADING),
1558 Q(ATSPI_ROLE_SECTION),
1559 Q(ATSPI_ROLE_REDUNDANT_OBJECT),
1562 Q(ATSPI_ROLE_INPUT_METHOD_WINDOW),
1563 Q(ATSPI_ROLE_TABLE_ROW),
1564 Q(ATSPI_ROLE_TREE_ITEM),
1565 Q(ATSPI_ROLE_DOCUMENT_SPREADSHEET),
1566 Q(ATSPI_ROLE_DOCUMENT_PRESENTATION),
1567 Q(ATSPI_ROLE_DOCUMENT_TEXT),
1568 Q(ATSPI_ROLE_DOCUMENT_WEB),
1569 Q(ATSPI_ROLE_DOCUMENT_EMAIL),
1570 Q(ATSPI_ROLE_COMMENT),
1571 Q(ATSPI_ROLE_LIST_BOX),
1572 Q(ATSPI_ROLE_GROUPING),
1573 Q(ATSPI_ROLE_IMAGE_MAP),
1574 Q(ATSPI_ROLE_NOTIFICATION),
1575 Q(ATSPI_ROLE_INFO_BAR),
1576 Q(ATSPI_ROLE_LEVEL_BAR),
1577 Q(ATSPI_ROLE_TITLE_BAR),
1578 Q(ATSPI_ROLE_BLOCK_QUOTE),
1579 Q(ATSPI_ROLE_AUDIO),
1580 Q(ATSPI_ROLE_VIDEO),
1581 Q(ATSPI_ROLE_DEFINITION),
1582 Q(ATSPI_ROLE_ARTICLE),
1583 Q(ATSPI_ROLE_LANDMARK),
1585 Q(ATSPI_ROLE_MARQUEE),
1587 Q(ATSPI_ROLE_RATING),
1588 Q(ATSPI_ROLE_TIMER),
1589 Q(ATSPI_ROLE_STATIC),
1590 Q(ATSPI_ROLE_MATH_FRACTION),
1591 Q(ATSPI_ROLE_MATH_ROOT),
1592 Q(ATSPI_ROLE_SUBSCRIPT),
1593 Q(ATSPI_ROLE_SUPERSCRIPT),
1594 Q(ATSPI_ROLE_LAST_DEFINED),
1596 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1597 variables[pair.first.substr(6)] = pair.second;
1601 static void threadFunc(StatPtr result, std::unique_ptr<BatchExecutor> exec, std::unique_ptr<std::ostream> outputPtr, Dlog dlog)
1603 EvaluationContext ec(*exec);
1604 exec->outputStream() << "waiting for context change...\n";
1605 auto until = std::chrono::high_resolution_clock::now() + std::chrono::seconds{ 2 };
1606 bool hasContext = false;
1608 auto h = exec->contextInfo.lock();
1609 hasContext = h.waitForCondition(until, [&]() {
1610 // h->root is pointer to root of visible at-spi hierarchy
1611 // if it's null, then either context building machinery didn't yet finish,
1612 // there's nothing visible to show or something went wrong
1613 // here we wait for it to be non-null (context finished constructing)
1614 // if it timeouts - something went wrong, so we bail out with an error
1615 return bool(h->root);
1619 auto ttsDlogHandler = dlog.registerCallback([&exec](const std::string & txt) {
1620 auto h = exec->ttsInfo.lock();
1621 if (!h->stack.empty()) {
1622 auto &w = h->stack.back();
1623 if (!w.searchLine) {
1626 if (txt.find("tts_add_text(") != std::string::npos) {
1627 static const auto prefix = std::string{ "Text is valid - text is '" };
1628 static const auto postfix = std::string{ "'" };
1630 auto z = txt.find(prefix);
1631 if (z != std::string::npos) {
1633 auto z2 = txt.rfind(postfix);
1634 if (z2 != std::string::npos && z2 > z) {
1635 auto sub = txt.substr(z, z2 - z);
1636 exec->outputStream() << "TTS: '" << sub << "'\n";
1637 if ((w.exact && sub == *w.searchLine) || (!w.exact && sub.find(*w.searchLine) != std::string::npos)) {
1645 h->stack.pop_back();
1648 auto dlogHandler = dlog.registerCallback([&exec](const std::string & txt) {
1649 auto h = exec->dlogInfo.lock();
1650 if (!h->stack.empty()) {
1651 auto &w = h->stack.back();
1652 if (txt.find(w.searchLine) != std::string::npos) {
1654 h->stack.pop_back();
1658 exec->outputStream() << "evaluation started\n";
1661 exec->outputStream() << "evaluation successed\n";
1662 } catch (ReturnValue &r) {
1663 exec->outputStream() << "script returned: " << r.getResult();
1664 } catch (ContinueValue) {
1665 exec->outputStream() << "unhandled continue statement";
1666 } catch (BreakValue &r) {
1667 exec->outputStream() << "unhandled break statement";
1668 } catch (EvaluationFailure &e) {
1669 if (e.hasLocation())
1670 exec->outputStream() << e.location().toString() << ": ";
1671 exec->outputStream() << e.message() << "\nevaluation failed\n";
1673 exec->outputStream() << "unhandled exception\nevaluation failed\n";
1676 exec->outputStream() << "timeouted\n";
1677 DEBUG("timeouted, when waiting for context change");
1680 executeOnMainThread([]() {
1681 DEBUG("main_loop_quit");
1682 ecore_main_loop_quit();
1684 DEBUG("done batch");
1687 Optional<std::thread> runBatch(const std::array<Optional<std::string>, (size_t)utils::Argument::_count> &arguments)
1689 const std::string &sourceName = *arguments[static_cast<size_t>(utils::Argument::SourcePath)];
1691 std::unique_ptr<std::ostream> outputPtr;
1692 auto outputPath = arguments[static_cast<size_t>(utils::Argument::OutputPath)] ?
1693 *arguments[static_cast<size_t>(utils::Argument::OutputPath)] : "/dev/null";
1694 outputPtr = std::make_unique<std::ofstream>(outputPath,
1695 std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
1697 if (!static_cast<std::ofstream *>(outputPtr.get())->is_open()) {
1698 ERROR("failed to open output file '%s'", outputPath.c_str());
1701 auto &output = *outputPtr;
1702 auto exec = std::make_unique<BatchExecutor>(*outputPtr);
1704 DEBUG("running batch file: %s", sourceName.c_str());
1705 auto content = exec->getFileContent(sourceName);
1708 auto tokens = lexTest(error, sourceName, content);
1709 if (!error.empty()) {
1710 output << error << "\nlexing failed\n";
1714 std::vector<std::string> errors;
1715 auto result = parseTokens(errors, tokens);
1717 if (!errors.empty() || !result) {
1718 for (auto &e : errors) {
1719 output << e << "\n";
1721 output << "parsing failed\n";
1726 if (!dlog.start()) {
1727 output << "launching dlogutil failed\n";
1731 output << "executing test '" << sourceName << "'\n";
1732 if (arguments[static_cast<size_t>(utils::Argument::WriteDebug)]) {
1733 auto &arg = *arguments[static_cast<size_t>(utils::Argument::WriteDebug)];
1734 if (!arg.empty() && arg != "0" && arg != "no" && arg != "n" && arg != "nie") {
1735 auto ptr = std::make_unique<StreamDebugEvaluatorInterface>(*outputPtr);
1736 DebugEvaluatorInterface::setCurrentInterface(std::move(ptr));
1739 DebugEvaluator{} << "Parsed source code:";
1740 result->printSelfInfo(0);
1741 return std::thread { threadFunc, std::move(result), std::move(exec), std::move(outputPtr), std::move(dlog) };