#include #include "../src/Interpreter.hpp" #define TEST_RUN(SRC, ...) \ test_run(SRC, {__VA_ARGS__}) using namespace sn; struct StateMock: public State { std::vector modified_files; explicit StateMock(std::vector const& p_modified_files) : modified_files { p_modified_files } { } virtual void init(std::vector const&) override { } virtual void update(std::filesystem::path) override { } virtual std::vector get_modified_files(std::vector const&) override { return modified_files; } }; struct LoaderMock: public Loader { std::string snakefile; explicit LoaderMock(std::string const& p_snakefile) : snakefile { p_snakefile } { } virtual std::filesystem::path find_snakefile() override { return ""; } virtual std::string load_snakefile() override { return snakefile; } }; struct ExecutorMock: public Executor { std::vector history; virtual std::string execute(std::string const& command) override { history.push_back(command); return ""; } }; struct FinderMock: public Finder { std::vector files; explicit FinderMock(std::vector const& p_files) : files { p_files } { } virtual std::vector get_paths(std::filesystem::path) override { return files; } }; class InterpreterTest { public: explicit InterpreterTest() {} virtual ~InterpreterTest() {} std::vector test_run(std::string const& snakefile, std::vector const& modified) { auto state = std::make_shared(modified); auto loader = std::make_shared(snakefile); auto executor = std::make_shared(); auto finder = std::make_shared (std::vector{ "first.cpp", "src/second.py", "third.py", "src/fourth.hpp", "fifth", "src/build/target/sixth" }); std::stringstream ss; Interpreter interpreter {state, loader, executor, finder, ss}; interpreter.run(); return executor->history; } std::string sort_line(std::string const& line) { std::vector words; std::string word; std::stringstream ss; ss << line; while (std::getline(ss, word, ' ')) { words.push_back(word); } std::sort(std::begin(words), std::end(words)); std::string res = ""; std::string sep = ""; for (auto w: words) { res += sep + w; sep = " "; } return res; } void test_contains(std::string const& str, std::vector vec) { if (vec.empty()) { INFO("'" << str << "' not found, got nothing"); REQUIRE(false); return; } INFO(str << "not found"); REQUIRE(std::any_of(std::begin(vec), std::end(vec), [&](auto const& line){ return sort_line(line) == sort_line(str); })); } void test_not_contains(std::string const& str, std::vector vec) { INFO("'" << str << "' found"); REQUIRE(std::find(std::begin(vec), std::end(vec), str) == std::end(vec)); } protected: }; TEST_CASE_METHOD(InterpreterTest, "Interpreter_simple_var") { std::stringstream ss; ss << "$X = a" << std::endl; ss << "b -> $X {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("executed", res); } TEST_CASE_METHOD(InterpreterTest, "Interpreter_array_var") { SECTION("value") { std::stringstream ss; ss << "$X = (a b c)" << std::endl; ss << "res -> a {" << std::endl; ss << "$X" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("a b c", res); } SECTION("as dep") { std::stringstream ss; ss << "$X = (a b c)" << std::endl; ss << "z -> $X {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; SECTION("a") { auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("executed", res); } SECTION("b") { auto res = TEST_RUN(ss.str(), std::filesystem::path("b")); test_contains("executed", res); } SECTION("c") { auto res = TEST_RUN(ss.str(), std::filesystem::path("c")); test_contains("executed", res); } } SECTION("direct use of array") { std::stringstream ss; ss << "z -> (a b c) {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; SECTION("a") { auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("executed", res); } SECTION("b") { auto res = TEST_RUN(ss.str(), std::filesystem::path("b")); test_contains("executed", res); } SECTION("c") { auto res = TEST_RUN(ss.str(), std::filesystem::path("c")); test_contains("executed", res); } } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_array_index") { SECTION("get second: ok") { std::stringstream ss; ss << "$X = (a b c)" << std::endl; ss << "d -> $X[1] {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("b")); test_contains("executed", res); } SECTION("get second: not ok") { std::stringstream ss; ss << "$X = (a b c)" << std::endl; ss << "d -> $X[1] {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_not_contains("executed", res); } SECTION("get last: ok") { std::stringstream ss; ss << "$X = (a b c)" << std::endl; ss << "d -> $X[-1] {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("c")); test_contains("executed", res); } SECTION("get last: not ok") { std::stringstream ss; ss << "$X = (a b c)" << std::endl; ss << "d -> $X[-1] {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("b")); test_not_contains("executed", res); } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_array_literal_index") { SECTION("get second: ok") { std::stringstream ss; ss << "$X = (a b c)[1]" << std::endl; ss << "d -> $X {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("b")); test_contains("executed", res); } SECTION("get second: not ok") { std::stringstream ss; ss << "$X = (a b c)[1]" << std::endl; ss << "d -> $X {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_not_contains("executed", res); } SECTION("get last: ok") { std::stringstream ss; ss << "$X = (a b c)[-1]" << std::endl; ss << "d -> $X {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("c")); test_contains("executed", res); } SECTION("get last: not ok") { std::stringstream ss; ss << "$X = (a b c)[-1]" << std::endl; ss << "d -> $X {" << std::endl; ss << "executed" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("b")); test_not_contains("executed", res); } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_select_ext") { SECTION("on var") { std::stringstream ss; ss << "$Y = g.h" << std::endl; ss << "d -> a {" << std::endl; ss << "$Y $Y[.hpp] $Y[.cpp]" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("g.h g.hpp g.cpp", res); } SECTION("on array") { std::stringstream ss; ss << "$Y = (x.c y.c z.d)" << std::endl; ss << "d -> a {" << std::endl; ss << "$Y[.cpp]" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("x.cpp y.cpp z.cpp", res); } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_replace_ext") { SECTION("on array") { std::stringstream ss; ss << "$Y = (x.c y.c z.d w t.h)" << std::endl; ss << "d -> a {" << std::endl; ss << "$Y[.c=.h]" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("x.h y.h z.d w t.h", res); } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_in_out") { SECTION("on array") { std::stringstream ss; ss << "x y z -> a b c {" << std::endl; ss << "$IN $OUT" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); test_contains("a b c x y z", res); } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_array_constructor") { SECTION("extension I") { std::stringstream ss; ss << "x y z -> (*.py) {" << std::endl; ss << "$IN" << std::endl; ss << "}" << std::endl; std::string result; result = "src/second.py third.py"; auto res = TEST_RUN(ss.str(), std::filesystem::path("third.py")); test_contains(result, res); } SECTION("extension II") { std::stringstream ss; ss << "x y z -> (*.cpp) {" << std::endl; ss << "$IN" << std::endl; ss << "}" << std::endl; std::string result; result = "first.cpp"; auto res = TEST_RUN(ss.str(), std::filesystem::path("first.cpp")); test_contains(result, res); } SECTION("all in directory") { std::stringstream ss; ss << "x y z -> (src/*) {" << std::endl; ss << "$IN" << std::endl; ss << "}" << std::endl; std::string result; result = "src/second.py src/fourth.hpp"; auto res = TEST_RUN(ss.str(), std::filesystem::path("src/second.py")); test_contains(result, res); } SECTION("all in sub directories") { std::stringstream ss; ss << "x y z -> (src/build/target/*) {" << std::endl; ss << "$IN" << std::endl; ss << "}" << std::endl; std::string result; result = "src/build/target/sixth"; auto res = TEST_RUN(ss.str(), std::filesystem::path("src/build/target/sixth")); test_contains(result, res); } } TEST_CASE_METHOD(InterpreterTest, "Interpreter_generics", "[.]") { SECTION("no rule to apply") { std::stringstream ss; ss << "^.o -> ^.cpp {" << std::endl; ss << "$IN $OUT" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a")); REQUIRE(res.empty()); } SECTION("call left") { std::stringstream ss; ss << "^.o -> ^.cpp ^.el {" << std::endl; ss << "$IN $OUT" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a.cpp")); test_contains("a.o a.cpp", res); } SECTION("call right") { std::stringstream ss; ss << "^.o -> ^.cpp ^.el {" << std::endl; ss << "$IN $OUT" << std::endl; ss << "}" << std::endl; auto res = TEST_RUN(ss.str(), std::filesystem::path("a.el")); test_contains("a.o a.el", res); } }