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 AssociationMaps associationMap = { 38 .ifaces = 39 { 40 { 41 "/test/object_path_0/descendent", 42 { 43 std::shared_ptr<sdbusplus::asio::dbus_interface>(), 44 { 45 "/test/object_path_0/child", 46 "/test/object_path_0/child/grandchild", 47 }, 48 }, 49 }, 50 { 51 "/test/object_path_0/child/descendent", 52 { 53 std::shared_ptr<sdbusplus::asio::dbus_interface>(), 54 { 55 "/test/object_path_0/child/grandchild", 56 }, 57 }, 58 }, 59 }, 60 .owners = {}, 61 .pending = {}, 62 }; 63 }; 64 65 TEST_F(TestHandler, AddObjectMapResult) 66 { 67 std::vector<InterfaceMapType::value_type> interfaceMaps; 68 addObjectMapResult(interfaceMaps, "test_object_path", 69 std::pair<std::string, InterfaceNames>( 70 "test_object_connection_0", { 71 "test_interface_0", 72 "test_interface_1", 73 })); 74 75 addObjectMapResult(interfaceMaps, "test_object_path", 76 std::pair<std::string, InterfaceNames>( 77 "test_object_connection_1", { 78 "test_interface_0", 79 "test_interface_1", 80 })); 81 ASSERT_EQ(interfaceMaps.size(), 1); 82 83 auto entry = std::find_if( 84 interfaceMaps.begin(), interfaceMaps.end(), 85 [](const auto& i) { return "test_object_path" == i.first; }); 86 ASSERT_NE(entry, interfaceMap.end()); 87 for (const auto& [_, interfaces] : entry->second) 88 { 89 ASSERT_THAT(interfaces, 90 ElementsAre("test_interface_0", "test_interface_1")); 91 } 92 93 // Change the interface, but expect it to be unchanged 94 addObjectMapResult(interfaceMaps, "test_object_path", 95 std::pair<std::string, InterfaceNames>( 96 "test_object_connection_0", {"test_interface_2"})); 97 addObjectMapResult(interfaceMaps, "test_object_path", 98 std::pair<std::string, InterfaceNames>( 99 "test_object_connection_1", {"test_interface_2"})); 100 entry = std::find_if( 101 interfaceMaps.begin(), interfaceMaps.end(), 102 [](const auto& i) { return "test_object_path" == i.first; }); 103 ASSERT_NE(entry, interfaceMaps.end()); 104 for (const auto& [_, interfaces] : entry->second) 105 { 106 ASSERT_THAT(interfaces, 107 ElementsAre("test_interface_0", "test_interface_1")); 108 } 109 } 110 111 TEST_F(TestHandler, getAncestorsBad) 112 { 113 std::string path = "/test/object_path_0/child/grandchild"; 114 std::vector<std::string> interfaces = {"bad_interface"}; 115 std::vector<InterfaceMapType::value_type> ancestors = 116 getAncestors(interfaceMap, path, interfaces); 117 ASSERT_TRUE(ancestors.empty()); 118 119 path = "/invalid_path"; 120 EXPECT_THROW( 121 getAncestors(interfaceMap, path, interfaces), 122 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 123 } 124 125 TEST_F(TestHandler, getAncestorsGood) 126 { 127 std::string path = "/test/object_path_0/child/grandchild"; 128 std::vector<std::string> interfaces = {"test_interface_0", 129 "test_interface_1"}; 130 std::vector<InterfaceMapType::value_type> ancestors = 131 getAncestors(interfaceMap, path, interfaces); 132 ASSERT_EQ(ancestors.size(), 2); 133 134 // Grand Parent 135 EXPECT_EQ(ancestors[0].first, "/test/object_path_0"); 136 ASSERT_EQ(ancestors[0].second.size(), 1); 137 auto grandParent = ancestors[0].second.find("test_object_connection_0"); 138 ASSERT_NE(grandParent, ancestors[0].second.end()); 139 ASSERT_THAT(grandParent->second, ElementsAre("test_interface_0")); 140 141 // Parent 142 ASSERT_EQ(ancestors[1].first, "/test/object_path_0/child"); 143 ASSERT_EQ(ancestors[1].second.size(), 1); 144 auto parent = ancestors[1].second.find("test_object_connection_1"); 145 ASSERT_NE(parent, ancestors[1].second.end()); 146 ASSERT_THAT(parent->second, ElementsAre("test_interface_1")); 147 } 148 149 TEST_F(TestHandler, getObjectBad) 150 { 151 std::string path = "/test/object_path_0"; 152 std::vector<std::string> interfaces = {"bad_interface"}; 153 EXPECT_THROW( 154 getObject(interfaceMap, path, interfaces), 155 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 156 157 path = "/invalid_path"; 158 EXPECT_THROW( 159 getObject(interfaceMap, path, interfaces), 160 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 161 162 path = "/"; 163 EXPECT_THROW( 164 getObject(interfaceMap, path, interfaces), 165 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 166 } 167 168 TEST_F(TestHandler, getObjectGood) 169 { 170 std::string path = "/test/object_path_0"; 171 std::vector<std::string> interfaces = {"test_interface_0", 172 "test_interface_1"}; 173 ConnectionNames connection = getObject(interfaceMap, path, interfaces); 174 auto object = connection.find("test_object_connection_0"); 175 ASSERT_NE(object, connection.end()); 176 ASSERT_THAT(object->second, ElementsAre("test_interface_0")); 177 178 path = "/test/object_path_0/child"; 179 connection = getObject(interfaceMap, path, interfaces); 180 object = connection.find("test_object_connection_1"); 181 ASSERT_NE(object, connection.end()); 182 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 183 } 184 185 TEST_F(TestHandler, getSubTreeBad) 186 { 187 std::string path = "/test/object_path_0"; 188 std::vector<std::string> interfaces = {"bad_interface"}; 189 std::vector<InterfaceMapType::value_type> subtree = 190 getSubTree(interfaceMap, path, 0, interfaces); 191 ASSERT_TRUE(subtree.empty()); 192 193 path = "/invalid_path"; 194 EXPECT_THROW( 195 getSubTree(interfaceMap, path, 0, interfaces), 196 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 197 } 198 199 void verifySubtree(std::span<InterfaceMapType::value_type> subtree) 200 { 201 ASSERT_EQ(subtree.size(), 2); 202 ConnectionNames connection = subtree[0].second; 203 auto object = connection.find("test_object_connection_1"); 204 ASSERT_NE(object, connection.end()); 205 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 206 207 connection = subtree[1].second; 208 object = connection.find("test_object_connection_3"); 209 ASSERT_NE(object, connection.end()); 210 ASSERT_THAT(object->second, ElementsAre("test_interface_3")); 211 } 212 213 TEST_F(TestHandler, getSubTreeGood) 214 { 215 std::string path0 = "/test/object_path_0"; 216 std::string path1 = "/test/object_path_0/child/grandchild"; 217 std::vector<std::string> interfaces = {"test_interface_1", 218 "test_interface_3"}; 219 // Root 220 std::vector<InterfaceMapType::value_type> subtree = 221 getSubTree(interfaceMap, "/", 0, interfaces); 222 verifySubtree(subtree); 223 224 // Path0 225 subtree = getSubTree(interfaceMap, path0, 0, interfaces); 226 verifySubtree(subtree); 227 228 // Path0 with Depth path of 1 229 subtree = getSubTree(interfaceMap, path0, 1, interfaces); 230 ASSERT_EQ(subtree.size(), 1); 231 ConnectionNames connection = subtree[0].second; 232 auto object = connection.find("test_object_connection_1"); 233 ASSERT_NE(object, connection.end()); 234 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 235 236 // Path1 237 subtree = getSubTree(interfaceMap, path1, 0, interfaces); 238 ASSERT_EQ(subtree.size(), 1); 239 connection = subtree[0].second; 240 object = connection.find("test_object_connection_3"); 241 ASSERT_NE(object, connection.end()); 242 ASSERT_THAT(object->second, ElementsAre("test_interface_3")); 243 } 244 245 TEST_F(TestHandler, getSubTreePathsBad) 246 { 247 std::string path = "/test/object_path_0"; 248 std::vector<std::string> interfaces = {"bad_interface"}; 249 std::vector<std::string> subtreePath = getSubTreePaths(interfaceMap, path, 250 0, interfaces); 251 ASSERT_TRUE(subtreePath.empty()); 252 253 path = "/invalid_path"; 254 EXPECT_THROW( 255 getSubTreePaths(interfaceMap, path, 0, interfaces), 256 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 257 } 258 259 TEST_F(TestHandler, getSubTreePathsGood) 260 { 261 std::string path0 = "/test/object_path_0"; 262 std::string path1 = "/test/object_path_0/child/grandchild"; 263 std::vector<std::string> interfaces = {"test_interface_1", 264 "test_interface_3"}; 265 // Root 266 std::vector<std::string> subtreePath = getSubTreePaths(interfaceMap, "/", 0, 267 interfaces); 268 ASSERT_THAT(subtreePath, 269 ElementsAre("/test/object_path_0/child", 270 "/test/object_path_0/child/grandchild/dog")); 271 272 // Path0 273 subtreePath = getSubTreePaths(interfaceMap, path0, 0, interfaces); 274 ASSERT_THAT(subtreePath, 275 ElementsAre("/test/object_path_0/child", 276 "/test/object_path_0/child/grandchild/dog")); 277 278 // Path0 + Depth path of 1 279 subtreePath = getSubTreePaths(interfaceMap, path0, 1, interfaces); 280 ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child")); 281 282 // Path1 283 subtreePath = getSubTreePaths(interfaceMap, path1, 0, interfaces); 284 ASSERT_THAT(subtreePath, 285 ElementsAre("/test/object_path_0/child/grandchild/dog")); 286 } 287 288 TEST_F(TestHandler, getAssociatedSubTreeBad) 289 { 290 sdbusplus::message::object_path path("/test/object_path_0"); 291 sdbusplus::message::object_path validAssociatedPath = path / "descendent"; 292 std::vector<std::string> invalidInterfaces = {"test_interface_3"}; 293 std::vector<std::string> validInterfaces = {"test_interface_1", 294 "test_interface_2"}; 295 // Associated path, but invalid interface 296 ASSERT_TRUE(getAssociatedSubTree(interfaceMap, associationMap, 297 validAssociatedPath, path, 0, 298 invalidInterfaces) 299 .empty()); 300 301 // Valid interface, not associated 302 ASSERT_TRUE(getAssociatedSubTree(interfaceMap, associationMap, path / "dog", 303 path, 0, validInterfaces) 304 .empty()); 305 306 // Invalid path, with valid association 307 path = sdbusplus::message::object_path("/invalid_path"); 308 EXPECT_THROW( 309 getAssociatedSubTree(interfaceMap, associationMap, validAssociatedPath, 310 path, 0, validInterfaces), 311 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 312 } 313 314 TEST_F(TestHandler, getAssociatedSubTreeGood) 315 { 316 sdbusplus::message::object_path path0("/test/object_path_0"); 317 sdbusplus::message::object_path path1("/test/object_path_0/child"); 318 sdbusplus::message::object_path associatedPath = path0 / "descendent"; 319 std::vector<std::string> interfaces = {"test_interface_1", 320 "test_interface_2", 321 // Not associated to path 322 "test_interface_3"}; 323 324 // Path0 325 std::vector<InterfaceMapType::value_type> subtree = getAssociatedSubTree( 326 interfaceMap, associationMap, associatedPath, path0, 0, interfaces); 327 ASSERT_EQ(subtree.size(), 2); 328 ConnectionNames connection = subtree[0].second; 329 auto object = connection.find("test_object_connection_1"); 330 ASSERT_NE(object, connection.end()); 331 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 332 333 connection = subtree[1].second; 334 object = connection.find("test_object_connection_2"); 335 ASSERT_NE(object, connection.end()); 336 ASSERT_THAT(object->second, ElementsAre("test_interface_2")); 337 338 // Path0 with Depth path of 1 339 subtree = getAssociatedSubTree(interfaceMap, associationMap, associatedPath, 340 path0, 1, interfaces); 341 ASSERT_EQ(subtree.size(), 1); 342 connection = subtree[0].second; 343 object = connection.find("test_object_connection_1"); 344 ASSERT_NE(object, connection.end()); 345 ASSERT_THAT(object->second, ElementsAre("test_interface_1")); 346 347 // Path1 348 subtree = getAssociatedSubTree(interfaceMap, associationMap, 349 path1 / "descendent", path1, 0, interfaces); 350 ASSERT_EQ(subtree.size(), 1); 351 connection = subtree[0].second; 352 object = connection.find("test_object_connection_2"); 353 ASSERT_NE(object, connection.end()); 354 ASSERT_THAT(object->second, ElementsAre("test_interface_2")); 355 } 356 357 TEST_F(TestHandler, getAssociatedSubTreePathsBad) 358 { 359 sdbusplus::message::object_path path("/test/object_path_0"); 360 sdbusplus::message::object_path validAssociatedPath = path / "descendent"; 361 std::vector<std::string> invalidInterfaces = {"test_interface_3"}; 362 std::vector<std::string> validInterfaces = {"test_interface_1", 363 "test_interface_2"}; 364 // Associated path, but invalid interface 365 ASSERT_TRUE(getAssociatedSubTreePaths(interfaceMap, associationMap, 366 validAssociatedPath, path, 0, 367 invalidInterfaces) 368 .empty()); 369 370 // Valid interface, not associated 371 ASSERT_TRUE(getAssociatedSubTreePaths(interfaceMap, associationMap, 372 path / "dog", path, 0, 373 validInterfaces) 374 .empty()); 375 376 // Invalid path, with valid association 377 path = sdbusplus::message::object_path("/invalid_path"); 378 EXPECT_THROW( 379 getAssociatedSubTreePaths(interfaceMap, associationMap, 380 validAssociatedPath, path, 0, 381 validInterfaces), 382 sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); 383 } 384 385 TEST_F(TestHandler, getAssociatedSubTreePathsGood) 386 { 387 sdbusplus::message::object_path path0("/test/object_path_0"); 388 sdbusplus::message::object_path path1("/test/object_path_0/child"); 389 sdbusplus::message::object_path associatedPath = path0 / "descendent"; 390 std::vector<std::string> interfaces = {"test_interface_1", 391 "test_interface_2", 392 // Not associated to path 393 "test_interface_3"}; 394 395 // Path0 396 std::vector<std::string> subtreePath = getAssociatedSubTreePaths( 397 interfaceMap, associationMap, associatedPath, path0, 0, interfaces); 398 ASSERT_THAT(subtreePath, 399 ElementsAre("/test/object_path_0/child", 400 "/test/object_path_0/child/grandchild")); 401 402 // Path0 with Depth path of 1 403 subtreePath = getAssociatedSubTreePaths( 404 interfaceMap, associationMap, associatedPath, path0, 1, interfaces); 405 ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child")); 406 407 // Path1 408 subtreePath = getAssociatedSubTreePaths(interfaceMap, associationMap, 409 path1 / "descendent", path1, 0, 410 interfaces); 411 ASSERT_THAT(subtreePath, 412 ElementsAre("/test/object_path_0/child/grandchild")); 413 } 414