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 
38 TEST_F(TestHandler, AddObjectMapResult)
39 {
40     std::vector<InterfaceMapType::value_type> interfaceMaps;
41     addObjectMapResult(interfaceMaps, "test_object_path",
42                        std::pair<std::string, InterfaceNames>(
43                            "test_object_connection_0", {
44                                                            "test_interface_0",
45                                                            "test_interface_1",
46                                                        }));
47 
48     addObjectMapResult(interfaceMaps, "test_object_path",
49                        std::pair<std::string, InterfaceNames>(
50                            "test_object_connection_1", {
51                                                            "test_interface_0",
52                                                            "test_interface_1",
53                                                        }));
54     ASSERT_EQ(interfaceMaps.size(), 1);
55 
56     auto entry = std::find_if(
57         interfaceMaps.begin(), interfaceMaps.end(),
58         [](const auto& i) { return "test_object_path" == i.first; });
59     ASSERT_NE(entry, interfaceMap.end());
60     for (const auto& [_, interfaces] : entry->second)
61     {
62         ASSERT_THAT(interfaces,
63                     ElementsAre("test_interface_0", "test_interface_1"));
64     }
65 
66     // Change the interface, but expect it to be unchanged
67     addObjectMapResult(interfaceMaps, "test_object_path",
68                        std::pair<std::string, InterfaceNames>(
69                            "test_object_connection_0", {"test_interface_2"}));
70     addObjectMapResult(interfaceMaps, "test_object_path",
71                        std::pair<std::string, InterfaceNames>(
72                            "test_object_connection_1", {"test_interface_2"}));
73     entry = std::find_if(
74         interfaceMaps.begin(), interfaceMaps.end(),
75         [](const auto& i) { return "test_object_path" == i.first; });
76     ASSERT_NE(entry, interfaceMaps.end());
77     for (const auto& [_, interfaces] : entry->second)
78     {
79         ASSERT_THAT(interfaces,
80                     ElementsAre("test_interface_0", "test_interface_1"));
81     }
82 }
83 
84 TEST_F(TestHandler, getAncestorsBad)
85 {
86     std::string path = "/test/object_path_0/child/grandchild";
87     std::vector<std::string> interfaces = {"bad_interface"};
88     std::vector<InterfaceMapType::value_type> ancestors =
89         getAncestors(interfaceMap, path, interfaces);
90     ASSERT_TRUE(ancestors.empty());
91 
92     path = "/invalid_path";
93     EXPECT_THROW(
94         getAncestors(interfaceMap, path, interfaces),
95         sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
96 }
97 
98 TEST_F(TestHandler, getAncestorsGood)
99 {
100     std::string path = "/test/object_path_0/child/grandchild";
101     std::vector<std::string> interfaces = {"test_interface_0",
102                                            "test_interface_1"};
103     std::vector<InterfaceMapType::value_type> ancestors =
104         getAncestors(interfaceMap, path, interfaces);
105     ASSERT_EQ(ancestors.size(), 2);
106 
107     // Grand Parent
108     EXPECT_EQ(ancestors[0].first, "/test/object_path_0");
109     ASSERT_EQ(ancestors[0].second.size(), 1);
110     auto grandParent = ancestors[0].second.find("test_object_connection_0");
111     ASSERT_NE(grandParent, ancestors[0].second.end());
112     ASSERT_THAT(grandParent->second, ElementsAre("test_interface_0"));
113 
114     // Parent
115     ASSERT_EQ(ancestors[1].first, "/test/object_path_0/child");
116     ASSERT_EQ(ancestors[1].second.size(), 1);
117     auto parent = ancestors[1].second.find("test_object_connection_1");
118     ASSERT_NE(parent, ancestors[1].second.end());
119     ASSERT_THAT(parent->second, ElementsAre("test_interface_1"));
120 }
121 
122 TEST_F(TestHandler, getObjectBad)
123 {
124     std::string path = "/test/object_path_0";
125     std::vector<std::string> interfaces = {"bad_interface"};
126     EXPECT_THROW(
127         getObject(interfaceMap, path, interfaces),
128         sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
129 
130     path = "/invalid_path";
131     EXPECT_THROW(
132         getObject(interfaceMap, path, interfaces),
133         sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
134 
135     path = "/";
136     EXPECT_THROW(
137         getObject(interfaceMap, path, interfaces),
138         sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
139 }
140 
141 TEST_F(TestHandler, getObjectGood)
142 {
143     std::string path = "/test/object_path_0";
144     std::vector<std::string> interfaces = {"test_interface_0",
145                                            "test_interface_1"};
146     ConnectionNames connection = getObject(interfaceMap, path, interfaces);
147     auto object = connection.find("test_object_connection_0");
148     ASSERT_NE(object, connection.end());
149     ASSERT_THAT(object->second, ElementsAre("test_interface_0"));
150 
151     path = "/test/object_path_0/child";
152     connection = getObject(interfaceMap, path, interfaces);
153     object = connection.find("test_object_connection_1");
154     ASSERT_NE(object, connection.end());
155     ASSERT_THAT(object->second, ElementsAre("test_interface_1"));
156 }
157 
158 TEST_F(TestHandler, getSubTreeBad)
159 {
160     std::string path = "/test/object_path_0";
161     std::vector<std::string> interfaces = {"bad_interface"};
162     std::vector<InterfaceMapType::value_type> subtree =
163         getSubTree(interfaceMap, path, 0, interfaces);
164     ASSERT_TRUE(subtree.empty());
165 
166     path = "/invalid_path";
167     EXPECT_THROW(
168         getSubTree(interfaceMap, path, 0, interfaces),
169         sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
170 }
171 
172 void verifySubtree(std::span<InterfaceMapType::value_type> subtree)
173 {
174     ASSERT_EQ(subtree.size(), 2);
175     ConnectionNames connection = subtree[0].second;
176     auto object = connection.find("test_object_connection_1");
177     ASSERT_NE(object, connection.end());
178     ASSERT_THAT(object->second, ElementsAre("test_interface_1"));
179 
180     connection = subtree[1].second;
181     object = connection.find("test_object_connection_3");
182     ASSERT_NE(object, connection.end());
183     ASSERT_THAT(object->second, ElementsAre("test_interface_3"));
184 }
185 
186 TEST_F(TestHandler, getSubTreeGood)
187 {
188     std::string path0 = "/test/object_path_0";
189     std::string path1 = "/test/object_path_0/child/grandchild";
190     std::vector<std::string> interfaces = {"test_interface_1",
191                                            "test_interface_3"};
192     // Root
193     std::vector<InterfaceMapType::value_type> subtree =
194         getSubTree(interfaceMap, "/", 0, interfaces);
195     verifySubtree(subtree);
196 
197     // Path0
198     subtree = getSubTree(interfaceMap, path0, 0, interfaces);
199     verifySubtree(subtree);
200 
201     // Path0 with Depth path of 1
202     subtree = getSubTree(interfaceMap, path0, 1, interfaces);
203     ASSERT_EQ(subtree.size(), 1);
204     ConnectionNames connection = subtree[0].second;
205     auto object = connection.find("test_object_connection_1");
206     ASSERT_NE(object, connection.end());
207     ASSERT_THAT(object->second, ElementsAre("test_interface_1"));
208 
209     // Path1
210     subtree = getSubTree(interfaceMap, path1, 0, interfaces);
211     ASSERT_EQ(subtree.size(), 1);
212     connection = subtree[0].second;
213     object = connection.find("test_object_connection_3");
214     ASSERT_NE(object, connection.end());
215     ASSERT_THAT(object->second, ElementsAre("test_interface_3"));
216 }
217 
218 TEST_F(TestHandler, getSubTreePathsBad)
219 {
220     std::string path = "/test/object_path_0";
221     std::vector<std::string> interfaces = {"bad_interface"};
222     std::vector<std::string> subtreePath =
223         getSubTreePaths(interfaceMap, path, 0, interfaces);
224     ASSERT_TRUE(subtreePath.empty());
225 
226     path = "/invalid_path";
227     EXPECT_THROW(
228         getSubTreePaths(interfaceMap, path, 0, interfaces),
229         sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
230 }
231 
232 TEST_F(TestHandler, getSubTreePathsGood)
233 {
234     std::string path0 = "/test/object_path_0";
235     std::string path1 = "/test/object_path_0/child/grandchild";
236     std::vector<std::string> interfaces = {"test_interface_1",
237                                            "test_interface_3"};
238     // Root
239     std::vector<std::string> subtreePath =
240         getSubTreePaths(interfaceMap, "/", 0, interfaces);
241     ASSERT_THAT(subtreePath,
242                 ElementsAre("/test/object_path_0/child",
243                             "/test/object_path_0/child/grandchild/dog"));
244 
245     // Path0
246     subtreePath = getSubTreePaths(interfaceMap, path0, 0, interfaces);
247     ASSERT_THAT(subtreePath,
248                 ElementsAre("/test/object_path_0/child",
249                             "/test/object_path_0/child/grandchild/dog"));
250 
251     // Path0 + Depth path of 1
252     subtreePath = getSubTreePaths(interfaceMap, path0, 1, interfaces);
253     ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child"));
254 
255     // Path1
256     subtreePath = getSubTreePaths(interfaceMap, path1, 0, interfaces);
257     ASSERT_THAT(subtreePath,
258                 ElementsAre("/test/object_path_0/child/grandchild/dog"));
259 }
260