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