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