new command: BmcBlobWriteMeta

Implement new command BmcBlobWriteMeta.

Change-Id: I2e148f4bde4ef5d24db7e30bb02bdde024d9166a
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/Makefile.am b/test/Makefile.am
index 29586bf..5e0cdb0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -21,6 +21,7 @@
 	ipmi_commit_unittest \
 	ipmi_read_unittest \
 	ipmi_write_unittest \
+	ipmi_writemeta_unittest \
 	ipmi_validate_unittest \
 	manager_unittest \
 	manager_getsession_unittest \
@@ -32,6 +33,7 @@
 	manager_delete_unittest \
 	manager_write_unittest \
 	manager_read_unittest \
+	manager_writemeta_unittest \
 	process_unittest \
 	crc_unittest
 TESTS = $(check_PROGRAMS)
@@ -69,6 +71,9 @@
 ipmi_write_unittest_SOURCES = ipmi_write_unittest.cpp
 ipmi_write_unittest_LDADD = $(top_builddir)/ipmi.o
 
+ipmi_writemeta_unittest_SOURCES = ipmi_writemeta_unittest.cpp
+ipmi_writemeta_unittest_LDADD = $(top_builddir)/ipmi.o
+
 ipmi_validate_unittest_SOURCES = ipmi_validate_unittest.cpp
 ipmi_validate_unittest_LDADD = $(top_builddir)/ipmi.o
 
@@ -102,6 +107,9 @@
 manager_read_unittest_SOURCES = manager_read_unittest.cpp
 manager_read_unittest_LDADD = $(top_builddir)/manager.o
 
+manager_writemeta_unittest_SOURCES = manager_writemeta_unittest.cpp
+manager_writemeta_unittest_LDADD = $(top_builddir)/manager.o
+
 process_unittest_SOURCES = process_unittest.cpp
 process_unittest_LDADD = $(top_builddir)/process.o $(top_builddir)/ipmi.o \
 	$(top_builddir)/crc.o
