xref: /openbmc/sdbusplus/test/message/append.cpp (revision 1837cd59)
1 #include <systemd/sd-bus-protocol.h>
2 
3 #include <sdbusplus/message.hpp>
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 
6 #include <array>
7 #include <map>
8 #include <set>
9 #include <span>
10 #include <string>
11 #include <tuple>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <variant>
15 #include <vector>
16 
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 
20 namespace
21 {
22 
23 using testing::Eq;
24 using testing::MatcherCast;
25 using testing::Pointee;
26 using testing::Return;
27 using testing::SafeMatcherCast;
28 using testing::StrEq;
29 
30 MATCHER_P(iovec_equal, match_string, "")
31 {
32     const char* start = std::bit_cast<char*>(arg->iov_base);
33     return std::string(start, arg->iov_len) == match_string;
34 }
35 
36 class AppendTest : public testing::Test
37 {
38   protected:
39     testing::StrictMock<sdbusplus::SdBusMock> mock;
40 
41     void SetUp() override
42     {
43         EXPECT_CALL(mock, sd_bus_message_new_method_call(testing::_, testing::_,
44                                                          nullptr, nullptr,
45                                                          nullptr, nullptr))
46             .WillRepeatedly(Return(0));
47     }
48 
49     sdbusplus::message_t new_message()
50     {
51         return sdbusplus::get_mocked_new(&mock).new_method_call(
52             nullptr, nullptr, nullptr, nullptr);
53     }
54 
55     template <typename T>
56     void expect_basic(char type, T val)
57     {
58         EXPECT_CALL(mock, sd_bus_message_append_basic(
59                               nullptr, type,
60                               MatcherCast<const void*>(
61                                   SafeMatcherCast<const T*>(Pointee(Eq(val))))))
62             .WillOnce(Return(0));
63     }
64 
65     void expect_basic_string(char type, const char* str)
66     {
67         EXPECT_CALL(mock, sd_bus_message_append_basic(
68                               nullptr, type,
69                               MatcherCast<const void*>(
70                                   SafeMatcherCast<const char*>(StrEq(str)))))
71             .WillOnce(Return(0));
72     }
73     void expect_basic_string_iovec(const char* str, size_t size)
74     {
75         std::string tmp = {str, size};
76         EXPECT_CALL(mock, sd_bus_message_append_string_iovec(
77                               nullptr, iovec_equal(tmp), 1))
78             .WillOnce(Return(0));
79     }
80 
81     void expect_open_container(char type, const char* contents)
82     {
83         EXPECT_CALL(
84             mock, sd_bus_message_open_container(nullptr, type, StrEq(contents)))
85             .WillOnce(Return(0));
86     }
87 
88     void expect_close_container()
89     {
90         EXPECT_CALL(mock, sd_bus_message_close_container(nullptr))
91             .WillOnce(Return(0));
92     }
93 };
94 
95 TEST_F(AppendTest, RValueInt)
96 {
97     static_assert(
98         sdbusplus::message::details::can_append_multiple_v<decltype(1)>);
99     expect_basic<int>(SD_BUS_TYPE_INT32, 1);
100     new_message().append(1);
101 }
102 
103 TEST_F(AppendTest, LValueInt)
104 {
105     const int a = 1;
106     static_assert(
107         sdbusplus::message::details::can_append_multiple_v<decltype(a)>);
108     expect_basic<int>(SD_BUS_TYPE_INT32, a);
109     new_message().append(a);
110 }
111 
112 TEST_F(AppendTest, XValueInt)
113 {
114     int a = 1;
115     expect_basic<int>(SD_BUS_TYPE_INT32, a);
116     new_message().append(std::move(a));
117 }
118 
119 TEST_F(AppendTest, RValueBool)
120 {
121     expect_basic<int>(SD_BUS_TYPE_BOOLEAN, true);
122     new_message().append(true);
123 }
124 
125 TEST_F(AppendTest, LValueBool)
126 {
127     const bool a = false;
128     expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false);
129     new_message().append(a);
130 }
131 
132 TEST_F(AppendTest, XValueBool)
133 {
134     bool a = false;
135     expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false);
136     new_message().append(std::move(a));
137 }
138 
139 TEST_F(AppendTest, RValueDouble)
140 {
141     expect_basic<double>(SD_BUS_TYPE_DOUBLE, 1.1);
142     new_message().append(1.1);
143 }
144 
145 TEST_F(AppendTest, LValueDouble)
146 {
147     const double a = 1.1;
148     expect_basic<double>(SD_BUS_TYPE_DOUBLE, a);
149     new_message().append(a);
150 }
151 
152 TEST_F(AppendTest, XValueDouble)
153 {
154     double a = 1.1;
155     expect_basic<double>(SD_BUS_TYPE_DOUBLE, a);
156     new_message().append(std::move(a));
157 }
158 
159 TEST_F(AppendTest, RValueCString)
160 {
161     expect_basic_string(SD_BUS_TYPE_STRING, "asdf");
162     new_message().append("asdf");
163 }
164 
165 TEST_F(AppendTest, LValueCString)
166 {
167     const char* const s = "asdf";
168     expect_basic_string(SD_BUS_TYPE_STRING, s);
169     new_message().append(s);
170 }
171 
172 TEST_F(AppendTest, XValueCString)
173 {
174     const char* s = "asdf";
175     static_assert(
176         sdbusplus::message::details::can_append_multiple_v<decltype(s)>);
177     expect_basic_string(SD_BUS_TYPE_STRING, s);
178     new_message().append(std::move(s));
179 }
180 
181 TEST_F(AppendTest, RValueString)
182 {
183     expect_basic_string(SD_BUS_TYPE_STRING, "asdf");
184     new_message().append(std::string{"asdf"});
185 }
186 
187 TEST_F(AppendTest, LValueString)
188 {
189     std::string s{"asdf"};
190     static_assert(
191         !sdbusplus::message::details::can_append_multiple_v<decltype(s)>);
192     expect_basic_string(SD_BUS_TYPE_STRING, s.c_str());
193     new_message().append(s);
194 }
195 
196 TEST_F(AppendTest, XValueString)
197 {
198     std::string s{"asdf"};
199     expect_basic_string(SD_BUS_TYPE_STRING, s.c_str());
200     new_message().append(std::move(s));
201 }
202 
203 TEST_F(AppendTest, LValueStringView)
204 {
205     std::string_view s{"asdf"};
206     expect_basic_string_iovec(s.data(), s.size());
207     new_message().append(s);
208 }
209 
210 TEST_F(AppendTest, RValueStringView)
211 {
212     std::string_view s{"asdf"};
213     expect_basic_string_iovec(s.data(), s.size());
214     new_message().append(std::string_view{"asdf"});
215 }
216 
217 TEST_F(AppendTest, ObjectPath)
218 {
219     sdbusplus::message::object_path o{"/asdf"};
220     expect_basic_string(SD_BUS_TYPE_OBJECT_PATH, o.str.c_str());
221     new_message().append(o);
222 }
223 
224 TEST_F(AppendTest, Signature)
225 {
226     sdbusplus::message::signature g{"ii"};
227     expect_basic_string(SD_BUS_TYPE_SIGNATURE, g.str.c_str());
228     new_message().append(g);
229 }
230 
231 TEST_F(AppendTest, CombinedBasic)
232 {
233     const int c = 3;
234     const std::string s1{"fdsa"};
235     const char* const s2 = "asdf";
236 
237     {
238         testing::InSequence seq;
239         expect_basic<int>(SD_BUS_TYPE_INT32, 1);
240         expect_basic<double>(SD_BUS_TYPE_DOUBLE, 2.2);
241         expect_basic<int>(SD_BUS_TYPE_INT32, c);
242         expect_basic_string(SD_BUS_TYPE_STRING, s1.c_str());
243         expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false);
244         expect_basic_string(SD_BUS_TYPE_STRING, s2);
245     }
246     new_message().append(1, 2.2, c, s1, false, s2);
247 }
248 
249 TEST_F(AppendTest, Array)
250 {
251     const std::array<double, 4> a{1.1, 2.2, 3.3, 4.4};
252     static_assert(
253         !sdbusplus::message::details::can_append_multiple_v<decltype(a)>);
254 
255     {
256         testing::InSequence seq;
257         expect_open_container(SD_BUS_TYPE_ARRAY, "d");
258         for (const auto& i : a)
259         {
260             expect_basic<double>(SD_BUS_TYPE_DOUBLE, i);
261         }
262         expect_close_container();
263     }
264     new_message().append(a);
265 }
266 
267 TEST_F(AppendTest, Span)
268 {
269     const std::array<double, 4> a{1.1, 2.2, 3.3, 4.4};
270     auto s = std::span{a};
271     static_assert(
272         !sdbusplus::message::details::can_append_multiple_v<decltype(s)>);
273 
274     {
275         testing::InSequence seq;
276         expect_open_container(SD_BUS_TYPE_ARRAY, "d");
277         for (const auto& i : s)
278         {
279             expect_basic<double>(SD_BUS_TYPE_DOUBLE, i);
280         }
281         expect_close_container();
282     }
283     new_message().append(s);
284 }
285 
286 TEST_F(AppendTest, Vector)
287 {
288     const std::vector<int> v{1, 2, 3, 4};
289 
290     {
291         testing::InSequence seq;
292         expect_open_container(SD_BUS_TYPE_ARRAY, "i");
293         for (const auto& i : v)
294         {
295             expect_basic<int>(SD_BUS_TYPE_INT32, i);
296         }
297         expect_close_container();
298     }
299     new_message().append(v);
300 }
301 
302 TEST_F(AppendTest, Set)
303 {
304     const std::set<std::string> s{"one", "two", "eight"};
305 
306     {
307         testing::InSequence seq;
308         expect_open_container(SD_BUS_TYPE_ARRAY, "s");
309         for (const auto& i : s)
310         {
311             expect_basic_string(SD_BUS_TYPE_STRING, i.c_str());
312         }
313         expect_close_container();
314     }
315     new_message().append(s);
316 }
317 
318 TEST_F(AppendTest, UnorderedSet)
319 {
320     const std::unordered_set<std::string> s{"one", "two", "eight"};
321 
322     {
323         testing::InSequence seq;
324         expect_open_container(SD_BUS_TYPE_ARRAY, "s");
325         for (const auto& i : s)
326         {
327             expect_basic_string(SD_BUS_TYPE_STRING, i.c_str());
328         }
329         expect_close_container();
330     }
331     new_message().append(s);
332 }
333 
334 TEST_F(AppendTest, Map)
335 {
336     const std::map<int, std::string> m{
337         {1, "a"},
338         {2, "bc"},
339         {3, "def"},
340         {4, "ghij"},
341     };
342 
343     {
344         testing::InSequence seq;
345         expect_open_container(SD_BUS_TYPE_ARRAY, "{is}");
346         for (const auto& i : m)
347         {
348             expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "is");
349             expect_basic<int>(SD_BUS_TYPE_INT32, i.first);
350             expect_basic_string(SD_BUS_TYPE_STRING, i.second.c_str());
351             expect_close_container();
352         }
353         expect_close_container();
354     }
355     new_message().append(m);
356 }
357 
358 TEST_F(AppendTest, UnorderedMap)
359 {
360     const std::unordered_map<int, bool> m{
361         {1, false},
362         {2, true},
363         {3, true},
364         {4, false},
365     };
366 
367     {
368         testing::InSequence seq;
369         expect_open_container(SD_BUS_TYPE_ARRAY, "{ib}");
370         for (const auto& i : m)
371         {
372             expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "ib");
373             expect_basic<int>(SD_BUS_TYPE_INT32, i.first);
374             expect_basic<int>(SD_BUS_TYPE_BOOLEAN, i.second);
375             expect_close_container();
376         }
377         expect_close_container();
378     }
379     new_message().append(m);
380 }
381 
382 TEST_F(AppendTest, Tuple)
383 {
384     const std::tuple<int, std::string, bool> t{5, "asdf", false};
385 
386     {
387         testing::InSequence seq;
388         expect_open_container(SD_BUS_TYPE_STRUCT, "isb");
389         expect_basic<int>(SD_BUS_TYPE_INT32, std::get<0>(t));
390         expect_basic_string(SD_BUS_TYPE_STRING, std::get<1>(t).c_str());
391         expect_basic<int>(SD_BUS_TYPE_BOOLEAN, std::get<2>(t));
392         expect_close_container();
393     }
394     new_message().append(t);
395 }
396 
397 TEST_F(AppendTest, Variant)
398 {
399     const bool b1 = false;
400     const std::string s2{"asdf"};
401     const std::variant<int, std::string, bool> v1{b1}, v2{s2};
402 
403     {
404         testing::InSequence seq;
405         expect_open_container(SD_BUS_TYPE_VARIANT, "b");
406         expect_basic<int>(SD_BUS_TYPE_BOOLEAN, b1);
407         expect_close_container();
408         expect_open_container(SD_BUS_TYPE_VARIANT, "s");
409         expect_basic_string(SD_BUS_TYPE_STRING, s2.c_str());
410         expect_close_container();
411     }
412     new_message().append(v1, v2);
413 }
414 
415 TEST_F(AppendTest, LargeCombo)
416 {
417     std::vector<std::array<std::string, 3>> vas{{"a", "b", "c"},
418                                                 {"d", "", "e"}};
419     std::map<std::string, std::variant<int, double>> msv = {
420         {"a", 3.3}, {"b", 1}, {"c", 4.4}};
421 
422     {
423         testing::InSequence seq;
424 
425         expect_open_container(SD_BUS_TYPE_ARRAY, "as");
426         for (const auto& as : vas)
427         {
428             expect_open_container(SD_BUS_TYPE_ARRAY, "s");
429             for (const auto& s : as)
430             {
431                 expect_basic_string(SD_BUS_TYPE_STRING, s.c_str());
432             }
433             expect_close_container();
434         }
435         expect_close_container();
436 
437         expect_open_container(SD_BUS_TYPE_ARRAY, "{sv}");
438         for (const auto& sv : msv)
439         {
440             expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "sv");
441             expect_basic_string(SD_BUS_TYPE_STRING, sv.first.c_str());
442             if (std::holds_alternative<int>(sv.second))
443             {
444                 expect_open_container(SD_BUS_TYPE_VARIANT, "i");
445                 expect_basic<int>(SD_BUS_TYPE_INT32, std::get<int>(sv.second));
446                 expect_close_container();
447             }
448             else
449             {
450                 expect_open_container(SD_BUS_TYPE_VARIANT, "d");
451                 expect_basic<double>(SD_BUS_TYPE_DOUBLE,
452                                      std::get<double>(sv.second));
453                 expect_close_container();
454             }
455             expect_close_container();
456         }
457         expect_close_container();
458     }
459     new_message().append(vas, msv);
460 }
461 
462 } // namespace
463