1 #include <sdbusplus/unpack_properties.hpp>
2
3 #include <gmock/gmock.h>
4
5 namespace sdbusplus
6 {
7
8 struct ThrowingUnpack
9 {
10 template <typename... Args>
operator ()sdbusplus::ThrowingUnpack11 bool operator()(Args&&... args) const
12 {
13 unpackProperties(std::forward<Args>(args)...);
14 return false;
15 }
16 };
17
18 struct NonThrowingUnpack
19 {
20 struct UnpackError
21 {
UnpackErrorsdbusplus::NonThrowingUnpack::UnpackError22 UnpackError(sdbusplus::UnpackErrorReason r, const std::string& p) :
23 reason(r), property(p)
24 {}
25 sdbusplus::UnpackErrorReason reason;
26 std::string property;
27 };
28
29 template <typename... Args>
operator ()sdbusplus::NonThrowingUnpack30 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>
captureException(F && code)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 {
SetUpsdbusplus::UnpackPropertiesTest75 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
TYPED_TEST(UnpackPropertiesTest,returnsValueWhenKeyIsPresentAndTypeMatches)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
TYPED_TEST(UnpackPropertiesTest,unpackDoesntChangeOriginalDataWhenPassedAsNonConstReference)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
TYPED_TEST(UnpackPropertiesTest,doesntReportMissingPropertyForOptional)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
TYPED_TEST(UnpackPropertiesTest,setPresentPointersOnSuccess)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(
147 this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", val2,
148 "Key-3", val3, "Key-4", 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
TYPED_TEST(UnpackPropertiesThrowingTest,throwsErrorWhenKeyIsMissing)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
TYPED_TEST(UnpackPropertiesThrowingTest,throwsErrorWhenTypeDoesntMatch)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
TYPED_TEST(UnpackPropertiesThrowingTest,throwsErrorWhenOptionalTypeDoesntMatch)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
TYPED_TEST(UnpackPropertiesNonThrowingTest,ErrorWhenKeyIsMissing)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
TYPED_TEST(UnpackPropertiesNonThrowingTest,ErrorWhenTypeDoesntMatch)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
TYPED_TEST(UnpackPropertiesNonThrowingTest,ErrorWhenOptionalTypeDoesntMatch)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
TYPED_TEST(UnpackPropertiesTest_ForVector,silentlyDiscardsDuplicatedKeyInData)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