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