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, ReadVersionReturnsEmptyIfPollingFails)
214 {
215     /* It can return an empty result, 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_THAT(updater.readVersion(ipmi_flash::biosVersionBlobId), IsEmpty());
224 }
225 
226 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException)
227 {
228     /* On open, it can except and this is converted to a ToolException. */
229     EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
230         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
231     EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
232                  ToolException);
233 }
234 
235 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen)
236 {
237     /* It only tries to commit if it's able to open the blob.  However, if
238      * committing fails, this error is ignored.
239      */
240     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
241         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
242     EXPECT_CALL(blobMock, commit(_, _)).Times(0);
243     EXPECT_CALL(blobMock, closeBlob(_)).Times(0);
244 
245     updater.cleanArtifacts();
246 }
247 
248 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose)
249 {
250     /* The closeBlob call is called even if commit excepts. */
251     std::uint16_t session = 0xa5eb;
252     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
253         .WillOnce(Return(session));
254     EXPECT_CALL(blobMock, commit(session, _))
255         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
256     EXPECT_CALL(blobMock, closeBlob(session));
257 
258     updater.cleanArtifacts();
259 }
260 
261 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath)
262 {
263     std::uint16_t session = 0xa5eb;
264     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
265         .WillOnce(Return(session));
266     EXPECT_CALL(blobMock, commit(session, _));
267     EXPECT_CALL(blobMock, closeBlob(session));
268 
269     updater.cleanArtifacts();
270 }
271 
272 class UpdaterTest : public ::testing::Test
273 {
274   protected:
275     static constexpr char image[] = "image.bin";
276     static constexpr char signature[] = "signature.bin";
277     static constexpr char layout[] = "static";
278     static constexpr char path[] = "/flash/static";
279 
280     ipmiblob::BlobInterfaceMock blobMock;
281     std::uint16_t session = 0xbeef;
282     bool defaultIgnore = false;
283 };
284 
285 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess)
286 {
287     UpdateHandlerMock handler;
288 
289     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
290     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
291     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
292         .WillOnce(Return());
293     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
294         .WillOnce(Return(true));
295     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
296         .WillOnce(Return(true));
297 
298     updaterMain(&handler, image, signature, layout, defaultIgnore);
299 }
300 
301 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate)
302 {
303     UpdateHandlerMock handler;
304     bool updateIgnore = true;
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, updateIgnore))
313         .WillOnce(Return(true));
314 
315     updaterMain(&handler, image, signature, layout, updateIgnore);
316 }
317 
318 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure)
319 {
320     UpdateHandlerMock handler;
321 
322     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
323     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
324     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
325         .WillOnce(Return());
326     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
327         .WillOnce(Return(false));
328     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
329 
330     EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
331                  ToolException);
332 }
333 
334 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure)
335 {
336     UpdateHandlerMock handler;
337 
338     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
339     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
340     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
341         .WillOnce(Return());
342     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
343         .WillOnce(Return(true));
344     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
345         .WillOnce(Return(false));
346     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
347 
348     EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
349                  ToolException);
350 }
351 
352 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound)
353 {
354     UpdateHandlerMock handler;
355 
356     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
357 
358     EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
359                  ToolException);
360 }
361 
362 } // namespace host_tool
363