Added utility functions getAllProperties and unpackProperties
Tested:
- Added example to verify that functions work correctly
- Added new unit tests that are passing
- All other tests are still passing after this change
- Added handling of new type (std::monostate) which can
be used as first type in variant to represent that none
of the other types was matched
Change-Id: Ic8e7c8d3116d64b94be37147ae8a80ebb5d3811d
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/test/meson.build b/test/meson.build
index 374dada..02e4550 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -28,6 +28,7 @@
'message/native_types',
'message/types',
'timer',
+ 'unpack_properties',
'utility/tuple_to_array',
'utility/type_traits',
]
diff --git a/test/unpack_properties.cpp b/test/unpack_properties.cpp
new file mode 100644
index 0000000..90415cb
--- /dev/null
+++ b/test/unpack_properties.cpp
@@ -0,0 +1,193 @@
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/unpack_properties.hpp>
+
+#include <gmock/gmock.h>
+
+namespace sdbusplus
+{
+
+using VariantType = std::variant<std::string, uint32_t, float, double>;
+using ContainerTypes =
+ testing::Types<std::vector<std::pair<std::string, VariantType>>,
+ boost::container::flat_map<std::string, VariantType>,
+ std::map<std::string, VariantType>>;
+
+template <typename Exception, typename F>
+std::optional<Exception> captureException(F&& code)
+{
+ try
+ {
+ code();
+ }
+ catch (const Exception& e)
+ {
+ return e;
+ }
+
+ return std::nullopt;
+}
+
+template <typename Container>
+struct UnpackPropertiesTest : public testing::Test
+{
+ void SetUp() override
+ {
+ using namespace std::string_literals;
+
+ data.insert(data.end(),
+ std::make_pair("Key-1"s, VariantType("string"s)));
+ data.insert(data.end(), std::make_pair("Key-2"s, VariantType(42.f)));
+ data.insert(data.end(), std::make_pair("Key-3"s, VariantType(15.)));
+ }
+
+ Container data;
+};
+
+TYPED_TEST_SUITE(UnpackPropertiesTest, ContainerTypes);
+
+TYPED_TEST(UnpackPropertiesTest, returnsValueWhenKeyIsPresentAndTypeMatches)
+{
+ using namespace testing;
+
+ std::string val1;
+ float val2 = 0.f;
+ double val3 = 0.;
+
+ unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3", val3);
+
+ ASSERT_THAT(val1, Eq("string"));
+ ASSERT_THAT(val2, FloatEq(42.f));
+ ASSERT_THAT(val3, DoubleEq(15.));
+}
+
+TYPED_TEST(UnpackPropertiesTest,
+ unpackChangesOriginalDataWhenPassedAsNonConstReference)
+{
+ using namespace testing;
+
+ std::string val1, val2;
+
+ unpackProperties(this->data, "Key-1", val1);
+ unpackProperties(this->data, "Key-1", val2);
+
+ ASSERT_THAT(val1, Eq("string"));
+ ASSERT_THAT(val2, Not(Eq("string")));
+}
+
+TYPED_TEST(UnpackPropertiesTest,
+ unpackDoesntChangeOriginalDataWhenPassesAsConstReference)
+{
+ using namespace testing;
+
+ std::string val1, val2;
+
+ unpackProperties(Const(this->data), "Key-1", val1);
+ unpackProperties(Const(this->data), "Key-1", val2);
+
+ ASSERT_THAT(val1, Eq("string"));
+ ASSERT_THAT(val2, Eq("string"));
+}
+
+TYPED_TEST(UnpackPropertiesTest, throwsErrorWhenKeyIsMissing)
+{
+ using namespace testing;
+
+ std::string val1;
+ float val2 = 0.f;
+ double val3 = 0.;
+
+ auto error = captureException<exception::UnpackPropertyError>([&] {
+ unpackProperties(this->data, "Key-1", val1, "Key-4", val2, "Key-3",
+ val3);
+ });
+
+ ASSERT_TRUE(error);
+ ASSERT_THAT(error->reason,
+ Eq(exception::UnpackPropertyError::reasonMissingProperty));
+ ASSERT_THAT(error->propertyName, Eq("Key-4"));
+}
+
+TYPED_TEST(UnpackPropertiesTest, throwsErrorWhenTypeDoesntMatch)
+{
+ using namespace testing;
+
+ std::string val1;
+ std::string val2;
+ double val3 = 0.;
+
+ auto error = captureException<exception::UnpackPropertyError>([&] {
+ unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3",
+ val3);
+ });
+
+ ASSERT_TRUE(error);
+ ASSERT_THAT(error->reason,
+ Eq(exception::UnpackPropertyError::reasonTypeNotMatched));
+ ASSERT_THAT(error->propertyName, Eq("Key-2"));
+}
+
+TYPED_TEST(UnpackPropertiesTest,
+ returnsUndefinedValueForDuplicatedKeysWhenDataIsNonConstReference)
+{
+ using namespace testing;
+ using namespace std::string_literals;
+
+ std::string val1;
+ float val2 = 0.f;
+ double val3 = 0.;
+ std::string val4;
+
+ unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3", val3,
+ "Key-1", val4);
+
+ ASSERT_THAT(val1, Eq("string"));
+ ASSERT_THAT(val2, FloatEq(42.f));
+ ASSERT_THAT(val3, DoubleEq(15.));
+ ASSERT_THAT(val4, Not(Eq("string")));
+}
+
+TYPED_TEST(UnpackPropertiesTest,
+ returnsValueForDuplicatedKeysWhenDataIsConstReference)
+{
+ using namespace testing;
+ using namespace std::string_literals;
+
+ std::string val1;
+ float val2 = 0.f;
+ double val3 = 0.;
+ std::string val4;
+
+ unpackProperties(Const(this->data), "Key-1", val1, "Key-2", val2, "Key-3",
+ val3, "Key-1", val4);
+
+ ASSERT_THAT(val1, Eq("string"));
+ ASSERT_THAT(val2, FloatEq(42.f));
+ ASSERT_THAT(val3, DoubleEq(15.));
+ ASSERT_THAT(val4, Eq("string"));
+}
+
+struct UnpackPropertiesTest_ForVector :
+ public UnpackPropertiesTest<
+ std::vector<std::pair<std::string, VariantType>>>
+{};
+
+TEST_F(UnpackPropertiesTest_ForVector, silentlyDiscardsDuplicatedKeyInData)
+{
+ using namespace testing;
+ using namespace std::string_literals;
+
+ std::string val1;
+ float val2 = 0.f;
+ double val3 = 0.;
+
+ this->data.insert(this->data.end(),
+ std::make_pair("Key-1"s, VariantType("string2"s)));
+
+ unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3", val3);
+
+ ASSERT_THAT(val1, Eq("string"));
+ ASSERT_THAT(val2, FloatEq(42.f));
+ ASSERT_THAT(val3, DoubleEq(15.));
+}
+
+} // namespace sdbusplus
diff --git a/test/utility/type_traits.cpp b/test/utility/type_traits.cpp
index 58ade36..33bf8c1 100644
--- a/test/utility/type_traits.cpp
+++ b/test/utility/type_traits.cpp
@@ -2,7 +2,7 @@
#include <type_traits>
-#include <gtest/gtest.h>
+#include <gmock/gmock.h>
namespace
{
@@ -26,4 +26,60 @@
"array_to_ptr_t<int, char[100]> != char[100]");
}
+TEST(TypeTraits, HasMemberFind)
+{
+ using sdbusplus::utility::has_member_find_v;
+ using namespace testing;
+
+ ASSERT_THAT((has_member_find_v<std::map<std::string, int>>), Eq(true));
+ ASSERT_THAT((has_member_find_v<std::vector<std::pair<std::string, int>>>),
+ Eq(false));
+
+ struct Foo
+ {
+ using value_type = std::pair<int, int>;
+
+ void find(std::tuple_element_t<0, value_type>)
+ {}
+ };
+
+ struct Bar
+ {};
+
+ ASSERT_THAT(has_member_find_v<Foo>, Eq(true));
+ ASSERT_THAT(has_member_find_v<Foo&>, Eq(true));
+ ASSERT_THAT(has_member_find_v<const Foo&>, Eq(true));
+
+ ASSERT_THAT(has_member_find_v<Bar>, Eq(false));
+}
+
+TEST(TypeTraits, HasMemberContains)
+{
+ using sdbusplus::utility::has_member_contains_v;
+ using namespace testing;
+
+ // std::map has member_contains from c++20
+ ASSERT_THAT((has_member_contains_v<std::map<std::string, int>>), Eq(false));
+ ASSERT_THAT(
+ (has_member_contains_v<std::vector<std::pair<std::string, int>>>),
+ Eq(false));
+
+ struct Foo
+ {
+ using value_type = std::pair<int, int>;
+
+ void contains(std::tuple_element_t<0, value_type>)
+ {}
+ };
+
+ struct Bar
+ {};
+
+ ASSERT_THAT(has_member_contains_v<Foo>, Eq(true));
+ ASSERT_THAT(has_member_contains_v<Foo&>, Eq(true));
+ ASSERT_THAT(has_member_contains_v<const Foo&>, Eq(true));
+
+ ASSERT_THAT(has_member_contains_v<Bar>, Eq(false));
+}
+
} // namespace