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         .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
112 
113     EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
114         .WillOnce(Return(session));
115 
116     EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
117         .WillOnce(Return(false));
118 
119     EXPECT_CALL(blobMock, closeBlob(session)).Times(1);
120 
121     EXPECT_THROW(
122         updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage),
123         ToolException);
124 }
125 
126 TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccess)
127 {
128     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
129         .WillOnce(Return(session));
130     EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return());
131     ipmiblob::StatResponse verificationResponse = {};
132     /* the other details of the response are ignored, and should be. */
133     verificationResponse.metadata.push_back(
134         static_cast<std::uint8_t>(ipmi_flash::ActionStatus::success));
135 
136     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
137         .WillOnce(Return(verificationResponse));
138     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
139 
140     EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false));
141 }
142 
143 TEST_F(UpdateHandlerTest, VerifyFileHandleSkipsPollingIfIgnoreStatus)
144 {
145     /* if ignoreStatus, it'll skip polling for a verification result. */
146     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
147         .WillOnce(Return(session));
148     EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return());
149 
150     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
151 
152     EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, true));
153 }
154 
155 TEST_F(UpdateHandlerTest, VerifyFileConvertsOpenBlobExceptionToToolException)
156 {
157     /* On open, it can except and this is converted to a ToolException. */
158     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
159         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
160     EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
161                  ToolException);
162 }
163 
164 TEST_F(UpdateHandlerTest, VerifyFileCommitExceptionForwards)
165 {
166     /* On commit, it can except. */
167     EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
168         .WillOnce(Return(session));
169     EXPECT_CALL(blobMock, commit(session, _))
170         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
171     EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
172                  ToolException);
173 }
174 
175 TEST_F(UpdateHandlerTest, ReadVerisonReturnExpected)
176 {
177     /* It can return as expected, when polling and readBytes succeeds. */
178     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
179         .WillOnce(Return(session));
180     ipmiblob::StatResponse readVersionResponse = {};
181     readVersionResponse.blob_state =
182         blobs::StateFlags::open_read | blobs::StateFlags::committed;
183     readVersionResponse.size = 10;
184     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
185         .WillOnce(Return(readVersionResponse));
186     std::vector<uint8_t> resp = {0x2d, 0xfe};
187     EXPECT_CALL(blobMock, readBytes(session, 0, _)).WillOnce(Return(resp));
188 
189     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
190     EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId));
191 }
192 
193 TEST_F(UpdateHandlerTest, ReadVersionExceptionWhenPollingSucceedsReadBytesFails)
194 {
195     /* On readBytes, it can except. */
196     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
197         .WillOnce(Return(session));
198     ipmiblob::StatResponse readVersionResponse = {};
199     readVersionResponse.blob_state =
200         blobs::StateFlags::open_read | blobs::StateFlags::committed;
201     readVersionResponse.size = 10;
202     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
203         .WillOnce(Return(readVersionResponse));
204     EXPECT_CALL(blobMock, readBytes(session, 0, _))
205         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
206     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
207     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
208                  ToolException);
209 }
210 
211 TEST_F(UpdateHandlerTest, ReadVersionReturnsErrorIfPollingFails)
212 {
213     /* It can throw an error, when polling fails. */
214     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
215         .WillOnce(Return(session));
216     ipmiblob::StatResponse readVersionResponse = {};
217     readVersionResponse.blob_state = blobs::StateFlags::commit_error;
218     EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
219         .WillOnce(Return(readVersionResponse));
220     EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return());
221     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
222                  ToolException);
223 }
224 
225 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException)
226 {
227     /* On open, it can except and this is converted to a ToolException. */
228     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
229         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
230     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
231                  ToolException);
232 }
233 
234 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen)
235 {
236     /* It only tries to commit if it's able to open the blob.  However, if
237      * committing fails, this error is ignored.
238      */
239     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
240         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
241     EXPECT_CALL(blobMock, commit(_, _)).Times(0);
242     EXPECT_CALL(blobMock, closeBlob(_)).Times(0);
243 
244     updater.cleanArtifacts();
245 }
246 
247 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose)
248 {
249     /* The closeBlob call is called even if commit excepts. */
250     std::uint16_t session = 0xa5eb;
251     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
252         .WillOnce(Return(session));
253     EXPECT_CALL(blobMock, commit(session, _))
254         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
255     EXPECT_CALL(blobMock, closeBlob(session));
256 
257     updater.cleanArtifacts();
258 }
259 
260 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath)
261 {
262     std::uint16_t session = 0xa5eb;
263     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
264         .WillOnce(Return(session));
265     EXPECT_CALL(blobMock, commit(session, _));
266     EXPECT_CALL(blobMock, closeBlob(session));
267 
268     updater.cleanArtifacts();
269 }
270 
271 class UpdaterTest : public ::testing::Test
272 {
273   protected:
274     static constexpr char image[] = "image.bin";
275     static constexpr char signature[] = "signature.bin";
276     static constexpr char layout[] = "static";
277     static constexpr char path[] = "/flash/static";
278 
279     ipmiblob::BlobInterfaceMock blobMock;
280     std::uint16_t session = 0xbeef;
281     bool defaultIgnore = false;
282 };
283 
284 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess)
285 {
286     UpdateHandlerMock handler;
287 
288     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
289     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
290     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
291         .WillOnce(Return());
292     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
293         .WillOnce(Return(true));
294     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
295         .WillOnce(Return(true));
296     EXPECT_CALL(blobMock, getBlobList())
297         .WillOnce(Return(std::vector<std::string>({})));
298 
299     updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
300 }
301 
302 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob)
303 {
304     UpdateHandlerMock handler;
305 
306     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
307     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
308     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
309         .WillOnce(Return());
310     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
311         .WillOnce(Return(true));
312     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
313         .WillOnce(Return(true));
314     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
315     EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId))
316         .WillOnce(Return(true));
317     EXPECT_CALL(blobMock, getBlobList())
318         .WillOnce(Return(std::vector<std::string>(
319             {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId})));
320 
321     updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
322 }
323 
324 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate)
325 {
326     UpdateHandlerMock handler;
327     bool updateIgnore = true;
328 
329     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
330     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
331     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
332         .WillOnce(Return());
333     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
334         .WillOnce(Return(true));
335     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore))
336         .WillOnce(Return(true));
337     EXPECT_CALL(blobMock, getBlobList())
338         .WillOnce(Return(std::vector<std::string>({})));
339 
340     updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore);
341 }
342 
343 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure)
344 {
345     UpdateHandlerMock handler;
346 
347     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
348     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
349     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
350         .WillOnce(Return());
351     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
352         .WillOnce(Return(false));
353     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
354     EXPECT_CALL(blobMock, getBlobList())
355         .WillOnce(Return(std::vector<std::string>({})));
356 
357     EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
358                              defaultIgnore),
359                  ToolException);
360 }
361 
362 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure)
363 {
364     UpdateHandlerMock handler;
365 
366     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
367     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
368     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
369         .WillOnce(Return());
370     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
371         .WillOnce(Return(true));
372     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
373         .WillOnce(Return(false));
374     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
375     EXPECT_CALL(blobMock, getBlobList())
376         .WillOnce(Return(std::vector<std::string>({})));
377 
378     EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
379                              defaultIgnore),
380                  ToolException);
381 }
382 
383 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound)
384 {
385     UpdateHandlerMock handler;
386 
387     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
388 
389     EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
390                              defaultIgnore),
391                  ToolException);
392 }
393 
394 } // namespace host_tool
395