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