system: Installation progress notifications

Implement installation progress notifications as defined in
czechlight-system model
(/czechlight-system:firmware/installation/update).

The RAUC D-Bus API invokes PropertyChanged signal on its Progress [1]
property whenever the installation progresses. We capture those signals
and use them to generate Sysrepo notifications to clients.

The update contains current progress (in percents) and a message
containing current installation stage.

[1] https://rauc.readthedocs.io/en/v1.4/reference.html#gdbus-property-de-pengutronix-rauc-installer-progress

Change-Id: I310752919dde523a93943a2a1d7af79f073aedfe
diff --git a/tests/sysrepo_system-czechlightsystem.cpp b/tests/sysrepo_system-czechlightsystem.cpp
index 07dc575..fe0f2a2 100644
--- a/tests/sysrepo_system-czechlightsystem.cpp
+++ b/tests/sysrepo_system-czechlightsystem.cpp
@@ -5,6 +5,7 @@
 #include "test_log_setup.h"
 #include "test_sysrepo_helpers.h"
 #include "tests/configure.cmake.h"
+#include "tests/mock/sysrepo/events.h"
 
 using namespace std::literals;
 
@@ -109,6 +110,12 @@
                 {"/installation/message", ""},
                 {"/installation/status", "in-progress"},
             };
+            size_t expectedNotificationsCount;
+            std::string expectedLastNotificationMsg;
+
+            // subscribe to notifications
+            EventWatcher events;
+            subscription->event_notif_subscribe("czechlight-system", events, "/czechlight-system:firmware/installation/update");
 
             SECTION("Successfull install")
             {
@@ -118,6 +125,8 @@
                     {"/installation/message", ""},
                     {"/installation/status", "succeeded"},
                 };
+                expectedNotificationsCount = 22;
+                expectedLastNotificationMsg = "Installing done.";
             }
 
             SECTION("Unsuccessfull install")
@@ -128,6 +137,8 @@
                     {"/installation/message", "Failed to download bundle https://10.88.3.11:8000/update.raucb: Transfer failed: error:1408F10B:SSL routines:ssl3_get_record:wrong version number"},
                     {"/installation/status", "failed"},
                 };
+                expectedNotificationsCount = 6;
+                expectedLastNotificationMsg = "Installing failed.";
             }
 
             raucServer.installBundleBehaviour(installType);
@@ -139,6 +150,20 @@
 
             std::this_thread::sleep_for(2s); // lets wait a while, so the installation can finish
             REQUIRE(dataFromSysrepo(client, "/czechlight-system:firmware", SR_DS_OPERATIONAL) == expectedFinished);
+
+            // check updates notification count and that at least some of them are reasonable
+            REQUIRE(events.count() == expectedNotificationsCount);
+            REQUIRE(events.peek(0).data["/czechlight-system:firmware/installation/update/message"] == "Installing");
+            REQUIRE(events.peek(0).data["/czechlight-system:firmware/installation/update/progress"] == "0");
+            REQUIRE(events.peek(events.count() - 1).data["/czechlight-system:firmware/installation/update/message"] == expectedLastNotificationMsg);
+            REQUIRE(events.peek(events.count() - 1).data["/czechlight-system:firmware/installation/update/progress"] == "100");
+
+            // check updates notification progress is an increasing sequence
+            for (size_t i = 1; i < events.count(); i++) {
+                auto prevProgress = std::stoi(events.peek(i - 1).data["/czechlight-system:firmware/installation/update/progress"]);
+                auto currProgress = std::stoi(events.peek(i).data["/czechlight-system:firmware/installation/update/progress"]);
+                REQUIRE(prevProgress <= currProgress);
+            }
         }
 
         SECTION("Invoke another installation before the first finishes")
diff --git a/tests/test_sysrepo_helpers.h b/tests/test_sysrepo_helpers.h
index bf8bf5f..54d2d71 100644
--- a/tests/test_sysrepo_helpers.h
+++ b/tests/test_sysrepo_helpers.h
@@ -44,6 +44,7 @@
     auto srSess = std::make_shared<sysrepo::Session>(srConn); \
     auto srSubs = std::make_shared<sysrepo::Subscribe>(srSess);
 
-#define TEST_SYSREPO_INIT_CLIENT                               \
-    auto clientConn = std::make_shared<sysrepo::Connection>(); \
-    auto client = std::make_shared<sysrepo::Session>(clientConn);
+#define TEST_SYSREPO_INIT_CLIENT                                  \
+    auto clientConn = std::make_shared<sysrepo::Connection>();    \
+    auto client = std::make_shared<sysrepo::Session>(clientConn); \
+    auto subscription = std::make_shared<sysrepo::Subscribe>(client);