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