• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_blob_store/blob_store.h"
16 
17 #include <array>
18 #include <cstddef>
19 #include <cstring>
20 
21 #include "gtest/gtest.h"
22 #include "pw_kvs/crc16_checksum.h"
23 #include "pw_kvs/fake_flash_memory.h"
24 #include "pw_kvs/flash_memory.h"
25 #include "pw_kvs/test_key_value_store.h"
26 #include "pw_log/log.h"
27 #include "pw_random/xor_shift.h"
28 #include "pw_span/span.h"
29 
30 #ifndef PW_FLASH_TEST_ALIGNMENT
31 #define PW_FLASH_TEST_ALIGNMENT 1
32 #endif
33 
34 namespace pw::blob_store {
35 namespace {
36 
37 class BlobStoreTest : public ::testing::Test {
38  protected:
39   static constexpr char kBlobTitle[] = "TestBlobBlock";
40 
BlobStoreTest()41   BlobStoreTest() : flash_(kFlashAlignment), partition_(&flash_) {}
42 
InitFlashTo(span<const std::byte> contents)43   void InitFlashTo(span<const std::byte> contents) {
44     ASSERT_EQ(OkStatus(), partition_.Erase());
45     std::memcpy(flash_.buffer().data(), contents.data(), contents.size());
46   }
47 
InitSourceBufferToRandom(uint64_t seed,size_t init_size_bytes=kBlobDataSize)48   void InitSourceBufferToRandom(uint64_t seed,
49                                 size_t init_size_bytes = kBlobDataSize) {
50     ASSERT_LE(init_size_bytes, source_buffer_.size());
51     random::XorShiftStarRng64 rng(seed);
52 
53     std::memset(source_buffer_.data(),
54                 static_cast<int>(flash_.erased_memory_content()),
55                 source_buffer_.size());
56     rng.Get(span(source_buffer_).first(init_size_bytes));
57   }
58 
InitSourceBufferToFill(char fill,size_t fill_size_bytes=kBlobDataSize)59   void InitSourceBufferToFill(char fill,
60                               size_t fill_size_bytes = kBlobDataSize) {
61     ASSERT_LE(fill_size_bytes, source_buffer_.size());
62     std::memset(source_buffer_.data(),
63                 static_cast<int>(flash_.erased_memory_content()),
64                 source_buffer_.size());
65     std::memset(source_buffer_.data(), fill, fill_size_bytes);
66   }
67 
68   // Fill the source buffer with random pattern based on given seed, written to
69   // BlobStore in specified chunk size.
WriteTestBlock(size_t write_size_bytes=kBlobDataSize)70   void WriteTestBlock(size_t write_size_bytes = kBlobDataSize) {
71     ASSERT_LE(write_size_bytes, source_buffer_.size());
72     constexpr size_t kBufferSize = 256;
73     kvs::ChecksumCrc16 checksum;
74 
75     ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
76 
77     BlobStoreBuffer<kBufferSize> blob(
78         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
79     EXPECT_EQ(OkStatus(), blob.Init());
80 
81     BlobStore::BlobWriterWithBuffer writer(blob);
82     EXPECT_EQ(OkStatus(), writer.Open());
83     ASSERT_EQ(OkStatus(), writer.Write(write_data));
84     EXPECT_EQ(OkStatus(), writer.Close());
85 
86     // Use reader to check for valid data.
87     BlobStore::BlobReader reader(blob);
88     ASSERT_EQ(OkStatus(), reader.Open());
89     Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
90     ASSERT_TRUE(result.ok());
91     EXPECT_EQ(write_size_bytes, result.value().size_bytes());
92     VerifyFlash(result.value().first(write_size_bytes));
93     VerifyFlash(flash_.buffer().first(write_size_bytes));
94     EXPECT_EQ(OkStatus(), reader.Close());
95   }
96 
97   // Open a new blob instance and read the blob using the given read chunk size.
ChunkReadTest(size_t read_chunk_size)98   void ChunkReadTest(size_t read_chunk_size) {
99     kvs::ChecksumCrc16 checksum;
100 
101     VerifyFlash(flash_.buffer());
102 
103     constexpr size_t kBufferSize = 16;
104     BlobStoreBuffer<kBufferSize> blob(
105         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
106     EXPECT_EQ(OkStatus(), blob.Init());
107 
108     // Use reader to check for valid data.
109     BlobStore::BlobReader reader1(blob);
110     ASSERT_EQ(OkStatus(), reader1.Open());
111     Result<ConstByteSpan> possible_blob = reader1.GetMemoryMappedBlob();
112     ASSERT_TRUE(possible_blob.ok());
113     VerifyFlash(possible_blob.value());
114     EXPECT_EQ(OkStatus(), reader1.Close());
115 
116     BlobStore::BlobReader reader(blob);
117     ASSERT_EQ(OkStatus(), reader.Open());
118 
119     std::array<std::byte, kBlobDataSize> read_buffer;
120 
121     ByteSpan read_span = read_buffer;
122     while (read_span.size_bytes() > 0) {
123       size_t read_size = std::min(read_span.size_bytes(), read_chunk_size);
124 
125       PW_LOG_DEBUG("Do write of %u bytes, %u bytes remain",
126                    static_cast<unsigned>(read_size),
127                    static_cast<unsigned>(read_span.size_bytes()));
128 
129       ASSERT_EQ(read_span.size_bytes(), reader.ConservativeReadLimit());
130       auto result = reader.Read(read_span.first(read_size));
131       ASSERT_EQ(result.status(), OkStatus());
132       read_span = read_span.subspan(read_size);
133     }
134     EXPECT_EQ(OkStatus(), reader.Close());
135 
136     VerifyFlash(read_buffer);
137   }
138 
VerifyFlash(ConstByteSpan verify_bytes,size_t offset=0)139   void VerifyFlash(ConstByteSpan verify_bytes, size_t offset = 0) {
140     // Should be defined as same size.
141     EXPECT_EQ(source_buffer_.size(), flash_.buffer().size_bytes());
142 
143     // Can't allow it to march off the end of source_buffer_.
144     ASSERT_LE((verify_bytes.size_bytes() + offset), source_buffer_.size());
145 
146     for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
147       ASSERT_EQ(source_buffer_[i + offset], verify_bytes[i]);
148     }
149   }
150 
151   static constexpr size_t kFlashAlignment = PW_FLASH_TEST_ALIGNMENT;
152   static constexpr size_t kSectorSize = 2048;
153   static constexpr size_t kSectorCount = 2;
154   static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
155 
156   kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
157   kvs::FlashPartition partition_;
158   std::array<std::byte, kBlobDataSize> source_buffer_;
159 };
160 
TEST_F(BlobStoreTest,Init_Ok)161 TEST_F(BlobStoreTest, Init_Ok) {
162   // TODO(davidrogers): Do init test with flash/kvs explicitly in the different
163   // possible entry states.
164   constexpr size_t kBufferSize = 256;
165   BlobStoreBuffer<kBufferSize> blob(
166       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
167   EXPECT_EQ(OkStatus(), blob.Init());
168 }
169 
TEST_F(BlobStoreTest,Writer_ConservativeLimits)170 TEST_F(BlobStoreTest, Writer_ConservativeLimits) {
171   constexpr size_t kBufferSize = 256;
172   BlobStoreBuffer<kBufferSize> blob(
173       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
174   ASSERT_EQ(OkStatus(), blob.Init());
175 
176   BlobStore::BlobWriterWithBuffer writer(blob);
177   ASSERT_EQ(OkStatus(), writer.Open());
178   EXPECT_EQ(writer.ConservativeReadLimit(), 0u);
179   EXPECT_EQ(writer.ConservativeWriteLimit(), kSectorSize * kSectorCount);
180   ASSERT_EQ(OkStatus(), writer.Close());
181 
182   BlobStore::DeferredWriterWithBuffer deferred_writer(blob);
183   ASSERT_EQ(OkStatus(), deferred_writer.Open());
184   EXPECT_EQ(deferred_writer.ConservativeReadLimit(), 0u);
185   EXPECT_EQ(deferred_writer.ConservativeWriteLimit(), kBufferSize);
186 }
187 
188 // Write to the blob using a flash_write_size_bytes smaller than the
189 // buffer size. Use Write operations smaller than flash_write_size_bytes
190 // to ensure it checks the internal buffering path.
TEST_F(BlobStoreTest,OversizedWriteBuffer)191 TEST_F(BlobStoreTest, OversizedWriteBuffer) {
192   size_t write_size_bytes = 8;
193   ASSERT_LE(write_size_bytes, source_buffer_.size());
194   constexpr size_t kBufferSize = 256;
195   kvs::ChecksumCrc16 checksum;
196 
197   InitSourceBufferToRandom(0x123d123);
198 
199   ConstByteSpan write_data = span(source_buffer_);
200   ConstByteSpan original_source = span(source_buffer_);
201 
202   EXPECT_EQ(OkStatus(), partition_.Erase());
203 
204   BlobStoreBuffer<kBufferSize> blob(
205       kBlobTitle, partition_, &checksum, kvs::TestKvs(), 64);
206   EXPECT_EQ(OkStatus(), blob.Init());
207 
208   BlobStore::BlobWriterWithBuffer writer(blob);
209   EXPECT_EQ(OkStatus(), writer.Open());
210   while (write_data.size_bytes() > 0) {
211     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(write_size_bytes)));
212     write_data = write_data.subspan(write_size_bytes);
213   }
214   EXPECT_EQ(OkStatus(), writer.Close());
215 
216   // Use reader to check for valid data.
217   BlobStore::BlobReader reader(blob);
218   ASSERT_EQ(OkStatus(), reader.Open());
219   Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
220   ASSERT_TRUE(result.ok());
221   EXPECT_EQ(original_source.size_bytes(), result.value().size_bytes());
222   VerifyFlash(result.value());
223   VerifyFlash(flash_.buffer());
224   EXPECT_EQ(OkStatus(), reader.Close());
225 }
226 
TEST_F(BlobStoreTest,Reader_ConservativeLimits)227 TEST_F(BlobStoreTest, Reader_ConservativeLimits) {
228   InitSourceBufferToRandom(0x11309);
229   WriteTestBlock();
230 
231   kvs::ChecksumCrc16 checksum;
232   constexpr size_t kBufferSize = 16;
233   BlobStoreBuffer<kBufferSize> blob(
234       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
235   EXPECT_EQ(OkStatus(), blob.Init());
236   EXPECT_TRUE(blob.HasData());
237   BlobStore::BlobReader reader(blob);
238   ASSERT_EQ(OkStatus(), reader.Open());
239 
240   EXPECT_EQ(kBlobDataSize, reader.ConservativeReadLimit());
241   EXPECT_EQ(0u, reader.ConservativeWriteLimit());
242 }
243 
TEST_F(BlobStoreTest,IsOpen)244 TEST_F(BlobStoreTest, IsOpen) {
245   constexpr size_t kBufferSize = 256;
246   BlobStoreBuffer<kBufferSize> blob(
247       "Blob_open", partition_, nullptr, kvs::TestKvs(), kBufferSize);
248   EXPECT_EQ(OkStatus(), blob.Init());
249 
250   BlobStore::DeferredWriterWithBuffer deferred_writer(blob);
251   EXPECT_EQ(false, deferred_writer.IsOpen());
252   EXPECT_EQ(OkStatus(), deferred_writer.Open());
253   EXPECT_EQ(true, deferred_writer.IsOpen());
254   EXPECT_EQ(OkStatus(), deferred_writer.Close());
255   EXPECT_EQ(false, deferred_writer.IsOpen());
256 
257   BlobStore::BlobWriterWithBuffer writer(blob);
258   EXPECT_EQ(false, writer.IsOpen());
259   EXPECT_EQ(OkStatus(), writer.Open());
260   EXPECT_EQ(true, writer.IsOpen());
261 
262   EXPECT_FALSE(blob.HasData());
263 
264   // Need to write something, so the blob reader is able to open.
265   std::array<std::byte, 64> tmp_buffer = {};
266   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
267   EXPECT_EQ(OkStatus(), writer.Close());
268   EXPECT_EQ(false, writer.IsOpen());
269 
270   EXPECT_TRUE(blob.HasData());
271   BlobStore::BlobReader reader(blob);
272   EXPECT_EQ(false, reader.IsOpen());
273   ASSERT_EQ(OkStatus(), reader.Open());
274   EXPECT_EQ(true, reader.IsOpen());
275   EXPECT_EQ(OkStatus(), reader.Close());
276   EXPECT_EQ(false, reader.IsOpen());
277 }
278 
279 // Write to the blob using no write buffer size. Write operations must be
280 // multiples of flash_write_size_bytes.
TEST_F(BlobStoreTest,NoWriteBuffer_1Alignment)281 TEST_F(BlobStoreTest, NoWriteBuffer_1Alignment) {
282   if (kFlashAlignment > 1) {
283     // Test not valid for flash alignments greater than 1.
284     return;
285   }
286 
287   const size_t kWriteSizeBytes = 1;
288   kvs::ChecksumCrc16 checksum;
289 
290   InitSourceBufferToRandom(0xaabd123);
291 
292   ConstByteSpan write_data = span(source_buffer_);
293   ConstByteSpan original_source = span(source_buffer_);
294 
295   EXPECT_EQ(OkStatus(), partition_.Erase());
296 
297   BlobStore blob(kBlobTitle,
298                  partition_,
299                  &checksum,
300                  kvs::TestKvs(),
301                  span<std::byte>(),
302                  kWriteSizeBytes);
303   EXPECT_EQ(OkStatus(), blob.Init());
304 
305   BlobStore::BlobWriterWithBuffer writer(blob);
306   EXPECT_EQ(OkStatus(), writer.Open());
307 
308   size_t test_write_size[] = {1, 1, 2, 4, 32, 128};
309 
310   for (size_t size : test_write_size) {
311     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(size)));
312     write_data = write_data.subspan(size);
313   }
314 
315   while (write_data.size_bytes() > 0) {
316     const size_t finish_write_size = 8;
317     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(finish_write_size)));
318     write_data = write_data.subspan(finish_write_size);
319   }
320   EXPECT_EQ(write_data.size_bytes(), 0U);
321   EXPECT_EQ(OkStatus(), writer.Close());
322 
323   // Use reader to check for valid data.
324   BlobStore::BlobReader reader(blob);
325   ASSERT_EQ(OkStatus(), reader.Open());
326   Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
327   ASSERT_TRUE(result.ok());
328   EXPECT_EQ(original_source.size_bytes(), result.value().size_bytes());
329   VerifyFlash(result.value());
330   VerifyFlash(flash_.buffer());
331   EXPECT_EQ(OkStatus(), reader.Close());
332 }
333 
334 // Write to the blob using no write buffer size. Write operations must be
335 // multiples of flash_write_size_bytes.
TEST_F(BlobStoreTest,NoWriteBuffer_16Alignment)336 TEST_F(BlobStoreTest, NoWriteBuffer_16Alignment) {
337   if (kFlashAlignment > 16) {
338     // Test not valid for flash alignments greater than 16.
339     return;
340   }
341 
342   const size_t kWriteSizeBytes = 16;
343   kvs::ChecksumCrc16 checksum;
344 
345   InitSourceBufferToRandom(0x6745d123);
346 
347   ConstByteSpan write_data = span(source_buffer_);
348   ConstByteSpan original_source = span(source_buffer_);
349 
350   EXPECT_EQ(OkStatus(), partition_.Erase());
351 
352   BlobStore blob(kBlobTitle,
353                  partition_,
354                  &checksum,
355                  kvs::TestKvs(),
356                  span<std::byte>(),
357                  kWriteSizeBytes);
358   EXPECT_EQ(OkStatus(), blob.Init());
359 
360   BlobStore::BlobWriterWithBuffer writer(blob);
361   EXPECT_EQ(OkStatus(), writer.Open());
362   ASSERT_EQ(Status::InvalidArgument(), writer.Write(write_data.first(1)));
363   ASSERT_EQ(Status::InvalidArgument(),
364             writer.Write(write_data.first(kWriteSizeBytes / 2)));
365 
366   ASSERT_EQ(OkStatus(), writer.Write(write_data.first(4 * kWriteSizeBytes)));
367   write_data = write_data.subspan(4 * kWriteSizeBytes);
368 
369   ASSERT_EQ(Status::InvalidArgument(), writer.Write(write_data.first(1)));
370   ASSERT_EQ(Status::InvalidArgument(),
371             writer.Write(write_data.first(kWriteSizeBytes / 2)));
372 
373   while (write_data.size_bytes() > 0) {
374     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(kWriteSizeBytes)));
375     write_data = write_data.subspan(kWriteSizeBytes);
376   }
377   EXPECT_EQ(OkStatus(), writer.Close());
378 
379   // Use reader to check for valid data.
380   BlobStore::BlobReader reader(blob);
381   ASSERT_EQ(OkStatus(), reader.Open());
382   Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
383   ASSERT_TRUE(result.ok());
384   EXPECT_EQ(original_source.size_bytes(), result.value().size_bytes());
385   VerifyFlash(result.value());
386   VerifyFlash(flash_.buffer());
387   EXPECT_EQ(OkStatus(), reader.Close());
388 }
389 
TEST_F(BlobStoreTest,FileName)390 TEST_F(BlobStoreTest, FileName) {
391   InitSourceBufferToRandom(0x8675309);
392   WriteTestBlock();
393   constexpr std::string_view kFileName("my_file_1.bin");
394   std::array<std::byte, 64> tmp_buffer = {};
395   static_assert(kFileName.size() <= tmp_buffer.size());
396   kvs::ChecksumCrc16 checksum;
397   constexpr size_t kBufferSize = 256;
398   {
399     // Create/init a blob store in a nested scope so it can be re-initialized
400     // later when checking the read.
401     BlobStoreBuffer<kBufferSize> blob(
402         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
403     EXPECT_EQ(OkStatus(), blob.Init());
404 
405     BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
406 
407     EXPECT_EQ(OkStatus(), writer.Open());
408     EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
409     EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
410     EXPECT_EQ(OkStatus(), writer.Close());
411     EXPECT_EQ(OkStatus(),
412               kvs::TestKvs().acquire()->Get(kBlobTitle, tmp_buffer).status());
413   }
414 
415   BlobStoreBuffer<kBufferSize> blob(
416       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
417   EXPECT_EQ(OkStatus(), blob.Init());
418 
419   // Ensure the file name can be read from a reader.
420   BlobStore::BlobReader reader(blob);
421   ASSERT_EQ(OkStatus(), reader.Open());
422 
423   memset(tmp_buffer.data(), 0, tmp_buffer.size());
424   StatusWithSize sws = reader.GetFileName(
425       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
426 
427   EXPECT_EQ(OkStatus(), sws.status());
428   ASSERT_EQ(kFileName.size(), sws.size());
429   EXPECT_EQ(memcmp(kFileName.data(), tmp_buffer.data(), kFileName.size()), 0);
430 }
431 
TEST_F(BlobStoreTest,FileNameUndersizedRead)432 TEST_F(BlobStoreTest, FileNameUndersizedRead) {
433   InitSourceBufferToRandom(0x8675309);
434   WriteTestBlock();
435   constexpr std::string_view kFileName("my_file_1.bin");
436   std::array<std::byte, 4> tmp_buffer = {};
437   static_assert(kFileName.size() > tmp_buffer.size());
438 
439   kvs::ChecksumCrc16 checksum;
440   constexpr size_t kBufferSize = 256;
441   BlobStoreBuffer<kBufferSize> blob(
442       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
443   EXPECT_EQ(OkStatus(), blob.Init());
444 
445   BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
446 
447   EXPECT_EQ(OkStatus(), writer.Open());
448   EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
449   EXPECT_EQ(OkStatus(), writer.Write(as_bytes(span("some interesting data"))));
450   EXPECT_EQ(OkStatus(), writer.Close());
451 
452   // Ensure the file name can be read from a reader.
453   BlobStore::BlobReader reader(blob);
454   ASSERT_EQ(OkStatus(), reader.Open());
455 
456   StatusWithSize sws = reader.GetFileName(
457       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
458   EXPECT_EQ(Status::ResourceExhausted(), sws.status());
459   ASSERT_EQ(tmp_buffer.size(), sws.size());
460   EXPECT_EQ(memcmp(kFileName.data(), tmp_buffer.data(), sws.size()), 0);
461 }
462 
TEST_F(BlobStoreTest,FileNameUndersizedSet)463 TEST_F(BlobStoreTest, FileNameUndersizedSet) {
464   InitSourceBufferToRandom(0x8675309);
465   WriteTestBlock();
466   constexpr std::string_view kFileName("my_file_1.bin");
467 
468   kvs::ChecksumCrc16 checksum;
469   constexpr size_t kBufferSize = 256;
470   BlobStoreBuffer<kBufferSize> blob(
471       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
472   EXPECT_EQ(OkStatus(), blob.Init());
473 
474   BlobStore::BlobWriterWithBuffer<2> writer(blob);
475 
476   EXPECT_EQ(OkStatus(), writer.Open());
477   EXPECT_EQ(Status::ResourceExhausted(), writer.SetFileName(kFileName));
478   EXPECT_EQ(OkStatus(), writer.Close());
479 }
480 
TEST_F(BlobStoreTest,FileNameInvalidation)481 TEST_F(BlobStoreTest, FileNameInvalidation) {
482   InitSourceBufferToRandom(0x8675309);
483   WriteTestBlock();
484 
485   constexpr std::string_view kFileName("sliced_cheese.png");
486   std::array<std::byte, 64> tmp_buffer = {};
487   static_assert(kFileName.size() <= tmp_buffer.size());
488 
489   kvs::ChecksumCrc16 checksum;
490   constexpr size_t kBufferSize = 256;
491   BlobStoreBuffer<kBufferSize> blob(
492       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
493   EXPECT_EQ(OkStatus(), blob.Init());
494 
495   BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
496 
497   EXPECT_EQ(OkStatus(), writer.Open());
498   EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
499   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
500   EXPECT_EQ(OkStatus(), writer.Discard());
501   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
502   EXPECT_EQ(OkStatus(), writer.Close());
503 
504   // Check that the file name was discarded by Discard().
505   memset(tmp_buffer.data(), 0, tmp_buffer.size());
506   BlobStore::BlobReader reader(blob);
507   ASSERT_EQ(OkStatus(), reader.Open());
508   StatusWithSize sws = reader.GetFileName(
509       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
510   EXPECT_EQ(Status::NotFound(), sws.status());
511   ASSERT_EQ(0u, sws.size());
512 }
513 
TEST_F(BlobStoreTest,NoFileName)514 TEST_F(BlobStoreTest, NoFileName) {
515   InitSourceBufferToRandom(0x8675309);
516   WriteTestBlock();
517 
518   std::array<std::byte, 64> tmp_buffer = {};
519   kvs::ChecksumCrc16 checksum;
520   constexpr size_t kBufferSize = 256;
521   BlobStoreBuffer<kBufferSize> blob(
522       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
523   EXPECT_EQ(OkStatus(), blob.Init());
524 
525   // Ensure blobs with no file names work as expected.
526   BlobStore::BlobReader reader(blob);
527   ASSERT_EQ(OkStatus(), reader.Open());
528 
529   StatusWithSize sws = reader.GetFileName(
530       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
531   EXPECT_EQ(Status::NotFound(), sws.status());
532   ASSERT_EQ(0u, sws.size());
533 }
534 
TEST_F(BlobStoreTest,V1MetadataBackwardsCompatible)535 TEST_F(BlobStoreTest, V1MetadataBackwardsCompatible) {
536   constexpr size_t kWriteSize = 25;
537   WriteTestBlock(kWriteSize);
538 
539   kvs::ChecksumCrc16 checksum;
540   constexpr size_t kBufferSize = 16;
541   BlobStoreBuffer<kBufferSize> blob(
542       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
543   EXPECT_EQ(OkStatus(), blob.Init());
544 
545   // Read the written data in the current format.
546   internal::BlobMetadataHeader current_metadata;
547   ASSERT_EQ(OkStatus(),
548             kvs::TestKvs().acquire()->Get(kBlobTitle, &current_metadata));
549 
550   // Re-save only the V1 metadata contents.
551   ASSERT_EQ(
552       OkStatus(),
553       kvs::TestKvs().acquire()->Put(kBlobTitle, current_metadata.v1_metadata));
554 
555   // Ensure the BlobStore's contents aren't invalid.
556   BlobStore::BlobReader reader(blob);
557   ASSERT_EQ(OkStatus(), reader.Open());
558   ASSERT_EQ(kWriteSize, reader.ConservativeReadLimit());
559   ASSERT_EQ(current_metadata.v1_metadata.data_size_bytes,
560             reader.ConservativeReadLimit());
561 }
562 
TEST_F(BlobStoreTest,Discard)563 TEST_F(BlobStoreTest, Discard) {
564   InitSourceBufferToRandom(0x8675309);
565   WriteTestBlock();
566   constexpr char blob_title[] = "TestBlobBlock";
567   std::array<std::byte, 64> tmp_buffer = {};
568 
569   kvs::ChecksumCrc16 checksum;
570 
571   // TODO(davidrogers): Do this test with flash/kvs in the different entry state
572   // combinations.
573 
574   constexpr size_t kBufferSize = 256;
575   BlobStoreBuffer<kBufferSize> blob(
576       blob_title, partition_, &checksum, kvs::TestKvs(), kBufferSize);
577   EXPECT_EQ(OkStatus(), blob.Init());
578 
579   EXPECT_TRUE(blob.HasData());
580 
581   BlobStore::BlobWriterWithBuffer writer(blob);
582 
583   EXPECT_EQ(OkStatus(), writer.Open());
584   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
585 
586   // Blob should NOT be valid to read, because the write data was only buffered,
587   // and has not been written to flash yet.
588   EXPECT_FALSE(blob.HasData());
589 
590   // The write does an implicit erase so there should be no key for this blob.
591   EXPECT_EQ(Status::NotFound(),
592             kvs::TestKvs().acquire()->Get(blob_title, tmp_buffer).status());
593   EXPECT_EQ(OkStatus(), writer.Close());
594 
595   EXPECT_TRUE(blob.HasData());
596 
597   EXPECT_EQ(OkStatus(),
598             kvs::TestKvs().acquire()->Get(blob_title, tmp_buffer).status());
599 
600   EXPECT_EQ(OkStatus(), writer.Open());
601   EXPECT_EQ(OkStatus(), writer.Discard());
602   EXPECT_EQ(OkStatus(), writer.Close());
603 
604   EXPECT_FALSE(blob.HasData());
605 
606   EXPECT_EQ(Status::NotFound(),
607             kvs::TestKvs().acquire()->Get(blob_title, tmp_buffer).status());
608 }
609 
TEST_F(BlobStoreTest,MultipleErase)610 TEST_F(BlobStoreTest, MultipleErase) {
611   constexpr size_t kBufferSize = 256;
612   BlobStoreBuffer<kBufferSize> blob(
613       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
614   EXPECT_EQ(OkStatus(), blob.Init());
615 
616   BlobStore::BlobWriterWithBuffer writer(blob);
617   EXPECT_EQ(OkStatus(), writer.Open());
618 
619   EXPECT_EQ(OkStatus(), writer.Erase());
620   EXPECT_EQ(OkStatus(), writer.Erase());
621   EXPECT_EQ(OkStatus(), writer.Erase());
622 }
623 
TEST_F(BlobStoreTest,OffsetRead)624 TEST_F(BlobStoreTest, OffsetRead) {
625   InitSourceBufferToRandom(0x11309);
626   WriteTestBlock();
627 
628   constexpr size_t kOffset = 10;
629   ASSERT_LT(kOffset, kBlobDataSize);
630 
631   kvs::ChecksumCrc16 checksum;
632 
633   char name[16] = "TestBlobBlock";
634   constexpr size_t kBufferSize = 16;
635   BlobStoreBuffer<kBufferSize> blob(
636       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
637   EXPECT_EQ(OkStatus(), blob.Init());
638   BlobStore::BlobReader reader(blob);
639   ASSERT_EQ(OkStatus(), reader.Open(kOffset));
640 
641   std::array<std::byte, kBlobDataSize - kOffset> read_buffer;
642   ByteSpan read_span = read_buffer;
643   ASSERT_EQ(read_span.size_bytes(), reader.ConservativeReadLimit());
644 
645   auto result = reader.Read(read_span);
646   ASSERT_EQ(result.status(), OkStatus());
647   EXPECT_EQ(OkStatus(), reader.Close());
648   VerifyFlash(read_buffer, kOffset);
649 }
650 
TEST_F(BlobStoreTest,SeekOffsetRead)651 TEST_F(BlobStoreTest, SeekOffsetRead) {
652   InitSourceBufferToRandom(0x11309);
653   WriteTestBlock();
654 
655   constexpr size_t kOffset = 10;
656   ASSERT_LT(kOffset, kBlobDataSize);
657 
658   kvs::ChecksumCrc16 checksum;
659 
660   char name[16] = "TestBlobBlock";
661   constexpr size_t kBufferSize = 16;
662   BlobStoreBuffer<kBufferSize> blob(
663       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
664   EXPECT_EQ(OkStatus(), blob.Init());
665   BlobStore::BlobReader reader(blob);
666   ASSERT_EQ(OkStatus(), reader.Open());
667   ASSERT_EQ(OkStatus(), reader.Seek(kOffset));
668 
669   std::array<std::byte, kBlobDataSize - kOffset> read_buffer;
670   ByteSpan read_span = read_buffer;
671   ASSERT_EQ(read_span.size_bytes(), reader.ConservativeReadLimit());
672 
673   auto result = reader.Read(read_span);
674   ASSERT_EQ(result.status(), OkStatus());
675   EXPECT_EQ(OkStatus(), reader.Close());
676   VerifyFlash(read_buffer, kOffset);
677 }
678 
TEST_F(BlobStoreTest,InvalidReadOffset)679 TEST_F(BlobStoreTest, InvalidReadOffset) {
680   InitSourceBufferToRandom(0x11309);
681   WriteTestBlock();
682 
683   constexpr size_t kOffset = kBlobDataSize;
684 
685   kvs::ChecksumCrc16 checksum;
686 
687   char name[16] = "TestBlobBlock";
688   constexpr size_t kBufferSize = 16;
689   BlobStoreBuffer<kBufferSize> blob(
690       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
691   EXPECT_EQ(OkStatus(), blob.Init());
692   BlobStore::BlobReader reader(blob);
693   ASSERT_EQ(Status::InvalidArgument(), reader.Open(kOffset));
694 }
695 
TEST_F(BlobStoreTest,ReadSeekClosedReader)696 TEST_F(BlobStoreTest, ReadSeekClosedReader) {
697   InitSourceBufferToRandom(0x11309);
698   WriteTestBlock();
699 
700   kvs::ChecksumCrc16 checksum;
701 
702   char name[16] = "TestBlobBlock";
703   constexpr size_t kBufferSize = 16;
704   BlobStoreBuffer<kBufferSize> blob(
705       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
706   EXPECT_EQ(OkStatus(), blob.Init());
707   BlobStore::BlobReader reader(blob);
708   ASSERT_EQ(OkStatus(), reader.Open());
709   ASSERT_EQ(OkStatus(), reader.Close());
710 
711   EXPECT_EQ(Status::FailedPrecondition(), reader.Seek(0));
712 
713   std::byte read_buffer[32];
714   EXPECT_EQ(Status::FailedPrecondition(), reader.Read(read_buffer).status());
715 }
716 
TEST_F(BlobStoreTest,InvalidSeekOffset)717 TEST_F(BlobStoreTest, InvalidSeekOffset) {
718   InitSourceBufferToRandom(0x11309);
719   WriteTestBlock();
720 
721   constexpr size_t kOffset = kBlobDataSize;
722 
723   kvs::ChecksumCrc16 checksum;
724 
725   char name[16] = "TestBlobBlock";
726   constexpr size_t kBufferSize = 16;
727   BlobStoreBuffer<kBufferSize> blob(
728       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
729   EXPECT_EQ(OkStatus(), blob.Init());
730   BlobStore::BlobReader reader(blob);
731   ASSERT_EQ(OkStatus(), reader.Open());
732   ASSERT_EQ(Status::OutOfRange(), reader.Seek(kOffset));
733 }
734 
735 // Write a block to blob and close with part of a write buffer with unflushed
736 // data.
TEST_F(BlobStoreTest,WriteBufferWithRemainderInBuffer)737 TEST_F(BlobStoreTest, WriteBufferWithRemainderInBuffer) {
738   InitSourceBufferToRandom(0x11309);
739 
740   kvs::ChecksumCrc16 checksum;
741   constexpr size_t kBufferSize = 256;
742   BlobStoreBuffer<kBufferSize> blob(
743       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
744   EXPECT_EQ(OkStatus(), blob.Init());
745 
746   const size_t write_size_bytes = kBlobDataSize - 10;
747   ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
748 
749   BlobStore::BlobWriterWithBuffer writer(blob);
750   EXPECT_EQ(OkStatus(), writer.Open());
751   ASSERT_EQ(OkStatus(), writer.Write(write_data));
752   EXPECT_EQ(OkStatus(), writer.Close());
753 
754   BlobStore::BlobReader reader(blob);
755   ASSERT_EQ(OkStatus(), reader.Open());
756   EXPECT_EQ(write_size_bytes, reader.ConservativeReadLimit());
757 }
758 
759 // Test reading with a read buffer larger than the available data in the blob.
TEST_F(BlobStoreTest,ReadBufferIsLargerThanData)760 TEST_F(BlobStoreTest, ReadBufferIsLargerThanData) {
761   InitSourceBufferToRandom(0x57326);
762 
763   constexpr size_t kWriteBytes = 64;
764   WriteTestBlock(kWriteBytes);
765 
766   kvs::ChecksumCrc16 checksum;
767 
768   char name[16] = "TestBlobBlock";
769   constexpr size_t kBufferSize = 16;
770   BlobStoreBuffer<kBufferSize> blob(
771       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
772   EXPECT_EQ(OkStatus(), blob.Init());
773   BlobStore::BlobReader reader(blob);
774   ASSERT_EQ(OkStatus(), reader.Open());
775   EXPECT_EQ(kWriteBytes, reader.ConservativeReadLimit());
776 
777   std::array<std::byte, kWriteBytes + 10> read_buffer;
778   ByteSpan read_span = read_buffer;
779 
780   auto result = reader.Read(read_span);
781   ASSERT_EQ(result.status(), OkStatus());
782   EXPECT_EQ(OkStatus(), reader.Close());
783 }
784 
TEST_F(BlobStoreTest,ChunkRead1)785 TEST_F(BlobStoreTest, ChunkRead1) {
786   InitSourceBufferToRandom(0x8675309);
787   WriteTestBlock();
788   ChunkReadTest(1);
789 }
790 
TEST_F(BlobStoreTest,ChunkRead3)791 TEST_F(BlobStoreTest, ChunkRead3) {
792   InitSourceBufferToFill(0);
793   WriteTestBlock();
794   ChunkReadTest(3);
795 }
796 
TEST_F(BlobStoreTest,ChunkRead4)797 TEST_F(BlobStoreTest, ChunkRead4) {
798   InitSourceBufferToFill(1);
799   WriteTestBlock();
800   ChunkReadTest(4);
801 }
802 
TEST_F(BlobStoreTest,ChunkRead5)803 TEST_F(BlobStoreTest, ChunkRead5) {
804   InitSourceBufferToFill(0xff);
805   WriteTestBlock();
806   ChunkReadTest(5);
807 }
808 
TEST_F(BlobStoreTest,ChunkRead16)809 TEST_F(BlobStoreTest, ChunkRead16) {
810   InitSourceBufferToRandom(0x86);
811   WriteTestBlock();
812   ChunkReadTest(16);
813 }
814 
TEST_F(BlobStoreTest,ChunkRead64)815 TEST_F(BlobStoreTest, ChunkRead64) {
816   InitSourceBufferToRandom(0x9);
817   WriteTestBlock();
818   ChunkReadTest(64);
819 }
820 
TEST_F(BlobStoreTest,ChunkReadFull)821 TEST_F(BlobStoreTest, ChunkReadFull) {
822   InitSourceBufferToRandom(0x9);
823   WriteTestBlock();
824   ChunkReadTest(kBlobDataSize);
825 }
826 
TEST_F(BlobStoreTest,PartialBufferThenClose)827 TEST_F(BlobStoreTest, PartialBufferThenClose) {
828   // Do write of only a partial chunk, which will only have bytes in buffer
829   // (none written to flash) at close.
830   size_t data_bytes = 12;
831   InitSourceBufferToRandom(0x111, data_bytes);
832   WriteTestBlock(data_bytes);
833 
834   // Do write with several full chunks and then some partial.
835   data_bytes = 158;
836   InitSourceBufferToRandom(0x3222, data_bytes);
837 }
838 
839 // Test to do write/close, write/close multiple times.
TEST_F(BlobStoreTest,MultipleWrites)840 TEST_F(BlobStoreTest, MultipleWrites) {
841   InitSourceBufferToRandom(0x1121);
842   WriteTestBlock();
843   InitSourceBufferToRandom(0x515);
844   WriteTestBlock();
845   InitSourceBufferToRandom(0x4321);
846   WriteTestBlock();
847 }
848 
849 }  // namespace
850 }  // namespace pw::blob_store
851