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::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, ReadVerisonReturnExpected) 176 { 177 /* It can return as expected, when polling and readBytes succeeds. */ 178 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 179 .WillOnce(Return(session)); 180 ipmiblob::StatResponse readVersionResponse = {}; 181 readVersionResponse.blob_state = 182 blobs::StateFlags::open_read | blobs::StateFlags::committed; 183 readVersionResponse.size = 10; 184 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 185 .WillOnce(Return(readVersionResponse)); 186 std::vector<uint8_t> resp = {0x2d, 0xfe}; 187 EXPECT_CALL(blobMock, readBytes(session, 0, _)).WillOnce(Return(resp)); 188 189 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 190 EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId)); 191 } 192 193 TEST_F(UpdateHandlerTest, ReadVersionExceptionWhenPollingSucceedsReadBytesFails) 194 { 195 /* On readBytes, it can except. */ 196 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 197 .WillOnce(Return(session)); 198 ipmiblob::StatResponse readVersionResponse = {}; 199 readVersionResponse.blob_state = 200 blobs::StateFlags::open_read | blobs::StateFlags::committed; 201 readVersionResponse.size = 10; 202 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 203 .WillOnce(Return(readVersionResponse)); 204 EXPECT_CALL(blobMock, readBytes(session, 0, _)) 205 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 206 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 207 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 208 ToolException); 209 } 210 211 TEST_F(UpdateHandlerTest, ReadVersionReturnsErrorIfPollingFails) 212 { 213 /* It can throw an error, when polling fails. */ 214 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 215 .WillOnce(Return(session)); 216 ipmiblob::StatResponse readVersionResponse = {}; 217 readVersionResponse.blob_state = blobs::StateFlags::commit_error; 218 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 219 .WillOnce(Return(readVersionResponse)); 220 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 221 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 222 ToolException); 223 } 224 225 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException) 226 { 227 /* On open, it can except and this is converted to a ToolException. */ 228 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 229 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 230 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 231 ToolException); 232 } 233 234 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen) 235 { 236 /* It only tries to commit if it's able to open the blob. However, if 237 * committing fails, this error is ignored. 238 */ 239 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 240 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 241 EXPECT_CALL(blobMock, commit(_, _)).Times(0); 242 EXPECT_CALL(blobMock, closeBlob(_)).Times(0); 243 244 updater.cleanArtifacts(); 245 } 246 247 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose) 248 { 249 /* The closeBlob call is called even if commit excepts. */ 250 std::uint16_t session = 0xa5eb; 251 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 252 .WillOnce(Return(session)); 253 EXPECT_CALL(blobMock, commit(session, _)) 254 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 255 EXPECT_CALL(blobMock, closeBlob(session)); 256 257 updater.cleanArtifacts(); 258 } 259 260 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath) 261 { 262 std::uint16_t session = 0xa5eb; 263 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 264 .WillOnce(Return(session)); 265 EXPECT_CALL(blobMock, commit(session, _)); 266 EXPECT_CALL(blobMock, closeBlob(session)); 267 268 updater.cleanArtifacts(); 269 } 270 271 class UpdaterTest : public ::testing::Test 272 { 273 protected: 274 static constexpr char image[] = "image.bin"; 275 static constexpr char signature[] = "signature.bin"; 276 static constexpr char layout[] = "static"; 277 static constexpr char path[] = "/flash/static"; 278 279 ipmiblob::BlobInterfaceMock blobMock; 280 std::uint16_t session = 0xbeef; 281 bool defaultIgnore = false; 282 }; 283 284 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess) 285 { 286 UpdateHandlerMock handler; 287 288 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 289 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 290 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 291 .WillOnce(Return()); 292 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 293 .WillOnce(Return(true)); 294 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 295 .WillOnce(Return(true)); 296 EXPECT_CALL(blobMock, getBlobList()) 297 .WillOnce(Return(std::vector<std::string>({}))); 298 299 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 300 } 301 302 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob) 303 { 304 UpdateHandlerMock handler; 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, defaultIgnore)) 313 .WillOnce(Return(true)); 314 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 315 EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId)) 316 .WillOnce(Return(true)); 317 EXPECT_CALL(blobMock, getBlobList()) 318 .WillOnce(Return(std::vector<std::string>( 319 {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId}))); 320 321 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 322 } 323 324 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate) 325 { 326 UpdateHandlerMock handler; 327 bool updateIgnore = true; 328 329 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 330 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 331 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 332 .WillOnce(Return()); 333 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 334 .WillOnce(Return(true)); 335 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore)) 336 .WillOnce(Return(true)); 337 EXPECT_CALL(blobMock, getBlobList()) 338 .WillOnce(Return(std::vector<std::string>({}))); 339 340 updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore); 341 } 342 343 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure) 344 { 345 UpdateHandlerMock handler; 346 347 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 348 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 349 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 350 .WillOnce(Return()); 351 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 352 .WillOnce(Return(false)); 353 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 354 EXPECT_CALL(blobMock, getBlobList()) 355 .WillOnce(Return(std::vector<std::string>({}))); 356 357 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 358 defaultIgnore), 359 ToolException); 360 } 361 362 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure) 363 { 364 UpdateHandlerMock handler; 365 366 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 367 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 368 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 369 .WillOnce(Return()); 370 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 371 .WillOnce(Return(true)); 372 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 373 .WillOnce(Return(false)); 374 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 375 EXPECT_CALL(blobMock, getBlobList()) 376 .WillOnce(Return(std::vector<std::string>({}))); 377 378 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 379 defaultIgnore), 380 ToolException); 381 } 382 383 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound) 384 { 385 UpdateHandlerMock handler; 386 387 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false)); 388 389 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 390 defaultIgnore), 391 ToolException); 392 } 393 394 } // namespace host_tool 395