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::ContainerEq;
12 using ::testing::Pointee;
13 using ::testing::Return;
14 using ::testing::ReturnArg;
15 using ::testing::StrEq;
16 
17 using std::experimental::any;
18 
19 class TestItemUpdater : public ::testing::Test
20 {
21   public:
22     using Properties = ItemUpdater::Properties;
23     using PropertyType = utils::UtilsInterface::PropertyType;
24 
25     TestItemUpdater() :
26         mockedUtils(
27             reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
28     {
29         ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>());
30         ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT)))
31             .WillByDefault(Return(any(PropertyType(true))));
32     }
33 
34     ~TestItemUpdater()
35     {
36         utils::freeUtils();
37     }
38 
39     auto& GetActivations()
40     {
41         return itemUpdater->activations;
42     }
43 
44     std::string getObjPath(const std::string& versionId)
45     {
46         return std::string(dBusPath) + "/" + versionId;
47     }
48 
49     void onPsuInventoryChanged(const std::string& psuPath,
50                                const Properties& properties)
51     {
52         itemUpdater->onPsuInventoryChanged(psuPath, properties);
53     }
54 
55     void scanDirectory(const fs::path& p)
56     {
57         itemUpdater->scanDirectory(p);
58     }
59 
60     static constexpr auto dBusPath = SOFTWARE_OBJPATH;
61     sdbusplus::SdBusMock sdbusMock;
62     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
63     const utils::MockedUtils& mockedUtils;
64     std::unique_ptr<ItemUpdater> itemUpdater;
65     Properties propAdded{{PRESENT, PropertyType(true)}};
66     Properties propRemoved{{PRESENT, PropertyType(false)}};
67     Properties propModel{{MODEL, PropertyType(std::string("dummyModel"))}};
68 };
69 
70 TEST_F(TestItemUpdater, ctordtor)
71 {
72     EXPECT_CALL(mockedUtils, getLatestVersion(_)).Times(1);
73     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
74 }
75 
76 TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent)
77 {
78     constexpr auto psuPath = "/com/example/inventory/psu0";
79     constexpr auto service = "com.example.Software.Psu";
80     constexpr auto version = "version0";
81     std::string objPath = getObjPath(version);
82     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
83         .WillOnce(Return(std::vector<std::string>({psuPath})));
84     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
85         .WillOnce(Return(service));
86     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
87         .WillOnce(Return(std::string(version)));
88     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
89                                              _, StrEq(PRESENT)))
90         .WillOnce(Return(any(PropertyType(false)))); // not present
91 
92     // The item updater itself
93     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
94         .Times(1);
95 
96     // No activation/version objects are created
97     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
98         .Times(0);
99     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
100 }
101 
102 TEST_F(TestItemUpdater, CreateOnePSUOnPresent)
103 {
104     constexpr auto psuPath = "/com/example/inventory/psu0";
105     constexpr auto service = "com.example.Software.Psu";
106     constexpr auto version = "version0";
107     std::string objPath = getObjPath(version);
108     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
109         .WillOnce(Return(std::vector<std::string>({psuPath})));
110     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
111         .WillOnce(Return(service));
112     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
113         .WillOnce(Return(std::string(version)));
114     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
115                                              _, StrEq(PRESENT)))
116         .WillOnce(Return(any(PropertyType(true)))); // present
117 
118     // The item updater itself
119     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
120         .Times(1);
121 
122     // activation and version object will be added
123     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
124         .Times(2);
125     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
126 }
127 
128 TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion)
129 {
130     constexpr auto psu0 = "/com/example/inventory/psu0";
131     constexpr auto psu1 = "/com/example/inventory/psu1";
132     constexpr auto service = "com.example.Software.Psu";
133     auto version0 = std::string("version0");
134     auto version1 = std::string("version0");
135     auto objPath0 = getObjPath(version0);
136     auto objPath1 = getObjPath(version1);
137 
138     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
139         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
140     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
141         .WillOnce(Return(service));
142     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
143         .WillOnce(Return(service));
144     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
145         .WillOnce(Return(std::string(version0)));
146     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
147                                              StrEq(PRESENT)))
148         .WillOnce(Return(any(PropertyType(true)))); // present
149     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
150         .WillOnce(Return(std::string(version1)));
151     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
152                                              StrEq(PRESENT)))
153         .WillOnce(Return(any(PropertyType(true)))); // present
154 
155     // The item updater itself
156     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
157         .Times(1);
158 
159     // activation and version object will be added
160     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
161         .Times(2);
162     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
163 
164     // Verify there is only one activation and it has two associations
165     const auto& activations = GetActivations();
166     EXPECT_EQ(1u, activations.size());
167     const auto& activation = activations.find(version0)->second;
168     const auto& assocs = activation->associations();
169     EXPECT_EQ(2u, assocs.size());
170     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
171     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
172 }
173 
174 TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion)
175 {
176     constexpr auto psu0 = "/com/example/inventory/psu0";
177     constexpr auto psu1 = "/com/example/inventory/psu1";
178     constexpr auto service = "com.example.Software.Psu";
179     auto version0 = std::string("version0");
180     auto version1 = std::string("version1");
181     auto objPath0 = getObjPath(version0);
182     auto objPath1 = getObjPath(version1);
183 
184     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
185         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
186     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
187         .WillOnce(Return(service));
188     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
189         .WillOnce(Return(service));
190     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
191         .WillOnce(Return(std::string(version0)));
192     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
193                                              StrEq(PRESENT)))
194         .WillOnce(Return(any(PropertyType(true)))); // present
195     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
196         .WillOnce(Return(std::string(version1)));
197     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
198                                              StrEq(PRESENT)))
199         .WillOnce(Return(any(PropertyType(true)))); // present
200 
201     // The item updater itself
202     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
203         .Times(1);
204 
205     // two new activation and version objects will be added
206     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
207         .Times(2);
208     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
209         .Times(2);
210     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
211 
212     // Verify there are two activations and each with one association
213     const auto& activations = GetActivations();
214     EXPECT_EQ(2u, activations.size());
215     const auto& activation0 = activations.find(version0)->second;
216     const auto& assocs0 = activation0->associations();
217     EXPECT_EQ(1u, assocs0.size());
218     EXPECT_EQ(psu0, std::get<2>(assocs0[0]));
219 
220     const auto& activation1 = activations.find(version1)->second;
221     const auto& assocs1 = activation1->associations();
222     EXPECT_EQ(1u, assocs1.size());
223     EXPECT_EQ(psu1, std::get<2>(assocs1[0]));
224 }
225 
226 TEST_F(TestItemUpdater, OnOnePSURemoved)
227 {
228     constexpr auto psuPath = "/com/example/inventory/psu0";
229     constexpr auto service = "com.example.Software.Psu";
230     constexpr auto version = "version0";
231     std::string objPath = getObjPath(version);
232     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
233         .WillOnce(Return(std::vector<std::string>({psuPath})));
234     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
235         .WillOnce(Return(service));
236     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
237         .WillOnce(Return(std::string(version)));
238     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
239                                              _, StrEq(PRESENT)))
240         .WillOnce(Return(any(PropertyType(true)))); // present
241 
242     // The item updater itself
243     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
244         .Times(1);
245 
246     // activation and version object will be added
247     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
248         .Times(2);
249     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
250 
251     // the activation and version object will be removed
252     Properties p{{PRESENT, PropertyType(false)}};
253     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
254         .Times(2);
255     onPsuInventoryChanged(psuPath, p);
256 
257     // on exit, item updater is removed
258     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
259         .Times(1);
260 }
261 
262 TEST_F(TestItemUpdater, OnOnePSUAdded)
263 {
264     constexpr auto psuPath = "/com/example/inventory/psu0";
265     constexpr auto service = "com.example.Software.Psu";
266     constexpr auto version = "version0";
267     std::string objPath = getObjPath(version);
268     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
269         .WillOnce(Return(std::vector<std::string>({psuPath})));
270     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
271         .WillOnce(Return(service));
272     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
273         .WillOnce(Return(std::string(version)));
274     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
275                                              _, StrEq(PRESENT)))
276         .WillOnce(Return(any(PropertyType(false)))); // not present
277 
278     // The item updater itself
279     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
280         .Times(1);
281 
282     // No activation/version objects are created
283     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
284         .Times(0);
285     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
286 
287     // The PSU is present and version is added in a single call
288     Properties propAddedAndModel{
289         {PRESENT, PropertyType(true)},
290         {MODEL, PropertyType(std::string("testModel"))}};
291     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
292         .WillOnce(Return(std::string(version)));
293     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
294         .Times(2);
295     onPsuInventoryChanged(psuPath, propAddedAndModel);
296 }
297 
298 TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithLatestVersion)
299 {
300     constexpr auto psuPath = "/com/example/inventory/psu0";
301     constexpr auto service = "com.example.Software.Psu";
302     constexpr auto version = "version0";
303     std::string objPath = getObjPath(version);
304     ON_CALL(mockedUtils, getPSUInventoryPath(_))
305         .WillByDefault(Return(std::vector<std::string>({psuPath})));
306     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
307         .WillOnce(Return(service));
308     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
309         .WillOnce(Return(std::string(version)));
310     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
311                                              _, StrEq(PRESENT)))
312         .WillOnce(Return(any(PropertyType(true)))); // present
313 
314     // The item updater itself
315     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
316         .Times(1);
317 
318     // activation and version object will be added
319     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
320         .Times(2);
321     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
322 
323     // the activation and version object will be removed
324     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
325         .Times(2);
326     onPsuInventoryChanged(psuPath, propRemoved);
327 
328     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
329         .WillOnce(Return(std::string(version)));
330     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
331         .Times(2);
332 
333     // On PSU inserted, it shall check if it's the latest version
334     std::set<std::string> expectedVersions = {version};
335     EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions)))
336         .WillOnce(Return(version));
337     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psuPath), _))
338         .WillOnce(Return(true));
339     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
340                                                           StrEq("StartUnit")))
341         .Times(0);
342     onPsuInventoryChanged(psuPath, propAdded);
343     onPsuInventoryChanged(psuPath, propModel);
344 
345     // on exit, objects are removed
346     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
347         .Times(2);
348     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
349         .Times(1);
350 }
351 
352 TEST_F(TestItemUpdater,
353        TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion)
354 {
355     constexpr auto psu0 = "/com/example/inventory/psu0";
356     constexpr auto psu1 = "/com/example/inventory/psu1";
357     constexpr auto service = "com.example.Software.Psu";
358     auto version0 = std::string("version0");
359     auto version1 = std::string("version0");
360     auto objPath0 = getObjPath(version0);
361     auto objPath1 = getObjPath(version1);
362 
363     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
364         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
365     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
366         .WillOnce(Return(service));
367     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
368         .WillOnce(Return(service));
369     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
370         .WillOnce(Return(std::string(version0)));
371     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
372                                              StrEq(PRESENT)))
373         .WillOnce(Return(any(PropertyType(true)))); // present
374     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
375         .WillOnce(Return(std::string(version1)));
376     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
377                                              StrEq(PRESENT)))
378         .WillOnce(Return(any(PropertyType(true)))); // present
379 
380     // The item updater itself
381     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
382         .Times(1);
383 
384     // activation and version object will be added
385     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
386         .Times(2);
387     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
388 
389     // Verify there is only one activation and it has two associations
390     const auto& activations = GetActivations();
391     EXPECT_EQ(1u, activations.size());
392     const auto& activation = activations.find(version0)->second;
393     auto assocs = activation->associations();
394     EXPECT_EQ(2u, assocs.size());
395     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
396     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
397 
398     // PSU0 is removed, only associations shall be updated
399     onPsuInventoryChanged(psu0, propRemoved);
400     assocs = activation->associations();
401     EXPECT_EQ(1u, assocs.size());
402     EXPECT_EQ(psu1, std::get<2>(assocs[0]));
403 
404     // PSU1 is removed, the activation and version object shall be removed
405     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
406         .Times(2);
407     onPsuInventoryChanged(psu1, propRemoved);
408 
409     // Add PSU0 and PSU1 back, but PSU1 with a different version
410     version1 = "version1";
411     objPath1 = getObjPath(version1);
412     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
413         .WillOnce(Return(std::string(version0)));
414     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
415         .WillOnce(Return(std::string(version1)));
416     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0)))
417         .Times(2);
418     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1)))
419         .Times(2);
420     onPsuInventoryChanged(psu0, propAdded);
421     onPsuInventoryChanged(psu1, propModel);
422     onPsuInventoryChanged(psu1, propAdded);
423     onPsuInventoryChanged(psu0, propModel);
424 
425     // on exit, objects are removed
426     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0)))
427         .Times(2);
428     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1)))
429         .Times(2);
430     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath)))
431         .Times(1);
432 }
433 
434 TEST_F(TestItemUpdater, scanDirOnNoPSU)
435 {
436     constexpr auto psuPath = "/com/example/inventory/psu0";
437     constexpr auto service = "com.example.Software.Psu";
438     constexpr auto version = "version0";
439     std::string objPath = getObjPath(version);
440     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
441         .WillOnce(Return(std::vector<std::string>({psuPath})));
442     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
443         .WillOnce(Return(service));
444     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
445         .WillOnce(Return(std::string(version)));
446     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
447                                              _, StrEq(PRESENT)))
448         .WillOnce(Return(any(PropertyType(false)))); // not present
449 
450     // The item updater itself
451     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
452         .Times(1);
453 
454     // No activation/version objects are created
455     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
456         .Times(0);
457     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
458 
459     // The valid image in test/psu-images-one-valid-one-invalid/model-1/
460     auto objPathValid = getObjPath("psu-test.v0.4");
461     auto objPathInvalid = getObjPath("psu-test.v0.5");
462     // activation and version object will be added on scan dir
463     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathValid)))
464         .Times(2);
465     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathInvalid)))
466         .Times(0);
467     scanDirectory("./psu-images-one-valid-one-invalid");
468 }
469 
470 TEST_F(TestItemUpdater, scanDirOnSamePSUVersion)
471 {
472     constexpr auto psuPath = "/com/example/inventory/psu0";
473     constexpr auto service = "com.example.Software.Psu";
474     constexpr auto version = "version0";
475     std::string objPath = getObjPath(version);
476     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
477         .WillOnce(Return(std::vector<std::string>({psuPath})));
478     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
479         .WillOnce(Return(service));
480     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
481         .WillOnce(Return(std::string(version)));
482     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
483                                              _, StrEq(PRESENT)))
484         .WillOnce(Return(any(PropertyType(true)))); // present
485 
486     // The item updater itself
487     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath)))
488         .Times(1);
489 
490     // activation and version object will be added
491     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
492         .Times(2);
493     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
494 
495     // The valid image in test/psu-images-valid-version0/model-3/ has the same
496     // version as the running PSU, so no objects will be created, but only the
497     // path will be set to the version object
498     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
499         .Times(0);
500     EXPECT_CALL(sdbusMock, sd_bus_emit_properties_changed_strv(
501                                _, StrEq(objPath),
502                                StrEq("xyz.openbmc_project.Common.FilePath"),
503                                Pointee(StrEq("Path"))))
504         .Times(1);
505     scanDirectory("./psu-images-valid-version0");
506 }
507 
508 TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithSameVersion)
509 {
510     // Simulate there are two PSUs with same version, and updated to a new
511     // version
512     constexpr auto psu0 = "/com/example/inventory/psu0";
513     constexpr auto psu1 = "/com/example/inventory/psu1";
514     constexpr auto service = "com.example.Software.Psu";
515     auto version0 = std::string("version0");
516     auto version1 = std::string("version0");
517     auto objPath0 = getObjPath(version0);
518     auto objPath1 = getObjPath(version1);
519 
520     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
521         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
522     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
523         .WillOnce(Return(service));
524     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
525         .WillOnce(Return(service));
526     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
527         .WillOnce(Return(std::string(version0)));
528     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
529                                              StrEq(PRESENT)))
530         .WillOnce(Return(any(PropertyType(true)))); // present
531     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
532         .WillOnce(Return(std::string(version1)));
533     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
534                                              StrEq(PRESENT)))
535         .WillOnce(Return(any(PropertyType(true)))); // present
536 
537     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
538 
539     std::string newVersionId = "NewVersionId";
540     AssociationList associations;
541     auto dummyActivation = std::make_unique<Activation>(
542         mockedBus, dBusPath, newVersionId, "", Activation::Status::Active,
543         associations, "", itemUpdater.get(), itemUpdater.get());
544 
545     // Now there is one activation and it has two associations
546     auto& activations = GetActivations();
547     activations.emplace(newVersionId, std::move(dummyActivation));
548     auto& activation = activations.find(version0)->second;
549     auto assocs = activation->associations();
550     EXPECT_EQ(2u, assocs.size());
551     EXPECT_EQ(psu0, std::get<2>(assocs[0]));
552     EXPECT_EQ(psu1, std::get<2>(assocs[1]));
553 
554     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
555         .WillOnce(Return(true));
556     itemUpdater->onUpdateDone(newVersionId, psu0);
557 
558     // Now the activation should have one assoiation
559     assocs = activation->associations();
560     EXPECT_EQ(1u, assocs.size());
561     EXPECT_EQ(psu1, std::get<2>(assocs[0]));
562 
563     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _))
564         .WillOnce(Return(true));
565     itemUpdater->onUpdateDone(newVersionId, psu1);
566 
567     // Now the activation shall be erased and only the dummy one is left
568     EXPECT_EQ(1u, activations.size());
569     EXPECT_NE(activations.find(newVersionId), activations.end());
570 }
571 
572 TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithDifferentVersion)
573 {
574     // Simulate there are two PSUs with different version, and updated to a new
575     // version
576     constexpr auto psu0 = "/com/example/inventory/psu0";
577     constexpr auto psu1 = "/com/example/inventory/psu1";
578     constexpr auto service = "com.example.Software.Psu";
579     auto version0 = std::string("version0");
580     auto version1 = std::string("version1");
581     auto objPath0 = getObjPath(version0);
582     auto objPath1 = getObjPath(version1);
583 
584     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_))
585         .WillOnce(Return(std::vector<std::string>({psu0, psu1})));
586     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _))
587         .WillOnce(Return(service));
588     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _))
589         .WillOnce(Return(service));
590     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0)))
591         .WillOnce(Return(std::string(version0)));
592     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
593                                              StrEq(PRESENT)))
594         .WillOnce(Return(any(PropertyType(true)))); // present
595     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1)))
596         .WillOnce(Return(std::string(version1)));
597     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
598                                              StrEq(PRESENT)))
599         .WillOnce(Return(any(PropertyType(true)))); // present
600 
601     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
602 
603     std::string newVersionId = "NewVersionId";
604     AssociationList associations;
605     auto dummyActivation = std::make_unique<Activation>(
606         mockedBus, dBusPath, newVersionId, "", Activation::Status::Active,
607         associations, "", itemUpdater.get(), itemUpdater.get());
608 
609     auto& activations = GetActivations();
610     activations.emplace(newVersionId, std::move(dummyActivation));
611 
612     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
613         .WillOnce(Return(true));
614 
615     // After psu0 is done, two activations should be left
616     itemUpdater->onUpdateDone(newVersionId, psu0);
617     EXPECT_EQ(2u, activations.size());
618     const auto& activation1 = activations.find(version1)->second;
619     const auto& assocs1 = activation1->associations();
620     EXPECT_EQ(1u, assocs1.size());
621     EXPECT_EQ(psu1, std::get<2>(assocs1[0]));
622 
623     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _))
624         .WillOnce(Return(true));
625     // After psu1 is done, only the dummy activation should be left
626     itemUpdater->onUpdateDone(newVersionId, psu1);
627     EXPECT_EQ(1u, activations.size());
628     EXPECT_NE(activations.find(newVersionId), activations.end());
629 }
630 
631 TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithOldVersion)
632 {
633     constexpr auto psuPath = "/com/example/inventory/psu0";
634     constexpr auto service = "com.example.Software.Psu";
635     constexpr auto version = "version0";
636     std::string versionId =
637         version; // In testing versionId is the same as version
638     std::string objPath = getObjPath(version);
639     ON_CALL(mockedUtils, getPSUInventoryPath(_))
640         .WillByDefault(Return(std::vector<std::string>({psuPath})));
641     ON_CALL(mockedUtils, getService(_, StrEq(psuPath), _))
642         .WillByDefault(Return(service));
643     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
644         .WillOnce(Return(std::string(version)));
645     ON_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _,
646                                          StrEq(PRESENT)))
647         .WillByDefault(Return(any(PropertyType(true)))); // present
648 
649     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath);
650 
651     // Add an association to simulate that it has image in BMC filesystem
652     auto& activation = GetActivations().find(versionId)->second;
653     auto assocs = activation->associations();
654     assocs.emplace_back(ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
655                         "SomePath");
656     activation->associations(assocs);
657     activation->path("SomeFilePath");
658 
659     onPsuInventoryChanged(psuPath, propRemoved);
660 
661     // On PSU inserted, it checks and finds a newer version
662     auto oldVersion = "old-version";
663     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath)))
664         .WillOnce(Return(std::string(oldVersion)));
665     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
666                                              _, StrEq(MANUFACTURER)))
667         .WillOnce(
668             Return(any(PropertyType(std::string(""))))); // Checking compatible
669     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
670                                              _, StrEq(MODEL)))
671         .WillOnce(
672             Return(any(PropertyType(std::string(""))))); // Checking compatible
673     std::set<std::string> expectedVersions = {version, oldVersion};
674     EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions)))
675         .WillOnce(Return(version));
676     ON_CALL(mockedUtils, isAssociated(StrEq(psuPath), _))
677         .WillByDefault(Return(false));
678     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
679                                                           StrEq("StartUnit")))
680         .Times(1);
681     onPsuInventoryChanged(psuPath, propAdded);
682     onPsuInventoryChanged(psuPath, propModel);
683 }
684