VconfImpl: Allow register and unregister callbacks during callback call 81/137781/3
authorPaweł Stawicki <p.stawicki@partner.samsung.com>
Mon, 10 Jul 2017 10:20:30 +0000 (12:20 +0200)
committerPaweł Stawicki <p.stawicki@partner.samsung.com>
Mon, 10 Jul 2017 10:23:48 +0000 (12:23 +0200)
Change-Id: I999ef9e371901045ce0ee77f39a5a4067fe92451

src/Singleton.hpp
src/VConf.cpp
src/VConf.hpp
tests/VConfImplTests.cpp

index 975a969..052ce8d 100644 (file)
@@ -4,20 +4,19 @@
 #include "UniversalSwitchLog.hpp"
 
 #include <memory>
-#include <vector>
 
-class Singleton
+class SingletonBase
 {
 protected:
-       Singleton() = default;
-       Singleton(const Singleton &) = delete;
-       Singleton(Singleton &&) = delete;
-       Singleton &operator = (const Singleton &) = delete;
-       Singleton &operator = (Singleton &&) = delete;
+       SingletonBase() = default;
+       SingletonBase(const SingletonBase &) = delete;
+       SingletonBase(SingletonBase &&) = delete;
+       SingletonBase &operator = (const SingletonBase &) = delete;
+       SingletonBase &operator = (SingletonBase &&) = delete;
 };
 
 template <class Interface>
-class MockableSingleton : public Singleton
+class MockableSingleton : protected SingletonBase
 {
 public:
        using InterfaceType = Interface;
@@ -37,23 +36,4 @@ private:
        static std::unique_ptr<InterfaceType> implementation;
 };
 
-template<class MockableSingletonType>
-class CallbackHandlerKeeper
-{
-public:
-       ~CallbackHandlerKeeper()
-       {
-               for (auto e : handlers)
-                       MockableSingletonType::instance().unregisterCb(e);
-       }
-
-       void add(typename MockableSingletonType::InterfaceType::CallbackHandle handler)
-       {
-               handlers.push_back(std::move(handler));
-       }
-
-private:
-       std::vector<typename MockableSingletonType::InterfaceType::CallbackHandle> handlers;
-};
-
 #endif
index 683ed7b..094bfee 100644 (file)
@@ -1,16 +1,20 @@
-
 #include "VConf.hpp"
+
 #include <vconf.h>
 #include <unordered_map>
