binarystore: Implement open/close

Opening a blob will either have read flag for read only access, or
read|write flag for read/write access. When opening a supported path if
the blob doesn't exist yet, the handler will create an empty blob.

Signed-off-by: Kun Yi <kunyi@google.com>
Change-Id: I3d2c5b761227b77cda8bbc6fa66515e9921c066c
diff --git a/binarystore.cpp b/binarystore.cpp
index aa9c0dc..092a069 100644
--- a/binarystore.cpp
+++ b/binarystore.cpp
@@ -1,5 +1,8 @@
 #include "binarystore.hpp"
 
+#include <algorithm>
+#include <blobs-ipmid/blobs.hpp>
+
 namespace binstore
 {
 
@@ -32,7 +35,37 @@
 
 bool BinaryStore::openOrCreateBlob(const std::string& blobId, uint16_t flags)
 {
-    return false;
+    if (!(flags & blobs::OpenFlags::read))
+    {
+        return false;
+    }
+
+    if (currentBlob_ && (currentBlob_->blob_id() != blobId))
+    {
+        /* Already handling a different blob */
+        return false;
+    }
+
+    writable_ = flags & blobs::OpenFlags::write;
+
+    /* Iterate and find if there is an existing blob with this id.
+     * blobsPtr points to a BinaryBlob container with STL-like semantics*/
+    auto blobsPtr = blob_.mutable_blobs();
+    auto blobIt =
+        std::find_if(blobsPtr->begin(), blobsPtr->end(),
+                     [&](const auto& b) { return b.blob_id() == blobId; });
+
+    if (blobIt != blobsPtr->end())
+    {
+        currentBlob_ = &(*blobIt);
+        return true;
+    }
+
+    /* Otherwise, create the blob and append it */
+    currentBlob_ = blob_.add_blobs();
+    currentBlob_->set_blob_id(blobId);
+
+    return true;
 }
 
 bool BinaryStore::deleteBlob(const std::string& blobId)
@@ -58,7 +91,9 @@
 
 bool BinaryStore::close()
 {
-    return false;
+    currentBlob_ = nullptr;
+    writable_ = false;
+    return true;
 }
 
 bool BinaryStore::stat()
diff --git a/binarystore.hpp b/binarystore.hpp
index 7020cff..4091fd4 100644
--- a/binarystore.hpp
+++ b/binarystore.hpp
@@ -98,7 +98,7 @@
     BinaryStore(const std::string& baseBlobId, int fd, uint32_t offset,
                 uint32_t maxSize) :
         baseBlobId_(baseBlobId),
-        fd_(fd), offset_(offset), maxSize_(maxSize)
+        fd_(fd), offset_(offset), maxSize_(maxSize), currentBlob_(nullptr)
     {
     }
 
@@ -141,6 +141,7 @@
     uint32_t maxSize_;
     binaryblobproto::BinaryBlobBase blob_;
     binaryblobproto::BinaryBlob* currentBlob_;
+    bool writable_ = false;
 };
 
 } // namespace binstore
diff --git a/binarystore_mock.hpp b/binarystore_mock.hpp
index eae08fe..34bec6a 100644
--- a/binarystore_mock.hpp
+++ b/binarystore_mock.hpp
@@ -21,6 +21,11 @@
             .WillByDefault(Invoke(&real_store_, &BinaryStore::getBaseBlobId));
         ON_CALL(*this, getBlobIds)
             .WillByDefault(Invoke(&real_store_, &BinaryStore::getBlobIds));
+        ON_CALL(*this, openOrCreateBlob)
+            .WillByDefault(
+                Invoke(&real_store_, &BinaryStore::openOrCreateBlob));
+        ON_CALL(*this, close)
+            .WillByDefault(Invoke(&real_store_, &BinaryStore::close));
     }
     MOCK_CONST_METHOD0(getBaseBlobId, std::string());
     MOCK_CONST_METHOD0(getBlobIds, std::vector<std::string>());
diff --git a/test/handler_open_unittest.cpp b/test/handler_open_unittest.cpp
index c3d10aa..5706041 100644
--- a/test/handler_open_unittest.cpp
+++ b/test/handler_open_unittest.cpp
@@ -3,6 +3,7 @@
 using ::testing::_;
 using ::testing::Return;
 using ::testing::StartsWith;
+using ::testing::UnorderedElementsAreArray;
 
 using namespace std::string_literals;
 using namespace binstore;
@@ -21,13 +22,13 @@
     static inline uint16_t openTestSessionId = 0;
 };
 
-TEST_F(BinaryStoreBlobHandlerOpenTest, FailWhenCannotHandleId)
+TEST_F(BinaryStoreBlobHandlerOpenTest, OpenFailWhenNoBlob)
 {
-    EXPECT_FALSE(handler.open(openTestSessionId, openTestROFlags,
-                              openTestInvalidBlobId));
+    EXPECT_FALSE(
+        handler.open(openTestSessionId, openTestROFlags, openTestBlobId));
 }
 
-TEST_F(BinaryStoreBlobHandlerOpenTest, FailWhenStoreOpenReturnsFailure)
+TEST_F(BinaryStoreBlobHandlerOpenTest, OpenFailWhenStoreOpenReturnsFailure)
 {
     auto store = defaultMockStore(openTestBaseId);
 
@@ -40,7 +41,7 @@
         handler.open(openTestSessionId, openTestROFlags, openTestBlobId));
 }
 
-TEST_F(BinaryStoreBlobHandlerOpenTest, SucceedWhenStoreOpenReturnsTrue)
+TEST_F(BinaryStoreBlobHandlerOpenTest, OpenSucceedWhenStoreOpenReturnsTrue)
 {
     auto store = defaultMockStore(openTestBaseId);
 
@@ -53,9 +54,41 @@
         handler.open(openTestSessionId, openTestROFlags, openTestBlobId));
 }
 
+TEST_F(BinaryStoreBlobHandlerOpenTest, OpenFailForNonMatchingBasePath)
+{
+    addDefaultStore(openTestBaseId);
+    EXPECT_FALSE(handler.open(openTestSessionId, openTestROFlags,
+                              openTestInvalidBlobId));
+}
+
+TEST_F(BinaryStoreBlobHandlerOpenTest, OpenCloseSucceedForValidBlobId)
+{
+    addDefaultStore(openTestBaseId);
+
+    EXPECT_FALSE(handler.close(openTestSessionId)); // Haven't open
+    EXPECT_TRUE(
+        handler.open(openTestSessionId, openTestROFlags, openTestBlobId));
+    EXPECT_TRUE(handler.close(openTestSessionId));
+    EXPECT_FALSE(handler.close(openTestSessionId)); // Already closed
+}
+
+TEST_F(BinaryStoreBlobHandlerOpenTest, OpenSuccessShowsBlobId)
+{
+    addDefaultStore(openTestBaseId);
+
+    EXPECT_TRUE(
+        handler.open(openTestSessionId, openTestROFlags, openTestBlobId));
+    EXPECT_THAT(handler.getBlobIds(),
+                UnorderedElementsAreArray({openTestBaseId, openTestBlobId}));
+}
+
 TEST_F(BinaryStoreBlobHandlerOpenTest, CloseFailForInvalidSession)
 {
     uint16_t invalidSessionId = 1;
+
+    addDefaultStore(openTestBaseId);
+    EXPECT_TRUE(
+        handler.open(openTestSessionId, openTestROFlags, openTestBlobId));
     EXPECT_FALSE(handler.close(invalidSessionId));
 }