| #include <boost/container/flat_map.hpp> |
| #include <sdbusplus/unpack_properties.hpp> |
| |
| #include <gmock/gmock.h> |
| |
| namespace sdbusplus |
| { |
| |
| struct ThrowingUnpack |
| { |
| template <typename... Args> |
| bool operator()(Args&&... args) const |
| { |
| unpackProperties(std::forward<Args>(args)...); |
| return false; |
| } |
| }; |
| |
| struct NonThrowingUnpack |
| { |
| struct UnpackError |
| { |
| UnpackError(sdbusplus::UnpackErrorReason r, const std::string& p) : |
| reason(r), property(p){}; |
| sdbusplus::UnpackErrorReason reason; |
| std::string property; |
| }; |
| |
| template <typename... Args> |
| std::optional<UnpackError> operator()(Args&&... args) const |
| { |
| std::optional<UnpackError> error; |
| unpackPropertiesNoThrow( |
| [&error](sdbusplus::UnpackErrorReason reason, |
| const std::string& property) { |
| error.emplace(reason, property); |
| }, |
| std::forward<Args>(args)...); |
| return error; |
| } |
| }; |
| |
| template <typename A, typename B> |
| struct TestingTypes |
| { |
| using SystemUnderTest = A; |
| using Container = B; |
| }; |
| |
| using VariantType = std::variant<std::string, uint32_t, float, double>; |
| using ContainerTypes = testing::Types< |
| TestingTypes<NonThrowingUnpack, |
| std::vector<std::pair<std::string, VariantType>>>, |
| TestingTypes<ThrowingUnpack, |
| std::vector<std::pair<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 Params> |
| 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.))); |
| } |
| |
| typename Params::Container data; |
| typename Params::SystemUnderTest unpackPropertiesCall; |
| }; |
| |
| TYPED_TEST_SUITE(UnpackPropertiesTest, ContainerTypes); |
| |
| TYPED_TEST(UnpackPropertiesTest, returnsValueWhenKeyIsPresentAndTypeMatches) |
| { |
| using namespace testing; |
| |
| std::string val1; |
| float val2 = 0.f; |
| double val3 = 0.; |
| |
| EXPECT_FALSE(this->unpackPropertiesCall(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, |
| unpackDoesntChangeOriginalDataWhenPassedAsNonConstReference) |
| { |
| using namespace testing; |
| |
| std::string val1, val2; |
| |
| EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1)); |
| EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val2)); |
| |
| ASSERT_THAT(val1, Eq("string")); |
| ASSERT_THAT(val2, Eq("string")); |
| } |
| |
| TYPED_TEST(UnpackPropertiesTest, doesntReportMissingPropertyForOptional) |
| { |
| using namespace testing; |
| using namespace std::string_literals; |
| |
| std::optional<std::string> val1; |
| std::optional<std::string> val4; |
| |
| EXPECT_FALSE( |
| this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-4", val4)); |
| |
| ASSERT_THAT(val1, Eq("string")); |
| ASSERT_THAT(val4, Eq(std::nullopt)); |
| } |
| |
| TYPED_TEST(UnpackPropertiesTest, setPresentPointersOnSuccess) |
| { |
| using namespace testing; |
| using namespace std::string_literals; |
| |
| const std::string* val1 = nullptr; |
| const float* val2 = nullptr; |
| const double* val3 = nullptr; |
| const std::string* val4 = nullptr; |
| |
| EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", |
| val2, "Key-3", val3, "Key-4", |
| val4)); |
| |
| ASSERT_TRUE(val1 && val2 && val3); |
| ASSERT_TRUE(!val4); |
| |
| ASSERT_THAT(*val1, Eq("string")); |
| ASSERT_THAT(*val2, FloatEq(42.f)); |
| ASSERT_THAT(*val3, DoubleEq(15.)); |
| } |
| |
| template <typename Params> |
| struct UnpackPropertiesThrowingTest : public UnpackPropertiesTest<Params> |
| {}; |
| |
| using ContainerTypesThrowing = testing::Types<TestingTypes< |
| ThrowingUnpack, std::vector<std::pair<std::string, VariantType>>>>; |
| |
| TYPED_TEST_SUITE(UnpackPropertiesThrowingTest, ContainerTypesThrowing); |
| |
| TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenKeyIsMissing) |
| { |
| using namespace testing; |
| |
| std::string val1; |
| float val2 = 0.f; |
| double val3 = 0.; |
| |
| auto error = captureException<exception::UnpackPropertyError>([&] { |
| this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-4", val2, |
| "Key-3", val3); |
| }); |
| |
| ASSERT_TRUE(error); |
| ASSERT_THAT(error->reason, Eq(UnpackErrorReason::missingProperty)); |
| ASSERT_THAT(error->propertyName, Eq("Key-4")); |
| } |
| |
| TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenTypeDoesntMatch) |
| { |
| using namespace testing; |
| |
| std::string val1; |
| std::string val2; |
| double val3 = 0.; |
| |
| auto error = captureException<exception::UnpackPropertyError>([&] { |
| this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", val2, |
| "Key-3", val3); |
| }); |
| |
| ASSERT_TRUE(error); |
| ASSERT_THAT(error->reason, Eq(UnpackErrorReason::wrongType)); |
| ASSERT_THAT(error->propertyName, Eq("Key-2")); |
| } |
| |
| TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenOptionalTypeDoesntMatch) |
| { |
| using namespace testing; |
| |
| std::optional<std::string> val1; |
| std::optional<std::string> val2; |
| |
| auto error = captureException<exception::UnpackPropertyError>([&] { |
| this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", val2); |
| }); |
| |
| ASSERT_TRUE(error); |
| ASSERT_THAT(error->reason, Eq(UnpackErrorReason::wrongType)); |
| ASSERT_THAT(error->propertyName, Eq("Key-2")); |
| } |
| |
| template <typename Params> |
| struct UnpackPropertiesNonThrowingTest : public UnpackPropertiesTest<Params> |
| {}; |
| |
| using ContainerTypesNonThrowing = testing::Types<TestingTypes< |
| NonThrowingUnpack, std::vector<std::pair<std::string, VariantType>>>>; |
| |
| TYPED_TEST_SUITE(UnpackPropertiesNonThrowingTest, ContainerTypesNonThrowing); |
| |
| TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenKeyIsMissing) |
| { |
| using namespace testing; |
| |
| std::string val1; |
| float val2 = 0.f; |
| double val3 = 0.; |
| |
| auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1, |
| "Key-4", val2, "Key-3", val3); |
| |
| ASSERT_TRUE(badProperty); |
| EXPECT_THAT(badProperty->reason, Eq(UnpackErrorReason::missingProperty)); |
| EXPECT_THAT(badProperty->property, Eq("Key-4")); |
| } |
| |
| TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenTypeDoesntMatch) |
| { |
| using namespace testing; |
| |
| std::string val1; |
| std::string val2; |
| double val3 = 0.; |
| |
| auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1, |
| "Key-2", val2, "Key-3", val3); |
| |
| ASSERT_TRUE(badProperty); |
| EXPECT_THAT(badProperty->reason, Eq(UnpackErrorReason::wrongType)); |
| EXPECT_THAT(badProperty->property, Eq("Key-2")); |
| } |
| |
| TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenOptionalTypeDoesntMatch) |
| { |
| using namespace testing; |
| |
| std::optional<std::string> val1; |
| std::optional<std::string> val2; |
| |
| auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1, |
| "Key-2", val2); |
| |
| ASSERT_TRUE(badProperty); |
| EXPECT_THAT(badProperty->reason, Eq(UnpackErrorReason::wrongType)); |
| EXPECT_THAT(badProperty->property, Eq("Key-2")); |
| } |
| |
| template <typename Params> |
| struct UnpackPropertiesTest_ForVector : public UnpackPropertiesTest<Params> |
| {}; |
| |
| using ContainerTypesVector = testing::Types< |
| TestingTypes<NonThrowingUnpack, |
| std::vector<std::pair<std::string, VariantType>>>, |
| TestingTypes<ThrowingUnpack, |
| std::vector<std::pair<std::string, VariantType>>>>; |
| |
| TYPED_TEST_SUITE(UnpackPropertiesTest_ForVector, ContainerTypesVector); |
| |
| TYPED_TEST(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))); |
| |
| EXPECT_FALSE(this->unpackPropertiesCall(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 |