Adds support for executing batch file given in command line 52/164552/34
authorRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Tue, 19 Dec 2017 15:02:53 +0000 (16:02 +0100)
committerRadoslaw Cybulski <r.cybulski@partner.samsung.com>
Wed, 17 Jan 2018 16:17:36 +0000 (17:17 +0100)
Change-Id: If7322e67dc39687b6d3a6eea65d60f1557dc328f

src/UniversalSwitch.cpp
src/batch/BatchRunner.cpp [new file with mode: 0644]
src/batch/BatchRunner.hpp [new file with mode: 0644]

index fbe1083..36c0504 100644 (file)
@@ -34,6 +34,7 @@
 #include "dbusLocators.hpp"
 #include "utils.hpp"
 #include "DBus.hpp"
+#include "batch/BatchRunner.hpp"
 
 #include <device/display.h>
 
@@ -47,6 +48,7 @@ void UniversalSwitch::initialize(const std::array<Optional<std::string>, (size_t
                batchOutputPath = *arguments[(size_t)utils::Argument::OutputPath];
 
                changeMode(Mode::testExecution);
+               runBatch(batchFilePath, batchOutputPath);
                return;
        }
 
diff --git a/src/batch/BatchRunner.cpp b/src/batch/BatchRunner.cpp
new file mode 100644 (file)
index 0000000..87b0f47
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2017  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../UniversalSwitch.hpp"
+#include "../UniversalSwitchLog.hpp"
+#include "Lexer.hpp"
+#include "Parser.hpp"
+#include "EvaluationContext.hpp"
+#include "Evaluator.hpp"
+#include "../UIElement.hpp"
+#include "../NavigationInterface.hpp"
+
+#include <mutex>
+#include <chrono>
+#include <thread>
+#include <fstream>
+
+struct ExecuteOnMainThreadHelper {
+       std::function<void()> callback;
+       Optional<EvaluationFailure> failure;
+};
+static void *executeOnMainThreadCb(void *d)
+{
+       auto helper = static_cast<ExecuteOnMainThreadHelper *>(d);
+       try {
+               helper->callback();
+       } catch (EvaluationFailure &e) {
+               helper->failure = std::move(e);
+       } catch (std::exception &e) {
+               helper->failure = EvaluationFailure{} << "unhandled expection (" << e.what() << ")";
+       } catch (...) {
+               helper->failure = EvaluationFailure{} << "unhandled expection";
+       }
+       return nullptr;
+}
+
+void executeOnMainThread(std::function<void()> fnc)
+{
+       ExecuteOnMainThreadHelper tmp{ std::move(fnc) };
+       ecore_main_loop_thread_safe_call_sync(executeOnMainThreadCb, &tmp);
+       if (tmp.failure) throw *tmp.failure;
+}
+
+struct TestExecutor : ExecutorInterface {
+       NavigationInterface::CallbackHandle contextChangedHandle;
+       std::shared_ptr<UIElement> root;
+       std::shared_ptr<NavigationElement> navigationContext;
+       std::unordered_map<std::string, EvaluationValue> variables;
+       std::mutex mt;
+       std::ostream &output;
+
+       // NOTE: constructor of TestExecutor must be called on main thread
+       // constructor calls NavigationInterface object, which might be
+       // in progress of updating itself (context change) on main thread
+       TestExecutor(std::ostream &output) : output(output)
+       {
+               variables["sleep"] = EvaluationValue::UserFunctionType<double> {
+                       [&](EvaluationContext & ec, double tm) -> EvaluationValue {
+                               if (tm > 0)
+                               {
+                                       auto sleepTime = std::chrono::milliseconds{ static_cast<int>(std::floor(1000.0 * tm + 0.5)) };
+                                       std::this_thread::sleep_for(sleepTime);
+                               }
+                               return {};
+                       }
+               };
+               auto nav = Singleton<UniversalSwitch>::instance().getNavigationInterface();
+               this->navigationContext = nav->getCurrentNavigationContext();
+               this->root = nav->getCurrentVisibleRoot();
+               contextChangedHandle = Singleton<UniversalSwitch>::instance().getNavigationInterface()->registerCb<NavigationCallbackType::ContextChanged>(
+               [this](std::shared_ptr<UIElement> root, std::shared_ptr<NavigationElement> navigationContext) {
+                       ASSERT(navigationContext);
+                       std::lock_guard<std::mutex> lock(mt);
+                       this->navigationContext = std::move(navigationContext);
+                       this->root = std::move(root);
+               });
+       }
+       ~TestExecutor() = default;
+       EvaluationValue getVariableByName(const std::string &name) override
+       {
+               auto it = variables.find(name);
+               if (it != variables.end())
+                       return it->second;
+               return ExecutorInterface::getVariableByName(name);
+       }
+       std::shared_ptr<UIElement> getVisibleRoot() override
+       {
+               std::lock_guard<std::mutex> lock(mt);
+               return root;
+       }
+
+       std::ostream &outputStream() override
+       {
+               return output;
+       }
+};
+
+bool runTestCaseImpl(std::string sourceName, std::string outputName)
+{
+       std::unique_ptr<std::ostream> outputPtr;
+       outputPtr = std::make_unique<std::ofstream>(outputName.empty() ? "/dev/null" : outputName, std::ios_base::out);
+
+       if (!static_cast<std::ofstream *>(outputPtr.get())->is_open()) {
+               ERROR("failed to open output file '%s'", outputName.c_str());
+               return false;
+       }
+       auto &output = *outputPtr;
+       auto source = std::ifstream{ sourceName, std::ios_base::in };
+       if (!source.is_open()) {
+               output << "failed to open file '" << sourceName << "'\n";
+               return false;
+       }
+
+       source.seekg(0, std::ios_base::end);
+       auto total = source.tellg();
+       source.seekg(0);
+       std::vector<char> bufor;
+       bufor.resize(total);
+       source.read(bufor.data(), total);
+       if ((size_t)source.gcount() != total) {
+               output << "error while reading file '" << sourceName << "'\n";
+               return false;
+       }
+       auto content = std::string{ bufor.data(), (size_t)total };
+
+       std::string error;
+       auto tokens = lexTest(error, sourceName, content);
+       if (!error.empty()) {
+               output << error << "\nlexing failed\n";
+               return false;
+       }
+
+       std::vector<std::string> errors;
+       auto result = parseTokens(errors, tokens);
+
+       if (!errors.empty() || !result) {
+               for (auto &e : errors) {
+                       output << e << "\n";
+               }
+               output << "parsing failed\n";
+               return false;
+       }
+
+       // std::ostringstream os;
+       // result->debug(os, 0);
+       // output << os.str() << "\n";
+
+       output << "executing test '" << sourceName << "'\n";
+       auto exec = std::make_unique<TestExecutor>(*outputPtr);
+       std::thread { [result, exec = std::move(exec), outputPtr = std::move(outputPtr)]()
+       {
+               EvaluationContext ec { *exec };
+               exec->outputStream() << "waiting for context change...";
+               auto until = std::chrono::high_resolution_clock::now() + std::chrono::seconds{ 2 };
+               while (true) {
+                       if (std::chrono::high_resolution_clock::now() >= until) {
+                               exec->outputStream() << "timeouted\n";
+                               DEBUG("timeouted, when waiting for context change");
+                               break;
+                       }
+                       if (exec->getVisibleRoot()) {
+                               exec->outputStream() << "evaluation started\n";
+                               try {
+                                       result->evaluate(ec);
+                                       exec->outputStream() << "evaluation successed\n";
+                                       std::this_thread::sleep_for(std::chrono::seconds{ 1 });
+                               } catch (EvaluationFailure &e) {
+                                       if (e.hasLocation())
+                                               exec->outputStream() << e.location().toString() << ": ";
+                                       exec->outputStream() << e.message() << "\nevaluation failed\n";
+                               } catch (...) {
+                                       exec->outputStream() << "unhandled exception\nevaluation failed\n";
+                               }
+                               break;
+                       }
+                       std::this_thread::sleep_for(std::chrono::milliseconds{ 10 });
+               }
+
+               executeOnMainThread([]() {
+                       ecore_main_loop_quit();
+               });
+       } } .detach();
+       return true;
+}
+
+void runBatch(const std::string &sourceName, const std::string &outputName)
+{
+       auto result = runTestCaseImpl(sourceName, outputName);
+       if (!result) {
+               Singleton<UniversalSwitch>::instance().terminate();
+               std::exit(1);
+       }
+}
diff --git a/src/batch/BatchRunner.hpp b/src/batch/BatchRunner.hpp
new file mode 100644 (file)
index 0000000..5a95659
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BATCH_RUNNER_HPP
+#define BATCH_RUNNER_HPP
+
+#include <string>
+
+void runBatch(const std::string &sourceName, const std::string &outputName);
+
+#endif