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 <ipmiblob/blob_errors.hpp>
10 #include <ipmiblob/test/blob_interface_mock.hpp>
11 
12 #include <string>
13 #include <vector>
14 
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17 
18 namespace host_tool
19 {
20 
21 using ::testing::_;
22 using ::testing::Eq;
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, CleanArtifactsSkipsCleanupIfUnableToOpen)
176 {
177     /* It only tries to commit if it's able to open the blob.  However, if
178      * committing fails, this error is ignored.
179      */
180     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
181         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
182     EXPECT_CALL(blobMock, commit(_, _)).Times(0);
183     EXPECT_CALL(blobMock, closeBlob(_)).Times(0);
184 
185     updater.cleanArtifacts();
186 }
187 
188 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose)
189 {
190     /* The closeBlob call is called even if commit excepts. */
191     std::uint16_t session = 0xa5eb;
192     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
193         .WillOnce(Return(session));
194     EXPECT_CALL(blobMock, commit(session, _))
195         .WillOnce(Throw(ipmiblob::BlobException("asdf")));
196     EXPECT_CALL(blobMock, closeBlob(session));
197 
198     updater.cleanArtifacts();
199 }
200 
201 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath)
202 {
203     std::uint16_t session = 0xa5eb;
204     EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _))
205         .WillOnce(Return(session));
206     EXPECT_CALL(blobMock, commit(session, _));
207     EXPECT_CALL(blobMock, closeBlob(session));
208 
209     updater.cleanArtifacts();
210 }
211 
212 class UpdaterTest : public ::testing::Test
213 {
214   protected:
215     static constexpr char image[] = "image.bin";
216     static constexpr char signature[] = "signature.bin";
217     static constexpr char layout[] = "static";
218     static constexpr char path[] = "/flash/static";
219 
220     ipmiblob::BlobInterfaceMock blobMock;
221     std::uint16_t session = 0xbeef;
222     bool defaultIgnore = false;
223 };
224 
225 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess)
226 {
227     UpdateHandlerMock handler;
228 
229     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
230     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
231     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
232         .WillOnce(Return());
233     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
234         .WillOnce(Return(true));
235     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
236         .WillOnce(Return(true));
237 
238     updaterMain(&handler, image, signature, layout, defaultIgnore);
239 }
240 
241 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate)
242 {
243     UpdateHandlerMock handler;
244     bool updateIgnore = true;
245 
246     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
247     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
248     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
249         .WillOnce(Return());
250     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
251         .WillOnce(Return(true));
252     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore))
253         .WillOnce(Return(true));
254 
255     updaterMain(&handler, image, signature, layout, updateIgnore);
256 }
257 
258 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure)
259 {
260     UpdateHandlerMock handler;
261 
262     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
263     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
264     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
265         .WillOnce(Return());
266     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
267         .WillOnce(Return(false));
268     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
269 
270     EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
271                  ToolException);
272 }
273 
274 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure)
275 {
276     UpdateHandlerMock handler;
277 
278     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true));
279     EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return());
280     EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature))
281         .WillOnce(Return());
282     EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore))
283         .WillOnce(Return(true));
284     EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore))
285         .WillOnce(Return(false));
286     EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
287 
288     EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
289                  ToolException);
290 }
291 
292 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound)
293 {
294     UpdateHandlerMock handler;
295 
296     EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
297 
298     EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore),
299                  ToolException);
300 }
301 
302 } // namespace host_tool
303