Add PluginServices::DeleteProperty, test for property accessors

This commit is contained in:
Alexey Rybalchenko 2019-06-12 16:49:24 +02:00 committed by Dennis Klein
parent 5271d4236e
commit 7bea2bc0e6
4 changed files with 101 additions and 40 deletions

View File

@ -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<typename T>
auto SubscribeToPropertyChange(std::function<void(const std::string& key, T newValue)> callback) -> void { fPluginServices->SubscribeToPropertyChange<T>(fkName, callback); }
template<typename T>

View File

@ -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<typename T>
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, ".")};
}
}
void SetProperties(const fair::mq::Properties& props) { fConfig.SetProperties(props); }
template<typename T>
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

View File

@ -50,6 +50,7 @@ pair<string, string> getStringPair(const boost::any& v, const string& label)
unordered_map<type_index, function<pair<string, string>(const Property&)>> PropertyHelper::fTypeInfos = {
{ type_index(typeid(char)), [](const Property& p) { return pair<string, string>{ string(1, any_cast<char>(p)), "char" }; } },
{ type_index(typeid(unsigned char)), [](const Property& p) { return pair<string, string>{ string(1, any_cast<unsigned char>(p)), "unsigned char" }; } },
{ type_index(typeid(const char*)), [](const Property& p) { return pair<string, string>{ string(any_cast<const char*>(p)), "string" }; } },
{ type_index(typeid(string)), [](const Property& p) { return pair<string, string>{ any_cast<string>(p), "string" }; } },
{ type_index(typeid(int)), [](const Property& p) { return getString<int>(p, "int"); } },
{ type_index(typeid(size_t)), [](const Property& p) { return getString<size_t>(p, "size_t"); } },
@ -87,6 +88,7 @@ unordered_map<type_index, function<pair<string, string>(const Property&)>> Prope
unordered_map<type_index, void(*)(const EventManager&, const string&, const Property&)> PropertyHelper::fEventEmitters = {
{ type_index(typeid(char)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, char>(k, any_cast<char>(p)); } },
{ type_index(typeid(unsigned char)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, unsigned char>(k, any_cast<unsigned char>(p)); } },
{ type_index(typeid(const char*)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, string>(k, string(any_cast<const char*>(p))); } },
{ type_index(typeid(string)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, string>(k, any_cast<string>(p)); } },
{ type_index(typeid(int)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, int>(k, any_cast<int>(p)); } },
{ type_index(typeid(size_t)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, size_t>(k, any_cast<size_t>(p)); } },

View File

@ -8,12 +8,36 @@
#include "Fixture.h"
#include <fairmq/Tools.h>
#include <fairmq/PropertyOutput.h>
#include <algorithm>
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<int>("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<int>("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<string>("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<int>("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<string>("chans.data.0.address", "tcp://localhost:4321");
mServices.SetProperty<int>("chans.data.0.rcvBufSize", 100);
mServices.SetProperty<double>("data-rate", 0.9);
break;
default:
break;
}
});
mServices.UnsubscribeFromPropertyChange<int>("test");
mServices.SetProperty<int>("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<int>("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<const char*>(properties.at("custom1")), "test1");
ASSERT_EQ(boost::any_cast<const char*>(properties.at("custom2")), "test2");
ASSERT_EQ(boost::any_cast<int>(properties.at("custom3")), 67890);
ASSERT_EQ(boost::any_cast<int>(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<int>(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<int>("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<int>("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<MyClass>();
// after calling AddType proper string value should be returned
ASSERT_EQ(mServices.GetPropertyAsString("customType"), "message");
}
} /* namespace */