1 #include <boost/container/flat_map.hpp> 2 #include <sdbusplus/unpack_properties.hpp> 3 4 #include <gmock/gmock.h> 5 6 namespace sdbusplus 7 { 8 9 struct ThrowingUnpack 10 { 11 template <typename... Args> 12 bool operator()(Args&&... args) const 13 { 14 unpackProperties(std::forward<Args>(args)...); 15 return false; 16 } 17 }; 18 19 struct NonThrowingUnpack 20 { 21 template <typename... Args> 22 std::optional<std::string> operator()(Args&&... args) const 23 { 24 return unpackPropertiesNoThrow(std::forward<Args>(args)...); 25 } 26 }; 27 28 template <typename A, typename B> 29 struct TestingTypes 30 { 31 using SystemUnderTest = A; 32 using Container = B; 33 }; 34 35 using VariantType = std::variant<std::string, uint32_t, float, double>; 36 using ContainerTypes = testing::Types< 37 TestingTypes<NonThrowingUnpack, 38 std::vector<std::pair<std::string, VariantType>>>, 39 TestingTypes<NonThrowingUnpack, 40 boost::container::flat_map<std::string, VariantType>>, 41 TestingTypes<NonThrowingUnpack, std::map<std::string, VariantType>>, 42 TestingTypes<ThrowingUnpack, 43 std::vector<std::pair<std::string, VariantType>>>, 44 TestingTypes<ThrowingUnpack, 45 boost::container::flat_map<std::string, VariantType>>, 46 TestingTypes<ThrowingUnpack, std::map<std::string, VariantType>>>; 47 48 template <typename Exception, typename F> 49 std::optional<Exception> captureException(F&& code) 50 { 51 try 52 { 53 code(); 54 } 55 catch (const Exception& e) 56 { 57 return e; 58 } 59 60 return std::nullopt; 61 } 62 63 template <typename Params> 64 struct UnpackPropertiesTest : public testing::Test 65 { 66 void SetUp() override 67 { 68 using namespace std::string_literals; 69 70 data.insert(data.end(), 71 std::make_pair("Key-1"s, VariantType("string"s))); 72 data.insert(data.end(), std::make_pair("Key-2"s, VariantType(42.f))); 73 data.insert(data.end(), std::make_pair("Key-3"s, VariantType(15.))); 74 } 75 76 typename Params::Container data; 77 typename Params::SystemUnderTest unpackPropertiesCall; 78 }; 79 80 TYPED_TEST_SUITE(UnpackPropertiesTest, ContainerTypes); 81 82 TYPED_TEST(UnpackPropertiesTest, returnsValueWhenKeyIsPresentAndTypeMatches) 83 { 84 using namespace testing; 85 86 std::string val1; 87 float val2 = 0.f; 88 double val3 = 0.; 89 90 EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", 91 val2, "Key-3", val3)); 92 93 ASSERT_THAT(val1, Eq("string")); 94 ASSERT_THAT(val2, FloatEq(42.f)); 95 ASSERT_THAT(val3, DoubleEq(15.)); 96 } 97 98 TYPED_TEST(UnpackPropertiesTest, 99 unpackChangesOriginalDataWhenPassedAsNonConstReference) 100 { 101 using namespace testing; 102 103 std::string val1, val2; 104 105 EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1)); 106 EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val2)); 107 108 ASSERT_THAT(val1, Eq("string")); 109 ASSERT_THAT(val2, Not(Eq("string"))); 110 } 111 112 TYPED_TEST(UnpackPropertiesTest, 113 unpackDoesntChangeOriginalDataWhenPassesAsConstReference) 114 { 115 using namespace testing; 116 117 std::string val1, val2; 118 119 EXPECT_FALSE(this->unpackPropertiesCall(Const(this->data), "Key-1", val1)); 120 EXPECT_FALSE(this->unpackPropertiesCall(Const(this->data), "Key-1", val2)); 121 122 ASSERT_THAT(val1, Eq("string")); 123 ASSERT_THAT(val2, Eq("string")); 124 } 125 126 TYPED_TEST(UnpackPropertiesTest, 127 returnsUndefinedValueForDuplicatedKeysWhenDataIsNonConstReference) 128 { 129 using namespace testing; 130 using namespace std::string_literals; 131 132 std::string val1; 133 float val2 = 0.f; 134 double val3 = 0.; 135 std::string val4; 136 137 EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", 138 val2, "Key-3", val3, "Key-1", 139 val4)); 140 141 ASSERT_THAT(val1, Eq("string")); 142 ASSERT_THAT(val2, FloatEq(42.f)); 143 ASSERT_THAT(val3, DoubleEq(15.)); 144 ASSERT_THAT(val4, Not(Eq("string"))); 145 } 146 147 TYPED_TEST(UnpackPropertiesTest, 148 returnsValueForDuplicatedKeysWhenDataIsConstReference) 149 { 150 using namespace testing; 151 using namespace std::string_literals; 152 153 std::string val1; 154 float val2 = 0.f; 155 double val3 = 0.; 156 std::string val4; 157 158 EXPECT_FALSE(this->unpackPropertiesCall(Const(this->data), "Key-1", val1, 159 "Key-2", val2, "Key-3", val3, 160 "Key-1", val4)); 161 162 ASSERT_THAT(val1, Eq("string")); 163 ASSERT_THAT(val2, FloatEq(42.f)); 164 ASSERT_THAT(val3, DoubleEq(15.)); 165 ASSERT_THAT(val4, Eq("string")); 166 } 167 168 template <typename Params> 169 struct UnpackPropertiesThrowingTest : public UnpackPropertiesTest<Params> 170 {}; 171 172 using ContainerTypesThrowing = testing::Types< 173 TestingTypes<ThrowingUnpack, 174 std::vector<std::pair<std::string, VariantType>>>, 175 TestingTypes<ThrowingUnpack, 176 boost::container::flat_map<std::string, VariantType>>, 177 TestingTypes<ThrowingUnpack, std::map<std::string, VariantType>>>; 178 179 TYPED_TEST_SUITE(UnpackPropertiesThrowingTest, ContainerTypesThrowing); 180 181 TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenKeyIsMissing) 182 { 183 using namespace testing; 184 185 std::string val1; 186 float val2 = 0.f; 187 double val3 = 0.; 188 189 auto error = captureException<exception::UnpackPropertyError>([&] { 190 this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-4", val2, 191 "Key-3", val3); 192 }); 193 194 ASSERT_TRUE(error); 195 ASSERT_THAT(error->reason, 196 Eq(exception::UnpackPropertyError::reasonMissingProperty)); 197 ASSERT_THAT(error->propertyName, Eq("Key-4")); 198 } 199 200 TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenTypeDoesntMatch) 201 { 202 using namespace testing; 203 204 std::string val1; 205 std::string val2; 206 double val3 = 0.; 207 208 auto error = captureException<exception::UnpackPropertyError>([&] { 209 this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", val2, 210 "Key-3", val3); 211 }); 212 213 ASSERT_TRUE(error); 214 ASSERT_THAT(error->reason, 215 Eq(exception::UnpackPropertyError::reasonTypeNotMatched)); 216 ASSERT_THAT(error->propertyName, Eq("Key-2")); 217 } 218 219 template <typename Params> 220 struct UnpackPropertiesNonThrowingTest : public UnpackPropertiesTest<Params> 221 {}; 222 223 using ContainerTypesNonThrowing = testing::Types< 224 TestingTypes<NonThrowingUnpack, 225 std::vector<std::pair<std::string, VariantType>>>, 226 TestingTypes<NonThrowingUnpack, 227 boost::container::flat_map<std::string, VariantType>>, 228 TestingTypes<NonThrowingUnpack, std::map<std::string, VariantType>>>; 229 230 TYPED_TEST_SUITE(UnpackPropertiesNonThrowingTest, ContainerTypesNonThrowing); 231 232 TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenKeyIsMissing) 233 { 234 using namespace testing; 235 236 std::string val1; 237 float val2 = 0.f; 238 double val3 = 0.; 239 240 auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1, 241 "Key-4", val2, "Key-3", val3); 242 243 ASSERT_TRUE(badProperty); 244 ASSERT_THAT(*badProperty, Eq("Key-4")); 245 } 246 247 TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenTypeDoesntMatch) 248 { 249 using namespace testing; 250 251 std::string val1; 252 std::string val2; 253 double val3 = 0.; 254 255 auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1, 256 "Key-2", val2, "Key-3", val3); 257 258 ASSERT_TRUE(badProperty); 259 ASSERT_THAT(*badProperty, Eq("Key-2")); 260 } 261 262 template <typename Params> 263 struct UnpackPropertiesTest_ForVector : public UnpackPropertiesTest<Params> 264 {}; 265 266 using ContainerTypesVector = testing::Types< 267 TestingTypes<NonThrowingUnpack, 268 std::vector<std::pair<std::string, VariantType>>>, 269 TestingTypes<ThrowingUnpack, 270 std::vector<std::pair<std::string, VariantType>>>>; 271 272 TYPED_TEST_SUITE(UnpackPropertiesTest_ForVector, ContainerTypesVector); 273 274 TYPED_TEST(UnpackPropertiesTest_ForVector, silentlyDiscardsDuplicatedKeyInData) 275 { 276 using namespace testing; 277 using namespace std::string_literals; 278 279 std::string val1; 280 float val2 = 0.f; 281 double val3 = 0.; 282 283 this->data.insert(this->data.end(), 284 std::make_pair("Key-1"s, VariantType("string2"s))); 285 286 EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", 287 val2, "Key-3", val3)); 288 289 ASSERT_THAT(val1, Eq("string")); 290 ASSERT_THAT(val2, FloatEq(42.f)); 291 ASSERT_THAT(val3, DoubleEq(15.)); 292 } 293 294 } // namespace sdbusplus 295