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