| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 1 | #include "binarystore.hpp" | 
|  | 2 | #include "binarystore_interface.hpp" | 
|  | 3 | #include "sys_file.hpp" | 
|  | 4 |  | 
|  | 5 | #include <google/protobuf/text_format.h> | 
|  | 6 |  | 
|  | 7 | #include <iostream> | 
|  | 8 | #include <ipmid/handler.hpp> | 
|  | 9 | #include <iterator> | 
|  | 10 | #include <memory> | 
|  | 11 | #include <sstream> | 
| Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame^] | 12 | #include <stdplus/print.hpp> | 
| Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 13 | #include <vector> | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 14 |  | 
|  | 15 | #include <gmock/gmock.h> | 
|  | 16 |  | 
|  | 17 | const std::string blobData = "jW7jiID}kD&gm&Azi:^]JT]'Ov4" | 
|  | 18 | "Y.Oey]mw}yak9Wf3[S`+$!g]@[0}gikis^"; | 
|  | 19 | const std::string inputProto = "blob_base_id: \"/blob/my-test\"" | 
|  | 20 | "blobs: [{ " | 
|  | 21 | "    blob_id: \"/blob/my-test/0\"" | 
|  | 22 | "    data:\"" + | 
|  | 23 | blobData + | 
|  | 24 | "\"" | 
|  | 25 | "}, { " | 
|  | 26 | "    blob_id: \"/blob/my-test/1\"" | 
|  | 27 | "    data:\"" + | 
|  | 28 | blobData + | 
|  | 29 | "\"" | 
|  | 30 | "}, { " | 
|  | 31 | "    blob_id: \"/blob/my-test/2\"" | 
|  | 32 | "    data:\"" + | 
|  | 33 | blobData + | 
|  | 34 | "\"" | 
|  | 35 | "}, {" | 
|  | 36 | "    blob_id: \"/blob/my-test/3\"" | 
|  | 37 | "    data:\"" + | 
|  | 38 | blobData + | 
|  | 39 | "\"" | 
|  | 40 | "}] " | 
|  | 41 | "max_size_bytes: 64"; | 
| Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 42 | const std::string smallInputProto = "blob_base_id: \"/s/test\"" | 
|  | 43 | "blobs: [{ " | 
|  | 44 | "    blob_id: \"/s/test/0\"" | 
|  | 45 | "}] " | 
|  | 46 | "max_size_bytes: 64"; | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 47 |  | 
|  | 48 | class SysFileBuf : public binstore::SysFile | 
|  | 49 | { | 
|  | 50 | public: | 
| Willy Tu | fdebaa3 | 2022-02-08 16:34:20 -0800 | [diff] [blame] | 51 | explicit SysFileBuf(std::string* storage) : data_{storage} | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 52 | { | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 | size_t readToBuf(size_t pos, size_t count, char* buf) const override | 
|  | 56 | { | 
| Willy Tu | ca170bb | 2023-11-06 00:26:43 -0800 | [diff] [blame] | 57 | stdplus::print(stderr, "Read {} bytes at {}\n", count, pos); | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 58 | return data_->copy(buf, count, pos); | 
|  | 59 | } | 
|  | 60 |  | 
|  | 61 | std::string readAsStr(size_t pos, size_t count) const override | 
|  | 62 | { | 
| Willy Tu | ca170bb | 2023-11-06 00:26:43 -0800 | [diff] [blame] | 63 | stdplus::print(stderr, "Read as str {} bytes at {}\n", count, pos); | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 64 | return data_->substr(pos, count); | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | std::string readRemainingAsStr(size_t pos) const override | 
|  | 68 | { | 
|  | 69 | return data_->substr(pos); | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | void writeStr(const std::string& data, size_t pos) override | 
|  | 73 | { | 
|  | 74 | data_->replace(pos, data.size(), data); | 
|  | 75 | } | 
|  | 76 |  | 
|  | 77 | std::string* data_; | 
|  | 78 | }; | 
|  | 79 |  | 
|  | 80 | using binstore::binaryblobproto::BinaryBlobBase; | 
|  | 81 | using google::protobuf::TextFormat; | 
|  | 82 |  | 
|  | 83 | using testing::ElementsAreArray; | 
|  | 84 | using testing::UnorderedElementsAre; | 
|  | 85 |  | 
|  | 86 | class BinaryStoreTest : public testing::Test | 
|  | 87 | { | 
|  | 88 | public: | 
|  | 89 | std::unique_ptr<SysFileBuf> createBlobStorage(const std::string& textProto) | 
|  | 90 | { | 
|  | 91 | BinaryBlobBase storeProto; | 
|  | 92 | TextFormat::ParseFromString(textProto, &storeProto); | 
|  | 93 |  | 
|  | 94 | std::stringstream storage; | 
|  | 95 | std::string data; | 
|  | 96 | storeProto.SerializeToString(&data); | 
|  | 97 |  | 
|  | 98 | const uint64_t dataSize = data.size(); | 
|  | 99 | storage.write(reinterpret_cast<const char*>(&dataSize), | 
|  | 100 | sizeof(dataSize)); | 
|  | 101 | storage << data; | 
|  | 102 |  | 
|  | 103 | blobDataStorage = storage.str(); | 
|  | 104 | return std::make_unique<SysFileBuf>(&blobDataStorage); | 
|  | 105 | } | 
|  | 106 |  | 
|  | 107 | std::string blobDataStorage; | 
|  | 108 | }; | 
|  | 109 |  | 
|  | 110 | TEST_F(BinaryStoreTest, SimpleLoad) | 
|  | 111 | { | 
|  | 112 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 113 | auto initialData = blobDataStorage; | 
|  | 114 | auto store = binstore::BinaryStore::createFromConfig( | 
|  | 115 | "/blob/my-test", std::move(testDataFile)); | 
|  | 116 | EXPECT_THAT(store->getBlobIds(), | 
|  | 117 | UnorderedElementsAre("/blob/my-test", "/blob/my-test/0", | 
|  | 118 | "/blob/my-test/1", "/blob/my-test/2", | 
|  | 119 | "/blob/my-test/3")); | 
|  | 120 | EXPECT_EQ(initialData, blobDataStorage); | 
|  | 121 | } | 
|  | 122 |  | 
| Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame^] | 123 | TEST_F(BinaryStoreTest, SimpleLoadWithAlias) | 
|  | 124 | { | 
|  | 125 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 126 | auto initialData = blobDataStorage; | 
|  | 127 | auto store = binstore::BinaryStore::createFromConfig( | 
|  | 128 | "/blob/my-test-2", std::move(testDataFile), std::nullopt, | 
|  | 129 | "/blob/my-test"); | 
|  | 130 | EXPECT_THAT(store->getBlobIds(), | 
|  | 131 | UnorderedElementsAre("/blob/my-test-2", "/blob/my-test-2/0", | 
|  | 132 | "/blob/my-test-2/1", "/blob/my-test-2/2", | 
|  | 133 | "/blob/my-test-2/3")); | 
|  | 134 | EXPECT_NE(initialData, blobDataStorage); | 
|  | 135 | } | 
|  | 136 |  | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 137 | TEST_F(BinaryStoreTest, TestCreateFromFile) | 
|  | 138 | { | 
|  | 139 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 140 | auto initialData = blobDataStorage; | 
|  | 141 | auto store = | 
|  | 142 | binstore::BinaryStore::createFromFile(std::move(testDataFile), true); | 
|  | 143 | ASSERT_TRUE(store); | 
|  | 144 | EXPECT_EQ("/blob/my-test", store->getBaseBlobId()); | 
|  | 145 | EXPECT_THAT(store->getBlobIds(), | 
|  | 146 | UnorderedElementsAre("/blob/my-test", "/blob/my-test/0", | 
|  | 147 | "/blob/my-test/1", "/blob/my-test/2", | 
|  | 148 | "/blob/my-test/3")); | 
|  | 149 | // Check that the storage has not changed | 
|  | 150 | EXPECT_EQ(initialData, blobDataStorage); | 
|  | 151 | } | 
|  | 152 |  | 
| Willy Tu | 7f10780 | 2023-11-06 23:05:25 -0800 | [diff] [blame^] | 153 | TEST_F(BinaryStoreTest, TestSetBaseBlobId) | 
|  | 154 | { | 
|  | 155 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 156 | auto initialData = blobDataStorage; | 
|  | 157 | auto store = binstore::BinaryStore::createFromConfig( | 
|  | 158 | "/blob/my-test", std::move(testDataFile), std::nullopt); | 
|  | 159 | ASSERT_TRUE(store); | 
|  | 160 | EXPECT_EQ("/blob/my-test", store->getBaseBlobId()); | 
|  | 161 | EXPECT_TRUE(store->setBaseBlobId("/blob/my-test-1")); | 
|  | 162 | EXPECT_EQ("/blob/my-test-1", store->getBaseBlobId()); | 
|  | 163 | EXPECT_THAT(store->getBlobIds(), | 
|  | 164 | UnorderedElementsAre("/blob/my-test-1", "/blob/my-test-1/0", | 
|  | 165 | "/blob/my-test-1/1", "/blob/my-test-1/2", | 
|  | 166 | "/blob/my-test-1/3")); | 
|  | 167 | // Check that the storage has changed | 
|  | 168 | EXPECT_NE(initialData, blobDataStorage); | 
|  | 169 | } | 
|  | 170 |  | 
| Maksym Sloyko | eb27411 | 2021-10-27 23:48:32 +0000 | [diff] [blame] | 171 | TEST_F(BinaryStoreTest, TestReadBlob) | 
|  | 172 | { | 
|  | 173 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 174 | auto store = | 
|  | 175 | binstore::BinaryStore::createFromFile(std::move(testDataFile), true); | 
|  | 176 | ASSERT_TRUE(store); | 
|  | 177 |  | 
|  | 178 | const auto blobStoredData = store->readBlob("/blob/my-test/1"); | 
|  | 179 | EXPECT_FALSE(blobStoredData.empty()); | 
|  | 180 |  | 
|  | 181 | decltype(blobStoredData) origData(blobData.begin(), blobData.end()); | 
|  | 182 |  | 
|  | 183 | EXPECT_THAT(blobStoredData, ElementsAreArray(origData)); | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | TEST_F(BinaryStoreTest, TestReadBlobError) | 
|  | 187 | { | 
|  | 188 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 189 | auto store = | 
|  | 190 | binstore::BinaryStore::createFromFile(std::move(testDataFile), true); | 
|  | 191 | ASSERT_TRUE(store); | 
|  | 192 |  | 
|  | 193 | EXPECT_THROW(store->readBlob("/nonexistent/1"), ipmi::HandlerCompletion); | 
|  | 194 | } | 
|  | 195 |  | 
|  | 196 | TEST_F(BinaryStoreTest, TestOpenReadOnlyBlob) | 
|  | 197 | { | 
|  | 198 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 199 | auto store = | 
|  | 200 | binstore::BinaryStore::createFromFile(std::move(testDataFile), true); | 
|  | 201 | ASSERT_TRUE(store); | 
|  | 202 |  | 
|  | 203 | EXPECT_TRUE( | 
|  | 204 | store->openOrCreateBlob("/blob/my-test/2", blobs::OpenFlags::read)); | 
|  | 205 | EXPECT_FALSE(store->openOrCreateBlob( | 
|  | 206 | "/blob/my-test/2", blobs::OpenFlags::read & blobs::OpenFlags::write)); | 
|  | 207 | } | 
| Willy Tu | 67391d5 | 2021-12-07 19:53:49 -0800 | [diff] [blame] | 208 |  | 
|  | 209 | TEST_F(BinaryStoreTest, TestWriteExceedMaxSize) | 
|  | 210 | { | 
|  | 211 | std::vector<uint8_t> writeData(10, 0); | 
|  | 212 | auto testDataFile = createBlobStorage(smallInputProto); | 
|  | 213 | auto store = binstore::BinaryStore::createFromConfig( | 
|  | 214 | "/s/test", std::move(testDataFile), 48); | 
|  | 215 | ASSERT_TRUE(store); | 
|  | 216 |  | 
|  | 217 | EXPECT_TRUE(store->openOrCreateBlob( | 
|  | 218 | "/s/test/0", blobs::OpenFlags::write | blobs::OpenFlags::read)); | 
|  | 219 | // Current size 24(blob_ + max_size) + 8(size var) = 32 | 
|  | 220 | EXPECT_TRUE(store->write( | 
|  | 221 | 0, writeData)); // 44 =  32(existing) + 10 (data) + 2 (blob_id '/0') | 
|  | 222 | EXPECT_FALSE( | 
|  | 223 | store->write(10, writeData)); // 54 = 44 (existing) + 10 (new data) | 
|  | 224 | EXPECT_FALSE( | 
|  | 225 | store->write(5, writeData)); // 49 = 44 (existing) + 5 (new data) | 
|  | 226 | EXPECT_TRUE( | 
|  | 227 | store->write(4, writeData)); // 48 = 44 (existing) + 4 (new data) | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | TEST_F(BinaryStoreTest, TestCreateFromConfigExceedMaxSize) | 
|  | 231 | { | 
|  | 232 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 233 | auto store = binstore::BinaryStore::createFromConfig( | 
|  | 234 | "/blob/my-test", std::move(testDataFile), 1); | 
|  | 235 | ASSERT_TRUE(store); | 
|  | 236 | EXPECT_FALSE(store->commit()); | 
|  | 237 | } | 
|  | 238 |  | 
|  | 239 | TEST_F(BinaryStoreTest, TestCreateFromFileExceedMaxSize) | 
|  | 240 | { | 
|  | 241 | auto testDataFile = createBlobStorage(inputProto); | 
|  | 242 | auto store = binstore::BinaryStore::createFromFile(std::move(testDataFile), | 
|  | 243 | false, 1); | 
|  | 244 |  | 
|  | 245 | // Reading from File is expected to call loadSerializedData and fail at the | 
|  | 246 | // commit() | 
|  | 247 | EXPECT_FALSE(store); | 
|  | 248 | } |