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);
231 updateContextInfo(nav->getCurrentVisibleRoot(), nav->getCurrentKeyboardVisibleRoot(), nav->getCurrentNavigationContext());
232 contextChangedHandle = nav->registerCb<NavigationCallbackType::ContextChanged>(std::move(updateContextInfo));
235 EvaluationValue BatchExecutor::getVariableByName(const std::string &name)
237 auto it = variables.find(name);
238 if (it != variables.end())
240 throw EvaluationFailure{} << "unknown variable '" << name << "'";
243 std::shared_ptr<UIElement> BatchExecutor::getVisibleRoot()
245 auto h = contextInfo.lock();
249 std::shared_ptr<UIElement> BatchExecutor::getKeyboardRoot()
251 auto h = contextInfo.lock();
252 return h->keyboardRoot;
255 std::ostream &BatchExecutor::outputStream()
260 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(Point pt)
262 return executeOnMainThread([&]() {
263 auto root = getVisibleRoot();
264 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
265 auto found = atspi->getAtPoint(pt, Atspi::CoordType::Screen, root->getObject());
266 if (!found) throw EvaluationFailure{} << "no at-spi object found at position <" <<
267 pt.x << ", " << pt.y << ">";
268 return std::make_shared<UIElement>(std::move(found), pt, root->getApplicationCategory());
272 std::unordered_map<std::string, std::string> BatchExecutor::getUIElementAttributes(const std::shared_ptr<UIElement> &uiElem)
274 return executeOnMainThread([&]() {
275 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
276 auto found = atspi->getAttributes(uiElem->getObject());
277 std::unordered_map<std::string, std::string> res;
278 for (auto &it : found)
279 res.insert(std::move(it));
280 return std::move(res);
284 unsigned int BatchExecutor::getUIElementRole(const std::shared_ptr<UIElement> &uiElem)
286 return executeOnMainThread([&]() {
287 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
288 auto found = atspi->getRole(uiElem->getObject());
290 return static_cast<unsigned int>(*found);
291 throw EvaluationFailure{} << "failed to get at-spi object's role (use dlogutil to get at-spi error message)";
295 Atspi::StateSet BatchExecutor::getUIElementStates(const std::shared_ptr<UIElement> &uiElem)
297 return executeOnMainThread([&]() {
298 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
299 auto states = atspi->getStateSet(uiElem->getObject());
301 return std::move(*states);
303 throw EvaluationFailure{} << "failed to get at-spi state set (use dlogutil to get at-spi error message)";
307 Rectangle BatchExecutor::getUIElementPosition(const std::shared_ptr<UIElement> &uiElem)
309 return executeOnMainThread([&]() {
310 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
311 auto component = atspi->getComponentInterface(uiElem->getObject());
313 auto found = atspi->getScreenPosition(component);
315 return std::move(*found);
317 throw EvaluationFailure{} << "failed to get at-spi object's position (use dlogutil to get at-spi error message)";
321 std::string BatchExecutor::getUIElementName(const std::shared_ptr<UIElement> &uiElem)
323 return executeOnMainThread([&]() {
324 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
325 auto found = atspi->getName(uiElem->getObject());
327 return std::move(*found);
328 throw EvaluationFailure{} << "failed to get at-spi object's name (use dlogutil to get at-spi error message)";
332 std::string BatchExecutor::getUIElementUniqueId(const std::shared_ptr<UIElement> &uiElem)
334 return executeOnMainThread([&]() {
335 return Atspi::getUniqueId(uiElem->getObject());
339 void BatchExecutor::findByName(const std::vector<AtspiAccessiblePtr> &elems, std::string requestedName, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback)
342 std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback;
343 std::string requestedName;
344 std::vector<AtspiAccessiblePtr> foundElements;
345 Optional<DBus::Error> error;
349 if (error) callback(*error);
350 else callback(std::move(foundElements));
353 auto exec = std::make_shared<Exec>();
354 exec->callback = std::move(callback);
355 exec->requestedName = std::move(requestedName);
356 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
357 for (auto &e : elems) {
358 atspi->getName(e, [e, exec](DBus::ValueOrError<std::string> name) {
361 exec->error = name.getError();
363 if (std::get<0>(name) == exec->requestedName) {
364 exec->foundElements.push_back(e);
371 void BatchExecutor::getAllObjects(AtspiAccessiblePtr root, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback,
372 const std::vector<int> &roles, int roleMode, const std::vector<int> &states, int stateMode)
374 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
375 auto col = atspi->getCollectionInterface(root);
377 callback(DBus::Error{ "root '" + Atspi::getUniqueId(root) + "' doesn't have collection interface" });
379 auto m = Atspi::Matcher();
380 if (!states.empty() && stateMode < 0) stateMode = ATSPI_Collection_MATCH_ALL;
381 if (stateMode >= 0) m.states(states.begin(), states.end(), static_cast<AtspiCollectionMatchType>(stateMode));
383 if (!roles.empty() && roleMode < 0) roleMode = ATSPI_Collection_MATCH_ANY;
384 if (roleMode >= 0) m.roles(roles.begin(), roles.end(), static_cast<AtspiCollectionMatchType>(roleMode));
385 atspi->getMatchedElements(col, ATSPI_Collection_SORT_ORDER_CANONICAL, 0,
386 m, false, std::move(callback));
390 void BatchExecutor::makeUIElement(AtspiAccessiblePtr src, std::function<void(DBus::ValueOrError<std::shared_ptr<UIElement>>)> callback)
392 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
393 atspi->getComponentInterface(src, [src, callback = std::move(callback)](DBus::ValueOrError<AtspiComponentPtr> comp) {
394 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
396 callback(comp.getError());
399 atspi->getScreenPosition(std::get<0>(comp), [src, callback = std::move(callback)](DBus::ValueOrError<Rectangle> pos) {
401 callback(pos.getError());
404 callback(std::make_shared<UIElement>(src, std::get<0>(pos).getCenterPoint(), UIElement::ApplicationCategory::OTHER));
409 void BatchExecutor::makeUIElements(std::vector<AtspiAccessiblePtr> sources,
410 std::function<void(DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>>)> callback)
413 std::function<void(DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>>)> callback;
414 std::vector<std::shared_ptr<UIElement>> results;
415 Optional<DBus::Error> error;
422 for (auto e : results)
423 ASSERT(e && e->getObject());
424 callback(std::move(results));
428 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
429 auto exec = std::make_shared<Exec>();
430 exec->callback = std::move(callback);
431 exec->results.resize(sources.size());
432 for (auto i = 0u; i < sources.size(); ++i) {
433 makeUIElement(sources[i], [i, exec](DBus::ValueOrError<std::shared_ptr<UIElement>> ui) {
436 exec->error = std::move(ui.getError());
440 exec->results[i] = std::move(std::get<0>(ui));
446 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(const std::string &requestedName)
449 getVisibleRoot(), getKeyboardRoot()
451 if (root && root->getObject()) {
452 Monitor<BatchValueOrError<std::vector<std::shared_ptr<UIElement>>>> monitor;
453 auto r = executeOnMainThread([&]() {
454 getAllObjects(root->getObject(), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> allElements) {
455 findByName(std::get<0>(allElements), requestedName, wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
456 auto &elems = std::get<0>(elements);
457 makeUIElements(std::move(elems), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>> uiElems) {
458 auto h = monitor.lock();
459 h->setValue(std::move(std::get<0>(uiElems)));
466 std::ostringstream names;
467 for (auto &elem : r) names << " " << Atspi::getUniqueId(elem->getObject());
468 throw EvaluationFailure{} << "found more, than one at-spi object with name '" <<
469 requestedName << "' (see" << names.str() << ")";
471 return std::move(r[0]);
475 throw EvaluationFailure{} << "found no at-spi object with name '" << requestedName << "'";
478 std::string BatchExecutor::getFileContent(const std::string &filename)
480 auto source = std::ifstream{ filename, std::ios_base::in };
481 if (!source.is_open())
482 throw EvaluationFailure{} << "failed to open file '" << filename << "'\n";
484 source.seekg(0, std::ios_base::end);
485 auto total = source.tellg();
487 std::vector<char> bufor;
489 source.read(bufor.data(), total);
490 if ((size_t) source.gcount() != total)
491 throw EvaluationFailure{} << "error while reading file '" << filename << "'\n";
493 return std::string(bufor.data(), (size_t)total);
496 //TODO: when switching to c++17 we should use std::filename::path
497 std::string getPathRelativeToFile(const std::string &relativePath, const std::string &file)
499 if (!relativePath.empty() && relativePath[0] == '/') {
500 //not a relative path
503 auto pos = file.rfind('/');
504 if (pos == std::string::npos) {
507 return file.substr(0, pos + 1) + relativePath;
510 std::string errorToStr(int err)
512 std::ostringstream tmp;
516 if (first) first = false; \
521 Q(TIZEN_ERROR_NOT_PERMITTED)
522 Q(TIZEN_ERROR_NO_SUCH_FILE)
523 Q(TIZEN_ERROR_NO_SUCH_PROCESS)
524 Q(TIZEN_ERROR_INTERRUPTED_SYS_CALL)
525 Q(TIZEN_ERROR_IO_ERROR)
526 Q(TIZEN_ERROR_NO_SUCH_DEVICE)
527 Q(TIZEN_ERROR_ARGUMENT_LIST_TOO_LONG)
528 Q(TIZEN_ERROR_EXEC_FORMAT_ERROR)
529 Q(TIZEN_ERROR_BAD_FILE_NUMBER)
530 Q(TIZEN_ERROR_TRY_AGAIN)
531 Q(TIZEN_ERROR_OUT_OF_MEMORY)
532 Q(TIZEN_ERROR_PERMISSION_DENIED)
533 Q(TIZEN_ERROR_BAD_ADDRESS)
534 Q(TIZEN_ERROR_BLOCK_DEVICE_REQUIRED)
535 Q(TIZEN_ERROR_RESOURCE_BUSY)
536 Q(TIZEN_ERROR_FILE_EXISTS)
537 Q(TIZEN_ERROR_CROSS_DEVICE_LINK)
538 Q(TIZEN_ERROR_NOT_A_DIRECTORY)
539 Q(TIZEN_ERROR_IS_A_DIRECTORY)
540 Q(TIZEN_ERROR_INVALID_PARAMETER)
541 Q(TIZEN_ERROR_FILE_TABLE_OVERFLOW)
542 Q(TIZEN_ERROR_TOO_MANY_OPEN_FILES)
543 Q(TIZEN_ERROR_TOO_NOT_A_TERMINAL)
544 Q(TIZEN_ERROR_TOO_TEXT_FILE_BUSY)
545 Q(TIZEN_ERROR_FILE_TOO_LARGE)
546 Q(TIZEN_ERROR_FILE_NO_SPACE_ON_DEVICE)
547 Q(TIZEN_ERROR_ILLEGAL_SEEK)
548 Q(TIZEN_ERROR_READ_ONLY_FILESYSTEM)
549 Q(TIZEN_ERROR_NO_DATA)
550 Q(TIZEN_ERROR_TOO_MANY_LINKS)
551 Q(TIZEN_ERROR_BROKEN_PIPE)
552 Q(TIZEN_ERROR_ARGUMENT_OUT_OF_DOMAIN)
553 Q(TIZEN_ERROR_RESULT_OUT_OF_RANGE)
554 Q(TIZEN_ERROR_WOULD_CAUSE_DEADLOCK)
555 Q(TIZEN_ERROR_FILE_NAME_TOO_LONG)
556 Q(TIZEN_ERROR_FILE_NO_LOCKS_AVAILABLE)
557 Q(TIZEN_ERROR_INVALID_OPERATION)
558 Q(TIZEN_ERROR_DIR_NOT_EMPTY)
559 Q(TIZEN_ERROR_TOO_MANY_SYMBOLIC_LINKS)
560 Q(TIZEN_ERROR_WOULD_BLOCK)
561 Q(TIZEN_ERROR_CORRUPTED_SHARED_LIB)
562 Q(TIZEN_ERROR_LIB_SECTION_CORRUPTED)
563 Q(TIZEN_ERROR_LINK_TOO_MANY_SHARED_LIB)
564 Q(TIZEN_ERROR_SHARED_LIB_EXEC)
565 Q(TIZEN_ERROR_ILLEGAL_BYTE_SEQ)
566 Q(TIZEN_ERROR_SYSTEM_CALL_RESTART)
567 Q(TIZEN_ERROR_STREAMS_PIPE)
568 Q(TIZEN_ERROR_TOO_MANY_USERS)
569 Q(TIZEN_ERROR_NON_SOCKET)
570 Q(TIZEN_ERROR_NO_DEST_ADDRESS)
571 Q(TIZEN_ERROR_MSG_TOO_LONG)
572 Q(TIZEN_ERROR_PROTOCOL_WRONG_TYPE)
573 Q(TIZEN_ERROR_PROTOCOL_NOT_AVALIABLE)
574 Q(TIZEN_ERROR_PROTOCOL_NOT_SUPPORTED)
575 Q(TIZEN_ERROR_SOCKET_TYPE_NOT_SUPPORTED)
576 Q(TIZEN_ERROR_ENDPOINT_OPERATIN_NOT_SUPPORTED)
577 Q(TIZEN_ERROR_PROTOCOL_FAMILY_NOT_SUPPORTED)
578 Q(TIZEN_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED)
579 Q(TIZEN_ERROR_ADDRES_IN_USE)
580 Q(TIZEN_ERROR_CANNOT_ASSIGN_ADDRESS)
581 Q(TIZEN_ERROR_NETWORK_DOWN)
582 Q(TIZEN_ERROR_NETWORK_UNREACHABLE)
583 Q(TIZEN_ERROR_NETWORK_RESET)
584 Q(TIZEN_ERROR_CONNECTION_ABORTED)
585 Q(TIZEN_ERROR_CONNECTION_RESET_BY_PEER)
586 Q(TIZEN_ERROR_BUFFER_SPACE)
587 Q(TIZEN_ERROR_ENDPOINT_CONNECTED)
588 Q(TIZEN_ERROR_ENDPOINT_NOT_CONNECTED)
589 Q(TIZEN_ERROR_ENDPOINT_SHUTDOWN)
590 Q(TIZEN_ERROR_TOO_MANY_REFERENCES)
591 Q(TIZEN_ERROR_CONNECTION_TIME_OUT)
592 Q(TIZEN_ERROR_CONNECTION_REFUSED)
593 Q(TIZEN_ERROR_HOST_DOWN)
594 Q(TIZEN_ERROR_NO_ROUTE_TO_HOST)
595 Q(TIZEN_ERROR_ALREADY_IN_PROGRESS)
596 Q(TIZEN_ERROR_NOW_IN_PROGRESS)
597 Q(TIZEN_ERROR_STALE_NFS_FILE_HANDLE)
598 Q(TIZEN_ERROR_STRUCTURE_UNCLEAN)
599 Q(TIZEN_ERROR_NOT_XENIX_NAMED_TYPE_FILE)
600 Q(TIZEN_ERROR_NO_XENIX_SEMAPHORES_AVAILABLE)
601 Q(TIZEN_ERROR_IS_NAMED_TYPE_FILE)
602 Q(TIZEN_ERROR_REMOTE_IO)
603 Q(TIZEN_ERROR_QUOTA_EXCEEDED)
604 Q(TIZEN_ERROR_NO_MEDIUM)
605 Q(TIZEN_ERROR_WRONG_MEDIUM_TYPE)
606 Q(TIZEN_ERROR_CANCELED)
607 Q(TIZEN_ERROR_KEY_NOT_AVAILABLE)
608 Q(TIZEN_ERROR_KEY_EXPIRED)
609 Q(TIZEN_ERROR_KEY_REVOKED)
610 Q(TIZEN_ERROR_KEY_REJECTED)
611 Q(TIZEN_ERROR_OWNER_DEAD)
612 Q(TIZEN_ERROR_UNKNOWN)
613 Q(TIZEN_ERROR_TIMED_OUT)
614 Q(TIZEN_ERROR_NOT_SUPPORTED)
615 Q(TIZEN_ERROR_USER_NOT_CONSENTED)
616 Q(TIZEN_ERROR_DEVICE_POLICY_RESTRICTION)
617 Q(TIZEN_ERROR_END_OF_COLLECTION)
622 void BatchExecutor::killApplication(const std::pair<std::string, std::string> &appInstance)
624 auto &appid = appInstance.first;
625 auto &instance_id = appInstance.second;
626 app_context_h context = NULL;
629 if (appid == "org.tizen.tts-engine-default-sr")
632 output << "killing application " << appInstance.first << (appid.empty() ? "" : ":") << instance_id << "\n";
633 if (!instance_id.empty()) {
634 ret = app_manager_get_app_context_by_instance_id(appid.c_str(), instance_id.c_str(), &context);
636 bool running = false;
637 ret = app_manager_is_running(appid.c_str(), &running);
638 if (ret != APP_MANAGER_ERROR_NONE) {
639 throw EvaluationFailure{} << "fail to app_manager_is_running[" << ret << "]: " << get_error_message(ret);
645 ret = app_manager_get_app_context(appid.c_str(), &context);
648 if (ret != APP_MANAGER_ERROR_NONE) {
649 throw EvaluationFailure{} << "fail to app_manager_get_app_contextfailed[" << ret << "]: " << get_error_message(ret);
652 ret = app_manager_terminate_app(context);
653 app_context_destroy(context);
655 if (ret != APP_MANAGER_ERROR_NONE) {
656 throw EvaluationFailure{} << "fail to terminate_app '" << appid << ":" << instance_id << "'" <<
657 "[" << ret << "]: " << get_error_message(ret);
661 std::pair<std::string, std::string> BatchExecutor::getApplicationInfo(app_info_h app_info, struct rua_rec *record)
667 char *appid = const_cast<char *>("");
668 char *instanceid = const_cast<char *>("");
670 int ret = app_info_get_app_id(app_info, &appid);
671 if (ret != APP_MANAGER_ERROR_NONE) {
672 throw EvaluationFailure{} << "app_info_get_app_id failed[" << ret << "]: " << get_error_message(ret);
675 if (record->instance_id) {
676 instanceid = record->instance_id;
678 return { appid, instanceid };
681 using ApplicationInfoIterCbDataType =
682 std::tuple<struct rua_rec *, std::vector<std::pair<std::string, std::string>>, BatchExecutor *, std::string>;
684 bool BatchExecutor::applicationInfoIterCb(app_info_h app_info, void *user_data)
686 auto &data = *(ApplicationInfoIterCbDataType *)user_data;
687 return (std::get<2>(data))->applicationInfoIterCbImpl(app_info, user_data);
690 bool BatchExecutor::applicationInfoIterCbImpl(app_info_h app_info, void *user_data)
692 auto &data = *(ApplicationInfoIterCbDataType *)user_data;
694 struct rua_rec *record = std::get<0>(data);
697 std::get<1>(data).push_back(std::get<2>(data)->getApplicationInfo(app_info, record));
698 } catch (EvaluationFailure &ev) {
699 std::get<3>(data) = ev.message();
705 void BatchExecutor::clearApplications()
707 char **table = nullptr;
708 int rows = 0, cols = 0;
709 struct rua_rec record;
710 app_info_filter_h filter = nullptr;
713 throw EvaluationFailure{} << "rua_init failed";
716 DefferedCall rua_init_cb{ [&]()
721 if (rua_history_load_db(&table, &rows, &cols) || !table) {
722 throw EvaluationFailure{} << "rua_history_load_db failed";
724 DefferedCall rua_history_load_db_cb{ [&]()
726 rua_history_unload_db(&table);
729 int ret = app_info_filter_create(&filter);
730 if (ret != APP_MANAGER_ERROR_NONE) {
731 throw EvaluationFailure{} << "filter_create failed";
733 DefferedCall app_info_filter_create_cb{ [&]()
735 app_info_filter_destroy(filter);
738 ret = app_info_filter_add_bool(filter, PACKAGE_INFO_PROP_APP_TASKMANAGE, true);
739 if (ret != APP_MANAGER_ERROR_NONE) {
740 throw EvaluationFailure{} << "app_info_filer_add_bool failed[" << ret << "]: " << get_error_message(ret);
743 auto data = ApplicationInfoIterCbDataType{ nullptr, {}, this, "" };
745 for (auto row = 0; row < rows; ++row) {
746 rua_history_get_rec(&record, table, rows, cols, row);
747 std::get<0>(data) = &record;
749 int ret = app_info_filter_add_string(filter, PACKAGE_INFO_PROP_APP_ID, record.pkg_name);
750 if (ret != APP_MANAGER_ERROR_NONE) {
751 throw EvaluationFailure{} << "app_info_filter_add_string failed[" << ret << "]: " << get_error_message(ret);
754 ret = app_info_filter_foreach_appinfo(filter, applicationInfoIterCb, &data);
755 if (ret != APP_MANAGER_ERROR_NONE) {
756 throw EvaluationFailure{} << "app_info_filter_foreach_app_info failed[" << ret << "]: " << get_error_message(ret);
758 if (!std::get<3>(data).empty()) {
759 throw EvaluationFailure{} << std::get<3>(data);
763 for (auto &r : std::get<1>(data)) {
768 void BatchExecutor::insertMethods()
770 variables["get_batch_file"] = [&]() -> EvaluationValue {
771 // this function is called through Evaluator.cpp:CallEvaluator::evaluate()
772 // which pushes it's own location to path stack, so path stack's top will be valid here.
773 return EvaluationContext::getCurrentEvaluationContext().topBatchPath();
776 variables["import"] = EvaluationValueFunction{ [&](std::string filename) -> EvaluationValue {
779 auto currentRdlFile = EvaluationContext::getCurrentEvaluationContext().topBatchPath();
780 auto filenameFullPath = getPathRelativeToFile(filename, currentRdlFile);
781 auto it = imports.insert({ filenameFullPath, {} });
784 result = it.first->second;
786 throw EvaluationFailure{} << "nested import found in " << filenameFullPath;
792 auto content = getFileContent(filenameFullPath);
794 auto tokens = lexTest(error, filenameFullPath, content);
797 throw EvaluationFailure{} << "Lexing failed\n" << error;
799 std::vector<std::string> errors;
800 result = parseTokens(errors, tokens);
804 auto error = EvaluationFailure{};
805 error << "Parsing failed\n";
806 for (auto &e : errors) {
809 throw std::move(error);
812 CallOnExit _tmp{ [&]()
814 it.first->second = std::move(result);
819 } catch (ReturnValue)
823 throw EvaluationFailure{} << "break used on global scope in file " << filename;
824 } catch (ContinueValue)
826 throw EvaluationFailure{} << "continue used on global scope in file " << filename;
829 }, { {"filename"} } };
831 variables["sleep"] = EvaluationValueFunction{ [&](double seconds) -> EvaluationValue {
834 auto sleepTime = std::chrono::milliseconds{ static_cast<int>(std::floor(1000.0 * seconds + 0.5)) };
835 std::this_thread::sleep_for(sleepTime);
838 }, { {"seconds"} } };
840 variables["print"] = EvaluationValueFunction{ [&](EvaluationValue text) -> EvaluationValue {
841 DebugEvaluator{} << text << "\n";
845 variables["range"] = EvaluationValueFunction{ [&](EvaluationValue min, EvaluationValue max, EvaluationValue step) -> EvaluationValue {
846 if (max.isEmpty() && step.isEmpty())
852 auto mn = min.convertToInteger();
853 auto mx = max.convertToInteger();
854 if (step.isEmpty()) step = mn <= mx ? 1 : -1;
855 auto st = step.convertToInteger();
856 struct Iterator : public EvaluationValueIteratorInterface {
857 EvaluationValueInteger min, max, step;
859 Iterator(EvaluationValueInteger min, EvaluationValueInteger max, EvaluationValueInteger step) :
860 min(min), max(max), step(step) { }
862 Optional<EvaluationValue> get() const override {
863 if ((step > 0 && min < max) || (step < 0 && min > max)) return min;
866 void next() override {
867 if ((step > 0 && min < max) || (step < 0 && min > max)) min += step;
870 return EvaluationValueBase::create(std::make_shared<Iterator>(mn, mx, st));
871 }, { {"min"}, { "max", EvaluationValue{} }, { "step", EvaluationValue{} } } };
873 variables["str"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
874 std::ostringstream tmp;
879 variables["is_point_on_screen"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
880 auto dims = executeOnMainThread([&]()
882 return Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions();
884 return dims.contains(point);
887 variables["len"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
888 if (e.isString()) return static_cast<int>(e.asString().size());
889 if (e.isVector()) return static_cast<int>(e.asVector().size());
890 if (e.isSet()) return static_cast<int>(e.asSet().size());
891 if (e.isDict()) return static_cast<int>(e.asDict().size());
892 throw EvaluationFailure{} << "value of type " << e.typeName() << " doesn't have size property";
895 variables["get_at_point"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
896 return convertToUIElement(point);
899 variables["assert"] = EvaluationValueFunction{ [&](bool condition) -> EvaluationValue {
900 if (!condition) throw EvaluationFailure{} << "assertion failed";
902 }, { {"condition"} } };
904 variables["clear_applications"] = EvaluationValueFunction{
905 [&]() -> EvaluationValue {
910 variables["find_by_criteria"] = EvaluationValueFunction{
911 [&](std::vector<int> roles, int roleMode, std::vector<int> states, int stateMode) -> EvaluationValue {
912 auto root = getVisibleRoot();
913 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
914 ASSERT(root->getObject());
915 Monitor<BatchValueOrError<bool>> monitor;
916 std::vector<EvaluationValue> result;
918 executeOnMainThread([&]()
920 getAllObjects(root->getObject(), wrap(monitor, [ =, result = &result ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
921 auto &e = std::get<0>(elements);
923 auto h = monitor.lock();
926 result->reserve(e.size());
928 makeUIElement(f, wrap(monitor, [ = ](DBus::ValueOrError<std::shared_ptr<UIElement>> elem) {
929 result->push_back(std::move(std::get<0>(elem)));
930 if (result->size() == result->capacity()) {
931 auto h = monitor.lock();
937 }), roles, roleMode, states, stateMode);
939 return std::move(result);
940 }, { { "roles", EvaluationValueSet() }, { "roleMode", -1 },
941 { "states", EvaluationValueSet() }, { "stateMode", -1 }
944 variables["find_by_name"] = EvaluationValueFunction{ [&](std::string name) -> EvaluationValue {
945 for (auto root : { getVisibleRoot(), getKeyboardRoot() })
947 if (root && root->getObject()) {
948 Monitor<BatchValueOrError<std::vector<std::shared_ptr<UIElement>>>> monitor;
949 auto r = executeOnMainThread([&]() {
950 getAllObjects(root->getObject(), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> allElements) {
951 findByName(std::get<0>(allElements), name, wrap(monitor, [ = ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
952 auto &elems = std::get<0>(elements);
953 makeUIElements(std::move(elems), wrap(monitor, [ = ](DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>> uiElems) {
954 auto h = monitor.lock();
955 h->setValue(std::move(std::get<0>(uiElems)));
960 if (!r.empty()) return std::move(r);
963 return std::vector<std::shared_ptr<UIElement>>{};
966 auto generateTapFunction = [&](size_t tapCount) {
967 Optional<EvaluationValue> defValue;
969 defValue = EvaluationValue{};
970 return EvaluationValueFunction{ [ &, tapCount](EvaluationValue target, EvaluationValue fingers) -> EvaluationValue {
971 auto root = getVisibleRoot();
972 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
973 ASSERT(root->getObject());
975 const auto fingerCount = fingers.convertToInteger();
976 if (fingerCount <= 0 || fingerCount > 3)
977 throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
979 auto coord = Point{ 300, 300 };
980 if (!target.isEmpty())
982 if (target.isString()) {
983 target = this->convertToUIElement(target.convertToString());
985 auto dest = target.convertToUIElement();
986 coord = getUIElementPosition(dest).getCenterPoint();
990 throw EvaluationFailure{} << "no target value passed (you need target to do single tap)";
993 auto sleep_until = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds{ 400 };
994 for (auto i = 0u; i < tapCount; ++i)
996 if (i > 0) std::this_thread::sleep_for(std::chrono::milliseconds{ 100 });
997 executeOnMainThread([&]() {
998 auto res = utils::generateTapGesture(coord.x, coord.y, 0.0f, fingerCount);
1000 throw EvaluationFailure{} << "failed to execute " << tapCount << " tap gesture, " << res.getError().message;
1003 std::this_thread::sleep_until(sleep_until);
1005 return EvaluationValue{};
1006 }, { { "target", defValue }, { "fingers", 1 } } };
1008 variables["TAP"] = generateTapFunction(1);
1009 variables["DOUBLE_TAP"] = generateTapFunction(2);
1010 variables["TRIPLE_TAP"] = generateTapFunction(3);
1012 auto generateFlickFunction = [&](int x0, int y0, int x1, int y1) {
1013 return EvaluationValueFunction{ [ &, x0, y0, x1, y1](EvaluationValue fingers) -> EvaluationValue {
1014 auto root = getVisibleRoot();
1015 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
1016 ASSERT(root->getObject());
1018 auto fingerCount = fingers.convertToInteger();
1019 if (fingerCount <= 0 || fingerCount > 3)
1020 throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
1022 executeOnMainThread([&]()
1024 utils::generateDragGesture(x0, y0, x1, y1, 20, 0.0, fingerCount);
1026 std::this_thread::sleep_for(std::chrono::milliseconds{ 600 });
1027 return EvaluationValue{};
1028 }, { { "fingers", 1 } } };
1030 variables["FLICK_RIGHT"] = generateFlickFunction(200, 200, 400, 200);
1031 variables["FLICK_LEFT"] = generateFlickFunction(400, 200, 200, 200);
1032 variables["FLICK_UP"] = generateFlickFunction(300, 600, 300, 200);
1033 variables["FLICK_DOWN"] = generateFlickFunction(300, 200, 300, 600);
1036 class PredicateWaitInterface : public EvaluationValueWaitInterface
1039 PredicateWaitInterface(BatchExecutor *executor, std::chrono::milliseconds timeout)
1040 : self(executor), delay(timeout)
1043 void join() override
1045 timeout = std::chrono::high_resolution_clock::now() + delay;
1050 virtual void joinImpl() = 0;
1052 BatchExecutor *self;
1053 std::chrono::high_resolution_clock::time_point timeout;
1054 std::chrono::milliseconds delay;
1057 class WaitDlog : public PredicateWaitInterface
1060 WaitDlog(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern)
1061 : PredicateWaitInterface(executor, timeout)
1066 void prepare() override
1068 auto h = self->dlogInfo.lock();
1069 h->stack.push_back({ std::move(pattern) });
1070 h->stack.back().found = &found;
1072 void joinImpl() override
1074 auto h = self->dlogInfo.lock();
1079 assert(!h->stack.empty());
1080 assert(h->stack.back().found == &found);
1081 h->stack.pop_back();
1085 auto res = h.waitForCondition(timeout, [&]() {
1090 throw EvaluationFailure{} << "wait for dlog ('" << pattern << "'): operation timeouted";
1093 std::string pattern;
1097 class WaitTTS : public PredicateWaitInterface
1100 WaitTTS(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern, bool exact)
1101 : PredicateWaitInterface(executor, timeout), exact(exact)
1103 this->pattern = pattern;
1107 void prepare() override
1109 auto h = self->ttsInfo.lock();
1110 h->stack.push_back({});
1111 auto &w = h->stack.back();
1112 if (!pattern.empty())
1113 w.searchLine = pattern;
1117 void joinImpl() override
1119 auto h = self->ttsInfo.lock();
1124 assert(!h->stack.empty());
1125 assert(h->stack.back().found == &found);
1126 h->stack.pop_back();
1130 auto res = h.waitForCondition(timeout, [&]() {
1135 throw EvaluationFailure{} << "wait for tts ('" << pattern << "'): operation timeouted";
1138 std::string pattern;
1139 bool exact = false, found = false;
1142 class WaitGui : public PredicateWaitInterface
1145 WaitGui(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string name)
1146 : PredicateWaitInterface(executor, timeout), name(std::move(name))
1148 auto h = self->contextInfo.lock();
1149 this->currentContextName = h->rootName;
1152 void prepare() override
1154 auto h = self->contextInfo.lock();
1155 currentContextName = h->rootName;
1157 void joinImpl() override
1159 auto h = self->contextInfo.lock();
1161 if (currentContextName != h->rootName) {
1162 self->outputStream() << "context name changed from '" << currentContextName << "', to '" << h->rootName << "'\n";
1163 currentContextName = h->rootName;
1168 return !name.empty() && name == h->rootName;
1170 auto res = h.waitForCondition(timeout, pred);
1173 throw EvaluationFailure{} << "wait for gui: operation timeouted, context change never came";
1174 throw EvaluationFailure{} << "wait for gui ('" << name << "'): operation timeouted, " <<
1175 "current root name is '" << h->rootName << "'";
1179 std::string name, currentContextName;
1182 void BatchExecutor::insertWaits()
1184 auto dlogTTS = [&](std::string pattern, double timeout) -> EvaluationValue {
1185 auto impl = std::make_shared<WaitDlog>(this, std::chrono::milliseconds{ static_cast<size_t>(timeout * 1000) }, std::move(pattern));
1186 return EvaluationValue{ impl };
1188 variables["dlog"] = EvaluationValueFunction{ std::move(dlogTTS), { { "pattern" }, { "timeout", 5.0 } } };
1190 auto waitTTS = [&](std::string pattern, double timeout, bool exact) -> EvaluationValue {
1191 auto impl = std::make_shared<WaitTTS>(this, std::chrono::milliseconds{ static_cast<size_t>(timeout * 1000) }, std::move(pattern), exact);
1192 return EvaluationValue{ impl };
1194 variables["tts"] = EvaluationValueFunction{ std::move(waitTTS), { { "pattern", "" }, { "timeout", 5.0 }, { "exact", true } } };
1196 auto waitGui = [&](std::string name, double timeout) -> EvaluationValue {
1197 auto impl = std::make_shared<WaitGui>(this, std::chrono::milliseconds{ static_cast<size_t>(timeout * 1000) }, std::move(name));
1198 return EvaluationValue{ impl };
1200 variables["gui"] = EvaluationValueFunction{ std::move(waitGui), { { "name", "" }, { "timeout", 5.0 } } };
1203 void BatchExecutor::callActivity(const std::string &activityName, const EvaluationValueFunction::Args &args)
1205 auto activity = executeOnMainThread([&]() {
1206 return ActivityFactory::getInstance()->createActivity(activityName);
1209 throw EvaluationFailure{} << "failed to construct '" << activityName << "' activity";
1210 auto numOfArgs = activity->getRequiredNumberOfArgumentsIfAllowedInBatchProcessing();
1212 throw EvaluationFailure{} << "activity '" << activityName << "' is not supported";
1213 if (*numOfArgs != args.size())
1214 throw EvaluationFailure{} << "invalid number of arguments for activity '" << activityName <<
1215 "', got " << args.size() << ", expected " << *numOfArgs;
1216 auto uiActivity = std::dynamic_pointer_cast<UIActivity>(activity);
1218 // activity must inherit from UIActivity if it returns non zero expected arguments
1219 ASSERT(args.empty() || uiActivity);
1221 std::vector<std::shared_ptr<UIElement>> uiArgs(args.size());
1222 for (size_t i = 0; i < args.size(); ++i) {
1224 uiArgs[i] = args[i].convertToUIElement();
1226 throw EvaluationFailure{} << "can't convert argument " << (i + 1) <<
1227 " of kind " << args[i].typeName() << " to UIElement";
1229 Monitor<BatchValueOrError<bool>> monitor;
1231 executeOnMainThread([&]() {
1232 DEBUG("calling activity %s", activityName.c_str());
1233 for (auto &arg : uiArgs) {
1235 uiActivity->update(std::move(arg));
1237 activity->process(DoneCallback{ [ = ] {
1238 auto h = monitor.lock();
1241 DEBUG("calling activity %s done", activityName.c_str());
1243 if (!activity->isCompleted())
1244 throw EvaluationFailure{} << "activity '" << activityName << "' is not marked as completed!";
1245 auto h = monitor.lock();
1246 ASSERT(*h); // sanity check, must be set, otherwise an exception was thrown
1250 void BatchExecutor::insertActivities()
1252 for (auto activityName : ActivityFactory::getInstance()->getAllActivityTypes()) {
1253 if (variables.find(activityName) != variables.end())
1255 variables[activityName] = [ = ](EvaluationValueFunction::Args args) -> EvaluationValue {
1256 callActivity(activityName, args);
1262 void BatchExecutor::insertStateConstants()
1264 // from at-spi2-core v. 2.16.0
1265 for (auto pair : std::initializer_list<std::pair<std::string, int>> {
1266 #define Q(a) { #a, a }
1267 Q(ATSPI_STATE_INVALID),
1268 Q(ATSPI_STATE_ACTIVE),
1269 Q(ATSPI_STATE_ARMED),
1270 Q(ATSPI_STATE_BUSY),
1271 Q(ATSPI_STATE_CHECKED),
1272 Q(ATSPI_STATE_COLLAPSED),
1273 Q(ATSPI_STATE_DEFUNCT),
1274 Q(ATSPI_STATE_EDITABLE),
1275 Q(ATSPI_STATE_ENABLED),
1276 Q(ATSPI_STATE_EXPANDABLE),
1277 Q(ATSPI_STATE_EXPANDED),
1278 Q(ATSPI_STATE_FOCUSABLE),
1279 Q(ATSPI_STATE_FOCUSED),
1280 Q(ATSPI_STATE_HAS_TOOLTIP),
1281 Q(ATSPI_STATE_HORIZONTAL),
1282 Q(ATSPI_STATE_ICONIFIED),
1283 Q(ATSPI_STATE_MODAL),
1284 Q(ATSPI_STATE_MULTI_LINE),
1285 Q(ATSPI_STATE_MULTISELECTABLE),
1286 Q(ATSPI_STATE_OPAQUE),
1287 Q(ATSPI_STATE_PRESSED),
1288 Q(ATSPI_STATE_RESIZABLE),
1289 Q(ATSPI_STATE_SELECTABLE),
1290 Q(ATSPI_STATE_SELECTED),
1291 Q(ATSPI_STATE_SENSITIVE),
1292 Q(ATSPI_STATE_SHOWING),
1293 Q(ATSPI_STATE_SINGLE_LINE),
1294 Q(ATSPI_STATE_STALE),
1295 Q(ATSPI_STATE_TRANSIENT),
1296 Q(ATSPI_STATE_VERTICAL),
1297 Q(ATSPI_STATE_VISIBLE),
1298 Q(ATSPI_STATE_MANAGES_DESCENDANTS),
1299 Q(ATSPI_STATE_INDETERMINATE),
1300 Q(ATSPI_STATE_REQUIRED),
1301 Q(ATSPI_STATE_TRUNCATED),
1302 Q(ATSPI_STATE_ANIMATED),
1303 Q(ATSPI_STATE_INVALID_ENTRY),
1304 Q(ATSPI_STATE_SUPPORTS_AUTOCOMPLETION),
1305 Q(ATSPI_STATE_SELECTABLE_TEXT),
1306 Q(ATSPI_STATE_IS_DEFAULT),
1307 Q(ATSPI_STATE_VISITED),
1308 Q(ATSPI_STATE_CHECKABLE),
1309 Q(ATSPI_STATE_HAS_POPUP),
1310 Q(ATSPI_STATE_READ_ONLY),
1311 Q(ATSPI_STATE_HIGHLIGHTED),
1312 Q(ATSPI_STATE_HIGHLIGHTABLE),
1313 Q(ATSPI_STATE_LAST_DEFINED),
1316 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1317 variables[pair.first.substr(6)] = pair.second;
1321 void BatchExecutor::insertCollectionConstants()
1323 for (auto pair : std::initializer_list<std::pair<std::string, int>> {
1324 #define Q(a) { #a, a }
1325 Q(ATSPI_Collection_MATCH_ALL),
1326 Q(ATSPI_Collection_MATCH_ANY),
1327 Q(ATSPI_Collection_MATCH_NONE),
1328 Q(ATSPI_Collection_MATCH_EMPTY),
1330 ASSERT(pair.first.substr(0, 6 + 11) == "ATSPI_Collection_");
1331 variables[pair.first.substr(6 + 11)] = pair.second;
1334 void BatchExecutor::insertRoleConstants()
1336 // from at-spi2-core v. 2.16.0
1337 for (auto pair : std::initializer_list<std::pair<std::string, int>> {
1338 #define Q(a) { #a, a }
1339 Q(ATSPI_ROLE_INVALID),
1340 Q(ATSPI_ROLE_ACCELERATOR_LABEL),
1341 Q(ATSPI_ROLE_ALERT),
1342 Q(ATSPI_ROLE_ANIMATION),
1343 Q(ATSPI_ROLE_ARROW),
1344 Q(ATSPI_ROLE_CALENDAR),
1345 Q(ATSPI_ROLE_CANVAS),
1346 Q(ATSPI_ROLE_CHECK_BOX),
1347 Q(ATSPI_ROLE_CHECK_MENU_ITEM),
1348 Q(ATSPI_ROLE_COLOR_CHOOSER),
1349 Q(ATSPI_ROLE_COLUMN_HEADER),
1350 Q(ATSPI_ROLE_COMBO_BOX),
1351 Q(ATSPI_ROLE_DATE_EDITOR),
1352 Q(ATSPI_ROLE_DESKTOP_ICON),
1353 Q(ATSPI_ROLE_DESKTOP_FRAME),
1355 Q(ATSPI_ROLE_DIALOG),
1356 Q(ATSPI_ROLE_DIRECTORY_PANE),
1357 Q(ATSPI_ROLE_DRAWING_AREA),
1358 Q(ATSPI_ROLE_FILE_CHOOSER),
1359 Q(ATSPI_ROLE_FILLER),
1360 Q(ATSPI_ROLE_FOCUS_TRAVERSABLE),
1361 Q(ATSPI_ROLE_FONT_CHOOSER),
1362 Q(ATSPI_ROLE_FRAME),
1363 Q(ATSPI_ROLE_GLASS_PANE),
1364 Q(ATSPI_ROLE_HTML_CONTAINER),
1366 Q(ATSPI_ROLE_IMAGE),
1367 Q(ATSPI_ROLE_INTERNAL_FRAME),
1368 Q(ATSPI_ROLE_LABEL),
1369 Q(ATSPI_ROLE_LAYERED_PANE),
1371 Q(ATSPI_ROLE_LIST_ITEM),
1373 Q(ATSPI_ROLE_MENU_BAR),
1374 Q(ATSPI_ROLE_MENU_ITEM),
1375 Q(ATSPI_ROLE_OPTION_PANE),
1376 Q(ATSPI_ROLE_PAGE_TAB),
1377 Q(ATSPI_ROLE_PAGE_TAB_LIST),
1378 Q(ATSPI_ROLE_PANEL),
1379 Q(ATSPI_ROLE_PASSWORD_TEXT),
1380 Q(ATSPI_ROLE_POPUP_MENU),
1381 Q(ATSPI_ROLE_PROGRESS_BAR),
1382 Q(ATSPI_ROLE_PUSH_BUTTON),
1383 Q(ATSPI_ROLE_RADIO_BUTTON),
1384 Q(ATSPI_ROLE_RADIO_MENU_ITEM),
1385 Q(ATSPI_ROLE_ROOT_PANE),
1386 Q(ATSPI_ROLE_ROW_HEADER),
1387 Q(ATSPI_ROLE_SCROLL_BAR),
1388 Q(ATSPI_ROLE_SCROLL_PANE),
1389 Q(ATSPI_ROLE_SEPARATOR),
1390 Q(ATSPI_ROLE_SLIDER),
1391 Q(ATSPI_ROLE_SPIN_BUTTON),
1392 Q(ATSPI_ROLE_SPLIT_PANE),
1393 Q(ATSPI_ROLE_STATUS_BAR),
1394 Q(ATSPI_ROLE_TABLE),
1395 Q(ATSPI_ROLE_TABLE_CELL),
1396 Q(ATSPI_ROLE_TABLE_COLUMN_HEADER),
1397 Q(ATSPI_ROLE_TABLE_ROW_HEADER),
1398 Q(ATSPI_ROLE_TEAROFF_MENU_ITEM),
1399 Q(ATSPI_ROLE_TERMINAL),
1401 Q(ATSPI_ROLE_TOGGLE_BUTTON),
1402 Q(ATSPI_ROLE_TOOL_BAR),
1403 Q(ATSPI_ROLE_TOOL_TIP),
1405 Q(ATSPI_ROLE_TREE_TABLE),
1406 Q(ATSPI_ROLE_UNKNOWN),
1407 Q(ATSPI_ROLE_VIEWPORT),
1408 Q(ATSPI_ROLE_WINDOW),
1409 Q(ATSPI_ROLE_EXTENDED),
1410 Q(ATSPI_ROLE_HEADER),
1411 Q(ATSPI_ROLE_FOOTER),
1412 Q(ATSPI_ROLE_PARAGRAPH),
1413 Q(ATSPI_ROLE_RULER),
1414 Q(ATSPI_ROLE_APPLICATION),
1415 Q(ATSPI_ROLE_AUTOCOMPLETE),
1416 Q(ATSPI_ROLE_EDITBAR),
1417 Q(ATSPI_ROLE_EMBEDDED),
1418 Q(ATSPI_ROLE_ENTRY),
1419 Q(ATSPI_ROLE_CHART),
1420 Q(ATSPI_ROLE_CAPTION),
1421 Q(ATSPI_ROLE_DOCUMENT_FRAME),
1422 Q(ATSPI_ROLE_HEADING),
1424 Q(ATSPI_ROLE_SECTION),
1425 Q(ATSPI_ROLE_REDUNDANT_OBJECT),
1428 Q(ATSPI_ROLE_INPUT_METHOD_WINDOW),
1429 Q(ATSPI_ROLE_TABLE_ROW),
1430 Q(ATSPI_ROLE_TREE_ITEM),
1431 Q(ATSPI_ROLE_DOCUMENT_SPREADSHEET),
1432 Q(ATSPI_ROLE_DOCUMENT_PRESENTATION),
1433 Q(ATSPI_ROLE_DOCUMENT_TEXT),
1434 Q(ATSPI_ROLE_DOCUMENT_WEB),
1435 Q(ATSPI_ROLE_DOCUMENT_EMAIL),
1436 Q(ATSPI_ROLE_COMMENT),
1437 Q(ATSPI_ROLE_LIST_BOX),
1438 Q(ATSPI_ROLE_GROUPING),
1439 Q(ATSPI_ROLE_IMAGE_MAP),
1440 Q(ATSPI_ROLE_NOTIFICATION),
1441 Q(ATSPI_ROLE_INFO_BAR),
1442 Q(ATSPI_ROLE_LEVEL_BAR),
1443 Q(ATSPI_ROLE_TITLE_BAR),
1444 Q(ATSPI_ROLE_BLOCK_QUOTE),
1445 Q(ATSPI_ROLE_AUDIO),
1446 Q(ATSPI_ROLE_VIDEO),
1447 Q(ATSPI_ROLE_DEFINITION),
1448 Q(ATSPI_ROLE_ARTICLE),
1449 Q(ATSPI_ROLE_LANDMARK),
1451 Q(ATSPI_ROLE_MARQUEE),
1453 Q(ATSPI_ROLE_RATING),
1454 Q(ATSPI_ROLE_TIMER),
1455 Q(ATSPI_ROLE_STATIC),
1456 Q(ATSPI_ROLE_MATH_FRACTION),
1457 Q(ATSPI_ROLE_MATH_ROOT),
1458 Q(ATSPI_ROLE_SUBSCRIPT),
1459 Q(ATSPI_ROLE_SUPERSCRIPT),
1460 Q(ATSPI_ROLE_LAST_DEFINED),
1462 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1463 variables[pair.first.substr(6)] = pair.second;
1467 static void threadFunc(StatPtr result, std::unique_ptr<BatchExecutor> exec, std::unique_ptr<std::ostream> outputPtr, Dlog dlog)
1469 EvaluationContext ec(*exec);
1470 exec->outputStream() << "waiting for context change...\n";
1471 auto until = std::chrono::high_resolution_clock::now() + std::chrono::seconds{ 2 };
1472 bool hasContext = false;
1474 auto h = exec->contextInfo.lock();
1475 hasContext = h.waitForCondition(until, [&]() {
1476 // h->root is pointer to root of visible at-spi hierarchy
1477 // if it's null, then either context building machinery didn't yet finish,
1478 // there's nothing visible to show or something went wrong
1479 // here we wait for it to be non-null (context finished constructing)
1480 // if it timeouts - something went wrong, so we bail out with an error
1481 return bool(h->root);
1485 auto ttsDlogHandler = dlog.registerCallback([&exec](const std::string & txt) {
1486 auto h = exec->ttsInfo.lock();
1487 if (!h->stack.empty()) {
1488 auto &w = h->stack.back();
1489 if (!w.searchLine) {
1492 if (txt.find("(tts_speak_customized)") != std::string::npos) {
1493 static const auto prefix = std::string{ "TTS reading text '" };
1494 static const auto postfix = std::string{ "'" };
1496 auto z = txt.find(prefix);
1497 if (z != std::string::npos) {
1499 auto z2 = txt.rfind(postfix);
1500 if (z2 != std::string::npos && z2 > z) {
1501 auto sub = txt.substr(z, z2 - z);
1502 exec->outputStream() << "TTS: '" << sub << "'\n";
1503 if ((w.exact && sub == *w.searchLine) || (!w.exact && sub.find(*w.searchLine) != std::string::npos)) {
1511 h->stack.pop_back();
1514 auto dlogHandler = dlog.registerCallback([&exec](const std::string & txt) {
1515 auto h = exec->dlogInfo.lock();
1516 if (!h->stack.empty()) {
1517 auto &w = h->stack.back();
1518 if (txt.find(w.searchLine) != std::string::npos) {
1520 h->stack.pop_back();
1524 exec->outputStream() << "evaluation started\n";
1527 exec->outputStream() << "evaluation successed\n";
1528 } catch (ReturnValue &r) {
1529 exec->outputStream() << "script returned: " << r.getResult();
1530 } catch (ContinueValue) {
1531 exec->outputStream() << "unhandled continue statement";
1532 } catch (BreakValue &r) {
1533 exec->outputStream() << "unhandled break statement";
1534 } catch (EvaluationFailure &e) {
1535 if (e.hasLocation())
1536 exec->outputStream() << e.location().toString() << ": ";
1537 exec->outputStream() << e.message() << "\nevaluation failed\n";
1539 exec->outputStream() << "unhandled exception\nevaluation failed\n";
1542 exec->outputStream() << "timeouted\n";
1543 DEBUG("timeouted, when waiting for context change");
1546 executeOnMainThread([]() {
1547 DEBUG("main_loop_quit");
1548 ecore_main_loop_quit();
1550 DEBUG("done batch");
1553 Optional<std::thread> runBatch(const std::array<Optional<std::string>, (size_t)utils::Argument::_count> &arguments)
1555 const std::string &sourceName = *arguments[static_cast<size_t>(utils::Argument::SourcePath)];
1557 std::unique_ptr<std::ostream> outputPtr;
1558 auto outputPath = arguments[static_cast<size_t>(utils::Argument::OutputPath)] ?
1559 *arguments[static_cast<size_t>(utils::Argument::OutputPath)] : "/dev/null";
1560 outputPtr = std::make_unique<std::ofstream>(outputPath,
1561 std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
1563 if (!static_cast<std::ofstream *>(outputPtr.get())->is_open()) {
1564 ERROR("failed to open output file '%s'", outputPath.c_str());
1567 auto &output = *outputPtr;
1568 auto exec = std::make_unique<BatchExecutor>(*outputPtr);
1570 DEBUG("running batch file: %s", sourceName.c_str());
1571 auto content = exec->getFileContent(sourceName);
1574 auto tokens = lexTest(error, sourceName, content);
1575 if (!error.empty()) {
1576 output << error << "\nlexing failed\n";
1580 std::vector<std::string> errors;
1581 auto result = parseTokens(errors, tokens);
1583 if (!errors.empty() || !result) {
1584 for (auto &e : errors) {
1585 output << e << "\n";
1587 output << "parsing failed\n";
1592 if (!dlog.start()) {
1593 output << "launching dlogutil failed\n";
1597 output << "executing test '" << sourceName << "'\n";
1598 if (arguments[static_cast<size_t>(utils::Argument::WriteDebug)]) {
1599 auto &arg = *arguments[static_cast<size_t>(utils::Argument::WriteDebug)];
1600 if (!arg.empty() && arg != "0" && arg != "no" && arg != "n" && arg != "nie") {
1601 auto ptr = std::make_unique<StreamDebugEvaluatorInterface>(*outputPtr);
1602 DebugEvaluatorInterface::setCurrentInterface(std::move(ptr));
1605 DebugEvaluator{} << "Parsed source code:";
1606 result->printSelfInfo(0);
1607 return std::thread { threadFunc, std::move(result), std::move(exec), std::move(outputPtr), std::move(dlog) };