1 /**
2  * The goal of these tests is to verify the behavior of all blob commands given
3  * the current state is uploadInProgress.  This state is achieved when an image
4  * or hash blob is opened and the handler is expected to receive bytes.
5  */
6 #include "firmware_handler.hpp"
7 #include "firmware_unittest.hpp"
8 #include "util.hpp"
9 
10 #include <cstdint>
11 #include <string>
12 #include <vector>
13 
14 #include <gtest/gtest.h>
15 
16 namespace ipmi_flash
17 {
18 namespace
19 {
20 
21 using ::testing::ContainerEq;
22 using ::testing::IsEmpty;
23 using ::testing::Return;
24 using ::testing::UnorderedElementsAreArray;
25 
26 /*
27  * There are the following calls (parameters may vary):
28  * canHandleBlob(blob)
29  * getBlobIds
30  * deleteBlob(blob)
31  * stat(blob)
32  * stat(session)
33  * open(blob)
34  * close(session)
35  * writemeta(session)
36  * write(session)
37  * read(session)
38  * commit(session)
39  *
40  * Testing canHandleBlob is uninteresting in this state.  Getting the BlobIDs
41  * will inform what canHandleBlob will return.
42  */
43 class FirmwareHandlerUploadInProgressTest : public IpmiOnlyFirmwareStaticTest
44 {};
45 
46 TEST_F(FirmwareHandlerUploadInProgressTest, GetBlobIdsVerifyOutputActiveImage)
47 {
48     /* Opening the image file will add the active image blob id */
49     openToInProgress(staticLayoutBlobId);
50 
51     EXPECT_THAT(handler->getBlobIds(),
52                 UnorderedElementsAreArray(
53                     {staticLayoutBlobId, hashBlobId, activeImageBlobId}));
54 }
55 
56 TEST_F(FirmwareHandlerUploadInProgressTest, GetBlobIdsVerifyOutputActiveHash)
57 {
58     /* Opening the image file will add the active image blob id */
59     openToInProgress(hashBlobId);
60 
61     EXPECT_THAT(handler->getBlobIds(),
62                 UnorderedElementsAreArray(
63                     {staticLayoutBlobId, hashBlobId, activeHashBlobId}));
64 }
65 
66 /*
67  * stat(blob)
68  */
69 TEST_F(FirmwareHandlerUploadInProgressTest, StatOnActiveImageReturnsFailure)
70 {
71     /* you cannot call stat() on the active image or the active hash or the
72      * verify blob.
73      */
74     openToInProgress(staticLayoutBlobId);
75     ASSERT_TRUE(handler->canHandleBlob(activeImageBlobId));
76 
77     blobs::BlobMeta meta;
78     EXPECT_FALSE(handler->stat(activeImageBlobId, &meta));
79 }
80 
81 TEST_F(FirmwareHandlerUploadInProgressTest, StatOnActiveHashReturnsFailure)
82 {
83     /* this test is separate from the active image one so that the state doesn't
84      * change from close.
85      */
86     openToInProgress(hashBlobId);
87     ASSERT_TRUE(handler->canHandleBlob(activeHashBlobId));
88 
89     blobs::BlobMeta meta;
90     EXPECT_FALSE(handler->stat(activeHashBlobId, &meta));
91 }
92 
93 TEST_F(FirmwareHandlerUploadInProgressTest, StatOnNormalBlobsReturnsSuccess)
94 {
95     /* Calling stat() on the normal blobs (not the active) ones will work and
96      * return the same information as in the notYetStarted state.
97      */
98     openToInProgress(staticLayoutBlobId);
99 
100     std::vector<std::string> testBlobs = {staticLayoutBlobId, hashBlobId};
101     for (const auto& blob : testBlobs)
102     {
103         blobs::BlobMeta meta = {};
104         EXPECT_TRUE(handler->stat(blob, &meta));
105         EXPECT_EQ(expectedIdleMeta, meta);
106     }
107 }
108 
109 /*
110  * stat(session)
111  */
112 TEST_F(FirmwareHandlerUploadInProgressTest,
113        CallingStatOnActiveImageOrHashSessionReturnsDetails)
114 {
115     /* This test will verify that the underlying image handler is called with
116      * this stat, in addition to the normal information.
117      */
118     openToInProgress(staticLayoutBlobId);
119 
120     EXPECT_CALL(*imageMock2, getSize()).WillOnce(Return(32));
121 
122     blobs::BlobMeta meta, expectedMeta = {};
123     expectedMeta.size = 32;
124     expectedMeta.blobState =
125         blobs::OpenFlags::write | FirmwareFlags::UpdateFlags::ipmi;
126     EXPECT_TRUE(handler->stat(session, &meta));
127     EXPECT_EQ(expectedMeta, meta);
128 }
129 
130 /*
131  * open(blob) - While any blob is open, all other fail.
132  *
133  * The fullBlobsList is all the blob_ids present if both /flash/image and
134  * /flash/hash are opened, and one is left open (so there's no verify blob). if
135  * left closed, we'd be in verificationPending, not uploadInProgress.
136  */
137 const std::vector<std::string> fullBlobsList = {
138     activeHashBlobId, activeImageBlobId, hashBlobId, staticLayoutBlobId};
139 
140 TEST_F(FirmwareHandlerUploadInProgressTest, OpeningHashFileWhileImageOpenFails)
141 {
142     /* To be in this state, something must be open (and specifically either an
143      * active image (or tarball) or the hash file. Also verifies you can't just
144      * re-open the currently open file.
145      */
146     openToInProgress(staticLayoutBlobId);
147 
148     for (const auto& blob : fullBlobsList)
149     {
150         EXPECT_FALSE(handler->open(2, flags, blob));
151     }
152 }
153 
154 TEST_F(FirmwareHandlerUploadInProgressTest, OpeningImageFileWhileHashOpenFails)
155 {
156     openToInProgress(hashBlobId);
157 
158     for (const auto& blob : fullBlobsList)
159     {
160         EXPECT_FALSE(handler->open(2, flags, blob));
161     }
162 }
163 
164 /*
165  * close(session) - closing the hash or image will trigger a state transition to
166  * verificationPending.
167  *
168  * NOTE: Re-opening /flash/image will transition back to uploadInProgress, but
169  * that is verified in the verificationPending::open tests.
170  */
171 TEST_F(FirmwareHandlerUploadInProgressTest,
172        ClosingImageFileTransitionsToVerificationPending)
173 {
174     EXPECT_FALSE(handler->canHandleBlob(verifyBlobId));
175     openToInProgress(staticLayoutBlobId);
176 
177     handler->close(session);
178     expectedState(FirmwareBlobHandler::UpdateState::verificationPending);
179 
180     EXPECT_TRUE(handler->canHandleBlob(verifyBlobId));
181 }
182 
183 TEST_F(FirmwareHandlerUploadInProgressTest,
184        ClosingHashFileTransitionsToVerificationPending)
185 {
186     EXPECT_FALSE(handler->canHandleBlob(verifyBlobId));
187     openToInProgress(hashBlobId);
188 
189     handler->close(session);
190     expectedState(FirmwareBlobHandler::UpdateState::verificationPending);
191 
192     EXPECT_FALSE(handler->canHandleBlob(verifyBlobId));
193 }
194 
195 /*
196  * writemeta(session)
197  */
198 TEST_F(FirmwareHandlerUploadInProgressTest,
199        WriteMetaAgainstImageReturnsFailureIfNoDataHandler)
200 {
201     /* Calling write/read/writeMeta are uninteresting against the open blob in
202      * this case because the blob will just pass the call along.  Whereas
203      * calling against the verify or update blob may be more interesting.
204      */
205     openToInProgress(staticLayoutBlobId);
206 
207     /* TODO: Consider adding a test that has a data handler, but that test
208      * already exists under the general writeMeta test suite.
209      */
210     /* Note: with IPMI as the transport there's no data handler, so this should
211      * fail nicely. */
212     std::vector<std::uint8_t> bytes = {0x01, 0x02};
213     EXPECT_FALSE(handler->writeMeta(session, 0, bytes));
214 }
215 
216 /*
217  * write(session)
218  */
219 TEST_F(FirmwareHandlerUploadInProgressTest, WriteToImageReturnsSuccess)
220 {
221     openToInProgress(staticLayoutBlobId);
222     std::vector<std::uint8_t> bytes = {0x01, 0x02};
223     EXPECT_CALL(*imageMock2, write(0, ContainerEq(bytes)))
224         .WillOnce(Return(true));
225     EXPECT_TRUE(handler->write(session, 0, bytes));
226 }
227 
228 TEST_F(FirmwareHandlerUploadInProgressTest, WriteToHashReturnsSuccess)
229 {
230     openToInProgress(hashBlobId);
231     std::vector<std::uint8_t> bytes = {0x01, 0x02};
232     EXPECT_CALL(*hashImageMock, write(0, ContainerEq(bytes)))
233         .WillOnce(Return(true));
234     EXPECT_TRUE(handler->write(session, 0, bytes));
235 }
236 
237 /*
238  * read(session)
239  */
240 TEST_F(FirmwareHandlerUploadInProgressTest, ReadImageFileReturnsFailure)
241 {
242     /* Read is not supported. */
243     openToInProgress(staticLayoutBlobId);
244     EXPECT_THAT(handler->read(session, 0, 32), IsEmpty());
245 }
246 
247 /*
248  * commit(session)
249  */
250 TEST_F(FirmwareHandlerUploadInProgressTest,
251        CommitAgainstImageFileReturnsFailure)
252 {
253     /* Commit is only valid against specific blobs. */
254     openToInProgress(staticLayoutBlobId);
255     EXPECT_FALSE(handler->commit(session, {}));
256 }
257 
258 TEST_F(FirmwareHandlerUploadInProgressTest, CommitAgainstHashFileReturnsFailure)
259 {
260     openToInProgress(hashBlobId);
261     EXPECT_FALSE(handler->commit(session, {}));
262 }
263 
264 /*
265  * deleteBlob(blob)
266  */
267 TEST_F(FirmwareHandlerUploadInProgressTest, DeleteBlobReturnsFalse)
268 {
269     /* Try deleting all blobs, it doesn't really matter which though because you
270      * cannot close out an open session, therefore you must fail to delete
271      * anything unless everything is closed.
272      */
273     openToInProgress(staticLayoutBlobId);
274     auto blobs = handler->getBlobIds();
275     for (const auto& b : blobs)
276     {
277         EXPECT_FALSE(handler->deleteBlob(b));
278     }
279 }
280 
281 /*
282  * expire(session)
283  */
284 TEST_F(FirmwareHandlerUploadInProgressTest, ExpireAbortsProcess)
285 {
286     openToInProgress(staticLayoutBlobId);
287 
288     ASSERT_TRUE(handler->expire(session));
289     EXPECT_THAT(handler->getBlobIds(),
290                 UnorderedElementsAreArray(startingBlobs));
291     expectedState(FirmwareBlobHandler::UpdateState::notYetStarted);
292 }
293 
294 } // namespace
295 } // namespace ipmi_flash
296