diff options
Diffstat (limited to 'test/process_unittest.cpp')
-rw-r--r-- | test/process_unittest.cpp | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/test/process_unittest.cpp b/test/process_unittest.cpp new file mode 100644 index 0000000..2ffb023 --- /dev/null +++ b/test/process_unittest.cpp @@ -0,0 +1,290 @@ +#include "crc.hpp" +#include "crc_mock.hpp" +#include "ipmi.hpp" +#include "manager_mock.hpp" +#include "process.hpp" + +#include <cstring> + +#include <gtest/gtest.h> + +namespace blobs +{ + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::StrictMock; + +// ipmid.hpp isn't installed where we can grab it and this value is per BMC +// SoC. +#define MAX_IPMI_BUFFER 64 + +namespace +{ + +void EqualFunctions(IpmiBlobHandler lhs, IpmiBlobHandler rhs) +{ + EXPECT_FALSE(lhs == nullptr); + EXPECT_FALSE(rhs == nullptr); + + ipmi_ret_t (*const* lPtr)(ManagerInterface*, const uint8_t*, uint8_t*, + size_t*) = + lhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*, + size_t*)>(); + + ipmi_ret_t (*const* rPtr)(ManagerInterface*, const uint8_t*, uint8_t*, + size_t*) = + rhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*, + size_t*)>(); + + EXPECT_TRUE(lPtr); + EXPECT_TRUE(rPtr); + EXPECT_EQ(*lPtr, *rPtr); + return; +} + +} // namespace + +TEST(ValidateBlobCommandTest, InvalidCommandReturnsFailure) +{ + // Verify we handle an invalid command. + + StrictMock<CrcMock> crc; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + request[0] = 0xff; // There is no command 0xff. + dataLen = sizeof(uint8_t); // There is no payload for CRC. + + EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); +} + +TEST(ValidateBlobCommandTest, ValidCommandWithoutPayload) +{ + // Verify we handle a valid command that doesn't have a payload. + + StrictMock<CrcMock> crc; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + request[0] = BlobOEMCommands::bmcBlobGetCount; + dataLen = sizeof(uint8_t); // There is no payload for CRC. + + IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); + EXPECT_FALSE(res == nullptr); + EqualFunctions(getBlobCount, res); +} + +TEST(ValidateBlobCommandTest, WithPayloadMinimumLengthIs3VerifyChecks) +{ + // Verify that if there's a payload, it's at least one command byte and + // two bytes for the crc16 and then one data byte. + + StrictMock<CrcMock> crc; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + request[0] = BlobOEMCommands::bmcBlobGetCount; + dataLen = sizeof(uint8_t) + sizeof(uint16_t); + // There is a payload, but there are insufficient bytes. + + EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); +} + +TEST(ValidateBlobCommandTest, WithPayloadAndInvalidCrc) +{ + // Verify that the CRC is checked, and failure is reported. + + StrictMock<CrcMock> crc; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request); + req->cmd = BlobOEMCommands::bmcBlobWrite; + req->crc = 0x34; + req->sessionId = 0x54; + req->offset = 0x100; + + uint8_t expectedBytes[2] = {0x66, 0x67}; + std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes)); + + dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes); + + // skip over cmd and crc. + size_t expectedLen = dataLen - 3; + + EXPECT_CALL(crc, clear()); + EXPECT_CALL(crc, compute(_, expectedLen)) + .WillOnce(Invoke([&](const uint8_t* bytes, uint32_t length) { + EXPECT_EQ(0, std::memcmp(&request[3], bytes, length)); + })); + EXPECT_CALL(crc, get()).WillOnce(Return(0x1234)); + + EXPECT_EQ(nullptr, validateBlobCommand(&crc, request, reply, &dataLen)); +} + +TEST(ValidateBlobCommandTest, WithPayloadAndValidCrc) +{ + // Verify the CRC is checked and if it matches, return the handler. + + StrictMock<CrcMock> crc; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request); + req->cmd = BlobOEMCommands::bmcBlobWrite; + req->crc = 0x3412; + req->sessionId = 0x54; + req->offset = 0x100; + + uint8_t expectedBytes[2] = {0x66, 0x67}; + std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes)); + + dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes); + + // skip over cmd and crc. + size_t expectedLen = dataLen - 3; + + EXPECT_CALL(crc, clear()); + EXPECT_CALL(crc, compute(_, expectedLen)) + .WillOnce(Invoke([&](const uint8_t* bytes, uint32_t length) { + EXPECT_EQ(0, std::memcmp(&request[3], bytes, length)); + })); + EXPECT_CALL(crc, get()).WillOnce(Return(0x3412)); + + IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); + EXPECT_FALSE(res == nullptr); + EqualFunctions(writeBlob, res); +} + +TEST(ValidateBlobCommandTest, InputIntegrationTest) +{ + // Given a request buffer generated by the host-side utility, verify it is + // properly routed. + + Crc16 crc; + size_t dataLen; + uint8_t request[] = {0x02, 0x88, 0x21, 0x03, 0x00, 0x2f, 0x64, 0x65, 0x76, + 0x2f, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x2f, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x73, + 0x73, 0x74, 0x68, 0x72, 0x75, 0x00}; + + // The above request to open a file for reading & writing named: + // "/dev/haven/command_passthru" + + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + dataLen = sizeof(request); + + IpmiBlobHandler res = validateBlobCommand(&crc, request, reply, &dataLen); + EXPECT_FALSE(res == nullptr); + EqualFunctions(openBlob, res); +} + +TEST(ProcessBlobCommandTest, CommandReturnsNotOk) +{ + // Verify that if the IPMI command handler returns not OK that this is + // noticed and returned. + + StrictMock<CrcMock> crc; + StrictMock<ManagerMock> manager; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, + size_t* dataLen) { return IPMI_CC_INVALID; }; + + dataLen = sizeof(request); + + EXPECT_EQ(IPMI_CC_INVALID, + processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); +} + +TEST(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload) +{ + // Verify that if the IPMI command handler returns OK but without a payload + // it doesn't try to compute a CRC. + + StrictMock<CrcMock> crc; + StrictMock<ManagerMock> manager; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) { + (*dataLen) = 0; + return IPMI_CC_OK; + }; + + dataLen = sizeof(request); + + EXPECT_EQ(IPMI_CC_OK, + processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); +} + +TEST(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength) +{ + // There is a minimum payload length of 3 bytes, this command returns a + // payload of 2 bytes. + + StrictMock<CrcMock> crc; + StrictMock<ManagerMock> manager; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + + IpmiBlobHandler h = [](ManagerInterface* mgr, const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) { + (*dataLen) = sizeof(uint16_t); + return IPMI_CC_OK; + }; + + dataLen = sizeof(request); + + EXPECT_EQ(IPMI_CC_INVALID, + processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); +} + +TEST(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength) +{ + // There is a minimum payload length of 3 bytes, this command returns a + // payload of 3 bytes and the crc code is called to process the payload. + + StrictMock<CrcMock> crc; + StrictMock<ManagerMock> manager; + size_t dataLen; + uint8_t request[MAX_IPMI_BUFFER] = {0}; + uint8_t reply[MAX_IPMI_BUFFER] = {0}; + uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t); + + IpmiBlobHandler h = [payloadLen](ManagerInterface* mgr, + const uint8_t* reqBuf, + uint8_t* replyCmdBuf, size_t* dataLen) { + (*dataLen) = payloadLen; + replyCmdBuf[2] = 0x56; + return IPMI_CC_OK; + }; + + dataLen = sizeof(request); + + EXPECT_CALL(crc, clear()); + EXPECT_CALL(crc, compute(_, payloadLen)); + EXPECT_CALL(crc, get()).WillOnce(Return(0x3412)); + + EXPECT_EQ(IPMI_CC_OK, + processBlobCommand(h, &manager, &crc, request, reply, &dataLen)); + EXPECT_EQ(dataLen, payloadLen); + + uint8_t expectedBytes[3] = {0x12, 0x34, 0x56}; + EXPECT_EQ(0, std::memcmp(expectedBytes, reply, sizeof(expectedBytes))); +} +} // namespace blobs |