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