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