void BatchExecutor::registerContextChangeCallback()
{
+ auto nav = Singleton<UniversalSwitch>::instance().getNavigationInterface();
+ if (!nav) {
+ DEBUG("no navigation interface found, context changed callback not registered");
+ return;
+ }
auto updateContextInfo = [this](std::shared_ptr<UIElement> root, std::shared_ptr<NavigationElement> navigationContext) {
std::string name;
if (root && root->getObject()) {
h->root = std::move(root);
h->rootName = std::move(name);
};
- auto nav = Singleton<UniversalSwitch>::instance().getNavigationInterface();
updateContextInfo(nav->getCurrentVisibleRoot(), nav->getCurrentNavigationContext());
- contextChangedHandle = Singleton<UniversalSwitch>::instance().getNavigationInterface()->
- registerCb<NavigationCallbackType::ContextChanged>(std::move(updateContextInfo));
+ contextChangedHandle = nav->registerCb<NavigationCallbackType::ContextChanged>(std::move(updateContextInfo));
}
EvaluationValue BatchExecutor::getVariableByName(const std::string &name)
}, monitor);
}
+std::string BatchExecutor::getFileContent(const std::string &filename)
+{
+ auto source = std::ifstream{ filename, std::ios_base::in };
+ if (!source.is_open())
+ throw EvaluationFailure{} << "failed to open file '" << filename << "'\n";
+
+ 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)
+ throw EvaluationFailure{} << "error while reading file '" << filename << "'\n";
+
+ return std::string(bufor.data(), (size_t)total);
+}
+
+namespace
+{
+ //TODO: when switching to c++17 we should use std::filename::path
+ std::string getPathRelativeToFile(const std::string &relativePath, const std::string &file)
+ {
+ if (!relativePath.empty() && relativePath[0] == '/') {
+ //not a relative path
+ return relativePath;
+ }
+ auto pos = file.rfind('/');
+ if (pos == std::string::npos) {
+ return relativePath;
+ }
+ return file.substr(0, pos + 1) + relativePath;
+ }
+}
+
void BatchExecutor::insertMethods()
{
+ variables["get_batch_file"] = [&]() -> EvaluationValue {
+ // this function is called through Evaluator.cpp:CallEvaluator::evaluate()
+ // which pushes it's own location to path stack, so path stack's top will be valid here.
+ return EvaluationContext::getCurrentEvaluationContext().topBatchPath();
+ };
+
+ variables["import"] = [&](std::string filename) -> EvaluationValue {
+
+ StatPtr result;
+ auto currentRdlFile = EvaluationContext::getCurrentEvaluationContext().topBatchPath();
+ auto filenameFullPath = getPathRelativeToFile(filename, currentRdlFile);
+ auto it = imports.insert({ filenameFullPath, {} });
+ if (!it.second)
+ {
+ result = it.first->second;
+ if (!result) {
+ throw EvaluationFailure{} << "nested import found in " << filenameFullPath;
+ }
+ result->evaluate();
+ return {};
+ }
+
+ auto content = getFileContent(filenameFullPath);
+ std::string error;
+ auto tokens = lexTest(error, filenameFullPath, content);
+
+ if (!error.empty())
+ throw EvaluationFailure{} << "Lexing failed\n" << error;
+
+ std::vector<std::string> errors;
+ result = parseTokens(errors, tokens);
+
+ if (!errors.empty())
+ {
+ auto error = EvaluationFailure{};
+ error << "Parsing failed\n";
+ for (auto &e : errors) {
+ error << e << "\n";
+ }
+ throw std::move(error);
+ }
+ ASSERT(result);
+ result->evaluate();
+ it.first->second = std::move(result);
+ return {};
+ };
+
variables["sleep"] = [&](double tm) -> EvaluationValue {
if (tm > 0)
{
return {};
}
auto &output = *outputPtr;
- auto source = std::ifstream{ sourceName, std::ios_base::in };
- if (!source.is_open()) {
- output << "failed to open file '" << sourceName << "'\n";
- return {};
- }
+ auto exec = std::make_unique<BatchExecutor>(*outputPtr);
- 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 {};
- }
- auto content = std::string{ bufor.data(), (size_t)total };
+ DEBUG("running batch file: %s", sourceName.c_str());
+ auto content = exec->getFileContent(sourceName);
std::string error;
auto tokens = lexTest(error, sourceName, content);
*outputPtr << "Parsed source code:\n";
*outputPtr << os.str() << "\n\n";
}
- auto exec = std::make_unique<BatchExecutor>(*outputPtr);
return std::thread { threadFunc, std::move(result), std::move(exec), std::move(outputPtr), std::move(dlog) };
}
EvaluationValue getVariableByName(const std::string &name) override;
std::ostream &outputStream() override;
+ std::string getFileContent(const std::string &filename) override;
std::shared_ptr<UIElement> getVisibleRoot() override;
std::shared_ptr<UIElement> convertToUIElement(Point pt) override;
}
}
+void EvaluationContext::pushBatchPath(const std::string &path)
+{
+ batchPathStack.push_back(&path);
+}
+
+void EvaluationContext::popBatchPath()
+{
+ batchPathStack.pop_back();
+}
+
+const std::string &EvaluationContext::topBatchPath()
+{
+ ASSERT(!batchPathStack.empty());
+ return *batchPathStack.back();
+}
+
+
std::string ExecutorInterface::getUIElementName(const std::shared_ptr<UIElement> &elem)
{
throw EvaluationFailure{} << "getUIElementName not implemeneted";
#include <string>
#include <ostream>
#include <tuple>
+#include <vector>
class UIElement;
* There's only single stream for all kinds of output, debug or not.
*/
virtual std::ostream &outputStream() = 0;
+
+ /**
+ * @brief Returns file content as a string
+ */
+ virtual std::string getFileContent(const std::string &filename) = 0;
};
/**
* getCurrentEvaluationContext function) only from the same thread, on which they were created.
*/
static EvaluationContext &getCurrentEvaluationContext();
+
+ /**
+ * @brief Push script file name of the currently executing batch to stack
+ *
+ */
+ void pushBatchPath(const std::string &path);
+
+ /**
+ * @brief Remove last batch file name from stack
+ *
+ */
+ void popBatchPath();
+
+ /**
+ * @brief Get file name to currently executing batch file
+ *
+ */
+ const std::string &topBatchPath();
+
private:
void initializeGlobalContext(ExecutorInterface &ei);
std::unordered_map<std::string, EvaluationValue> variables;
EvaluationContext *parent = nullptr;
std::shared_ptr<GlobalContext> globalContext;
+ std::vector<const std::string *> batchPathStack;
};
#endif
for (auto &it : keywordArgs) {
keywordValues[it.first] = it.second->evaluate();
}
- return function->evaluate()(values, keywordValues);
+ EvaluationContext::getCurrentEvaluationContext().pushBatchPath(location().fileName());
+ auto result = function->evaluate()(values, keywordValues);
+ EvaluationContext::getCurrentEvaluationContext().popBatchPath();
+ return std::move(result);
}
void CallEvaluator::printSelfInfo(std::ostringstream &os, unsigned int depth) const
#!/bin/bash
+BATCH_FILE=$(readlink -f "$1")
+BATCH_DIRNAME=$(dirname "$BATCH_FILE")
+
PREFIX=qe9fh483fb4i3fb8we3_universal_switch
DLOG=/tmp/${PREFIX}_dlog.txt
-TEST_FILE=/tmp/${PREFIX}_test_file
OUTPUT=/tmp/${PREFIX}_output.txt
PERMISSIONS=User::Pkg::org.tizen.universal-switch::RO
-dlogutil -c
-mkfifo -m 0666 $DLOG
-(
- dlogutil '*' > $DLOG 2> /dev/null
-)&
-chsmack -a $PERMISSIONS $TEST_FILE
-chsmack -a $PERMISSIONS $DLOG
-JOBS=$(jobs -p)
-echo "JOB is $JOBS"
-
-rm -f $OUTPUT
-app_launcher -t org.tizen.universal-switch
-STATUS=$(app_launcher -s org.tizen.universal-switch TESTS_PATH $TEST_FILE TESTS_OUTPUT $OUTPUT)
-STATUS2=${STATUS#*successfully launched pid = }
-PID=${STATUS2% with debug*}
-tail --pid=$PID -f /dev/null
-kill -9 $JOBS
-
-rm -f $DLOG
-cat $OUTPUT
+
+function createDlogPipe()
+{
+ dlogutil -c
+ rm -f "$DLOG"
+
+ mkfifo -m 0666 "$DLOG"
+ (
+ dlogutil '*' > "$DLOG" 2> /dev/null
+ )&
+ chsmack -a $PERMISSIONS "$DLOG"
+
+ JOBS=$(jobs -p)
+ echo "JOB is $JOBS"
+}
+
+function doCleanup()
+{
+ app_launcher -t org.tizen.universal-switch
+ rm -f "$DLOG"
+ rm -f $OUTPUT
+ kill -9 $JOBS 2> /dev/null
+ exit
+}
+
+function runTest()
+{
+ rm -f $OUTPUT
+ app_launcher -t org.tizen.universal-switch
+ STATUS=$(app_launcher -s org.tizen.universal-switch TESTS_PATH $BATCH_FILE TESTS_OUTPUT $OUTPUT)
+ STATUS2=${STATUS#*successfully launched pid = }
+ PID=${STATUS2% with debug*}
+
+ tail --pid=$PID -f /dev/null
+ cat "$OUTPUT"
+}
+
+REPEAT=1
+(( "$2" > 1 )) 2>/dev/null && REPEAT="$2"
+
+trap doCleanup SIGINT
+
+createDlogPipe
+
+for((i=1;i<=$REPEAT;i++))
+do
+ echo "Test run: $i"
+ runTest
+done
+
+doCleanup
#!/bin/bash
-P=$(readlink -f "$1")
+BATCH_FILE=$(readlink -f "$1")
+BATCH_DIRNAME=$(dirname "$BATCH_FILE")
+BATCH_BASENAME=$(basename "$BATCH_FILE")
SCRIPT=$(readlink -f "$0")
+SCRIPT_DIRNAME=$(dirname "$SCRIPT")
+BATCH_PATTERN='*.rdl'
+PERMISSIONS=User::Pkg::org.tizen.universal-switch::RO
+REPEAT="$2"
+
+TMP=$(mktemp -d)
+echo "tmp dir: $TMP"
+
+cd "$BATCH_DIRNAME"
+find . -name "$BATCH_PATTERN" -exec cp --parents {} "$TMP" ";"
+cp "$BATCH_FILE" "$TMP"
+cp "$SCRIPT_DIRNAME/batch_exec.sh" "$TMP"
+sdb root on
+sdb push $TMP $TMP
+sdb shell chsmack -ra $PERMISSIONS "$TMP"
+
+
cd $(dirname "$SCRIPT")
cd ../..
-
./project-tool -j
-sdb root on
-sdb push $P /tmp/qe9fh483fb4i3fb8we3_universal_switch_test_file
-sdb push src/batch/batch_exec.sh /tmp/qe9fh483fb4i3fb8we3_universal_switch_test_exec
-sdb shell chsmack -a _ /tmp/qe9fh483fb4i3fb8we3_universal_switch_test_exec
-sdb shell "cd /tmp; /bin/bash /tmp/qe9fh483fb4i3fb8we3_universal_switch_test_exec; echo done"
+sdb shell "cd $TMP; /bin/bash batch_exec.sh $TMP/$BATCH_BASENAME $REPEAT ; echo done"
+
+rm -r "$TMP"
+sdb shell "rm -r $TMP"
\ No newline at end of file
#include "batch/Parser.hpp"
#include "batch/EvaluationContext.hpp"
#include "batch/Evaluator.hpp"
+#include "batch/BatchRunner.hpp"
#include <sstream>
using VarsType = std::unordered_map<std::string, EvaluationValue>;
{
return std::cout;
}
+ std::string getFileContent(const std::string &filename) override
+ {
+ ASSERT(0);
+ return {};
+ }
+
+
private:
VarsType variables;
ActsType activities;
}
}
+struct TestBatchExecutor : BatchExecutor {
+ TestBatchExecutor() : BatchExecutor(std::cout) { }
+
+ std::string getFileContent(const std::string &filename) override
+ {
+ auto it = files.find(filename);
+ if (it != files.end()) {
+ return it->second;
+ }
+ return BatchExecutor::getFileContent(filename);
+ }
+ void addFileContent(std::string filename, std::string content)
+ {
+ files[std::move(filename)] = std::move(content);
+ }
+
+private:
+ std::unordered_map<std::string, std::string> files;
+};
+
+
+TEST_F(ScriptTest, Import)
+{
+ std::tuple<int, int, int, int> val, expected;
+ int funcCount = 0;
+
+ TestBatchExecutor exec;
+
+ exec.variables["func"] = EvaluationValueFunction {
+ [&](int a1, int a2, int a3, int a4) -> EvaluationValue {
+ std::get<0>(val) = a1;
+ std::get<1>(val) = a2;
+ std::get<2>(val) = a3;
+ std::get<3>(val) = a4;
+ funcCount++;
+ return {};
+ }, {
+ { "a1" },
+ { "a2" },
+ { "a3", 1 },
+ { "a4", 2 }
+ } };
+
+ EvaluationContext ec{ exec };
+
+ exec.addFileContent("test.rdl", "func(10,10,9,9)");
+ execute("import('test.rdl')", ec, true);
+ expected = { 10, 10, 9, 9};
+ ASSERT_EQ(val, expected);
+
+ //"test.rdl" file already parsed and cached, changes
+ // shouldn't be visible as long as test is running
+ exec.addFileContent("test.rdl", "func(12,12,11,11)");
+ execute("import('test.rdl')", ec, true);
+ expected = { 10, 10, 9, 9};
+ ASSERT_EQ(val, expected);
+
+ try {
+ execute("import('file_does_not_exist.rdl')", ec, true);
+ FAIL() << "file should not exist";
+ } catch (EvaluationFailure &e) {}
+
+ funcCount = 0;
+ exec.addFileContent("error.rdl", "func(1,2,3,4)\n"
+ "unknown_function(12,12,11,11)");
+ try {
+ execute("import('error.rdl')", ec, true);
+ FAIL() << "function should throw an evaluation error";
+ } catch (EvaluationFailure &e) {}
+ ASSERT_EQ(funcCount, 1);
+
+ funcCount = 0;
+ exec.addFileContent("recursive.rdl", "func(1,2,3,4)\n"
+ "import('recursive.rdl')");
+ try {
+ execute("import('recursive.rdl')", ec, true);
+ FAIL() << "nested import";
+ } catch (EvaluationFailure &e) {}
+ ASSERT_EQ(funcCount, 1);
+
+ funcCount = 0;
+ exec.addFileContent("test2.rdl", "func(1,2,3,4)");
+ execute("import('test2.rdl')\n"
+ "import('test2.rdl')", ec, true);
+ ASSERT_EQ(funcCount, 2);
+}
+
int main(int argc, char *argv[])
{
try {