Merge "Sync dependencies"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7e70b41..baec077 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -272,7 +272,7 @@
cli_test(set_value_completion)
target_link_libraries(test_set_value_completion leaf_data_type)
cli_test(list_manipulation)
- cli_test(ls_interpreter)
+ cli_test(interpreter)
cli_test(path_utils)
target_link_libraries(test_path_utils path)
cli_test(keyvalue_completion)
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 0079962..0d8a606 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -115,6 +115,62 @@
{
}
+namespace {
+template <typename T, typename U>
+auto findFirstOf(const std::vector<U>& nodes)
+{
+ return std::find_if(nodes.begin(), nodes.end(), [](const auto& e) {
+ return std::holds_alternative<T>(e.m_suffix);
+ });
+}
+
+template <typename T>
+void validatePathNodes(const std::vector<T>& nodes)
+{
+ static_assert(std::is_same<T, dataNode_>() || std::is_same<T, schemaNode_>());
+
+ if (nodes.empty()) {
+ // there are default ctors, so it makes sense to specify the same thing via explicit args and not fail
+ return;
+ }
+
+ if (auto firstLeaf = findFirstOf<leaf_>(nodes);
+ firstLeaf != nodes.end() && firstLeaf != nodes.end() - 1) {
+ throw std::logic_error{"Cannot put any extra nodes after a leaf"};
+ }
+
+ if (auto firstLeafList = findFirstOf<leafList_>(nodes);
+ firstLeafList != nodes.end() && firstLeafList != nodes.end() - 1) {
+ throw std::logic_error{"Cannot put any extra nodes after a leaf-list"};
+ }
+
+ if constexpr (std::is_same<T, dataNode_>()) {
+ if (auto firstLeafListElements = findFirstOf<leafListElement_>(nodes);
+ firstLeafListElements != nodes.end() && firstLeafListElements != nodes.end() - 1) {
+ throw std::logic_error{"Cannot put any extra nodes after a leaf-list with element specification"};
+ }
+ if (auto firstList = findFirstOf<list_>(nodes);
+ firstList != nodes.end() && firstList != nodes.end() - 1) {
+ throw std::logic_error{
+ "A list with no key specification can be present only as a last item in a dataPath. Did you mean to use a schemaPath?"
+ };
+ }
+ }
+}
+}
+
+schemaPath_::schemaPath_()
+{
+}
+
+schemaPath_::schemaPath_(const Scope scope, const std::vector<schemaNode_>& nodes, const TrailingSlash trailingSlash)
+ : m_scope(scope)
+ , m_nodes(nodes)
+ , m_trailingSlash(trailingSlash)
+{
+ validatePathNodes(m_nodes);
+}
+
bool schemaPath_::operator==(const schemaPath_& b) const
{
if (this->m_nodes.size() != b.m_nodes.size())
@@ -122,6 +178,18 @@
return this->m_nodes == b.m_nodes;
}
+dataPath_::dataPath_()
+{
+}
+
+dataPath_::dataPath_(const Scope scope, const std::vector<dataNode_>& nodes, const TrailingSlash trailingSlash)
+ : m_scope(scope)
+ , m_nodes(nodes)
+ , m_trailingSlash(trailingSlash)
+{
+ validatePathNodes(m_nodes);
+}
+
bool dataPath_::operator==(const dataPath_& b) const
{
if (this->m_nodes.size() != b.m_nodes.size())
@@ -258,3 +326,29 @@
return res;
}
+
+namespace {
+template <typename NodeType>
+void impl_pushFragment(std::vector<NodeType>& where, const NodeType& what)
+{
+ if (std::holds_alternative<nodeup_>(what.m_suffix)) {
+ if (!where.empty()) { // Allow going up, when already at root
+ where.pop_back();
+ }
+ } else {
+ where.push_back(what);
+ }
+}
+}
+
+void schemaPath_::pushFragment(const schemaNode_& fragment)
+{
+ impl_pushFragment(m_nodes, fragment);
+ validatePathNodes(m_nodes);
+}
+
+void dataPath_::pushFragment(const dataNode_& fragment)
+{
+ impl_pushFragment(m_nodes, fragment);
+ validatePathNodes(m_nodes);
+}
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index da99a26..83727ff 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -113,17 +113,26 @@
};
struct schemaPath_ {
+ schemaPath_();
+ schemaPath_(const Scope scope, const std::vector<schemaNode_>& nodes, const TrailingSlash trailingSlash = TrailingSlash::NonPresent);
bool operator==(const schemaPath_& b) const;
Scope m_scope = Scope::Relative;
std::vector<schemaNode_> m_nodes;
TrailingSlash m_trailingSlash = TrailingSlash::NonPresent;
+ // @brief Pushes a new fragment. Pops a fragment if it's nodeup_
+ void pushFragment(const schemaNode_& fragment);
};
struct dataPath_ {
+ dataPath_();
+ dataPath_(const Scope scope, const std::vector<dataNode_>& nodes, const TrailingSlash trailingSlash = TrailingSlash::NonPresent);
bool operator==(const dataPath_& b) const;
Scope m_scope = Scope::Relative;
std::vector<dataNode_> m_nodes;
TrailingSlash m_trailingSlash = TrailingSlash::NonPresent;
+
+ // @brief Pushes a new fragment. Pops a fragment if it's nodeup_
+ void pushFragment(const dataNode_& fragment);
};
std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node);
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index cfd0a5d..3543e93 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -14,6 +14,28 @@
#include "interpreter.hpp"
#include "utils.hpp"
+struct pathToStringVisitor : boost::static_visitor<std::string> {
+ std::string operator()(const module_& path) const
+ {
+ using namespace std::string_literals;
+ return "/"s + boost::get<module_>(path).m_name + ":*";
+ }
+ std::string operator()(const schemaPath_& path) const
+ {
+ return pathToSchemaString(path, Prefixes::WhenNeeded);
+ }
+ std::string operator()(const dataPath_& path) const
+ {
+ return pathToDataString(path, Prefixes::WhenNeeded);
+ }
+};
+
+template <typename PathType>
+std::string pathToString(const PathType& path)
+{
+ return boost::apply_visitor(pathToStringVisitor(), path);
+}
+
void Interpreter::operator()(const commit_&) const
{
m_datastore.commitChanges();
@@ -36,12 +58,12 @@
data = identityRef;
}
}
- m_datastore.setLeaf(absolutePathFromCommand(set), data);
+ m_datastore.setLeaf(pathToString(toCanonicalPath(set.m_path)), data);
}
void Interpreter::operator()(const get_& get) const
{
- auto items = m_datastore.getItems(absolutePathFromCommand(get));
+ auto items = m_datastore.getItems(pathToString(toCanonicalPath(get.m_path)));
for (auto it = items.begin(); it != items.end(); it++) {
auto [path, value] = *it;
if (value.type() == typeid(special_) && boost::get<special_>(value).m_value == SpecialValue::LeafList) {
@@ -66,21 +88,21 @@
void Interpreter::operator()(const create_& create) const
{
if (std::holds_alternative<listElement_>(create.m_path.m_nodes.back().m_suffix))
- m_datastore.createListInstance(absolutePathFromCommand(create));
+ m_datastore.createListInstance(pathToString(toCanonicalPath(create.m_path)));
else if (std::holds_alternative<leafListElement_>(create.m_path.m_nodes.back().m_suffix))
- m_datastore.createLeafListInstance(absolutePathFromCommand(create));
+ m_datastore.createLeafListInstance(pathToString(toCanonicalPath(create.m_path)));
else
- m_datastore.createPresenceContainer(absolutePathFromCommand(create));
+ m_datastore.createPresenceContainer(pathToString(toCanonicalPath(create.m_path)));
}
void Interpreter::operator()(const delete_& delet) const
{
if (std::holds_alternative<container_>(delet.m_path.m_nodes.back().m_suffix))
- m_datastore.deletePresenceContainer(absolutePathFromCommand(delet));
+ m_datastore.deletePresenceContainer(pathToString(toCanonicalPath(delet.m_path)));
else if (std::holds_alternative<leafListElement_>(delet.m_path.m_nodes.back().m_suffix))
- m_datastore.deleteLeafListInstance(absolutePathFromCommand(delet));
+ m_datastore.deleteLeafListInstance(pathToString(toCanonicalPath(delet.m_path)));
else
- m_datastore.deleteListInstance(absolutePathFromCommand(delet));
+ m_datastore.deleteListInstance(pathToString(toCanonicalPath(delet.m_path)));
}
void Interpreter::operator()(const ls_& ls) const
@@ -92,24 +114,7 @@
recursion = Recursion::Recursive;
}
- std::set<ModuleNodePair> toPrint;
-
- auto pathArg = dataPathToSchemaPath(m_parser.currentPath());
- if (ls.m_path) {
- if (ls.m_path->type() == typeid(module_)) {
- toPrint = m_datastore.schema()->availableNodes(*ls.m_path, recursion);
- } else {
- auto schemaPath = anyPathToSchemaPath(*ls.m_path);
- if (schemaPath.m_scope == Scope::Absolute) {
- pathArg = schemaPath;
- } else {
- pathArg.m_nodes.insert(pathArg.m_nodes.end(), schemaPath.m_nodes.begin(), schemaPath.m_nodes.end());
- }
- toPrint = m_datastore.schema()->availableNodes(pathArg, recursion);
- }
- } else {
- toPrint = m_datastore.schema()->availableNodes(pathArg, recursion);
- }
+ auto toPrint = m_datastore.schema()->availableNodes(toCanonicalPath(ls.m_path), recursion);
for (const auto& it : toPrint) {
std::cout << (it.first ? *it.first + ":" : "" ) + it.second << std::endl;
@@ -183,7 +188,7 @@
void Interpreter::operator()(const describe_& describe) const
{
- auto path = absolutePathFromCommand(describe);
+ auto path = pathToString(toCanonicalPath(describe.m_path));
auto status = m_datastore.schema()->status(path);
auto statusStr = status == yang::Status::Deprecated ? " (deprecated)" :
status == yang::Status::Obsolete ? " (obsolete)" :
@@ -226,73 +231,69 @@
});
}
-template <typename T>
-std::string Interpreter::absolutePathFromCommand(const T& command) const
+template <typename PathType>
+boost::variant<dataPath_, schemaPath_, module_> Interpreter::toCanonicalPath(const boost::optional<PathType>& optPath) const
{
- if (command.m_path.m_scope == Scope::Absolute)
- return pathToDataString(command.m_path, Prefixes::WhenNeeded);
- else
- return joinPaths(m_parser.currentNode(), pathToDataString(command.m_path, Prefixes::WhenNeeded));
+ if (!optPath) {
+ return m_parser.currentPath();
+ }
+ return toCanonicalPath(*optPath);
}
-struct pathToStringVisitor : boost::static_visitor<std::string> {
- std::string operator()(const module_& path) const
- {
- using namespace std::string_literals;
- return "/"s + boost::get<module_>(path).m_name + ":*";
- }
- std::string operator()(const schemaPath_& path) const
- {
- return pathToSchemaString(path, Prefixes::WhenNeeded);
- }
- std::string operator()(const dataPath_& path) const
- {
- return pathToDataString(path, Prefixes::WhenNeeded);
- }
-};
+struct impl_toCanonicalPath {
+ const dataPath_& m_parserPath;
-struct getPathScopeVisitor : boost::static_visitor<Scope> {
- Scope operator()(const module_&) const
+ using ReturnType = boost::variant<dataPath_, schemaPath_, module_>;
+
+ impl_toCanonicalPath(const dataPath_& parserPath)
+ : m_parserPath(parserPath)
{
- throw std::logic_error("Interpreter: a top-level module has no scope.");
+ }
+ ReturnType operator()(const module_& path) const
+ {
+ return path;
+ }
+ ReturnType operator()(const schemaPath_& path) const
+ {
+ return impl(path);
+ }
+ ReturnType operator()(const dataPath_& path) const
+ {
+ return impl(path);
}
- template <typename T>
- Scope operator()(const T& path) const
+private:
+ template <typename PathType>
+ ReturnType impl(const PathType& suffix) const
{
- return path.m_scope;
- }
-};
+ PathType res = [this] {
+ if constexpr (std::is_same<PathType, schemaPath_>()) {
+ return dataPathToSchemaPath(m_parserPath);
+ } else {
+ return m_parserPath;
+ }
+ }();
-std::string Interpreter::absolutePathFromCommand(const get_& get) const
-{
- using namespace std::string_literals;
- if (!get.m_path) {
- return m_parser.currentNode();
- }
-
- const auto path = *get.m_path;
- if (path.type() == typeid(module_)) {
- return boost::apply_visitor(pathToStringVisitor(), path);
- } else {
- std::string pathString = boost::apply_visitor(pathToStringVisitor(), path);
- auto pathScope{boost::apply_visitor(getPathScopeVisitor(), path)};
-
- if (pathScope == Scope::Absolute) {
- return pathString;
- } else {
- return joinPaths(m_parser.currentNode(), pathString);
+ if (suffix.m_scope == Scope::Absolute) {
+ res = {Scope::Absolute, {}};
}
- }
-}
-std::string Interpreter::absolutePathFromCommand(const describe_& describe) const
+ for (const auto& fragment : suffix.m_nodes) {
+ res.pushFragment(fragment);
+ }
+
+ return res;
+ }
+};
+
+template <typename PathType>
+boost::variant<dataPath_, schemaPath_, module_> Interpreter::toCanonicalPath(const PathType& path) const
{
- auto pathStr = boost::apply_visitor(pathToStringVisitor(), describe.m_path);
- if (boost::apply_visitor(getPathScopeVisitor(), describe.m_path) == Scope::Absolute)
- return pathStr;
- else
- return joinPaths(m_parser.currentNode(), pathStr);
+ if constexpr (std::is_same<PathType, dataPath_>()) {
+ return impl_toCanonicalPath(m_parser.currentPath())(path);
+ } else {
+ return boost::apply_visitor(impl_toCanonicalPath(m_parser.currentPath()), path);
+ }
}
Interpreter::Interpreter(Parser& parser, DatastoreAccess& datastore)
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index bd90a60..e008375 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -12,6 +12,7 @@
#include "datastore_access.hpp"
#include "parser.hpp"
+
struct Interpreter : boost::static_visitor<void> {
Interpreter(Parser& parser, DatastoreAccess& datastore);
@@ -29,12 +30,14 @@
void operator()(const move_& move) const;
private:
- template <typename T>
- std::string absolutePathFromCommand(const T& command) const;
- std::string absolutePathFromCommand(const get_& command) const;
- std::string absolutePathFromCommand(const describe_& describe) const;
std::string buildTypeInfo(const std::string& path) const;
+ template <typename PathType>
+ boost::variant<dataPath_, schemaPath_, module_> toCanonicalPath(const boost::optional<PathType>& path) const;
+
+ template <typename PathType>
+ boost::variant<dataPath_, schemaPath_, module_> toCanonicalPath(const PathType& path) const;
+
Parser& m_parser;
DatastoreAccess& m_datastore;
};
diff --git a/src/parser.cpp b/src/parser.cpp
index ce583ea..bf00a7e 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -86,10 +86,7 @@
m_curDir = name;
} else {
for (const auto& it : name.m_nodes) {
- if (std::holds_alternative<nodeup_>(it.m_suffix))
- m_curDir.m_nodes.pop_back();
- else
- m_curDir.m_nodes.push_back(it);
+ m_curDir.pushFragment(it);
}
}
}
diff --git a/src/parser_context.cpp b/src/parser_context.cpp
index 723fbae..17f6604 100644
--- a/src/parser_context.cpp
+++ b/src/parser_context.cpp
@@ -39,18 +39,10 @@
void ParserContext::pushPathFragment(const dataNode_& node)
{
- auto pushNode = [] (auto& where, const auto& what) {
- if (std::holds_alternative<nodeup_>(what.m_suffix)) {
- where.m_nodes.pop_back();
- } else {
- where.m_nodes.push_back(what);
- }
- };
-
if (m_curPath.type() == typeid(dataPath_)) {
- pushNode(boost::get<dataPath_>(m_curPath), node);
+ boost::get<dataPath_>(m_curPath).pushFragment(node);
} else {
- pushNode(boost::get<schemaPath_>(m_curPath), dataNodeToSchemaNode(node));
+ boost::get<schemaPath_>(m_curPath).pushFragment(dataNodeToSchemaNode(node));
}
}
diff --git a/tests/cd.cpp b/tests/cd.cpp
index f85593b..e0d6293 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -136,6 +136,19 @@
SECTION("moving up")
{
+ SECTION("moving up when already in root")
+ {
+ input = "cd ..";
+ expected.m_path.m_nodes.push_back(dataNode_(nodeup_()));
+ }
+
+ SECTION("moving up TWICE when already in root")
+ {
+ input = "cd ../..";
+ expected.m_path.m_nodes.push_back(dataNode_(nodeup_()));
+ expected.m_path.m_nodes.push_back(dataNode_(nodeup_()));
+ }
+
SECTION("example:a/..")
{
input = "cd example:a/..";
diff --git a/tests/interpreter.cpp b/tests/interpreter.cpp
new file mode 100644
index 0000000..3acef2b
--- /dev/null
+++ b/tests/interpreter.cpp
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+*/
+
+#include <experimental/iterator>
+#include "trompeloeil_doctest.hpp"
+#include "ast_commands.hpp"
+#include "interpreter.hpp"
+#include "datastoreaccess_mock.hpp"
+#include "parser.hpp"
+#include "pretty_printers.hpp"
+#include "static_schema.hpp"
+
+class MockSchema : public trompeloeil::mock_interface<Schema> {
+public:
+ IMPLEMENT_CONST_MOCK1(defaultValue);
+ IMPLEMENT_CONST_MOCK1(description);
+ IMPLEMENT_CONST_MOCK2(availableNodes);
+ IMPLEMENT_CONST_MOCK1(isConfig);
+ MAKE_CONST_MOCK1(leafType, yang::TypeInfo(const std::string&), override);
+ MAKE_CONST_MOCK2(leafType, yang::TypeInfo(const schemaPath_&, const ModuleNodePair&), override);
+ IMPLEMENT_CONST_MOCK1(leafTypeName);
+ IMPLEMENT_CONST_MOCK1(isModule);
+ IMPLEMENT_CONST_MOCK1(leafrefPath);
+ IMPLEMENT_CONST_MOCK2(listHasKey);
+ IMPLEMENT_CONST_MOCK1(leafIsKey);
+ IMPLEMENT_CONST_MOCK1(listKeys);
+ MAKE_CONST_MOCK1(nodeType, yang::NodeTypes(const std::string&), override);
+ MAKE_CONST_MOCK2(nodeType, yang::NodeTypes(const schemaPath_&, const ModuleNodePair&), override);
+ IMPLEMENT_CONST_MOCK1(status);
+};
+
+TEST_CASE("interpreter tests")
+{
+ auto schema = std::make_shared<MockSchema>();
+ Parser parser(schema);
+ MockDatastoreAccess datastore;
+ std::vector<std::unique_ptr<trompeloeil::expectation>> expectations;
+
+ std::vector<command_> toInterpret;
+
+ SECTION("ls")
+ {
+ boost::variant<dataPath_, schemaPath_, module_> expectedPath;
+ boost::optional<boost::variant<dataPath_, schemaPath_, module_>> lsArg;
+ SECTION("cwd: /")
+ {
+ SECTION("arg: <none>")
+ {
+ expectedPath = dataPath_{};
+ }
+
+ SECTION("arg: ..")
+ {
+ lsArg = dataPath_{Scope::Relative, {dataNode_{nodeup_{}}}};
+ expectedPath = dataPath_{};
+ }
+
+ SECTION("arg: /..")
+ {
+ lsArg = dataPath_{Scope::Absolute, {dataNode_{nodeup_{}}}};
+ expectedPath = dataPath_{Scope::Absolute, {}};
+ }
+
+ SECTION("arg: /example:a/../example:a")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}},
+ {nodeup_{}},
+ {module_{"example"}, container_{"a"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ }
+
+ SECTION("arg: example:a")
+ {
+ lsArg = dataPath_{Scope::Relative, {{module_{"example"}, container_{"a"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ }
+
+ SECTION("arg: example:list")
+ {
+ lsArg = dataPath_{Scope::Relative, {{module_{"example"}, list_{"list"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ }
+
+ SECTION("arg: /example:a")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ }
+
+ SECTION("arg: /example:list")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ }
+
+ SECTION("arg example:*")
+ {
+ lsArg = module_{"example"};
+ expectedPath = module_{"example"};
+ }
+ }
+
+ SECTION("cwd: /example:a")
+ {
+ parser.changeNode({Scope::Relative, {{module_{"example"}, container_{"a"}}}});
+
+ SECTION("arg: <none>")
+ {
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ }
+
+ SECTION("arg: example:a2")
+ {
+ lsArg = dataPath_{Scope::Relative, {{container_{"a2"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}, {container_{"a2"}}}};
+ }
+
+ SECTION("arg: example:listInCont")
+ {
+ lsArg = dataPath_{Scope::Relative, {{list_{"listInCont"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}, {list_{"listInCont"}}}};
+ }
+
+ SECTION("arg: /example:a")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ }
+
+ SECTION("arg: /example:list")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ }
+ }
+ SECTION("cwd: /example:list")
+ {
+ parser.changeNode({Scope::Relative, {{module_{"example"}, list_{"list"}}}});
+
+ SECTION("arg: <none>")
+ {
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ }
+
+ SECTION("arg: example:contInList")
+ {
+ lsArg = schemaPath_{Scope::Relative, {{container_{"contInList"}}}};
+ expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}, {container_{"contInList"}}}};
+ }
+
+ SECTION("arg: /example:a")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
+ }
+
+ SECTION("arg: /example:list")
+ {
+ lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
+ }
+
+ SECTION("arg example:*")
+ {
+ lsArg = module_{"example"};
+ expectedPath = module_{"example"};
+ }
+ }
+ ls_ ls;
+ ls.m_path = lsArg;
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, schema()).RETURN(schema));
+ expectations.push_back(NAMED_REQUIRE_CALL(*schema, availableNodes(expectedPath, Recursion::NonRecursive)).RETURN(std::set<ModuleNodePair>{}));
+ toInterpret.push_back(ls);
+ }
+
+ SECTION("get")
+ {
+ using namespace std::string_literals;
+ DatastoreAccess::Tree treeReturned;
+ decltype(get_::m_path) inputPath;
+ std::string expectedPathArg;
+
+ SECTION("paths")
+ {
+ SECTION("/")
+ {
+ expectedPathArg = "/";
+ }
+
+ SECTION("module")
+ {
+ inputPath = module_{"mod"};
+ expectedPathArg = "/mod:*";
+ }
+
+ SECTION("path to a leaf")
+ {
+ expectedPathArg = "/mod:myLeaf";
+ Scope scope;
+ SECTION("cwd: /")
+ {
+ SECTION("absolute")
+ {
+ scope = Scope::Absolute;
+ }
+
+ SECTION("relative")
+ {
+ scope = Scope::Relative;
+ }
+
+ inputPath = dataPath_{scope, {dataNode_{{"mod"}, leaf_{"myLeaf"}}}};
+ }
+
+ SECTION("cwd: /mod:whatever")
+ {
+ parser.changeNode(dataPath_{Scope::Relative, {dataNode_{{"mod"}, container_{"whatever"}}}});
+ SECTION("absolute")
+ {
+ scope = Scope::Absolute;
+ inputPath = dataPath_{scope, {dataNode_{{"mod"}, leaf_{"myLeaf"}}}};
+ }
+
+ SECTION("relative")
+ {
+ scope = Scope::Relative;
+ inputPath = dataPath_{scope, {dataNode_{nodeup_{}}, dataNode_{{"mod"}, leaf_{"myLeaf"}}}};
+ }
+
+ }
+ }
+
+ SECTION("path to a list")
+ {
+ expectedPathArg = "/mod:myList[name='AHOJ']";
+ Scope scope;
+ SECTION("cwd: /")
+ {
+ SECTION("absolute")
+ {
+ scope = Scope::Absolute;
+ }
+
+ SECTION("relative")
+ {
+ scope = Scope::Relative;
+ }
+
+ inputPath = dataPath_{scope, {dataNode_{{"mod"}, listElement_{"myList", {{"name", "AHOJ"s}}}}}};
+ }
+
+ SECTION("cwd: /mod:whatever")
+ {
+ parser.changeNode(dataPath_{Scope::Relative, {dataNode_{{"mod"}, container_{"whatever"}}}});
+ SECTION("absolute")
+ {
+ scope = Scope::Absolute;
+ inputPath = dataPath_{scope, {dataNode_{{"mod"}, listElement_{"myList", {{"name", "AHOJ"s}}}}}};
+ }
+
+ SECTION("relative")
+ {
+ scope = Scope::Relative;
+ inputPath = dataPath_{scope, {dataNode_{nodeup_{}}, dataNode_{{"mod"}, listElement_{"myList", {{"name", "AHOJ"s}}}}}};
+ }
+ }
+ }
+ }
+
+ SECTION("trees")
+ {
+ expectedPathArg = "/";
+ SECTION("no leaflists")
+ {
+ treeReturned = {
+ {"/mod:AHOJ", 30},
+ {"/mod:CAU", std::string{"AYYY"}},
+ {"/mod:CUS", bool{true}}
+ };
+ }
+
+ SECTION("leaflist at the beginning of a tree")
+ {
+ treeReturned = {
+ {"/mod:addresses", special_{SpecialValue::LeafList}},
+ {"/mod:addresses[.='0.0.0.0']", std::string{"0.0.0.0"}},
+ {"/mod:addresses[.='127.0.0.1']", std::string{"127.0.0.1"}},
+ {"/mod:addresses[.='192.168.0.1']", std::string{"192.168.0.1"}},
+ {"/mod:AHOJ", 30},
+ {"/mod:CAU", std::string{"AYYY"}},
+ };
+ }
+
+ SECTION("leaflist in the middle of a tree")
+ {
+ treeReturned = {
+ {"/mod:AHOJ", 30},
+ {"/mod:addresses", special_{SpecialValue::LeafList}},
+ {"/mod:addresses[.='0.0.0.0']", std::string{"0.0.0.0"}},
+ {"/mod:addresses[.='127.0.0.1']", std::string{"127.0.0.1"}},
+ {"/mod:addresses[.='192.168.0.1']", std::string{"192.168.0.1"}},
+ {"/mod:CAU", std::string{"AYYY"}},
+ };
+ }
+
+ SECTION("leaflist at the end of a tree")
+ {
+ treeReturned = {
+ {"/mod:AHOJ", 30},
+ {"/mod:CAU", std::string{"AYYY"}},
+ {"/mod:addresses", special_{SpecialValue::LeafList}},
+ {"/mod:addresses[.='0.0.0.0']", std::string{"0.0.0.0"}},
+ {"/mod:addresses[.='127.0.0.1']", std::string{"127.0.0.1"}},
+ {"/mod:addresses[.='192.168.0.1']", std::string{"192.168.0.1"}},
+ };
+ }
+ }
+
+ get_ getCmd;
+ getCmd.m_path = inputPath;
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, getItems(expectedPathArg)).RETURN(treeReturned));
+ toInterpret.push_back(getCmd);
+ }
+
+ SECTION("create/delete")
+ {
+ using namespace std::string_literals;
+ dataPath_ inputPath;
+
+ SECTION("list instance")
+ {
+ inputPath.m_nodes = {dataNode_{{"mod"}, listElement_{"department", {{"name", "engineering"s}}}}};
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, createListInstance("/mod:department[name='engineering']")));
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, deleteListInstance("/mod:department[name='engineering']")));
+ }
+
+ SECTION("leaflist instance")
+ {
+ inputPath.m_nodes = {dataNode_{{"mod"}, leafListElement_{"addresses", "127.0.0.1"s}}};
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, createLeafListInstance("/mod:addresses[.='127.0.0.1']")));
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, deleteLeafListInstance("/mod:addresses[.='127.0.0.1']")));
+ }
+
+ SECTION("presence container")
+ {
+ inputPath.m_nodes = {dataNode_{{"mod"}, container_{"pContainer"}}};
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, createPresenceContainer("/mod:pContainer")));
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, deletePresenceContainer("/mod:pContainer")));
+ }
+
+ create_ createCmd;
+ createCmd.m_path = inputPath;
+ delete_ deleteCmd;
+ deleteCmd.m_path = inputPath;
+ toInterpret.push_back(createCmd);
+ toInterpret.push_back(deleteCmd);
+ }
+
+ SECTION("commit")
+ {
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, commitChanges()));
+ toInterpret.push_back(commit_{});
+ }
+
+ SECTION("discard")
+ {
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, discardChanges()));
+ toInterpret.push_back(discard_{});
+ }
+
+
+ SECTION("set")
+ {
+ dataPath_ inputPath;
+ leaf_data_ inputData;
+
+ SECTION("setting identityRef without module") // The parser has to fill in the module
+ {
+ inputPath.m_nodes = {dataNode_{{"mod"}, leaf_{"animal"}}};
+ inputData = identityRef_{"Doge"};
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, setLeaf("/mod:animal", identityRef_{"mod", "Doge"})));
+ }
+
+
+ set_ setCmd;
+ setCmd.m_path = inputPath;
+ setCmd.m_data = inputData;
+ toInterpret.push_back(setCmd);
+ }
+
+
+ SECTION("copy")
+ {
+ SECTION("running -> startup")
+ {
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, copyConfig(Datastore::Running, Datastore::Startup)));
+ toInterpret.push_back(copy_{{}, Datastore::Running, Datastore::Startup});
+ }
+
+ SECTION("startup -> running")
+ {
+ expectations.push_back(NAMED_REQUIRE_CALL(datastore, copyConfig(Datastore::Startup, Datastore::Running)));
+ toInterpret.push_back(copy_{{}, Datastore::Startup, Datastore::Running});
+ }
+ }
+
+ for (const auto& command : toInterpret) {
+ boost::apply_visitor(Interpreter(parser, datastore), command);
+ }
+}
diff --git a/tests/ls_interpreter.cpp b/tests/ls_interpreter.cpp
deleted file mode 100644
index 2ae0b7c..0000000
--- a/tests/ls_interpreter.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
- * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
- *
- * Written by Václav Kubernát <kubervac@fit.cvut.cz>
- *
-*/
-
-#include <experimental/iterator>
-#include "trompeloeil_doctest.hpp"
-#include "ast_commands.hpp"
-#include "interpreter.hpp"
-#include "datastoreaccess_mock.hpp"
-#include "parser.hpp"
-#include "pretty_printers.hpp"
-#include "static_schema.hpp"
-
-class MockSchema : public trompeloeil::mock_interface<Schema> {
-public:
- IMPLEMENT_CONST_MOCK1(defaultValue);
- IMPLEMENT_CONST_MOCK1(description);
- IMPLEMENT_CONST_MOCK2(availableNodes);
- IMPLEMENT_CONST_MOCK1(isConfig);
- MAKE_CONST_MOCK1(leafType, yang::TypeInfo(const std::string&), override);
- MAKE_CONST_MOCK2(leafType, yang::TypeInfo(const schemaPath_&, const ModuleNodePair&), override);
- IMPLEMENT_CONST_MOCK1(leafTypeName);
- IMPLEMENT_CONST_MOCK1(isModule);
- IMPLEMENT_CONST_MOCK1(leafrefPath);
- IMPLEMENT_CONST_MOCK2(listHasKey);
- IMPLEMENT_CONST_MOCK1(leafIsKey);
- IMPLEMENT_CONST_MOCK1(listKeys);
- MAKE_CONST_MOCK1(nodeType, yang::NodeTypes(const std::string&), override);
- MAKE_CONST_MOCK2(nodeType, yang::NodeTypes(const schemaPath_&, const ModuleNodePair&), override);
- IMPLEMENT_CONST_MOCK1(status);
-};
-
-TEST_CASE("ls interpreter")
-{
- auto schema = std::make_shared<MockSchema>();
- Parser parser(schema);
-
- boost::variant<dataPath_, schemaPath_, module_> expectedPath;
- boost::optional<boost::variant<dataPath_, schemaPath_, module_>> lsArg{boost::none};
- SECTION("cwd: /")
- {
- SECTION("arg: <none>")
- {
- expectedPath = schemaPath_{};
- }
-
- SECTION("arg: example:a")
- {
- lsArg = dataPath_{Scope::Relative, {{module_{"example"}, container_{"a"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- }
-
- SECTION("arg: example:list")
- {
- lsArg = dataPath_{Scope::Relative, {{module_{"example"}, list_{"list"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- }
-
- SECTION("arg: /example:a")
- {
- lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- }
-
- SECTION("arg: /example:list")
- {
- lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- }
-
- SECTION("arg example:*")
- {
- lsArg = module_{"example"};
- expectedPath = module_{"example"};
- }
- }
-
- SECTION("cwd: /example:a")
- {
- parser.changeNode({Scope::Relative, {{module_{"example"}, container_{"a"}}}});
-
- SECTION("arg: <none>")
- {
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- }
-
- SECTION("arg: example:a2")
- {
- lsArg = dataPath_{Scope::Relative, {{container_{"a2"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}, {container_{"a2"}}}};
- }
-
- SECTION("arg: example:listInCont")
- {
- lsArg = dataPath_{Scope::Relative, {{list_{"listInCont"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}, {list_{"listInCont"}}}};
- }
-
- SECTION("arg: /example:a")
- {
- lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- }
-
- SECTION("arg: /example:list")
- {
- lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- }
- }
- SECTION("cwd: /example:list")
- {
- parser.changeNode({Scope::Relative, {{module_{"example"}, list_{"list"}}}});
-
- SECTION("arg: <none>")
- {
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- }
-
- SECTION("arg: example:contInList")
- {
- lsArg = dataPath_{Scope::Relative, {{container_{"contInList"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}, {container_{"contInList"}}}};
- }
-
- SECTION("arg: /example:a")
- {
- lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
- }
-
- SECTION("arg: /example:list")
- {
- lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
- }
-
- SECTION("arg example:*")
- {
- lsArg = module_{"example"};
- expectedPath = module_{"example"};
- }
- }
- MockDatastoreAccess datastore;
- REQUIRE_CALL(datastore, schema()).RETURN(schema);
- ls_ ls;
- ls.m_path = lsArg;
- REQUIRE_CALL(*schema, availableNodes(expectedPath, Recursion::NonRecursive)).RETURN(std::set<ModuleNodePair>{});
- Interpreter(parser, datastore)(ls);
-}