1 /* The goal of these tests is to verify the behavior of all blob commands given
2  * the current state is updatePending.  This state is achieved as an exit from
3  * verificationCompleted.
4  */
5 #include "firmware_handler.hpp"
6 #include "firmware_unittest.hpp"
7 #include "status.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::IsEmpty;
22 using ::testing::Return;
23 using ::testing::UnorderedElementsAreArray;
24 
25 /*
26  * There are the following calls (parameters may vary):
27  * canHandleBlob(blob)
28  * getBlobIds
29  * deleteBlob(blob)
30  * stat(blob)
31  * stat(session)
32  * open(blob)
33  * close(session)
34  * writemeta(session)
35  * write(session)
36  * read(session)
37  * commit(session)
38  *
39  * Testing canHandleBlob is uninteresting in this state.  Getting the BlobIDs
40  * will inform what canHandleBlob will return.
41  */
42 
43 class FirmwareHandlerUpdatePendingTest : public IpmiOnlyFirmwareStaticTest
44 {};
45 
46 /*
47  * There are the following calls (parameters may vary):
48  * canHandleBlob(blob)
49  * getBlobIds
50  */
51 TEST_F(FirmwareHandlerUpdatePendingTest, GetBlobsListHasExpectedValues)
52 {
53     getToUpdatePending();
54 
55     EXPECT_THAT(handler->getBlobIds(),
56                 UnorderedElementsAreArray({updateBlobId, activeImageBlobId,
57                                            hashBlobId, staticLayoutBlobId}));
58 }
59 
60 /*
61  * open(blob) - because updatePending is in a fileOpen==false state, one can
62  * then open blobs. However, because we're in a special state, we will restrict
63  * them s.t. they can only open the updateBlobId.
64  */
65 TEST_F(FirmwareHandlerUpdatePendingTest,
66        OpenUpdateBlobIdIsSuccessfulAndDoesNotChangeState)
67 {
68     getToUpdatePending();
69 
70     /* Opening the update blob isn't interesting, except it's required for
71      * commit() which triggers the update process.
72      */
73     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
74     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
75 }
76 
77 TEST_F(FirmwareHandlerUpdatePendingTest, OpenAnyBlobOtherThanUpdateFails)
78 {
79     getToUpdatePending();
80 
81     auto blobs = handler->getBlobIds();
82     for (const auto& blob : blobs)
83     {
84         if (blob == updateBlobId)
85         {
86             continue;
87         }
88         EXPECT_FALSE(handler->open(session, flags, blob));
89     }
90 }
91 
92 /*
93  * close(session) - close from this state is uninteresting.
94  */
95 TEST_F(FirmwareHandlerUpdatePendingTest, CloseUpdateBlobDoesNotChangeState)
96 {
97     /* Verify nothing changes when one just opens, then closes the updateBlobId.
98      */
99     getToUpdatePending();
100 
101     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
102 
103     handler->close(session);
104 
105     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
106     EXPECT_TRUE(handler->canHandleBlob(updateBlobId));
107 }
108 
109 /*
110  * writemeta(session) - this will return failure.
111  */
112 TEST_F(FirmwareHandlerUpdatePendingTest, WriteMetaToUpdateBlobReturnsFailure)
113 {
114     getToUpdatePending();
115 
116     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
117     EXPECT_FALSE(handler->writeMeta(session, 0, {0x01}));
118 }
119 
120 /*
121  * write(session)
122  */
123 TEST_F(FirmwareHandlerUpdatePendingTest, WriteToUpdateBlobReturnsFailure)
124 {
125     getToUpdatePending();
126 
127     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
128     EXPECT_FALSE(handler->write(session, 0, {0x01}));
129 }
130 
131 /*
132  * read(session)
133  */
134 TEST_F(FirmwareHandlerUpdatePendingTest, ReadFromUpdateBlobIdReturnsEmpty)
135 {
136     getToUpdatePending();
137     EXPECT_THAT(handler->read(session, 0, 1), IsEmpty());
138 }
139 
140 /*
141  * stat(blob)
142  */
143 TEST_F(FirmwareHandlerUpdatePendingTest, StatOnActiveImageReturnsFailure)
144 {
145     getToUpdatePending();
146     ASSERT_TRUE(handler->canHandleBlob(activeImageBlobId));
147 
148     blobs::BlobMeta meta;
149     EXPECT_FALSE(handler->stat(activeImageBlobId, &meta));
150 }
151 
152 TEST_F(FirmwareHandlerUpdatePendingTest, StatOnUpdateBlobReturnsFailure)
153 {
154     getToUpdatePending();
155     ASSERT_TRUE(handler->canHandleBlob(updateBlobId));
156 
157     blobs::BlobMeta meta;
158     EXPECT_FALSE(handler->stat(updateBlobId, &meta));
159 }
160 
161 TEST_F(FirmwareHandlerUpdatePendingTest, StatOnNormalBlobsReturnsSuccess)
162 {
163     getToUpdatePending();
164 
165     for (const auto& blob : startingBlobs)
166     {
167         ASSERT_TRUE(handler->canHandleBlob(blob));
168 
169         blobs::BlobMeta meta = {};
170         EXPECT_TRUE(handler->stat(blob, &meta));
171         EXPECT_EQ(expectedIdleMeta, meta);
172     }
173 }
174 
175 /*
176  * stat(session)
177  * In this case, you can open updateBlobId without changing state, therefore,
178  * let's call stat() against a session against this file. This done, ahead of
179  * commit() should report the state as "other."
180  */
181 TEST_F(FirmwareHandlerUpdatePendingTest,
182        SessionStatOnUpdateBlobIdReturnsFailure)
183 {
184     getToUpdatePending();
185     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
186     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
187 
188     blobs::BlobMeta meta, expectedMeta = {};
189     expectedMeta.size = 0;
190     expectedMeta.blobState = flags;
191     expectedMeta.metadata.push_back(
192         static_cast<std::uint8_t>(ActionStatus::unknown));
193 
194     EXPECT_TRUE(handler->stat(session, &meta));
195     EXPECT_EQ(expectedMeta, meta);
196     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
197 }
198 
199 /*
200  * commit(session)
201  */
202 TEST_F(FirmwareHandlerUpdatePendingTest,
203        CommitOnUpdateBlobTriggersUpdateAndChangesState)
204 {
205     /* Commit triggers the update mechanism (similarly for the verifyBlobId) and
206      * changes state to updateStarted.
207      */
208     getToUpdatePending();
209     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
210     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
211 
212     EXPECT_CALL(*updateMockPtr, trigger()).WillOnce(Return(true));
213 
214     EXPECT_TRUE(handler->commit(session, {}));
215     expectedState(FirmwareBlobHandler::UpdateState::updateStarted);
216 }
217 
218 TEST_F(FirmwareHandlerUpdatePendingTest,
219        CommitOnUpdateBlobTriggersUpdateAndReturnsFailureDoesNotChangeState)
220 {
221     getToUpdatePending();
222     EXPECT_TRUE(handler->open(session, flags, updateBlobId));
223     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
224 
225     EXPECT_CALL(*updateMockPtr, trigger()).WillOnce(Return(false));
226 
227     EXPECT_FALSE(handler->commit(session, {}));
228     expectedState(FirmwareBlobHandler::UpdateState::updatePending);
229 }
230 
231 /*
232  * deleteBlob(blob)
233  */
234 TEST_F(FirmwareHandlerUpdatePendingTest, DeleteUpdateAbortsProcess)
235 {
236     /* It doesn't matter what blob id is used to delete in the design, so just
237      * delete the update blob id
238      */
239     getToUpdatePending();
240 
241     EXPECT_CALL(*updateMockPtr, abort()).Times(0);
242 
243     ASSERT_TRUE(handler->canHandleBlob(updateBlobId));
244     EXPECT_TRUE(handler->deleteBlob(updateBlobId));
245 
246     EXPECT_THAT(handler->getBlobIds(),
247                 UnorderedElementsAreArray(startingBlobs));
248     expectedState(FirmwareBlobHandler::UpdateState::notYetStarted);
249 }
250 
251 TEST_F(FirmwareHandlerUpdatePendingTest, DeleteActiveImageAbortsProcess)
252 {
253     getToUpdatePending();
254 
255     EXPECT_CALL(*updateMockPtr, abort()).Times(0);
256 
257     ASSERT_TRUE(handler->canHandleBlob(activeImageBlobId));
258     EXPECT_TRUE(handler->deleteBlob(activeImageBlobId));
259 
260     EXPECT_THAT(handler->getBlobIds(),
261                 UnorderedElementsAreArray(startingBlobs));
262     expectedState(FirmwareBlobHandler::UpdateState::notYetStarted);
263 }
264 
265 TEST_F(FirmwareHandlerUpdatePendingTest, DeleteStaticLayoutAbortsProcess)
266 {
267     getToUpdatePending();
268 
269     EXPECT_CALL(*updateMockPtr, abort()).Times(0);
270 
271     ASSERT_TRUE(handler->canHandleBlob(staticLayoutBlobId));
272     EXPECT_TRUE(handler->deleteBlob(staticLayoutBlobId));
273 
274     EXPECT_THAT(handler->getBlobIds(),
275                 UnorderedElementsAreArray(startingBlobs));
276     expectedState(FirmwareBlobHandler::UpdateState::notYetStarted);
277 }
278 
279 TEST_F(FirmwareHandlerUpdatePendingTest, DeleteHashAbortsProcess)
280 {
281     getToUpdatePending();
282 
283     EXPECT_CALL(*updateMockPtr, abort()).Times(0);
284 
285     ASSERT_TRUE(handler->canHandleBlob(hashBlobId));
286     EXPECT_TRUE(handler->deleteBlob(hashBlobId));
287 
288     EXPECT_THAT(handler->getBlobIds(),
289                 UnorderedElementsAreArray(startingBlobs));
290     expectedState(FirmwareBlobHandler::UpdateState::notYetStarted);
291 }
292 
293 /*
294  * expire(session)
295  */
296 TEST_F(FirmwareHandlerUpdatePendingTest, ExpireOnUpdatePendingAborstsProcess)
297 {
298     getToUpdatePending();
299 
300     EXPECT_CALL(*updateMockPtr, abort()).Times(0);
301 
302     ASSERT_TRUE(handler->expire(session));
303     EXPECT_THAT(handler->getBlobIds(),
304                 UnorderedElementsAreArray(startingBlobs));
305     expectedState(FirmwareBlobHandler::UpdateState::notYetStarted);
306 }
307 
308 } // namespace
309 } // namespace ipmi_flash
310