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