blob: acaaee797731be0325a4a47e1fca666e6165b312 [file] [log] [blame]
#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 <blobs-ipmid/blobs.hpp>
#include <ipmiblob/blob_errors.hpp>
#include <ipmiblob/test/blob_interface_mock.hpp>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace host_tool
{
using ::testing::_;
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<std::string>({ipmi_flash::staticLayoutBlobId})));
EXPECT_TRUE(updater.checkAvailable(ipmi_flash::staticLayoutBlobId));
}
TEST_F(UpdateHandlerTest, CheckAvailableFailure)
{
EXPECT_CALL(blobMock, getBlobList())
.WillOnce(Return(std::vector<std::string>()));
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<std::uint16_t>(
ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
static_cast<std::uint16_t>(
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<std::uint16_t>(
ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
static_cast<std::uint16_t>(
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<std::uint16_t>(
ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
static_cast<std::uint16_t>(
ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
EXPECT_CALL(handlerMock, supportedType())
.WillRepeatedly(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
.WillRepeatedly(Return(session));
EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
.WillRepeatedly(Return(false));
EXPECT_CALL(blobMock, closeBlob(session)).Times(3);
EXPECT_THROW(
updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage),
ToolException);
}
TEST_F(UpdateHandlerTest, SendFileHandlerPassWithRetries)
{
std::string firmwareImage = "image.bin";
std::uint16_t supported =
static_cast<std::uint16_t>(
ipmi_flash::FirmwareFlags::UpdateFlags::lpc) |
static_cast<std::uint16_t>(
ipmi_flash::FirmwareFlags::UpdateFlags::openWrite);
EXPECT_CALL(handlerMock, supportedType())
.WillRepeatedly(Return(ipmi_flash::FirmwareFlags::UpdateFlags::lpc));
EXPECT_CALL(blobMock, openBlob(ipmi_flash::staticLayoutBlobId, supported))
.WillRepeatedly(Return(session));
EXPECT_CALL(handlerMock, sendContents(firmwareImage, session))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_CALL(blobMock, closeBlob(session)).Times(3);
updater.sendFile(ipmi_flash::staticLayoutBlobId, firmwareImage);
}
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<std::uint8_t>(ipmi_flash::ActionStatus::success));
EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(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, VerifyFileToolException)
{
/* On open, it can except and this is converted to a ToolException. */
EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
.Times(3)
.WillRepeatedly(Throw(ToolException("asdf")));
EXPECT_THROW(updater.verifyFile(ipmi_flash::verifyBlobId, false),
ToolException);
}
TEST_F(UpdateHandlerTest, VerifyFileHandleReturnsTrueOnSuccessWithRetries)
{
EXPECT_CALL(blobMock, openBlob(ipmi_flash::verifyBlobId, _))
.Times(3)
.WillRepeatedly(Return(session));
EXPECT_CALL(blobMock, commit(session, _))
.WillOnce(Throw(ToolException("asdf")))
.WillOnce(Throw(ToolException("asdf")))
.WillOnce(Return());
ipmiblob::StatResponse verificationResponse = {};
/* the other details of the response are ignored, and should be. */
verificationResponse.metadata.push_back(
static_cast<std::uint8_t>(ipmi_flash::ActionStatus::success));
EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
.WillOnce(Return(verificationResponse));
EXPECT_CALL(blobMock, closeBlob(session)).Times(3).WillRepeatedly(Return());
EXPECT_TRUE(updater.verifyFile(ipmi_flash::verifyBlobId, false));
}
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<std::uint16_t>(session)))
.WillOnce(Return(readVersionResponse));
std::vector<uint8_t> 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<std::uint16_t>(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, ReadVersionReturnsErrorIfPollingFails)
{
/* It can throw an error, when polling fails. */
EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
.Times(3)
.WillRepeatedly(Return(session));
ipmiblob::StatResponse readVersionResponse = {};
readVersionResponse.blob_state = blobs::StateFlags::commit_error;
EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
.Times(3)
.WillRepeatedly(Return(readVersionResponse));
EXPECT_CALL(blobMock, closeBlob(session)).Times(3).WillRepeatedly(Return());
EXPECT_THROW(updater.readVersion(ipmi_flash::biosVersionBlobId),
ToolException);
}
TEST_F(UpdateHandlerTest, ReadVersionReturnExpectedWithRetries)
{
/* It can return as expected, when polling and readBytes succeeds. */
EXPECT_CALL(blobMock, openBlob(ipmi_flash::biosVersionBlobId, _))
.Times(3)
.WillRepeatedly(Return(session));
ipmiblob::StatResponse readVersionResponse = {};
readVersionResponse.blob_state = blobs::StateFlags::open_read |
blobs::StateFlags::committed;
readVersionResponse.size = 10;
EXPECT_CALL(blobMock, getStat(TypedEq<std::uint16_t>(session)))
.Times(3)
.WillRepeatedly(Return(readVersionResponse));
std::vector<uint8_t> resp = {0x2d, 0xfe};
EXPECT_CALL(blobMock, readBytes(session, 0, _))
.WillOnce(Throw(ToolException("asdf")))
.WillOnce(Throw(ToolException("asdf")))
.WillOnce(Return(resp));
EXPECT_CALL(blobMock, closeBlob(session)).Times(3).WillRepeatedly(Return());
EXPECT_EQ(resp, updater.readVersion(ipmi_flash::biosVersionBlobId));
}
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));
EXPECT_CALL(blobMock, getBlobList())
.WillOnce(Return(std::vector<std::string>({})));
updaterMain(&handler, &blobMock, image, signature, layout, defaultIgnore);
}
TEST_F(UpdaterTest, UpdateMainReturnsSuccessIfAllSuccessWithDeleteActiveBlob)
{
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));
EXPECT_CALL(handler, cleanArtifacts()).WillOnce(Return());
EXPECT_CALL(blobMock, deleteBlob(ipmi_flash::activeImageBlobId))
.WillOnce(Return(true));
EXPECT_CALL(blobMock, getBlobList())
.WillOnce(Return(std::vector<std::string>(
{ipmi_flash::staticLayoutBlobId, ipmi_flash::activeImageBlobId})));
updaterMain(&handler, &blobMock, 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));
EXPECT_CALL(blobMock, getBlobList())
.WillOnce(Return(std::vector<std::string>({})));
updaterMain(&handler, &blobMock, 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_CALL(blobMock, getBlobList())
.WillOnce(Return(std::vector<std::string>({})));
EXPECT_THROW(updaterMain(&handler, &blobMock, 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_CALL(blobMock, getBlobList())
.WillOnce(Return(std::vector<std::string>({})));
EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
defaultIgnore),
ToolException);
}
TEST_F(UpdaterTest, UpdateMainExceptsIfAvailableNotFound)
{
UpdateHandlerMock handler;
EXPECT_CALL(handler, checkAvailable(path)).WillOnce(Return(false));
EXPECT_THROW(updaterMain(&handler, &blobMock, image, signature, layout,
defaultIgnore),
ToolException);
}
} // namespace host_tool