blob: 89fbd29589adda4e7136a08570ee56334fe28814 [file] [log] [blame]
Václav Kubernát5452ede2020-06-10 12:05:11 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
9#include <experimental/iterator>
10#include "trompeloeil_doctest.hpp"
11#include "ast_commands.hpp"
12#include "interpreter.hpp"
13#include "datastoreaccess_mock.hpp"
14#include "parser.hpp"
15#include "pretty_printers.hpp"
16#include "static_schema.hpp"
17
18class MockSchema : public trompeloeil::mock_interface<Schema> {
19public:
20 IMPLEMENT_CONST_MOCK1(defaultValue);
21 IMPLEMENT_CONST_MOCK1(description);
22 IMPLEMENT_CONST_MOCK2(availableNodes);
23 IMPLEMENT_CONST_MOCK1(isConfig);
24 MAKE_CONST_MOCK1(leafType, yang::TypeInfo(const std::string&), override);
25 MAKE_CONST_MOCK2(leafType, yang::TypeInfo(const schemaPath_&, const ModuleNodePair&), override);
26 IMPLEMENT_CONST_MOCK1(leafTypeName);
27 IMPLEMENT_CONST_MOCK1(isModule);
28 IMPLEMENT_CONST_MOCK1(leafrefPath);
29 IMPLEMENT_CONST_MOCK2(listHasKey);
30 IMPLEMENT_CONST_MOCK1(leafIsKey);
31 IMPLEMENT_CONST_MOCK1(listKeys);
32 MAKE_CONST_MOCK1(nodeType, yang::NodeTypes(const std::string&), override);
33 MAKE_CONST_MOCK2(nodeType, yang::NodeTypes(const schemaPath_&, const ModuleNodePair&), override);
34 IMPLEMENT_CONST_MOCK1(status);
35};
36
37TEST_CASE("interpreter tests")
38{
39 auto schema = std::make_shared<MockSchema>();
40 Parser parser(schema);
Václav Kubernát48e9dfa2020-07-08 10:55:12 +020041 auto datastore = std::make_shared<MockDatastoreAccess>();
42 ProxyDatastore proxyDatastore(datastore);
Václav Kubernát5452ede2020-06-10 12:05:11 +020043 std::vector<std::unique_ptr<trompeloeil::expectation>> expectations;
44
45 std::vector<command_> toInterpret;
46
47 SECTION("ls")
48 {
49 boost::variant<dataPath_, schemaPath_, module_> expectedPath;
50 boost::optional<boost::variant<dataPath_, schemaPath_, module_>> lsArg;
51 SECTION("cwd: /")
52 {
53 SECTION("arg: <none>")
54 {
55 expectedPath = dataPath_{};
56 }
57
Václav Kubernát59be0de2020-06-15 13:58:45 +020058 SECTION("arg: ..")
59 {
60 lsArg = dataPath_{Scope::Relative, {dataNode_{nodeup_{}}}};
61 expectedPath = dataPath_{};
62 }
63
64 SECTION("arg: /..")
65 {
66 lsArg = dataPath_{Scope::Absolute, {dataNode_{nodeup_{}}}};
67 expectedPath = dataPath_{Scope::Absolute, {}};
68 }
69
70 SECTION("arg: /example:a/../example:a")
71 {
72 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}},
73 {nodeup_{}},
74 {module_{"example"}, container_{"a"}}}};
75 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
76 }
77
Václav Kubernát5452ede2020-06-10 12:05:11 +020078 SECTION("arg: example:a")
79 {
80 lsArg = dataPath_{Scope::Relative, {{module_{"example"}, container_{"a"}}}};
81 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
82 }
83
84 SECTION("arg: example:list")
85 {
86 lsArg = dataPath_{Scope::Relative, {{module_{"example"}, list_{"list"}}}};
87 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
88 }
89
90 SECTION("arg: /example:a")
91 {
92 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
93 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
94 }
95
96 SECTION("arg: /example:list")
97 {
98 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
99 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
100 }
101
102 SECTION("arg example:*")
103 {
104 lsArg = module_{"example"};
105 expectedPath = module_{"example"};
106 }
107 }
108
109 SECTION("cwd: /example:a")
110 {
111 parser.changeNode({Scope::Relative, {{module_{"example"}, container_{"a"}}}});
112
113 SECTION("arg: <none>")
114 {
115 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
116 }
117
118 SECTION("arg: example:a2")
119 {
120 lsArg = dataPath_{Scope::Relative, {{container_{"a2"}}}};
121 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}, {container_{"a2"}}}};
122 }
123
124 SECTION("arg: example:listInCont")
125 {
126 lsArg = dataPath_{Scope::Relative, {{list_{"listInCont"}}}};
127 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}, {list_{"listInCont"}}}};
128 }
129
130 SECTION("arg: /example:a")
131 {
132 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
133 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
134 }
135
136 SECTION("arg: /example:list")
137 {
138 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
139 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
140 }
141 }
142 SECTION("cwd: /example:list")
143 {
144 parser.changeNode({Scope::Relative, {{module_{"example"}, list_{"list"}}}});
145
146 SECTION("arg: <none>")
147 {
148 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
149 }
150
151 SECTION("arg: example:contInList")
152 {
153 lsArg = schemaPath_{Scope::Relative, {{container_{"contInList"}}}};
154 expectedPath = schemaPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}, {container_{"contInList"}}}};
155 }
156
157 SECTION("arg: /example:a")
158 {
159 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
160 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, container_{"a"}}}};
161 }
162
163 SECTION("arg: /example:list")
164 {
165 lsArg = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
166 expectedPath = dataPath_{Scope::Absolute, {{module_{"example"}, list_{"list"}}}};
167 }
168
169 SECTION("arg example:*")
170 {
171 lsArg = module_{"example"};
172 expectedPath = module_{"example"};
173 }
174 }
175 ls_ ls;
176 ls.m_path = lsArg;
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200177 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, schema()).RETURN(schema));
Václav Kubernátfaacd022020-07-08 16:44:38 +0200178 expectations.emplace_back(NAMED_REQUIRE_CALL(*schema, availableNodes(expectedPath, Recursion::NonRecursive)).RETURN(std::set<ModuleNodePair>{}));
179 toInterpret.emplace_back(ls);
Václav Kubernát5452ede2020-06-10 12:05:11 +0200180 }
181
182 SECTION("get")
183 {
184 using namespace std::string_literals;
185 DatastoreAccess::Tree treeReturned;
186 decltype(get_::m_path) inputPath;
187 std::string expectedPathArg;
188
189 SECTION("paths")
190 {
191 SECTION("/")
192 {
193 expectedPathArg = "/";
194 }
195
196 SECTION("module")
197 {
198 inputPath = module_{"mod"};
199 expectedPathArg = "/mod:*";
200 }
201
202 SECTION("path to a leaf")
203 {
204 expectedPathArg = "/mod:myLeaf";
205 Scope scope;
206 SECTION("cwd: /")
207 {
208 SECTION("absolute")
209 {
210 scope = Scope::Absolute;
211 }
212
213 SECTION("relative")
214 {
215 scope = Scope::Relative;
216 }
217
218 inputPath = dataPath_{scope, {dataNode_{{"mod"}, leaf_{"myLeaf"}}}};
219 }
220
221 SECTION("cwd: /mod:whatever")
222 {
223 parser.changeNode(dataPath_{Scope::Relative, {dataNode_{{"mod"}, container_{"whatever"}}}});
224 SECTION("absolute")
225 {
226 scope = Scope::Absolute;
227 inputPath = dataPath_{scope, {dataNode_{{"mod"}, leaf_{"myLeaf"}}}};
228 }
229
230 SECTION("relative")
231 {
232 scope = Scope::Relative;
233 inputPath = dataPath_{scope, {dataNode_{nodeup_{}}, dataNode_{{"mod"}, leaf_{"myLeaf"}}}};
234 }
235
236 }
237 }
238
239 SECTION("path to a list")
240 {
241 expectedPathArg = "/mod:myList[name='AHOJ']";
242 Scope scope;
243 SECTION("cwd: /")
244 {
245 SECTION("absolute")
246 {
247 scope = Scope::Absolute;
248 }
249
250 SECTION("relative")
251 {
252 scope = Scope::Relative;
253 }
254
255 inputPath = dataPath_{scope, {dataNode_{{"mod"}, listElement_{"myList", {{"name", "AHOJ"s}}}}}};
256 }
257
258 SECTION("cwd: /mod:whatever")
259 {
260 parser.changeNode(dataPath_{Scope::Relative, {dataNode_{{"mod"}, container_{"whatever"}}}});
261 SECTION("absolute")
262 {
263 scope = Scope::Absolute;
264 inputPath = dataPath_{scope, {dataNode_{{"mod"}, listElement_{"myList", {{"name", "AHOJ"s}}}}}};
265 }
266
267 SECTION("relative")
268 {
269 scope = Scope::Relative;
270 inputPath = dataPath_{scope, {dataNode_{nodeup_{}}, dataNode_{{"mod"}, listElement_{"myList", {{"name", "AHOJ"s}}}}}};
271 }
272 }
273 }
274 }
275
276 SECTION("trees")
277 {
278 expectedPathArg = "/";
279 SECTION("no leaflists")
280 {
281 treeReturned = {
282 {"/mod:AHOJ", 30},
283 {"/mod:CAU", std::string{"AYYY"}},
284 {"/mod:CUS", bool{true}}
285 };
286 }
287
288 SECTION("leaflist at the beginning of a tree")
289 {
290 treeReturned = {
291 {"/mod:addresses", special_{SpecialValue::LeafList}},
292 {"/mod:addresses[.='0.0.0.0']", std::string{"0.0.0.0"}},
293 {"/mod:addresses[.='127.0.0.1']", std::string{"127.0.0.1"}},
294 {"/mod:addresses[.='192.168.0.1']", std::string{"192.168.0.1"}},
295 {"/mod:AHOJ", 30},
296 {"/mod:CAU", std::string{"AYYY"}},
297 };
298 }
299
300 SECTION("leaflist in the middle of a tree")
301 {
302 treeReturned = {
303 {"/mod:AHOJ", 30},
304 {"/mod:addresses", special_{SpecialValue::LeafList}},
305 {"/mod:addresses[.='0.0.0.0']", std::string{"0.0.0.0"}},
306 {"/mod:addresses[.='127.0.0.1']", std::string{"127.0.0.1"}},
307 {"/mod:addresses[.='192.168.0.1']", std::string{"192.168.0.1"}},
308 {"/mod:CAU", std::string{"AYYY"}},
309 };
310 }
311
312 SECTION("leaflist at the end of a tree")
313 {
314 treeReturned = {
315 {"/mod:AHOJ", 30},
316 {"/mod:CAU", std::string{"AYYY"}},
317 {"/mod:addresses", special_{SpecialValue::LeafList}},
318 {"/mod:addresses[.='0.0.0.0']", std::string{"0.0.0.0"}},
319 {"/mod:addresses[.='127.0.0.1']", std::string{"127.0.0.1"}},
320 {"/mod:addresses[.='192.168.0.1']", std::string{"192.168.0.1"}},
321 };
322 }
323 }
324
325 get_ getCmd;
326 getCmd.m_path = inputPath;
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200327 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, getItems(expectedPathArg)).RETURN(treeReturned));
Václav Kubernátfaacd022020-07-08 16:44:38 +0200328 toInterpret.emplace_back(getCmd);
Václav Kubernát5452ede2020-06-10 12:05:11 +0200329 }
330
331 SECTION("create/delete")
332 {
333 using namespace std::string_literals;
334 dataPath_ inputPath;
335
336 SECTION("list instance")
337 {
338 inputPath.m_nodes = {dataNode_{{"mod"}, listElement_{"department", {{"name", "engineering"s}}}}};
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200339 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, createItem("/mod:department[name='engineering']")));
340 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, deleteItem("/mod:department[name='engineering']")));
Václav Kubernát5452ede2020-06-10 12:05:11 +0200341 }
342
343 SECTION("leaflist instance")
344 {
345 inputPath.m_nodes = {dataNode_{{"mod"}, leafListElement_{"addresses", "127.0.0.1"s}}};
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200346 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, createItem("/mod:addresses[.='127.0.0.1']")));
347 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, deleteItem("/mod:addresses[.='127.0.0.1']")));
Václav Kubernát5452ede2020-06-10 12:05:11 +0200348 }
349
350 SECTION("presence container")
351 {
352 inputPath.m_nodes = {dataNode_{{"mod"}, container_{"pContainer"}}};
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200353 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, createItem("/mod:pContainer")));
354 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, deleteItem("/mod:pContainer")));
Václav Kubernát5452ede2020-06-10 12:05:11 +0200355 }
356
357 create_ createCmd;
358 createCmd.m_path = inputPath;
359 delete_ deleteCmd;
360 deleteCmd.m_path = inputPath;
Václav Kubernátfaacd022020-07-08 16:44:38 +0200361 toInterpret.emplace_back(createCmd);
362 toInterpret.emplace_back(deleteCmd);
Václav Kubernát5452ede2020-06-10 12:05:11 +0200363 }
364
Jan Kundrátb7206ad2020-06-18 21:08:14 +0200365 SECTION("delete a leaf")
366 {
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200367 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, deleteItem("/mod:someLeaf")));
Jan Kundrátb7206ad2020-06-18 21:08:14 +0200368 delete_ deleteCmd;
369 deleteCmd.m_path = {Scope::Absolute, {dataNode_{{"mod"}, leaf_{"someLeaf"}}, }};
Václav Kubernátfaacd022020-07-08 16:44:38 +0200370 toInterpret.emplace_back(deleteCmd);
Jan Kundrátb7206ad2020-06-18 21:08:14 +0200371 }
372
Václav Kubernát5452ede2020-06-10 12:05:11 +0200373 SECTION("commit")
374 {
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200375 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, commitChanges()));
Václav Kubernátfaacd022020-07-08 16:44:38 +0200376 toInterpret.emplace_back(commit_{});
Václav Kubernát5452ede2020-06-10 12:05:11 +0200377 }
378
379 SECTION("discard")
380 {
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200381 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, discardChanges()));
Václav Kubernátfaacd022020-07-08 16:44:38 +0200382 toInterpret.emplace_back(discard_{});
Václav Kubernát5452ede2020-06-10 12:05:11 +0200383 }
384
385
386 SECTION("set")
387 {
388 dataPath_ inputPath;
389 leaf_data_ inputData;
390
391 SECTION("setting identityRef without module") // The parser has to fill in the module
392 {
393 inputPath.m_nodes = {dataNode_{{"mod"}, leaf_{"animal"}}};
394 inputData = identityRef_{"Doge"};
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200395 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, setLeaf("/mod:animal", identityRef_{"mod", "Doge"})));
Václav Kubernát5452ede2020-06-10 12:05:11 +0200396 }
397
398
399 set_ setCmd;
400 setCmd.m_path = inputPath;
401 setCmd.m_data = inputData;
Václav Kubernátfaacd022020-07-08 16:44:38 +0200402 toInterpret.emplace_back(setCmd);
Václav Kubernát5452ede2020-06-10 12:05:11 +0200403 }
404
405
406 SECTION("copy")
407 {
408 SECTION("running -> startup")
409 {
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200410 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, copyConfig(Datastore::Running, Datastore::Startup)));
Václav Kubernátfaacd022020-07-08 16:44:38 +0200411 toInterpret.emplace_back(copy_{{}, Datastore::Running, Datastore::Startup});
Václav Kubernát5452ede2020-06-10 12:05:11 +0200412 }
413
414 SECTION("startup -> running")
415 {
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200416 expectations.emplace_back(NAMED_REQUIRE_CALL(*datastore, copyConfig(Datastore::Startup, Datastore::Running)));
Václav Kubernátfaacd022020-07-08 16:44:38 +0200417 toInterpret.emplace_back(copy_{{}, Datastore::Startup, Datastore::Running});
Václav Kubernát5452ede2020-06-10 12:05:11 +0200418 }
419 }
420
421 for (const auto& command : toInterpret) {
Václav Kubernát48e9dfa2020-07-08 10:55:12 +0200422 boost::apply_visitor(Interpreter(parser, proxyDatastore), command);
Václav Kubernát5452ede2020-06-10 12:05:11 +0200423 }
424}