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 static int on_array_append(sd_bus_message*, char, const void*, size_t) 95 { 96 return 0; 97 } 98 99 void expect_append_array(char type, size_t sz) 100 { 101 EXPECT_CALL(mock, 102 sd_bus_message_append_array(nullptr, type, testing::_, sz)) 103 .WillOnce(testing::Invoke(on_array_append)); 104 } 105 }; 106 107 TEST_F(AppendTest, RValueInt) 108 { 109 expect_basic<int>(SD_BUS_TYPE_INT32, 1); 110 new_message().append(1); 111 } 112 113 TEST_F(AppendTest, LValueInt) 114 { 115 const int a = 1; 116 expect_basic<int>(SD_BUS_TYPE_INT32, a); 117 new_message().append(a); 118 } 119 120 TEST_F(AppendTest, XValueInt) 121 { 122 int a = 1; 123 expect_basic<int>(SD_BUS_TYPE_INT32, a); 124 new_message().append(std::move(a)); 125 } 126 127 TEST_F(AppendTest, RValueBool) 128 { 129 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, true); 130 new_message().append(true); 131 } 132 133 TEST_F(AppendTest, LValueBool) 134 { 135 const bool a = false; 136 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false); 137 new_message().append(a); 138 } 139 140 TEST_F(AppendTest, XValueBool) 141 { 142 bool a = false; 143 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false); 144 new_message().append(std::move(a)); 145 } 146 147 TEST_F(AppendTest, RValueDouble) 148 { 149 expect_basic<double>(SD_BUS_TYPE_DOUBLE, 1.1); 150 new_message().append(1.1); 151 } 152 153 TEST_F(AppendTest, LValueDouble) 154 { 155 const double a = 1.1; 156 expect_basic<double>(SD_BUS_TYPE_DOUBLE, a); 157 new_message().append(a); 158 } 159 160 TEST_F(AppendTest, XValueDouble) 161 { 162 double a = 1.1; 163 expect_basic<double>(SD_BUS_TYPE_DOUBLE, a); 164 new_message().append(std::move(a)); 165 } 166 167 TEST_F(AppendTest, RValueCString) 168 { 169 expect_basic_string(SD_BUS_TYPE_STRING, "asdf"); 170 new_message().append("asdf"); 171 } 172 173 TEST_F(AppendTest, LValueCString) 174 { 175 const char* const s = "asdf"; 176 expect_basic_string(SD_BUS_TYPE_STRING, s); 177 new_message().append(s); 178 } 179 180 TEST_F(AppendTest, XValueCString) 181 { 182 const char* s = "asdf"; 183 expect_basic_string(SD_BUS_TYPE_STRING, s); 184 new_message().append(std::move(s)); 185 } 186 187 TEST_F(AppendTest, RValueString) 188 { 189 expect_basic_string(SD_BUS_TYPE_STRING, "asdf"); 190 new_message().append(std::string{"asdf"}); 191 } 192 193 TEST_F(AppendTest, LValueString) 194 { 195 std::string s{"asdf"}; 196 expect_basic_string(SD_BUS_TYPE_STRING, s.c_str()); 197 new_message().append(s); 198 } 199 200 TEST_F(AppendTest, XValueString) 201 { 202 std::string s{"asdf"}; 203 expect_basic_string(SD_BUS_TYPE_STRING, s.c_str()); 204 new_message().append(std::move(s)); 205 } 206 207 TEST_F(AppendTest, LValueStringView) 208 { 209 std::string_view s{"asdf"}; 210 expect_basic_string_iovec(s.data(), s.size()); 211 new_message().append(s); 212 } 213 214 TEST_F(AppendTest, RValueStringView) 215 { 216 std::string_view s{"asdf"}; 217 expect_basic_string_iovec(s.data(), s.size()); 218 new_message().append(std::string_view{"asdf"}); 219 } 220 221 TEST_F(AppendTest, ObjectPath) 222 { 223 sdbusplus::message::object_path o{"/asdf"}; 224 expect_basic_string(SD_BUS_TYPE_OBJECT_PATH, o.str.c_str()); 225 new_message().append(o); 226 } 227 228 TEST_F(AppendTest, Signature) 229 { 230 sdbusplus::message::signature g{"ii"}; 231 expect_basic_string(SD_BUS_TYPE_SIGNATURE, g.str.c_str()); 232 new_message().append(g); 233 } 234 235 TEST_F(AppendTest, CombinedBasic) 236 { 237 const int c = 3; 238 const std::string s1{"fdsa"}; 239 const char* const s2 = "asdf"; 240 241 { 242 testing::InSequence seq; 243 expect_basic<int>(SD_BUS_TYPE_INT32, 1); 244 expect_basic<double>(SD_BUS_TYPE_DOUBLE, 2.2); 245 expect_basic<int>(SD_BUS_TYPE_INT32, c); 246 expect_basic_string(SD_BUS_TYPE_STRING, s1.c_str()); 247 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false); 248 expect_basic_string(SD_BUS_TYPE_STRING, s2); 249 } 250 new_message().append(1, 2.2, c, s1, false, s2); 251 } 252 253 TEST_F(AppendTest, Array) 254 { 255 const std::array<double, 4> a{1.1, 2.2, 3.3, 4.4}; 256 257 { 258 expect_append_array(SD_BUS_TYPE_DOUBLE, a.size() * sizeof(double)); 259 } 260 new_message().append(a); 261 } 262 263 TEST_F(AppendTest, Span) 264 { 265 const std::array<double, 4> a{1.1, 2.2, 3.3, 4.4}; 266 auto s = std::span{a}; 267 268 { 269 expect_append_array(SD_BUS_TYPE_DOUBLE, a.size() * sizeof(double)); 270 } 271 new_message().append(s); 272 } 273 274 TEST_F(AppendTest, Vector) 275 { 276 const std::vector<std::string> v{"a", "b", "c", "d"}; 277 278 { 279 testing::InSequence seq; 280 expect_open_container(SD_BUS_TYPE_ARRAY, "s"); 281 for (const auto& i : v) 282 { 283 expect_basic_string(SD_BUS_TYPE_STRING, i.c_str()); 284 } 285 expect_close_container(); 286 } 287 new_message().append(v); 288 } 289 290 TEST_F(AppendTest, VectorIntegral) 291 { 292 const std::vector<int32_t> v{1, 2, 3, 4}; 293 expect_append_array(SD_BUS_TYPE_INT32, v.size() * sizeof(int32_t)); 294 new_message().append(v); 295 } 296 297 TEST_F(AppendTest, VectorBoolean) 298 { 299 const std::vector<bool> v{false, true, false, true}; 300 { 301 testing::InSequence seq; 302 expect_open_container(SD_BUS_TYPE_ARRAY, "b"); 303 for (const auto& i : v) 304 { 305 expect_basic<bool>(SD_BUS_TYPE_BOOLEAN, (int)i); 306 } 307 expect_close_container(); 308 } 309 new_message().append(v); 310 } 311 312 TEST_F(AppendTest, VectorNestIntegral) 313 { 314 const std::vector<std::array<int32_t, 3>> v{ 315 {1, 2, 3}, {3, 4, 5}, {6, 7, 8}}; 316 317 { 318 testing::InSequence seq; 319 expect_open_container(SD_BUS_TYPE_ARRAY, "ai"); 320 for (long unsigned int i = 0; i < v.size(); i++) 321 { 322 expect_append_array(SD_BUS_TYPE_INT32, 323 v[i].size() * sizeof(int32_t)); 324 } 325 expect_close_container(); 326 } 327 new_message().append(v); 328 } 329 330 TEST_F(AppendTest, Set) 331 { 332 const std::set<std::string> s{"one", "two", "eight"}; 333 334 { 335 testing::InSequence seq; 336 expect_open_container(SD_BUS_TYPE_ARRAY, "s"); 337 for (const auto& i : s) 338 { 339 expect_basic_string(SD_BUS_TYPE_STRING, i.c_str()); 340 } 341 expect_close_container(); 342 } 343 new_message().append(s); 344 } 345 346 TEST_F(AppendTest, UnorderedSet) 347 { 348 const std::unordered_set<std::string> s{"one", "two", "eight"}; 349 350 { 351 testing::InSequence seq; 352 expect_open_container(SD_BUS_TYPE_ARRAY, "s"); 353 for (const auto& i : s) 354 { 355 expect_basic_string(SD_BUS_TYPE_STRING, i.c_str()); 356 } 357 expect_close_container(); 358 } 359 new_message().append(s); 360 } 361 362 TEST_F(AppendTest, Map) 363 { 364 const std::map<int, std::string> m{ 365 {1, "a"}, 366 {2, "bc"}, 367 {3, "def"}, 368 {4, "ghij"}, 369 }; 370 371 { 372 testing::InSequence seq; 373 expect_open_container(SD_BUS_TYPE_ARRAY, "{is}"); 374 for (const auto& i : m) 375 { 376 expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "is"); 377 expect_basic<int>(SD_BUS_TYPE_INT32, i.first); 378 expect_basic_string(SD_BUS_TYPE_STRING, i.second.c_str()); 379 expect_close_container(); 380 } 381 expect_close_container(); 382 } 383 new_message().append(m); 384 } 385 386 TEST_F(AppendTest, UnorderedMap) 387 { 388 const std::unordered_map<int, bool> m{ 389 {1, false}, 390 {2, true}, 391 {3, true}, 392 {4, false}, 393 }; 394 395 { 396 testing::InSequence seq; 397 expect_open_container(SD_BUS_TYPE_ARRAY, "{ib}"); 398 for (const auto& i : m) 399 { 400 expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "ib"); 401 expect_basic<int>(SD_BUS_TYPE_INT32, i.first); 402 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, i.second); 403 expect_close_container(); 404 } 405 expect_close_container(); 406 } 407 new_message().append(m); 408 } 409 410 TEST_F(AppendTest, Tuple) 411 { 412 const std::tuple<int, std::string, bool> t{5, "asdf", false}; 413 414 { 415 testing::InSequence seq; 416 expect_open_container(SD_BUS_TYPE_STRUCT, "isb"); 417 expect_basic<int>(SD_BUS_TYPE_INT32, std::get<0>(t)); 418 expect_basic_string(SD_BUS_TYPE_STRING, std::get<1>(t).c_str()); 419 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, std::get<2>(t)); 420 expect_close_container(); 421 } 422 new_message().append(t); 423 } 424 425 TEST_F(AppendTest, Variant) 426 { 427 const bool b1 = false; 428 const std::string s2{"asdf"}; 429 const std::variant<int, std::string, bool> v1{b1}, v2{s2}; 430 431 { 432 testing::InSequence seq; 433 expect_open_container(SD_BUS_TYPE_VARIANT, "b"); 434 expect_basic<int>(SD_BUS_TYPE_BOOLEAN, b1); 435 expect_close_container(); 436 expect_open_container(SD_BUS_TYPE_VARIANT, "s"); 437 expect_basic_string(SD_BUS_TYPE_STRING, s2.c_str()); 438 expect_close_container(); 439 } 440 new_message().append(v1, v2); 441 } 442 443 TEST_F(AppendTest, LargeCombo) 444 { 445 std::vector<std::array<std::string, 3>> vas{{"a", "b", "c"}, 446 {"d", "", "e"}}; 447 std::map<std::string, std::variant<int, double>> msv = { 448 {"a", 3.3}, {"b", 1}, {"c", 4.4}}; 449 450 { 451 testing::InSequence seq; 452 453 expect_open_container(SD_BUS_TYPE_ARRAY, "as"); 454 for (const auto& as : vas) 455 { 456 expect_open_container(SD_BUS_TYPE_ARRAY, "s"); 457 for (const auto& s : as) 458 { 459 expect_basic_string(SD_BUS_TYPE_STRING, s.c_str()); 460 } 461 expect_close_container(); 462 } 463 expect_close_container(); 464 465 expect_open_container(SD_BUS_TYPE_ARRAY, "{sv}"); 466 for (const auto& sv : msv) 467 { 468 expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "sv"); 469 expect_basic_string(SD_BUS_TYPE_STRING, sv.first.c_str()); 470 if (std::holds_alternative<int>(sv.second)) 471 { 472 expect_open_container(SD_BUS_TYPE_VARIANT, "i"); 473 expect_basic<int>(SD_BUS_TYPE_INT32, std::get<int>(sv.second)); 474 expect_close_container(); 475 } 476 else 477 { 478 expect_open_container(SD_BUS_TYPE_VARIANT, "d"); 479 expect_basic<double>(SD_BUS_TYPE_DOUBLE, 480 std::get<double>(sv.second)); 481 expect_close_container(); 482 } 483 expect_close_container(); 484 } 485 expect_close_container(); 486 } 487 new_message().append(vas, msv); 488 } 489 490 } // namespace 491