blob: 84c6705e7488941fdd8f5ebeb15519fd3521fcd2 [file] [log] [blame]
Václav Kubernát73109382018-09-14 19:52:03 +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
Václav Kubernát26b56082020-02-03 18:28:56 +01009#include "trompeloeil_doctest.hpp"
Jan Kundrát6ee84792020-01-24 01:43:36 +010010#include <sysrepo-cpp/Session.hpp>
Václav Kubernáte7248b22020-06-26 15:38:59 +020011#include "proxy_datastore.hpp"
Václav Kubernátb4e5b182020-11-16 19:55:09 +010012#include "yang_schema.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020013
Václav Kubernátc31bd602019-03-07 11:44:48 +010014#ifdef sysrepo_BACKEND
Václav Kubernát73109382018-09-14 19:52:03 +020015#include "sysrepo_access.hpp"
Jan Kundrát7ec214d2020-06-19 17:05:07 +020016using OnInvalidSchemaPathCreate = DatastoreException;
Václav Kubernát654303f2020-07-31 13:16:54 +020017using OnInvalidSchemaPathDelete = DatastoreException;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020018using OnInvalidSchemaPathMove = sysrepo::sysrepo_exception;
Václav Kubernát40776132021-02-03 08:47:33 +010019using OnInvalidRpcPath = std::runtime_error;
Václav Kubernát2edfe542021-02-03 08:08:29 +010020using OnInvalidRpcInput = sysrepo::sysrepo_exception;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020021using OnKeyNotFound = void;
Václav Kubernáta8789602020-07-20 15:18:19 +020022using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010023#elif defined(netconf_BACKEND)
Jan Kundrát7ec214d2020-06-19 17:05:07 +020024using OnInvalidSchemaPathCreate = std::runtime_error;
25using OnInvalidSchemaPathDelete = std::runtime_error;
26using OnInvalidSchemaPathMove = std::runtime_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020027using OnInvalidRpcPath = std::runtime_error;
Václav Kubernát2edfe542021-02-03 08:08:29 +010028using OnInvalidRpcInput = std::runtime_error;
Jan Kundrát7ec214d2020-06-19 17:05:07 +020029using OnKeyNotFound = std::runtime_error;
Václav Kubernáta8789602020-07-20 15:18:19 +020030using OnExec = void;
Václav Kubernátc31bd602019-03-07 11:44:48 +010031#include "netconf_access.hpp"
Václav Kubernát74487df2020-06-04 01:29:28 +020032#elif defined(yang_BACKEND)
33#include <fstream>
34#include "yang_access.hpp"
35#include "yang_access_test_vars.hpp"
36using OnInvalidSchemaPathCreate = DatastoreException;
37using OnInvalidSchemaPathDelete = DatastoreException;
38using OnInvalidSchemaPathMove = DatastoreException;
39using OnInvalidRpcPath = DatastoreException;
Václav Kubernát2edfe542021-02-03 08:08:29 +010040using OnInvalidRpcInput = std::logic_error;
Václav Kubernát74487df2020-06-04 01:29:28 +020041using OnKeyNotFound = DatastoreException;
Václav Kubernáta8789602020-07-20 15:18:19 +020042using OnExec = std::logic_error;
Václav Kubernátc31bd602019-03-07 11:44:48 +010043#else
44#error "Unknown backend"
45#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +010046#include "pretty_printers.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020047#include "sysrepo_subscription.hpp"
Václav Kubernát8e121ff2019-10-15 15:47:45 +020048#include "utils.hpp"
Václav Kubernát73109382018-09-14 19:52:03 +020049
Jan Kundrát6ee84792020-01-24 01:43:36 +010050using namespace std::literals::string_literals;
51
Václav Kubernát69aabe92020-01-24 16:53:12 +010052class MockRecorder : public trompeloeil::mock_interface<Recorder> {
Václav Kubernát73109382018-09-14 19:52:03 +020053public:
Václav Kubernát69aabe92020-01-24 16:53:12 +010054 IMPLEMENT_MOCK3(write);
Václav Kubernát73109382018-09-14 19:52:03 +020055};
56
Jan Kundrátbb525b42020-02-04 11:56:59 +010057class MockDataSupplier : public trompeloeil::mock_interface<DataSupplier> {
58public:
59 IMPLEMENT_CONST_MOCK1(get_data);
60};
61
Jan Kundrát7ec214d2020-06-19 17:05:07 +020062namespace {
63template <class ...> constexpr std::false_type always_false [[maybe_unused]] {};
64template <class Exception, typename Callable> void catching(const Callable& what) {
65
66 if constexpr (std::is_same_v<Exception, void>) {
Jan Kundrát3867c9e2020-06-18 20:26:45 +020067 what();
Jan Kundrát7ec214d2020-06-19 17:05:07 +020068 } else if constexpr (std::is_same<Exception, std::runtime_error>()) {
69 // cannot use REQUIRE_THROWS_AS(..., Exception) directly because that one
70 // needs an extra `typename` deep in the bowels of doctest
71 REQUIRE_THROWS_AS(what(), std::runtime_error);
Václav Kubernát74487df2020-06-04 01:29:28 +020072 } else if constexpr (std::is_same<Exception, std::logic_error>()) {
73 REQUIRE_THROWS_AS(what(), std::logic_error);
Jan Kundrát7ec214d2020-06-19 17:05:07 +020074 } else if constexpr (std::is_same<Exception, DatastoreException>()) {
75 REQUIRE_THROWS_AS(what(), DatastoreException);
76 } else if constexpr (std::is_same<Exception, sysrepo::sysrepo_exception>()) {
77 REQUIRE_THROWS_AS(what(), sysrepo::sysrepo_exception);
78 } else {
79 static_assert(always_false<Exception>); // https://stackoverflow.com/a/53945549/2245623
Jan Kundrát3867c9e2020-06-18 20:26:45 +020080 }
81}
Jan Kundrát7ec214d2020-06-19 17:05:07 +020082}
Jan Kundrát3867c9e2020-06-18 20:26:45 +020083
Václav Kubernát74487df2020-06-04 01:29:28 +020084#if defined(yang_BACKEND)
85class TestYangAccess : public YangAccess {
86public:
87 void commitChanges() override
88 {
89 YangAccess::commitChanges();
90 dumpToSysrepo();
91 }
92
93 void copyConfig(const Datastore source, const Datastore destination) override
94 {
95 YangAccess::copyConfig(source, destination);
96 dumpToSysrepo();
97 }
98
99private:
100 void dumpToSysrepo()
101 {
102 {
103 std::ofstream of(testConfigFile);
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200104 of << dump(DataFormat::Xml);
Václav Kubernát74487df2020-06-04 01:29:28 +0200105 }
Jan Kundrát86840ec2021-01-27 15:38:08 +0100106 auto command = std::string(sysrepocfgExecutable) + " --import=" + testConfigFile + " --format=xml --datastore=running --module=example-schema";
Václav Kubernát74487df2020-06-04 01:29:28 +0200107 REQUIRE(std::system(command.c_str()) == 0);
108 }
109};
110#endif
111
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200112TEST_CASE("setting/getting values")
Václav Kubernát73109382018-09-14 19:52:03 +0200113{
Václav Kubernát654303f2020-07-31 13:16:54 +0200114 sr_log_stderr(SR_LL_DBG);
Václav Kubernát73109382018-09-14 19:52:03 +0200115 trompeloeil::sequence seq1;
Václav Kubernáteaf56682021-02-22 17:15:41 +0100116 MockRecorder mockRunning;
117 MockRecorder mockStartup;
Václav Kubernát654303f2020-07-31 13:16:54 +0200118 {
119 auto conn = std::make_shared<sysrepo::Connection>();
120 auto sess = std::make_shared<sysrepo::Session>(conn);
121 sess->copy_config(SR_DS_STARTUP, "example-schema", 1000, true);
122 }
Václav Kubernáteaf56682021-02-22 17:15:41 +0100123 SysrepoSubscription subRunning("example-schema", &mockRunning);
124 SysrepoSubscription subStartup("example-schema", &mockStartup, SR_DS_STARTUP);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100125
126#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200127 SysrepoAccess datastore(Datastore::Running);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100128#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200129 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
130 NetconfAccess datastore(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200131#elif defined(yang_BACKEND)
132 TestYangAccess datastore;
133 datastore.addSchemaDir(schemaDir);
134 datastore.addSchemaFile(exampleSchemaFile);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100135#else
136#error "Unknown backend"
137#endif
Václav Kubernát73109382018-09-14 19:52:03 +0200138
Václav Kubernát69aabe92020-01-24 16:53:12 +0100139
Václav Kubernát134d78f2019-09-03 16:42:29 +0200140 SECTION("set leafInt8 to -128")
Václav Kubernát73109382018-09-14 19:52:03 +0200141 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100142 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt8", std::nullopt, "-128"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200143 datastore.setLeaf("/example-schema:leafInt8", int8_t{-128});
144 datastore.commitChanges();
145 }
146
147 SECTION("set leafInt16 to -32768")
148 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100149 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt16", std::nullopt, "-32768"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200150 datastore.setLeaf("/example-schema:leafInt16", int16_t{-32768});
151 datastore.commitChanges();
152 }
153
154 SECTION("set leafInt32 to -2147483648")
155 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100156 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt32", std::nullopt, "-2147483648"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200157 datastore.setLeaf("/example-schema:leafInt32", int32_t{-2147483648});
158 datastore.commitChanges();
159 }
160
161 SECTION("set leafInt64 to -50000000000")
162 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100163 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt64", std::nullopt, "-50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200164 datastore.setLeaf("/example-schema:leafInt64", int64_t{-50000000000});
165 datastore.commitChanges();
166 }
167
168 SECTION("set leafUInt8 to 255")
169 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100170 REQUIRE_CALL(mockRunning, write("/example-schema:leafUInt8", std::nullopt, "255"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200171 datastore.setLeaf("/example-schema:leafUInt8", uint8_t{255});
172 datastore.commitChanges();
173 }
174
175 SECTION("set leafUInt16 to 65535")
176 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100177 REQUIRE_CALL(mockRunning, write("/example-schema:leafUInt16", std::nullopt, "65535"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200178 datastore.setLeaf("/example-schema:leafUInt16", uint16_t{65535});
179 datastore.commitChanges();
180 }
181
182 SECTION("set leafUInt32 to 4294967295")
183 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100184 REQUIRE_CALL(mockRunning, write("/example-schema:leafUInt32", std::nullopt, "4294967295"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200185 datastore.setLeaf("/example-schema:leafUInt32", uint32_t{4294967295});
186 datastore.commitChanges();
187 }
188
189 SECTION("set leafUInt64 to 50000000000")
190 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100191 REQUIRE_CALL(mockRunning, write("/example-schema:leafUInt64", std::nullopt, "50000000000"s));
Václav Kubernát134d78f2019-09-03 16:42:29 +0200192 datastore.setLeaf("/example-schema:leafUInt64", uint64_t{50000000000});
Václav Kubernát73109382018-09-14 19:52:03 +0200193 datastore.commitChanges();
194 }
195
196 SECTION("set leafEnum to coze")
197 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100198 REQUIRE_CALL(mockRunning, write("/example-schema:leafEnum", std::nullopt, "coze"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200199 datastore.setLeaf("/example-schema:leafEnum", enum_{"coze"});
200 datastore.commitChanges();
201 }
202
203 SECTION("set leafDecimal to 123.544")
204 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100205 REQUIRE_CALL(mockRunning, write("/example-schema:leafDecimal", std::nullopt, "123.544"s));
Václav Kubernát73109382018-09-14 19:52:03 +0200206 datastore.setLeaf("/example-schema:leafDecimal", 123.544);
207 datastore.commitChanges();
208 }
209
Jan Kundrátd2872862020-06-18 21:02:00 +0200210 SECTION("set a string, then delete it")
211 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100212 REQUIRE_CALL(mockRunning, write("/example-schema:leafString", std::nullopt, "blah"s));
Jan Kundrátd2872862020-06-18 21:02:00 +0200213 datastore.setLeaf("/example-schema:leafString", "blah"s);
214 datastore.commitChanges();
215 DatastoreAccess::Tree expected{{"/example-schema:leafString", "blah"s}};
216 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
217
Václav Kubernáteaf56682021-02-22 17:15:41 +0100218 REQUIRE_CALL(mockRunning, write("/example-schema:leafString", "blah"s, std::nullopt));
Jan Kundrátd2872862020-06-18 21:02:00 +0200219 datastore.deleteItem("/example-schema:leafString");
220 datastore.commitChanges();
221 expected.clear();
222 REQUIRE(datastore.getItems("/example-schema:leafString") == expected);
223 }
224
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200225 SECTION("set a non-existing leaf")
226 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100227 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200228 datastore.setLeaf("/example-schema:non-existing", "what"s);
229 });
230 }
231
Václav Kubernát73109382018-09-14 19:52:03 +0200232 SECTION("create presence container")
233 {
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200234 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") == std::string::npos);
Václav Kubernáteaf56682021-02-22 17:15:41 +0100235 REQUIRE_CALL(mockRunning, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200236 datastore.createItem("/example-schema:pContainer");
Václav Kubernát73109382018-09-14 19:52:03 +0200237 datastore.commitChanges();
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200238 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:pContainer") != std::string::npos);
Václav Kubernát73109382018-09-14 19:52:03 +0200239 }
240
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100241 SECTION("create/delete a list instance")
Václav Kubernát45f4a822019-05-29 21:10:50 +0200242 {
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100243 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100244 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Nguyen']", std::nullopt, ""s));
245 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Nguyen']/name", std::nullopt, "Nguyen"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200246 datastore.createItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100247 datastore.commitChanges();
248 }
249 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100250 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Nguyen']", ""s, std::nullopt));
251 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Nguyen']/name", "Nguyen"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200252 datastore.deleteItem("/example-schema:person[name='Nguyen']");
Václav Kubernátcc7a93f2020-02-04 11:48:15 +0100253 datastore.commitChanges();
254 }
Václav Kubernát45f4a822019-05-29 21:10:50 +0200255 }
256
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200257 SECTION("deleting non-existing list keys")
258 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100259 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200260 datastore.deleteItem("/example-schema:person[name='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200261 datastore.commitChanges();
262 });
263 }
264
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200265 SECTION("accessing non-existing schema nodes as a list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200266 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100267 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200268 datastore.createItem("/example-schema:non-existing-list[xxx='blah']");
269 datastore.commitChanges();
270 });
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100271 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200272 datastore.deleteItem("/example-schema:non-existing-list[xxx='non existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200273 datastore.commitChanges();
274 });
275 }
276
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200277 SECTION("leafref pointing to a key of a list")
278 {
279 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100280 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Dan']", std::nullopt, ""s));
281 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Dan']/name", std::nullopt, "Dan"s));
282 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Elfi']", std::nullopt, ""s));
283 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Elfi']/name", std::nullopt, "Elfi"s));
284 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Kolafa']", std::nullopt, ""s));
285 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Kolafa']/name", std::nullopt, "Kolafa"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200286 datastore.createItem("/example-schema:person[name='Dan']");
287 datastore.createItem("/example-schema:person[name='Elfi']");
288 datastore.createItem("/example-schema:person[name='Kolafa']");
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200289 datastore.commitChanges();
290 }
291
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200292 std::string value;
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200293 SECTION("Dan")
294 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200295 value = "Dan";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200296 }
297
298 SECTION("Elfi")
299 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200300 value = "Elfi";
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200301 }
302
303 SECTION("Kolafa")
304 {
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200305 value = "Kolafa";
306 }
307
308 datastore.setLeaf("/example-schema:bossPerson", value);
309 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100310 REQUIRE_CALL(mockRunning, write("/example-schema:bossPerson", std::nullopt, value));
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200311 datastore.commitChanges();
312 }
Václav Kubernát7b191ce2020-06-30 16:22:53 +0200313 REQUIRE(datastore.getItems("/example-schema:bossPerson") == DatastoreAccess::Tree{{"/example-schema:bossPerson", value}});
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200314 }
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200315 SECTION("bool values get correctly represented as bools")
316 {
317 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100318 REQUIRE_CALL(mockRunning, write("/example-schema:down", std::nullopt, "true"s));
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200319 datastore.setLeaf("/example-schema:down", bool{true});
320 datastore.commitChanges();
321 }
322
Jan Kundrátb331b552020-01-23 15:25:29 +0100323 DatastoreAccess::Tree expected{{"/example-schema:down", bool{true}}};
Václav Kubernát8e121ff2019-10-15 15:47:45 +0200324 REQUIRE(datastore.getItems("/example-schema:down") == expected);
325 }
Václav Kubernát3efb5ca2019-10-09 20:07:40 +0200326
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200327 SECTION("getting items from the whole module")
328 {
329 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100330 REQUIRE_CALL(mockRunning, write("/example-schema:up", std::nullopt, "true"s));
331 REQUIRE_CALL(mockRunning, write("/example-schema:down", std::nullopt, "false"s));
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200332 datastore.setLeaf("/example-schema:up", bool{true});
333 datastore.setLeaf("/example-schema:down", bool{false});
334 datastore.commitChanges();
335 }
336
Václav Kubernátcf9224f2020-06-02 09:55:29 +0200337 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200338 {"/example-schema:up", bool{true}},
339 {"/example-schema:down", bool{false}}
340 };
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200341 REQUIRE(datastore.getItems("/example-schema:*") == expected);
342 }
343
Václav Kubernát152ce222019-12-19 12:23:32 +0100344 SECTION("getItems returns correct datatypes")
345 {
346 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100347 REQUIRE_CALL(mockRunning, write("/example-schema:leafEnum", std::nullopt, "lol"s));
Václav Kubernát152ce222019-12-19 12:23:32 +0100348 datastore.setLeaf("/example-schema:leafEnum", enum_{"lol"});
349 datastore.commitChanges();
350 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100351 DatastoreAccess::Tree expected{{"/example-schema:leafEnum", enum_{"lol"}}};
Václav Kubernát152ce222019-12-19 12:23:32 +0100352
353 REQUIRE(datastore.getItems("/example-schema:leafEnum") == expected);
354 }
355
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100356 SECTION("getItems on a list")
357 {
358 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100359 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Jan']", std::nullopt, ""s));
360 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Jan']/name", std::nullopt, "Jan"s));
361 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Michal']", std::nullopt, ""s));
362 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Michal']/name", std::nullopt, "Michal"s));
363 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Petr']", std::nullopt, ""s));
364 REQUIRE_CALL(mockRunning, write("/example-schema:person[name='Petr']/name", std::nullopt, "Petr"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200365 datastore.createItem("/example-schema:person[name='Jan']");
366 datastore.createItem("/example-schema:person[name='Michal']");
367 datastore.createItem("/example-schema:person[name='Petr']");
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100368 datastore.commitChanges();
369 }
Jan Kundrátb331b552020-01-23 15:25:29 +0100370 DatastoreAccess::Tree expected{
Václav Kubernát144729d2020-01-08 15:20:35 +0100371 {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100372 {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100373 {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100374 {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
Václav Kubernát144729d2020-01-08 15:20:35 +0100375 {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
Václav Kubernátd812cfb2020-01-07 17:30:20 +0100376 {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
377 };
378
379 REQUIRE(datastore.getItems("/example-schema:person") == expected);
380 }
381
Václav Kubernát69aabe92020-01-24 16:53:12 +0100382 SECTION("presence containers")
383 {
384 DatastoreAccess::Tree expected;
385 // Make sure it's not there before we create it
386 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
387
388 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100389 REQUIRE_CALL(mockRunning, write("/example-schema:pContainer", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200390 datastore.createItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100391 datastore.commitChanges();
392 }
393 expected = {
394 {"/example-schema:pContainer", special_{SpecialValue::PresenceContainer}}
395 };
396 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
397
398 // Make sure it's not there after we delete it
399 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100400 REQUIRE_CALL(mockRunning, write("/example-schema:pContainer", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200401 datastore.deleteItem("/example-schema:pContainer");
Václav Kubernát69aabe92020-01-24 16:53:12 +0100402 datastore.commitChanges();
403 }
404 expected = {};
405 REQUIRE(datastore.getItems("/example-schema:pContainer") == expected);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100406 }
Václav Kubernát69aabe92020-01-24 16:53:12 +0100407
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200408 SECTION("creating a non-existing schema node as a container")
409 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100410 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200411 datastore.createItem("/example-schema:non-existing-presence-container");
412 datastore.commitChanges();
413 });
414 }
415
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200416 SECTION("deleting a non-existing schema node as a container or leaf")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200417 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100418 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200419 datastore.deleteItem("/example-schema:non-existing-presence-container");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200420 datastore.commitChanges();
421 });
422 }
423
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100424 SECTION("nested presence container")
425 {
426 DatastoreAccess::Tree expected;
427 // Make sure it's not there before we create it
428 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
429 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100430 REQUIRE_CALL(mockRunning, write("/example-schema:inventory/stuff", std::nullopt, ""s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200431 datastore.createItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100432 datastore.commitChanges();
433 }
434 expected = {
435 {"/example-schema:inventory/stuff", special_{SpecialValue::PresenceContainer}}
436 };
437 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
438 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100439 REQUIRE_CALL(mockRunning, write("/example-schema:inventory/stuff", ""s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200440 datastore.deleteItem("/example-schema:inventory/stuff");
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100441 datastore.commitChanges();
442 }
443 expected = {};
444 REQUIRE(datastore.getItems("/example-schema:inventory/stuff") == expected);
Václav Kubernát69aabe92020-01-24 16:53:12 +0100445 }
446
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100447 SECTION("floats")
448 {
449 datastore.setLeaf("/example-schema:leafDecimal", 123.4);
Václav Kubernáteaf56682021-02-22 17:15:41 +0100450 REQUIRE_CALL(mockRunning, write("/example-schema:leafDecimal", std::nullopt, "123.4"s));
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100451 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100452 DatastoreAccess::Tree expected{
Jan Kundrátbd3169c2020-02-03 19:31:34 +0100453 {"/example-schema:leafDecimal", 123.4},
454 };
455 REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
456 }
457
Jan Kundrát3ff50122020-05-07 00:37:50 +0200458 SECTION("unions")
459 {
460 datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100461 REQUIRE_CALL(mockRunning, write("/example-schema:unionIntString", std::nullopt, "10"s));
Jan Kundrát3ff50122020-05-07 00:37:50 +0200462 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100463 DatastoreAccess::Tree expected{
Jan Kundrát3ff50122020-05-07 00:37:50 +0200464 {"/example-schema:unionIntString", int32_t{10}},
465 };
466 REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
467 }
468
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100469 SECTION("identityref")
470 {
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200471 datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100472 REQUIRE_CALL(mockRunning, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200473 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100474 DatastoreAccess::Tree expected{
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200475 {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
476 };
477 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
478
479 datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100480 REQUIRE_CALL(mockRunning, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
Jan Kundrát0d8abd12020-05-07 02:00:14 +0200481 datastore.commitChanges();
482 expected = {
483 {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
484 };
485 REQUIRE(datastore.getItems("/example-schema:beast") == expected);
486 }
487
Jan Kundrát68985442020-05-07 02:15:34 +0200488 SECTION("binary")
489 {
490 datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100491 REQUIRE_CALL(mockRunning, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
Jan Kundrát68985442020-05-07 02:15:34 +0200492 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100493 DatastoreAccess::Tree expected{
Jan Kundrát68985442020-05-07 02:15:34 +0200494 {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
495 };
496 REQUIRE(datastore.getItems("/example-schema:blob") == expected);
497 }
498
Jan Kundrát379bb572020-05-07 03:23:13 +0200499 SECTION("empty")
500 {
501 datastore.setLeaf("/example-schema:dummy", empty_{});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100502 REQUIRE_CALL(mockRunning, write("/example-schema:dummy", std::nullopt, ""s));
Jan Kundrát379bb572020-05-07 03:23:13 +0200503 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100504 DatastoreAccess::Tree expected{
Jan Kundrát379bb572020-05-07 03:23:13 +0200505 {"/example-schema:dummy", empty_{}},
506 };
507 REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
508 }
509
Václav Kubernát19097f32020-10-05 10:08:29 +0200510 SECTION("bits")
511 {
512 datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100513 REQUIRE_CALL(mockRunning, write("/example-schema:flags", std::nullopt, "carry sign"s));
Václav Kubernát19097f32020-10-05 10:08:29 +0200514 datastore.commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100515 DatastoreAccess::Tree expected{
Václav Kubernát19097f32020-10-05 10:08:29 +0200516 {"/example-schema:flags", bits_{{"carry", "sign"}}},
517 };
518 REQUIRE(datastore.getItems("/example-schema:flags") == expected);
519 }
520
Václav Kubernát74487df2020-06-04 01:29:28 +0200521#if not defined(yang_BACKEND)
Jan Kundrátbb525b42020-02-04 11:56:59 +0100522 SECTION("operational data")
523 {
524 MockDataSupplier mockOpsData;
Václav Kubernát654303f2020-07-31 13:16:54 +0200525 OperationalDataSubscription opsDataSub("example-schema", "/example-schema:temperature", mockOpsData);
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100526 OperationalDataSubscription opsDataSub2("example-schema", "/example-schema:users", mockOpsData);
Jan Kundrátbb525b42020-02-04 11:56:59 +0100527 DatastoreAccess::Tree expected;
528 std::string xpath;
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100529
Jan Kundrátbb525b42020-02-04 11:56:59 +0100530 SECTION("temperature")
531 {
532 expected = {{"/example-schema:temperature", int32_t{22}}};
533 xpath = "/example-schema:temperature";
534 }
535
Václav Kubernátf90a0b52020-11-06 05:53:03 +0100536 SECTION("key-less lists")
537 {
538 expected = {
539 {"/example-schema:users/userList[1]", special_{SpecialValue::List}},
540 {"/example-schema:users/userList[1]/name", std::string{"John"}},
541 {"/example-schema:users/userList[1]/otherfield", std::string{"LOL"}},
542 {"/example-schema:users/userList[2]", special_{SpecialValue::List}},
543 {"/example-schema:users/userList[2]/name", std::string{"Foo"}},
544 {"/example-schema:users/userList[2]/otherfield", std::string{"Bar"}},
545 };
546 xpath = "/example-schema:users";
547 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100548 REQUIRE_CALL(mockOpsData, get_data(xpath)).RETURN(expected);
549 REQUIRE(datastore.getItems(xpath) == expected);
550 }
Václav Kubernát74487df2020-06-04 01:29:28 +0200551#endif
Jan Kundrátbb525b42020-02-04 11:56:59 +0100552
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200553 SECTION("leaf list")
554 {
555 DatastoreAccess::Tree expected;
Václav Kubernáteaf56682021-02-22 17:15:41 +0100556 REQUIRE_CALL(mockRunning, write("/example-schema:addresses", std::nullopt, "0.0.0.0"s));
557 REQUIRE_CALL(mockRunning, write("/example-schema:addresses", std::nullopt, "127.0.0.1"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200558 datastore.createItem("/example-schema:addresses[.='0.0.0.0']");
559 datastore.createItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200560 datastore.commitChanges();
561 expected = {
562 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
563 {"/example-schema:addresses[.='0.0.0.0']", "0.0.0.0"s},
564 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
565 };
566 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
567
Václav Kubernáteaf56682021-02-22 17:15:41 +0100568 REQUIRE_CALL(mockRunning, write("/example-schema:addresses", "0.0.0.0"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200569 datastore.deleteItem("/example-schema:addresses[.='0.0.0.0']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200570 datastore.commitChanges();
571 expected = {
572 {"/example-schema:addresses", special_{SpecialValue::LeafList}},
573 {"/example-schema:addresses[.='127.0.0.1']", "127.0.0.1"s},
574 };
575 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
576
Václav Kubernáteaf56682021-02-22 17:15:41 +0100577 REQUIRE_CALL(mockRunning, write("/example-schema:addresses", "127.0.0.1"s, std::nullopt));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200578 datastore.deleteItem("/example-schema:addresses[.='127.0.0.1']");
Václav Kubernát5b8a8f32020-05-20 00:57:22 +0200579 datastore.commitChanges();
580 expected = {};
581 REQUIRE(datastore.getItems("/example-schema:addresses") == expected);
582 }
Jan Kundrátbb525b42020-02-04 11:56:59 +0100583
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200584 SECTION("deleting a non-existing leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200585 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100586 catching<OnKeyNotFound>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200587 datastore.deleteItem("/example-schema:addresses[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200588 datastore.commitChanges();
589 });
590 }
591
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200592 SECTION("accessing a non-existing schema node as a leaf-list")
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200593 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100594 catching<OnInvalidSchemaPathCreate>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200595 datastore.createItem("/example-schema:non-existing[.='non-existing']");
596 datastore.commitChanges();
597 });
598
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100599 catching<OnInvalidSchemaPathDelete>([&] {
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200600 datastore.deleteItem("/example-schema:non-existing[.='non-existing']");
Jan Kundrát3867c9e2020-06-18 20:26:45 +0200601 datastore.commitChanges();
602 });
603 }
604
Václav Kubernát7160a132020-04-03 02:11:01 +0200605 SECTION("copying data from startup refreshes the data")
606 {
607 {
608 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100609 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt16", std::nullopt, "123"s));
Václav Kubernát7160a132020-04-03 02:11:01 +0200610 datastore.setLeaf("/example-schema:leafInt16", int16_t{123});
611 datastore.commitChanges();
612 }
613 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{{"/example-schema:leafInt16", int16_t{123}}});
Václav Kubernáteaf56682021-02-22 17:15:41 +0100614 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt16", "123"s, std::nullopt));
Václav Kubernát7160a132020-04-03 02:11:01 +0200615 datastore.copyConfig(Datastore::Startup, Datastore::Running);
616 REQUIRE(datastore.getItems("/example-schema:leafInt16") == DatastoreAccess::Tree{});
617 }
618
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200619 SECTION("moving leaflist instances")
620 {
621 DatastoreAccess::Tree expected;
622 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100623 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", std::nullopt, "http"s));
Václav Kubernát654303f2020-07-31 13:16:54 +0200624 // FIXME: Why no notifications for these??
625 // ... possibly because my subscription doesn't extract it properly?
626 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "ftp"s));
627 // REQUIRE_CALL(mock, write("/example-schema:protocols", std::nullopt, "pop3"s));
Václav Kubernáteaf56682021-02-22 17:15:41 +0100628 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", "http"s, "ftp"s));
629 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", "ftp"s, "pop3"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200630 datastore.createItem("/example-schema:protocols[.='http']");
631 datastore.createItem("/example-schema:protocols[.='ftp']");
632 datastore.createItem("/example-schema:protocols[.='pop3']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200633 datastore.commitChanges();
634 expected = {
635 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
636 {"/example-schema:protocols[.='http']", "http"s},
637 {"/example-schema:protocols[.='ftp']", "ftp"s},
638 {"/example-schema:protocols[.='pop3']", "pop3"s},
639 };
640 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
641 }
642
643 std::string sourcePath;
644 SECTION("begin")
645 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100646 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", std::nullopt, "pop3"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200647 sourcePath = "/example-schema:protocols[.='pop3']";
648 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
649 datastore.commitChanges();
650 expected = {
651 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
652 {"/example-schema:protocols[.='pop3']", "pop3"s},
653 {"/example-schema:protocols[.='http']", "http"s},
654 {"/example-schema:protocols[.='ftp']", "ftp"s},
655 };
656 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
657 }
658
659 SECTION("end")
660 {
661 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100662 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", "pop3"s, "http"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200663 datastore.moveItem(sourcePath, yang::move::Absolute::End);
664 datastore.commitChanges();
665 expected = {
666 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
667 {"/example-schema:protocols[.='ftp']", "ftp"s},
668 {"/example-schema:protocols[.='pop3']", "pop3"s},
669 {"/example-schema:protocols[.='http']", "http"s},
670 };
671 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
672 }
673
674 SECTION("after")
675 {
676 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100677 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", "ftp"s, "http"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200678 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{".", "ftp"s}}});
679 datastore.commitChanges();
680 expected = {
681 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
682 {"/example-schema:protocols[.='ftp']", "ftp"s},
683 {"/example-schema:protocols[.='http']", "http"s},
684 {"/example-schema:protocols[.='pop3']", "pop3"s},
685 };
686 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
687 }
688
689 SECTION("before")
690 {
691 sourcePath = "/example-schema:protocols[.='http']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100692 REQUIRE_CALL(mockRunning, write("/example-schema:protocols", "ftp"s, "http"s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200693 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{".", "pop3"s}}});
694 datastore.commitChanges();
695 expected = {
696 {"/example-schema:protocols", special_{SpecialValue::LeafList}},
697 {"/example-schema:protocols[.='ftp']", "ftp"s},
698 {"/example-schema:protocols[.='http']", "http"s},
699 {"/example-schema:protocols[.='pop3']", "pop3"s},
700 };
701 REQUIRE(datastore.getItems("/example-schema:protocols") == expected);
702 }
703 }
704
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200705 SECTION("moving non-existing schema nodes")
706 {
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100707 catching<OnInvalidSchemaPathMove>([&] {
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200708 datastore.moveItem("/example-schema:non-existing", yang::move::Absolute::Begin);
709 datastore.commitChanges();
710 });
711 }
712
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200713 SECTION("moving list instances")
714 {
715 DatastoreAccess::Tree expected;
716 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100717 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='John']", std::nullopt, ""s));
718 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='John']/name", std::nullopt, "John"s));
719 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='Eve']", ""s, ""s));
720 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='Eve']/name", std::nullopt, "Eve"s));
721 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='Adam']", ""s, ""s));
722 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='Adam']/name", std::nullopt, "Adam"s));
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200723 datastore.createItem("/example-schema:players[name='John']");
724 datastore.createItem("/example-schema:players[name='Eve']");
725 datastore.createItem("/example-schema:players[name='Adam']");
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200726 datastore.commitChanges();
727 expected = {
728 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
729 {"/example-schema:players[name='John']/name", "John"s},
730 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
731 {"/example-schema:players[name='Eve']/name", "Eve"s},
732 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
733 {"/example-schema:players[name='Adam']/name", "Adam"s},
734 };
735 REQUIRE(datastore.getItems("/example-schema:players") == expected);
736 }
737
738 std::string sourcePath;
739 SECTION("begin")
740 {
741 sourcePath = "/example-schema:players[name='Adam']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100742 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='Adam']", std::nullopt, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200743 datastore.moveItem(sourcePath, yang::move::Absolute::Begin);
744 datastore.commitChanges();
745 expected = {
746 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
747 {"/example-schema:players[name='Adam']/name", "Adam"s},
748 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
749 {"/example-schema:players[name='John']/name", "John"s},
750 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
751 {"/example-schema:players[name='Eve']/name", "Eve"s},
752 };
753 REQUIRE(datastore.getItems("/example-schema:players") == expected);
754 }
755
756 SECTION("end")
757 {
758 sourcePath = "/example-schema:players[name='John']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100759 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='John']", ""s, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200760 datastore.moveItem(sourcePath, yang::move::Absolute::End);
761 datastore.commitChanges();
762 expected = {
763 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
764 {"/example-schema:players[name='Eve']/name", "Eve"s},
765 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
766 {"/example-schema:players[name='Adam']/name", "Adam"s},
767 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
768 {"/example-schema:players[name='John']/name", "John"s},
769 };
770 REQUIRE(datastore.getItems("/example-schema:players") == expected);
771 }
772
773 SECTION("after")
774 {
775 sourcePath = "/example-schema:players[name='John']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100776 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='John']", ""s, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200777 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::After, {{"name", "Eve"s}}});
778 datastore.commitChanges();
779 expected = {
780 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
781 {"/example-schema:players[name='Eve']/name", "Eve"s},
782 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
783 {"/example-schema:players[name='John']/name", "John"s},
784 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
785 {"/example-schema:players[name='Adam']/name", "Adam"s},
786 };
787 REQUIRE(datastore.getItems("/example-schema:players") == expected);
788 }
789
790 SECTION("before")
791 {
792 sourcePath = "/example-schema:players[name='John']";
Václav Kubernáteaf56682021-02-22 17:15:41 +0100793 REQUIRE_CALL(mockRunning, write("/example-schema:players[name='John']", ""s, ""s));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200794 datastore.moveItem(sourcePath, yang::move::Relative{yang::move::Relative::Position::Before, {{"name", "Adam"s}}});
795 datastore.commitChanges();
796 expected = {
797 {"/example-schema:players[name='Eve']", special_{SpecialValue::List}},
798 {"/example-schema:players[name='Eve']/name", "Eve"s},
799 {"/example-schema:players[name='John']", special_{SpecialValue::List}},
800 {"/example-schema:players[name='John']/name", "John"s},
801 {"/example-schema:players[name='Adam']", special_{SpecialValue::List}},
802 {"/example-schema:players[name='Adam']/name", "Adam"s},
803 };
804 REQUIRE(datastore.getItems("/example-schema:players") == expected);
805 }
806 }
807
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200808 SECTION("getting /")
809 {
810 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100811 REQUIRE_CALL(mockRunning, write("/example-schema:leafInt32", std::nullopt, "64"s));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200812 datastore.setLeaf("/example-schema:leafInt32", 64);
813 datastore.commitChanges();
814 }
815
816 DatastoreAccess::Tree expected{
Václav Kubernát654303f2020-07-31 13:16:54 +0200817 {"/example-schema:leafInt32", 64}
818 };
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200819 // This tests if we at least get the data WE added.
Václav Kubernát654303f2020-07-31 13:16:54 +0200820 REQUIRE(std::all_of(expected.begin(), expected.end(), [items = datastore.getItems("/")] (const auto& item) {
821 return std::find(items.begin(), items.end(), item) != items.end();
822 }));
Václav Kubernátfa96ac22020-06-18 17:03:52 +0200823 }
824
Václav Kubernát36986c52020-06-25 10:30:05 +0200825 SECTION("setting and removing without commit")
826 {
827 datastore.setLeaf("/example-schema:leafInt32", 64);
828 datastore.deleteItem("/example-schema:leafInt32");
829 }
830
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200831 SECTION("two key lists")
832 {
Václav Kubernáteaf56682021-02-22 17:15:41 +0100833 REQUIRE_CALL(mockRunning, write("/example-schema:point[x='12'][y='10']", std::nullopt, ""s));
834 REQUIRE_CALL(mockRunning, write("/example-schema:point[x='12'][y='10']/x", std::nullopt, "12"s));
835 REQUIRE_CALL(mockRunning, write("/example-schema:point[x='12'][y='10']/y", std::nullopt, "10"s));
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200836 datastore.createItem("/example-schema:point[x='12'][y='10']");
837 datastore.commitChanges();
838 REQUIRE(datastore.dump(DataFormat::Json).find("example-schema:point") != std::string::npos);
839 }
840
Václav Kubernát73109382018-09-14 19:52:03 +0200841 waitForCompletionAndBitMore(seq1);
842}
Jan Kundrát6ee84792020-01-24 01:43:36 +0100843
Václav Kubernát654303f2020-07-31 13:16:54 +0200844struct ActionCb {
845 int operator()(sysrepo::S_Session session,
846 const char* xpath,
847 [[maybe_unused]] const sysrepo::S_Vals input,
848 [[maybe_unused]] sr_event_t event,
849 [[maybe_unused]] uint32_t request_id,
850 sysrepo::S_Vals_Holder output)
Václav Kubernáta8789602020-07-20 15:18:19 +0200851 {
Václav Kubernát654303f2020-07-31 13:16:54 +0200852 if (session->get_context()->get_node(nullptr, xpath)->path(LYS_PATH_FIRST_PREFIX) == "/example-schema:ports/shutdown") {
Václav Kubernáta8789602020-07-20 15:18:19 +0200853 auto buf = output->allocate(1);
854 buf->val(0)->set(joinPaths(xpath, "success").c_str(), true);
855 return SR_ERR_OK;
856 }
857 throw std::runtime_error("unrecognized RPC");
858 }
Václav Kubernát654303f2020-07-31 13:16:54 +0200859};
Václav Kubernáta8789602020-07-20 15:18:19 +0200860
Václav Kubernát654303f2020-07-31 13:16:54 +0200861struct RpcCb {
862 int operator()([[maybe_unused]] sysrepo::S_Session session,
863 const char* xpath,
864 const sysrepo::S_Vals input,
865 [[maybe_unused]] sr_event_t event,
866 [[maybe_unused]] uint32_t request_id,
867 sysrepo::S_Vals_Holder output)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100868 {
869 const auto nukes = "/example-schema:launch-nukes"s;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200870 if (xpath == "/example-schema:noop"s || xpath == "/example-schema:fire"s) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100871 return SR_ERR_OK;
Václav Kubernáte7248b22020-06-26 15:38:59 +0200872 }
873
874 if (xpath == nukes) {
Jan Kundrát6ee84792020-01-24 01:43:36 +0100875 uint64_t kilotons = 0;
876 bool hasCities = false;
877 for (size_t i = 0; i < input->val_cnt(); ++i) {
878 const auto& val = input->val(i);
Václav Kubernátf4b6a932020-07-09 10:34:19 +0200879 if (val->xpath() == nukes + "/payload") {
880 continue; // ignore, container
881 }
882 if (val->xpath() == nukes + "/description") {
883 continue; // unused
884 }
885
Jan Kundrát6ee84792020-01-24 01:43:36 +0100886 if (val->xpath() == nukes + "/payload/kilotons") {
887 kilotons = val->data()->get_uint64();
Jan Kundrát6ee84792020-01-24 01:43:36 +0100888 } else if (std::string_view{val->xpath()}.find(nukes + "/cities") == 0) {
889 hasCities = true;
890 } else {
891 throw std::runtime_error("RPC launch-nukes: unexpected input "s + val->xpath());
892 }
893 }
894 if (kilotons == 333'666) {
895 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
896 return SR_ERR_OK;
897 }
898 auto buf = output->allocate(2);
899 size_t i = 0;
900 buf->val(i++)->set((nukes + "/blast-radius").c_str(), uint32_t{33'666});
901 buf->val(i++)->set((nukes + "/actual-yield").c_str(), static_cast<uint64_t>(1.33 * kilotons));
902 if (hasCities) {
903 buf = output->reallocate(output->val_cnt() + 2);
904 buf->val(i++)->set((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London");
905 buf->val(i++)->set((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin");
906 }
907 return SR_ERR_OK;
908 }
909 throw std::runtime_error("unrecognized RPC");
910 }
911};
912
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100913TEST_CASE("rpc/action")
914{
Jan Kundrát6ee84792020-01-24 01:43:36 +0100915 trompeloeil::sequence seq1;
Jan Kundrát6ee84792020-01-24 01:43:36 +0100916
917#ifdef sysrepo_BACKEND
Václav Kubernát654303f2020-07-31 13:16:54 +0200918 auto datastore = std::make_shared<SysrepoAccess>(Datastore::Running);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100919#elif defined(netconf_BACKEND)
Václav Kubernátd1beedc2020-09-07 12:09:05 +0200920 const auto NETOPEER_SOCKET = getenv("NETOPEER_SOCKET");
921 auto datastore = std::make_shared<NetconfAccess>(NETOPEER_SOCKET);
Václav Kubernát74487df2020-06-04 01:29:28 +0200922#elif defined(yang_BACKEND)
Václav Kubernáte7248b22020-06-26 15:38:59 +0200923 auto datastore = std::make_shared<YangAccess>();
924 datastore->addSchemaDir(schemaDir);
925 datastore->addSchemaFile(exampleSchemaFile);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100926#else
927#error "Unknown backend"
928#endif
929
Václav Kubernát654303f2020-07-31 13:16:54 +0200930 auto srConn = std::make_shared<sysrepo::Connection>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200931 auto srSession = std::make_shared<sysrepo::Session>(srConn);
932 auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSession);
Václav Kubernát654303f2020-07-31 13:16:54 +0200933 auto rpcCb = std::make_shared<RpcCb>();
934 auto actionCb = std::make_shared<ActionCb>();
Václav Kubernáta8789602020-07-20 15:18:19 +0200935 sysrepo::Logs{}.set_stderr(SR_LL_INF);
Václav Kubernát654303f2020-07-31 13:16:54 +0200936 SysrepoSubscription subscription("example-schema", nullptr);
Václav Kubernáta8789602020-07-20 15:18:19 +0200937 // careful here, sysrepo insists on module_change CBs being registered before RPC CBs, otherwise there's a memleak
Václav Kubernát654303f2020-07-31 13:16:54 +0200938 srSubscription->rpc_subscribe("/example-schema:noop", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
939 srSubscription->rpc_subscribe("/example-schema:launch-nukes", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
940 srSubscription->rpc_subscribe("/example-schema:fire", RpcCb{}, 0, SR_SUBSCR_CTX_REUSE);
941 srSubscription->rpc_subscribe("/example-schema:ports/shutdown", ActionCb{}, 0, SR_SUBSCR_CTX_REUSE);
Václav Kubernáte7248b22020-06-26 15:38:59 +0200942
Václav Kubernáta8789602020-07-20 15:18:19 +0200943 SECTION("rpc")
Jan Kundrát7ec214d2020-06-19 17:05:07 +0200944 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200945 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
946 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
947 };
948 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100949
Václav Kubernáta8789602020-07-20 15:18:19 +0200950 // ProxyDatastore cannot easily read DatastoreAccess::Tree, so we need to set the input via create/setLeaf/etc.
951 SECTION("valid")
952 {
953 std::string rpc;
954 DatastoreAccess::Tree input, output;
955
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100956 SECTION("noop")
957 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200958 rpc = "/example-schema:noop";
Václav Kubernátb3960f82020-12-01 03:21:48 +0100959 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200960 }
961
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100962 SECTION("small nuke")
963 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200964 rpc = "/example-schema:launch-nukes";
965 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +0100966 {joinPaths(rpc, "description"), "dummy"s},
967 {joinPaths(rpc, "payload/kilotons"), uint64_t{333'666}},
Václav Kubernáta8789602020-07-20 15:18:19 +0200968 };
Václav Kubernátb3960f82020-12-01 03:21:48 +0100969 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200970 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{333'666});
971 // no data are returned
972 }
973
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100974 SECTION("small nuke")
975 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200976 rpc = "/example-schema:launch-nukes";
977 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +0100978 {joinPaths(rpc, "description"), "dummy"s},
979 {joinPaths(rpc, "payload/kilotons"), uint64_t{4}},
Václav Kubernáta8789602020-07-20 15:18:19 +0200980 };
Václav Kubernátb3960f82020-12-01 03:21:48 +0100981 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200982 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{4});
983
984 output = {
985 {"blast-radius", uint32_t{33'666}},
986 {"actual-yield", uint64_t{5}},
987 };
988 }
989
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100990 SECTION("with lists")
991 {
Václav Kubernáta8789602020-07-20 15:18:19 +0200992 rpc = "/example-schema:launch-nukes";
993 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +0100994 {joinPaths(rpc, "payload/kilotons"), uint64_t{6}},
995 {joinPaths(rpc, "cities/targets[city='Prague']/city"), "Prague"s},
Václav Kubernáta8789602020-07-20 15:18:19 +0200996 };
Václav Kubernátb3960f82020-12-01 03:21:48 +0100997 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +0200998 proxyDatastore.setLeaf("/example-schema:launch-nukes/example-schema:payload/example-schema:kilotons", uint64_t{6});
999 proxyDatastore.createItem("/example-schema:launch-nukes/example-schema:cities/example-schema:targets[city='Prague']");
1000 output = {
1001 {"blast-radius", uint32_t{33'666}},
1002 {"actual-yield", uint64_t{7}},
1003 {"damaged-places", special_{SpecialValue::PresenceContainer}},
1004 {"damaged-places/targets[city='London']", special_{SpecialValue::List}},
1005 {"damaged-places/targets[city='London']/city", "London"s},
1006 {"damaged-places/targets[city='Berlin']", special_{SpecialValue::List}},
1007 {"damaged-places/targets[city='Berlin']/city", "Berlin"s},
1008 };
1009 }
1010
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001011 SECTION("with leafref")
1012 {
Václav Kubernáta8789602020-07-20 15:18:19 +02001013 datastore->createItem("/example-schema:person[name='Colton']");
1014 datastore->commitChanges();
1015
1016 rpc = "/example-schema:fire";
1017 input = {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001018 {joinPaths(rpc, "whom"), "Colton"s}
Václav Kubernáta8789602020-07-20 15:18:19 +02001019 };
Václav Kubernátb3960f82020-12-01 03:21:48 +01001020 proxyDatastore.initiate(rpc);
Václav Kubernáta8789602020-07-20 15:18:19 +02001021 proxyDatastore.setLeaf("/example-schema:fire/example-schema:whom", "Colton"s);
1022 }
1023
Václav Kubernátb3960f82020-12-01 03:21:48 +01001024 catching<OnExec>([&] { REQUIRE(datastore->execute(rpc, input) == output); });
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001025 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001026 }
1027
Václav Kubernáta8789602020-07-20 15:18:19 +02001028 SECTION("non-existing RPC")
1029 {
Václav Kubernátb3960f82020-12-01 03:21:48 +01001030 catching<OnInvalidRpcPath>([&] { datastore->execute("/example-schema:non-existing", DatastoreAccess::Tree{}); });
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001031 }
Václav Kubernát2edfe542021-02-03 08:08:29 +01001032
1033 SECTION("invalid RPC exec resets temporary datastore")
1034 {
1035 proxyDatastore.initiate("/example-schema:setIp");
1036 catching<OnInvalidRpcInput>([&] { auto output = proxyDatastore.execute(); });
1037 REQUIRE(proxyDatastore.inputDatastorePath() == std::nullopt);
1038 }
Jan Kundrát6ee84792020-01-24 01:43:36 +01001039 }
1040
Václav Kubernáta8789602020-07-20 15:18:19 +02001041 SECTION("action")
Jan Kundrát7ec214d2020-06-19 17:05:07 +02001042 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001043 auto createTemporaryDatastore = [](const std::shared_ptr<DatastoreAccess>& datastore) {
1044 return std::make_shared<YangAccess>(std::static_pointer_cast<YangSchema>(datastore->schema()));
1045 };
1046 ProxyDatastore proxyDatastore(datastore, createTemporaryDatastore);
Václav Kubernáta8789602020-07-20 15:18:19 +02001047 std::string path;
1048 DatastoreAccess::Tree input, output;
1049
1050 output = {
Václav Kubernáta8789602020-07-20 15:18:19 +02001051 {"success", true}
1052 };
1053 datastore->createItem("/example-schema:ports[name='A']");
1054 datastore->commitChanges();
Václav Kubernátb4e5b182020-11-16 19:55:09 +01001055 SECTION("shutdown")
1056 {
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001057 path = "/example-schema:ports[name='A']/example-schema:shutdown";
1058 input = {
1059 {"/example-schema:ports[name='A']/shutdown/force", true}
1060 };
1061 proxyDatastore.initiate(path);
1062 proxyDatastore.setLeaf("/example-schema:ports[name='A']/example-schema:shutdown/example-schema:force", true);
1063
Václav Kubernáta8789602020-07-20 15:18:19 +02001064 }
1065
Václav Kubernátb3960f82020-12-01 03:21:48 +01001066 catching<OnExec>([&] { REQUIRE(datastore->execute(path, input) == output); });
Václav Kubernát94bb7cf2021-02-03 09:59:39 +01001067 catching<OnExec>([&] { REQUIRE(proxyDatastore.execute() == output); });
Jan Kundrát6ee84792020-01-24 01:43:36 +01001068 }
1069
Jan Kundrát6ee84792020-01-24 01:43:36 +01001070 waitForCompletionAndBitMore(seq1);
1071}