firmware: tie implementation of session into write

To demonstrate how session will work, implement the write command.
Everything isn't wired up with open(), therefore this code itself will
only work in isolation.

This requires wiring up the open command to verify write will use the
handler we specify.

Change-Id: Icf5cfad4ddb531bc271642e24d505cbb9abf8f22
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/Makefile.am b/test/Makefile.am
index 7ea9cd6..243e733 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,7 +13,8 @@
 	firmware_handler_unittest \
 	firmware_stat_unittest \
 	firmware_canhandle_unittest \
-	firmware_open_unittest
+	firmware_open_unittest \
+	firmware_write_unittest
 
 TESTS = $(check_PROGRAMS)
 
@@ -28,3 +29,6 @@
 
 firmware_open_unittest_SOURCES = firmware_open_unittest.cpp
 firmware_open_unittest_LDADD = $(top_builddir)/firmware_handler.o
+
+firmware_write_unittest_SOURCES = firmware_write_unittest.cpp
+firmware_write_unittest_LDADD = $(top_builddir)/firmware_handler.o
diff --git a/test/firmware_canhandle_unittest.cpp b/test/firmware_canhandle_unittest.cpp
index 600be4b..44223d4 100644
--- a/test/firmware_canhandle_unittest.cpp
+++ b/test/firmware_canhandle_unittest.cpp
@@ -23,6 +23,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
         {"bcdf", &imageMock},
     };
diff --git a/test/firmware_handler_unittest.cpp b/test/firmware_handler_unittest.cpp
index 6a1ffe6..e6998c1 100644
--- a/test/firmware_handler_unittest.cpp
+++ b/test/firmware_handler_unittest.cpp
@@ -24,14 +24,16 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
 
     auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, {});
     EXPECT_EQ(handler, nullptr);
 }
-TEST(FirmwareHandlerTest, CreateEmptyListVerifyHasHash)
+TEST(FirmwareHandlerTest, VerifyHashRequiredForHappiness)
 {
+    /* This works fine only if you also pass in the hash handler. */
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
@@ -42,6 +44,11 @@
     };
 
     auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
