blob: 97efe7932d3485b1775cba269a80609fee00e87c [file] [log] [blame]
Lei YU12c9f4c2019-09-11 15:08:15 +08001#include "activation.hpp"
Lei YU7f2a2152019-09-16 16:50:18 +08002#include "mocked_association_interface.hpp"
Lei YUff83c2a2019-09-12 13:55:18 +08003#include "mocked_utils.hpp"
Lei YU12c9f4c2019-09-11 15:08:15 +08004
5#include <sdbusplus/test/sdbus_mock.hpp>
6
7#include <gmock/gmock.h>
8#include <gtest/gtest.h>
9
10using namespace phosphor::software::updater;
11
Lei YUff83c2a2019-09-12 13:55:18 +080012using ::testing::_;
13using ::testing::Return;
Lei YU9edb7332019-09-19 14:46:19 +080014using ::testing::StrEq;
15
16using std::experimental::any;
Lei YUff83c2a2019-09-12 13:55:18 +080017
Lei YU12c9f4c2019-09-11 15:08:15 +080018class TestActivation : public ::testing::Test
19{
20 public:
Lei YU9edb7332019-09-19 14:46:19 +080021 using PropertyType = utils::UtilsInterface::PropertyType;
Lei YUff83c2a2019-09-12 13:55:18 +080022 using Status = Activation::Status;
23 using RequestedStatus = Activation::RequestedActivations;
24 TestActivation() :
25 mockedUtils(
26 reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
Lei YU12c9f4c2019-09-11 15:08:15 +080027 {
Lei YU9edb7332019-09-19 14:46:19 +080028 // By default make it compatible with the test software
29 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
30 .WillByDefault(Return(any(PropertyType(std::string("TestManu")))));
31 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL)))
32 .WillByDefault(Return(any(PropertyType(std::string("TestModel")))));
Lei YU63f9e712019-10-12 15:16:55 +080033 ON_CALL(mockedUtils, isAssociated(_, _)).WillByDefault(Return(false));
Lei YU12c9f4c2019-09-11 15:08:15 +080034 }
35 ~TestActivation()
36 {
Lei YUc09155b2019-10-11 17:30:48 +080037 utils::freeUtils();
Lei YU12c9f4c2019-09-11 15:08:15 +080038 }
Lei YUff83c2a2019-09-12 13:55:18 +080039
40 void onUpdateDone()
41 {
42 activation->onUpdateDone();
43 }
44 void onUpdateFailed()
45 {
46 activation->onUpdateFailed();
47 }
48 int getProgress()
49 {
50 return activation->activationProgress->progress();
51 }
Lei YU9edb7332019-09-19 14:46:19 +080052 const auto& getPsuQueue()
53 {
54 return activation->psuQueue;
55 }
Lei YUe8945ea2019-09-29 17:25:31 +080056 std::string getUpdateService(const std::string& psuInventoryPath)
57 {
58 return activation->getUpdateService(psuInventoryPath);
59 }
Lei YU9edb7332019-09-19 14:46:19 +080060
Lei YU12c9f4c2019-09-11 15:08:15 +080061 sdbusplus::SdBusMock sdbusMock;
62 sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
Lei YUff83c2a2019-09-12 13:55:18 +080063 const utils::MockedUtils& mockedUtils;
Lei YU7f2a2152019-09-16 16:50:18 +080064 MockedAssociationInterface mockedAssociationInterface;
Lei YU12c9f4c2019-09-11 15:08:15 +080065 std::unique_ptr<Activation> activation;
66 std::string versionId = "abcdefgh";
Lei YU9edb7332019-09-19 14:46:19 +080067 std::string extVersion = "manufacturer=TestManu,model=TestModel";
Lei YU63f9e712019-10-12 15:16:55 +080068 std::string filePath = "/tmp/images/abcdefgh";
Lei YU7f2a2152019-09-16 16:50:18 +080069 std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId;
Lei YUff83c2a2019-09-12 13:55:18 +080070 Status status = Status::Ready;
Lei YU12c9f4c2019-09-11 15:08:15 +080071 AssociationList associations;
72};
73
74TEST_F(TestActivation, ctordtor)
75{
Lei YU99301372019-09-29 16:27:12 +080076 activation = std::make_unique<Activation>(
77 mockedBus, dBusPath, versionId, extVersion, status, associations,
78 &mockedAssociationInterface, filePath);
Lei YU12c9f4c2019-09-11 15:08:15 +080079}
80
Lei YU58c26e32019-09-27 17:52:06 +080081TEST_F(TestActivation, ctorWithInvalidExtVersion)
82{
83 extVersion = "invalid text";
84 activation = std::make_unique<Activation>(
85 mockedBus, dBusPath, versionId, extVersion, status, associations,
86 &mockedAssociationInterface, filePath);
87}
88
Lei YU12c9f4c2019-09-11 15:08:15 +080089TEST_F(TestActivation, getUpdateService)
90{
91 std::string psuInventoryPath = "/com/example/inventory/powersupply1";
Lei YU12c9f4c2019-09-11 15:08:15 +080092 std::string toCompare = "psu-update@-com-example-inventory-"
93 "powersupply1\\x20-tmp-images-12345678.service";
Lei YUe8945ea2019-09-29 17:25:31 +080094 versionId = "12345678";
95 filePath = "/tmp/images/12345678";
Lei YU12c9f4c2019-09-11 15:08:15 +080096
Lei YUe8945ea2019-09-29 17:25:31 +080097 activation = std::make_unique<Activation>(
98 mockedBus, dBusPath, versionId, extVersion, status, associations,
99 &mockedAssociationInterface, filePath);
100
101 auto service = getUpdateService(psuInventoryPath);
Lei YU12c9f4c2019-09-11 15:08:15 +0800102 EXPECT_EQ(toCompare, service);
103}
Lei YUff83c2a2019-09-12 13:55:18 +0800104
105TEST_F(TestActivation, doUpdateWhenNoPSU)
106{
Lei YU99301372019-09-29 16:27:12 +0800107 activation = std::make_unique<Activation>(
108 mockedBus, dBusPath, versionId, extVersion, status, associations,
109 &mockedAssociationInterface, filePath);
Lei YUff83c2a2019-09-12 13:55:18 +0800110 ON_CALL(mockedUtils, getPSUInventoryPath(_))
111 .WillByDefault(
112 Return(std::vector<std::string>({}))); // No PSU inventory
113 activation->requestedActivation(RequestedStatus::Active);
114
Lei YU7f2a2152019-09-16 16:50:18 +0800115 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
116 .Times(0);
117 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
118 .Times(0);
Lei YUff83c2a2019-09-12 13:55:18 +0800119 EXPECT_EQ(Status::Failed, activation->activation());
120}
121
122TEST_F(TestActivation, doUpdateOnePSUOK)
123{
124 constexpr auto psu0 = "/com/example/inventory/psu0";
Lei YU99301372019-09-29 16:27:12 +0800125 activation = std::make_unique<Activation>(
126 mockedBus, dBusPath, versionId, extVersion, status, associations,
127 &mockedAssociationInterface, filePath);
Lei YUff83c2a2019-09-12 13:55:18 +0800128 ON_CALL(mockedUtils, getPSUInventoryPath(_))
129 .WillByDefault(
130 Return(std::vector<std::string>({psu0}))); // One PSU inventory
131 activation->requestedActivation(RequestedStatus::Active);
132
133 EXPECT_EQ(Status::Activating, activation->activation());
134
Lei YU7f2a2152019-09-16 16:50:18 +0800135 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
136 .Times(1);
137 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
138 .Times(1);
Lei YUff83c2a2019-09-12 13:55:18 +0800139 onUpdateDone();
140 EXPECT_EQ(Status::Active, activation->activation());
141}
142
143TEST_F(TestActivation, doUpdateFourPSUsOK)
144{
145 constexpr auto psu0 = "/com/example/inventory/psu0";
146 constexpr auto psu1 = "/com/example/inventory/psu1";
147 constexpr auto psu2 = "/com/example/inventory/psu2";
148 constexpr auto psu3 = "/com/example/inventory/psu3";
Lei YU99301372019-09-29 16:27:12 +0800149 activation = std::make_unique<Activation>(
150 mockedBus, dBusPath, versionId, extVersion, status, associations,
151 &mockedAssociationInterface, filePath);
Lei YUff83c2a2019-09-12 13:55:18 +0800152 ON_CALL(mockedUtils, getPSUInventoryPath(_))
153 .WillByDefault(Return(
154 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
155 activation->requestedActivation(RequestedStatus::Active);
156
157 EXPECT_EQ(Status::Activating, activation->activation());
158 EXPECT_EQ(10, getProgress());
159
160 onUpdateDone();
161 EXPECT_EQ(Status::Activating, activation->activation());
162 EXPECT_EQ(30, getProgress());
163
164 onUpdateDone();
165 EXPECT_EQ(Status::Activating, activation->activation());
166 EXPECT_EQ(50, getProgress());
167
168 onUpdateDone();
169 EXPECT_EQ(Status::Activating, activation->activation());
170 EXPECT_EQ(70, getProgress());
171
Lei YU7f2a2152019-09-16 16:50:18 +0800172 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
173 .Times(1);
174 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
175 .Times(1);
176
Lei YUff83c2a2019-09-12 13:55:18 +0800177 onUpdateDone();
178 EXPECT_EQ(Status::Active, activation->activation());
179}
180
181TEST_F(TestActivation, doUpdateFourPSUsFailonSecond)
182{
183 constexpr auto psu0 = "/com/example/inventory/psu0";
184 constexpr auto psu1 = "/com/example/inventory/psu1";
185 constexpr auto psu2 = "/com/example/inventory/psu2";
186 constexpr auto psu3 = "/com/example/inventory/psu3";
Lei YU99301372019-09-29 16:27:12 +0800187 activation = std::make_unique<Activation>(
188 mockedBus, dBusPath, versionId, extVersion, status, associations,
189 &mockedAssociationInterface, filePath);
Lei YUff83c2a2019-09-12 13:55:18 +0800190 ON_CALL(mockedUtils, getPSUInventoryPath(_))
191 .WillByDefault(Return(
192 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
193 activation->requestedActivation(RequestedStatus::Active);
194
195 EXPECT_EQ(Status::Activating, activation->activation());
196 EXPECT_EQ(10, getProgress());
197
198 onUpdateDone();
199 EXPECT_EQ(Status::Activating, activation->activation());
200 EXPECT_EQ(30, getProgress());
201
Lei YU7f2a2152019-09-16 16:50:18 +0800202 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
203 .Times(0);
204 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
205 .Times(0);
Lei YUff83c2a2019-09-12 13:55:18 +0800206 onUpdateFailed();
207 EXPECT_EQ(Status::Failed, activation->activation());
208}
209
210TEST_F(TestActivation, doUpdateOnExceptionFromDbus)
211{
212 constexpr auto psu0 = "/com/example/inventory/psu0";
Lei YU99301372019-09-29 16:27:12 +0800213 activation = std::make_unique<Activation>(
214 mockedBus, dBusPath, versionId, extVersion, status, associations,
215 &mockedAssociationInterface, filePath);
Lei YUff83c2a2019-09-12 13:55:18 +0800216 ON_CALL(mockedUtils, getPSUInventoryPath(_))
217 .WillByDefault(
218 Return(std::vector<std::string>({psu0}))); // One PSU inventory
219 ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr))
220 .WillByDefault(Return(-1)); // Make sdbus call failure
221 activation->requestedActivation(RequestedStatus::Active);
222
223 EXPECT_EQ(Status::Failed, activation->activation());
224}
Lei YU9edb7332019-09-19 14:46:19 +0800225
226TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible)
227{
228 constexpr auto psu0 = "/com/example/inventory/psu0";
229 extVersion = "manufacturer=TestManu,model=DifferentModel";
Lei YU99301372019-09-29 16:27:12 +0800230 activation = std::make_unique<Activation>(
231 mockedBus, dBusPath, versionId, extVersion, status, associations,
232 &mockedAssociationInterface, filePath);
Lei YU9edb7332019-09-19 14:46:19 +0800233 ON_CALL(mockedUtils, getPSUInventoryPath(_))
234 .WillByDefault(Return(std::vector<std::string>({psu0})));
235 activation->requestedActivation(RequestedStatus::Active);
236
Lei YU63f9e712019-10-12 15:16:55 +0800237 EXPECT_EQ(Status::Ready, activation->activation());
Lei YU9edb7332019-09-19 14:46:19 +0800238}
239
240TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible)
241{
242 constexpr auto psu0 = "/com/example/inventory/psu0";
243 extVersion = "manufacturer=DifferentManu,model=TestModel";
Lei YU99301372019-09-29 16:27:12 +0800244 activation = std::make_unique<Activation>(
245 mockedBus, dBusPath, versionId, extVersion, status, associations,
246 &mockedAssociationInterface, filePath);
Lei YU9edb7332019-09-19 14:46:19 +0800247 ON_CALL(mockedUtils, getPSUInventoryPath(_))
248 .WillByDefault(Return(std::vector<std::string>({psu0})));
249 activation->requestedActivation(RequestedStatus::Active);
250
Lei YU63f9e712019-10-12 15:16:55 +0800251 EXPECT_EQ(Status::Ready, activation->activation());
Lei YU9edb7332019-09-19 14:46:19 +0800252}
253
254TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty)
255{
256 ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
257 .WillByDefault(Return(any(PropertyType(std::string("")))));
258 extVersion = "manufacturer=AnyManu,model=TestModel";
259 // Below is the same as doUpdateOnePSUOK case
260 constexpr auto psu0 = "/com/example/inventory/psu0";
Lei YU99301372019-09-29 16:27:12 +0800261 activation = std::make_unique<Activation>(
262 mockedBus, dBusPath, versionId, extVersion, status, associations,
263 &mockedAssociationInterface, filePath);
Lei YU9edb7332019-09-19 14:46:19 +0800264 ON_CALL(mockedUtils, getPSUInventoryPath(_))
265 .WillByDefault(
266 Return(std::vector<std::string>({psu0}))); // One PSU inventory
267 activation->requestedActivation(RequestedStatus::Active);
268
269 EXPECT_EQ(Status::Activating, activation->activation());
270
271 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
272 .Times(1);
273 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
274 .Times(1);
275 onUpdateDone();
276 EXPECT_EQ(Status::Active, activation->activation());
277}
278
279TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible)
280{
281 constexpr auto psu0 = "/com/example/inventory/psu0";
282 constexpr auto psu1 = "/com/example/inventory/psu1";
283 constexpr auto psu2 = "/com/example/inventory/psu2";
284 constexpr auto psu3 = "/com/example/inventory/psu3";
285 ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL)))
286 .WillByDefault(
287 Return(any(PropertyType(std::string("DifferentModel")))));
Lei YU99301372019-09-29 16:27:12 +0800288 activation = std::make_unique<Activation>(
289 mockedBus, dBusPath, versionId, extVersion, status, associations,
290 &mockedAssociationInterface, filePath);
Lei YU9edb7332019-09-19 14:46:19 +0800291 ON_CALL(mockedUtils, getPSUInventoryPath(_))
292 .WillByDefault(Return(
293 std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
294 activation->requestedActivation(RequestedStatus::Active);
295
296 const auto& psuQueue = getPsuQueue();
297 EXPECT_EQ(3u, psuQueue.size());
298
299 // Only 3 PSUs shall be updated, and psu1 shall be skipped
300 EXPECT_EQ(Status::Activating, activation->activation());
301 EXPECT_EQ(10, getProgress());
302
303 onUpdateDone();
304 EXPECT_EQ(Status::Activating, activation->activation());
305 EXPECT_EQ(36, getProgress());
306
307 onUpdateDone();
308 EXPECT_EQ(Status::Activating, activation->activation());
309 EXPECT_EQ(62, getProgress());
310
311 EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
312 .Times(1);
313 EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
314 .Times(1);
315
316 onUpdateDone();
317 EXPECT_EQ(Status::Active, activation->activation());
318}
Lei YU63f9e712019-10-12 15:16:55 +0800319
320TEST_F(TestActivation, doUpdateWhenNoFilePathInActiveState)
321{
322 filePath = "";
323 status = Status::Active; // Typically, a running PSU software is active
324 // without file path
325 constexpr auto psu0 = "/com/example/inventory/psu0";
326 activation = std::make_unique<Activation>(
327 mockedBus, dBusPath, versionId, extVersion, status, associations,
328 &mockedAssociationInterface, filePath);
329
330 ON_CALL(mockedUtils, getPSUInventoryPath(_))
331 .WillByDefault(
332 Return(std::vector<std::string>({psu0}))); // One PSU inventory
333
334 // There shall be no DBus call to start update service
335 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
336 StrEq("StartUnit")))
337 .Times(0);
338
339 activation->requestedActivation(RequestedStatus::Active);
340 EXPECT_EQ(Status::Active, activation->activation());
341}
342
343TEST_F(TestActivation, doUpdateWhenNoFilePathInReadyState)
344{
345 filePath = "";
346 status = Status::Ready; // Usually a Ready activation should have file path,
347 // but we are testing this case as well
348 constexpr auto psu0 = "/com/example/inventory/psu0";
349 activation = std::make_unique<Activation>(
350 mockedBus, dBusPath, versionId, extVersion, status, associations,
351 &mockedAssociationInterface, filePath);
352
353 ON_CALL(mockedUtils, getPSUInventoryPath(_))
354 .WillByDefault(
355 Return(std::vector<std::string>({psu0}))); // One PSU inventory
356
357 // There shall be no DBus call to start update service
358 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
359 StrEq("StartUnit")))
360 .Times(0);
361
362 activation->requestedActivation(RequestedStatus::Active);
363 EXPECT_EQ(Status::Ready, activation->activation());
364}
365
366TEST_F(TestActivation, doUpdateWhenPSUIsAssociated)
367{
368 constexpr auto psu0 = "/com/example/inventory/psu0";
369 status = Status::Active; // Typically, a running PSU software is associated
370 activation = std::make_unique<Activation>(
371 mockedBus, dBusPath, versionId, extVersion, status, associations,
372 &mockedAssociationInterface, filePath);
373
374 ON_CALL(mockedUtils, getPSUInventoryPath(_))
375 .WillByDefault(
376 Return(std::vector<std::string>({psu0}))); // One PSU inventory
377
378 // When PSU is already associated, there shall be no DBus call to start
379 // update service
380 EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _))
381 .WillOnce(Return(true));
382 EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
383 StrEq("StartUnit")))
384 .Times(0);
385
386 activation->requestedActivation(RequestedStatus::Active);
387 EXPECT_EQ(Status::Active, activation->activation());
388}