Add class interface BinaryStore for storage abstraction
Represent each storage location as a separate class instance. Add an
interface BinaryStore for this purpose, and add basic unit tests
using a mock.
Signed-off-by: Kun Yi <kunyi@google.com>
Change-Id: I67a140280985db567a4f31d0fe5439105b0a47f9
diff --git a/binarystore.hpp b/binarystore.hpp
new file mode 100644
index 0000000..068db5a
--- /dev/null
+++ b/binarystore.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+using std::size_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::uint8_t;
+
+namespace binstore
+{
+
+/**
+ * @class BinaryStoreInterface is an abstraction for a storage location.
+ * Each instance would be uniquely identified by a baseId string.
+ */
+class BinaryStoreInterface
+{
+ public:
+ virtual ~BinaryStoreInterface() = default;
+
+ virtual std::string getBaseBlobId() const = 0;
+ virtual std::vector<std::string> getBlobIds() const = 0;
+ virtual bool canHandleBlob(const std::string& blobId) const = 0;
+ virtual bool openOrCreateBlob(const std::string& blobId) = 0;
+ virtual std::vector<uint8_t> read(uint32_t offset,
+ uint32_t requestedSize) = 0;
+ virtual bool write(uint32_t offset, const std::vector<uint8_t>& data) = 0;
+ virtual bool commit() = 0;
+ virtual bool close() = 0;
+ virtual bool stat() = 0;
+};
+
+} // namespace binstore
diff --git a/binarystore_mock.hpp b/binarystore_mock.hpp
new file mode 100644
index 0000000..3ea1201
--- /dev/null
+++ b/binarystore_mock.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "binarystore.hpp"
+
+#include <gmock/gmock.h>
+
+namespace binstore
+{
+
+class MockBinaryStore : public BinaryStoreInterface
+{
+ public:
+ MOCK_CONST_METHOD0(getBaseBlobId, std::string());
+ MOCK_CONST_METHOD1(canHandleBlob, bool(const std::string&));
+ MOCK_CONST_METHOD0(getBlobIds, std::vector<std::string>());
+ MOCK_METHOD1(openOrCreateBlob, bool(const std::string&));
+ MOCK_METHOD2(read, std::vector<uint8_t>(uint32_t, uint32_t));
+ MOCK_METHOD2(write, bool(uint32_t, const std::vector<uint8_t>&));
+ MOCK_METHOD0(commit, bool());
+ MOCK_METHOD0(close, bool());
+ MOCK_METHOD0(stat, bool());
+};
+
+} // namespace binstore
diff --git a/handler.cpp b/handler.cpp
index 967a8c2..4e6e6ea 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -1,18 +1,35 @@
#include "handler.hpp"
+#include <algorithm>
+
namespace blobs
{
+void BinaryStoreBlobHandler::addNewBinaryStore(
+ std::unique_ptr<binstore::BinaryStoreInterface> store)
+{
+ // TODO: this is a very rough measure to test the mock interface for now.
+ stores_[store->getBaseBlobId()] = std::move(store);
+}
+
bool BinaryStoreBlobHandler::canHandleBlob(const std::string& path)
{
- // TODO: implement
- return false;
+ return std::any_of(stores_.begin(), stores_.end(),
+ [&](const auto& baseStorePair) {
+ return baseStorePair.second->canHandleBlob(path);
+ });
}
std::vector<std::string> BinaryStoreBlobHandler::getBlobIds()
{
- // TODO: implement
std::vector<std::string> result;
+
+ for (const auto& baseStorePair : stores_)
+ {
+ const auto& ids = baseStorePair.second->getBlobIds();
+ result.insert(result.end(), ids.begin(), ids.end());
+ }
+
return result;
}
@@ -32,6 +49,18 @@
bool BinaryStoreBlobHandler::open(uint16_t session, uint16_t flags,
const std::string& path)
{
+ if (!canHandleBlob(path))
+ {
+ return false;
+ }
+
+ auto found = sessions_.find(session);
+ if (found != sessions_.end())
+ {
+ /* This session is already active */
+ return false;
+ }
+
// TODO: implement
return false;
}
diff --git a/handler.hpp b/handler.hpp
index dcc17b4..61ded36 100644
--- a/handler.hpp
+++ b/handler.hpp
@@ -1,8 +1,13 @@
#pragma once
+#include "binarystore.hpp"
+
#include <blobs-ipmid/blobs.hpp>
#include <cstdint>
+#include <map>
+#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
using std::size_t;
@@ -40,6 +45,24 @@
bool close(uint16_t session) override;
bool stat(uint16_t session, struct BlobMeta* meta) override;
bool expire(uint16_t session) override;
+
+ /**
+ * Registers a binarystore in the main handler. Once called, handler will
+ * take over the ownership of of enclosed binary store.
+ *
+ * @param store: pointer to a valid BinaryStore.
+ * TODO: a minimal amount of error checking would be better
+ */
+ void addNewBinaryStore(
+ std::unique_ptr<binstore::BinaryStoreInterface> store);
+
+ private:
+ /* map of baseId: binaryStore, which has a 1:1 relationship. */
+ std::map<std::string, std::unique_ptr<binstore::BinaryStoreInterface>>
+ stores_;
+
+ /* map of sessionId: open binaryStore base, which has a 1:1 relationship. */
+ std::unordered_map<uint16_t, std::string> sessions_;
};
} // namespace blobs
diff --git a/test/handler_unittest.cpp b/test/handler_unittest.cpp
index 124a3ff..d96fc41 100644
--- a/test/handler_unittest.cpp
+++ b/test/handler_unittest.cpp
@@ -1,12 +1,106 @@
#include "handler_unittest.hpp"
+using ::testing::_;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::StrNe;
+using namespace std::string_literals;
+using namespace binstore;
+
namespace blobs
{
+class BinaryStoreBlobHandlerBasicTest : public BinaryStoreBlobHandlerTest
+{
+ protected:
+ static inline std::string basicTestBaseId = "/test/"s;
+ static inline std::string basicTestBlobId = "/test/blob0"s;
+ static inline std::string basicTestInvalidBlobId = "/invalid/blob0"s;
+};
+
+TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobZeroStoreFail)
+{
+ // Cannot handle since there is no store. Shouldn't crash.
+ EXPECT_FALSE(handler.canHandleBlob(basicTestInvalidBlobId));
+}
+
TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobChecksNameInvalid)
{
+ auto bstore = std::make_unique<MockBinaryStore>();
+
+ handler.addNewBinaryStore(std::move(bstore));
+
// Verify canHandleBlob checks and returns false on an invalid name.
- EXPECT_FALSE(bstore.canHandleBlob("asdf"));
+ EXPECT_FALSE(handler.canHandleBlob(basicTestInvalidBlobId));
+}
+
+TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobCanOpenValidBlob)
+{
+ auto bstore = std::make_unique<MockBinaryStore>();
+
+ EXPECT_CALL(*bstore, getBaseBlobId())
+ .WillRepeatedly(Return(basicTestBaseId));
+ EXPECT_CALL(*bstore, canHandleBlob(StrNe(basicTestBlobId)))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*bstore, canHandleBlob(StrEq(basicTestBlobId)))
+ .WillRepeatedly(Return(true));
+ handler.addNewBinaryStore(std::move(bstore));
+
+ // Verify canHandleBlob return true for a blob id that it can handle
+ EXPECT_FALSE(handler.canHandleBlob(basicTestInvalidBlobId));
+ EXPECT_TRUE(handler.canHandleBlob(basicTestBlobId));
+}
+
+TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobCanOpenValidBlobMultiple)
+{
+ auto bstore = std::make_unique<MockBinaryStore>();
+ auto bstore1 = std::make_unique<MockBinaryStore>();
+ const std::string anotherBaseId = "/another/"s;
+ const std::string anotherBlobId = "/another/blob/id"s;
+
+ EXPECT_CALL(*bstore, getBaseBlobId())
+ .WillRepeatedly(Return(basicTestBaseId));
+ EXPECT_CALL(*bstore, canHandleBlob(StrNe(basicTestBlobId)))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*bstore, canHandleBlob(StrEq(basicTestBlobId)))
+ .WillRepeatedly(Return(true));
+ handler.addNewBinaryStore(std::move(bstore));
+
+ EXPECT_CALL(*bstore1, getBaseBlobId())
+ .WillRepeatedly(Return(anotherBaseId));
+ EXPECT_CALL(*bstore1, canHandleBlob(StrNe(anotherBlobId)))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*bstore1, canHandleBlob(StrEq(anotherBlobId)))
+ .WillRepeatedly(Return(true));
+ handler.addNewBinaryStore(std::move(bstore1));
+
+ // Verify canHandleBlob return true for a blob id that it can handle
+ EXPECT_FALSE(handler.canHandleBlob(basicTestInvalidBlobId));
+ EXPECT_TRUE(handler.canHandleBlob(basicTestBlobId));
+ EXPECT_TRUE(handler.canHandleBlob(anotherBlobId));
+}
+
+TEST_F(BinaryStoreBlobHandlerBasicTest, GetBlobIdEqualsConcatenationsOfIds)
+{
+ std::string baseId0 = "/test/"s;
+ std::string baseId1 = "/test1/"s;
+ std::vector<std::string> idList0 = {"/test/"s, "/test/0"s};
+ std::vector<std::string> idList1 = {"/test1/"s, "/test1/2"s};
+ auto expectedIdList = idList0;
+ expectedIdList.insert(expectedIdList.end(), idList1.begin(), idList1.end());
+
+ auto bstore0 = std::make_unique<MockBinaryStore>();
+ EXPECT_CALL(*bstore0, getBaseBlobId()).WillOnce(Return(baseId0));
+ EXPECT_CALL(*bstore0, getBlobIds()).WillOnce(Return(idList0));
+ handler.addNewBinaryStore(std::move(bstore0));
+
+ auto bstore1 = std::make_unique<MockBinaryStore>();
+ EXPECT_CALL(*bstore1, getBaseBlobId()).WillOnce(Return(baseId1));
+ EXPECT_CALL(*bstore1, getBlobIds()).WillOnce(Return(idList1));
+ handler.addNewBinaryStore(std::move(bstore1));
+
+ // Verify canHandleBlob return true for a blob id that it can handle
+ EXPECT_EQ(expectedIdList, handler.getBlobIds());
}
} // namespace blobs
diff --git a/test/handler_unittest.hpp b/test/handler_unittest.hpp
index fd93de4..f6390fc 100644
--- a/test/handler_unittest.hpp
+++ b/test/handler_unittest.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "binarystore_mock.hpp"
#include "handler.hpp"
#include <gtest/gtest.h>
@@ -11,12 +12,7 @@
{
protected:
BinaryStoreBlobHandlerTest() = default;
-
- BinaryStoreBlobHandler bstore;
-};
-
-class BinaryStoreBlobHandlerBasicTest : public BinaryStoreBlobHandlerTest
-{
+ BinaryStoreBlobHandler handler;
};
} // namespace blobs