+    EXPECT_EQ(handler, nullptr);
+
+    blobs.push_back({FirmwareBlobHandler::hashBlobID, &imageMock});
+
+    handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
     auto result = handler->getBlobIds();
     EXPECT_EQ(2, result.size());
     EXPECT_EQ(2, std::count(result.begin(), result.end(), "asdf") +
diff --git a/test/firmware_open_unittest.cpp b/test/firmware_open_unittest.cpp
index bc6ece2..2b1a330 100644
--- a/test/firmware_open_unittest.cpp
+++ b/test/firmware_open_unittest.cpp
@@ -20,6 +20,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
     std::vector<DataHandlerPack> data = {
@@ -43,6 +44,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
     std::vector<DataHandlerPack> data = {
@@ -65,6 +67,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
     std::vector<DataHandlerPack> data = {
@@ -84,6 +87,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
     std::vector<DataHandlerPack> data = {
@@ -103,6 +107,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
     std::vector<DataHandlerPack> data = {
diff --git a/test/firmware_stat_unittest.cpp b/test/firmware_stat_unittest.cpp
index e4b09bc..ad43b60 100644
--- a/test/firmware_stat_unittest.cpp
+++ b/test/firmware_stat_unittest.cpp
@@ -20,6 +20,7 @@
     ImageHandlerMock imageMock;
 
     std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock},
         {"asdf", &imageMock},
     };
     std::vector<DataHandlerPack> data = {
diff --git a/test/firmware_write_unittest.cpp b/test/firmware_write_unittest.cpp
new file mode 100644
index 0000000..bac2d39
--- /dev/null
+++ b/test/firmware_write_unittest.cpp
@@ -0,0 +1,121 @@
+#include "data_mock.hpp"
+#include "firmware_handler.hpp"
+#include "image_mock.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace blobs
+{
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::Return;
+
+TEST(FirmwareHandlerWriteTest, DataTypeIpmiWriteSuccess)
+{
+    /* Verify if data type ipmi, it calls write with the bytes. */
+
+    ImageHandlerMock imageMock1, imageMock2;
+    std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock1},
+        {"asdf", &imageMock2},
+    };
+
+    std::vector<DataHandlerPack> data = {
+        {FirmwareBlobHandler::FirmwareUpdateFlags::ipmi, nullptr},
+    };
+
+    auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
+
+    EXPECT_CALL(imageMock2, open("asdf")).WillOnce(Return(true));
+
+    EXPECT_TRUE(handler->open(
+        0, OpenFlags::write | FirmwareBlobHandler::FirmwareUpdateFlags::ipmi,
+        "asdf"));
+
+    std::vector<std::uint8_t> bytes = {0xaa, 0x55};
+
+    EXPECT_CALL(imageMock2, write(0, Eq(bytes))).WillOnce(Return(true));
+    EXPECT_TRUE(handler->write(0, 0, bytes));
+}
+
+TEST(FirmwareHandlerWriteTest, DataTypeNonIpmiWriteSuccess)
+{
+    /* Verify if data type non-ipmi, it calls write with the length. */
+
+    ImageHandlerMock imageMock1, imageMock2;
+    std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock1},
+        {"asdf", &imageMock2},
+    };
+
+    DataHandlerMock dataMock;
+
+    std::vector<DataHandlerPack> data = {
+        {FirmwareBlobHandler::FirmwareUpdateFlags::ipmi, nullptr},
+        {FirmwareBlobHandler::FirmwareUpdateFlags::lpc, &dataMock},
+    };
+
+    auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
+
+    EXPECT_CALL(imageMock2, open("asdf")).WillOnce(Return(true));
+
+    EXPECT_TRUE(handler->open(
+        0, OpenFlags::write | FirmwareBlobHandler::FirmwareUpdateFlags::lpc,
+        "asdf"));
+
+    struct ExtChunkHdr request;
+    request.length = 4; /* number of bytes to read. */
+    std::vector<std::uint8_t> ipmiRequest;
+    ipmiRequest.resize(sizeof(request));
+    std::memcpy(ipmiRequest.data(), &request, sizeof(request));
+
+    std::vector<std::uint8_t> bytes = {0x01, 0x02, 0x03, 0x04};
+
+    EXPECT_CALL(dataMock, copyFrom(request.length)).WillOnce(Return(bytes));
+    EXPECT_CALL(imageMock2, write(0, Eq(bytes))).WillOnce(Return(true));
+    EXPECT_TRUE(handler->write(0, 0, ipmiRequest));
+}
+
+TEST(FirmwareHandlerWriteTest, DataTypeNonIpmiWriteFailsBadRequest)
+{
+    /* Verify the data type non-ipmi, if the request's structure doesn't match,
+     * return failure. */
+
+    ImageHandlerMock imageMock1, imageMock2;
+    std::vector<HandlerPack> blobs = {
+        {FirmwareBlobHandler::hashBlobID, &imageMock1},
+        {"asdf", &imageMock2},
+    };
+
+    DataHandlerMock dataMock;
+
+    std::vector<DataHandlerPack> data = {
+        {FirmwareBlobHandler::FirmwareUpdateFlags::ipmi, nullptr},
+        {FirmwareBlobHandler::FirmwareUpdateFlags::lpc, &dataMock},
+    };
+
+    auto handler = FirmwareBlobHandler::CreateFirmwareBlobHandler(blobs, data);
+
+    EXPECT_CALL(imageMock2, open("asdf")).WillOnce(Return(true));
+
+    EXPECT_TRUE(handler->open(
+        0, OpenFlags::write | FirmwareBlobHandler::FirmwareUpdateFlags::lpc,
+        "asdf"));
+
+    struct ExtChunkHdr request;
+    request.length = 4; /* number of bytes to read. */
+
+    std::vector<std::uint8_t> ipmiRequest;
+    ipmiRequest.resize(sizeof(request));
+    std::memcpy(ipmiRequest.data(), &request, sizeof(request));
+    ipmiRequest.push_back(1);
+
+    /* ipmiRequest is too large by one byte. */
+    EXPECT_FALSE(handler->write(0, 0, ipmiRequest));
+}
+
+} // namespace blobs