1 #include "src/associations.hpp"
2 
3 #include "src/test/util/asio_server_class.hpp"
4 #include "src/test/util/association_objects.hpp"
5 #include "src/test/util/debug_output.hpp"
6 
7 #include <sdbusplus/asio/connection.hpp>
8 #include <sdbusplus/asio/object_server.hpp>
9 
10 #include <gtest/gtest.h>
11 
12 class TestAssociations : public AsioServerClassTest
13 {
14 };
15 sdbusplus::asio::object_server* TestAssociations::AsioServerClassTest::server =
16     nullptr;
17 
18 // Verify call when path is not in associated owners
19 TEST_F(TestAssociations, SourcePathNotInAssociations)
20 {
21     EXPECT_NE(nullptr, server);
22     std::string sourcePath = "/xyz/openbmc_project/no/association";
23     AssociationOwnersType assocOwners;
24     AssociationInterfaces assocInterfaces;
25 
26     removeAssociation(sourcePath, DEFAULT_DBUS_SVC, *server, assocOwners,
27                       assocInterfaces);
28 }
29 
30 // Verify call when owner is not in associated owners
31 TEST_F(TestAssociations, OwnerNotInAssociations)
32 {
33     AssociationInterfaces assocInterfaces;
34 
35     auto assocOwners = createDefaultOwnerAssociation();
36 
37     removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
38                       assocOwners, assocInterfaces);
39 }
40 
41 // Verify call when path is not in associated interfaces
42 TEST_F(TestAssociations, PathNotInAssocInterfaces)
43 {
44     AssociationInterfaces assocInterfaces;
45 
46     auto assocOwners = createDefaultOwnerAssociation();
47 
48     removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
49                       assocOwners, assocInterfaces);
50 
51     EXPECT_TRUE(assocOwners.empty());
52 }
53 
54 // Verify call when path is in associated interfaces
55 TEST_F(TestAssociations, PathIsInAssociatedInterfaces)
56 {
57     // Build up these objects so that an associated interface will match
58     // with the associated owner being removed
59     auto assocOwners = createDefaultOwnerAssociation();
60     auto assocInterfaces = createDefaultInterfaceAssociation(server);
61 
62     removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
63                       assocOwners, assocInterfaces);
64 
65     // Verify owner association was deleted
66     EXPECT_TRUE(assocOwners.empty());
67 
68     // Verify endpoint was deleted from interface association
69     auto intfEndpoints =
70         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
71     EXPECT_EQ(intfEndpoints.size(), 0);
72     intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
73     EXPECT_EQ(intfEndpoints.size(), 0);
74 }
75 
76 // Verify call when path is in associated interfaces, with extra endpoints
77 TEST_F(TestAssociations, PathIsInAssociatedInterfacesExtraEndpoints)
78 {
79     // Build up these objects so that an associated interface will match
80     // with the associated owner being removed
81     auto assocOwners = createDefaultOwnerAssociation();
82     auto assocInterfaces = createDefaultInterfaceAssociation(server);
83 
84     // Add another endpoint to the assoc interfaces
85     addEndpointToInterfaceAssociation(assocInterfaces);
86 
87     removeAssociation(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC, *server,
88                       assocOwners, assocInterfaces);
89 
90     // Verify owner association was deleted
91     EXPECT_TRUE(assocOwners.empty());
92 
93     // Verify all endpoints are deleted since source path was deleted
94     auto intfEndpoints =
95         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
96     EXPECT_EQ(intfEndpoints.size(), 0);
97     intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
98     EXPECT_EQ(intfEndpoints.size(), 0);
99 }
100 
101 // Verify no associations or endpoints removed when the change is identical
102 TEST_F(TestAssociations, checkAssociationEndpointRemovesNoEpRemove)
103 {
104 
105     AssociationPaths newAssocPaths = {
106         {DEFAULT_FWD_PATH, {DEFAULT_ENDPOINT}},
107         {DEFAULT_REV_PATH, {DEFAULT_SOURCE_PATH}}};
108 
109     auto assocOwners = createDefaultOwnerAssociation();
110     auto assocInterfaces = createDefaultInterfaceAssociation(server);
111 
112     checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
113                                     newAssocPaths, *server, assocOwners,
114                                     assocInterfaces);
115 
116     // Verify endpoints were not deleted because they matche with what was
117     // in the original
118     auto intfEndpoints =
119         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
120     EXPECT_EQ(intfEndpoints.size(), 1);
121     intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
122     EXPECT_EQ(intfEndpoints.size(), 1);
123 }
124 
125 // Verify endpoint is removed when assoc path is different
126 TEST_F(TestAssociations, checkAssociationEndpointRemovesEpRemoveApDiff)
127 {
128     AssociationPaths newAssocPaths = {{"/different/path", {DEFAULT_ENDPOINT}}};
129 
130     auto assocOwners = createDefaultOwnerAssociation();
131     auto assocInterfaces = createDefaultInterfaceAssociation(server);
132 
133     checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
134                                     newAssocPaths, *server, assocOwners,
135                                     assocInterfaces);
136 
137     // Verify initial endpoints were deleted because the new path
138     auto intfEndpoints =
139         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
140     EXPECT_EQ(intfEndpoints.size(), 0);
141     intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
142     EXPECT_EQ(intfEndpoints.size(), 0);
143 }
144 
145 // Verify endpoint is removed when endpoint is different
146 TEST_F(TestAssociations, checkAssociationEndpointRemovesEpRemoveEpChanged)
147 {
148     AssociationPaths newAssocPaths = {
149         {DEFAULT_FWD_PATH, {DEFAULT_ENDPOINT + "/different"}},
150         {DEFAULT_REV_PATH, {DEFAULT_SOURCE_PATH + "/different"}}};
151 
152     auto assocOwners = createDefaultOwnerAssociation();
153     auto assocInterfaces = createDefaultInterfaceAssociation(server);
154 
155     checkAssociationEndpointRemoves(DEFAULT_SOURCE_PATH, DEFAULT_DBUS_SVC,
156                                     newAssocPaths, *server, assocOwners,
157                                     assocInterfaces);
158 
159     // Verify initial endpoints were deleted because of different endpoints
160     auto intfEndpoints =
161         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
162     EXPECT_EQ(intfEndpoints.size(), 0);
163     intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
164     EXPECT_EQ(intfEndpoints.size(), 0);
165 }
166 
167 // Verify existing endpoint deleted when empty endpoint is provided
168 TEST_F(TestAssociations, associationChangedEmptyEndpoint)
169 {
170     std::vector<Association> associations = {{"inventory", "error", ""}};
171 
172     auto assocOwners = createDefaultOwnerAssociation();
173     auto assocInterfaces = createDefaultInterfaceAssociation(server);
174 
175     // Empty endpoint will result in deletion of corresponding assocInterface
176     associationChanged(*server, associations, DEFAULT_SOURCE_PATH,
177                        DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);
178 
179     // Both of these should be 0 since we have an invalid endpoint
180     auto intfEndpoints =
181         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
182     EXPECT_EQ(intfEndpoints.size(), 0);
183     intfEndpoints = std::get<endpointsPos>(assocInterfaces[DEFAULT_REV_PATH]);
184     EXPECT_EQ(intfEndpoints.size(), 0);
185 }
186 
187 // Add a new association with endpoint
188 TEST_F(TestAssociations, associationChangedAddNewAssoc)
189 {
190     std::vector<Association> associations = {
191         {"abc", "def", "/xyz/openbmc_project/new/endpoint"}};
192 
193     auto assocOwners = createDefaultOwnerAssociation();
194     auto assocInterfaces = createDefaultInterfaceAssociation(server);
195 
196     associationChanged(*server, associations, "/new/source/path",
197                        DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);
198 
199     // Two source paths
200     EXPECT_EQ(assocOwners.size(), 2);
201 
202     // Four interfaces
203     EXPECT_EQ(assocInterfaces.size(), 4);
204 
205     // New endpoint so assocInterfaces should be same size
206     auto intfEndpoints =
207         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
208     EXPECT_EQ(intfEndpoints.size(), 1);
209 }
210 
211 // Add a new association to empty objects
212 TEST_F(TestAssociations, associationChangedAddNewAssocEmptyObj)
213 {
214     std::string sourcePath = "/logging/entry/1";
215     std::string owner = "xyz.openbmc_project.Test";
216     std::vector<Association> associations = {
217         {"inventory", "error",
218          "/xyz/openbmc_project/inventory/system/chassis"}};
219 
220     // Empty objects because this test will ensure assocOwners adds the
221     // changed association and interface
222     AssociationInterfaces assocInterfaces;
223     AssociationOwnersType assocOwners;
224 
225     associationChanged(*server, associations, DEFAULT_SOURCE_PATH,
226                        DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);
227 
228     // New associations so ensure it now contains a single entry
229     EXPECT_EQ(assocOwners.size(), 1);
230 
231     // Verify corresponding assoc paths each have one endpoint in assoc
232     // interfaces and that those endpoints match
233     auto singleOwner = assocOwners[DEFAULT_SOURCE_PATH];
234     auto singleIntf = singleOwner[DEFAULT_DBUS_SVC];
235     for (auto i : singleIntf)
236     {
237         auto intfEndpoints = std::get<endpointsPos>(assocInterfaces[i.first]);
238         EXPECT_EQ(intfEndpoints.size(), 1);
239         EXPECT_EQ(intfEndpoints[0], *i.second.begin());
240     }
241 }
242 
243 // Add a new association to same source path but with new owner
244 TEST_F(TestAssociations, associationChangedAddNewAssocNewOwner)
245 {
246     std::string newOwner = "xyz.openbmc_project.Test2";
247     std::vector<Association> associations = {
248         {"inventory", "error",
249          "/xyz/openbmc_project/inventory/system/chassis"}};
250 
251     auto assocOwners = createDefaultOwnerAssociation();
252     auto assocInterfaces = createDefaultInterfaceAssociation(server);
253 
254     associationChanged(*server, associations, DEFAULT_SOURCE_PATH, newOwner,
255                        assocOwners, assocInterfaces);
256 
257     // New endpoint so assocOwners should be same size
258     EXPECT_EQ(assocOwners.size(), 1);
259 
260     // Ensure only one endpoint under first path
261     auto intfEndpoints =
262         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
263     EXPECT_EQ(intfEndpoints.size(), 1);
264 
265     // Ensure the 2 new association endpoints are under the new owner
266     auto a = assocOwners.find(DEFAULT_SOURCE_PATH);
267     auto o = a->second.find(newOwner);
268     EXPECT_EQ(o->second.size(), 2);
269 }
270 
271 // Add a new association to existing interface path
272 TEST_F(TestAssociations, associationChangedAddNewAssocSameInterface)
273 {
274     std::vector<Association> associations = {
275         {"abc", "error", "/xyz/openbmc_project/inventory/system/chassis"}};
276 
277     auto assocOwners = createDefaultOwnerAssociation();
278     auto assocInterfaces = createDefaultInterfaceAssociation(server);
279 
280     associationChanged(*server, associations, DEFAULT_SOURCE_PATH,
281                        DEFAULT_DBUS_SVC, assocOwners, assocInterfaces);
282 
283     // Should have 3 entries in AssociationInterfaces, one is just missing an
284     // endpoint
285     EXPECT_EQ(assocInterfaces.size(), 3);
286 
287     // Change to existing interface so it will be removed here
288     auto intfEndpoints =
289         std::get<endpointsPos>(assocInterfaces[DEFAULT_FWD_PATH]);
290     EXPECT_EQ(intfEndpoints.size(), 0);
291 
292     // The new endpoint should exist though in it's place
293     intfEndpoints = std::get<endpointsPos>(
294         assocInterfaces[DEFAULT_SOURCE_PATH + "/" + "abc"]);
295     EXPECT_EQ(intfEndpoints.size(), 1);
296 
297     // Added to an existing owner path so still 1
298     EXPECT_EQ(assocOwners.size(), 1);
299 }
300