tests: easier mocking of notification handling
I think it's much, much easier to use features of the mocking framework
we have *and* the modern C++ bindings. While this removes some
features (time tracking, could be easily added when needed), it also
cleans up bits which were never really used before, such as notification
replay and explicit start/stop operations.
The good thing is that we can now delegate partial matching to the
trompeloeil and its WITH macro, for example.
Change-Id: Id7e4b73575de4a1b1fbe38d4a0316ae75bd677dc
diff --git a/tests/mock/sysrepo/events.cpp b/tests/mock/sysrepo/events.cpp
index bc654f5..562cf7f 100644
--- a/tests/mock/sysrepo/events.cpp
+++ b/tests/mock/sysrepo/events.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016-2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2016-2022 CESNET, https://photonics.cesnet.cz/
*
* Written by Jan Kundrát <jan.kundrat@cesnet.cz>
*
@@ -8,79 +8,31 @@
#include "events.h"
namespace {
-std::string sr_ev_notif_type_to_string(const sysrepo::NotificationType notif_type)
+std::string module_from_xpath(const std::string& xpath)
{
- std::ostringstream oss;
- oss << notif_type;
- return oss.str();
-}
-}
-
-EventWatcher::EventWatcher(std::function<void(Event)> callback)
- : notifRecvCb(std::move(callback))
-{
-}
-
-EventWatcher::~EventWatcher()
-{
-}
-
-void EventWatcher::operator()(
- sysrepo::Session,
- uint32_t ,
- const sysrepo::NotificationType type,
- const std::optional<libyang::DataNode> notificationTree,
- const sysrepo::NotificationTimeStamp timestamp)
-{
- Event e;
- e.xPath = std::string{notificationTree ? std::string{notificationTree->path()} : "<no-xpath>"};
- e.received = std::chrono::steady_clock::now();
- auto log = spdlog::get("main");
- log->info("SR event {} {} {}", sr_ev_notif_type_to_string(type), timestamp.time_since_epoch().count(), e.xPath);
-
- if (notificationTree) {
- for (const auto& node : notificationTree->childrenDfs()) {
- auto path = std::string{node.path()};
- auto val = [&] {
- if (node.schema().nodeType() == libyang::NodeType::Leaf) {
- return std::string{node.asTerm().valueStr()};
- }
-
- return std::string{""};
- }();
- e.data[path] = val;
- log->debug(" {}: {}", path, val);
- }
+ auto pos = xpath.find(":");
+ if (pos == 0 || pos == std::string::npos || xpath[0] != '/') {
+ throw std::logic_error{"NotificationWatcher: Malformed XPath " + xpath};
}
-
- {
- std::lock_guard<std::mutex> lock(*mutex);
- events->push_back(e);
- }
-
- switch (type) {
- case sysrepo::NotificationType::Realtime:
- case sysrepo::NotificationType::Replay:
- notifRecvCb(e);
- break;
- case sysrepo::NotificationType::ReplayComplete:
- case sysrepo::NotificationType::Terminated:
- case sysrepo::NotificationType::Modified:
- case sysrepo::NotificationType::Suspended:
- case sysrepo::NotificationType::Resumed:
- break;
- }
-
+ return xpath.substr(1, pos - 1);
+}
}
-std::vector<EventWatcher::Event>::size_type EventWatcher::count() const
+NotificationWatcher::NotificationWatcher(sysrepo::Session& session, const std::string& xpath)
+ : m_sub{session.onNotification(module_from_xpath(xpath),
+ [this, xpath](sysrepo::Session, uint32_t, const sysrepo::NotificationType type, const std::optional<libyang::DataNode> tree, const sysrepo::NotificationTimeStamp) {
+ if (type != sysrepo::NotificationType::Realtime) {
+ return;
+ }
+ data_t data;
+ for (const auto& it : tree->findPath(xpath)->childrenDfs()) {
+ if (!it.isTerm()) {
+ continue;
+ }
+ data[it.path().substr(xpath.size() + 1 /* trailing slash */)] = std::visit(libyang::ValuePrinter{}, it.asTerm().value());
+ }
+ notified(data);
+ },
+ xpath)}
{
- std::lock_guard<std::mutex> lock(*mutex);
- return events->size();
-}
-
-EventWatcher::Event EventWatcher::peek(const std::vector<EventWatcher::Event>::size_type index) const
-{
- std::lock_guard<std::mutex> lock(*mutex);
- return (*events)[index];
}
diff --git a/tests/mock/sysrepo/events.h b/tests/mock/sysrepo/events.h
index 67779eb..bf77d57 100644
--- a/tests/mock/sysrepo/events.h
+++ b/tests/mock/sysrepo/events.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016-2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2016-2022 CESNET, https://photonics.cesnet.cz/
*
* Written by Jan Kundrát <jan.kundrat@cesnet.cz>
*
@@ -7,29 +7,19 @@
#pragma once
-#include <chrono>
-#include <mutex>
-#include <sysrepo-cpp/Session.hpp>
+#include <sysrepo-cpp/Subscription.hpp>
+#include "trompeloeil_doctest.h"
#include "test_log_setup.h"
-/** @short YANG notifications */
-class EventWatcher {
-public:
- struct Event {
- std::string xPath;
- std::map<std::string, std::string> data;
- std::chrono::time_point<std::chrono::steady_clock> received;
- };
+/** @short Watch for a given YANG notification
- explicit EventWatcher(std::function<void(Event)> callback);
- ~EventWatcher();
- void operator()(sysrepo::Session session, uint32_t subscriptionId, const sysrepo::NotificationType type, const std::optional<libyang::DataNode> notificationTree, const sysrepo::NotificationTimeStamp timestamp);
-
- Event peek(std::vector<Event>::size_type index) const;
- std::vector<Event>::size_type count() const;
-
+When a real-time notification is recieved, the `notified()` method is invoked with stringified values
+of all terminals that were passed to the original notification.
+*/
+struct NotificationWatcher {
+ using data_t = std::map<std::string, std::string>;
+ NotificationWatcher(sysrepo::Session& session, const std::string& xpath);
+ MAKE_MOCK1(notified, void(const data_t&));
private:
- std::function<void(Event)> notifRecvCb;
- mutable std::shared_ptr<std::mutex> mutex = std::make_shared<std::mutex>();
- std::shared_ptr<std::vector<Event>> events = std::make_shared<std::vector<Event>>();
+ sysrepo::Subscription m_sub;
};