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