xref: /openbmc/phosphor-psu-code-mgmt/test/test_activation.cpp (revision b8590f56389a9dfd1af6ae4f722cc2fed46de918)
1 #include "activation.hpp"
2 #include "mocked_activation_listener.hpp"
3 #include "mocked_association_interface.hpp"
4 #include "mocked_utils.hpp"
5 
6 #include <sdbusplus/test/sdbus_mock.hpp>
7 
8 #include <gmock/gmock.h>
9 #include <gtest/gtest.h>
10 
11 using namespace phosphor::software::updater;
12 
13 using ::testing::_;
14 using ::testing::NiceMock;
15 using ::testing::Return;
16 using ::testing::StrEq;
17 
18 using std::any;
19 
20 class TestActivation : public ::testing::Test
21 {
22   public:
23     using PropertyType = utils::UtilsInterface::PropertyType;
24     using Status = Activation::Status;
25     using RequestedStatus = Activation::RequestedActivations;
26 
27     TestActivation(const TestActivation&) = delete;
28     TestActivation& operator=(const TestActivation&) = delete;
29     TestActivation(TestActivation&&) = delete;
30     TestActivation& operator=(TestActivation&&) = delete;
31 
TestActivation()32     TestActivation() :
33         mockedUtils(
34             reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
35     {
36         // By default make PSU present
37         ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT)))
38             .WillByDefault(Return(any(PropertyType(true))));
39 
40         // By default make PSU compatible with the test software
41         ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
42             .WillByDefault(Return(any(PropertyType(std::string("TestManu")))));
43         ON_CALL(mockedUtils, getModel(_))
44             .WillByDefault(Return(std::string("TestModel")));
45         ON_CALL(mockedUtils, isAssociated(_, _)).WillByDefault(Return(false));
46     }
~TestActivation()47     ~TestActivation() override
48     {
49         utils::freeUtils();
50     }
51 
onUpdateDone() const52     void onUpdateDone() const
53     {
54         activation->onUpdateDone();
55     }
onUpdateFailed() const56     void onUpdateFailed() const
57     {
58         activation->onUpdateFailed();
59     }
getProgress() const60     int getProgress() const
61     {
62         return activation->activationProgress->progress();
63     }
getPsuQueue() const64     const auto& getPsuQueue() const
65     {
66         return activation->psuQueue;
67     }
getUpdateService(const std::string & psuInventoryPath) const68     std::string getUpdateService(const std::string& psuInventoryPath) const
69     {
70         return activation->getUpdateService(psuInventoryPath);
71     }
72 
73     NiceMock<sdbusplus::SdBusMock> sdbusMock;
74     sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
75     const utils::MockedUtils& mockedUtils;
76     MockedAssociationInterface mockedAssociationInterface;
77     MockedActivationListener mockedActivationListener;
78     std::unique_ptr<Activation> activation;
79     std::string versionId = "abcdefgh";
80     std::string extVersion = "manufacturer=TestManu,model=TestModel";
81     std::string filePath = "/tmp/images/abcdefgh";
82     std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId;
83     Status status = Status::Ready;
84     AssociationList associations;
85 };
86 
TEST_F(TestActivation,ctordtor)87 TEST_F(TestActivation, ctordtor)
88 {
89     activation = std::make_unique<Activation>(
90         mockedBus, dBusPath, versionId, extVersion, status, associations,
91         filePath, &mockedAssociationInterface, &mockedActivationListener);
92 }
93 
TEST_F(TestActivation,ctorWithInvalidExtVersion)94 TEST_F(TestActivation, ctorWithInvalidExtVersion)
95 {
96     extVersion = "invalid text";
97     activation = std::make_unique<Activation>(
98         mockedBus, dBusPath, versionId, extVersion, status, associations,
99         filePath, &mockedAssociationInterface, &mockedActivationListener);
100 }
101 
TEST_F(TestActivation,getUpdateService)102 TEST_F(TestActivation, getUpdateService)
103 {
104     std::string psuInventoryPath = "/com/example/inventory/powersupply1";
105     std::string toCompare = "psu-update@-com-example-inventory-"
106                             "powersupply1\\x20-tmp-images-12345678.service";
107     versionId = "12345678";
108     filePath = "/tmp/images/12345678";
109 
110     activation = std::make_unique<Activation>(
111         mockedBus, dBusPath, versionId, extVersion, status, associations,
112         filePath, &mockedAssociationInterface, &mockedActivationListener);
113 
114     auto service = getUpdateService(psuInventoryPath);
115     EXPECT_EQ(toCompare, service);
116 }
117 
TEST_F(TestActivation,doUpdateWhenNoPSU)118 TEST_F(TestActivation, doUpdateWhenNoPSU)
119 {
120     activation = std::make_unique<Activation>(
121         mockedBus, dBusPath, versionId, extVersion, status, associations,
122         filePath, &mockedAssociationInterface, &mockedActivationListener);
123     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
124         .WillByDefault(
125             Return(std::vector<std::string>({}))); // No PSU inventory
126     activation->requestedActivation(RequestedStatus::Active);
127 
128     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
129         .Times(0);
130     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
131         .Times(0);
132     EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
133         .Times(0);
134     EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
135     EXPECT_EQ(Status::Failed, activation->activation());
136 }
137 
TEST_F(TestActivation,doUpdateOnePSUOK)138 TEST_F(TestActivation, doUpdateOnePSUOK)
139 {
140     constexpr auto psu0 = "/com/example/inventory/psu0";
141     activation = std::make_unique<Activation>(
142         mockedBus, dBusPath, versionId, extVersion, status, associations,
143         filePath, &mockedAssociationInterface, &mockedActivationListener);
144     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
145         .WillByDefault(
146             Return(std::vector<std::string>({psu0}))); // One PSU inventory
147     activation->requestedActivation(RequestedStatus::Active);
148 
149     EXPECT_EQ(Status::Activating, activation->activation());
150 
151     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
152         .Times(1);
153     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
154         .Times(1);
155     EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
156         .Times(1);
157     EXPECT_CALL(mockedActivationListener,
158                 onUpdateDone(StrEq(versionId), StrEq(psu0)))
159         .Times(1);
160     onUpdateDone();
161     EXPECT_EQ(Status::Active, activation->activation());
162 }
163 
TEST_F(TestActivation,doUpdateFourPSUsOK)164 TEST_F(TestActivation, doUpdateFourPSUsOK)
165 {
166     constexpr auto psu0 = "/com/example/inventory/psu0";
167     constexpr auto psu1 = "/com/example/inventory/psu1";
168     constexpr auto psu2 = "/com/example/inventory/psu2";
169     constexpr auto psu3 = "/com/example/inventory/psu3";
170     activation = std::make_unique<Activation>(
171         mockedBus, dBusPath, versionId, extVersion, status, associations,
172         filePath, &mockedAssociationInterface, &mockedActivationListener);
173     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
174         .WillByDefault(Return(
175             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
176     activation->requestedActivation(RequestedStatus::Active);
177 
178     EXPECT_EQ(Status::Activating, activation->activation());
179     EXPECT_EQ(10, getProgress());
180 
181     EXPECT_CALL(mockedActivationListener,
182                 onUpdateDone(StrEq(versionId), StrEq(psu0)))
183         .Times(1);
184     onUpdateDone();
185     EXPECT_EQ(Status::Activating, activation->activation());
186     EXPECT_EQ(30, getProgress());
187 
188     EXPECT_CALL(mockedActivationListener,
189                 onUpdateDone(StrEq(versionId), StrEq(psu1)))
190         .Times(1);
191     onUpdateDone();
192     EXPECT_EQ(Status::Activating, activation->activation());
193     EXPECT_EQ(50, getProgress());
194 
195     EXPECT_CALL(mockedActivationListener,
196                 onUpdateDone(StrEq(versionId), StrEq(psu2)))
197         .Times(1);
198     onUpdateDone();
199     EXPECT_EQ(Status::Activating, activation->activation());
200     EXPECT_EQ(70, getProgress());
201 
202     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
203         .Times(1);
204     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
205         .Times(1);
206     EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
207         .Times(1);
208 
209     EXPECT_CALL(mockedActivationListener,
210                 onUpdateDone(StrEq(versionId), StrEq(psu3)))
211         .Times(1);
212     onUpdateDone();
213     EXPECT_EQ(Status::Active, activation->activation());
214 }
215 
TEST_F(TestActivation,doUpdateFourPSUsFailonSecond)216 TEST_F(TestActivation, doUpdateFourPSUsFailonSecond)
217 {
218     constexpr auto psu0 = "/com/example/inventory/psu0";
219     constexpr auto psu1 = "/com/example/inventory/psu1";
220     constexpr auto psu2 = "/com/example/inventory/psu2";
221     constexpr auto psu3 = "/com/example/inventory/psu3";
222     activation = std::make_unique<Activation>(
223         mockedBus, dBusPath, versionId, extVersion, status, associations,
224         filePath, &mockedAssociationInterface, &mockedActivationListener);
225     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
226         .WillByDefault(Return(
227             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
228     activation->requestedActivation(RequestedStatus::Active);
229 
230     EXPECT_EQ(Status::Activating, activation->activation());
231     EXPECT_EQ(10, getProgress());
232 
233     EXPECT_CALL(mockedActivationListener,
234                 onUpdateDone(StrEq(versionId), StrEq(psu0)))
235         .Times(1);
236     onUpdateDone();
237     EXPECT_EQ(Status::Activating, activation->activation());
238     EXPECT_EQ(30, getProgress());
239 
240     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
241         .Times(0);
242     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
243         .Times(0);
244     EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
245         .Times(0);
246     EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
247     onUpdateFailed();
248     EXPECT_EQ(Status::Failed, activation->activation());
249 }
250 
TEST_F(TestActivation,doUpdateOnExceptionFromDbus)251 TEST_F(TestActivation, doUpdateOnExceptionFromDbus)
252 {
253     constexpr auto psu0 = "/com/example/inventory/psu0";
254     activation = std::make_unique<Activation>(
255         mockedBus, dBusPath, versionId, extVersion, status, associations,
256         filePath, &mockedAssociationInterface, &mockedActivationListener);
257     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
258         .WillByDefault(
259             Return(std::vector<std::string>({psu0}))); // One PSU inventory
260     ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr))
261         .WillByDefault(Return(-1)); // Make sdbus call failure
262     activation->requestedActivation(RequestedStatus::Active);
263 
264     EXPECT_EQ(Status::Failed, activation->activation());
265 }
266 
TEST_F(TestActivation,doUpdateOnePSUNotPresent)267 TEST_F(TestActivation, doUpdateOnePSUNotPresent)
268 {
269     constexpr auto psu0 = "/com/example/inventory/psu0";
270     activation = std::make_unique<Activation>(
271         mockedBus, dBusPath, versionId, extVersion, status, associations,
272         filePath, &mockedAssociationInterface, &mockedActivationListener);
273     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
274         .WillByDefault(Return(std::vector<std::string>({psu0})));
275     ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT)))
276         .WillByDefault(Return(any(PropertyType(false)))); // not present
277     activation->requestedActivation(RequestedStatus::Active);
278 
279     EXPECT_EQ(Status::Ready, activation->activation());
280 }
281 
TEST_F(TestActivation,doUpdateOnePSUModelNotCompatible)282 TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible)
283 {
284     constexpr auto psu0 = "/com/example/inventory/psu0";
285     extVersion = "manufacturer=TestManu,model=DifferentModel";
286     activation = std::make_unique<Activation>(
287         mockedBus, dBusPath, versionId, extVersion, status, associations,
288         filePath, &mockedAssociationInterface, &mockedActivationListener);
289     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
290         .WillByDefault(Return(std::vector<std::string>({psu0})));
291     activation->requestedActivation(RequestedStatus::Active);
292 
293     EXPECT_EQ(Status::Ready, activation->activation());
294 }
295 
TEST_F(TestActivation,doUpdateOnePSUManufactureNotCompatible)296 TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible)
297 {
298     constexpr auto psu0 = "/com/example/inventory/psu0";
299     extVersion = "manufacturer=DifferentManu,model=TestModel";
300     activation = std::make_unique<Activation>(
301         mockedBus, dBusPath, versionId, extVersion, status, associations,
302         filePath, &mockedAssociationInterface, &mockedActivationListener);
303     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
304         .WillByDefault(Return(std::vector<std::string>({psu0})));
305     activation->requestedActivation(RequestedStatus::Active);
306 
307     EXPECT_EQ(Status::Ready, activation->activation());
308 }
309 
TEST_F(TestActivation,doUpdateOnePSUSelfManufactureIsEmpty)310 TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty)
311 {
312     ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
313         .WillByDefault(Return(any(PropertyType(std::string("")))));
314     extVersion = "manufacturer=AnyManu,model=TestModel";
315     // Below is the same as doUpdateOnePSUOK case
316     constexpr auto psu0 = "/com/example/inventory/psu0";
317     activation = std::make_unique<Activation>(
318         mockedBus, dBusPath, versionId, extVersion, status, associations,
319         filePath, &mockedAssociationInterface, &mockedActivationListener);
320     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
321         .WillByDefault(
322             Return(std::vector<std::string>({psu0}))); // One PSU inventory
323     activation->requestedActivation(RequestedStatus::Active);
324 
325     EXPECT_EQ(Status::Activating, activation->activation());
326 
327     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
328         .Times(1);
329     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
330         .Times(1);
331     EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
332         .Times(1);
333     onUpdateDone();
334     EXPECT_EQ(Status::Active, activation->activation());
335 }
336 
TEST_F(TestActivation,doUpdateFourPSUsSecondPSUNotCompatible)337 TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible)
338 {
339     constexpr auto psu0 = "/com/example/inventory/psu0";
340     constexpr auto psu1 = "/com/example/inventory/psu1";
341     constexpr auto psu2 = "/com/example/inventory/psu2";
342     constexpr auto psu3 = "/com/example/inventory/psu3";
343     ON_CALL(mockedUtils, getModel(StrEq(psu1)))
344         .WillByDefault(Return(std::string("DifferentModel")));
345     activation = std::make_unique<Activation>(
346         mockedBus, dBusPath, versionId, extVersion, status, associations,
347         filePath, &mockedAssociationInterface, &mockedActivationListener);
348     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
349         .WillByDefault(Return(
350             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
351     activation->requestedActivation(RequestedStatus::Active);
352 
353     const auto& psuQueue = getPsuQueue();
354     EXPECT_EQ(3U, psuQueue.size());
355 
356     // Only 3 PSUs shall be updated, and psu1 shall be skipped
357     EXPECT_EQ(Status::Activating, activation->activation());
358     EXPECT_EQ(10, getProgress());
359 
360     onUpdateDone();
361     EXPECT_EQ(Status::Activating, activation->activation());
362     EXPECT_EQ(36, getProgress());
363 
364     onUpdateDone();
365     EXPECT_EQ(Status::Activating, activation->activation());
366     EXPECT_EQ(62, getProgress());
367 
368     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
369         .Times(1);
370     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
371         .Times(1);
372     EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
373         .Times(1);
374 
375     onUpdateDone();
376     EXPECT_EQ(Status::Active, activation->activation());
377 }
378 
TEST_F(TestActivation,doUpdateWhenNoFilePathInActiveState)379 TEST_F(TestActivation, doUpdateWhenNoFilePathInActiveState)
380 {
381     filePath = "";
382     status = Status::Active; // Typically, a running PSU software is active
383                              // without file path
384     constexpr auto psu0 = "/com/example/inventory/psu0";
385     activation = std::make_unique<Activation>(
386         mockedBus, dBusPath, versionId, extVersion, status, associations,
387         filePath, &mockedAssociationInterface, &mockedActivationListener);
388 
389     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
390         .WillByDefault(
391             Return(std::vector<std::string>({psu0}))); // One PSU inventory
392 
393     // There shall be no DBus call to start update service
394     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
395                                                           StrEq("StartUnit")))
396         .Times(0);
397 
398     activation->requestedActivation(RequestedStatus::Active);
399     EXPECT_EQ(Status::Active, activation->activation());
400 }
401 
TEST_F(TestActivation,doUpdateWhenNoFilePathInReadyState)402 TEST_F(TestActivation, doUpdateWhenNoFilePathInReadyState)
403 {
404     filePath = "";
405     status = Status::Ready; // Usually a Ready activation should have file path,
406                             // but we are testing this case as well
407     constexpr auto psu0 = "/com/example/inventory/psu0";
408     activation = std::make_unique<Activation>(
409         mockedBus, dBusPath, versionId, extVersion, status, associations,
410         filePath, &mockedAssociationInterface, &mockedActivationListener);
411 
412     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
413         .WillByDefault(
414             Return(std::vector<std::string>({psu0}))); // One PSU inventory
415 
416     // There shall be no DBus call to start update service
417     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
418                                                           StrEq("StartUnit")))
419         .Times(0);
420 
421     activation->requestedActivation(RequestedStatus::Active);
422     EXPECT_EQ(Status::Ready, activation->activation());
423 }
424 
TEST_F(TestActivation,doUpdateWhenPSUIsAssociated)425 TEST_F(TestActivation, doUpdateWhenPSUIsAssociated)
426 {
427     constexpr auto psu0 = "/com/example/inventory/psu0";
428     status = Status::Active; // Typically, a running PSU software is associated
429     activation = std::make_unique<Activation>(
430         mockedBus, dBusPath, versionId, extVersion, status, associations,
431         filePath, &mockedAssociationInterface, &mockedActivationListener);
432 
433     ON_CALL(mockedUtils, getPSUInventoryPaths(_))
434         .WillByDefault(
435             Return(std::vector<std::string>({psu0}))); // One PSU inventory
436 
437     // When PSU is already associated, there shall be no DBus call to start
438     // update service
439     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
440         .WillOnce(Return(true));
441     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
442                                                           StrEq("StartUnit")))
443         .Times(0);
444 
445     activation->requestedActivation(RequestedStatus::Active);
446     EXPECT_EQ(Status::Active, activation->activation());
447 }
448