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