#include "src/handler.hpp" #include "src/types.hpp" #include <xyz/openbmc_project/Common/error.hpp> #include <span> #include <utility> #include <vector> #include <gmock/gmock.h> #include <gtest/gtest.h> using ::testing::ElementsAre; class TestHandler : public testing::Test { protected: InterfaceMapType interfaceMap = { { "/test/object_path_0", {{"test_object_connection_0", {"test_interface_0"}}}, }, { "/test/object_path_0/child", {{"test_object_connection_1", {"test_interface_1"}}}, }, { "/test/object_path_0/child/grandchild", {{"test_object_connection_2", {"test_interface_2"}}}, }, { "/test/object_path_0/child/grandchild/dog", {{"test_object_connection_3", {"test_interface_3"}}}, }}; AssociationMaps associationMap = { .ifaces = { { "/test/object_path_0/descendent", { std::shared_ptr<sdbusplus::asio::dbus_interface>(), { "/test/object_path_0/child", "/test/object_path_0/child/grandchild", }, }, }, { "/test/object_path_0/child/descendent", { std::shared_ptr<sdbusplus::asio::dbus_interface>(), { "/test/object_path_0/child/grandchild", }, }, }, }, .owners = {}, .pending = {}, }; }; TEST_F(TestHandler, AddObjectMapResult) { std::vector<InterfaceMapType::value_type> interfaceMaps; addObjectMapResult(interfaceMaps, "test_object_path", std::pair<std::string, InterfaceNames>( "test_object_connection_0", { "test_interface_0", "test_interface_1", })); addObjectMapResult(interfaceMaps, "test_object_path", std::pair<std::string, InterfaceNames>( "test_object_connection_1", { "test_interface_0", "test_interface_1", })); ASSERT_EQ(interfaceMaps.size(), 1); auto entry = std::find_if( interfaceMaps.begin(), interfaceMaps.end(), [](const auto& i) { return "test_object_path" == i.first; }); ASSERT_NE(entry, interfaceMap.end()); for (const auto& [_, interfaces] : entry->second) { ASSERT_THAT(interfaces, ElementsAre("test_interface_0", "test_interface_1")); } // Change the interface, but expect it to be unchanged addObjectMapResult(interfaceMaps, "test_object_path", std::pair<std::string, InterfaceNames>( "test_object_connection_0", {"test_interface_2"})); addObjectMapResult(interfaceMaps, "test_object_path", std::pair<std::string, InterfaceNames>( "test_object_connection_1", {"test_interface_2"})); entry = std::find_if( interfaceMaps.begin(), interfaceMaps.end(), [](const auto& i) { return "test_object_path" == i.first; }); ASSERT_NE(entry, interfaceMaps.end()); for (const auto& [_, interfaces] : entry->second) { ASSERT_THAT(interfaces, ElementsAre("test_interface_0", "test_interface_1")); } } TEST_F(TestHandler, getAncestorsBad) { std::string path = "/test/object_path_0/child/grandchild"; std::vector<std::string> interfaces = {"bad_interface"}; std::vector<InterfaceMapType::value_type> ancestors = getAncestors(interfaceMap, path, interfaces); ASSERT_TRUE(ancestors.empty()); path = "/invalid_path"; EXPECT_THROW( getAncestors(interfaceMap, path, interfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); } TEST_F(TestHandler, getAncestorsGood) { std::string path = "/test/object_path_0/child/grandchild"; std::vector<std::string> interfaces = {"test_interface_0", "test_interface_1"}; std::vector<InterfaceMapType::value_type> ancestors = getAncestors(interfaceMap, path, interfaces); ASSERT_EQ(ancestors.size(), 2); // Grand Parent EXPECT_EQ(ancestors[0].first, "/test/object_path_0"); ASSERT_EQ(ancestors[0].second.size(), 1); auto grandParent = ancestors[0].second.find("test_object_connection_0"); ASSERT_NE(grandParent, ancestors[0].second.end()); ASSERT_THAT(grandParent->second, ElementsAre("test_interface_0")); // Parent ASSERT_EQ(ancestors[1].first, "/test/object_path_0/child"); ASSERT_EQ(ancestors[1].second.size(), 1); auto parent = ancestors[1].second.find("test_object_connection_1"); ASSERT_NE(parent, ancestors[1].second.end()); ASSERT_THAT(parent->second, ElementsAre("test_interface_1")); } TEST_F(TestHandler, getObjectBad) { std::string path = "/test/object_path_0"; std::vector<std::string> interfaces = {"bad_interface"}; EXPECT_THROW( getObject(interfaceMap, path, interfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); path = "/invalid_path"; EXPECT_THROW( getObject(interfaceMap, path, interfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); path = "/"; EXPECT_THROW( getObject(interfaceMap, path, interfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); } TEST_F(TestHandler, getObjectGood) { std::string path = "/test/object_path_0"; std::vector<std::string> interfaces = {"test_interface_0", "test_interface_1"}; ConnectionNames connection = getObject(interfaceMap, path, interfaces); auto object = connection.find("test_object_connection_0"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_0")); path = "/test/object_path_0/child"; connection = getObject(interfaceMap, path, interfaces); object = connection.find("test_object_connection_1"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_1")); } TEST_F(TestHandler, getSubTreeBad) { std::string path = "/test/object_path_0"; std::vector<std::string> interfaces = {"bad_interface"}; std::vector<InterfaceMapType::value_type> subtree = getSubTree(interfaceMap, path, 0, interfaces); ASSERT_TRUE(subtree.empty()); path = "/invalid_path"; EXPECT_THROW( getSubTree(interfaceMap, path, 0, interfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); } void verifySubtree(std::span<InterfaceMapType::value_type> subtree) { ASSERT_EQ(subtree.size(), 2); ConnectionNames connection = subtree[0].second; auto object = connection.find("test_object_connection_1"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_1")); connection = subtree[1].second; object = connection.find("test_object_connection_3"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_3")); } TEST_F(TestHandler, getSubTreeGood) { std::string path0 = "/test/object_path_0"; std::string path1 = "/test/object_path_0/child/grandchild"; std::vector<std::string> interfaces = {"test_interface_1", "test_interface_3"}; // Root std::vector<InterfaceMapType::value_type> subtree = getSubTree(interfaceMap, "/", 0, interfaces); verifySubtree(subtree); // Path0 subtree = getSubTree(interfaceMap, path0, 0, interfaces); verifySubtree(subtree); // Path0 with Depth path of 1 subtree = getSubTree(interfaceMap, path0, 1, interfaces); ASSERT_EQ(subtree.size(), 1); ConnectionNames connection = subtree[0].second; auto object = connection.find("test_object_connection_1"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_1")); // Path1 subtree = getSubTree(interfaceMap, path1, 0, interfaces); ASSERT_EQ(subtree.size(), 1); connection = subtree[0].second; object = connection.find("test_object_connection_3"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_3")); } TEST_F(TestHandler, getSubTreePathsBad) { std::string path = "/test/object_path_0"; std::vector<std::string> interfaces = {"bad_interface"}; std::vector<std::string> subtreePath = getSubTreePaths(interfaceMap, path, 0, interfaces); ASSERT_TRUE(subtreePath.empty()); path = "/invalid_path"; EXPECT_THROW( getSubTreePaths(interfaceMap, path, 0, interfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); } TEST_F(TestHandler, getSubTreePathsGood) { std::string path0 = "/test/object_path_0"; std::string path1 = "/test/object_path_0/child/grandchild"; std::vector<std::string> interfaces = {"test_interface_1", "test_interface_3"}; // Root std::vector<std::string> subtreePath = getSubTreePaths(interfaceMap, "/", 0, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child", "/test/object_path_0/child/grandchild/dog")); // Path0 subtreePath = getSubTreePaths(interfaceMap, path0, 0, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child", "/test/object_path_0/child/grandchild/dog")); // Path0 + Depth path of 1 subtreePath = getSubTreePaths(interfaceMap, path0, 1, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child")); // Path1 subtreePath = getSubTreePaths(interfaceMap, path1, 0, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child/grandchild/dog")); } TEST_F(TestHandler, getAssociatedSubTreeBad) { sdbusplus::message::object_path path("/test/object_path_0"); sdbusplus::message::object_path validAssociatedPath = path / "descendent"; std::vector<std::string> invalidInterfaces = {"test_interface_3"}; std::vector<std::string> validInterfaces = {"test_interface_1", "test_interface_2"}; // Associated path, but invalid interface ASSERT_TRUE( getAssociatedSubTree(interfaceMap, associationMap, validAssociatedPath, path, 0, invalidInterfaces) .empty()); // Valid interface, not associated ASSERT_TRUE(getAssociatedSubTree(interfaceMap, associationMap, path / "dog", path, 0, validInterfaces) .empty()); // Invalid path, with valid association path = sdbusplus::message::object_path("/invalid_path"); EXPECT_THROW( getAssociatedSubTree(interfaceMap, associationMap, validAssociatedPath, path, 0, validInterfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); } TEST_F(TestHandler, getAssociatedSubTreeGood) { sdbusplus::message::object_path path0("/test/object_path_0"); sdbusplus::message::object_path path1("/test/object_path_0/child"); sdbusplus::message::object_path associatedPath = path0 / "descendent"; std::vector<std::string> interfaces = { "test_interface_1", "test_interface_2", // Not associated to path "test_interface_3"}; // Path0 std::vector<InterfaceMapType::value_type> subtree = getAssociatedSubTree( interfaceMap, associationMap, associatedPath, path0, 0, interfaces); ASSERT_EQ(subtree.size(), 2); ConnectionNames connection = subtree[0].second; auto object = connection.find("test_object_connection_1"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_1")); connection = subtree[1].second; object = connection.find("test_object_connection_2"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_2")); // Path0 with Depth path of 1 subtree = getAssociatedSubTree(interfaceMap, associationMap, associatedPath, path0, 1, interfaces); ASSERT_EQ(subtree.size(), 1); connection = subtree[0].second; object = connection.find("test_object_connection_1"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_1")); // Path1 subtree = getAssociatedSubTree(interfaceMap, associationMap, path1 / "descendent", path1, 0, interfaces); ASSERT_EQ(subtree.size(), 1); connection = subtree[0].second; object = connection.find("test_object_connection_2"); ASSERT_NE(object, connection.end()); ASSERT_THAT(object->second, ElementsAre("test_interface_2")); } TEST_F(TestHandler, getAssociatedSubTreePathsBad) { sdbusplus::message::object_path path("/test/object_path_0"); sdbusplus::message::object_path validAssociatedPath = path / "descendent"; std::vector<std::string> invalidInterfaces = {"test_interface_3"}; std::vector<std::string> validInterfaces = {"test_interface_1", "test_interface_2"}; // Associated path, but invalid interface ASSERT_TRUE(getAssociatedSubTreePaths(interfaceMap, associationMap, validAssociatedPath, path, 0, invalidInterfaces) .empty()); // Valid interface, not associated ASSERT_TRUE( getAssociatedSubTreePaths(interfaceMap, associationMap, path / "dog", path, 0, validInterfaces) .empty()); // Invalid path, with valid association path = sdbusplus::message::object_path("/invalid_path"); EXPECT_THROW( getAssociatedSubTreePaths(interfaceMap, associationMap, validAssociatedPath, path, 0, validInterfaces), sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound); } TEST_F(TestHandler, getAssociatedSubTreePathsGood) { sdbusplus::message::object_path path0("/test/object_path_0"); sdbusplus::message::object_path path1("/test/object_path_0/child"); sdbusplus::message::object_path associatedPath = path0 / "descendent"; std::vector<std::string> interfaces = { "test_interface_1", "test_interface_2", // Not associated to path "test_interface_3"}; // Path0 std::vector<std::string> subtreePath = getAssociatedSubTreePaths( interfaceMap, associationMap, associatedPath, path0, 0, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child", "/test/object_path_0/child/grandchild")); // Path0 with Depth path of 1 subtreePath = getAssociatedSubTreePaths( interfaceMap, associationMap, associatedPath, path0, 1, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child")); // Path1 subtreePath = getAssociatedSubTreePaths(interfaceMap, associationMap, path1 / "descendent", path1, 0, interfaces); ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child/grandchild")); }