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