#include "data_interface_mock.hpp" #include "flags.hpp" #include "status.hpp" #include "tool_errors.hpp" #include "updater.hpp" #include "updater_mock.hpp" #include "util.hpp" #include #include #include #include #include #include #include namespace host_tool { using ::testing::_; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::Return; using ::testing::Throw; using ::testing::TypedEq; class UpdateHandlerTest : public ::testing::Test { protected: const std::uint16_t session = 0xbeef; DataInterfaceMock handlerMock; ipmiblob::BlobInterfaceMock blobMock; UpdateHandler updater{&blobMock, &handlerMock}; }; TEST_F(UpdateHandlerTest, CheckAvailableSuccess) { EXPECT_CALL(blobMock, getBlobList()) .WillOnce( Return(std::vector({ipmi_flash::staticLayoutBlobId}))); EXPECT_TRUE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId)); } TEST_F(UpdateHandlerTest, CheckAvailableFailure) { EXPECT_CALL(blobMock, getBlobList()) .WillOnce(Return(std::vector())); EXPECT_FALSE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId)); } TEST_F(UpdateHandlerTest, SendFileSuccess) { /* Call sendFile to verify it does what we expect. */ std::string firmwareImage = "image.bin"; std::uint16_t supported = static_cast( ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | static_cast( ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); EXPECT_CALL(handlerMock, supportedType()) .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) .WillOnce(Return(session)); EXPECT_CALL(handlerMock, sendContents(firmwareImage, session)) .WillOnce(Return(true)); EXPECT_CALL(blobMock, closeBlob(session)).Times(1); updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage); } TEST_F(UpdateHandlerTest, SendFileExceptsOnBlobOpening) { std::string firmwareImage = "image.bin"; std::uint16_t supported = static_cast( ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | static_cast( ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); EXPECT_CALL(handlerMock, supportedType()) .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_THROW( updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage), ToolException); } TEST_F(UpdateHandlerTest, SendFileHandlerFailureCausesException) { std::string firmwareImage = "image.bin"; std::uint16_t supported = static_cast( ipmi_flash::FirmwareFlags::UpdateFlags::lpc) | static_cast( ipmi_flash::FirmwareFlags::UpdateFlags::openWrite); EXPECT_CALL(handlerMock, supportedType()) .WillOnce(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc)); EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported)) .WillOnce(Return(session)); EXPECT_CALL(handlerMock, sendContents(firmwareImage, session)) .WillOnce(Return(false)); EXPECT_CALL(blobMock, closeBlob(session)).Times(1); EXPECT_THROW( updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage), ToolException); } TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccess) { EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) .WillOnce(Return(session)); EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return()); ipmiblob::StatResponse verificationResponse = {}; /* the other details of the response are ignored, and should be. */ verificationResponse.metadata.push_back( static_cast(ipmi_flash::ActionStatus::success)); EXPECT_CALL(blobMock, getStat(TypedEq(session))) .WillOnce(Return(verificationResponse)); EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false)); } TEST_F(UpdateHandlerTest, VerifyFileHandleSkipsPollingIfIgnoreStatus) { /* if ignoreStatus, it'll skip polling for a verification result. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) .WillOnce(Return(session)); EXPECT_CALL(blobMock, commit(session, _)).WillOnce(Return()); EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, true)); } TEST_F(UpdateHandlerTest, VerifyFileConvertsOpenBlobExceptionToToolException) { /* On open, it can except and this is converted to a ToolException. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), ToolException); } TEST_F(UpdateHandlerTest, VerifyFileCommitExceptionForwards) { /* On commit, it can except. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _)) .WillOnce(Return(session)); EXPECT_CALL(blobMock, commit(session, _)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false), ToolException); } TEST_F(UpdateHandlerTest, ReadVerisonReturnExpected) { /* It can return as expected, when polling and readBytes succeeds. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) .WillOnce(Return(session)); ipmiblob::StatResponse readVersionResponse = {}; readVersionResponse.blob_state = blobs::StateFlags::open_read | blobs::StateFlags::committed; readVersionResponse.size = 10; EXPECT_CALL(blobMock, getStat(TypedEq(session))) .WillOnce(Return(readVersionResponse)); std::vector resp = {0x2d, 0xfe}; EXPECT_CALL(blobMock, readBytes(session, 0, _)).WillOnce(Return(resp)); EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId)); } TEST_F(UpdateHandlerTest, ReadVersionExceptionWhenPollingSucceedsReadBytesFails) { /* On readBytes, it can except. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) .WillOnce(Return(session)); ipmiblob::StatResponse readVersionResponse = {}; readVersionResponse.blob_state = blobs::StateFlags::open_read | blobs::StateFlags::committed; readVersionResponse.size = 10; EXPECT_CALL(blobMock, getStat(TypedEq(session))) .WillOnce(Return(readVersionResponse)); EXPECT_CALL(blobMock, readBytes(session, 0, _)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), ToolException); } TEST_F(UpdateHandlerTest, ReadVersionReturnsEmptyIfPollingFails) { /* It can return an empty result, when polling fails. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) .WillOnce(Return(session)); ipmiblob::StatResponse readVersionResponse = {}; readVersionResponse.blob_state = blobs::StateFlags::commit_error; EXPECT_CALL(blobMock, getStat(TypedEq(session))) .WillOnce(Return(readVersionResponse)); EXPECT_CALL(blobMock, closeBlob(session)).WillOnce(Return()); EXPECT_THAT(updater.readVersion(ipmi_flash::biosVersionBlobId), IsEmpty()); } TEST_F(UpdateHandlerTest, ReadVersionCovertsOpenBlobExceptionToToolException) { /* On open, it can except and this is converted to a ToolException. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId), ToolException); } TEST_F(UpdateHandlerTest, CleanArtifactsSkipsCleanupIfUnableToOpen) { /* It only tries to commit if it's able to open the blob. However, if * committing fails, this error is ignored. */ EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_CALL(blobMock, commit(_, _)).Times(0); EXPECT_CALL(blobMock, closeBlob(_)).Times(0); updater.cleanArtifacts(); } TEST_F(UpdateHandlerTest, CleanArtifactsIfOpenDoesClose) { /* The closeBlob call is called even if commit excepts. */ std::uint16_t session = 0xa5eb; EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) .WillOnce(Return(session)); EXPECT_CALL(blobMock, commit(session, _)) .WillOnce(Throw(ipmiblob::BlobException("asdf"))); EXPECT_CALL(blobMock, closeBlob(session)); updater.cleanArtifacts(); } TEST_F(UpdateHandlerTest, CleanArtifactsSuccessPath) { std::uint16_t session = 0xa5eb; EXPECT_CALL(blobMock, openBlob(ipmi_flash::cleanupBlobId, _)) .WillOnce(Return(session)); EXPECT_CALL(blobMock, commit(session, _)); EXPECT_CALL(blobMock, closeBlob(session)); updater.cleanArtifacts(); } class UpdaterTest : public ::testing::Test { protected: static constexpr char image[] = "image.bin"; static constexpr char signature[] = "signature.bin"; static constexpr char layout[] = "static"; static constexpr char path[] = "/flash/static"; ipmiblob::BlobInterfaceMock blobMock; std::uint16_t session = 0xbeef; bool defaultIgnore = false; }; TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccess) { UpdateHandlerMock handler; EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) .WillOnce(Return()); EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) .WillOnce(Return(true)); EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) .WillOnce(Return(true)); updaterMain(&handler, image, signature, layout, defaultIgnore); } TEST_F(UpdaterTest, UpdateMainReturnsSuccessWithIgnoreUpdate) { UpdateHandlerMock handler; bool updateIgnore = true; EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) .WillOnce(Return()); EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) .WillOnce(Return(true)); EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, updateIgnore)) .WillOnce(Return(true)); updaterMain(&handler, image, signature, layout, updateIgnore); } TEST_F(UpdaterTest, UpdateMainCleansUpOnFailure) { UpdateHandlerMock handler; EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) .WillOnce(Return()); EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) .WillOnce(Return(false)); EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore), ToolException); } TEST_F(UpdaterTest, UpdateMainExceptsOnUpdateBlobFailure) { UpdateHandlerMock handler; EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(true)); EXPECT_CALL(handler, sendFile(path, image)).WillOnce(Return()); EXPECT_CALL(handler, sendFile(ipmi_flash::hashBlobId, signature)) .WillOnce(Return()); EXPECT_CALL(handler, verifyFile(ipmi_flash::verifyBlobId, defaultIgnore)) .WillOnce(Return(true)); EXPECT_CALL(handler, verifyFile(ipmi_flash::updateBlobId, defaultIgnore)) .WillOnce(Return(false)); EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return()); EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore), ToolException); } TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound) { UpdateHandlerMock handler; EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false)); EXPECT_THROW(updaterMain(&handler, image, signature, layout, defaultIgnore), ToolException); } } // namespace host_tool