xref: /openbmc/phosphor-ipmi-flash/tools/test/tools_updater_unittest.cpp (revision 1038836c25abccea9f7ba71b97f7a12938132ba4)
1 #include "data_interface_mock.hpp"
2 #include "flags.hpp"
3 #include "status.hpp"
4 #include "tool_errors.hpp"
5 #include "updater.hpp"
6 #include "updater_mock.hpp"
7 #include "util.hpp"
8 
9 #include <blobs-ipmid/blobs.hpp>
10 #include <ipmiblob/blob_errors.hpp>
11 #include <ipmiblob/test/blob_interface_mock.hpp>
12 
13 #include <string>
14 #include <vector>
15 
16 #include <gmock/gmock.h>
17 #include <gtest/gtest.h>
18 
19 namespace host_tool
20 {
21 
22 using ::testing::_;
23 using ::testing::Return;
24 using ::testing::Throw;
25 using ::testing::TypedEq;
26 
27 class UpdateHandlerTest : public ::testing::Test
28 {
29   protected:
30     const std::uint16_t session = 0xbeef;
31 
32     DataInterfaceMock handlerMock;
33     ipmiblob::BlobInterfaceMock blobMock;
34     UpdateHandler updater{&blobMock, &handlerMock};
35 };
36 
37 TEST_F(UpdateHandlerTest, CheckAvailableSuccess)
38 {
39     EXPECT_CALL(blobMock, getBlobList())
40         .WillOnce(
41             Return(std::vector<std::string>({ipmi_flash::staticLayoutBlobId})));
42 
43     EXPECT_TRUE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId));
44 }
45 
46 TEST_F(UpdateHandlerTest, CheckAvailableFailure)
47 {
48     EXPECT_CALL(blobMock, getBlobList())
49         .WillOnce(Return(std::vector<std::string>()));
50 
51     EXPECT_FALSE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId));
52 }
53 
54 TEST_F(UpdateHandlerTest, SendFileSuccess)
55 {
56     /* Call sendFile to verify it does what we expect. */
57     std::string firmwareImage = "image.bin";
58 
59     std::uint16_t supported =
60         static_cast<std::uint16_t>(
61             ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
62         static_cast<std::uint16_t>(
63             ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
64 
65     EXPECT_CALL(handlerMock, supportedType())
66         .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
67 
68     EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
69         .WillOnce(Return(session));
70 
71     EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
72         .WillOnce(Return(true));
73 
74     EXPECT_CALL(blobMock, closeBlob(session)).Times(1);
75 
76     updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage);
77 }
78 
79 TEST_F(UpdateHandlerTest, SendFileExceptsOnBlobOpening)
80 {
81     std::string firmwareImage = "image.bin";
82 
83     std::uint16_t supported =
84         static_cast<std::uint16_t>(
85             ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
86         static_cast<std::uint16_t>(
87             ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
88 
89     EXPECT_CALL(handlerMock, supportedType())
90         .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
91 
92     EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
93         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
94 
95     EXPECT_THROW(
96         updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage),
97         ToolException);
98 }
99 
100 TEST_F(UpdateHandlerTest, SendFileHandlerFailureCausesException)
101 {
102     std::string firmwareImage = "image.bin";
103 
104     std::uint16_t supported =
105         static_cast<std::uint16_t>(
106             ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
107         static_cast<std::uint16_t>(
108             ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
109 
110     EXPECT_CALL(handlerMock, supportedType())
111         .WillRepeatedly(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
112 
113     EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
114         .WillRepeatedly(Return(session));
115 
116     EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
117         .WillRepeatedly(Return(false));
118 
119     EXPECT_CALL(blobMock, closeBlob(session)).Times(3);
120 
121     EXPECT_THROW(
122         updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage),
123         ToolException);
124 }
125 
126 TEST_F(UpdateHandlerTest, SendFileHandlerPassWithRetries)
127 {
128     std::string firmwareImage = "image.bin";
129 
130     std::uint16_t supported =
131         static_cast<std::uint16_t>(
132             ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
133         static_cast<std::uint16_t>(
134             ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
135 
136     EXPECT_CALL(handlerMock, supportedType())
137         .WillRepeatedly(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
138 
139     EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
140         .WillRepeatedly(Return(session));
141 
142     EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
143         .WillOnce(Return(false))
144         .WillOnce(Return(false))
145         .WillOnce(Return(true));
146 
147     EXPECT_CALL(blobMock, closeBlob(session)).Times(3);
148 
149     updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage);
150 }
151 
152 TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccess)
153 {
154     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
155         .WillOnce(Return(session));
156     EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return());
157     ipmiblob::StatResponse verificationResponse = {};
158     /* the other details of the response are ignored, and should be. */
159     verificationResponse.metadata.push_back(
160         static_cast<std::uint8_t>(ipmi_flash::ActionStatus::success));
161 
162     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
163         .WillOnce(Return(verificationResponse));
164     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
165 
166     EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false));
167 }
168 
169 TEST_F(UpdateHandlerTest, VerifyFileHandleSkipsPollingIfIgnoreStatus)
170 {
171     /* if ignoreStatus, it'll skip polling for a verification result. */
172     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
173         .WillOnce(Return(session));
174     EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return());
175 
176     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
177 
178     EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, true));
179 }
180 
181 TEST_F(UpdateHandlerTest, VerifyFileConvertsOpenBlobExceptionToToolException)
182 {
183     /* On open, it can except and this is converted to a ToolException. */
184     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
185         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
186     EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
187                  ToolException);
188 }
189 
190 TEST_F(UpdateHandlerTest, VerifyFileCommitExceptionForwards)
191 {
192     /* On commit, it can except. */
193     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
194         .WillOnce(Return(session));
195     EXPECT_CALL(blobMock, commit(session, _))
196         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
197     EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
198                  ToolException);
199 }
200 
201 TEST_F(UpdateHandlerTest, ReadVerisonReturnExpected)
202 {
203     /* It can return as expected, when polling and readBytes succeeds. */
204     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
205         .WillOnce(Return(session));
206     ipmiblob::StatResponse readVersionResponse = {};
207     readVersionResponse.blob_state = blobs::StateFlags::open_read |
208                                      blobs::StateFlags::committed;
209     readVersionResponse.size = 10;
210     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
211         .WillOnce(Return(readVersionResponse));
212     std::vector<uint8_t> resp = {0x2d, 0xfe};
213     EXPECT_CALL(blobMock, readBytes(session, 0, _)).WillOnce(Return(resp));
214 
215     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
216     EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId));
217 }
218 
219 TEST_F(UpdateHandlerTest, ReadVersionExceptionWhenPollingSucceedsReadBytesFails)
220 {
221     /* On readBytes, it can except. */
222     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
223         .WillOnce(Return(session));
224     ipmiblob::StatResponse readVersionResponse = {};
225     readVersionResponse.blob_state = blobs::StateFlags::open_read |
226                                      blobs::StateFlags::committed;
227     readVersionResponse.size = 10;
228     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
229         .WillOnce(Return(readVersionResponse));
230     EXPECT_CALL(blobMock, readBytes(session, 0, _))
231         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
232     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
233     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
234                  ToolException);
235 }
236 
237 TEST_F(UpdateHandlerTest, ReadVersionReturnsErrorIfPollingFails)
238 {
239     /* It can throw an error, when polling fails. */
240     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
241         .WillOnce(Return(session));
242     ipmiblob::StatResponse readVersionResponse = {};
243     readVersionResponse.blob_state = blobs::StateFlags::commit_error;
244     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
245         .WillOnce(Return(readVersionResponse));
246     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
247     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
248                  ToolException);
249 }
250 
251 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException)
252 {
253     /* On open, it can except and this is converted to a ToolException. */
254     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
255         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
256     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
257                  ToolException);
258 }
259 
260 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen)
261 {
262     /* It only tries to commit if it's able to open the blob.  However, if
263      * committing fails, this error is ignored.
264      */
265     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
266         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
267     EXPECT_CALL(blobMock, commit(_, _)).Times(0);
268     EXPECT_CALL(blobMock, closeBlob(_)).Times(0);
269 
270     updater.cleanArtifacts();
271 }
272 
273 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose)
274 {
275     /* The closeBlob call is called even if commit excepts. */
276     std::uint16_t session = 0xa5eb;
277     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
278         .WillOnce(Return(session));
279     EXPECT_CALL(blobMock, commit(session, _))
280         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
281     EXPECT_CALL(blobMock, closeBlob(session));
282 
283     updater.cleanArtifacts();
284 }
285 
286 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath)
287 {
288     std::uint16_t session = 0xa5eb;
289     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
290         .WillOnce(Return(session));
291     EXPECT_CALL(blobMock, commit(session, _));
292     EXPECT_CALL(blobMock, closeBlob(session));
293 
294     updater.cleanArtifacts();
295 }
296 
297 class UpdaterTest : public ::testing::Test
298 {
299   protected:
300     static constexpr char image[] = "image.bin";
301     static constexpr char signature[] = "signature.bin";
302     static constexpr char layout[] = "static";
303     static constexpr char path[] = "/flash/static";
304 
305     ipmiblob::BlobInterfaceMock blobMock;
306     std::uint16_t session = 0xbeef;
307     bool defaultIgnore = false;
308 };
309 
310 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess)
311 {
312     UpdateHandlerMock handler;
313 
314     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
315     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
316     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
317         .WillOnce(Return());
318     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
319         .WillOnce(Return(true));
320     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
321         .WillOnce(Return(true));
322     EXPECT_CALL(blobMock, getBlobList())
323         .WillOnce(Return(std::vector<std::string>({})));
324 
325     updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
326 }
327 
328 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob)
329 {
330     UpdateHandlerMock handler;
331 
332     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
333     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
334     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
335         .WillOnce(Return());
336     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
337         .WillOnce(Return(true));
338     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
339         .WillOnce(Return(true));
340     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
341     EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId))
342         .WillOnce(Return(true));
343     EXPECT_CALL(blobMock, getBlobList())
344         .WillOnce(Return(std::vector<std::string>(
345             {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId})));
346 
347     updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
348 }
349 
350 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate)
351 {
352     UpdateHandlerMock handler;
353     bool updateIgnore = true;
354 
355     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
356     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
357     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
358         .WillOnce(Return());
359     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
360         .WillOnce(Return(true));
361     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore))
362         .WillOnce(Return(true));
363     EXPECT_CALL(blobMock, getBlobList())
364         .WillOnce(Return(std::vector<std::string>({})));
365 
366     updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore);
367 }
368 
369 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure)
370 {
371     UpdateHandlerMock handler;
372 
373     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
374     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
375     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
376         .WillOnce(Return());
377     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
378         .WillOnce(Return(false));
379     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
380     EXPECT_CALL(blobMock, getBlobList())
381         .WillOnce(Return(std::vector<std::string>({})));
382 
383     EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
384                              defaultIgnore),
385                  ToolException);
386 }
387 
388 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure)
389 {
390     UpdateHandlerMock handler;
391 
392     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
393     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
394     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
395         .WillOnce(Return());
396     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
397         .WillOnce(Return(true));
398     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
399         .WillOnce(Return(false));
400     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
401     EXPECT_CALL(blobMock, getBlobList())
402         .WillOnce(Return(std::vector<std::string>({})));
403 
404     EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
405                              defaultIgnore),
406                  ToolException);
407 }
408 
409 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound)
410 {
411     UpdateHandlerMock handler;
412 
413     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
414 
415     EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
416                              defaultIgnore),
417                  ToolException);
418 }
419 
420 } // namespace host_tool
421