blob: 5b618e37772f388c3adc51afaa5fe9ad21030b3b [file] [log] [blame]
Maksym Sloykoeb274112021-10-27 23:48:32 +00001#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 Tu7f107802023-11-06 23:05:25 -080012#include <stdplus/print.hpp>
Willy Tu67391d52021-12-07 19:53:49 -080013#include <vector>
Maksym Sloykoeb274112021-10-27 23:48:32 +000014
15#include <gmock/gmock.h>
16
17const std::string blobData = "jW7jiID}kD&gm&Azi:^]JT]'Ov4"
18 "Y.Oey]mw}yak9Wf3[S`+$!g]@[0}gikis^";
19const 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 Tu67391d52021-12-07 19:53:49 -080042const std::string smallInputProto = "blob_base_id: \"/s/test\""
43 "blobs: [{ "
44 " blob_id: \"/s/test/0\""
45 "}] "
46 "max_size_bytes: 64";
Maksym Sloykoeb274112021-10-27 23:48:32 +000047
48class SysFileBuf : public binstore::SysFile
49{
50 public:
Willy Tufdebaa32022-02-08 16:34:20 -080051 explicit SysFileBuf(std::string* storage) : data_{storage}
Maksym Sloykoeb274112021-10-27 23:48:32 +000052 {
53 }
54
55 size_t readToBuf(size_t pos, size_t count, char* buf) const override
56 {
Willy Tuca170bb2023-11-06 00:26:43 -080057 stdplus::print(stderr, "Read {} bytes at {}\n", count, pos);
Maksym Sloykoeb274112021-10-27 23:48:32 +000058 return data_->copy(buf, count, pos);
59 }
60
61 std::string readAsStr(size_t pos, size_t count) const override
62 {
Willy Tuca170bb2023-11-06 00:26:43 -080063 stdplus::print(stderr, "Read as str {} bytes at {}\n", count, pos);
Maksym Sloykoeb274112021-10-27 23:48:32 +000064 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
80using binstore::binaryblobproto::BinaryBlobBase;
81using google::protobuf::TextFormat;
82
83using testing::ElementsAreArray;
84using testing::UnorderedElementsAre;
85
86class 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
110TEST_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 Tu7f107802023-11-06 23:05:25 -0800123TEST_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 Sloykoeb274112021-10-27 23:48:32 +0000137TEST_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 Tu7f107802023-11-06 23:05:25 -0800153TEST_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 Sloykoeb274112021-10-27 23:48:32 +0000171TEST_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
186TEST_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
196TEST_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 Tu67391d52021-12-07 19:53:49 -0800208
209TEST_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
230TEST_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
239TEST_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}