From 7953627c147902e7cf3d38e475b81e075b44100c Mon Sep 17 00:00:00 2001
From: Andy Ford <AndyTWF@users.noreply.github.com>
Date: Tue, 11 Jan 2022 20:22:03 +0000
Subject: [PATCH] fix(push-events): data not synchronising on second instance
 startup (#400)

* fix(push-events): data not synchronising on second instance startup

Until now, some push events (stands etc) were not syncing up when a second
instance of EuroScope was started. This fixes the synchronisation.

Fix #399

* Tidy CMake
---
 src/plugin/CMakeLists.txt                   |  4 +-
 src/plugin/push/ProxyPushDataSync.cpp       | 21 +++++++++++
 src/plugin/push/ProxyPushDataSync.h         | 25 +++++++++++++
 src/plugin/push/PushEventBootstrap.cpp      |  3 ++
 test/plugin/CMakeLists.txt                  |  2 +-
 test/plugin/push/ProxyPushDataSyncTest.cpp  | 41 +++++++++++++++++++++
 test/plugin/push/PushEventBootstrapTest.cpp |  8 +++-
 7 files changed, 100 insertions(+), 4 deletions(-)
 create mode 100644 src/plugin/push/ProxyPushDataSync.cpp
 create mode 100644 src/plugin/push/ProxyPushDataSync.h
 create mode 100644 test/plugin/push/ProxyPushDataSyncTest.cpp

diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt
index a6554a9ed..cbe567cc9 100644
--- a/src/plugin/CMakeLists.txt
+++ b/src/plugin/CMakeLists.txt
@@ -667,7 +667,9 @@ set(src__push
     "push/PushEventProxyWindow.h"
     "push/PushEventSubscription.cpp"
     "push/PushEventSubscription.h"
-        push/PushEvent.cpp push/PushEventConnectionInterface.cpp)
+    push/PushEvent.cpp
+    push/PushEventConnectionInterface.cpp
+    push/ProxyPushDataSync.cpp push/ProxyPushDataSync.h)
 source_group("src\\push" FILES ${src__push})
 
 set(src__radarscreen
diff --git a/src/plugin/push/ProxyPushDataSync.cpp b/src/plugin/push/ProxyPushDataSync.cpp
new file mode 100644
index 000000000..be7ab2584
--- /dev/null
+++ b/src/plugin/push/ProxyPushDataSync.cpp
@@ -0,0 +1,21 @@
+#include "ProxyPushDataSync.h"
+#include "PushEventProcessorCollection.h"
+
+namespace UKControllerPlugin::Push {
+
+    ProxyPushDataSync::ProxyPushDataSync(const PushEventProcessorCollection& processors)
+        : processors(processors), synced(false)
+    {
+    }
+
+    void ProxyPushDataSync::TimedEventTrigger()
+    {
+        if (synced) {
+            return;
+        }
+
+        LogInfo("Syncing proxy push event data");
+        processors.PluginEventsSynced();
+        synced = true;
+    }
+} // namespace UKControllerPlugin::Push
diff --git a/src/plugin/push/ProxyPushDataSync.h b/src/plugin/push/ProxyPushDataSync.h
new file mode 100644
index 000000000..d02a0f6d8
--- /dev/null
+++ b/src/plugin/push/ProxyPushDataSync.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "timedevent/AbstractTimedEvent.h"
+
+namespace UKControllerPlugin::Push {
+    class PushEventProcessorCollection;
+
+    /*
+     * When proxy connections are created, we need to make sure their data is synced (more or less)
+     * with the API. This handler triggers on a timer once ES is loaded (so we know all the handlers are registered)
+     * and triggers their synced event.
+     */
+    class ProxyPushDataSync : public TimedEvent::AbstractTimedEvent
+    {
+        public:
+        ProxyPushDataSync(const PushEventProcessorCollection& processors);
+        void TimedEventTrigger() override;
+
+        private:
+        // All the push event processors
+        const PushEventProcessorCollection& processors;
+
+        // Has the sync been done
+        bool synced;
+    };
+} // namespace UKControllerPlugin::Push
diff --git a/src/plugin/push/PushEventBootstrap.cpp b/src/plugin/push/PushEventBootstrap.cpp
index c89a95616..dd82bbe15 100644
--- a/src/plugin/push/PushEventBootstrap.cpp
+++ b/src/plugin/push/PushEventBootstrap.cpp
@@ -1,4 +1,5 @@
 #include "PollingPushEventConnection.h"
+#include "ProxyPushDataSync.h"
 #include "PushEventBootstrap.h"
 #include "PushEventProtocolHandler.h"
 #include "PushEventProxyConnection.h"
@@ -22,6 +23,8 @@ namespace UKControllerPlugin::Push {
         // Create a websocket connection depending on whether we're the main plugin
         if (duplicatePlugin) {
             pushEvents = std::make_shared<PushEventProxyConnection>();
+            container.timedHandler->RegisterEvent(
+                std::make_shared<ProxyPushDataSync>(*container.pushEventProcessors), 5);
         } else {
             const auto pollingEvents = std::make_shared<PollingPushEventConnection>(
                 *container.api, *container.taskRunner, *container.pushEventProcessors);
diff --git a/test/plugin/CMakeLists.txt b/test/plugin/CMakeLists.txt
index 7a959219c..1611f7626 100644
--- a/test/plugin/CMakeLists.txt
+++ b/test/plugin/CMakeLists.txt
@@ -401,7 +401,7 @@ set(test__push
     "push/PushEventProtocolHandlerTest.cpp"
     "push/PushEventProxyConnetionTest.cpp"
     "push/PushEventProxyHandlerTest.cpp"
-)
+    push/ProxyPushDataSyncTest.cpp)
 source_group("test\\push" FILES ${test__push})
 
 set(test__radarscreen
diff --git a/test/plugin/push/ProxyPushDataSyncTest.cpp b/test/plugin/push/ProxyPushDataSyncTest.cpp
new file mode 100644
index 000000000..07160318d
--- /dev/null
+++ b/test/plugin/push/ProxyPushDataSyncTest.cpp
@@ -0,0 +1,41 @@
+#include "push/ProxyPushDataSync.h"
+#include "push/PushEventProcessorCollection.h"
+#include "push/PushEventSubscription.h"
+
+using testing::NiceMock;
+using testing::Test;
+using UKControllerPlugin::Push::ProxyPushDataSync;
+using UKControllerPlugin::Push::PushEventProcessorCollection;
+using UKControllerPlugin::Push::PushEventSubscription;
+
+namespace UKControllerPluginTest::Push {
+
+    class ProxyPushDataSyncTest : public Test
+    {
+        public:
+        ProxyPushDataSyncTest() : eventProcessor(std::make_shared<NiceMock<MockPushEventProcessor>>()), sync(collection)
+        {
+            this->collection.AddProcessor(this->eventProcessor);
+        }
+
+        PushEventProcessorCollection collection;
+        std::shared_ptr<NiceMock<MockPushEventProcessor>> eventProcessor;
+        ProxyPushDataSync sync;
+    };
+
+    TEST_F(ProxyPushDataSyncTest, ItSyncsProxyDataOnFirstTimedEvent)
+    {
+        EXPECT_CALL(*this->eventProcessor, PluginEventsSynced).Times(1);
+
+        sync.TimedEventTrigger();
+    }
+
+    TEST_F(ProxyPushDataSyncTest, ItDoesntSyncOnSubsequentTimedEvents)
+    {
+        EXPECT_CALL(*this->eventProcessor, PluginEventsSynced).Times(1);
+
+        sync.TimedEventTrigger();
+        sync.TimedEventTrigger();
+        sync.TimedEventTrigger();
+    }
+} // namespace UKControllerPluginTest::Push
diff --git a/test/plugin/push/PushEventBootstrapTest.cpp b/test/plugin/push/PushEventBootstrapTest.cpp
index b71a7878c..6395627d4 100644
--- a/test/plugin/push/PushEventBootstrapTest.cpp
+++ b/test/plugin/push/PushEventBootstrapTest.cpp
@@ -2,7 +2,6 @@
 #include "push/PushEventBootstrap.h"
 #include "push/PushEventProcessorCollection.h"
 #include "timedevent/TimedEventCollection.h"
-#include "memory"
 
 using testing::Test;
 using UKControllerPlugin::Bootstrap::PersistenceContainer;
@@ -44,10 +43,15 @@ namespace UKControllerPluginTest::Push {
     TEST_F(PushEventBootstrapTest, ItSetsUpProtocolHandlerForTimedEventsOnDuplicatePlugin)
     {
         BootstrapPlugin(this->container, true);
-        EXPECT_EQ(1, this->container.timedHandler->CountHandlers());
         EXPECT_EQ(1, this->container.timedHandler->CountHandlersForFrequency(1));
     }
 
+    TEST_F(PushEventBootstrapTest, ItSetsUpProxySyncOnDuplicatePlugin)
+    {
+        BootstrapPlugin(this->container, true);
+        EXPECT_EQ(1, this->container.timedHandler->CountHandlersForFrequency(5));
+    }
+
     TEST_F(PushEventBootstrapTest, ItSetsUpProxyHandlerForPushEventsOnNonDuplicatePlugin)
     {
         BootstrapPlugin(this->container, false);