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