initial commit

Add initial code from phosphor-ipmi-flash/tools that was not specific to
firmware update over ipmi-blobs.

Change-Id: I360537a7392347fe989397a699f6a712bc36e62c
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..05d9b1d
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,25 @@
+@VALGRIND_CHECK_RULES@
+@CODE_COVERAGE_RULES@
+
+gtest_cppflags = $(AM_CPPFLAGS) $(GTEST_CFLAGS) $(GMOCK_CFLAGS)
+gtest_ldadd = $(GTEST_LIBS) $(GMOCK_LIBS) -lgmock_main
+
+check_PROGRAMS =
+TESTS = $(check_PROGRAMS)
+
+check_PROGRAMS += tools_blob_unittest
+tools_blob_unittest_SOURCES = tools_blob_unittest.cpp
+tools_blob_unittest_CPPFLAGS = $(gtest_cppflags)
+tools_blob_unittest_LDADD = $(gtest_ldadd)
+tools_blob_unittest_LDADD += $(top_builddir)/src/ipmiblob/blob_handler.o
+
+check_PROGRAMS += tools_ipmi_unittest
+tools_ipmi_unittest_SOURCES = tools_ipmi_unittest.cpp
+tools_ipmi_unittest_CPPFLAGS = $(gtest_cppflags)
+tools_ipmi_unittest_LDADD = $(gtest_ldadd)
+tools_ipmi_unittest_LDADD += $(top_builddir)/src/ipmiblob/ipmi_handler.o
+
+check_PROGRAMS += tools_ipmi_error_unittest
+tools_ipmi_error_unittest_SOURCES = tools_ipmi_error_unittest.cpp
+tools_ipmi_error_unittest_CPPFLAGS = $(gtest_cppflags)
+tools_ipmi_error_unittest_LDADD = $(gtest_ldadd)
diff --git a/test/crc_mock.hpp b/test/crc_mock.hpp
new file mode 100644
index 0000000..293ec24
--- /dev/null
+++ b/test/crc_mock.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+class CrcInterface
+{
+  public:
+    virtual ~CrcInterface() = default;
+
+    virtual std::uint16_t
+        generateCrc(const std::vector<std::uint8_t>& data) const = 0;
+};
+
+class CrcMock : public CrcInterface
+{
+  public:
+    virtual ~CrcMock() = default;
+    MOCK_CONST_METHOD1(generateCrc,
+                       std::uint16_t(const std::vector<std::uint8_t>&));
+};
diff --git a/test/internal_sys_mock.hpp b/test/internal_sys_mock.hpp
new file mode 100644
index 0000000..b4ba4b1
--- /dev/null
+++ b/test/internal_sys_mock.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <unistd.h>
+
+#include <ipmiblob/internal/sys.hpp>
+
+#include <gmock/gmock.h>
+
+namespace internal
+{
+
+class InternalSysMock : public Sys
+{
+  public:
+    virtual ~InternalSysMock() = default;
+
+    MOCK_CONST_METHOD2(open, int(const char*, int));
+    MOCK_CONST_METHOD3(read, int(int, void*, std::size_t));
+    MOCK_CONST_METHOD1(close, int(int));
+    MOCK_CONST_METHOD6(mmap, void*(void*, std::size_t, int, int, int, off_t));
+    MOCK_CONST_METHOD2(munmap, int(void*, std::size_t));
+    MOCK_CONST_METHOD0(getpagesize, int());
+    MOCK_CONST_METHOD3(ioctl, int(int, unsigned long, void*));
+    MOCK_CONST_METHOD3(poll, int(struct pollfd*, nfds_t, int));
+};
+
+} // namespace internal
diff --git a/test/tools_blob_unittest.cpp b/test/tools_blob_unittest.cpp
new file mode 100644
index 0000000..f7f58c4
--- /dev/null
+++ b/test/tools_blob_unittest.cpp
@@ -0,0 +1,289 @@
+#include "crc_mock.hpp"
+
+#include <ipmiblob/blob_handler.hpp>
+#include <ipmiblob/test/ipmi_interface_mock.hpp>
+
+#include <gtest/gtest.h>
+
+namespace host_tool
+{
+CrcInterface* crcIntf = nullptr;
+
+std::uint16_t generateCrc(const std::vector<std::uint8_t>& data)
+{
+    return (crcIntf) ? crcIntf->generateCrc(data) : 0x00;
+}
+
+using ::testing::Eq;
+using ::testing::Return;
+
+class BlobHandlerTest : public ::testing::Test
+{
+  protected:
+    void SetUp() override
+    {
+        crcIntf = &crcMock;
+    }
+
+    CrcMock crcMock;
+};
+
+TEST_F(BlobHandlerTest, getCountIpmiHappy)
+{
+    /* Verify returns the value specified by the IPMI response. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobGetCount};
+
+    /* return 1 blob count. */
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00,
+                                      0x01, 0x00, 0x00, 0x00};
+
+    std::vector<std::uint8_t> bytes = {0x01, 0x00, 0x00, 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+    EXPECT_EQ(1, blob.getBlobCount());
+}
+
+TEST_F(BlobHandlerTest, enumerateBlobIpmiHappy)
+{
+    /* Verify returns the name specified by the IPMI response. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobEnumerate,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00};
+
+    /* return value. */
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00,
+                                      'a',  'b',  'c',  'd',  0x00};
+
+    std::vector<std::uint8_t> bytes = {'a', 'b', 'c', 'd', 0x00};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00, 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+    EXPECT_STREQ("abcd", blob.enumerateBlob(1).c_str());
+}
+
+TEST_F(BlobHandlerTest, enumerateBlobIpmiNoBytes)
+{
+    /* Simulate a case where the IPMI command returns no data. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobEnumerate,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00};
+
+    /* return value. */
+    std::vector<std::uint8_t> resp = {};
+
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00, 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+    EXPECT_STREQ("", blob.enumerateBlob(1).c_str());
+}
+
+TEST_F(BlobHandlerTest, getBlobListIpmiHappy)
+{
+    /* Verify returns the list built via the above two commands. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request1 = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobGetCount};
+
+    /* return 1 blob count. */
+    std::vector<std::uint8_t> resp1 = {0xcf, 0xc2, 0x00, 0x00, 0x00,
+                                       0x01, 0x00, 0x00, 0x00};
+
+    std::vector<std::uint8_t> bytes1 = {0x01, 0x00, 0x00, 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(bytes1))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request1))).WillOnce(Return(resp1));
+
+    std::vector<std::uint8_t> request2 = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobEnumerate,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00};
+
+    /* return value. */
+    std::vector<std::uint8_t> resp2 = {0xcf, 0xc2, 0x00, 0x00, 0x00,
+                                       'a',  'b',  'c',  'd',  0x00};
+
+    std::vector<std::uint8_t> reqCrc = {0x00, 0x00, 0x00, 0x00};
+    std::vector<std::uint8_t> bytes2 = {'a', 'b', 'c', 'd', 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(bytes2))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request2))).WillOnce(Return(resp2));
+
+    /* A std::string is not nul-terminated by default. */
+    std::vector<std::string> expectedList = {std::string{"abcd"}};
+
+    EXPECT_EQ(expectedList, blob.getBlobList());
+}
+
+TEST_F(BlobHandlerTest, getStatWithMetadata)
+{
+    /* Stat received metadata. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobStat,
+        0x00, 0x00, 'a',  'b',
+        'c',  'd',  0x00};
+
+    /* return blob_state: 0xffff, size: 0x00, metadata 0x3445 */
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00, 0xff, 0xff,
+                                      0x00, 0x00, 0x00, 0x00, 0x02, 0x34, 0x45};
+
+    std::vector<std::uint8_t> reqCrc = {'a', 'b', 'c', 'd', 0x00};
+    std::vector<std::uint8_t> respCrc = {0xff, 0xff, 0x00, 0x00, 0x00,
+                                         0x00, 0x02, 0x34, 0x45};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(respCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    auto meta = blob.getStat("abcd");
+    EXPECT_EQ(meta.blob_state, 0xffff);
+    EXPECT_EQ(meta.size, 0x00);
+    std::vector<std::uint8_t> metadata = {0x34, 0x45};
+    EXPECT_EQ(metadata, meta.metadata);
+}
+
+TEST_F(BlobHandlerTest, getStatNoMetadata)
+{
+    /* Stat received no metadata. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobStat,
+        0x00, 0x00, 'a',  'b',
+        'c',  'd',  0x00};
+
+    /* return blob_state: 0xffff, size: 0x00, metadata 0x3445 */
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00, 0xff,
+                                      0xff, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+    std::vector<std::uint8_t> reqCrc = {'a', 'b', 'c', 'd', 0x00};
+    std::vector<std::uint8_t> respCrc = {0xff, 0xff, 0x00, 0x00,
+                                         0x00, 0x00, 0x00};
+
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(respCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    auto meta = blob.getStat("abcd");
+    EXPECT_EQ(meta.blob_state, 0xffff);
+    EXPECT_EQ(meta.size, 0x00);
+    std::vector<std::uint8_t> metadata = {};
+    EXPECT_EQ(metadata, meta.metadata);
+}
+
+TEST_F(BlobHandlerTest, openBlobSucceeds)
+{
+    /* The open blob succeeds. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobOpen,
+        0x00, 0x00, 0x02, 0x04,
+        'a',  'b',  'c',  'd',
+        0x00};
+
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00, 0xfe, 0xed};
+
+    std::vector<std::uint8_t> reqCrc = {0x02, 0x04, 'a', 'b', 'c', 'd', 0x00};
+    std::vector<std::uint8_t> respCrc = {0xfe, 0xed};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(respCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    const int writeBit = (1 << 1);
+    const int lpcBit = (1 << 10);
+
+    auto session = blob.openBlob("abcd", writeBit | lpcBit);
+    EXPECT_EQ(0xedfe, session);
+}
+
+TEST_F(BlobHandlerTest, closeBlobSucceeds)
+{
+    /* The close succeeds. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobClose,
+        0x00, 0x00, 0x01, 0x00};
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    blob.closeBlob(0x0001);
+}
+
+TEST_F(BlobHandlerTest, writeBytesSucceeds)
+{
+    /* The write bytes succeeds. */
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobWrite,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        'a',  'b',  'c',  'd'};
+
+    std::vector<std::uint8_t> bytes = {'a', 'b', 'c', 'd'};
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00, 0x00, 0x00,
+                                        0x00, 'a',  'b',  'c',  'd'};
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    blob.writeBytes(0x0001, 0, bytes);
+}
+
+TEST_F(BlobHandlerTest, readBytesSucceeds)
+{
+    /* The reading of bytes succeeds. */
+
+    IpmiInterfaceMock ipmiMock;
+    BlobHandler blob(&ipmiMock);
+
+    std::vector<std::uint8_t> request = {
+        0xcf, 0xc2, 0x00, BlobHandler::BlobOEMCommands::bmcBlobRead,
+        0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00};
+
+    std::vector<std::uint8_t> expectedBytes = {'a', 'b', 'c', 'd'};
+    std::vector<std::uint8_t> resp = {0xcf, 0xc2, 0x00, 0x00, 0x00,
+                                      'a',  'b',  'c',  'd'};
+    std::vector<std::uint8_t> reqCrc = {0x01, 0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x04, 0x00, 0x00, 0x00};
+    std::vector<std::uint8_t> respCrc = {'a', 'b', 'c', 'd'};
+
+    EXPECT_CALL(crcMock, generateCrc(Eq(reqCrc))).WillOnce(Return(0x00));
+    EXPECT_CALL(crcMock, generateCrc(Eq(respCrc))).WillOnce(Return(0x00));
+
+    EXPECT_CALL(ipmiMock, sendPacket(Eq(request))).WillOnce(Return(resp));
+
+    EXPECT_EQ(blob.readBytes(0x0001, 0, 4), expectedBytes);
+}
+
+} // namespace host_tool
diff --git a/test/tools_ipmi_error_unittest.cpp b/test/tools_ipmi_error_unittest.cpp
new file mode 100644
index 0000000..05048e2
--- /dev/null
+++ b/test/tools_ipmi_error_unittest.cpp
@@ -0,0 +1,28 @@
+#include <ipmiblob/ipmi_errors.hpp>
+
+#include <gtest/gtest.h>
+
+namespace host_tool
+{
+
+TEST(IpmiExceptionTest, VerifyTimedOutIsString)
+{
+    /* Verify that throwing the exception with the cc code for timed out gets
+     * converted to the human readable string.
+     */
+    bool verified = false;
+
+    try
+    {
+        throw IpmiException(0xc3);
+    }
+    catch (const IpmiException& i)
+    {
+        EXPECT_STREQ("Received IPMI_CC: timeout", i.what());
+        verified = true;
+    }
+
+    EXPECT_TRUE(verified);
+}
+
+} // namespace host_tool
diff --git a/test/tools_ipmi_unittest.cpp b/test/tools_ipmi_unittest.cpp
new file mode 100644
index 0000000..9516d46
--- /dev/null
+++ b/test/tools_ipmi_unittest.cpp
@@ -0,0 +1,22 @@
+#include "internal_sys_mock.hpp"
+
+#include <ipmiblob/ipmi_errors.hpp>
+#include <ipmiblob/ipmi_handler.hpp>
+
+namespace host_tool
+{
+
+using ::testing::_;
+using ::testing::Return;
+
+TEST(IpmiHandlerTest, OpenAllFails)
+{
+    /* Open against all device files fail. */
+    internal::InternalSysMock sysMock;
+    IpmiHandler ipmi(&sysMock);
+
+    EXPECT_CALL(sysMock, open(_, _)).WillRepeatedly(Return(-1));
+    EXPECT_THROW(ipmi.open(), IpmiException);
+}
+
+} // namespace host_tool