1 #include "item_updater.hpp"
2 #include "mocked_utils.hpp"
3 
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 
6 #include <gmock/gmock.h>
7 #include <gtest/gtest.h>
8 
9 using namespace phosphor::software::updater;
10 using ::testing::_;
11 using ::testing::Return;
12 using ::testing::ReturnArg;
13 using ::testing::StrEq;
14 
15 using std::experimental::any;
16 
17 class TestItemUpdater : public ::testing::Test
18 {
19   public:
20     using Properties = ItemUpdater::Properties;
21     using PropertyType = utils::UtilsInterface::PropertyType;
22 
23     TestItemUpdater() :
24         mockedUtils(
25             reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
26     {
27         ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>());
28     }
29 
30     ~TestItemUpdater()
31     {
32     }
33 
34     const auto& GetActivations()
35     {
36         return itemUpdater->activations;
37     }
38 
39     std::string getObjPath(const std::string& versionId)
40     {
41         return std::string(dBusPath) + "/" + versionId;
42     }
43 
44     void onPsuInventoryChanged(const std::string& psuPath,
45                                const Properties& properties)
46     {
47         itemUpdater->onPsuInventoryChanged(psuPath, properties);
48     }
49 
50     static constexpr auto dBusPath = SOFTWARE_OBJPATH;
51     sdbusplus::SdBusMock sdbusMock;
52     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
53     const utils::MockedUtils& mockedUtils;
54     std::unique_ptr<ItemUpdater> itemUpdater;
55 };
56 
57 TEST_F(TestItemUpdater, ctordtor)
58 {
59     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
60 }
61 
62 TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent)
63 {
64     constexpr auto psuPath = "/com/example/inventory/psu0";
65     constexpr auto service = "com.example.Software.Psu";
66     constexpr auto version = "version0";
67     std::string objPath = getObjPath(version);
68     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
69         .WillOnce(Return(std::vector<std::string>({psuPath})));
70     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
71         .WillOnce(Return(service));
72     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
73         .WillOnce(Return(std::string(version)));
74     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
75                                              _, StrEq(PRESENT)))
76         .WillOnce(Return(any(PropertyType(false)))); // not present
77 
78     // The item updater itself
79     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
80         .Times(1);
81 
82     // No activation/version objects are created
83     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
84         .Times(0);
85     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
86 }
87 
88 TEST_F(TestItemUpdater, CreateOnePSUOnPresent)
89 {
90     constexpr auto psuPath = "/com/example/inventory/psu0";
91     constexpr auto service = "com.example.Software.Psu";
92     constexpr auto version = "version0";
93     std::string objPath = getObjPath(version);
94     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
95         .WillOnce(Return(std::vector<std::string>({psuPath})));
96     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
97         .WillOnce(Return(service));
98     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
99         .WillOnce(Return(std::string(version)));
100     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
101                                              _, StrEq(PRESENT)))
102         .WillOnce(Return(any(PropertyType(true)))); // present
103 
104     // The item updater itself
105     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
106         .Times(1);
107 
108     // activation and version object will be added
109     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
110         .Times(2);
111     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
112 }
113 
114 TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion)
115 {
116     constexpr auto psu0 = "/com/example/inventory/psu0";
117     constexpr auto psu1 = "/com/example/inventory/psu1";
118     constexpr auto service = "com.example.Software.Psu";
119     auto version0 = std::string("version0");
120     auto version1 = std::string("version0");
121     auto objPath0 = getObjPath(version0);
122     auto objPath1 = getObjPath(version1);
123 
124     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
125         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
126     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
127         .WillOnce(Return(service));
128     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
129         .WillOnce(Return(service));
130     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
131         .WillOnce(Return(std::string(version0)));
132     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
133                                              StrEq(PRESENT)))
134         .WillOnce(Return(any(PropertyType(true)))); // present
135     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
136         .WillOnce(Return(std::string(version1)));
137     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
138                                              StrEq(PRESENT)))
139         .WillOnce(Return(any(PropertyType(true)))); // present
140 
141     // The item updater itself
142     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
143         .Times(1);
144 
145     // activation and version object will be added
146     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
147         .Times(2);
148     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
149 
150     // Verify there is only one activation and it has two associations
151     const auto& activations = GetActivations();
152     EXPECT_EQ(1u, activations.size());
153     const auto& activation = activations.find(version0)->second;
154     const auto& assocs = activation->associations();
155     EXPECT_EQ(2u, assocs.size());
156     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
157     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
158 }
159 
160 TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion)
161 {
162     constexpr auto psu0 = "/com/example/inventory/psu0";
163     constexpr auto psu1 = "/com/example/inventory/psu1";
164     constexpr auto service = "com.example.Software.Psu";
165     auto version0 = std::string("version0");
166     auto version1 = std::string("version1");
167     auto objPath0 = getObjPath(version0);
168     auto objPath1 = getObjPath(version1);
169 
170     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
171         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
172     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
173         .WillOnce(Return(service));
174     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
175         .WillOnce(Return(service));
176     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
177         .WillOnce(Return(std::string(version0)));
178     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
179                                              StrEq(PRESENT)))
180         .WillOnce(Return(any(PropertyType(true)))); // present
181     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
182         .WillOnce(Return(std::string(version1)));
183     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
184                                              StrEq(PRESENT)))
185         .WillOnce(Return(any(PropertyType(true)))); // present
186 
187     // The item updater itself
188     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
189         .Times(1);
190 
191     // two new activation and version objects will be added
192     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
193         .Times(2);
194     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
195         .Times(2);
196     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
197 
198     // Verify there are two activations and each with one association
199     const auto& activations = GetActivations();
200     EXPECT_EQ(2u, activations.size());
201     const auto& activation0 = activations.find(version0)->second;
202     const auto& assocs0 = activation0->associations();
203     EXPECT_EQ(1u, assocs0.size());
204     EXPECT_EQ(psu0, std::get<2>(assocs0[0]));
205 
206     const auto& activation1 = activations.find(version1)->second;
207     const auto& assocs1 = activation1->associations();
208     EXPECT_EQ(1u, assocs1.size());
209     EXPECT_EQ(psu1, std::get<2>(assocs1[0]));
210 }
211 
212 TEST_F(TestItemUpdater, OnOnePSURemoved)
213 {
214     constexpr auto psuPath = "/com/example/inventory/psu0";
215     constexpr auto service = "com.example.Software.Psu";
216     constexpr auto version = "version0";
217     std::string objPath = getObjPath(version);
218     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
219         .WillOnce(Return(std::vector<std::string>({psuPath})));
220     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
221         .WillOnce(Return(service));
222     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
223         .WillOnce(Return(std::string(version)));
224     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
225                                              _, StrEq(PRESENT)))
226         .WillOnce(Return(any(PropertyType(true)))); // present
227 
228     // The item updater itself
229     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
230         .Times(1);
231 
232     // activation and version object will be added
233     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
234         .Times(2);
235     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
236 
237     // the activation and version object will be removed
238     Properties p{{PRESENT, PropertyType(false)}};
239     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
240         .Times(2);
241     onPsuInventoryChanged(psuPath, p);
242 
243     // on exit, item updater is removed
244     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
245         .Times(1);
246 }
247 
248 TEST_F(TestItemUpdater, OnOnePSUAdded)
249 {
250     constexpr auto psuPath = "/com/example/inventory/psu0";
251     constexpr auto service = "com.example.Software.Psu";
252     constexpr auto version = "version0";
253     std::string objPath = getObjPath(version);
254     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
255         .WillOnce(Return(std::vector<std::string>({psuPath})));
256     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
257         .WillOnce(Return(service));
258     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
259         .WillOnce(Return(std::string(version)));
260     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
261                                              _, StrEq(PRESENT)))
262         .WillOnce(Return(any(PropertyType(false)))); // not present
263 
264     // The item updater itself
265     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
266         .Times(1);
267 
268     // No activation/version objects are created
269     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
270         .Times(0);
271     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
272 
273     // The PSU is present and version is added in a single call
274     Properties propAdded{{PRESENT, PropertyType(true)},
275                          {VERSION, PropertyType(std::string(version))}};
276     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
277         .Times(2);
278     onPsuInventoryChanged(psuPath, propAdded);
279 }
280 
281 TEST_F(TestItemUpdater, OnOnePSURemovedAndAdded)
282 {
283     constexpr auto psuPath = "/com/example/inventory/psu0";
284     constexpr auto service = "com.example.Software.Psu";
285     constexpr auto version = "version0";
286     std::string objPath = getObjPath(version);
287     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
288         .WillOnce(Return(std::vector<std::string>({psuPath})));
289     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
290         .WillOnce(Return(service));
291     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
292         .WillOnce(Return(std::string(version)));
293     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
294                                              _, StrEq(PRESENT)))
295         .WillOnce(Return(any(PropertyType(true)))); // present
296 
297     // The item updater itself
298     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
299         .Times(1);
300 
301     // activation and version object will be added
302     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
303         .Times(2);
304     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
305 
306     // the activation and version object will be removed
307     Properties propRemoved{{PRESENT, PropertyType(false)}};
308     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
309         .Times(2);
310     onPsuInventoryChanged(psuPath, propRemoved);
311 
312     Properties propAdded{{PRESENT, PropertyType(true)}};
313     Properties propVersion{{VERSION, PropertyType(std::string(version))}};
314     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
315         .Times(2);
316     onPsuInventoryChanged(psuPath, propAdded);
317     onPsuInventoryChanged(psuPath, propVersion);
318 
319     // on exit, objects are removed
320     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
321         .Times(2);
322     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
323         .Times(1);
324 }
325 
326 TEST_F(TestItemUpdater,
327        TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion)
328 {
329     constexpr auto psu0 = "/com/example/inventory/psu0";
330     constexpr auto psu1 = "/com/example/inventory/psu1";
331     constexpr auto service = "com.example.Software.Psu";
332     auto version0 = std::string("version0");
333     auto version1 = std::string("version0");
334     auto objPath0 = getObjPath(version0);
335     auto objPath1 = getObjPath(version1);
336 
337     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
338         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
339     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
340         .WillOnce(Return(service));
341     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
342         .WillOnce(Return(service));
343     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
344         .WillOnce(Return(std::string(version0)));
345     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
346                                              StrEq(PRESENT)))
347         .WillOnce(Return(any(PropertyType(true)))); // present
348     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
349         .WillOnce(Return(std::string(version1)));
350     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
351                                              StrEq(PRESENT)))
352         .WillOnce(Return(any(PropertyType(true)))); // present
353 
354     // The item updater itself
355     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
356         .Times(1);
357 
358     // activation and version object will be added
359     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
360         .Times(2);
361     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
362 
363     // Verify there is only one activation and it has two associations
364     const auto& activations = GetActivations();
365     EXPECT_EQ(1u, activations.size());
366     const auto& activation = activations.find(version0)->second;
367     auto assocs = activation->associations();
368     EXPECT_EQ(2u, assocs.size());
369     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
370     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
371 
372     // PSU0 is removed, only associations shall be updated
373     Properties propRemoved{{PRESENT, PropertyType(false)}};
374     onPsuInventoryChanged(psu0, propRemoved);
375     assocs = activation->associations();
376     EXPECT_EQ(1u, assocs.size());
377     EXPECT_EQ(psu1, std::get<2>(assocs[0]));
378 
379     // PSU1 is removed, the activation and version object shall be removed
380     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
381         .Times(2);
382     onPsuInventoryChanged(psu1, propRemoved);
383 
384     // Add PSU0 and PSU1 back, but PSU1 with a different version
385     version1 = "version1";
386     objPath1 = getObjPath(version1);
387     Properties propAdded0{{PRESENT, PropertyType(true)},
388                           {VERSION, PropertyType(std::string(version0))}};
389     Properties propAdded1{{PRESENT, PropertyType(true)},
390                           {VERSION, PropertyType(std::string(version1))}};
391     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
392         .Times(2);
393     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
394         .Times(2);
395     onPsuInventoryChanged(psu0, propAdded0);
396     onPsuInventoryChanged(psu1, propAdded1);
397 
398     // on exit, objects are removed
399     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
400         .Times(2);
401     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1)))
402         .Times(2);
403     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
404         .Times(1);
405 }
406