diff --git a/test/ipmi_writemeta_unittest.cpp b/test/ipmi_writemeta_unittest.cpp
new file mode 100644
index 0000000..2dfbe59
--- /dev/null
+++ b/test/ipmi_writemeta_unittest.cpp
@@ -0,0 +1,72 @@
+#include "ipmi.hpp"
+
+#include <blobs-ipmid/test/manager_mock.hpp>
+#include <cstring>
+
+#include <gtest/gtest.h>
+
+namespace blobs
+{
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+
+// ipmid.hpp isn't installed where we can grab it and this value is per BMC
+// SoC.
+#define MAX_IPMI_BUFFER 64
+
+TEST(BlobWriteMetaTest, ManagerReturnsFailureReturnsFailure)
+{
+    // This verifies a failure from the manager is passed back.
+
+    ManagerMock mgr;
+    size_t dataLen;
+    uint8_t request[MAX_IPMI_BUFFER] = {0};
+    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    auto req = reinterpret_cast<struct BmcBlobWriteMetaTx*>(request);
+
+    req->cmd = BlobOEMCommands::bmcBlobWrite;
+    req->crc = 0;
+    req->sessionId = 0x54;
+    req->offset = 0x100;
+
+    uint8_t expectedBytes[2] = {0x66, 0x67};
+    std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes));
+
+    dataLen = sizeof(struct BmcBlobWriteMetaTx) + sizeof(expectedBytes);
+
+    EXPECT_CALL(
+        mgr, writeMeta(req->sessionId, req->offset,
+                       ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+        .WillOnce(Return(false));
+
+    EXPECT_EQ(IPMI_CC_INVALID, writeMeta(&mgr, request, reply, &dataLen));
+}
+
+TEST(BlobWriteMetaTest, ManagerReturnsTrueWriteSucceeds)
+{
+    // The case where everything works.
+
+    ManagerMock mgr;
+    size_t dataLen;
+    uint8_t request[MAX_IPMI_BUFFER] = {0};
+    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    auto req = reinterpret_cast<struct BmcBlobWriteMetaTx*>(request);
+
+    req->cmd = BlobOEMCommands::bmcBlobWrite;
+    req->crc = 0;
+    req->sessionId = 0x54;
+    req->offset = 0x100;
+
+    uint8_t expectedBytes[2] = {0x66, 0x67};
+    std::memcpy(req->data, &expectedBytes[0], sizeof(expectedBytes));
+
+    dataLen = sizeof(struct BmcBlobWriteMetaTx) + sizeof(expectedBytes);
+
+    EXPECT_CALL(
+        mgr, writeMeta(req->sessionId, req->offset,
+                       ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+        .WillOnce(Return(true));
+
+    EXPECT_EQ(IPMI_CC_OK, writeMeta(&mgr, request, reply, &dataLen));
+}
+} // namespace blobs
diff --git a/test/manager_writemeta_unittest.cpp b/test/manager_writemeta_unittest.cpp
new file mode 100644
index 0000000..e83c904
--- /dev/null
+++ b/test/manager_writemeta_unittest.cpp
@@ -0,0 +1,92 @@
+#include <blobs-ipmid/manager.hpp>
+#include <blobs-ipmid/test/blob_mock.hpp>
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace blobs
+{
+
+TEST(ManagerWriteMetaTest, WriteMetaNoSessionReturnsFalse)
+{
+    // Calling WriteMeta on a session that doesn't exist should return false.
+
+    BlobManager mgr;
+    uint16_t sess = 1;
+    uint32_t ofs = 0x54;
+    std::vector<uint8_t> data = {0x11, 0x22};
+
+    EXPECT_FALSE(mgr.writeMeta(sess, ofs, data));
+}
+
+TEST(ManagerWriteMetaTest, WriteMetaSessionFoundButHandlerReturnsFalse)
+{
+    // The handler was found but it returned failure.
+
+    BlobManager mgr;
+    std::unique_ptr<BlobMock> m1 = std::make_unique<BlobMock>();
+    auto m1ptr = m1.get();
+    EXPECT_TRUE(mgr.registerHandler(std::move(m1)));
+
+    uint16_t flags = OpenFlags::write, sess;
+    std::string path = "/asdf/asdf";
+    uint32_t ofs = 0x54;
+    std::vector<uint8_t> data = {0x11, 0x22};
+
+    EXPECT_CALL(*m1ptr, canHandleBlob(path)).WillOnce(Return(true));
+    EXPECT_CALL(*m1ptr, open(_, flags, path)).WillOnce(Return(true));
+    EXPECT_TRUE(mgr.open(flags, path, &sess));
+
+    EXPECT_CALL(*m1ptr, writeMeta(sess, ofs, data)).WillOnce(Return(false));
+
+    EXPECT_FALSE(mgr.writeMeta(sess, ofs, data));
+}
+
+TEST(ManagerWriteMetaTest, WriteMetaSucceedsEvenIfFileOpenedReadOnly)
+{
+    // The manager will not route a WriteMeta call to a file opened read-only.
+
+    BlobManager mgr;
+    std::unique_ptr<BlobMock> m1 = std::make_unique<BlobMock>();
+    auto m1ptr = m1.get();
+    EXPECT_TRUE(mgr.registerHandler(std::move(m1)));
+
+    uint16_t flags = OpenFlags::read, sess;
+    std::string path = "/asdf/asdf";
+    uint32_t ofs = 0x54;
+    std::vector<uint8_t> data = {0x11, 0x22};
+
+    EXPECT_CALL(*m1ptr, canHandleBlob(path)).WillOnce(Return(true));
+    EXPECT_CALL(*m1ptr, open(_, flags, path)).WillOnce(Return(true));
+    EXPECT_TRUE(mgr.open(flags, path, &sess));
+
+    EXPECT_CALL(*m1ptr, writeMeta(sess, ofs, data)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mgr.writeMeta(sess, ofs, data));
+}
+
+TEST(ManagerWriteMetaTest, WriteMetaMetaSessionFoundAndHandlerReturnsSuccess)
+{
+    // The handler was found and returned success.
+
+    BlobManager mgr;
+    std::unique_ptr<BlobMock> m1 = std::make_unique<BlobMock>();
+    auto m1ptr = m1.get();
+    EXPECT_TRUE(mgr.registerHandler(std::move(m1)));
+
+    uint16_t flags = OpenFlags::write, sess;
+    std::string path = "/asdf/asdf";
+    uint32_t ofs = 0x54;
+    std::vector<uint8_t> data = {0x11, 0x22};
+
+    EXPECT_CALL(*m1ptr, canHandleBlob(path)).WillOnce(Return(true));
+    EXPECT_CALL(*m1ptr, open(_, flags, path)).WillOnce(Return(true));
+    EXPECT_TRUE(mgr.open(flags, path, &sess));
+
+    EXPECT_CALL(*m1ptr, writeMeta(sess, ofs, data)).WillOnce(Return(true));
+
+    EXPECT_TRUE(mgr.writeMeta(sess, ofs, data));
+}
+} // namespace blobs