+#include <tuple>
+#include <map>
+#include <limits>
 
 namespace
 {
        template <class ValueType>
-       using CallbackMap = std::unordered_map<int, std::function<void(ValueType)>>;
+       using CallbackMap = std::map<int, std::function<void(ValueType)>>;
        template <class ValueType>
        using KeyMap = std::unordered_map<std::string, CallbackMap<ValueType>>;
+       static std::string inCallbackKeyGuard;
 
-       static int idxCount = 0;
+       static int idxCurrent = std::numeric_limits<int>::max();
        static KeyMap<int> intCallbackMap;
        static KeyMap<bool> boolCallbackMap;
        static KeyMap<double> doubleCallbackMap;
@@ -19,7 +23,7 @@ namespace
        template<class KeyMapType, class VconfCbType, class CallbackType>
        inline int vconfRegisterKeyChangedCb(KeyMapType &keyMap, VconfCbType &vconfCb, const std::string &key, CallbackType &&callback)
        {
-               auto idx = ++idxCount;
+               auto idx = --idxCurrent;
                auto iterator = keyMap.find(key);
                if (iterator == keyMap.end()) {
                        iterator = keyMap.emplace(key, typename KeyMapType::mapped_type()).first;
@@ -42,7 +46,7 @@ namespace
                }
                if (iterator->second.erase(idx) != 1)
                        ERROR("Vconf: could not remove callback for key: %s with idx: %d", key.c_str(), idx);
-               if (iterator->second.empty()) {
+               if (iterator->second.empty() && inCallbackKeyGuard != key) {
                        keyMap.erase(iterator);
                        if (vconf_ignore_key_changed(key.c_str(), vconfCb) != VCONF_OK) {
                                ERROR("Vconf: cant unregister callback for key: %s", key.c_str());
@@ -50,42 +54,93 @@ namespace
                }
        }
 
-       template<class KeyMapType, class vconfGetType>
-       inline void vconfCb(KeyMapType &keyMap, const vconfGetType &vconfGetFunction, const char *expectedType, int vconfType, keynode_t *keynode, void *data)
+
+
+       template<class KeyMapType, class VconfCbType, class vconfGetType>
+       inline void vconfCbRun(KeyMapType &keyMap, VconfCbType &vconfCb, const vconfGetType &vconfGetFunction, const char *expectedType, int vconfType, keynode_t *keynode, void *data)
        {
+               std::string key = vconf_keynode_get_name(keynode);
                if (vconf_keynode_get_type(keynode) != vconfType) {
-                       ERROR("vconf error: got '%s' changed with type: %d, expected type: %s", vconf_keynode_get_name(keynode), vconf_keynode_get_type(keynode), expectedType);
+                       ERROR("vconf error: got '%s' changed with type: %d, expected type: %s", key.c_str(), vconf_keynode_get_type(keynode), expectedType);
                        return;
                }
+               auto value = vconfGetFunction(keynode);
                auto callbackMapPtr = (typename KeyMapType::mapped_type *)data;
-               for (auto &idx : *callbackMapPtr) {
-                       auto &callback = idx.second;
-                       callback(vconfGetFunction(keynode));
+               ASSERT(!callbackMapPtr->empty(), "vconf error: no callbacks registered");
+
+               //we allow register and unregister callbacks during a callbeck call
+               {
+                       //make sure we don't delete callbackMap during callbacks execution
+                       inCallbackKeyGuard = key;
+                       int idx = 0;
+                       typename KeyMapType::mapped_type::iterator callbackIterator;
+                       while ((callbackIterator = callbackMapPtr->upper_bound(idx)) != callbackMapPtr->end()) {
+                               idx = callbackIterator->first;
+                               auto callback = callbackIterator->second;
+                               callback(value);
+                       }
+                       inCallbackKeyGuard.clear();
+                       if (callbackMapPtr->empty()) {
+                               //all callbacks removed
+                               keyMap.erase(key);
+                               if (vconf_ignore_key_changed(key.c_str(), vconfCb) != VCONF_OK) {
+                                       ERROR("Vconf: cant unregister callback for key: %s", key.c_str());
+                               }
+                       }
                }
        }
 
        void vconfIntCb(keynode_t *key, void *data)
        {
-               vconfCb(intCallbackMap, vconf_keynode_get_int, "int", VCONF_TYPE_INT, key, data);
+               vconfCbRun(intCallbackMap, vconfIntCb, vconf_keynode_get_int, "int", VCONF_TYPE_INT, key, data);
        }
        void vconfBoolCb(keynode_t *key, void *data)
        {
-               vconfCb(boolCallbackMap, vconf_keynode_get_bool, "bool", VCONF_TYPE_BOOL, key, data);
+               vconfCbRun(boolCallbackMap, vconfBoolCb, vconf_keynode_get_bool, "bool", VCONF_TYPE_BOOL, key, data);
        }
        void vconfDoubleCb(keynode_t *key, void *data)
        {
-               vconfCb(doubleCallbackMap, vconf_keynode_get_dbl, "double", VCONF_TYPE_DOUBLE, key, data);
+               vconfCbRun(doubleCallbackMap, vconfDoubleCb, vconf_keynode_get_dbl, "double", VCONF_TYPE_DOUBLE, key, data);
        }
        void vconfStringCb(keynode_t *key, void *data)
        {
-               vconfCb(stringCallbackMap, vconf_keynode_get_str, "char*", VCONF_TYPE_STRING, key, data);
+               vconfCbRun(stringCallbackMap, vconfStringCb, vconf_keynode_get_str, "char*", VCONF_TYPE_STRING, key, data);
        }
-
 }
 
+
 class VConfImpl : public VConfInterface
 {
 public:
+       enum class VConfRecordType {INT, BOOL, DOUBLE, STRING};
+
+       struct Handle : public HandleBase {
+               std::string key;
+               int callbackIdx;
+               VConfRecordType vconfRecordType;
+
+               Handle(std::string key, int callbackIdx, VConfRecordType vconfRecordType)
+                       : key{std::move(key)}, callbackIdx{callbackIdx}, vconfRecordType{vconfRecordType}
+               {}
+
+               ~Handle() override
+               {
+                       switch (vconfRecordType) {
+                       case VConfRecordType::INT:
+                               vconfUnregisterCb(intCallbackMap, vconfIntCb, key, callbackIdx);
+                               break;
+                       case VConfRecordType::BOOL:
+                               vconfUnregisterCb(boolCallbackMap, vconfBoolCb, key, callbackIdx);
+                               break;
+                       case VConfRecordType::DOUBLE:
+                               vconfUnregisterCb(doubleCallbackMap, vconfDoubleCb, key, callbackIdx);
+                               break;
+                       case VConfRecordType::STRING:
+                               vconfUnregisterCb(stringCallbackMap, vconfStringCb, key, callbackIdx);
+                               break;
+                       }
+               }
+       };
 
        int get(const std::string &key, int value) const override
        {
@@ -148,45 +203,27 @@ public:
        CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(int)> callback) override
        {
                auto idx = vconfRegisterKeyChangedCb(intCallbackMap, vconfIntCb, key, std::move(callback));
-               return std::make_tuple(key, idx, VConfRecordType::INT);
+               return CallbackHandle{ new Handle{key, idx, VConfRecordType::INT} };
        }
 
        CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(bool)> callback) override
        {
                auto idx = vconfRegisterKeyChangedCb(boolCallbackMap, vconfBoolCb, key, std::move(callback));
-               return std::make_tuple(key, idx, VConfRecordType::BOOL);
+               return CallbackHandle{ new Handle{key, idx, VConfRecordType::BOOL} };
        }
 
        CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(double)> callback) override
        {
                auto idx = vconfRegisterKeyChangedCb(doubleCallbackMap, vconfDoubleCb, key, std::move(callback));
-               return std::make_tuple(key, idx, VConfRecordType::DOUBLE);
+               return CallbackHandle{ new Handle{key, idx, VConfRecordType::DOUBLE} };
        }
 
        CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(std::string)> callback) override
        {
                auto idx = vconfRegisterKeyChangedCb(stringCallbackMap, vconfStringCb, key, std::move(callback));
-               return std::make_tuple(key, idx, VConfRecordType::STRING);
-       }
-
-
-       void unregisterCb(const CallbackHandle &ch) override
-       {
-               auto &key = std::get<0>(ch);
-               auto idx = std::get<1>(ch);
-               switch (std::get<2>(ch)) {
-               case VConfRecordType::INT:
-                       return vconfUnregisterCb(intCallbackMap, vconfIntCb, key, idx);
-               case VConfRecordType::BOOL:
-                       return vconfUnregisterCb(boolCallbackMap, vconfBoolCb, key, idx);
-               case VConfRecordType::DOUBLE:
-                       return vconfUnregisterCb(doubleCallbackMap, vconfDoubleCb, key, idx);
-               case VConfRecordType::STRING:
-                       return vconfUnregisterCb(stringCallbackMap, vconfStringCb, key, idx);
-               }
+               return CallbackHandle{ new Handle{key, idx, VConfRecordType::STRING} };
        }
 };
 
