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