blob: 789eefbe33849890ec64889dbb7b5ee982a076dd [file] [log] [blame]
#include "handler_unittest.hpp"
#include "fake_sys_file.hpp"
#include <google/protobuf/message_lite.h>
#include <google/protobuf/text_format.h>
#include <algorithm>
#include <boost/endian/arithmetic.hpp>
#include <memory>
#include <string>
#include <vector>
#include "binaryblob.pb.h"
#include <gtest/gtest.h>
using ::testing::_;
using ::testing::AtLeast;
using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
using ::testing::Return;
using ::testing::StrEq;
using ::testing::StrNe;
using ::testing::UnorderedElementsAreArray;
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 const std::vector<std::string> basicTestBaseIdList;
static inline std::string basicTestInvalidBlobId = "/invalid/blob0"s;
void addAllBaseIds()
{
for (size_t i = 0; i < basicTestBaseIdList.size(); ++i)
{
auto store = defaultMockStore(basicTestBaseIdList[i]);
EXPECT_CALL(*store, getBaseBlobId()).Times(AtLeast(1));
handler.addNewBinaryStore(std::move(store));
}
}
};
const std::vector<std::string>
BinaryStoreBlobHandlerBasicTest::basicTestBaseIdList = {
BinaryStoreBlobHandlerBasicTest::basicTestBaseId, "/another/"s,
"/null\0/"s};
TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobZeroStoreFail)
{
// Cannot handle since there is no store. Shouldn't crash.
EXPECT_FALSE(handler.canHandleBlob(basicTestInvalidBlobId));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobChecksName)
{
auto store = defaultMockStore(basicTestBaseId);
EXPECT_CALL(*store, getBaseBlobId()).Times(AtLeast(1));
handler.addNewBinaryStore(std::move(store));
// Verify canHandleBlob checks and returns false on an invalid name.
EXPECT_FALSE(handler.canHandleBlob(basicTestInvalidBlobId));
// Verify canHandleBlob return true for a blob id that it can handle
EXPECT_TRUE(handler.canHandleBlob(basicTestBlobId));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, GetBlobIdEqualsConcatenationsOfBaseIds)
{
addAllBaseIds();
// When there is no other blob id, ids are just base ids (might be
// re-ordered).
EXPECT_THAT(handler.getBlobIds(),
UnorderedElementsAreArray(basicTestBaseIdList));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobInvalidNames)
{
addAllBaseIds();
const std::vector<std::string> invalidNames = {
basicTestInvalidBlobId,
"/"s,
"//"s,
"/test"s, // Cannot handle the base path
"/test/"s,
"/test/this/blob"s, // Cannot handle nested path
};
// Unary helper for algorithm
auto canHandle = [this](const std::string& blobId) {
return handler.canHandleBlob(blobId);
};
EXPECT_TRUE(
std::none_of(invalidNames.cbegin(), invalidNames.cend(), canHandle));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, CanHandleBlobValidNames)
{
addAllBaseIds();
const std::vector<std::string> validNames = {
basicTestBlobId, "/test/test"s, "/test/xyz.abc"s,
"/another/blob"s, "/null\0/\0\0zer\0\0"s,
};
// Unary helper for algorithm
auto canHandle = [this](const std::string& blobId) {
return handler.canHandleBlob(blobId);
};
// Verify canHandleBlob can handle a valid blob under the path.
EXPECT_TRUE(std::all_of(validNames.cbegin(), validNames.cend(), canHandle));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, DeleteReturnsWhatStoreReturns)
{
auto store = defaultMockStore(basicTestBaseId);
EXPECT_CALL(*store, getBaseBlobId()).Times(AtLeast(1));
EXPECT_CALL(*store, deleteBlob(StrEq(basicTestBlobId)))
.WillOnce(Return(false))
.WillOnce(Return(true));
handler.addNewBinaryStore(std::move(store));
// Unary helper for algorithm
auto canHandle = [this](const std::string& blobId) {
return handler.canHandleBlob(blobId);
};
// Verify canHandleBlob return true for a blob id that it can handle
EXPECT_FALSE(canHandle(basicTestInvalidBlobId));
EXPECT_TRUE(canHandle(basicTestBlobId));
EXPECT_FALSE(handler.deleteBlob(basicTestInvalidBlobId));
EXPECT_FALSE(handler.deleteBlob(basicTestBlobId));
EXPECT_TRUE(handler.deleteBlob(basicTestBlobId));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, StaleDataIsClearedDuringCreation)
{
using namespace google::protobuf;
auto basicTestStaleBlobStr =
R"(blob_base_id: "/stale/"
blobs {
blob_id: "/stale/blob"
})";
// Create sysfile containing a valid but stale blob
const std::string staleBaseId = "/stale/"s;
const std::string staleBlobId = "/stale/blob"s;
binaryblobproto::BinaryBlobBase staleBlob;
EXPECT_TRUE(TextFormat::ParseFromString(basicTestStaleBlobStr, &staleBlob));
// Serialize to string stored in the fakeSysFile
auto staleBlobData = staleBlob.SerializeAsString();
boost::endian::little_uint64_t sizeLE = staleBlobData.size();
std::string commitData(reinterpret_cast<const char*>(sizeLE.data()),
sizeof(sizeLE));
commitData += staleBlobData;
std::vector<std::string> expectedIdList = {basicTestBaseId};
handler.addNewBinaryStore(BinaryStore::createFromConfig(
basicTestBaseId, std::make_unique<FakeSysFile>(commitData)));
EXPECT_FALSE(handler.canHandleBlob(staleBlobId));
EXPECT_EQ(handler.getBlobIds(), expectedIdList);
}
TEST_F(BinaryStoreBlobHandlerBasicTest, CreatingFromEmptySysfile)
{
const std::string emptyData;
EXPECT_NO_THROW(handler.addNewBinaryStore(BinaryStore::createFromConfig(
basicTestBaseId, std::make_unique<FakeSysFile>(emptyData))));
EXPECT_TRUE(handler.canHandleBlob(basicTestBlobId));
}
TEST_F(BinaryStoreBlobHandlerBasicTest, CreatingFromJunkData)
{
boost::endian::little_uint64_t tooLarge = 0xffffffffffffffffull;
const std::string junkDataWithLargeSize(
reinterpret_cast<const char*>(tooLarge.data()), sizeof(tooLarge));
EXPECT_GE(tooLarge, junkDataWithLargeSize.max_size());
EXPECT_NO_THROW(handler.addNewBinaryStore(BinaryStore::createFromConfig(
basicTestBaseId,
std::make_unique<FakeSysFile>(junkDataWithLargeSize))));
EXPECT_TRUE(handler.canHandleBlob(basicTestBlobId));
}
} // namespace blobs