-
 template<>
 std::unique_ptr<VConfInterface> VConfSingleton::implementation = std::unique_ptr<VConfInterface>(new VConfImpl);
index 997e793..08d0d43 100644 (file)
@@ -1,16 +1,20 @@
 #ifndef VCONF_HPP
 #define VCONF_HPP
 
+#include "UniversalSwitchLog.hpp"
 #include "Singleton.hpp"
+
 #include <string>
 #include <functional>
-#include <tuple>
+#include <memory>
 
 class VConfInterface
 {
 public:
-       enum class VConfRecordType {INT, BOOL, DOUBLE, STRING};
-       using CallbackHandle = std::tuple<std::string, int, VConfRecordType>;
+       struct HandleBase {
+               virtual ~HandleBase() = default;
+       };
+       using CallbackHandle = std::unique_ptr<HandleBase>;
 
        virtual int get(const std::string &key, int defaultValue) const = 0;
        virtual bool get(const std::string &key, bool defaultValue) const = 0;
@@ -26,7 +30,6 @@ public:
        virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(bool)>) = 0;
        virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(double)>) = 0;
        virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(std::string)>) = 0;
-       virtual void unregisterCb(const CallbackHandle &cb) = 0;
 
        template<class T>
        inline CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(T)> callback)
@@ -34,6 +37,13 @@ public:
                return registerKeyChangedCb(key, callback);
        }
 
+       template<class T>
+       CallbackHandle registerAndGet(const std::string &key, T defaultValue, std::function<void(T)> callback)
+       {
+               auto handle = registerKeyChangedCb(key, callback);
+               callback(get(key, defaultValue));
+               return handle;
+       }
 };
 
 using VConfSingleton = MockableSingleton<VConfInterface>;
