495 lines
12 KiB
C++
495 lines
12 KiB
C++
#include <catch2/catch.hpp>
|
|
#include "../src/Interpreter.hpp"
|
|
|
|
#define TEST_RUN(SRC, ...) \
|
|
test_run(SRC, {__VA_ARGS__})
|
|
|
|
using namespace sn;
|
|
|
|
struct StateMock: public State {
|
|
std::vector<std::filesystem::path> modified_files;
|
|
|
|
explicit StateMock(std::vector<std::filesystem::path> const& p_modified_files)
|
|
: modified_files { p_modified_files }
|
|
{
|
|
}
|
|
|
|
virtual void init(std::vector<std::filesystem::path> const&) override
|
|
{
|
|
}
|
|
|
|
virtual void update(std::filesystem::path) override
|
|
{
|
|
}
|
|
|
|
virtual std::vector<std::filesystem::path>
|
|
get_modified_files(std::vector<std::filesystem::path> 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<std::string> history;
|
|
|
|
virtual std::string execute(std::string const& command) override
|
|
{
|
|
history.push_back(command);
|
|
return "";
|
|
}
|
|
};
|
|
|
|
struct FinderMock: public Finder {
|
|
std::vector<std::filesystem::path> files;
|
|
|
|
explicit FinderMock(std::vector<std::filesystem::path> const& p_files)
|
|
: files { p_files }
|
|
{
|
|
}
|
|
|
|
virtual std::vector<std::filesystem::path>
|
|
get_paths(std::filesystem::path) override
|
|
{
|
|
return files;
|
|
}
|
|
};
|
|
|
|
class InterpreterTest
|
|
{
|
|
public:
|
|
explicit InterpreterTest() {}
|
|
virtual ~InterpreterTest() {}
|
|
|
|
std::vector<std::string>
|
|
test_run(std::string const& snakefile,
|
|
std::vector<std::filesystem::path> const& modified)
|
|
{
|
|
auto state = std::make_shared<StateMock>(modified);
|
|
|
|
auto loader = std::make_shared<LoaderMock>(snakefile);
|
|
auto executor = std::make_shared<ExecutorMock>();
|
|
auto finder = std::make_shared<FinderMock>
|
|
(std::vector<std::filesystem::path>{
|
|
"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<std::string> 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<std::string> 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<std::string> 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);
|
|
}
|
|
}
|