1 #include "src/handler.hpp" 2 3 #include "src/types.hpp" 4 5 #include <xyz/openbmc_project/Common/error.hpp> 6 7 #include <span> 8 #include <utility> 9 #include <vector> 10 11 #include <gmock/gmock.h> 12 #include <gtest/gtest.h> 13 14 using ::testing::ElementsAre; 15 16 class TestHandler : public testing::Test 17 { 18 protected: 19 InterfaceMapType interfaceMap = { 20 { 21 "/test/object_path_0", 22 {{"test_object_connection_0", {"test_interface_0"}}}, 23 }, 24 { 25 "/test/object_path_0/child", 26 {{"test_object_connection_1", {"test_interface_1"}}}, 27 }, 28 { 29 "/test/object_path_0/child/grandchild", 30 {{"test_object_connection_2", {"test_interface_2"}}}, 31 }, 32 { 33 "/test/object_path_0/child/grandchild/dog", 34 {{"test_object_connection_3", {"test_interface_3"}}}, 35 }, 36 { 37 "/test/object_path_0/child1", 38 {{"test_object_connection_4", {"test_interface_4"}}}, 39 }, 40 { 41 "/test/object_path_0/grandchild/child1", 42 {{"test_object_connection_5", {"test_interface_5"}}}, 43 }, 44 }; 45 46 AssociationMaps associationMap = { 47 .ifaces = 48 { 49 { 50 "/test/object_path_0/descendent", 51 { 52 std::shared_ptr<sdbusplus::asio::dbus_interface>(), 53 { 54 "/test/object_path_0/child", 55 "/test/object_path_0/child/grandchild", 56 }, 57 }, 58 }, 59 { 60 "/test/object_path_0/child/descendent", 61 { 62 std::shared_ptr<sdbusplus::asio::dbus_interface>(), 63 { 64 "/test/object_path_0/child/grandchild", 65 }, 66 }, 67 }, 68 { 69 "/test/object_path_0/grandchild/child1/descendent", 70 { 71 std::shared_ptr<sdbusplus::asio::dbus_interface>(), 72 { 73 "/test/object_path_0/child", 74 }, 75 }, 76 }, 77 { 78 "/test/object_path_0/child1/descendent", 79 { 80 std::shared_ptr<sdbusplus::asio::dbus_interface>(), 81 { 82 "/test/object_path_0/child1", 83 "/test/object_path_0/child1/grandchild", 84 }, 85 }, 86 }, 87 }, 88 .owners = {}, 89 .pending = {}, 90 }; 91 }; 92 93 TEST_F(TestHandler, AddObjectMapResult) 94 { 95 std::vector<InterfaceMapType::value_type> interfaceMaps; 96 addObjectMapResult(interfaceMaps, "test_object_path", 97 std::pair<std::string, InterfaceNames>( 98 "test_object_connection_0", { 99 "test_interface_0", 100 "test_interface_1", 101 })); 102 103 addObjectMapResult(interfaceMaps, "test_object_path", 104 std::pair<std::string, InterfaceNames>( 105 "test_object_connection_1", { 106 "test_interface_0", 107 "test_interface_1", 108 })); 109 ASSERT_EQ(interfaceMaps.size(), 1); 110 auto entry = std::find_if( 111 interfaceMaps.begin(), interfaceMaps.end(), 112 [](const auto& i) { return "test_object_path" == i.first; }); 113 ASSERT_NE(entry, interfaceMap.end()); 114 for (const auto& [_, interfaces] : entry->second) 115 { 116 ASSERT_THAT(interfaces, 117 ElementsAre("test_interface_0", "test_interface_1")); 118 } 119 120 // Change the interface, but expect it to be unchanged 121 addObjectMapResult(interfaceMaps, "test_object_path", 122 std::pair<std::string, InterfaceNames>( 123 "test_object_connection_0", {"test_interface_2"})); 124 addObjectMapResult(interfaceMaps, "test_object_path", 125 std::pair<std::string, InterfaceNames>( 126 "test_object_connection_1", {"test_interface_2"})); 127 entry = std::find_if( 128 interfaceMaps.begin(), interfaceMaps.end(), 129 [](const auto& i) { return "test_object_path" == i.first; }); 130 ASSERT_NE(entry, interfaceMaps.end()); 131 for (const auto& [_, interfaces] : entry->second) 132 { 133 ASSERT_THAT(interfaces, 134 ElementsAre("test_interface_0", "test_interface_1")); 135 } 136 } 137 138 TEST_F(TestHandler, getAncestorsBad) 139 { 140 std::string path = "/test/object_path_0/child/grandchild"; 141 std::vector<std::string> interfaces = {"bad_interface"}; 142 std::vector<InterfaceMapType::value_type> ancestors = 143 getAncestors(interfaceMap, path, interfaces); 144 ASSERT_TRUE(ancestors.empty()); 145 146 path = "/invalid_path"; 147 EXPECT_THROW( 148 getAncestors(interfaceMap, path, interfaces), 149 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 150 } 151 152 TEST_F(TestHandler, getAncestorsGood) 153 { 154 std::string path = "/test/object_path_0/child/grandchild"; 155 std::vector<std::string> interfaces = {"test_interface_0", 156 "test_interface_1"}; 157 std::vector<InterfaceMapType::value_type> ancestors = 158 getAncestors(interfaceMap, path, interfaces); 159 ASSERT_EQ(ancestors.size(), 2); 160 161 // Grand Parent 162 EXPECT_EQ(ancestors[0].first, "/test/object_path_0"); 163 ASSERT_EQ(ancestors[0].second.size(), 1); 164 auto grandParent = ancestors[0].second.find("test_object_connection_0"); 165 ASSERT_NE(grandParent, ancestors[0].second.end()); 166 ASSERT_THAT(grandParent->second, ElementsAre("test_interface_0")); 167 168 // Parent 169 ASSERT_EQ(ancestors[1].first, "/test/object_path_0/child"); 170 ASSERT_EQ(ancestors[1].second.size(), 1); 171 auto parent = ancestors[1].second.find("test_object_connection_1"); 172 ASSERT_NE(parent, ancestors[1].second.end()); 173 ASSERT_THAT(parent->second, ElementsAre("test_interface_1")); 174 } 175 176 TEST_F(TestHandler, getObjectBad) 177 { 178 std::string path = "/test/object_path_0"; 179 std::vector<std::string> interfaces = {"bad_interface"}; 180 EXPECT_THROW( 181 getObject(interfaceMap, path, interfaces), 182 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 183 184 path = "/invalid_path"; 185 EXPECT_THROW( 186 getObject(interfaceMap, path, interfaces), 187 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 188 189 path = "/"; 190 EXPECT_THROW( 191 getObject(interfaceMap, path, interfaces), 192 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 193 } 194 195 TEST_F(TestHandler, getObjectGood) 196 { 197 std::string path = "/test/object_path_0"; 198 std::vector<std::string> interfaces = {"test_interface_0", 199 "test_interface_1"}; 200 ConnectionNames connection = getObject(interfaceMap, path, interfaces); 201 auto object = connection.find("test_object_connection_0"); 202 ASSERT_NE(object, connection.end()); 203 ASSERT_THAT(object->second, ElementsAre("test_interface_0")); 204 205 path = "/test/object_path_0/child"; 206 connection = getObject(interfaceMap, path, interfaces); 207 object = connection.find("test_object_connection_1"); 208 ASSERT_NE(object, connection.end()); 209 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 210 } 211 212 TEST_F(TestHandler, getSubTreeBad) 213 { 214 std::string path = "/test/object_path_0"; 215 std::vector<std::string> interfaces = {"bad_interface"}; 216 std::vector<InterfaceMapType::value_type> subtree = 217 getSubTree(interfaceMap, path, 0, interfaces); 218 ASSERT_TRUE(subtree.empty()); 219 220 path = "/invalid_path"; 221 EXPECT_THROW( 222 getSubTree(interfaceMap, path, 0, interfaces), 223 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 224 } 225 226 void verifySubtree(std::span<InterfaceMapType::value_type> subtree) 227 { 228 ASSERT_EQ(subtree.size(), 2); 229 ConnectionNames connection = subtree[0].second; 230 auto object = connection.find("test_object_connection_1"); 231 ASSERT_NE(object, connection.end()); 232 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 233 234 connection = subtree[1].second; 235 object = connection.find("test_object_connection_3"); 236 ASSERT_NE(object, connection.end()); 237 ASSERT_THAT(object->second, ElementsAre("test_interface_3")); 238 } 239 240 TEST_F(TestHandler, getSubTreeGood) 241 { 242 std::string path0 = "/test/object_path_0"; 243 std::string path1 = "/test/object_path_0/child/grandchild"; 244 std::vector<std::string> interfaces = {"test_interface_1", 245 "test_interface_3"}; 246 // Root 247 std::vector<InterfaceMapType::value_type> subtree = 248 getSubTree(interfaceMap, "/", 0, interfaces); 249 verifySubtree(subtree); 250 251 // Path0 252 subtree = getSubTree(interfaceMap, path0, 0, interfaces); 253 verifySubtree(subtree); 254 255 // Path0 with Depth path of 1 256 subtree = getSubTree(interfaceMap, path0, 1, interfaces); 257 ASSERT_EQ(subtree.size(), 1); 258 ConnectionNames connection = subtree[0].second; 259 auto object = connection.find("test_object_connection_1"); 260 ASSERT_NE(object, connection.end()); 261 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 262 263 // Path1 264 subtree = getSubTree(interfaceMap, path1, 0, interfaces); 265 ASSERT_EQ(subtree.size(), 1); 266 connection = subtree[0].second; 267 object = connection.find("test_object_connection_3"); 268 ASSERT_NE(object, connection.end()); 269 ASSERT_THAT(object->second, ElementsAre("test_interface_3")); 270 } 271 272 TEST_F(TestHandler, getSubTreePathsBad) 273 { 274 std::string path = "/test/object_path_0"; 275 std::vector<std::string> interfaces = {"bad_interface"}; 276 std::vector<std::string> subtreePath = 277 getSubTreePaths(interfaceMap, path, 0, interfaces); 278 ASSERT_TRUE(subtreePath.empty()); 279 280 path = "/invalid_path"; 281 EXPECT_THROW( 282 getSubTreePaths(interfaceMap, path, 0, interfaces), 283 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 284 } 285 286 TEST_F(TestHandler, getSubTreePathsGood) 287 { 288 std::string path0 = "/test/object_path_0"; 289 std::string path1 = "/test/object_path_0/child/grandchild"; 290 std::vector<std::string> interfaces = {"test_interface_1", 291 "test_interface_3"}; 292 // Root 293 std::vector<std::string> subtreePath = 294 getSubTreePaths(interfaceMap, "/", 0, interfaces); 295 ASSERT_THAT(subtreePath, 296 ElementsAre("/test/object_path_0/child", 297 "/test/object_path_0/child/grandchild/dog")); 298 299 // Path0 300 subtreePath = getSubTreePaths(interfaceMap, path0, 0, interfaces); 301 ASSERT_THAT(subtreePath, 302 ElementsAre("/test/object_path_0/child", 303 "/test/object_path_0/child/grandchild/dog")); 304 305 // Path0 + Depth path of 1 306 subtreePath = getSubTreePaths(interfaceMap, path0, 1, interfaces); 307 ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child")); 308 309 // Path1 310 subtreePath = getSubTreePaths(interfaceMap, path1, 0, interfaces); 311 ASSERT_THAT(subtreePath, 312 ElementsAre("/test/object_path_0/child/grandchild/dog")); 313 } 314 315 TEST_F(TestHandler, getAssociatedSubTreeBad) 316 { 317 sdbusplus::message::object_path path("/test/object_path_0"); 318 sdbusplus::message::object_path validAssociatedPath = path / "descendent"; 319 std::vector<std::string> invalidInterfaces = {"test_interface_3"}; 320 std::vector<std::string> validInterfaces = {"test_interface_1", 321 "test_interface_2"}; 322 // Associated path, but invalid interface 323 ASSERT_TRUE( 324 getAssociatedSubTree(interfaceMap, associationMap, validAssociatedPath, 325 path, 0, invalidInterfaces) 326 .empty()); 327 328 // Valid interface, not associated 329 ASSERT_TRUE(getAssociatedSubTree(interfaceMap, associationMap, path / "dog", 330 path, 0, validInterfaces) 331 .empty()); 332 333 // Invalid path, with valid association 334 path = sdbusplus::message::object_path("/invalid_path"); 335 EXPECT_THROW( 336 getAssociatedSubTree(interfaceMap, associationMap, validAssociatedPath, 337 path, 0, validInterfaces), 338 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 339 } 340 341 TEST_F(TestHandler, getAssociatedSubTreeGood) 342 { 343 sdbusplus::message::object_path path0("/test/object_path_0"); 344 sdbusplus::message::object_path path1("/test/object_path_0/child"); 345 sdbusplus::message::object_path associatedPath = path0 / "descendent"; 346 std::vector<std::string> interfaces = { 347 "test_interface_1", "test_interface_2", 348 // Not associated to path 349 "test_interface_3"}; 350 351 // Path0 352 std::vector<InterfaceMapType::value_type> subtree = getAssociatedSubTree( 353 interfaceMap, associationMap, associatedPath, path0, 0, interfaces); 354 ASSERT_EQ(subtree.size(), 2); 355 ConnectionNames connection = subtree[0].second; 356 auto object = connection.find("test_object_connection_1"); 357 ASSERT_NE(object, connection.end()); 358 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 359 360 connection = subtree[1].second; 361 object = connection.find("test_object_connection_2"); 362 ASSERT_NE(object, connection.end()); 363 ASSERT_THAT(object->second, ElementsAre("test_interface_2")); 364 365 // Path0 with Depth path of 1 366 subtree = getAssociatedSubTree(interfaceMap, associationMap, associatedPath, 367 path0, 1, interfaces); 368 ASSERT_EQ(subtree.size(), 1); 369 connection = subtree[0].second; 370 object = connection.find("test_object_connection_1"); 371 ASSERT_NE(object, connection.end()); 372 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 373 374 // Path1 375 subtree = getAssociatedSubTree(interfaceMap, associationMap, 376 path1 / "descendent", path1, 0, interfaces); 377 ASSERT_EQ(subtree.size(), 1); 378 connection = subtree[0].second; 379 object = connection.find("test_object_connection_2"); 380 ASSERT_NE(object, connection.end()); 381 ASSERT_THAT(object->second, ElementsAre("test_interface_2")); 382 } 383 384 TEST_F(TestHandler, getAssociatedSubTreePathsBad) 385 { 386 sdbusplus::message::object_path path("/test/object_path_0"); 387 sdbusplus::message::object_path validAssociatedPath = path / "descendent"; 388 std::vector<std::string> invalidInterfaces = {"test_interface_3"}; 389 std::vector<std::string> validInterfaces = {"test_interface_1", 390 "test_interface_2"}; 391 // Associated path, but invalid interface 392 ASSERT_TRUE(getAssociatedSubTreePaths(interfaceMap, associationMap, 393 validAssociatedPath, path, 0, 394 invalidInterfaces) 395 .empty()); 396 397 // Valid interface, not associated 398 ASSERT_TRUE( 399 getAssociatedSubTreePaths(interfaceMap, associationMap, path / "dog", 400 path, 0, validInterfaces) 401 .empty()); 402 403 // Invalid path, with valid association 404 path = sdbusplus::message::object_path("/invalid_path"); 405 EXPECT_THROW( 406 getAssociatedSubTreePaths(interfaceMap, associationMap, 407 validAssociatedPath, path, 0, 408 validInterfaces), 409 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 410 } 411 412 TEST_F(TestHandler, getAssociatedSubTreePathsGood) 413 { 414 sdbusplus::message::object_path path0("/test/object_path_0"); 415 sdbusplus::message::object_path path1("/test/object_path_0/child"); 416 sdbusplus::message::object_path associatedPath = path0 / "descendent"; 417 std::vector<std::string> interfaces = { 418 "test_interface_1", "test_interface_2", 419 // Not associated to path 420 "test_interface_3"}; 421 422 // Path0 423 std::vector<std::string> subtreePath = getAssociatedSubTreePaths( 424 interfaceMap, associationMap, associatedPath, path0, 0, interfaces); 425 ASSERT_THAT(subtreePath, 426 ElementsAre("/test/object_path_0/child", 427 "/test/object_path_0/child/grandchild")); 428 429 // Path0 with Depth path of 1 430 subtreePath = getAssociatedSubTreePaths( 431 interfaceMap, associationMap, associatedPath, path0, 1, interfaces); 432 ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child")); 433 434 // Path1 435 subtreePath = 436 getAssociatedSubTreePaths(interfaceMap, associationMap, 437 path1 / "descendent", path1, 0, interfaces); 438 ASSERT_THAT(subtreePath, 439 ElementsAre("/test/object_path_0/child/grandchild")); 440 } 441 442 TEST_F(TestHandler, getAssociatedSubTreeByIdBad) 443 { 444 sdbusplus::message::object_path path("/test/object_path_0"); 445 std::vector<std::string> subtreeInterfaces = {"test_interface_1", 446 "test_interface_3"}; 447 std::vector<std::string> badsubtreeInterfaces = {"bad_interface"}; 448 std::vector<std::string> endpointinvalidInterfaces = {"test_interface_3"}; 449 std::vector<std::string> endpointvalidInterfaces = {"test_interface_1", 450 "test_interface_2"}; 451 // invalid id 452 ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, "childx", 453 path, subtreeInterfaces, "descendent", 454 endpointvalidInterfaces) 455 .empty()); 456 457 // invalid subtreeInterfaces 458 ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, "child", 459 path, badsubtreeInterfaces, 460 "descendent", endpointvalidInterfaces) 461 .empty()); 462 463 // invalid endpointinterface 464 ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, "child", 465 path, subtreeInterfaces, "descendent", 466 endpointinvalidInterfaces) 467 .empty()); 468 // valid id, but doesn't have specified interface 469 ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, 470 "grandchild", path, subtreeInterfaces, 471 "descendent", endpointvalidInterfaces) 472 .empty()); 473 474 // invalid association 475 ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, "child", 476 path, subtreeInterfaces, "dog", 477 endpointinvalidInterfaces) 478 .empty()); 479 480 // Invalid path 481 path = sdbusplus::message::object_path("/invalid_path"); 482 EXPECT_THROW( 483 getAssociatedSubTreeById(interfaceMap, associationMap, "child", path, 484 subtreeInterfaces, "descendent", 485 endpointvalidInterfaces), 486 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 487 } 488 489 TEST_F(TestHandler, getAssociatedSubTreeByIdGood) 490 { 491 sdbusplus::message::object_path path0("/test/object_path_0"); 492 std::vector<std::string> interfaces = { 493 "test_interface_1", "test_interface_2", "test_interface_3"}; 494 495 // Path0 496 std::vector<InterfaceMapType::value_type> subtree = 497 getAssociatedSubTreeById(interfaceMap, associationMap, "child", path0, 498 interfaces, "descendent", interfaces); 499 ASSERT_EQ(subtree.size(), 1); 500 ConnectionNames connection = subtree[0].second; 501 auto object = connection.find("test_object_connection_2"); 502 ASSERT_NE(object, connection.end()); 503 ASSERT_THAT(object->second, ElementsAre("test_interface_2")); 504 505 std::vector<std::string> interfaces1 = { 506 "test_interface_1", "test_interface_4", "test_interface_5"}; 507 // Path0 with Depth path of 0 508 subtree = 509 getAssociatedSubTreeById(interfaceMap, associationMap, "child1", path0, 510 interfaces1, "descendent", interfaces1); 511 ASSERT_EQ(subtree.size(), 2); 512 } 513 514 TEST_F(TestHandler, getAssociatedSubTreePathsByIdBad) 515 { 516 sdbusplus::message::object_path path("/test/object_path_0"); 517 std::vector<std::string> subtreeInterfaces = {"test_interface_1", 518 "test_interface_3"}; 519 std::vector<std::string> badsubtreeInterfaces = {"bad_interface"}; 520 std::vector<std::string> endpointinvalidInterfaces = {"test_interface_3"}; 521 std::vector<std::string> endpointvalidInterfaces = {"test_interface_1", 522 "test_interface_2"}; 523 // invalid id 524 ASSERT_TRUE(getAssociatedSubTreePathsById( 525 interfaceMap, associationMap, "childx", path, 526 subtreeInterfaces, "descendent", endpointvalidInterfaces) 527 .empty()); 528 529 // invalid subtreeInterfaces 530 ASSERT_TRUE(getAssociatedSubTreePathsById( 531 interfaceMap, associationMap, "child", path, 532 badsubtreeInterfaces, "descendent", endpointvalidInterfaces) 533 .empty()); 534 535 // invalid endpointinterface 536 ASSERT_TRUE(getAssociatedSubTreePathsById( 537 interfaceMap, associationMap, "child", path, 538 subtreeInterfaces, "descendent", endpointinvalidInterfaces) 539 .empty()); 540 // valid id, but doesn't have specified interface 541 ASSERT_TRUE(getAssociatedSubTreePathsById( 542 interfaceMap, associationMap, "grandchild", path, 543 subtreeInterfaces, "descendent", endpointvalidInterfaces) 544 .empty()); 545 546 // invalid association 547 ASSERT_TRUE(getAssociatedSubTreePathsById(interfaceMap, associationMap, 548 "child", path, subtreeInterfaces, 549 "dog", endpointinvalidInterfaces) 550 .empty()); 551 552 // Invalid path 553 path = sdbusplus::message::object_path("/invalid_path"); 554 EXPECT_THROW( 555 getAssociatedSubTreePathsById(interfaceMap, associationMap, "child", 556 path, subtreeInterfaces, "descendent", 557 endpointvalidInterfaces), 558 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 559 } 560 561 TEST_F(TestHandler, getAssociatedSubTreePathsByIdGood) 562 { 563 sdbusplus::message::object_path path0("/test/object_path_0"); 564 std::vector<std::string> interfaces = { 565 "test_interface_1", "test_interface_2", "test_interface_3"}; 566 567 // Path0 568 std::vector<std::string> subtreePath = getAssociatedSubTreePathsById( 569 interfaceMap, associationMap, "child", path0, interfaces, "descendent", 570 interfaces); 571 ASSERT_THAT(subtreePath, 572 ElementsAre("/test/object_path_0/child/grandchild")); 573 574 std::vector<std::string> interfaces1 = { 575 "test_interface_1", "test_interface_4", "test_interface_5"}; 576 // Path0 with Depth path of 0 577 subtreePath = getAssociatedSubTreePathsById( 578 interfaceMap, associationMap, "child1", path0, interfaces1, 579 "descendent", interfaces1); 580 ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child1", 581 "/test/object_path_0/child")); 582 } 583