index ae1e81f..198297f 100644 (file)
@@ -1,6 +1,7 @@
 #include "VConf.hpp"
 #include <gtest/gtest.h>
 #include <glib.h>
+#include <iostream>
 
 
 void restoreDefault()
@@ -100,34 +101,35 @@ void testCallback(int x)
 TEST(VconfImplTest, registerKeyChangedCb_int)
 {
        INFO("registerKeyChangedCb_int");
-       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", testCallback);
+       auto h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", testCallback);
        eventLoop::run([]() {
                VConfSingleton::instance().set("VconfImplTest/int/20", 132);
        });
        EXPECT_EQ(testCallbackValue, 132);
-       VConfSingleton::instance().unregisterCb(h);
-
 }
 
 TEST(VconfImplTest, registerKeyChangedCb_int_lambda)
 {
        int v = 0;
-       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+       int count = 0;
+       auto h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
                v = x;
+               count++;
        });
        eventLoop::run([]() {
                VConfSingleton::instance().set("VconfImplTest/int/20", 133);
        });
        EXPECT_EQ(v, 133);
-       VConfSingleton::instance().unregisterCb(h);
-
+       EXPECT_EQ(count, 1);
 }
 
 TEST(VconfImplTest, registerKeyChangedCb_bool_lambda)
 {
        bool v = false;
-       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<bool>("VconfImplTest/int/20", [&](bool x) {
+       int count = 0;
+       auto h = VConfSingleton::instance().registerKeyChangedCb<bool>("VconfImplTest/int/20", [&](bool x) {
                v = x;
+               count++;
        });
        eventLoop::run([]() {
                VConfSingleton::instance().set("VconfImplTest/int/20", true);
@@ -137,42 +139,42 @@ TEST(VconfImplTest, registerKeyChangedCb_bool_lambda)
                VConfSingleton::instance().set("VconfImplTest/int/20", false);
        });
        EXPECT_EQ(v, false);
-       VConfSingleton::instance().unregisterCb(h);
+       EXPECT_EQ(count, 2);
 }
 
 TEST(VconfImplTest, registerKeyChangedCb_double_lambda)
 {
        double v = 0.0;
-       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<double>("VconfImplTest/int/20", [&](double x) {
+       auto h = VConfSingleton::instance().registerKeyChangedCb<double>("VconfImplTest/int/20", [&](double x) {
                v = x;
        });
        eventLoop::run([]() {
                VConfSingleton::instance().set("VconfImplTest/int/20", 13.6);
        });
        EXPECT_EQ(v, 13.6);
-       VConfSingleton::instance().unregisterCb(h);
 }
 
 TEST(VconfImplTest, registerKeyChangedCb_string_lambda)
 {
        std::string v;
-       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<std::string>("VconfImplTest/int/20", [&](std::string x) {
+       auto h = VConfSingleton::instance().registerKeyChangedCb<std::string>("VconfImplTest/int/20", [&](std::string x) {
                v = x;
        });
        eventLoop::run([]() {
                VConfSingleton::instance().set("VconfImplTest/int/20", std::string("foo2"));
        });
        EXPECT_EQ(v, "foo2");
-       VConfSingleton::instance().unregisterCb(h);
 }
 
 
 TEST(VconfImplTest, registerKeyChangedCb_int_unregister)
 {
        int v = 0;
+       int count = 0;
        {
-               VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               auto h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
                        v = x;
+                       count++;
                });
                eventLoop::run([]() {
                        VConfSingleton::instance().set("VconfImplTest/int/20", 1);
@@ -182,21 +184,23 @@ TEST(VconfImplTest, registerKeyChangedCb_int_unregister)
                        VConfSingleton::instance().set("VconfImplTest/int/20", 2);
                });
                EXPECT_EQ(v, 2);
-               VConfSingleton::instance().unregisterCb(h);
        }
        eventLoop::run([]() {
                VConfSingleton::instance().set("VconfImplTest/int/20", 3);
        });
        EXPECT_EQ(v, 2);
+       EXPECT_EQ(count, 2);
 }
 
 TEST(VconfImplTest, registerKeyChangedCb_int_unregister_2x)
 {
+       int count = 0;
        {
                int v = 0;
                {
-                       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+                       auto h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
                                v = x;
+                               count++;
                        });
                        eventLoop::run([]() {
                                VConfSingleton::instance().set("VconfImplTest/int/20", 11);
@@ -206,7 +210,6 @@ TEST(VconfImplTest, registerKeyChangedCb_int_unregister_2x)
                                VConfSingleton::instance().set("VconfImplTest/int/20", 12);
                        });
                        EXPECT_EQ(v, 12);
-                       VConfSingleton::instance().unregisterCb(h);
                }
                eventLoop::run([]() {
                        VConfSingleton::instance().set("VconfImplTest/int/20", 13);
@@ -217,8 +220,9 @@ TEST(VconfImplTest, registerKeyChangedCb_int_unregister_2x)
        {
                int v = 0;
                {
-                       VConfInterface::CallbackHandle h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+                       auto h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
                                v = x;
+                               count++;
                        });
                        eventLoop::run([]() {
                                VConfSingleton::instance().set("VconfImplTest/int/20", 21);
@@ -228,7 +232,6 @@ TEST(VconfImplTest, registerKeyChangedCb_int_unregister_2x)
                                VConfSingleton::instance().set("VconfImplTest/int/20", 22);
                        });
                        EXPECT_EQ(v, 22);
-                       VConfSingleton::instance().unregisterCb(h);
                }
                eventLoop::run([]() {
                        VConfSingleton::instance().set("VconfImplTest/int/20", 23);
@@ -236,7 +239,7 @@ TEST(VconfImplTest, registerKeyChangedCb_int_unregister_2x)
                EXPECT_EQ(v, 22);
        }
 
-       restoreDefault();
+       EXPECT_EQ(count, 4);
 }
 
 
@@ -246,18 +249,23 @@ TEST(VconfImplTest, registerKeyChangedCb_all)
        bool b = false;
        double v = 0.0;
        std::string s("empty");
+       int count1 = 0, count2 = 0, count3 = 0, count4 = 0;
 
-       VConfInterface::CallbackHandle h1 = VConfSingleton::instance().registerKeyChangedCb<bool>("VconfImplTest/int/20", [&](bool x) {
+       auto h1 = VConfSingleton::instance().registerKeyChangedCb<bool>("VconfImplTest/int/20", [&](bool x) {
                b = x;
+               count1++;
        });
-       VConfInterface::CallbackHandle h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+       auto h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
                i = x;
+               count2++;
        });
-       VConfInterface::CallbackHandle h3 = VConfSingleton::instance().registerKeyChangedCb<double>("VconfImplTest/int/20", [&](double x) {
+       auto h3 = VConfSingleton::instance().registerKeyChangedCb<double>("VconfImplTest/int/20", [&](double x) {
                v = x;
+               count3++;
        });
-       VConfInterface::CallbackHandle h4 = VConfSingleton::instance().registerKeyChangedCb<std::string>("VconfImplTest/int/20", [&](std::string x) {
+       auto h4 = VConfSingleton::instance().registerKeyChangedCb<std::string>("VconfImplTest/int/20", [&](std::string x) {
                s = x;
+               count4++;
        });
 
        eventLoop::run([]() {
@@ -292,9 +300,213 @@ TEST(VconfImplTest, registerKeyChangedCb_all)
        EXPECT_EQ(v, 20.2);
        EXPECT_EQ(s, "foo");
 
-       VConfSingleton::instance().unregisterCb(h1);
-       VConfSingleton::instance().unregisterCb(h2);
-       VConfSingleton::instance().unregisterCb(h3);
-       VConfSingleton::instance().unregisterCb(h4);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 1);
+       EXPECT_EQ(count3, 1);
+       EXPECT_EQ(count4, 1);
+
+}
+
+TEST(VconfImplTest, registerKeyChangedCb_unregisterInCallback)
+{
+       int v = 0;
+       int count = 0;
+       VConfInterface::CallbackHandle h;
+       h = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v = x;
+               count++;
+               h.reset();
+       });
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 13);
+       });
+       EXPECT_EQ(v, 13);
+       EXPECT_EQ(count, 1);
+       EXPECT_EQ(h, nullptr);
+}
+
+TEST(VconfImplTest, registerKeyChangedCb_unregisterSecondInCallback)
+{
+       int v1 = 1, v2 = 2, v3 = 3;
+       int count1 = 0, count2 = 0, count3 = 0;
+       VConfInterface::CallbackHandle h1, h2, h3;
+       h1 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v1 = x;
+               count1++;
+       });
+       h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v2 = x;
+               count2++;
+       });
+       h3 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v3 = x;
+               h2.reset();
+               count3++;
+       });
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 14);
+       });
+       EXPECT_EQ(v1, 14);
+       EXPECT_EQ(v2, 2);
+       EXPECT_EQ(v3, 14);
+       EXPECT_NE(h1, nullptr);
+       EXPECT_EQ(h2, nullptr);
+       EXPECT_NE(h3, nullptr);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 0);
+       EXPECT_EQ(count3, 1);
+}
+
+
+TEST(VconfImplTest, registerKeyChangedCb_registerInCallback)
+{
+       int v1 = 1, v2 = 2;
+       int count1 = 0, count2 = 0;
+
+       VConfInterface::CallbackHandle h1, h2;
+       h1 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v1 = x;
+               count1++;
+               h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+                       v2 = x;
+                       count2++;
+               });
+       });
+       EXPECT_EQ(h2, nullptr);
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 15);
+       });
+       EXPECT_EQ(v1, 15);
+       EXPECT_EQ(v2, 2);
+       EXPECT_NE(h2, nullptr);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 0);
+
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 16);
+       });
+
+       EXPECT_EQ(v1, 16);
+       EXPECT_EQ(v2, 16);
+       EXPECT_EQ(count1, 2);
+       EXPECT_EQ(count2, 1);
+}
+
+TEST(VconfImplTest, registerKeyChangedCb_unregisterAndRegisterInCallback)
+{
+       int v1 = 1, v2 = 2;
+       int count1 = 0, count2 = 0;
+       VConfInterface::CallbackHandle h1, h2;
+       h1 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v1 = x;
+               count1++;
+               h1.reset();
+               h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+                       v2 = x;
+                       count2++;
+               });
+       });
+       EXPECT_EQ(h2, nullptr);
+       eventLoop::run([&]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 115);
+       });
+       EXPECT_NE(h2, nullptr);
+       EXPECT_EQ(v1, 115);
+       EXPECT_EQ(v2, 2);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 0);
+       eventLoop::run([&]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 116);
+       });
+       EXPECT_EQ(v1, 115);
+       EXPECT_EQ(v2, 116);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 1);
+
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 117);
+       });
+
+       EXPECT_EQ(v1, 115);
+       EXPECT_EQ(v2, 117);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 2);
+}
+
+TEST(VconfImplTest, registerKeyChangedCb_registerAndUnregisterInCallback)
+{
+       int v1 = 1, v2 = 2;
+       int count1 = 0, count2 = 0;
+       VConfInterface::CallbackHandle h1, h2;
+       h1 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v1 = x;
+               count1++;
+               h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+                       v2 = x;
+                       count2++;
+               });
+               h1.reset();
+       });
+       EXPECT_EQ(h2, nullptr);
+       eventLoop::run([&]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 115);
+       });
+       EXPECT_NE(h2, nullptr);
+       EXPECT_EQ(v1, 115);
+       EXPECT_EQ(v2, 2);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 0);
+       eventLoop::run([&]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 116);
+       });
+       EXPECT_EQ(v1, 115);
+       EXPECT_EQ(v2, 116);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 1);
+
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 117);
+       });
+
+       EXPECT_EQ(v1, 115);
+       EXPECT_EQ(v2, 117);
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 2);
+}
+
+
+TEST(VconfImplTest, registerKeyChangedCb_callbackOrder)
+{
+       int v = 1;
+       int count1 = 0, count2 = 0, count3 = 0;
+       VConfInterface::CallbackHandle h1, h2, h3;
+       h1 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v += 2;
+               v *= 3;
+               count1++;
+       });
+       h2 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v += 5;
+               v *= 7;
+               count2++;
+       });
+       h3 = VConfSingleton::instance().registerKeyChangedCb<int>("VconfImplTest/int/20", [&](int x) {
+               v += 11;
+               v *= 13;
+               count3++;
+       });
+       eventLoop::run([]() {
+               VConfSingleton::instance().set("VconfImplTest/int/20", 14);
+       });
+       EXPECT_EQ(v, (((((1 + 11) * 13) + 5) * 7) + 2) * 3); //calbacks are called in reverse order
+       EXPECT_EQ(count1, 1);
+       EXPECT_EQ(count2, 1);
+       EXPECT_EQ(count3, 1);
+}
+
+
+TEST(VconfImplTest, registerKeyChangedCb_restoreDefault)
+{
        restoreDefault();
 }
+