blob: a7ce5423f618a3f80e68f7fc1818457e38de02c2 [file] [log] [blame]
Lei YU12c9f4c2019-09-11 15:08:15 +08001#include "activation.hpp"
Lei YUffb36532019-10-15 13:55:24 +08002#include "mocked_activation_listener.hpp"
Lei YU7f2a2152019-09-16 16:50:18 +08003#include "mocked_association_interface.hpp"
Lei YUff83c2a2019-09-12 13:55:18 +08004#include "mocked_utils.hpp"
Lei YU12c9f4c2019-09-11 15:08:15 +08005
6#include <sdbusplus/test/sdbus_mock.hpp>
7
8#include <gmock/gmock.h>
9#include <gtest/gtest.h>
10
11using namespace phosphor::software::updater;
12
Lei YUff83c2a2019-09-12 13:55:18 +080013using ::testing::_;
14using ::testing::Return;
Lei YU9edb7332019-09-19 14:46:19 +080015using ::testing::StrEq;
16
17using std::experimental::any;
Lei YUff83c2a2019-09-12 13:55:18 +080018
Lei YU12c9f4c2019-09-11 15:08:15 +080019class TestActivation : public ::testing::Test
20{
21 public:
Lei YU9edb7332019-09-19 14:46:19 +080022 using PropertyType = utils::UtilsInterface::PropertyType;
Lei YUff83c2a2019-09-12 13:55:18 +080023 using Status = Activation::Status;
24 using RequestedStatus = Activation::RequestedActivations;
25 TestActivation() :
26 mockedUtils(
27 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
Lei YU12c9f4c2019-09-11 15:08:15 +080028 {
Lei YU9edb7332019-09-19 14:46:19 +080029 // By default make it compatible with the test software
30 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
31 .WillByDefault(Return(any(PropertyType(std::string("TestManu")))));
32 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL)))
33 .WillByDefault(Return(any(PropertyType(std::string("TestModel")))));
Lei YU63f9e712019-10-12 15:16:55 +080034 ON_CALL(mockedUtils, isAssociated(_, _)).WillByDefault(Return(false));
Lei YU12c9f4c2019-09-11 15:08:15 +080035 }
36 ~TestActivation()
37 {
Lei YUc09155b2019-10-11 17:30:48 +080038 utils::freeUtils();
Lei YU12c9f4c2019-09-11 15:08:15 +080039 }
Lei YUff83c2a2019-09-12 13:55:18 +080040
41 void onUpdateDone()
42 {
43 activation->onUpdateDone();
44 }
45 void onUpdateFailed()
46 {
47 activation->onUpdateFailed();
48 }
49 int getProgress()
50 {
51 return activation->activationProgress->progress();
52 }
Lei YU9edb7332019-09-19 14:46:19 +080053 const auto& getPsuQueue()
54 {
55 return activation->psuQueue;
56 }
Lei YUe8945ea2019-09-29 17:25:31 +080057 std::string getUpdateService(const std::string& psuInventoryPath)
58 {
59 return activation->getUpdateService(psuInventoryPath);
60 }
Lei YU9edb7332019-09-19 14:46:19 +080061
Lei YU12c9f4c2019-09-11 15:08:15 +080062 sdbusplus::SdBusMock sdbusMock;
63 sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
Lei YUff83c2a2019-09-12 13:55:18 +080064 const utils::MockedUtils& mockedUtils;
Lei YU7f2a2152019-09-16 16:50:18 +080065 MockedAssociationInterface mockedAssociationInterface;
Lei YUffb36532019-10-15 13:55:24 +080066 MockedActivationListener mockedActivationListener;
Lei YU12c9f4c2019-09-11 15:08:15 +080067 std::unique_ptr<Activation> activation;
68 std::string versionId = "abcdefgh";
Lei YU9edb7332019-09-19 14:46:19 +080069 std::string extVersion = "manufacturer=TestManu,model=TestModel";
Lei YU63f9e712019-10-12 15:16:55 +080070 std::string filePath = "/tmp/images/abcdefgh";
Lei YU7f2a2152019-09-16 16:50:18 +080071 std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId;
Lei YUff83c2a2019-09-12 13:55:18 +080072 Status status = Status::Ready;
Lei YU12c9f4c2019-09-11 15:08:15 +080073 AssociationList associations;
74};
75
76TEST_F(TestActivation, ctordtor)
77{
Lei YU99301372019-09-29 16:27:12 +080078 activation = std::make_unique<Activation>(
79 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +080080 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU12c9f4c2019-09-11 15:08:15 +080081}
82
Lei YU58c26e32019-09-27 17:52:06 +080083TEST_F(TestActivation, ctorWithInvalidExtVersion)
84{
85 extVersion = "invalid text";
86 activation = std::make_unique<Activation>(
87 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +080088 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU58c26e32019-09-27 17:52:06 +080089}
90
Lei YU12c9f4c2019-09-11 15:08:15 +080091TEST_F(TestActivation, getUpdateService)
92{
93 std::string psuInventoryPath = "/com/example/inventory/powersupply1";
Lei YU12c9f4c2019-09-11 15:08:15 +080094 std::string toCompare = "psu-update@-com-example-inventory-"
95 "powersupply1\\x20-tmp-images-12345678.service";
Lei YUe8945ea2019-09-29 17:25:31 +080096 versionId = "12345678";
97 filePath = "/tmp/images/12345678";
Lei YU12c9f4c2019-09-11 15:08:15 +080098
Lei YUe8945ea2019-09-29 17:25:31 +080099 activation = std::make_unique<Activation>(
100 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800101 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YUe8945ea2019-09-29 17:25:31 +0800102
103 auto service = getUpdateService(psuInventoryPath);
Lei YU12c9f4c2019-09-11 15:08:15 +0800104 EXPECT_EQ(toCompare, service);
105}
Lei YUff83c2a2019-09-12 13:55:18 +0800106
107TEST_F(TestActivation, doUpdateWhenNoPSU)
108{
Lei YU99301372019-09-29 16:27:12 +0800109 activation = std::make_unique<Activation>(
110 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800111 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YUff83c2a2019-09-12 13:55:18 +0800112 ON_CALL(mockedUtils, getPSUInventoryPath(_))
113 .WillByDefault(
114 Return(std::vector<std::string>({}))); // No PSU inventory
115 activation->requestedActivation(RequestedStatus::Active);
116
Lei YU7f2a2152019-09-16 16:50:18 +0800117 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
118 .Times(0);
119 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
120 .Times(0);
Lei YUffb36532019-10-15 13:55:24 +0800121 EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
Lei YUff83c2a2019-09-12 13:55:18 +0800122 EXPECT_EQ(Status::Failed, activation->activation());
123}
124
125TEST_F(TestActivation, doUpdateOnePSUOK)
126{
127 constexpr auto psu0 = "/com/example/inventory/psu0";
Lei YU99301372019-09-29 16:27:12 +0800128 activation = std::make_unique<Activation>(
129 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800130 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YUff83c2a2019-09-12 13:55:18 +0800131 ON_CALL(mockedUtils, getPSUInventoryPath(_))
132 .WillByDefault(
133 Return(std::vector<std::string>({psu0}))); // One PSU inventory
134 activation->requestedActivation(RequestedStatus::Active);
135
136 EXPECT_EQ(Status::Activating, activation->activation());
137
Lei YU7f2a2152019-09-16 16:50:18 +0800138 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
139 .Times(1);
140 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
141 .Times(1);
Lei YUffb36532019-10-15 13:55:24 +0800142 EXPECT_CALL(mockedActivationListener,
143 onUpdateDone(StrEq(versionId), StrEq(psu0)))
144 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800145 onUpdateDone();
146 EXPECT_EQ(Status::Active, activation->activation());
147}
148
149TEST_F(TestActivation, doUpdateFourPSUsOK)
150{
151 constexpr auto psu0 = "/com/example/inventory/psu0";
152 constexpr auto psu1 = "/com/example/inventory/psu1";
153 constexpr auto psu2 = "/com/example/inventory/psu2";
154 constexpr auto psu3 = "/com/example/inventory/psu3";
Lei YU99301372019-09-29 16:27:12 +0800155 activation = std::make_unique<Activation>(
156 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800157 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YUff83c2a2019-09-12 13:55:18 +0800158 ON_CALL(mockedUtils, getPSUInventoryPath(_))
159 .WillByDefault(Return(
160 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
161 activation->requestedActivation(RequestedStatus::Active);
162
163 EXPECT_EQ(Status::Activating, activation->activation());
164 EXPECT_EQ(10, getProgress());
165
Lei YUffb36532019-10-15 13:55:24 +0800166 EXPECT_CALL(mockedActivationListener,
167 onUpdateDone(StrEq(versionId), StrEq(psu0)))
168 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800169 onUpdateDone();
170 EXPECT_EQ(Status::Activating, activation->activation());
171 EXPECT_EQ(30, getProgress());
172
Lei YUffb36532019-10-15 13:55:24 +0800173 EXPECT_CALL(mockedActivationListener,
174 onUpdateDone(StrEq(versionId), StrEq(psu1)))
175 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800176 onUpdateDone();
177 EXPECT_EQ(Status::Activating, activation->activation());
178 EXPECT_EQ(50, getProgress());
179
Lei YUffb36532019-10-15 13:55:24 +0800180 EXPECT_CALL(mockedActivationListener,
181 onUpdateDone(StrEq(versionId), StrEq(psu2)))
182 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800183 onUpdateDone();
184 EXPECT_EQ(Status::Activating, activation->activation());
185 EXPECT_EQ(70, getProgress());
186
Lei YU7f2a2152019-09-16 16:50:18 +0800187 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
188 .Times(1);
189 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
190 .Times(1);
191
Lei YUffb36532019-10-15 13:55:24 +0800192 EXPECT_CALL(mockedActivationListener,
193 onUpdateDone(StrEq(versionId), StrEq(psu3)))
194 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800195 onUpdateDone();
196 EXPECT_EQ(Status::Active, activation->activation());
197}
198
199TEST_F(TestActivation, doUpdateFourPSUsFailonSecond)
200{
201 constexpr auto psu0 = "/com/example/inventory/psu0";
202 constexpr auto psu1 = "/com/example/inventory/psu1";
203 constexpr auto psu2 = "/com/example/inventory/psu2";
204 constexpr auto psu3 = "/com/example/inventory/psu3";
Lei YU99301372019-09-29 16:27:12 +0800205 activation = std::make_unique<Activation>(
206 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800207 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YUff83c2a2019-09-12 13:55:18 +0800208 ON_CALL(mockedUtils, getPSUInventoryPath(_))
209 .WillByDefault(Return(
210 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
211 activation->requestedActivation(RequestedStatus::Active);
212
213 EXPECT_EQ(Status::Activating, activation->activation());
214 EXPECT_EQ(10, getProgress());
215
Lei YUffb36532019-10-15 13:55:24 +0800216 EXPECT_CALL(mockedActivationListener,
217 onUpdateDone(StrEq(versionId), StrEq(psu0)))
218 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800219 onUpdateDone();
220 EXPECT_EQ(Status::Activating, activation->activation());
221 EXPECT_EQ(30, getProgress());
222
Lei YU7f2a2152019-09-16 16:50:18 +0800223 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
224 .Times(0);
225 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
226 .Times(0);
Lei YUffb36532019-10-15 13:55:24 +0800227 EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
Lei YUff83c2a2019-09-12 13:55:18 +0800228 onUpdateFailed();
229 EXPECT_EQ(Status::Failed, activation->activation());
230}
231
232TEST_F(TestActivation, doUpdateOnExceptionFromDbus)
233{
234 constexpr auto psu0 = "/com/example/inventory/psu0";
Lei YU99301372019-09-29 16:27:12 +0800235 activation = std::make_unique<Activation>(
236 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800237 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YUff83c2a2019-09-12 13:55:18 +0800238 ON_CALL(mockedUtils, getPSUInventoryPath(_))
239 .WillByDefault(
240 Return(std::vector<std::string>({psu0}))); // One PSU inventory
241 ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr))
242 .WillByDefault(Return(-1)); // Make sdbus call failure
243 activation->requestedActivation(RequestedStatus::Active);
244
245 EXPECT_EQ(Status::Failed, activation->activation());
246}
Lei YU9edb7332019-09-19 14:46:19 +0800247
248TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible)
249{
250 constexpr auto psu0 = "/com/example/inventory/psu0";
251 extVersion = "manufacturer=TestManu,model=DifferentModel";
Lei YU99301372019-09-29 16:27:12 +0800252 activation = std::make_unique<Activation>(
253 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800254 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU9edb7332019-09-19 14:46:19 +0800255 ON_CALL(mockedUtils, getPSUInventoryPath(_))
256 .WillByDefault(Return(std::vector<std::string>({psu0})));
257 activation->requestedActivation(RequestedStatus::Active);
258
Lei YU63f9e712019-10-12 15:16:55 +0800259 EXPECT_EQ(Status::Ready, activation->activation());
Lei YU9edb7332019-09-19 14:46:19 +0800260}
261
262TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible)
263{
264 constexpr auto psu0 = "/com/example/inventory/psu0";
265 extVersion = "manufacturer=DifferentManu,model=TestModel";
Lei YU99301372019-09-29 16:27:12 +0800266 activation = std::make_unique<Activation>(
267 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800268 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU9edb7332019-09-19 14:46:19 +0800269 ON_CALL(mockedUtils, getPSUInventoryPath(_))
270 .WillByDefault(Return(std::vector<std::string>({psu0})));
271 activation->requestedActivation(RequestedStatus::Active);
272
Lei YU63f9e712019-10-12 15:16:55 +0800273 EXPECT_EQ(Status::Ready, activation->activation());
Lei YU9edb7332019-09-19 14:46:19 +0800274}
275
276TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty)
277{
278 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
279 .WillByDefault(Return(any(PropertyType(std::string("")))));
280 extVersion = "manufacturer=AnyManu,model=TestModel";
281 // Below is the same as doUpdateOnePSUOK case
282 constexpr auto psu0 = "/com/example/inventory/psu0";
Lei YU99301372019-09-29 16:27:12 +0800283 activation = std::make_unique<Activation>(
284 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800285 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU9edb7332019-09-19 14:46:19 +0800286 ON_CALL(mockedUtils, getPSUInventoryPath(_))
287 .WillByDefault(
288 Return(std::vector<std::string>({psu0}))); // One PSU inventory
289 activation->requestedActivation(RequestedStatus::Active);
290
291 EXPECT_EQ(Status::Activating, activation->activation());
292
293 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
294 .Times(1);
295 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
296 .Times(1);
297 onUpdateDone();
298 EXPECT_EQ(Status::Active, activation->activation());
299}
300
301TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible)
302{
303 constexpr auto psu0 = "/com/example/inventory/psu0";
304 constexpr auto psu1 = "/com/example/inventory/psu1";
305 constexpr auto psu2 = "/com/example/inventory/psu2";
306 constexpr auto psu3 = "/com/example/inventory/psu3";
307 ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL)))
308 .WillByDefault(
309 Return(any(PropertyType(std::string("DifferentModel")))));
Lei YU99301372019-09-29 16:27:12 +0800310 activation = std::make_unique<Activation>(
311 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800312 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU9edb7332019-09-19 14:46:19 +0800313 ON_CALL(mockedUtils, getPSUInventoryPath(_))
314 .WillByDefault(Return(
315 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
316 activation->requestedActivation(RequestedStatus::Active);
317
318 const auto& psuQueue = getPsuQueue();
319 EXPECT_EQ(3u, psuQueue.size());
320
321 // Only 3 PSUs shall be updated, and psu1 shall be skipped
322 EXPECT_EQ(Status::Activating, activation->activation());
323 EXPECT_EQ(10, getProgress());
324
325 onUpdateDone();
326 EXPECT_EQ(Status::Activating, activation->activation());
327 EXPECT_EQ(36, getProgress());
328
329 onUpdateDone();
330 EXPECT_EQ(Status::Activating, activation->activation());
331 EXPECT_EQ(62, getProgress());
332
333 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
334 .Times(1);
335 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
336 .Times(1);
337
338 onUpdateDone();
339 EXPECT_EQ(Status::Active, activation->activation());
340}
Lei YU63f9e712019-10-12 15:16:55 +0800341
342TEST_F(TestActivation, doUpdateWhenNoFilePathInActiveState)
343{
344 filePath = "";
345 status = Status::Active; // Typically, a running PSU software is active
346 // without file path
347 constexpr auto psu0 = "/com/example/inventory/psu0";
348 activation = std::make_unique<Activation>(
349 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800350 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU63f9e712019-10-12 15:16:55 +0800351
352 ON_CALL(mockedUtils, getPSUInventoryPath(_))
353 .WillByDefault(
354 Return(std::vector<std::string>({psu0}))); // One PSU inventory
355
356 // There shall be no DBus call to start update service
357 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
358 StrEq("StartUnit")))
359 .Times(0);
360
361 activation->requestedActivation(RequestedStatus::Active);
362 EXPECT_EQ(Status::Active, activation->activation());
363}
364
365TEST_F(TestActivation, doUpdateWhenNoFilePathInReadyState)
366{
367 filePath = "";
368 status = Status::Ready; // Usually a Ready activation should have file path,
369 // but we are testing this case as well
370 constexpr auto psu0 = "/com/example/inventory/psu0";
371 activation = std::make_unique<Activation>(
372 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800373 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU63f9e712019-10-12 15:16:55 +0800374
375 ON_CALL(mockedUtils, getPSUInventoryPath(_))
376 .WillByDefault(
377 Return(std::vector<std::string>({psu0}))); // One PSU inventory
378
379 // There shall be no DBus call to start update service
380 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
381 StrEq("StartUnit")))
382 .Times(0);
383
384 activation->requestedActivation(RequestedStatus::Active);
385 EXPECT_EQ(Status::Ready, activation->activation());
386}
387
388TEST_F(TestActivation, doUpdateWhenPSUIsAssociated)
389{
390 constexpr auto psu0 = "/com/example/inventory/psu0";
391 status = Status::Active; // Typically, a running PSU software is associated
392 activation = std::make_unique<Activation>(
393 mockedBus, dBusPath, versionId, extVersion, status, associations,
Lei YUffb36532019-10-15 13:55:24 +0800394 filePath, &mockedAssociationInterface, &mockedActivationListener);
Lei YU63f9e712019-10-12 15:16:55 +0800395
396 ON_CALL(mockedUtils, getPSUInventoryPath(_))
397 .WillByDefault(
398 Return(std::vector<std::string>({psu0}))); // One PSU inventory
399
400 // When PSU is already associated, there shall be no DBus call to start
401 // update service
402 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
403 .WillOnce(Return(true));
404 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
405 StrEq("StartUnit")))
406 .Times(0);
407
408 activation->requestedActivation(RequestedStatus::Active);
409 EXPECT_EQ(Status::Active, activation->activation());
410}