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