Changed tts pattern which is searched in dlog
[platform/core/accessibility/universal-switch.git] / src / batch / BatchRunner.cpp
1 /*
2  * Copyright 2017  Samsung Electronics Co., Ltd
3  *
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
7  *
8  *  http://www.apache.org/licenses/LICENSE-2.0
9
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.
15  */
16
17 #include "BatchRunner.hpp"
18 #include "../UniversalSwitch.hpp"
19 #include "../Window.hpp"
20 #include "../UniversalSwitchLog.hpp"
21 #include "Lexer.hpp"
22 #include "Parser.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"
31 #include "Dlog.hpp"
32 #include "ReturnValue.hpp"
33 #include "EvaluationValueBase.hpp"
34
35 #include <mutex>
36 #include <chrono>
37 #include <thread>
38 #include <fstream>
39 #include <cmath>
40 #include <type_traits>
41 #include <app_manager.h>
42 #include <app_manager_extension.h>
43
44 namespace WrapDetails
45 {
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)
50         {
51                 return [monitor = std::move(monitor), func = std::move(func)](DBus::ValueOrError<ARGS...> val) -> void {
52                         try
53                         {
54                                 if (!val) {
55                                         auto h = monitor.lock();
56                                         h->setError(EvaluationFailure{} << val.getError().message);
57                                 } else {
58                                         func(std::move(val));
59                                 }
60                         } catch (EvaluationFailure &ev)
61                         {
62                                 auto h = monitor.lock();
63                                 h->setError(std::move(ev));
64                         } catch (std::exception &ev)
65                         {
66                                 auto h = monitor.lock();
67                                 h->setError(EvaluationFailure{} << "unhandled exception (" << ev.what() << ")");
68                         } catch (...)
69                         {
70                                 auto h = monitor.lock();
71                                 h->setError(EvaluationFailure{} << "unhandled unknown exception");
72                         }
73                 };
74         }
75
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.
88
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)
96                 {
97                         return wrapImpl(std::move(func), std::move(monitor));
98                 }
99         };
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)
106                 {
107                         return wrapImpl(std::move(func), std::move(monitor));
108                 }
109         };
110 }
111
112 template <typename MONITORED_TYPE, typename F>
113 auto wrap(Monitor<BatchValueOrError<MONITORED_TYPE>> monitor, F &&func)
114 {
115         return WrapDetails::WrapHelper<decltype(&std::remove_reference<F>::type::operator())>::
116                    call(std::forward<F>(func), std::move(monitor));
117 }
118
119 class ExecutorOnMainThread
120 {
121 public:
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)
125         {
126                 typename std::remove_reference<decltype(function())>::type ret;
127                 ExecutorOnMainThread tmp{ [&]()
128                 {
129                         ret = function();
130                 } };
131                 ecore_main_loop_thread_safe_call_sync(executeOnMainThreadCb, &tmp);
132                 ASSERT(!eina_main_loop_is());
133                 if (tmp.failure) throw *tmp.failure;
134                 return ret;
135         }
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)
139         {
140                 ExecutorOnMainThread tmp{ [&]()
141                 {
142                         function();
143                 } };
144                 ecore_main_loop_thread_safe_call_sync(executeOnMainThreadCb, &tmp);
145                 ASSERT(!eina_main_loop_is());
146                 if (tmp.failure) throw *tmp.failure;
147                 return;
148         }
149 private:
150         ExecutorOnMainThread() = delete;
151         ExecutorOnMainThread(std::function<void()> function) : func(std::move(function)) { }
152         ExecutorOnMainThread(const ExecutorOnMainThread &) = delete;
153         ExecutorOnMainThread(ExecutorOnMainThread &&) = delete;
154
155         ExecutorOnMainThread &operator = (const ExecutorOnMainThread &) = delete;
156         ExecutorOnMainThread &operator = (ExecutorOnMainThread &&) = delete;
157
158         std::function<void()> func;
159         Optional<EvaluationFailure> failure;
160
161         static void *executeOnMainThreadCb(void *d)
162         {
163                 auto helper = static_cast<ExecutorOnMainThread *>(d);
164                 try {
165                         helper->func();
166                 } catch (EvaluationFailure &e) {
167                         helper->failure = std::move(e);
168                 } catch (std::exception &e) {
169                         helper->failure = EvaluationFailure{} << "unhandled expection (" << e.what() << ")";
170                 } catch (...) {
171                         helper->failure = EvaluationFailure{} << "unhandled expection";
172                 }
173                 return nullptr;
174         }
175 };
176
177 template <typename T> auto executeOnMainThread(T &&fnc)
178 {
179         return ExecutorOnMainThread::execute(std::forward<T>(fnc));
180 }
181
182 template <typename T, typename MONITORED_TYPE> MONITORED_TYPE executeOnMainThread(T &&fnc,
183                 Monitor<BatchValueOrError<MONITORED_TYPE>> monitor,
184                 std::chrono::microseconds timeout = 3s)
185 {
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, [&]() {
190                 return bool(*h);
191         });
192         if (!success)
193                 throw EvaluationFailure{} << "evaluation timeouted";
194         ASSERT(*h);
195         return h->takeValue();
196 }
197
198 BatchExecutor::BatchExecutor(std::ostream &output) : output(output)
199 {
200         insertRoleConstants();
201         insertStateConstants();
202         insertCollectionConstants();
203         insertMethods();
204         insertWaits();
205         insertActivities();
206         registerContextChangeCallback();
207 }
208
209 void BatchExecutor::registerContextChangeCallback()
210 {
211         auto nav = Singleton<UniversalSwitch>::instance().getNavigationInterface();
212         if (!nav) {
213                 DEBUG("no navigation interface found, context changed callback not registered");
214                 return;
215         }
216         auto updateContextInfo = [this](std::shared_ptr<UIElement> root, std::shared_ptr<UIElement> keyboardRoot, std::shared_ptr<NavigationElement> navigationContext) {
217                 std::string name;
218                 if (root && root->getObject()) {
219                         auto r = Singleton<UniversalSwitch>::instance().getAtspi()->getName(root->getObject());
220                         if (r)
221                                 name = std::move(*r);
222                 }
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()) : "";
231         };
232         updateContextInfo(nav->getCurrentVisibleRoot(), nav->getCurrentKeyboardVisibleRoot(), nav->getCurrentNavigationContext());
233         contextChangedHandle = nav->registerCb<NavigationCallbackType::ContextChanged>(std::move(updateContextInfo));
234 }
235
236 EvaluationValue BatchExecutor::getVariableByName(const std::string &name)
237 {
238         auto it = variables.find(name);
239         if (it != variables.end())
240                 return it->second;
241         throw EvaluationFailure{} << "unknown variable '" << name << "'";
242 }
243
244 std::shared_ptr<UIElement> BatchExecutor::getVisibleRoot()
245 {
246         auto h = contextInfo.lock();
247         return h->root;
248 }
249
250 std::shared_ptr<UIElement> BatchExecutor::getKeyboardRoot()
251 {
252         auto h = contextInfo.lock();
253         return h->keyboardRoot;
254 }
255
256 std::ostream &BatchExecutor::outputStream()
257 {
258         return output;
259 }
260
261 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(Point pt)
262 {
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());
270         });
271 }
272
273 std::unordered_map<std::string, std::string> BatchExecutor::getUIElementAttributes(const std::shared_ptr<UIElement> &uiElem)
274 {
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);
282         });
283 }
284
285 unsigned int BatchExecutor::getUIElementRole(const std::shared_ptr<UIElement> &uiElem)
286 {
287         return executeOnMainThread([&]() {
288                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
289                 auto found = atspi->getRole(uiElem->getObject());
290                 if (found)
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)";
293         });
294 }
295
296 Atspi::StateSet BatchExecutor::getUIElementStates(const std::shared_ptr<UIElement> &uiElem)
297 {
298         return executeOnMainThread([&]() {
299                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
300                 auto states = atspi->getStateSet(uiElem->getObject());
301                 if (states) {
302                         return std::move(*states);
303                 }
304                 throw EvaluationFailure{} << "failed to get at-spi state set (use dlogutil to get at-spi error message)";
305         });
306 }
307
308 Rectangle BatchExecutor::getUIElementPosition(const std::shared_ptr<UIElement> &uiElem)
309 {
310         return executeOnMainThread([&]() {
311                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
312                 auto component = atspi->getComponentInterface(uiElem->getObject());
313                 if (component) {
314                         auto found = atspi->getScreenPosition(component);
315                         if (found)
316                                 return std::move(*found);
317                 }
318                 throw EvaluationFailure{} << "failed to get at-spi object's position (use dlogutil to get at-spi error message)";
319         });
320 }
321
322 std::string BatchExecutor::getUIElementName(const std::shared_ptr<UIElement> &uiElem)
323 {
324         return executeOnMainThread([&]() {
325                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
326                 auto found = atspi->getName(uiElem->getObject());
327                 if (found)
328                         return std::move(*found);
329                 throw EvaluationFailure{} << "failed to get at-spi object's name (use dlogutil to get at-spi error message)";
330         });
331 }
332
333 std::string BatchExecutor::getUIElementUniqueId(const std::shared_ptr<UIElement> &uiElem)
334 {
335         return executeOnMainThread([&]() {
336                 return Atspi::getUniqueId(uiElem->getObject());
337         });
338 }
339
340 void BatchExecutor::findByName(const std::vector<AtspiAccessiblePtr> &elems, std::string requestedName, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback)
341 {
342         struct Exec {
343                 std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback;
344                 std::string requestedName;
345                 std::vector<AtspiAccessiblePtr> foundElements;
346                 Optional<DBus::Error> error;
347
348                 ~Exec()
349                 {
350                         if (error) callback(*error);
351                         else callback(std::move(foundElements));
352                 }
353         };
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) {
360                         if (!name) {
361                                 if (!exec->error)
362                                         exec->error = name.getError();
363                         } else {
364                                 if (std::get<0>(name) == exec->requestedName) {
365                                         exec->foundElements.push_back(e);
366                                 }
367                         }
368                 });
369         }
370 }
371
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)
374 {
375         auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
376         auto col = atspi->getCollectionInterface(root);
377         if (!col) {
378                 callback(DBus::Error{ "root '" + Atspi::getUniqueId(root) + "' doesn't have collection interface" });
379         } else {
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));
383
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));
388         }
389 }
390
391 void BatchExecutor::makeUIElement(AtspiAccessiblePtr src, std::function<void(DBus::ValueOrError<std::shared_ptr<UIElement>>)> callback)
392 {
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();
396                 if (!comp) {
397                         callback(comp.getError());
398                         return;
399                 }
400                 atspi->getScreenPosition(std::get<0>(comp), [src, callback = std::move(callback)](DBus::ValueOrError<Rectangle> pos) {
401                         if (!pos) {
402                                 callback(pos.getError());
403                                 return;
404                         }
405                         callback(std::make_shared<UIElement>(src, std::get<0>(pos).getCenterPoint(), UIElement::ApplicationCategory::OTHER));
406                 });
407         });
408 }
409
410 void BatchExecutor::makeUIElements(std::vector<AtspiAccessiblePtr> sources,
411                                                                    std::function<void(DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>>)> callback)
412 {
413         struct Exec {
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;
417
418                 ~Exec()
419                 {
420                         if (error) {
421                                 callback(*error);
422                         } else {
423                                 for (auto e : results)
424                                         ASSERT(e && e->getObject());
425                                 callback(std::move(results));
426                         }
427                 }
428         };
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) {
435                         if (!ui) {
436                                 if (!exec->error)
437                                         exec->error = std::move(ui.getError());
438                                 return;
439                         }
440                         if (!exec->error) {
441                                 exec->results[i] = std::move(std::get<0>(ui));
442                         }
443                 });
444         }
445 }
446
447 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(const std::string &requestedName)
448 {
449         for (auto root : {
450                                 getVisibleRoot(), getKeyboardRoot()
451                         }) {
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)));
461                                                 }));
462                                         }));
463                                 }));
464                         }, monitor);
465                         if (!r.empty()) {
466                                 if (r.size() > 1) {
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() << ")";
471                                 }
472                                 return std::move(r[0]);
473                         }
474                 }
475         }
476         throw EvaluationFailure{} << "found no at-spi object with name '" << requestedName << "'";
477 }
478
479 std::string BatchExecutor::getFileContent(const std::string &filename)
480 {
481         auto source = std::ifstream{ filename, std::ios_base::in };
482         if (!source.is_open())
483                 throw EvaluationFailure{} << "failed to open file '" << filename << "'\n";
484
485         source.seekg(0, std::ios_base::end);
486         auto total = source.tellg();
487         source.seekg(0);
488         std::vector<char> bufor;
489         bufor.resize(total);
490         source.read(bufor.data(), total);
491         if ((size_t) source.gcount() != total)
492                 throw EvaluationFailure{} <<  "error while reading file '" << filename << "'\n";
493
494         return std::string(bufor.data(), (size_t)total);
495 }
496
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)
499 {
500         if (!relativePath.empty() && relativePath[0] == '/') {
501                 //not a relative path
502                 return relativePath;
503         }
504         auto pos = file.rfind('/');
505         if (pos == std::string::npos) {
506                 return relativePath;
507         }
508         return file.substr(0, pos + 1) + relativePath;
509 }
510
511 std::string errorToStr(int err)
512 {
513         std::ostringstream tmp;
514         bool first = true;
515 #define Q(a) \
516         if (a == err) { \
517                 if (first) first = false; \
518                 else tmp << ", "; \
519                 tmp << #a; \
520         }
521         Q(TIZEN_ERROR_NONE)
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)
619 #undef Q
620         return tmp.str();
621 }
622
623 void BatchExecutor::killApplication(const std::pair<std::string, std::string> &appInstance)
624 {
625         auto &appid = appInstance.first;
626         auto &instance_id = appInstance.second;
627         app_context_h context = NULL;
628         int ret;
629
630         if (appid == "org.tizen.tts-engine-default-sr")
631                 return;
632
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);
636         } else {
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);
641                 }
642
643                 if (!running) {
644                         return;
645                 }
646                 ret = app_manager_get_app_context(appid.c_str(), &context);
647         }
648
649         if (ret == APP_MANAGER_ERROR_APP_NO_RUNNING) {
650                 DebugEvaluator{} << "Application " << appInstance.first << " not found";
651                 return;
652         }
653
654         if (ret != APP_MANAGER_ERROR_NONE) {
655                 DebugEvaluator{} << "warning: fail to app_manager_get_app_context failed[" << ret << "]: " << get_error_message(ret);
656                 return;
657         }
658
659         ret = app_manager_terminate_app(context);
660         app_context_destroy(context);
661
662         if (ret != APP_MANAGER_ERROR_NONE) {
663                 throw EvaluationFailure{} << "fail to terminate_app '" << appid << ":" << instance_id << "'" <<
664                                                                   "[" << ret << "]: " << get_error_message(ret);
665         }
666 }
667
668 std::pair<std::string, std::string> BatchExecutor::getApplicationInfo(app_info_h app_info, struct rua_rec *record)
669 {
670         if (!app_info) {
671                 return {};
672         }
673
674         char *appid = const_cast<char *>("");
675         char *instanceid = const_cast<char *>("");
676
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);
680         }
681
682         if (record->instance_id) {
683                 instanceid = record->instance_id;
684         }
685         return { appid, instanceid };
686 }
687
688 using ApplicationInfoIterCbDataType =
689         std::tuple<struct rua_rec *, std::vector<std::pair<std::string, std::string>>, BatchExecutor *, std::string>;
690
691 bool BatchExecutor::applicationInfoIterCb(app_info_h app_info, void *user_data)
692 {
693         auto &data = *(ApplicationInfoIterCbDataType *)user_data;
694         return (std::get<2>(data))->applicationInfoIterCbImpl(app_info, user_data);
695 }
696
697 bool BatchExecutor::applicationInfoIterCbImpl(app_info_h app_info, void *user_data)
698 {
699         auto &data = *(ApplicationInfoIterCbDataType *)user_data;
700
701         struct rua_rec *record = std::get<0>(data);
702
703         try {
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();
707                 return true;
708         }
709         return false;
710 }
711
712 void BatchExecutor::clearApplications()
713 {
714         char **table = nullptr;
715         int rows = 0, cols = 0;
716         struct rua_rec record;
717         app_info_filter_h filter = nullptr;
718
719         if (rua_init()) {
720                 throw EvaluationFailure{} << "rua_init failed";
721         }
722
723         DefferedCall rua_init_cb{ [&]()
724         {
725                 rua_fini();
726         } };
727
728         if (rua_history_load_db(&table, &rows, &cols) || !table) {
729                 throw EvaluationFailure{} << "rua_history_load_db failed";
730         }
731         DefferedCall rua_history_load_db_cb{ [&]()
732         {
733                 rua_history_unload_db(&table);
734         } };
735
736         int ret = app_info_filter_create(&filter);
737         if (ret != APP_MANAGER_ERROR_NONE) {
738                 throw EvaluationFailure{} << "filter_create failed";
739         }
740         DefferedCall app_info_filter_create_cb{ [&]()
741         {
742                 app_info_filter_destroy(filter);
743         } };
744
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);
748         }
749
750         auto data = ApplicationInfoIterCbDataType{ nullptr, {}, this, "" };
751
752         for (auto row = 0; row < rows; ++row) {
753                 rua_history_get_rec(&record, table, rows, cols, row);
754                 std::get<0>(data) = &record;
755
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);
759                 }
760
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);
764                 }
765                 if (!std::get<3>(data).empty()) {
766                         DebugEvaluator{} << std::get<3>(data);
767                         throw EvaluationFailure{} << std::get<3>(data);
768                 }
769         }
770
771         for (auto &r : std::get<1>(data)) {
772                 killApplication(r);
773         }
774 }
775
776 void BatchExecutor::insertMethods()
777 {
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();
782         };
783
784         variables["import"] =  EvaluationValueFunction{ [&](std::string filename) -> EvaluationValue {
785
786                         StatPtr result;
787                         auto currentRdlFile = EvaluationContext::getCurrentEvaluationContext().topBatchPath();
788                         auto filenameFullPath = getPathRelativeToFile(filename, currentRdlFile);
789                         auto it = imports.insert({ filenameFullPath, {} });
790                         if (!it.second)
791                         {
792                                 result = it.first->second;
793                                 if (!result) {
794                                         throw EvaluationFailure{} << "nested import found in " << filenameFullPath;
795                                 }
796                                 result->evaluate();
797                                 return {};
798                         }
799
800                         auto content = getFileContent(filenameFullPath);
801                         std::string error;
802                         auto tokens = lexTest(error, filenameFullPath, content);
803
804                         if (!error.empty())
805                                 throw EvaluationFailure{} << "Lexing failed\n" << error;
806
807                         std::vector<std::string> errors;
808                         result = parseTokens(errors, tokens);
809
810                         if (!errors.empty())
811                         {
812                                 auto error = EvaluationFailure{};
813                                 error << "Parsing failed\n";
814                                 for (auto &e : errors) {
815                                         error << e << "\n";
816                                 }
817                                 throw std::move(error);
818                         }
819                         ASSERT(result);
820                         CallOnExit _tmp{ [&]()
821                         {
822                                 it.first->second = std::move(result);
823                         }};
824                         try
825                         {
826                                 result->evaluate();
827                         } catch (ReturnValue)
828                         {
829                         } catch (BreakValue)
830                         {
831                                 throw EvaluationFailure{} << "break used on global scope in file " << filename;
832                         } catch (ContinueValue)
833                         {
834                                 throw EvaluationFailure{} << "continue used on global scope in file " << filename;
835                         }
836                         return {};
837                 }, { {"filename"} } };
838
839         variables["sleep"] =  EvaluationValueFunction{ [&](double seconds) -> EvaluationValue {
840                         if (seconds > 0)
841                         {
842                                 auto sleepTime = std::chrono::milliseconds{ static_cast<int>(std::floor(1000.0 * seconds + 0.5)) };
843                                 std::this_thread::sleep_for(sleepTime);
844                         }
845                         return {};
846                 },  { {"seconds"} } };
847
848         variables["print"] = EvaluationValueFunction{ [&](EvaluationValue text) -> EvaluationValue {
849                         DebugEvaluator{} << text;
850                         outputStream() << text << "\n";
851                         return {};
852                 },  { {"text"} } };
853
854         variables["range"] = EvaluationValueFunction{ [&](EvaluationValue min, EvaluationValue max, EvaluationValue step) -> EvaluationValue {
855                         if (max.isEmpty() && step.isEmpty())
856                         {
857                                 max = min;
858                                 min = 0;
859                                 step = 1;
860                         }
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;
867
868                                 Iterator(EvaluationValueInteger min, EvaluationValueInteger max, EvaluationValueInteger step) :
869                                         min(min), max(max), step(step) { }
870
871                                 Optional<EvaluationValue> get() const override {
872                                         if ((step > 0 && min < max) || (step < 0 && min > max)) return min;
873                                         return {};
874                                 }
875                                 void next() override {
876                                         if ((step > 0 && min < max) || (step < 0 && min > max)) min += step;
877                                 }
878                         };
879                         return EvaluationValueBase::create(std::make_shared<Iterator>(mn, mx, st));
880                 },  { {"min"}, { "max", EvaluationValue{} }, { "step", EvaluationValue{} } } };
881
882         variables["str"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
883                         std::ostringstream tmp;
884                         tmp << e;
885                         return tmp.str();
886                 },  { {"value"} } };
887
888         variables["is_point_on_screen"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
889                         auto dims = executeOnMainThread([&]()
890                         {
891                                 return Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions();
892                         });
893                         return dims.contains(point);
894                 },  { {"point"} } };
895
896         variables["highlight"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
897                         AtspiAccessiblePtr obj;
898                         if (e.isPoint())
899                         {
900                                 auto root = getVisibleRoot();
901                                 obj = Singleton<UniversalSwitch>::instance().getAtspi()->getAtPoint(e.asPoint(), Atspi::CoordType::Screen, root->getObject());
902                         } else
903                         {
904                                 if (e.isString()) e = convertToUIElement(e.asString());
905                                 auto v = e.convertToUIElement();
906                                 obj = v->getObject();
907                         }
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)";
912                         return *res;
913                 },  { {"value"} } };
914
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";
921                 },  { {"value"} } };
922
923         variables["get_at_point"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
924                         return convertToUIElement(point);
925                 }, { {"point"} } };
926
927         variables["assert"] = EvaluationValueFunction{ [&](bool condition) -> EvaluationValue {
928                         if (!condition) throw EvaluationFailure{} << "assertion failed";
929                         return {};
930                 }, { {"condition"} } };
931
932         variables["clear_applications"] = EvaluationValueFunction{
933                 [&]() -> EvaluationValue {
934                         clearApplications();
935                         std::this_thread::sleep_for(std::chrono::milliseconds{ 1000 });
936                         return {};
937                 }, {} };
938
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;
946
947                         executeOnMainThread([&]()
948                         {
949                                 getAllObjects(root->getObject(), wrap(monitor, [ =, result = &result ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
950                                         auto &e = std::get<0>(elements);
951                                         if (e.empty()) {
952                                                 auto h = monitor.lock();
953                                                 h->setValue(true);
954                                         } else {
955                                                 result->reserve(e.size());
956                                                 for (auto &f : e) {
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();
961                                                                         h->setValue(true);
962                                                                 }
963                                                         }));
964                                                 }
965                                         }
966                                 }), roles, roleMode, states, stateMode);
967                         }, monitor);
968                         return std::move(result);
969                 }, { { "roles", EvaluationValueSet() }, { "roleMode", -1 },
970                         { "states", EvaluationValueSet() }, { "stateMode", -1 }
971                 } };
972
973         variables["find_by_name"] = EvaluationValueFunction{ [&](std::string name) -> EvaluationValue {
974                         for (auto root : { getVisibleRoot(), getKeyboardRoot() })
975                         {
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)));
985                                                                 }));
986                                                         }));
987                                                 }));
988                                         }, monitor);
989                                         if (!r.empty()) return std::move(r);
990                                 }
991                         }
992                         return std::vector<std::shared_ptr<UIElement>>{};
993                 }, { { "name" } } };
994
995         variables["launch_application"] = EvaluationValueFunction{ [&](std::string name) -> EvaluationValue {
996                         executeOnMainThread([&]()
997                         {
998                                 bool ret = utils::generateLaunchRequest(name);
999                                 if (!ret) throw EvaluationFailure{} << "failed to launch application " << name;
1000                         });
1001                         return EvaluationValue{};
1002                 }, { { "name" } } };
1003
1004         auto generateTapFunction = [&](size_t tapCount) {
1005
1006                 return EvaluationValueFunction{ [ &, tapCount](EvaluationValue target, EvaluationValue fingers) -> EvaluationValue {
1007
1008                                 auto root = getVisibleRoot();
1009                                 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
1010                                 ASSERT(root->getObject());
1011
1012                                 const auto fingerCount = fingers.convertToInteger();
1013                                 if (fingerCount <= 0 || fingerCount > 3)
1014                                         throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
1015
1016                                 auto coord = Point{ 200, 200 };
1017                                 if (!target.isEmpty())
1018                                 {
1019                                         if (target.isString()) {
1020                                                 target = this->convertToUIElement(target.convertToString());
1021                                         }
1022                                         auto dest = target.convertToUIElement();
1023                                         coord = getUIElementPosition(dest).getCenterPoint();
1024                                 } else
1025                                 {
1026                                         if (tapCount == 1 && fingers == 1)
1027                                                 throw EvaluationFailure{} << "no target value passed (you need target to do one finger single tap)";
1028                                 }
1029
1030                                 auto sleep_until = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds{ 400 };
1031                                 for (auto i = 0u; i < tapCount; ++i)
1032                                 {
1033                                         executeOnMainThread([&]() {
1034                                                 auto res = utils::generateTapGesture(coord.x, coord.y, 0.0f, fingerCount);
1035                                                 if (!res)
1036                                                         throw EvaluationFailure{} << "failed to execute " << tapCount << " tap gesture, " << res.getError().message;
1037                                         });
1038                                 }
1039                                 std::this_thread::sleep_until(sleep_until);
1040
1041                                 return EvaluationValue{};
1042                         }, { { "target", EvaluationValue{} }, { "fingers", 1 } } };
1043         };
1044         variables["TAP"] = generateTapFunction(1);
1045         variables["DOUBLE_TAP"] = generateTapFunction(2);
1046         variables["TRIPLE_TAP"] = generateTapFunction(3);
1047
1048         enum class FlickKind {
1049                 down, up, left, right
1050         };
1051         auto generateFlickFunction = [&](FlickKind kind, int steps) {
1052                 return EvaluationValueFunction{ [ &, kind, steps](EvaluationValue fingers) -> EvaluationValue {
1053                                 int dx, dy;
1054                                 auto size = executeOnMainThread([&]()
1055                                 {
1056                                         return Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions().size;
1057                                 });
1058
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;
1064
1065                                 switch (kind)
1066                                 {
1067                                 case FlickKind::right:
1068                                         dx = width;
1069                                         dy = 0;
1070                                         break;
1071                                 case FlickKind::left:
1072                                         dx = -width;
1073                                         dy = 0;
1074                                         break;
1075                                 case FlickKind::up:
1076                                         dx = 0;
1077                                         dy = -height;
1078                                         break;
1079                                 case FlickKind::down:
1080                                         dx = 0;
1081                                         dy = height;
1082                                         break;
1083                                 }
1084                                 auto root = getVisibleRoot();
1085                                 if (!root) throw EvaluationFailure{} << "no visible root (context changed didn't happen)";
1086                                 ASSERT(root->getObject());
1087
1088                                 auto fingerCount = fingers.convertToInteger();
1089                                 if (fingerCount <= 0 || fingerCount > 3)
1090                                         throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
1091
1092                                 executeOnMainThread([&]()
1093                                 {
1094                                         auto dims = Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions();
1095                                         auto mx = dims.getCenterPoint();
1096                                         auto x0 = mx.x - dx / 2;
1097                                         auto x1 = x0 + dx;
1098                                         auto y0 = mx.y - dy / 2;
1099                                         auto y1 = y0 + dy;
1100                                         utils::generateDragGesture(x0, y0, x1, y1, steps, 0.0, fingerCount);
1101                                 });
1102                                 std::this_thread::sleep_for(std::chrono::milliseconds{ 600 });
1103                                 return EvaluationValue{};
1104                         }, { { "fingers", 1 } } };
1105         };
1106
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);
1112
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);
1118
1119         auto generateWheelTurnFunction = [&]() {
1120                 return EvaluationValueFunction{ [&](bool clockwise, int multiplicity) -> EvaluationValue {
1121
1122                                 if (multiplicity <= 0)
1123                                         throw EvaluationFailure{} << "Invalid multiplicity (must be more that 0)";
1124
1125                                 auto delay = multiplicity > 1 ? 0.75 : 0;
1126
1127                                 executeOnMainThread([&]()
1128                                 {
1129                                         utils::generateWheelTurn(clockwise, multiplicity, delay);
1130                                 });
1131
1132                                 int await = 1000 * (multiplicity * delay);
1133                                 std::this_thread::sleep_for(std::chrono::milliseconds{ await });
1134
1135                                 return EvaluationValue{};
1136                         },  { {"clockwise", true}, {"multiplicity", 1} } };
1137         };
1138         variables["TURN_WHEEL"] = generateWheelTurnFunction();
1139
1140         auto generateKeyPressFunction = [&]() {
1141                 return EvaluationValueFunction{ [&](std::string keyId, unsigned multiplicity, double hold_time) -> EvaluationValue {
1142
1143                                 if (keyId == "")
1144                                         throw EvaluationFailure{} << "Invalid keyId";
1145
1146                                 if (multiplicity <= 0)
1147                                         throw EvaluationFailure{} << "Invalid multiplicity (must be more that 0)";
1148
1149                                 if (hold_time < 0)
1150                                         throw EvaluationFailure{} << "Invalid hold time (must be more or equal 0)";
1151
1152                                 executeOnMainThread([&]()
1153                                 {
1154                                         utils::generateKeyPress(keyId, multiplicity, hold_time);
1155                                 });
1156
1157                                 std::this_thread::sleep_for(std::chrono::milliseconds{ 800 });
1158
1159                                 return EvaluationValue{};
1160                         },  { {"keyId" }, {"multiplicity", 1 }, {"holdTime", 0} } };
1161         };
1162         variables["PRESS_KEY"] = generateKeyPressFunction();
1163 }
1164
1165 class PredicateWaitInterface : public EvaluationValueWaitInterface
1166 {
1167 public:
1168         PredicateWaitInterface(BatchExecutor *executor, std::chrono::milliseconds timeout)
1169                 : self(executor), delay(timeout)
1170         {}
1171
1172         void join() override
1173         {
1174                 timeout = std::chrono::high_resolution_clock::now() + delay;
1175                 joinImpl();
1176         }
1177
1178 protected:
1179         virtual void joinImpl() = 0;
1180
1181         BatchExecutor *self;
1182         std::chrono::high_resolution_clock::time_point timeout;
1183         std::chrono::milliseconds delay;
1184 };
1185
1186 class WaitDlog : public PredicateWaitInterface
1187 {
1188 public:
1189         WaitDlog(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern)
1190                 : PredicateWaitInterface(executor, timeout)
1191         {
1192         }
1193
1194 private:
1195         void prepare() override
1196         {
1197                 auto h = self->dlogInfo.lock();
1198                 h->stack.push_back({ std::move(pattern) });
1199                 h->stack.back().found = &found;
1200         }
1201         void joinImpl() override
1202         {
1203                 auto h = self->dlogInfo.lock();
1204                 CallOnExit _tmp{
1205                         [&]()
1206                         {
1207                                 if (!found) {
1208                                         assert(!h->stack.empty());
1209                                         assert(h->stack.back().found == &found);
1210                                         h->stack.pop_back();
1211                                 }
1212                         }
1213                 };
1214                 auto res = h.waitForCondition(timeout, [&]() {
1215                         return found;
1216                 });
1217
1218                 if (!res)
1219                         throw EvaluationFailure{} << "wait for dlog ('" << pattern << "'): operation timeouted";
1220         }
1221
1222         std::string pattern;
1223         bool found = false;
1224 };
1225
1226 class WaitTTS : public PredicateWaitInterface
1227 {
1228 public:
1229         WaitTTS(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern, bool exact)
1230                 : PredicateWaitInterface(executor, timeout), exact(exact)
1231         {
1232                 this->pattern = pattern;
1233         }
1234
1235 private:
1236         void prepare() override
1237         {
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;
1243                 w.exact = exact;
1244                 w.found = &found;
1245         }
1246         void joinImpl() override
1247         {
1248                 auto h = self->ttsInfo.lock();
1249                 CallOnExit _tmp{
1250                         [&]()
1251                         {
1252                                 if (!found) {
1253                                         assert(!h->stack.empty());
1254                                         assert(h->stack.back().found == &found);
1255                                         h->stack.pop_back();
1256                                 }
1257                         }
1258                 };
1259                 auto res = h.waitForCondition(timeout, [&]() {
1260                         return found;
1261                 });
1262
1263                 if (!res)
1264                         throw EvaluationFailure{} << "wait for tts ('" << pattern << "'): operation timeouted";
1265         }
1266
1267         std::string pattern;
1268         bool exact = false, found = false;
1269 };
1270
1271 class WaitGui : public PredicateWaitInterface
1272 {
1273 public:
1274         WaitGui(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string name)
1275                 : PredicateWaitInterface(executor, timeout), name(std::move(name))
1276         {
1277                 auto h = self->contextInfo.lock();
1278                 this->currentContextName = h->rootName;
1279                 this->currentContextAddress = h->rootAddress;
1280         }
1281 private:
1282         void prepare() override
1283         {
1284                 auto h = self->contextInfo.lock();
1285                 currentContextName = h->rootName;
1286                 currentContextAddress = h->rootAddress;
1287         }
1288         void joinImpl() override
1289         {
1290                 auto h = self->contextInfo.lock();
1291                 auto pred = [&]() {
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;
1298                                 if (name.empty())
1299                                         return true;
1300                         }
1301                         return !name.empty() && name == h->rootName;
1302                 };
1303                 auto res = h.waitForCondition(timeout, pred);
1304                 if (!res) {
1305                         if (name.empty())
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 << "'";
1309                 }
1310         }
1311
1312         std::string name, currentContextName;
1313         std::string currentContextAddress;
1314 };
1315
1316 void BatchExecutor::insertWaits()
1317 {
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 };
1321         };
1322         variables["dlog"] = EvaluationValueFunction{ std::move(dlogTTS), { { "pattern" }, { "timeout", 5.0 } } };
1323
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 };
1327         };
1328         variables["tts"] = EvaluationValueFunction{ std::move(waitTTS), { { "pattern", "" }, { "timeout", 5.0 }, { "exact", true } } };
1329
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 };
1333         };
1334         variables["gui"] = EvaluationValueFunction{ std::move(waitGui), { { "name", "" }, { "timeout", 5.0 } } };
1335 }
1336
1337 void BatchExecutor::callActivity(const std::string &activityName, const EvaluationValueFunction::Args &args)
1338 {
1339         auto activity = executeOnMainThread([&]() {
1340                 return ActivityFactory::getInstance()->createActivity(activityName);
1341         });
1342         if (!activity)
1343                 throw EvaluationFailure{} << "failed to construct '" << activityName << "' activity";
1344         auto numOfArgs = activity->getRequiredNumberOfArgumentsIfAllowedInBatchProcessing();
1345         if (!numOfArgs)
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);
1351
1352         // activity must inherit from UIActivity if it returns non zero expected arguments
1353         ASSERT(args.empty() || uiActivity);
1354
1355         std::vector<std::shared_ptr<UIElement>> uiArgs(args.size());
1356         for (size_t i = 0; i < args.size(); ++i) {
1357                 ASSERT(uiActivity);
1358                 uiArgs[i] = args[i].convertToUIElement();
1359                 if (!uiArgs[i])
1360                         throw EvaluationFailure{} << "can't convert argument " << (i + 1) <<
1361                                                                           " of kind " << args[i].typeName() << " to UIElement";
1362         }
1363         Monitor<BatchValueOrError<bool>> monitor;
1364         {
1365                 executeOnMainThread([&]() {
1366                         DEBUG("calling activity %s", activityName.c_str());
1367                         for (auto &arg : uiArgs) {
1368                                 ASSERT(uiActivity);
1369                                 uiActivity->update(std::move(arg));
1370                         }
1371                         activity->process(DoneCallback{ [ = ] {
1372                                         auto h = monitor.lock();
1373                                         h->setValue(true);
1374                                 } });
1375                         DEBUG("calling activity %s done", activityName.c_str());
1376                 }, monitor);
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
1381         }
1382 }
1383
1384 void BatchExecutor::insertActivities()
1385 {
1386         for (auto activityName : ActivityFactory::getInstance()->getAllActivityTypes()) {
1387                 if (variables.find(activityName) != variables.end())
1388                         continue;
1389                 variables[activityName] = [ = ](EvaluationValueFunction::Args args) -> EvaluationValue {
1390                         callActivity(activityName, args);
1391                         return {};
1392                 };
1393         }
1394 }
1395
1396 void BatchExecutor::insertStateConstants()
1397 {
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),
1448 #undef Q
1449         }) {
1450                 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1451                 variables[pair.first.substr(6)] = pair.second;
1452         }
1453 }
1454
1455 void BatchExecutor::insertCollectionConstants()
1456 {
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),
1463         }) {
1464                 ASSERT(pair.first.substr(0, 6 + 11) == "ATSPI_Collection_");
1465                 variables[pair.first.substr(6 + 11)] = pair.second;
1466         }
1467 }
1468 void BatchExecutor::insertRoleConstants()
1469 {
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),
1488                 Q(ATSPI_ROLE_DIAL),
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),
1499                 Q(ATSPI_ROLE_ICON),
1500                 Q(ATSPI_ROLE_IMAGE),
1501                 Q(ATSPI_ROLE_INTERNAL_FRAME),
1502                 Q(ATSPI_ROLE_LABEL),
1503                 Q(ATSPI_ROLE_LAYERED_PANE),
1504                 Q(ATSPI_ROLE_LIST),
1505                 Q(ATSPI_ROLE_LIST_ITEM),
1506                 Q(ATSPI_ROLE_MENU),
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),
1534                 Q(ATSPI_ROLE_TEXT),
1535                 Q(ATSPI_ROLE_TOGGLE_BUTTON),
1536                 Q(ATSPI_ROLE_TOOL_BAR),
1537                 Q(ATSPI_ROLE_TOOL_TIP),
1538                 Q(ATSPI_ROLE_TREE),
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),
1557                 Q(ATSPI_ROLE_PAGE),
1558                 Q(ATSPI_ROLE_SECTION),
1559                 Q(ATSPI_ROLE_REDUNDANT_OBJECT),
1560                 Q(ATSPI_ROLE_FORM),
1561                 Q(ATSPI_ROLE_LINK),
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),
1584                 Q(ATSPI_ROLE_LOG),
1585                 Q(ATSPI_ROLE_MARQUEE),
1586                 Q(ATSPI_ROLE_MATH),
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),
1595         }) {
1596                 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1597                 variables[pair.first.substr(6)] = pair.second;
1598         }
1599 }
1600
1601 static void threadFunc(StatPtr result, std::unique_ptr<BatchExecutor> exec, std::unique_ptr<std::ostream> outputPtr, Dlog dlog)
1602 {
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;
1607         {
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);
1616                 });
1617         }
1618         if (hasContext) {
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) {
1624                                         *w.found = true;
1625                                 } else {
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{ "'" };
1629
1630                                                 auto z = txt.find(prefix);
1631                                                 if (z != std::string::npos) {
1632                                                         z += prefix.size();
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)) {
1638                                                                         *w.found = true;
1639                                                                 }
1640                                                         }
1641                                                 }
1642                                         }
1643                                 }
1644                                 if (*w.found)
1645                                         h->stack.pop_back();
1646                         }
1647                 });
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) {
1653                                         *w.found = true;
1654                                         h->stack.pop_back();
1655                                 }
1656                         }
1657                 });
1658                 exec->outputStream() << "evaluation started\n";
1659                 try {
1660                         result->evaluate();
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";
1672                 } catch (...) {
1673                         exec->outputStream() << "unhandled exception\nevaluation failed\n";
1674                 }
1675         } else {
1676                 exec->outputStream() << "timeouted\n";
1677                 DEBUG("timeouted, when waiting for context change");
1678         }
1679         outputPtr->flush();
1680         executeOnMainThread([]() {
1681                 DEBUG("main_loop_quit");
1682                 ecore_main_loop_quit();
1683         });
1684         DEBUG("done batch");
1685 }
1686
1687 Optional<std::thread> runBatch(const std::array<Optional<std::string>, (size_t)utils::Argument::_count> &arguments)
1688 {
1689         const std::string &sourceName = *arguments[static_cast<size_t>(utils::Argument::SourcePath)];
1690
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);
1696
1697         if (!static_cast<std::ofstream *>(outputPtr.get())->is_open()) {
1698                 ERROR("failed to open output file '%s'", outputPath.c_str());
1699                 return {};
1700         }
1701         auto &output = *outputPtr;
1702         auto exec = std::make_unique<BatchExecutor>(*outputPtr);
1703
1704         DEBUG("running batch file: %s", sourceName.c_str());
1705         auto content = exec->getFileContent(sourceName);
1706
1707         std::string error;
1708         auto tokens = lexTest(error, sourceName, content);
1709         if (!error.empty()) {
1710                 output << error << "\nlexing failed\n";
1711                 return {};
1712         }
1713
1714         std::vector<std::string> errors;
1715         auto result = parseTokens(errors, tokens);
1716
1717         if (!errors.empty() || !result) {
1718                 for (auto &e : errors) {
1719                         output << e << "\n";
1720                 }
1721                 output << "parsing failed\n";
1722                 return {};
1723         }
1724
1725         Dlog dlog;
1726         if (!dlog.start()) {
1727                 output << "launching dlogutil failed\n";
1728                 return {};
1729         }
1730
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));
1737                 }
1738         }
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) };
1742 }