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 .WillRepeatedly(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); 112 113 EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) 114 .WillRepeatedly(Return(session)); 115 116 EXPECT_CALL(handlerMock, sendContents(firmwareImage, session)) 117 .WillRepeatedly(Return(false)); 118 119 EXPECT_CALL(blobMock, closeBlob(session)).Times(3); 120 121 EXPECT_THROW( 122 updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage), 123 ToolException); 124 } 125 126 TEST_F(UpdateHandlerTest, SendFileHandlerPassWithRetries) 127 { 128 std::string firmwareImage = "image.bin"; 129 130 std::uint16_t supported = 131 static_cast<std::uint16_t>( 132 ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | 133 static_cast<std::uint16_t>( 134 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); 135 136 EXPECT_CALL(handlerMock, supportedType()) 137 .WillRepeatedly(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); 138 139 EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) 140 .WillRepeatedly(Return(session)); 141 142 EXPECT_CALL(handlerMock, sendContents(firmwareImage, session)) 143 .WillOnce(Return(false)) 144 .WillOnce(Return(false)) 145 .WillOnce(Return(true)); 146 147 EXPECT_CALL(blobMock, closeBlob(session)).Times(3); 148 149 updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage); 150 } 151 152 TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccess) 153 { 154 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 155 .WillOnce(Return(session)); 156 EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return()); 157 ipmiblob::StatResponse verificationResponse = {}; 158 /* the other details of the response are ignored, and should be. */ 159 verificationResponse.metadata.push_back( 160 static_cast<std::uint8_t>(ipmi_flash::ActionStatus::success)); 161 162 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 163 .WillOnce(Return(verificationResponse)); 164 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 165 166 EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false)); 167 } 168 169 TEST_F(UpdateHandlerTest, VerifyFileHandleSkipsPollingIfIgnoreStatus) 170 { 171 /* if ignoreStatus, it'll skip polling for a verification result. */ 172 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 173 .WillOnce(Return(session)); 174 EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return()); 175 176 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 177 178 EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, true)); 179 } 180 181 TEST_F(UpdateHandlerTest, VerifyFileConvertsOpenBlobExceptionToToolException) 182 { 183 /* On open, it can except and this is converted to a ToolException. */ 184 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 185 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 186 EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), 187 ToolException); 188 } 189 190 TEST_F(UpdateHandlerTest, VerifyFileToolException) 191 { 192 /* On open, it can except and this is converted to a ToolException. */ 193 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 194 .Times(3) 195 .WillRepeatedly(Throw(ToolException("asdf"))); 196 EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), 197 ToolException); 198 } 199 200 TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccessWithRetries) 201 { 202 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 203 .Times(3) 204 .WillRepeatedly(Return(session)); 205 EXPECT_CALL(blobMock, commit(session, _)) 206 .WillOnce(Throw(ToolException("asdf"))) 207 .WillOnce(Throw(ToolException("asdf"))) 208 .WillOnce(Return()); 209 ipmiblob::StatResponse verificationResponse = {}; 210 /* the other details of the response are ignored, and should be. */ 211 verificationResponse.metadata.push_back( 212 static_cast<std::uint8_t>(ipmi_flash::ActionStatus::success)); 213 214 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 215 .WillOnce(Return(verificationResponse)); 216 EXPECT_CALL(blobMock, closeBlob(session)).Times(3).WillRepeatedly(Return()); 217 218 EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false)); 219 } 220 221 TEST_F(UpdateHandlerTest, VerifyFileCommitExceptionForwards) 222 { 223 /* On commit, it can except. */ 224 EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) 225 .WillOnce(Return(session)); 226 EXPECT_CALL(blobMock, commit(session, _)) 227 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 228 EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), 229 ToolException); 230 } 231 232 TEST_F(UpdateHandlerTest, ReadVerisonReturnExpected) 233 { 234 /* It can return as expected, when polling and readBytes succeeds. */ 235 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 236 .WillOnce(Return(session)); 237 ipmiblob::StatResponse readVersionResponse = {}; 238 readVersionResponse.blob_state = blobs::StateFlags::open_read | 239 blobs::StateFlags::committed; 240 readVersionResponse.size = 10; 241 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 242 .WillOnce(Return(readVersionResponse)); 243 std::vector<uint8_t> resp = {0x2d, 0xfe}; 244 EXPECT_CALL(blobMock, readBytes(session, 0, _)).WillOnce(Return(resp)); 245 246 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 247 EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId)); 248 } 249 250 TEST_F(UpdateHandlerTest, ReadVersionExceptionWhenPollingSucceedsReadBytesFails) 251 { 252 /* On readBytes, it can except. */ 253 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 254 .WillOnce(Return(session)); 255 ipmiblob::StatResponse readVersionResponse = {}; 256 readVersionResponse.blob_state = blobs::StateFlags::open_read | 257 blobs::StateFlags::committed; 258 readVersionResponse.size = 10; 259 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 260 .WillOnce(Return(readVersionResponse)); 261 EXPECT_CALL(blobMock, readBytes(session, 0, _)) 262 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 263 EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); 264 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 265 ToolException); 266 } 267 268 TEST_F(UpdateHandlerTest, ReadVersionReturnsErrorIfPollingFails) 269 { 270 /* It can throw an error, when polling fails. */ 271 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 272 .Times(3) 273 .WillRepeatedly(Return(session)); 274 ipmiblob::StatResponse readVersionResponse = {}; 275 readVersionResponse.blob_state = blobs::StateFlags::commit_error; 276 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 277 .Times(3) 278 .WillRepeatedly(Return(readVersionResponse)); 279 EXPECT_CALL(blobMock, closeBlob(session)).Times(3).WillRepeatedly(Return()); 280 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 281 ToolException); 282 } 283 284 TEST_F(UpdateHandlerTest, ReadVersionReturnExpectedWithRetries) 285 { 286 /* It can return as expected, when polling and readBytes succeeds. */ 287 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 288 .Times(3) 289 .WillRepeatedly(Return(session)); 290 ipmiblob::StatResponse readVersionResponse = {}; 291 readVersionResponse.blob_state = blobs::StateFlags::open_read | 292 blobs::StateFlags::committed; 293 readVersionResponse.size = 10; 294 EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session))) 295 .Times(3) 296 .WillRepeatedly(Return(readVersionResponse)); 297 std::vector<uint8_t> resp = {0x2d, 0xfe}; 298 EXPECT_CALL(blobMock, readBytes(session, 0, _)) 299 .WillOnce(Throw(ToolException("asdf"))) 300 .WillOnce(Throw(ToolException("asdf"))) 301 .WillOnce(Return(resp)); 302 303 EXPECT_CALL(blobMock, closeBlob(session)).Times(3).WillRepeatedly(Return()); 304 EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId)); 305 } 306 307 TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException) 308 { 309 /* On open, it can except and this is converted to a ToolException. */ 310 EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) 311 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 312 EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), 313 ToolException); 314 } 315 316 TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen) 317 { 318 /* It only tries to commit if it's able to open the blob. However, if 319 * committing fails, this error is ignored. 320 */ 321 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 322 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 323 EXPECT_CALL(blobMock, commit(_, _)).Times(0); 324 EXPECT_CALL(blobMock, closeBlob(_)).Times(0); 325 326 updater.cleanArtifacts(); 327 } 328 329 TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose) 330 { 331 /* The closeBlob call is called even if commit excepts. */ 332 std::uint16_t session = 0xa5eb; 333 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 334 .WillOnce(Return(session)); 335 EXPECT_CALL(blobMock, commit(session, _)) 336 .WillOnce(Throw(ipmiblob::BlobException("asdf"))); 337 EXPECT_CALL(blobMock, closeBlob(session)); 338 339 updater.cleanArtifacts(); 340 } 341 342 TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath) 343 { 344 std::uint16_t session = 0xa5eb; 345 EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) 346 .WillOnce(Return(session)); 347 EXPECT_CALL(blobMock, commit(session, _)); 348 EXPECT_CALL(blobMock, closeBlob(session)); 349 350 updater.cleanArtifacts(); 351 } 352 353 class UpdaterTest : public ::testing::Test 354 { 355 protected: 356 static constexpr char image[] = "image.bin"; 357 static constexpr char signature[] = "signature.bin"; 358 static constexpr char layout[] = "static"; 359 static constexpr char path[] = "/flash/static"; 360 361 ipmiblob::BlobInterfaceMock blobMock; 362 std::uint16_t session = 0xbeef; 363 bool defaultIgnore = false; 364 }; 365 366 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess) 367 { 368 UpdateHandlerMock handler; 369 370 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 371 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 372 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 373 .WillOnce(Return()); 374 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 375 .WillOnce(Return(true)); 376 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 377 .WillOnce(Return(true)); 378 EXPECT_CALL(blobMock, getBlobList()) 379 .WillOnce(Return(std::vector<std::string>({}))); 380 381 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 382 } 383 384 TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob) 385 { 386 UpdateHandlerMock handler; 387 388 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 389 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 390 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 391 .WillOnce(Return()); 392 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 393 .WillOnce(Return(true)); 394 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 395 .WillOnce(Return(true)); 396 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 397 EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId)) 398 .WillOnce(Return(true)); 399 EXPECT_CALL(blobMock, getBlobList()) 400 .WillOnce(Return(std::vector<std::string>( 401 {ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId}))); 402 403 updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore); 404 } 405 406 TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate) 407 { 408 UpdateHandlerMock handler; 409 bool updateIgnore = true; 410 411 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 412 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 413 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 414 .WillOnce(Return()); 415 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 416 .WillOnce(Return(true)); 417 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore)) 418 .WillOnce(Return(true)); 419 EXPECT_CALL(blobMock, getBlobList()) 420 .WillOnce(Return(std::vector<std::string>({}))); 421 422 updaterMain(&handler, &blobMock, image, signature, layout, updateIgnore); 423 } 424 425 TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure) 426 { 427 UpdateHandlerMock handler; 428 429 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 430 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 431 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 432 .WillOnce(Return()); 433 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 434 .WillOnce(Return(false)); 435 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 436 EXPECT_CALL(blobMock, getBlobList()) 437 .WillOnce(Return(std::vector<std::string>({}))); 438 439 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 440 defaultIgnore), 441 ToolException); 442 } 443 444 TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure) 445 { 446 UpdateHandlerMock handler; 447 448 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); 449 EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); 450 EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) 451 .WillOnce(Return()); 452 EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) 453 .WillOnce(Return(true)); 454 EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) 455 .WillOnce(Return(false)); 456 EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); 457 EXPECT_CALL(blobMock, getBlobList()) 458 .WillOnce(Return(std::vector<std::string>({}))); 459 460 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 461 defaultIgnore), 462 ToolException); 463 } 464 465 TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound) 466 { 467 UpdateHandlerMock handler; 468 469 EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false)); 470 471 EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout, 472 defaultIgnore), 473 ToolException); 474 } 475 476 } // namespace host_tool 477