1 #include "activation.hpp"
2 #include "mocked_association_interface.hpp"
3 #include "mocked_utils.hpp"
4 
5 #include <sdbusplus/test/sdbus_mock.hpp>
6 
7 #include <gmock/gmock.h>
8 #include <gtest/gtest.h>
9 
10 using namespace phosphor::software::updater;
11 
12 using ::testing::_;
13 using ::testing::Return;
14 using ::testing::StrEq;
15 
16 using std::experimental::any;
17 
18 class TestActivation : public ::testing::Test
19 {
20   public:
21     using PropertyType = utils::UtilsInterface::PropertyType;
22     using Status = Activation::Status;
23     using RequestedStatus = Activation::RequestedActivations;
24     TestActivation() :
25         mockedUtils(
26             reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
27     {
28         // 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")))));
33     }
34     ~TestActivation()
35     {
36     }
37 
38     void onUpdateDone()
39     {
40         activation->onUpdateDone();
41     }
42     void onUpdateFailed()
43     {
44         activation->onUpdateFailed();
45     }
46     int getProgress()
47     {
48         return activation->activationProgress->progress();
49     }
50     const auto& getPsuQueue()
51     {
52         return activation->psuQueue;
53     }
54 
55     sdbusplus::SdBusMock sdbusMock;
56     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
57     const utils::MockedUtils& mockedUtils;
58     MockedAssociationInterface mockedAssociationInterface;
59     std::unique_ptr<Activation> activation;
60     std::string versionId = "abcdefgh";
61     std::string extVersion = "manufacturer=TestManu,model=TestModel";
62     std::string filePath = "";
63     std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId;
64     Status status = Status::Ready;
65     AssociationList associations;
66 };
67 
68 TEST_F(TestActivation, ctordtor)
69 {
70     activation = std::make_unique<Activation>(
71         mockedBus, dBusPath, versionId, extVersion, status, associations,
72         &mockedAssociationInterface, filePath);
73 }
74 
75 TEST_F(TestActivation, ctorWithInvalidExtVersion)
76 {
77     extVersion = "invalid text";
78     activation = std::make_unique<Activation>(
79         mockedBus, dBusPath, versionId, extVersion, status, associations,
80         &mockedAssociationInterface, filePath);
81 }
82 
83 namespace phosphor::software::updater::internal
84 {
85 extern std::string getUpdateService(const std::string& psuInventoryPath,
86                                     const std::string& versionId);
87 }
88 
89 TEST_F(TestActivation, getUpdateService)
90 {
91     std::string psuInventoryPath = "/com/example/inventory/powersupply1";
92     std::string versionId = "12345678";
93     std::string toCompare = "psu-update@-com-example-inventory-"
94                             "powersupply1\\x20-tmp-images-12345678.service";
95 
96     auto service = phosphor::software::updater::internal::getUpdateService(
97         psuInventoryPath, versionId);
98     EXPECT_EQ(toCompare, service);
99 }
100 
101 TEST_F(TestActivation, doUpdateWhenNoPSU)
102 {
103     activation = std::make_unique<Activation>(
104         mockedBus, dBusPath, versionId, extVersion, status, associations,
105         &mockedAssociationInterface, filePath);
106     ON_CALL(mockedUtils, getPSUInventoryPath(_))
107         .WillByDefault(
108             Return(std::vector<std::string>({}))); // No PSU inventory
109     activation->requestedActivation(RequestedStatus::Active);
110 
111     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
112         .Times(0);
113     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
114         .Times(0);
115     EXPECT_EQ(Status::Failed, activation->activation());
116 }
117 
118 TEST_F(TestActivation, doUpdateOnePSUOK)
119 {
120     constexpr auto psu0 = "/com/example/inventory/psu0";
121     activation = std::make_unique<Activation>(
122         mockedBus, dBusPath, versionId, extVersion, status, associations,
123         &mockedAssociationInterface, filePath);
124     ON_CALL(mockedUtils, getPSUInventoryPath(_))
125         .WillByDefault(
126             Return(std::vector<std::string>({psu0}))); // One PSU inventory
127     activation->requestedActivation(RequestedStatus::Active);
128 
129     EXPECT_EQ(Status::Activating, activation->activation());
130 
131     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
132         .Times(1);
133     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
134         .Times(1);
135     onUpdateDone();
136     EXPECT_EQ(Status::Active, activation->activation());
137 }
138 
139 TEST_F(TestActivation, doUpdateFourPSUsOK)
140 {
141     constexpr auto psu0 = "/com/example/inventory/psu0";
142     constexpr auto psu1 = "/com/example/inventory/psu1";
143     constexpr auto psu2 = "/com/example/inventory/psu2";
144     constexpr auto psu3 = "/com/example/inventory/psu3";
145     activation = std::make_unique<Activation>(
146         mockedBus, dBusPath, versionId, extVersion, status, associations,
147         &mockedAssociationInterface, filePath);
148     ON_CALL(mockedUtils, getPSUInventoryPath(_))
149         .WillByDefault(Return(
150             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
151     activation->requestedActivation(RequestedStatus::Active);
152 
153     EXPECT_EQ(Status::Activating, activation->activation());
154     EXPECT_EQ(10, getProgress());
155 
156     onUpdateDone();
157     EXPECT_EQ(Status::Activating, activation->activation());
158     EXPECT_EQ(30, getProgress());
159 
160     onUpdateDone();
161     EXPECT_EQ(Status::Activating, activation->activation());
162     EXPECT_EQ(50, getProgress());
163 
164     onUpdateDone();
165     EXPECT_EQ(Status::Activating, activation->activation());
166     EXPECT_EQ(70, getProgress());
167 
168     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
169         .Times(1);
170     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
171         .Times(1);
172 
173     onUpdateDone();
174     EXPECT_EQ(Status::Active, activation->activation());
175 }
176 
177 TEST_F(TestActivation, doUpdateFourPSUsFailonSecond)
178 {
179     constexpr auto psu0 = "/com/example/inventory/psu0";
180     constexpr auto psu1 = "/com/example/inventory/psu1";
181     constexpr auto psu2 = "/com/example/inventory/psu2";
182     constexpr auto psu3 = "/com/example/inventory/psu3";
183     activation = std::make_unique<Activation>(
184         mockedBus, dBusPath, versionId, extVersion, status, associations,
185         &mockedAssociationInterface, filePath);
186     ON_CALL(mockedUtils, getPSUInventoryPath(_))
187         .WillByDefault(Return(
188             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
189     activation->requestedActivation(RequestedStatus::Active);
190 
191     EXPECT_EQ(Status::Activating, activation->activation());
192     EXPECT_EQ(10, getProgress());
193 
194     onUpdateDone();
195     EXPECT_EQ(Status::Activating, activation->activation());
196     EXPECT_EQ(30, getProgress());
197 
198     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
199         .Times(0);
200     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
201         .Times(0);
202     onUpdateFailed();
203     EXPECT_EQ(Status::Failed, activation->activation());
204 }
205 
206 TEST_F(TestActivation, doUpdateOnExceptionFromDbus)
207 {
208     constexpr auto psu0 = "/com/example/inventory/psu0";
209     activation = std::make_unique<Activation>(
210         mockedBus, dBusPath, versionId, extVersion, status, associations,
211         &mockedAssociationInterface, filePath);
212     ON_CALL(mockedUtils, getPSUInventoryPath(_))
213         .WillByDefault(
214             Return(std::vector<std::string>({psu0}))); // One PSU inventory
215     ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr))
216         .WillByDefault(Return(-1)); // Make sdbus call failure
217     activation->requestedActivation(RequestedStatus::Active);
218 
219     EXPECT_EQ(Status::Failed, activation->activation());
220 }
221 
222 TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible)
223 {
224     constexpr auto psu0 = "/com/example/inventory/psu0";
225     extVersion = "manufacturer=TestManu,model=DifferentModel";
226     activation = std::make_unique<Activation>(
227         mockedBus, dBusPath, versionId, extVersion, status, associations,
228         &mockedAssociationInterface, filePath);
229     ON_CALL(mockedUtils, getPSUInventoryPath(_))
230         .WillByDefault(Return(std::vector<std::string>({psu0})));
231     activation->requestedActivation(RequestedStatus::Active);
232 
233     EXPECT_EQ(Status::Failed, activation->activation());
234 }
235 
236 TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible)
237 {
238     constexpr auto psu0 = "/com/example/inventory/psu0";
239     extVersion = "manufacturer=DifferentManu,model=TestModel";
240     activation = std::make_unique<Activation>(
241         mockedBus, dBusPath, versionId, extVersion, status, associations,
242         &mockedAssociationInterface, filePath);
243     ON_CALL(mockedUtils, getPSUInventoryPath(_))
244         .WillByDefault(Return(std::vector<std::string>({psu0})));
245     activation->requestedActivation(RequestedStatus::Active);
246 
247     EXPECT_EQ(Status::Failed, activation->activation());
248 }
249 
250 TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty)
251 {
252     ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER)))
253         .WillByDefault(Return(any(PropertyType(std::string("")))));
254     extVersion = "manufacturer=AnyManu,model=TestModel";
255     // Below is the same as doUpdateOnePSUOK case
256     constexpr auto psu0 = "/com/example/inventory/psu0";
257     activation = std::make_unique<Activation>(
258         mockedBus, dBusPath, versionId, extVersion, status, associations,
259         &mockedAssociationInterface, filePath);
260     ON_CALL(mockedUtils, getPSUInventoryPath(_))
261         .WillByDefault(
262             Return(std::vector<std::string>({psu0}))); // One PSU inventory
263     activation->requestedActivation(RequestedStatus::Active);
264 
265     EXPECT_EQ(Status::Activating, activation->activation());
266 
267     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
268         .Times(1);
269     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
270         .Times(1);
271     onUpdateDone();
272     EXPECT_EQ(Status::Active, activation->activation());
273 }
274 
275 TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible)
276 {
277     constexpr auto psu0 = "/com/example/inventory/psu0";
278     constexpr auto psu1 = "/com/example/inventory/psu1";
279     constexpr auto psu2 = "/com/example/inventory/psu2";
280     constexpr auto psu3 = "/com/example/inventory/psu3";
281     ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL)))
282         .WillByDefault(
283             Return(any(PropertyType(std::string("DifferentModel")))));
284     activation = std::make_unique<Activation>(
285         mockedBus, dBusPath, versionId, extVersion, status, associations,
286         &mockedAssociationInterface, filePath);
287     ON_CALL(mockedUtils, getPSUInventoryPath(_))
288         .WillByDefault(Return(
289             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
290     activation->requestedActivation(RequestedStatus::Active);
291 
292     const auto& psuQueue = getPsuQueue();
293     EXPECT_EQ(3u, psuQueue.size());
294 
295     // Only 3 PSUs shall be updated, and psu1 shall be skipped
296     EXPECT_EQ(Status::Activating, activation->activation());
297     EXPECT_EQ(10, getProgress());
298 
299     onUpdateDone();
300     EXPECT_EQ(Status::Activating, activation->activation());
301     EXPECT_EQ(36, getProgress());
302 
303     onUpdateDone();
304     EXPECT_EQ(Status::Activating, activation->activation());
305     EXPECT_EQ(62, getProgress());
306 
307     EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
308         .Times(1);
309     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
310         .Times(1);
311 
312     onUpdateDone();
313     EXPECT_EQ(Status::Active, activation->activation());
314 }
315