diff --git a/fairmq/Plugin.h b/fairmq/Plugin.h index a9b08095..9affec37 100644 --- a/fairmq/Plugin.h +++ b/fairmq/Plugin.h @@ -107,6 +107,8 @@ class Plugin bool UpdateProperty(const std::string& key, T val) { return fPluginServices->UpdateProperty(key, val); } bool UpdateProperties(const fair::mq::Properties& input) { return fPluginServices->UpdateProperties(input); } + void DeleteProperty(const std::string& key) { fPluginServices->DeleteProperty(key); } + template auto SubscribeToPropertyChange(std::function callback) -> void { fPluginServices->SubscribeToPropertyChange(fkName, callback); } template diff --git a/fairmq/PluginServices.h b/fairmq/PluginServices.h index e745240d..68d1ef33 100644 --- a/fairmq/PluginServices.h +++ b/fairmq/PluginServices.h @@ -185,33 +185,20 @@ class PluginServices /// @brief Set config property /// @param key /// @param val - /// @throws fair::mq::PluginServices::InvalidStateError if method is called in unsupported device states /// /// Setting a config property will store the value in the FairMQ internal config store and notify any subscribers about the update. /// It is property dependent, if the call to this method will have an immediate, delayed or any effect at all. template auto SetProperty(const std::string& key, T val) -> void { - auto currentState = GetCurrentDeviceState(); - if ( (currentState == DeviceState::InitializingDevice) - || (currentState == DeviceState::Initialized) - || (currentState == DeviceState::Binding) - || (currentState == DeviceState::Bound) - || (currentState == DeviceState::Connecting) - || (currentState == DeviceState::Ready)) { - fConfig.SetProperty(key, val); - } else { - throw InvalidStateError{ - tools::ToString("PluginServices::SetProperty is not supported in device state ", currentState, ". ", - "Supported state is ", DeviceState::InitializingDevice, ".")}; - } + fConfig.SetProperty(key, val); } void SetProperties(const fair::mq::Properties& props) { fConfig.SetProperties(props); } template bool UpdateProperty(const std::string& key, T val) { return fConfig.UpdateProperty(key, val); } bool UpdateProperties(const fair::mq::Properties& input) { return fConfig.UpdateProperties(input); } - struct InvalidStateError : std::runtime_error { using std::runtime_error::runtime_error; }; + void DeleteProperty(const std::string& key) { fConfig.DeleteProperty(key); } /// @brief Read config property /// @param key diff --git a/fairmq/Properties.cxx b/fairmq/Properties.cxx index e5045217..086ef7d5 100644 --- a/fairmq/Properties.cxx +++ b/fairmq/Properties.cxx @@ -50,6 +50,7 @@ pair getStringPair(const boost::any& v, const string& label) unordered_map(const Property&)>> PropertyHelper::fTypeInfos = { { type_index(typeid(char)), [](const Property& p) { return pair{ string(1, any_cast(p)), "char" }; } }, { type_index(typeid(unsigned char)), [](const Property& p) { return pair{ string(1, any_cast(p)), "unsigned char" }; } }, + { type_index(typeid(const char*)), [](const Property& p) { return pair{ string(any_cast(p)), "string" }; } }, { type_index(typeid(string)), [](const Property& p) { return pair{ any_cast(p), "string" }; } }, { type_index(typeid(int)), [](const Property& p) { return getString(p, "int"); } }, { type_index(typeid(size_t)), [](const Property& p) { return getString(p, "size_t"); } }, @@ -87,6 +88,7 @@ unordered_map(const Property&)>> Prope unordered_map PropertyHelper::fEventEmitters = { { type_index(typeid(char)), [](const EventManager& em, const string& k, const Property& p) { em.Emit(k, any_cast(p)); } }, { type_index(typeid(unsigned char)), [](const EventManager& em, const string& k, const Property& p) { em.Emit(k, any_cast(p)); } }, + { type_index(typeid(const char*)), [](const EventManager& em, const string& k, const Property& p) { em.Emit(k, string(any_cast(p))); } }, { type_index(typeid(string)), [](const EventManager& em, const string& k, const Property& p) { em.Emit(k, any_cast(p)); } }, { type_index(typeid(int)), [](const EventManager& em, const string& k, const Property& p) { em.Emit(k, any_cast(p)); } }, { type_index(typeid(size_t)), [](const EventManager& em, const string& k, const Property& p) { em.Emit(k, any_cast(p)); } }, diff --git a/test/plugin_services/_config.cxx b/test/plugin_services/_config.cxx index 475a6584..de6632b8 100644 --- a/test/plugin_services/_config.cxx +++ b/test/plugin_services/_config.cxx @@ -8,12 +8,36 @@ #include "Fixture.h" #include +#include #include +struct MyClass +{ + MyClass() : msg() {} + MyClass(const std::string& a) : msg(a) {} + MyClass& operator=(const MyClass& b) { msg = b.msg; return *this; }; + std::string msg; +}; + +std::ostream& operator<<(std::ostream& os, const MyClass& b) +{ + os << b.msg; + return os; +} + +std::istream& operator>>(std::istream& in, MyClass& b) +{ + std::string val; + in >> val; + b.msg = val; + return in; +} + namespace { using namespace std; +using namespace fair::mq; using fair::mq::test::PluginServices; using DeviceState = fair::mq::PluginServices::DeviceState; using DeviceStateTransition = fair::mq::PluginServices::DeviceStateTransition; @@ -27,20 +51,7 @@ TEST_F(PluginServices, ConfigSynchronous) break; case DeviceState::DeviceReady: EXPECT_EQ(mServices.GetProperty("blubb"), 42); - EXPECT_EQ(mServices.GetPropertyAsString("blubb"), fair::mq::tools::ToString(42)); - break; - default: - break; - } - }); -} - -TEST_F(PluginServices, ConfigInvalidStateError) -{ - mServices.SubscribeToDeviceStateChange("test",[&](DeviceState newState){ - switch (newState) { - case DeviceState::DeviceReady: - ASSERT_THROW(mServices.SetProperty("blubb", 42), fair::mq::PluginServices::InvalidStateError); + EXPECT_EQ(mServices.GetPropertyAsString("blubb"), tools::ToString(42)); break; default: break; @@ -61,6 +72,8 @@ TEST_F(PluginServices, ConfigCallbacks) { mServices.SubscribeToPropertyChange("test", [](const string& key, string value) { if (key == "chans.data.0.address") { ASSERT_EQ(value, "tcp://localhost:4321"); } + if (key == "custom1") { ASSERT_EQ(value, "test1"); } + if (key == "custom2") { ASSERT_EQ(value, "test2"); } }); mServices.SubscribeToPropertyChange("test", [](const string& key, int /*value*/) { @@ -73,19 +86,76 @@ TEST_F(PluginServices, ConfigCallbacks) if (key == "data-rate") { ASSERT_EQ(value, 0.9); } }); - mServices.SubscribeToDeviceStateChange("test",[&](DeviceState newState){ - switch (newState) { - case DeviceState::InitializingDevice: - mServices.SetProperty("chans.data.0.address", "tcp://localhost:4321"); - mServices.SetProperty("chans.data.0.rcvBufSize", 100); - mServices.SetProperty("data-rate", 0.9); - break; - default: - break; - } - }); + mServices.SetProperty("chans.data.0.address", "tcp://localhost:4321"); + mServices.SetProperty("data-rate", 0.9); mServices.UnsubscribeFromPropertyChange("test"); + + mServices.SetProperty("chans.data.0.rcvBufSize", 100); +} + +TEST_F(PluginServices, Accessors) +{ + // try adding properties in bulk + Properties properties; + properties.emplace("custom1", "test1"); + properties.emplace("custom2", "test2"); + mServices.SetProperties(properties); + + // an existing property should exist + ASSERT_EQ(mServices.PropertyExists("custom1"), true); + // a not existing property should not exists + ASSERT_EQ(mServices.PropertyExists("custom3"), false); + // updating a not existing property should fail + ASSERT_EQ(mServices.UpdateProperty("custom3", 12345), false); + mServices.SetProperty("custom3", 12345); + // updating an existing existing property should succeed + ASSERT_EQ(mServices.UpdateProperty("custom3", 67890), true); + // updated property should return the correct value + ASSERT_EQ(mServices.GetProperty("custom3"), 67890); + // asking for non existing property should return the fallback if it is provided + ASSERT_EQ(mServices.GetProperty("custom4", 1005), 1005); + + properties = mServices.GetPropertiesStartingWith("custom"); + // bulk operation should return correct number elements + ASSERT_EQ(properties.size(), 3); + mServices.SetProperty("newcustom", 12345); + properties = mServices.GetProperties(".*custom.*"); + // bulk operation should return correct number elements + ASSERT_EQ(properties.size(), 4); + + // the container returned by the bulk operation should contain correct values + ASSERT_EQ(boost::any_cast(properties.at("custom1")), "test1"); + ASSERT_EQ(boost::any_cast(properties.at("custom2")), "test2"); + ASSERT_EQ(boost::any_cast(properties.at("custom3")), 67890); + ASSERT_EQ(boost::any_cast(properties.at("newcustom")), 12345); + + properties.at("custom3") = 11111; + mServices.UpdateProperties(properties); + + // bulk update should update values of all properties, but only if all of them exist + ASSERT_EQ(boost::any_cast(properties.at("custom3")), 11111); + + properties.at("custom3") = 22222; + properties.emplace("custom4", 17); + // bulk update should fail if any of the properties do not exist in the container ... + ASSERT_EQ(mServices.UpdateProperties(properties), false); + // ... all the values should remain unchanged + ASSERT_EQ(mServices.GetProperty("custom3"), 11111); + + mServices.DeleteProperty("custom3"); + // property should no longer exist after deletion + ASSERT_EQ(mServices.PropertyExists("custom3"), false); + // accessing this property should throw an exception + ASSERT_THROW(mServices.GetProperty("custom3"), fair::mq::PluginServices::PropertyNotFoundError); + + mServices.SetProperty("customType", MyClass("message")); + // without adding custom type information, proper string value will not be returned + ASSERT_NE(mServices.GetPropertyAsString("customType"), "message"); + ASSERT_EQ(mServices.GetPropertyAsString("customType"), "[unidentified_type]"); + PropertyHelper::AddType(); + // after calling AddType proper string value should be returned + ASSERT_EQ(mServices.GetPropertyAsString("customType"), "message"); } } /* namespace */