Slightly change flick timing's in batch mode
[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         };
231         updateContextInfo(nav->getCurrentVisibleRoot(), nav->getCurrentKeyboardVisibleRoot(), nav->getCurrentNavigationContext());
232         contextChangedHandle = nav->registerCb<NavigationCallbackType::ContextChanged>(std::move(updateContextInfo));
233 }
234
235 EvaluationValue BatchExecutor::getVariableByName(const std::string &name)
236 {
237         auto it = variables.find(name);
238         if (it != variables.end())
239                 return it->second;
240         throw EvaluationFailure{} << "unknown variable '" << name << "'";
241 }
242
243 std::shared_ptr<UIElement> BatchExecutor::getVisibleRoot()
244 {
245         auto h = contextInfo.lock();
246         return h->root;
247 }
248
249 std::shared_ptr<UIElement> BatchExecutor::getKeyboardRoot()
250 {
251         auto h = contextInfo.lock();
252         return h->keyboardRoot;
253 }
254
255 std::ostream &BatchExecutor::outputStream()
256 {
257         return output;
258 }
259
260 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(Point pt)
261 {
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());
269         });
270 }
271
272 std::unordered_map<std::string, std::string> BatchExecutor::getUIElementAttributes(const std::shared_ptr<UIElement> &uiElem)
273 {
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);
281         });
282 }
283
284 unsigned int BatchExecutor::getUIElementRole(const std::shared_ptr<UIElement> &uiElem)
285 {
286         return executeOnMainThread([&]() {
287                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
288                 auto found = atspi->getRole(uiElem->getObject());
289                 if (found)
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)";
292         });
293 }
294
295 Atspi::StateSet BatchExecutor::getUIElementStates(const std::shared_ptr<UIElement> &uiElem)
296 {
297         return executeOnMainThread([&]() {
298                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
299                 auto states = atspi->getStateSet(uiElem->getObject());
300                 if (states) {
301                         return std::move(*states);
302                 }
303                 throw EvaluationFailure{} << "failed to get at-spi state set (use dlogutil to get at-spi error message)";
304         });
305 }
306
307 Rectangle BatchExecutor::getUIElementPosition(const std::shared_ptr<UIElement> &uiElem)
308 {
309         return executeOnMainThread([&]() {
310                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
311                 auto component = atspi->getComponentInterface(uiElem->getObject());
312                 if (component) {
313                         auto found = atspi->getScreenPosition(component);
314                         if (found)
315                                 return std::move(*found);
316                 }
317                 throw EvaluationFailure{} << "failed to get at-spi object's position (use dlogutil to get at-spi error message)";
318         });
319 }
320
321 std::string BatchExecutor::getUIElementName(const std::shared_ptr<UIElement> &uiElem)
322 {
323         return executeOnMainThread([&]() {
324                 auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
325                 auto found = atspi->getName(uiElem->getObject());
326                 if (found)
327                         return std::move(*found);
328                 throw EvaluationFailure{} << "failed to get at-spi object's name (use dlogutil to get at-spi error message)";
329         });
330 }
331
332 std::string BatchExecutor::getUIElementUniqueId(const std::shared_ptr<UIElement> &uiElem)
333 {
334         return executeOnMainThread([&]() {
335                 return Atspi::getUniqueId(uiElem->getObject());
336         });
337 }
338
339 void BatchExecutor::findByName(const std::vector<AtspiAccessiblePtr> &elems, std::string requestedName, std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback)
340 {
341         struct Exec {
342                 std::function<void(DBus::ValueOrError<std::vector<AtspiAccessiblePtr>>)> callback;
343                 std::string requestedName;
344                 std::vector<AtspiAccessiblePtr> foundElements;
345                 Optional<DBus::Error> error;
346
347                 ~Exec()
348                 {
349                         if (error) callback(*error);
350                         else callback(std::move(foundElements));
351                 }
352         };
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) {
359                         if (!name) {
360                                 if (!exec->error)
361                                         exec->error = name.getError();
362                         } else {
363                                 if (std::get<0>(name) == exec->requestedName) {
364                                         exec->foundElements.push_back(e);
365                                 }
366                         }
367                 });
368         }
369 }
370
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)
373 {
374         auto atspi = Singleton<UniversalSwitch>::instance().getAtspi();
375         auto col = atspi->getCollectionInterface(root);
376         if (!col) {
377                 callback(DBus::Error{ "root '" + Atspi::getUniqueId(root) + "' doesn't have collection interface" });
378         } else {
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));
382
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));
387         }
388 }
389
390 void BatchExecutor::makeUIElement(AtspiAccessiblePtr src, std::function<void(DBus::ValueOrError<std::shared_ptr<UIElement>>)> callback)
391 {
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();
395                 if (!comp) {
396                         callback(comp.getError());
397                         return;
398                 }
399                 atspi->getScreenPosition(std::get<0>(comp), [src, callback = std::move(callback)](DBus::ValueOrError<Rectangle> pos) {
400                         if (!pos) {
401                                 callback(pos.getError());
402                                 return;
403                         }
404                         callback(std::make_shared<UIElement>(src, std::get<0>(pos).getCenterPoint(), UIElement::ApplicationCategory::OTHER));
405                 });
406         });
407 }
408
409 void BatchExecutor::makeUIElements(std::vector<AtspiAccessiblePtr> sources,
410                                                                    std::function<void(DBus::ValueOrError<std::vector<std::shared_ptr<UIElement>>>)> callback)
411 {
412         struct Exec {
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;
416
417                 ~Exec()
418                 {
419                         if (error) {
420                                 callback(*error);
421                         } else {
422                                 for (auto e : results)
423                                         ASSERT(e && e->getObject());
424                                 callback(std::move(results));
425                         }
426                 }
427         };
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) {
434                         if (!ui) {
435                                 if (!exec->error)
436                                         exec->error = std::move(ui.getError());
437                                 return;
438                         }
439                         if (!exec->error) {
440                                 exec->results[i] = std::move(std::get<0>(ui));
441                         }
442                 });
443         }
444 }
445
446 std::shared_ptr<UIElement> BatchExecutor::convertToUIElement(const std::string &requestedName)
447 {
448         for (auto root : {
449                                 getVisibleRoot(), getKeyboardRoot()
450                         }) {
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)));
460                                                 }));
461                                         }));
462                                 }));
463                         }, monitor);
464                         if (!r.empty()) {
465                                 if (r.size() > 1) {
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() << ")";
470                                 }
471                                 return std::move(r[0]);
472                         }
473                 }
474         }
475         throw EvaluationFailure{} << "found no at-spi object with name '" << requestedName << "'";
476 }
477
478 std::string BatchExecutor::getFileContent(const std::string &filename)
479 {
480         auto source = std::ifstream{ filename, std::ios_base::in };
481         if (!source.is_open())
482                 throw EvaluationFailure{} << "failed to open file '" << filename << "'\n";
483
484         source.seekg(0, std::ios_base::end);
485         auto total = source.tellg();
486         source.seekg(0);
487         std::vector<char> bufor;
488         bufor.resize(total);
489         source.read(bufor.data(), total);
490         if ((size_t) source.gcount() != total)
491                 throw EvaluationFailure{} <<  "error while reading file '" << filename << "'\n";
492
493         return std::string(bufor.data(), (size_t)total);
494 }
495
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)
498 {
499         if (!relativePath.empty() && relativePath[0] == '/') {
500                 //not a relative path
501                 return relativePath;
502         }
503         auto pos = file.rfind('/');
504         if (pos == std::string::npos) {
505                 return relativePath;
506         }
507         return file.substr(0, pos + 1) + relativePath;
508 }
509
510 std::string errorToStr(int err)
511 {
512         std::ostringstream tmp;
513         bool first = true;
514 #define Q(a) \
515         if (a == err) { \
516                 if (first) first = false; \
517                 else tmp << ", "; \
518                 tmp << #a; \
519         }
520         Q(TIZEN_ERROR_NONE)
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)
618 #undef Q
619         return tmp.str();
620 }
621
622 void BatchExecutor::killApplication(const std::pair<std::string, std::string> &appInstance)
623 {
624         auto &appid = appInstance.first;
625         auto &instance_id = appInstance.second;
626         app_context_h context = NULL;
627         int ret;
628
629         if (appid == "org.tizen.tts-engine-default-sr")
630                 return;
631
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);
635         } else {
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);
640                 }
641
642                 if (!running) {
643                         return;
644                 }
645                 ret = app_manager_get_app_context(appid.c_str(), &context);
646         }
647
648         if (ret != APP_MANAGER_ERROR_NONE) {
649                 throw EvaluationFailure{} << "fail to app_manager_get_app_contextfailed[" << ret << "]: " << get_error_message(ret);
650         }
651
652         ret = app_manager_terminate_app(context);
653         app_context_destroy(context);
654
655         if (ret != APP_MANAGER_ERROR_NONE) {
656                 throw EvaluationFailure{} << "fail to terminate_app '" << appid << ":" << instance_id << "'" <<
657                                                                   "[" << ret << "]: " << get_error_message(ret);
658         }
659 }
660
661 std::pair<std::string, std::string> BatchExecutor::getApplicationInfo(app_info_h app_info, struct rua_rec *record)
662 {
663         if (!app_info) {
664                 return {};
665         }
666
667         char *appid = const_cast<char *>("");
668         char *instanceid = const_cast<char *>("");
669
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);
673         }
674
675         if (record->instance_id) {
676                 instanceid = record->instance_id;
677         }
678         return { appid, instanceid };
679 }
680
681 using ApplicationInfoIterCbDataType =
682         std::tuple<struct rua_rec *, std::vector<std::pair<std::string, std::string>>, BatchExecutor *, std::string>;
683
684 bool BatchExecutor::applicationInfoIterCb(app_info_h app_info, void *user_data)
685 {
686         auto &data = *(ApplicationInfoIterCbDataType *)user_data;
687         return (std::get<2>(data))->applicationInfoIterCbImpl(app_info, user_data);
688 }
689
690 bool BatchExecutor::applicationInfoIterCbImpl(app_info_h app_info, void *user_data)
691 {
692         auto &data = *(ApplicationInfoIterCbDataType *)user_data;
693
694         struct rua_rec *record = std::get<0>(data);
695
696         try {
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();
700                 return true;
701         }
702         return false;
703 }
704
705 void BatchExecutor::clearApplications()
706 {
707         char **table = nullptr;
708         int rows = 0, cols = 0;
709         struct rua_rec record;
710         app_info_filter_h filter = nullptr;
711
712         if (rua_init()) {
713                 throw EvaluationFailure{} << "rua_init failed";
714         }
715
716         DefferedCall rua_init_cb{ [&]()
717         {
718                 rua_fini();
719         } };
720
721         if (rua_history_load_db(&table, &rows, &cols) || !table) {
722                 throw EvaluationFailure{} << "rua_history_load_db failed";
723         }
724         DefferedCall rua_history_load_db_cb{ [&]()
725         {
726                 rua_history_unload_db(&table);
727         } };
728
729         int ret = app_info_filter_create(&filter);
730         if (ret != APP_MANAGER_ERROR_NONE) {
731                 throw EvaluationFailure{} << "filter_create failed";
732         }
733         DefferedCall app_info_filter_create_cb{ [&]()
734         {
735                 app_info_filter_destroy(filter);
736         } };
737
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);
741         }
742
743         auto data = ApplicationInfoIterCbDataType{ nullptr, {}, this, "" };
744
745         for (auto row = 0; row < rows; ++row) {
746                 rua_history_get_rec(&record, table, rows, cols, row);
747                 std::get<0>(data) = &record;
748
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);
752                 }
753
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);
757                 }
758                 if (!std::get<3>(data).empty()) {
759                         throw EvaluationFailure{} << std::get<3>(data);
760                 }
761         }
762
763         for (auto &r : std::get<1>(data)) {
764                 killApplication(r);
765         }
766 }
767
768 void BatchExecutor::insertMethods()
769 {
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();
774         };
775
776         variables["import"] =  EvaluationValueFunction{ [&](std::string filename) -> EvaluationValue {
777
778                         StatPtr result;
779                         auto currentRdlFile = EvaluationContext::getCurrentEvaluationContext().topBatchPath();
780                         auto filenameFullPath = getPathRelativeToFile(filename, currentRdlFile);
781                         auto it = imports.insert({ filenameFullPath, {} });
782                         if (!it.second)
783                         {
784                                 result = it.first->second;
785                                 if (!result) {
786                                         throw EvaluationFailure{} << "nested import found in " << filenameFullPath;
787                                 }
788                                 result->evaluate();
789                                 return {};
790                         }
791
792                         auto content = getFileContent(filenameFullPath);
793                         std::string error;
794                         auto tokens = lexTest(error, filenameFullPath, content);
795
796                         if (!error.empty())
797                                 throw EvaluationFailure{} << "Lexing failed\n" << error;
798
799                         std::vector<std::string> errors;
800                         result = parseTokens(errors, tokens);
801
802                         if (!errors.empty())
803                         {
804                                 auto error = EvaluationFailure{};
805                                 error << "Parsing failed\n";
806                                 for (auto &e : errors) {
807                                         error << e << "\n";
808                                 }
809                                 throw std::move(error);
810                         }
811                         ASSERT(result);
812                         CallOnExit _tmp{ [&]()
813                         {
814                                 it.first->second = std::move(result);
815                         }};
816                         try
817                         {
818                                 result->evaluate();
819                         } catch (ReturnValue)
820                         {
821                         } catch (BreakValue)
822                         {
823                                 throw EvaluationFailure{} << "break used on global scope in file " << filename;
824                         } catch (ContinueValue)
825                         {
826                                 throw EvaluationFailure{} << "continue used on global scope in file " << filename;
827                         }
828                         return {};
829                 }, { {"filename"} } };
830
831         variables["sleep"] =  EvaluationValueFunction{ [&](double seconds) -> EvaluationValue {
832                         if (seconds > 0)
833                         {
834                                 auto sleepTime = std::chrono::milliseconds{ static_cast<int>(std::floor(1000.0 * seconds + 0.5)) };
835                                 std::this_thread::sleep_for(sleepTime);
836                         }
837                         return {};
838                 },  { {"seconds"} } };
839
840         variables["print"] = EvaluationValueFunction{ [&](EvaluationValue text) -> EvaluationValue {
841                         DebugEvaluator{} << text << "\n";
842                         return {};
843                 },  { {"text"} } };
844
845         variables["range"] = EvaluationValueFunction{ [&](EvaluationValue min, EvaluationValue max, EvaluationValue step) -> EvaluationValue {
846                         if (max.isEmpty() && step.isEmpty())
847                         {
848                                 max = min;
849                                 min = 0;
850                                 step = 1;
851                         }
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;
858
859                                 Iterator(EvaluationValueInteger min, EvaluationValueInteger max, EvaluationValueInteger step) :
860                                         min(min), max(max), step(step) { }
861
862                                 Optional<EvaluationValue> get() const override {
863                                         if ((step > 0 && min < max) || (step < 0 && min > max)) return min;
864                                         return {};
865                                 }
866                                 void next() override {
867                                         if ((step > 0 && min < max) || (step < 0 && min > max)) min += step;
868                                 }
869                         };
870                         return EvaluationValueBase::create(std::make_shared<Iterator>(mn, mx, st));
871                 },  { {"min"}, { "max", EvaluationValue{} }, { "step", EvaluationValue{} } } };
872
873         variables["str"] = EvaluationValueFunction{ [&](EvaluationValue e) -> EvaluationValue {
874                         std::ostringstream tmp;
875                         tmp << e;
876                         return tmp.str();
877                 },  { {"value"} } };
878
879         variables["is_point_on_screen"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
880                         auto dims = executeOnMainThread([&]()
881                         {
882                                 return Singleton<UniversalSwitch>::instance().getMainWindow()->getDimensions();
883                         });
884                         return dims.contains(point);
885                 },  { {"value"} } };
886
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";
893                 },  { {"value"} } };
894
895         variables["get_at_point"] = EvaluationValueFunction{ [&](Point point) -> EvaluationValue {
896                         return convertToUIElement(point);
897                 }, { {"point"} } };
898
899         variables["assert"] = EvaluationValueFunction{ [&](bool condition) -> EvaluationValue {
900                         if (!condition) throw EvaluationFailure{} << "assertion failed";
901                         return {};
902                 }, { {"condition"} } };
903
904         variables["clear_applications"] = EvaluationValueFunction{
905                 [&]() -> EvaluationValue {
906                         clearApplications();
907                         return {};
908                 }, {} };
909
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;
917
918                         executeOnMainThread([&]()
919                         {
920                                 getAllObjects(root->getObject(), wrap(monitor, [ =, result = &result ](DBus::ValueOrError<std::vector<AtspiAccessiblePtr>> elements) {
921                                         auto &e = std::get<0>(elements);
922                                         if (e.empty()) {
923                                                 auto h = monitor.lock();
924                                                 h->setValue(true);
925                                         } else {
926                                                 result->reserve(e.size());
927                                                 for (auto &f : e) {
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();
932                                                                         h->setValue(true);
933                                                                 }
934                                                         }));
935                                                 }
936                                         }
937                                 }), roles, roleMode, states, stateMode);
938                         }, monitor);
939                         return std::move(result);
940                 }, { { "roles", EvaluationValueSet() }, { "roleMode", -1 },
941                         { "states", EvaluationValueSet() }, { "stateMode", -1 }
942                 } };
943
944         variables["find_by_name"] = EvaluationValueFunction{ [&](std::string name) -> EvaluationValue {
945                         for (auto root : { getVisibleRoot(), getKeyboardRoot() })
946                         {
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)));
956                                                                 }));
957                                                         }));
958                                                 }));
959                                         }, monitor);
960                                         if (!r.empty()) return std::move(r);
961                                 }
962                         }
963                         return std::vector<std::shared_ptr<UIElement>>{};
964                 }, { { "name" } } };
965
966         auto generateTapFunction = [&](size_t tapCount) {
967                 Optional<EvaluationValue> defValue;
968                 if (tapCount > 1)
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());
974
975                                 const auto fingerCount = fingers.convertToInteger();
976                                 if (fingerCount <= 0 || fingerCount > 3)
977                                         throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
978
979                                 auto coord = Point{ 300, 300 };
980                                 if (!target.isEmpty())
981                                 {
982                                         if (target.isString()) {
983                                                 target = this->convertToUIElement(target.convertToString());
984                                         }
985                                         auto dest = target.convertToUIElement();
986                                         coord = getUIElementPosition(dest).getCenterPoint();
987                                 } else
988                                 {
989                                         if (tapCount == 1)
990                                                 throw EvaluationFailure{} << "no target value passed (you need target to do single tap)";
991                                 }
992
993                                 auto sleep_until = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds{ 400 };
994                                 for (auto i = 0u; i < tapCount; ++i)
995                                 {
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);
999                                                 if (!res)
1000                                                         throw EvaluationFailure{} << "failed to execute " << tapCount << " tap gesture, " << res.getError().message;
1001                                         });
1002                                 }
1003                                 std::this_thread::sleep_until(sleep_until);
1004
1005                                 return EvaluationValue{};
1006                         }, { { "target", defValue }, { "fingers", 1 } } };
1007         };
1008         variables["TAP"] = generateTapFunction(1);
1009         variables["DOUBLE_TAP"] = generateTapFunction(2);
1010         variables["TRIPLE_TAP"] = generateTapFunction(3);
1011
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());
1017
1018                                 auto fingerCount = fingers.convertToInteger();
1019                                 if (fingerCount <= 0 || fingerCount > 3)
1020                                         throw EvaluationFailure{} << "invalid finger count (must be between 1 and 3)";
1021
1022                                 executeOnMainThread([&]()
1023                                 {
1024                                         utils::generateDragGesture(x0, y0, x1, y1, 20, 0.0, fingerCount);
1025                                 });
1026                                 std::this_thread::sleep_for(std::chrono::milliseconds{ 600 });
1027                                 return EvaluationValue{};
1028                         }, { { "fingers", 1 } } };
1029         };
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);
1034 }
1035
1036 class PredicateWaitInterface : public EvaluationValueWaitInterface
1037 {
1038 public:
1039         PredicateWaitInterface(BatchExecutor *executor, std::chrono::milliseconds timeout)
1040                 : self(executor), delay(timeout)
1041         {}
1042
1043         void join() override
1044         {
1045                 timeout = std::chrono::high_resolution_clock::now() + delay;
1046                 joinImpl();
1047         }
1048
1049 protected:
1050         virtual void joinImpl() = 0;
1051
1052         BatchExecutor *self;
1053         std::chrono::high_resolution_clock::time_point timeout;
1054         std::chrono::milliseconds delay;
1055 };
1056
1057 class WaitDlog : public PredicateWaitInterface
1058 {
1059 public:
1060         WaitDlog(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern)
1061                 : PredicateWaitInterface(executor, timeout)
1062         {
1063         }
1064
1065 private:
1066         void prepare() override
1067         {
1068                 auto h = self->dlogInfo.lock();
1069                 h->stack.push_back({ std::move(pattern) });
1070                 h->stack.back().found = &found;
1071         }
1072         void joinImpl() override
1073         {
1074                 auto h = self->dlogInfo.lock();
1075                 CallOnExit _tmp{
1076                         [&]()
1077                         {
1078                                 if (!found) {
1079                                         assert(!h->stack.empty());
1080                                         assert(h->stack.back().found == &found);
1081                                         h->stack.pop_back();
1082                                 }
1083                         }
1084                 };
1085                 auto res = h.waitForCondition(timeout, [&]() {
1086                         return found;
1087                 });
1088
1089                 if (!res)
1090                         throw EvaluationFailure{} << "wait for dlog ('" << pattern << "'): operation timeouted";
1091         }
1092
1093         std::string pattern;
1094         bool found = false;
1095 };
1096
1097 class WaitTTS : public PredicateWaitInterface
1098 {
1099 public:
1100         WaitTTS(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string pattern, bool exact)
1101                 : PredicateWaitInterface(executor, timeout), exact(exact)
1102         {
1103                 this->pattern = pattern;
1104         }
1105
1106 private:
1107         void prepare() override
1108         {
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;
1114                 w.exact = exact;
1115                 w.found = &found;
1116         }
1117         void joinImpl() override
1118         {
1119                 auto h = self->ttsInfo.lock();
1120                 CallOnExit _tmp{
1121                         [&]()
1122                         {
1123                                 if (!found) {
1124                                         assert(!h->stack.empty());
1125                                         assert(h->stack.back().found == &found);
1126                                         h->stack.pop_back();
1127                                 }
1128                         }
1129                 };
1130                 auto res = h.waitForCondition(timeout, [&]() {
1131                         return found;
1132                 });
1133
1134                 if (!res)
1135                         throw EvaluationFailure{} << "wait for tts ('" << pattern << "'): operation timeouted";
1136         }
1137
1138         std::string pattern;
1139         bool exact = false, found = false;
1140 };
1141
1142 class WaitGui : public PredicateWaitInterface
1143 {
1144 public:
1145         WaitGui(BatchExecutor *executor, std::chrono::milliseconds timeout, std::string name)
1146                 : PredicateWaitInterface(executor, timeout), name(std::move(name))
1147         {
1148                 auto h = self->contextInfo.lock();
1149                 this->currentContextName = h->rootName;
1150         }
1151 private:
1152         void prepare() override
1153         {
1154                 auto h = self->contextInfo.lock();
1155                 currentContextName = h->rootName;
1156         }
1157         void joinImpl() override
1158         {
1159                 auto h = self->contextInfo.lock();
1160                 auto pred = [&]() {
1161                         if (currentContextName != h->rootName) {
1162                                 self->outputStream() << "context name changed from '" << currentContextName << "', to '" << h->rootName << "'\n";
1163                                 currentContextName = h->rootName;
1164                                 if (name.empty()) {
1165                                         return true;
1166                                 }
1167                         }
1168                         return !name.empty() && name == h->rootName;
1169                 };
1170                 auto res = h.waitForCondition(timeout, pred);
1171                 if (!res) {
1172                         if (name.empty())
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 << "'";
1176                 }
1177         }
1178
1179         std::string name, currentContextName;
1180 };
1181
1182 void BatchExecutor::insertWaits()
1183 {
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 };
1187         };
1188         variables["dlog"] = EvaluationValueFunction{ std::move(dlogTTS), { { "pattern" }, { "timeout", 5.0 } } };
1189
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 };
1193         };
1194         variables["tts"] = EvaluationValueFunction{ std::move(waitTTS), { { "pattern", "" }, { "timeout", 5.0 }, { "exact", true } } };
1195
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 };
1199         };
1200         variables["gui"] = EvaluationValueFunction{ std::move(waitGui), { { "name", "" }, { "timeout", 5.0 } } };
1201 }
1202
1203 void BatchExecutor::callActivity(const std::string &activityName, const EvaluationValueFunction::Args &args)
1204 {
1205         auto activity = executeOnMainThread([&]() {
1206                 return ActivityFactory::getInstance()->createActivity(activityName);
1207         });
1208         if (!activity)
1209                 throw EvaluationFailure{} << "failed to construct '" << activityName << "' activity";
1210         auto numOfArgs = activity->getRequiredNumberOfArgumentsIfAllowedInBatchProcessing();
1211         if (!numOfArgs)
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);
1217
1218         // activity must inherit from UIActivity if it returns non zero expected arguments
1219         ASSERT(args.empty() || uiActivity);
1220
1221         std::vector<std::shared_ptr<UIElement>> uiArgs(args.size());
1222         for (size_t i = 0; i < args.size(); ++i) {
1223                 ASSERT(uiActivity);
1224                 uiArgs[i] = args[i].convertToUIElement();
1225                 if (!uiArgs[i])
1226                         throw EvaluationFailure{} << "can't convert argument " << (i + 1) <<
1227                                                                           " of kind " << args[i].typeName() << " to UIElement";
1228         }
1229         Monitor<BatchValueOrError<bool>> monitor;
1230         {
1231                 executeOnMainThread([&]() {
1232                         DEBUG("calling activity %s", activityName.c_str());
1233                         for (auto &arg : uiArgs) {
1234                                 ASSERT(uiActivity);
1235                                 uiActivity->update(std::move(arg));
1236                         }
1237                         activity->process(DoneCallback{ [ = ] {
1238                                         auto h = monitor.lock();
1239                                         h->setValue(true);
1240                                 } });
1241                         DEBUG("calling activity %s done", activityName.c_str());
1242                 }, monitor);
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
1247         }
1248 }
1249
1250 void BatchExecutor::insertActivities()
1251 {
1252         for (auto activityName : ActivityFactory::getInstance()->getAllActivityTypes()) {
1253                 if (variables.find(activityName) != variables.end())
1254                         continue;
1255                 variables[activityName] = [ = ](EvaluationValueFunction::Args args) -> EvaluationValue {
1256                         callActivity(activityName, args);
1257                         return {};
1258                 };
1259         }
1260 }
1261
1262 void BatchExecutor::insertStateConstants()
1263 {
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),
1314 #undef Q
1315         }) {
1316                 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1317                 variables[pair.first.substr(6)] = pair.second;
1318         }
1319 }
1320
1321 void BatchExecutor::insertCollectionConstants()
1322 {
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),
1329         }) {
1330                 ASSERT(pair.first.substr(0, 6 + 11) == "ATSPI_Collection_");
1331                 variables[pair.first.substr(6 + 11)] = pair.second;
1332         }
1333 }
1334 void BatchExecutor::insertRoleConstants()
1335 {
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),
1354                 Q(ATSPI_ROLE_DIAL),
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),
1365                 Q(ATSPI_ROLE_ICON),
1366                 Q(ATSPI_ROLE_IMAGE),
1367                 Q(ATSPI_ROLE_INTERNAL_FRAME),
1368                 Q(ATSPI_ROLE_LABEL),
1369                 Q(ATSPI_ROLE_LAYERED_PANE),
1370                 Q(ATSPI_ROLE_LIST),
1371                 Q(ATSPI_ROLE_LIST_ITEM),
1372                 Q(ATSPI_ROLE_MENU),
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),
1400                 Q(ATSPI_ROLE_TEXT),
1401                 Q(ATSPI_ROLE_TOGGLE_BUTTON),
1402                 Q(ATSPI_ROLE_TOOL_BAR),
1403                 Q(ATSPI_ROLE_TOOL_TIP),
1404                 Q(ATSPI_ROLE_TREE),
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),
1423                 Q(ATSPI_ROLE_PAGE),
1424                 Q(ATSPI_ROLE_SECTION),
1425                 Q(ATSPI_ROLE_REDUNDANT_OBJECT),
1426                 Q(ATSPI_ROLE_FORM),
1427                 Q(ATSPI_ROLE_LINK),
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),
1450                 Q(ATSPI_ROLE_LOG),
1451                 Q(ATSPI_ROLE_MARQUEE),
1452                 Q(ATSPI_ROLE_MATH),
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),
1461         }) {
1462                 ASSERT(pair.first.substr(0, 6) == "ATSPI_");
1463                 variables[pair.first.substr(6)] = pair.second;
1464         }
1465 }
1466
1467 static void threadFunc(StatPtr result, std::unique_ptr<BatchExecutor> exec, std::unique_ptr<std::ostream> outputPtr, Dlog dlog)
1468 {
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;
1473         {
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);
1482                 });
1483         }
1484         if (hasContext) {
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) {
1490                                         *w.found = true;
1491                                 } else {
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{ "'" };
1495
1496                                                 auto z = txt.find(prefix);
1497                                                 if (z != std::string::npos) {
1498                                                         z += prefix.size();
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)) {
1504                                                                         *w.found = true;
1505                                                                 }
1506                                                         }
1507                                                 }
1508                                         }
1509                                 }
1510                                 if (*w.found)
1511                                         h->stack.pop_back();
1512                         }
1513                 });
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) {
1519                                         *w.found = true;
1520                                         h->stack.pop_back();
1521                                 }
1522                         }
1523                 });
1524                 exec->outputStream() << "evaluation started\n";
1525                 try {
1526                         result->evaluate();
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";
1538                 } catch (...) {
1539                         exec->outputStream() << "unhandled exception\nevaluation failed\n";
1540                 }
1541         } else {
1542                 exec->outputStream() << "timeouted\n";
1543                 DEBUG("timeouted, when waiting for context change");
1544         }
1545         outputPtr->flush();
1546         executeOnMainThread([]() {
1547                 DEBUG("main_loop_quit");
1548                 ecore_main_loop_quit();
1549         });
1550         DEBUG("done batch");
1551 }
1552
1553 Optional<std::thread> runBatch(const std::array<Optional<std::string>, (size_t)utils::Argument::_count> &arguments)
1554 {
1555         const std::string &sourceName = *arguments[static_cast<size_t>(utils::Argument::SourcePath)];
1556
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);
1562
1563         if (!static_cast<std::ofstream *>(outputPtr.get())->is_open()) {
1564                 ERROR("failed to open output file '%s'", outputPath.c_str());
1565                 return {};
1566         }
1567         auto &output = *outputPtr;
1568         auto exec = std::make_unique<BatchExecutor>(*outputPtr);
1569
1570         DEBUG("running batch file: %s", sourceName.c_str());
1571         auto content = exec->getFileContent(sourceName);
1572
1573         std::string error;
1574         auto tokens = lexTest(error, sourceName, content);
1575         if (!error.empty()) {
1576                 output << error << "\nlexing failed\n";
1577                 return {};
1578         }
1579
1580         std::vector<std::string> errors;
1581         auto result = parseTokens(errors, tokens);
1582
1583         if (!errors.empty() || !result) {
1584                 for (auto &e : errors) {
1585                         output << e << "\n";
1586                 }
1587                 output << "parsing failed\n";
1588                 return {};
1589         }
1590
1591         Dlog dlog;
1592         if (!dlog.start()) {
1593                 output << "launching dlogutil failed\n";
1594                 return {};
1595         }
1596
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));
1603                 }
1604         }
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) };
1608 }