flash-ipmi: implement flashHashData

Change-Id: Ib6a4f0219c623175562760461bffd0a45117d02e
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/flash-ipmi.cpp b/flash-ipmi.cpp
index f58363b..8fc9a52 100644
--- a/flash-ipmi.cpp
+++ b/flash-ipmi.cpp
@@ -166,7 +166,11 @@
 
 bool FlashUpdate::hashData(uint32_t offset, const std::vector<uint8_t>& bytes)
 {
-    /* TODO: implement. */
+    if (hashFd)
+    {
+        return writeBlock(hashFd, offset, bytes);
+    }
+
     return false;
 }
 
diff --git a/test/Makefile.am b/test/Makefile.am
index 9540688..1fc2d92 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -23,7 +23,8 @@
 	flash_start_unittest \
 	flash_flashdata_unittest \
 	flash_flashfinish_unittest \
-	flash_hashstart_unittest
+	flash_hashstart_unittest \
+	flash_hashdata_unittest
 
 TESTS = $(check_PROGRAMS)
 
@@ -68,3 +69,6 @@
 
 flash_hashstart_unittest_SOURCES = flash_hashstart_unittest.cpp
 flash_hashstart_unittest_LDADD = $(top_builddir)/flash-ipmi.o $(SDBUSPLUS_LIBS)
+
+flash_hashdata_unittest_SOURCES = flash_hashdata_unittest.cpp
+flash_hashdata_unittest_LDADD = $(top_builddir)/flash-ipmi.o $(SDBUSPLUS_LIBS)
diff --git a/test/flash_hashdata_unittest.cpp b/test/flash_hashdata_unittest.cpp
new file mode 100644
index 0000000..f9a3ffa
--- /dev/null
+++ b/test/flash_hashdata_unittest.cpp
@@ -0,0 +1,83 @@
+#include "flash-ipmi.hpp"
+
+#include <cstdio>
+#include <cstring>
+#include <gtest/gtest.h>
+#include <string>
+#include <vector>
+
+#define THIRTYTWO_MIB 33554432
+
+class FlashIpmiHashDataTest : public ::testing::Test
+{
+  protected:
+    FlashIpmiHashDataTest() = default;
+
+    void SetUp() override
+    {
+        name = std::tmpnam(nullptr);
+        name2 = std::tmpnam(nullptr);
+    }
+    void TearDown() override
+    {
+        (void)std::remove(name.c_str());
+        (void)std::remove(name2.c_str());
+    }
+
+    std::string name;
+    std::string name2;
+};
+
+TEST_F(FlashIpmiHashDataTest, CalledOutOfSequenceFails)
+{
+    // Verify that if you try to write to the hash before starting, it'll fail.
+    // Presently, start() will open the hash file.
+
+    std::vector<uint8_t> bytes = {0xaa, 0x55};
+
+    FlashUpdate updater(name, name2);
+    EXPECT_FALSE(updater.hashData(0, bytes));
+}
+
+TEST_F(FlashIpmiHashDataTest, CalledWithDataSucceeds)
+{
+    // Verify the normal use case works.
+    std::vector<uint8_t> bytes = {0xaa, 0x55};
+
+    FlashUpdate updater(name, name2);
+    EXPECT_TRUE(updater.start(THIRTYTWO_MIB));
+    EXPECT_TRUE(updater.startHash(THIRTYTWO_MIB));
+    EXPECT_TRUE(updater.hashData(0, bytes));
+
+    auto file = std::fopen(name2.c_str(), "r");
+    EXPECT_TRUE(file);
+
+    uint8_t buffer[2];
+    auto read = std::fread(buffer, 1, bytes.size(), file);
+    EXPECT_EQ(read, bytes.size());
+    EXPECT_EQ(0, std::memcmp(buffer, bytes.data(), bytes.size()));
+    std::fclose(file);
+}
+
+TEST_F(FlashIpmiHashDataTest, CalledNonZeroOffsetSucceeds)
+{
+    // Skipping bytes in POSIX can create a sparse file.  In this case,
+    // let's allow the behavior.  If we'd rather, we can have writeBlock
+    // check that the offset is where we expect.
+
+    std::vector<uint8_t> bytes = {0xaa, 0x55};
+
+    FlashUpdate updater(name, name2);
+    EXPECT_TRUE(updater.start(THIRTYTWO_MIB));
+    EXPECT_TRUE(updater.startHash(THIRTYTWO_MIB));
+    EXPECT_TRUE(updater.hashData(2, bytes));
+
+    auto file = std::fopen(name2.c_str(), "r");
+    EXPECT_TRUE(file);
+
+    uint8_t buffer[4];
+    auto read = std::fread(buffer, 1, sizeof(buffer), file);
+    EXPECT_EQ(read, sizeof(buffer));
+    EXPECT_EQ(0, std::memcmp(&buffer[2], bytes.data(), bytes.size()));
+    std::fclose(file);
+}