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