Add inter-service communication framework 65/37065/5
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 18 Mar 2015 16:10:30 +0000 (17:10 +0100)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 19 Mar 2015 16:26:02 +0000 (17:26 +0100)
[Issue#] N/A
[Feature/Bug] N/A
[Problem] Services need to communicate with each other
[Cause] N/A
[Solution] Framework for inter-service communication added.

[Verification] Run ckm-tests-internal -t MESSAGE_MANAGER_TEST

Change-Id: I28714ba52efe25c47402adb6ac1bef52859ed898

src/manager/main/communication-manager.h [new file with mode: 0644]
tests/CMakeLists.txt
tests/test_comm-manager.cpp [new file with mode: 0644]

diff --git a/src/manager/main/communication-manager.h b/src/manager/main/communication-manager.h
new file mode 100644 (file)
index 0000000..898d483
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file       communication-manager.h
+ * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
+ * @version    1.0
+ */
+
+#pragma once
+
+#include <functional>
+#include <list>
+#include <noncopyable.h>
+
+namespace CKM {
+
+/*
+ * class responsible for keeping a list of listeners for given M type of message and notifying them
+ */
+template <typename M>
+class MessageManager
+{
+public:
+    NONCOPYABLE(MessageManager);
+
+    // Listener is an object callable with const M& as argument
+    template <typename L>
+    void Register(L&& listener)
+    {
+        m_listeners.push_back(std::move(listener));
+    }
+
+    // Sends message of type M to all registered listeners
+    void SendMessage(const M& msg)
+    {
+        for(auto it : m_listeners)
+            it(msg);
+    }
+protected:
+    MessageManager() {}
+    // No one is going to destroy this class directly (only via inherited class). Hence no 'virtual'
+    ~MessageManager() {}
+
+private:
+    std::list<std::function<void(const M&)>> m_listeners;
+};
+
+// generic template declaration
+template <typename... Args>
+struct CommunicationManager;
+
+/*
+ * Class that combines MessageManagers of all requested Message types into a single object. Examples
+ * can be found in tests (test_msg-manager.cpp)
+ */
+template <typename First, typename... Args>
+struct CommunicationManager<First, Args...> :
+    public MessageManager<First>, public CommunicationManager<Args...>
+{
+public:
+    CommunicationManager() {}
+    NONCOPYABLE(CommunicationManager);
+
+    // M - message type, L - listener to register
+    template <typename M, typename L>
+    void Register(L&& listener)
+    {
+        MessageManager<M>::Register(std::move(listener));
+    }
+
+    // M message type
+    template <typename M>
+    void SendMessage(const M& msg)
+    {
+        MessageManager<M>::SendMessage(msg);
+    }
+};
+
+// stop condition for recursive inheritance
+template <>
+struct CommunicationManager<> {
+};
+
+} /* namespace CKM */
index e3d39af..985688c 100644 (file)
@@ -15,6 +15,7 @@ INCLUDE_DIRECTORIES(
     ${KEY_MANAGER_PATH}/dpl/log/include
     ${KEY_MANAGER_PATH}/sqlcipher
     ${KEY_MANAGER_PATH}/service
+    ${KEY_MANAGER_PATH}/main
     ${KEY_MANAGER_PATH}/common/
     ${KEY_MANAGER_PATH}/client-async/
     ${KEY_MANAGER_SRC_PATH}/include
@@ -31,6 +32,7 @@ SET(TEST_MERGED_SOURCES
     ${KEY_MANAGER_TEST_MERGED_SRC}/test-key-provider.cpp
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_safe-buffer.cpp
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_descriptor-set.cpp
+    ${KEY_MANAGER_TEST_MERGED_SRC}/test_comm-manager.cpp
     ${KEY_MANAGER_PATH}/service/db-crypto.cpp
     ${KEY_MANAGER_PATH}/service/key-provider.cpp
     ${KEY_MANAGER_PATH}/client-async/descriptor-set.cpp
diff --git a/tests/test_comm-manager.cpp b/tests/test_comm-manager.cpp
new file mode 100644 (file)
index 0000000..ec9ccaf
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ *  Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file       test_comm-manager.cpp
+ * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
+ * @version    1.0
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/results_reporter.hpp>
+#include <communication-manager.h>
+#include <string>
+#include <random>
+#include <chrono>
+
+namespace {
+struct MessageA {
+    MessageA(int ai) : i(ai) {}
+    int i;
+};
+
+struct MessageB {
+    MessageB(char ac) : c(ac) {}
+    char c;
+};
+
+struct MessageC {
+    MessageC(const std::string& astr) : str(astr) {}
+    std::string str;
+};
+
+struct Listener {
+    Listener() : i(0) {}
+
+    void Handle(const MessageA& msg) {
+        i = msg.i;
+    }
+
+    void Handle(const MessageC& msg) {
+        str = msg.str;
+    }
+
+    int i;
+    std::string str;
+};
+
+} // namespace anonymous
+
+BOOST_AUTO_TEST_SUITE(MESSAGE_MANAGER_TEST)
+
+BOOST_AUTO_TEST_CASE(TMM_0010_NoListener) {
+    CKM::CommunicationManager<MessageA> mgr;
+    //int reci = 0;
+    mgr.SendMessage(MessageA(22));
+}
+
+BOOST_AUTO_TEST_CASE(TMM_0020_Basic) {
+    CKM::CommunicationManager<MessageA> mgr;
+    int received = 0;
+    mgr.Register<MessageA>([&](const MessageA& msg){ received = msg.i; });
+    mgr.SendMessage(MessageA(4));
+    BOOST_REQUIRE_MESSAGE(received != 0, "Message not received");
+    BOOST_REQUIRE_MESSAGE(received == 4, "Wrong message received i=" << received);
+}
+
+BOOST_AUTO_TEST_CASE(TMM_0030_MultipleMessages) {
+    CKM::CommunicationManager<MessageA, MessageB> mgr;
+    int reci = 0;
+    char recc = 0;
+    mgr.Register<MessageA>([&](const MessageA& msg){ reci = msg.i; });
+    mgr.Register<MessageB>([&](const MessageB& msg){ recc = msg.c; });
+    mgr.SendMessage(MessageB('c'));
+    BOOST_REQUIRE_MESSAGE(reci == 0, "Unexpected message received");
+    BOOST_REQUIRE_MESSAGE(recc != 0, "Message not received");
+    BOOST_REQUIRE_MESSAGE(recc == 'c', "Wrong message received c=" << recc);
+
+    mgr.SendMessage(MessageA(42));
+    BOOST_REQUIRE_MESSAGE(reci!= 0, "Message not received");
+    BOOST_REQUIRE_MESSAGE(reci == 42, "Wrong message received i=" << reci);
+    BOOST_REQUIRE_MESSAGE(recc == 'c', "Previous message overwritten c=" << recc);
+}
+
+BOOST_AUTO_TEST_CASE(TMM_0040_Listener) {
+    CKM::CommunicationManager<MessageA, MessageB, MessageC> mgr;
+    Listener l;
+    mgr.Register<MessageC>([&](const MessageC& msg){ l.Handle(msg); });
+    mgr.Register<MessageA>([&](const MessageA& msg){ l.Handle(msg); });
+
+    mgr.SendMessage(MessageC("lorem ipsum"));
+    BOOST_REQUIRE_MESSAGE(l.i == 0, "Unexpected message received");
+    BOOST_REQUIRE_MESSAGE(!l.str.empty(), "Message not received");
+    BOOST_REQUIRE_MESSAGE(l.str == "lorem ipsum", "Wrong message received c=" << l.str);
+
+    mgr.SendMessage(MessageA(3));
+    BOOST_REQUIRE_MESSAGE(l.i!= 0, "Message not received");
+    BOOST_REQUIRE_MESSAGE(l.i == 3, "Wrong message received i=" << l.i);
+    BOOST_REQUIRE_MESSAGE(l.str == "lorem ipsum", "Previous message overwritten str=" << l.str);
+}
+
+BOOST_AUTO_TEST_CASE(TMM_0050_2Listeners) {
+    CKM::CommunicationManager<MessageA> mgr;
+    bool called[2];
+    called[0] = false;
+    called[1] = false;
+    mgr.Register<MessageA>([&](const MessageA& msg){
+        BOOST_REQUIRE_MESSAGE(msg.i == 5, "Unexpected message received i=" << msg.i);
+        called[0] = true;
+    });
+    mgr.Register<MessageA>([&](const MessageA& msg){
+        BOOST_REQUIRE_MESSAGE(msg.i == 5, "Unexpected message received i=" << msg.i);
+        called[1] = true;
+    });
+
+    mgr.SendMessage(MessageA(5));
+    BOOST_REQUIRE_MESSAGE(called[0], "First listener not called");
+    BOOST_REQUIRE_MESSAGE(called[1], "Second listener not called");
+}
+
+BOOST_AUTO_TEST_CASE(TMM_0060_Stress) {
+    CKM::CommunicationManager<MessageA, MessageB, MessageC> mgr;
+
+    std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
+    std::uniform_int_distribution<size_t> message_dist(0,2);
+    std::uniform_int_distribution<size_t> count_dist(1,10);
+
+    size_t a = 0;
+    size_t b = 0;
+    size_t c = 0;
+    mgr.Register<MessageA>([&](const MessageA& msg) {
+        BOOST_REQUIRE_MESSAGE(msg.i == 42, "Wrong message: " << msg.i);
+        a++;
+    });
+    mgr.Register<MessageB>([&](const MessageB& msg) {
+        BOOST_REQUIRE_MESSAGE(msg.c == 'c', "Wrong message: " << msg.c);
+        b++;
+    });
+    mgr.Register<MessageC>([&](const MessageC& msg) {
+        BOOST_REQUIRE_MESSAGE(msg.str == "lorem ipsum", "Wrong message: " << msg.str);
+        c++;
+    });
+
+    for (size_t i=0; i < 1000; i++)
+    {
+        size_t cnt = count_dist(generator);
+        for (size_t s = 0; s < cnt; s++) {
+            switch(message_dist(generator))
+            {
+            case 0:
+                mgr.SendMessage(MessageA(42));
+                a--;
+                break;
+            case 1:
+                mgr.SendMessage(MessageB('c'));
+                b--;
+                break;
+            case 2:
+                mgr.SendMessage(MessageC("lorem ipsum"));
+                c--;
+                break;
+            default:
+                BOOST_FAIL("Unexpected message type");
+            }
+        }
+    }
+    BOOST_REQUIRE_MESSAGE(a == 0, "Unexpected number of MessageA: " << a);
+    BOOST_REQUIRE_MESSAGE(b == 0, "Unexpected number of MessageB: " << b);
+    BOOST_REQUIRE_MESSAGE(c == 0, "Unexpected number of MessageC: " << c);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+