xref: /aosp_15_r20/external/pigweed/pw_blob_store/blob_store_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 "pw_kvs/crc16_checksum.h"
22 #include "pw_kvs/fake_flash_memory.h"
23 #include "pw_kvs/flash_memory.h"
24 #include "pw_kvs/test_key_value_store.h"
25 #include "pw_log/log.h"
26 #include "pw_random/xor_shift.h"
27 #include "pw_span/span.h"
28 #include "pw_unit_test/framework.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     VerifyBlob(blob, write_size_bytes);
87   }
88 
VerifyBlob(BlobStore & blob,size_t expected_data_size)89   void VerifyBlob(BlobStore& blob, size_t expected_data_size) {
90     EXPECT_TRUE(blob.HasData());
91     BlobStore::BlobReader reader(blob);
92     ASSERT_EQ(OkStatus(), reader.Open());
93     EXPECT_EQ(expected_data_size, reader.ConservativeReadLimit());
94     Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
95     ASSERT_TRUE(result.ok());
96     EXPECT_EQ(expected_data_size, result.value().size_bytes());
97     VerifyFlash(result.value().first(result.value().size_bytes()));
98     VerifyFlash(flash_.buffer().first(result.value().size_bytes()));
99     EXPECT_EQ(OkStatus(), reader.Close());
100   }
101 
102   // Open a new blob instance and read the blob using the given read chunk size.
ChunkReadTest(size_t read_chunk_size)103   void ChunkReadTest(size_t read_chunk_size) {
104     kvs::ChecksumCrc16 checksum;
105 
106     VerifyFlash(flash_.buffer());
107 
108     constexpr size_t kBufferSize = 16;
109     BlobStoreBuffer<kBufferSize> blob(
110         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
111     EXPECT_EQ(OkStatus(), blob.Init());
112 
113     // Use reader to check for valid data.
114     BlobStore::BlobReader reader1(blob);
115     ASSERT_EQ(OkStatus(), reader1.Open());
116     Result<ConstByteSpan> possible_blob = reader1.GetMemoryMappedBlob();
117     ASSERT_TRUE(possible_blob.ok());
118     VerifyFlash(possible_blob.value());
119     EXPECT_EQ(OkStatus(), reader1.Close());
120 
121     BlobStore::BlobReader reader(blob);
122     ASSERT_EQ(OkStatus(), reader.Open());
123 
124     std::array<std::byte, kBlobDataSize> read_buffer;
125 
126     ByteSpan read_span = read_buffer;
127     while (read_span.size_bytes() > 0) {
128       size_t read_size = std::min(read_span.size_bytes(), read_chunk_size);
129 
130       PW_LOG_DEBUG("Do write of %u bytes, %u bytes remain",
131                    static_cast<unsigned>(read_size),
132                    static_cast<unsigned>(read_span.size_bytes()));
133 
134       ASSERT_EQ(read_span.size_bytes(), reader.ConservativeReadLimit());
135       auto result = reader.Read(read_span.first(read_size));
136       ASSERT_EQ(result.status(), OkStatus());
137       read_span = read_span.subspan(read_size);
138     }
139     EXPECT_EQ(OkStatus(), reader.Close());
140 
141     VerifyFlash(read_buffer);
142   }
143 
VerifyFlash(ConstByteSpan verify_bytes,size_t offset=0)144   void VerifyFlash(ConstByteSpan verify_bytes, size_t offset = 0) {
145     // Should be defined as same size.
146     EXPECT_EQ(source_buffer_.size(), flash_.buffer().size_bytes());
147 
148     // Can't allow it to march off the end of source_buffer_.
149     ASSERT_LE((verify_bytes.size_bytes() + offset), source_buffer_.size());
150 
151     for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
152       ASSERT_EQ(source_buffer_[i + offset], verify_bytes[i]);
153     }
154   }
155 
156   static constexpr size_t kFlashAlignment = PW_FLASH_TEST_ALIGNMENT;
157   static constexpr size_t kSectorSize = 1024;
158   static constexpr size_t kSectorCount = 6;
159   static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
160 
161   kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
162   kvs::FlashPartition partition_;
163   std::array<std::byte, kBlobDataSize> source_buffer_;
164 };
165 
TEST_F(BlobStoreTest,Init_Ok)166 TEST_F(BlobStoreTest, Init_Ok) {
167   // TODO(davidrogers): Do init test with flash/kvs explicitly in the different
168   // possible entry states.
169   constexpr size_t kBufferSize = 256;
170   BlobStoreBuffer<kBufferSize> blob(
171       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
172   EXPECT_EQ(OkStatus(), blob.Init());
173 }
174 
TEST_F(BlobStoreTest,Writer_ConservativeLimits)175 TEST_F(BlobStoreTest, Writer_ConservativeLimits) {
176   constexpr size_t kBufferSize = 256;
177   BlobStoreBuffer<kBufferSize> blob(
178       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
179   ASSERT_EQ(OkStatus(), blob.Init());
180 
181   BlobStore::BlobWriterWithBuffer writer(blob);
182   ASSERT_EQ(OkStatus(), writer.Open());
183   EXPECT_EQ(writer.ConservativeReadLimit(), 0u);
184   EXPECT_EQ(writer.ConservativeWriteLimit(), kSectorSize * kSectorCount);
185   ASSERT_EQ(OkStatus(), writer.Close());
186 
187   BlobStore::DeferredWriterWithBuffer deferred_writer(blob);
188   ASSERT_EQ(OkStatus(), deferred_writer.Open());
189   EXPECT_EQ(deferred_writer.ConservativeReadLimit(), 0u);
190   EXPECT_EQ(deferred_writer.ConservativeWriteLimit(), kBufferSize);
191 }
192 
193 // Write to the blob using a flash_write_size_bytes smaller than the
194 // buffer size. Use Write operations smaller than flash_write_size_bytes
195 // to ensure it checks the internal buffering path.
TEST_F(BlobStoreTest,OversizedWriteBuffer)196 TEST_F(BlobStoreTest, OversizedWriteBuffer) {
197   size_t write_size_bytes = 8;
198   ASSERT_LE(write_size_bytes, source_buffer_.size());
199   constexpr size_t kBufferSize = 256;
200   kvs::ChecksumCrc16 checksum;
201 
202   InitSourceBufferToRandom(0x123d123);
203 
204   ConstByteSpan write_data = span(source_buffer_);
205   ConstByteSpan original_source = span(source_buffer_);
206 
207   EXPECT_EQ(OkStatus(), partition_.Erase());
208 
209   BlobStoreBuffer<kBufferSize> blob(
210       kBlobTitle, partition_, &checksum, kvs::TestKvs(), 64);
211   EXPECT_EQ(OkStatus(), blob.Init());
212 
213   BlobStore::BlobWriterWithBuffer writer(blob);
214   EXPECT_EQ(OkStatus(), writer.Open());
215   while (write_data.size_bytes() > 0) {
216     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(write_size_bytes)));
217     write_data = write_data.subspan(write_size_bytes);
218   }
219   EXPECT_EQ(OkStatus(), writer.Close());
220 
221   VerifyBlob(blob, original_source.size_bytes());
222 }
223 
TEST_F(BlobStoreTest,Reader_ConservativeLimits)224 TEST_F(BlobStoreTest, Reader_ConservativeLimits) {
225   InitSourceBufferToRandom(0x11309);
226   WriteTestBlock();
227 
228   kvs::ChecksumCrc16 checksum;
229   constexpr size_t kBufferSize = 16;
230   BlobStoreBuffer<kBufferSize> blob(
231       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
232   EXPECT_EQ(OkStatus(), blob.Init());
233   EXPECT_TRUE(blob.HasData());
234   BlobStore::BlobReader reader(blob);
235   ASSERT_EQ(OkStatus(), reader.Open());
236 
237   EXPECT_EQ(kBlobDataSize, reader.ConservativeReadLimit());
238   EXPECT_EQ(0u, reader.ConservativeWriteLimit());
239 }
240 
TEST_F(BlobStoreTest,IsOpen)241 TEST_F(BlobStoreTest, IsOpen) {
242   constexpr size_t kBufferSize = 256;
243   BlobStoreBuffer<kBufferSize> blob(
244       "Blob_open", partition_, nullptr, kvs::TestKvs(), kBufferSize);
245 
246   BlobStore::DeferredWriterWithBuffer deferred_writer(blob);
247   BlobStore::BlobWriterWithBuffer regular_writer(blob);
248   BlobStore::BlobReader reader(blob);
249   EXPECT_EQ(Status::FailedPrecondition(), regular_writer.Open());
250   EXPECT_EQ(Status::FailedPrecondition(), regular_writer.Resume().status());
251   EXPECT_EQ(Status::FailedPrecondition(), reader.Open());
252 
253   EXPECT_EQ(OkStatus(), blob.Init());
254 
255   EXPECT_EQ(false, deferred_writer.IsOpen());
256   EXPECT_EQ(OkStatus(), deferred_writer.Open());
257   EXPECT_EQ(true, deferred_writer.IsOpen());
258 
259   EXPECT_EQ(Status::Unavailable(), regular_writer.Open());
260   EXPECT_EQ(Status::Unavailable(), reader.Open());
261 
262   EXPECT_EQ(OkStatus(), deferred_writer.Close());
263   EXPECT_EQ(false, deferred_writer.IsOpen());
264 
265   EXPECT_EQ(false, regular_writer.IsOpen());
266   EXPECT_EQ(OkStatus(), regular_writer.Open());
267   EXPECT_EQ(true, regular_writer.IsOpen());
268 
269   EXPECT_FALSE(blob.HasData());
270 
271   // Need to write something, so the blob reader is able to open.
272   std::array<std::byte, 64> tmp_buffer = {};
273   EXPECT_EQ(OkStatus(), regular_writer.Write(tmp_buffer));
274   EXPECT_EQ(OkStatus(), regular_writer.Close());
275   EXPECT_EQ(false, regular_writer.IsOpen());
276 
277   EXPECT_TRUE(blob.HasData());
278   EXPECT_EQ(false, reader.IsOpen());
279   ASSERT_EQ(OkStatus(), reader.Open());
280   EXPECT_EQ(true, reader.IsOpen());
281   EXPECT_EQ(OkStatus(), reader.Close());
282   EXPECT_EQ(false, reader.IsOpen());
283 }
284 
285 // Write to the blob using no write buffer size. Write operations must be
286 // multiples of flash_write_size_bytes.
TEST_F(BlobStoreTest,NoWriteBuffer_1Alignment)287 TEST_F(BlobStoreTest, NoWriteBuffer_1Alignment) {
288   if (kFlashAlignment > 1) {
289     // Test not valid for flash alignments greater than 1.
290     return;
291   }
292 
293   const size_t kWriteSizeBytes = 1;
294   kvs::ChecksumCrc16 checksum;
295 
296   InitSourceBufferToRandom(0xaabd123);
297 
298   ConstByteSpan write_data = span(source_buffer_);
299   ConstByteSpan original_source = span(source_buffer_);
300 
301   EXPECT_EQ(OkStatus(), partition_.Erase());
302 
303   BlobStore blob(kBlobTitle,
304                  partition_,
305                  &checksum,
306                  kvs::TestKvs(),
307                  span<std::byte>(),
308                  kWriteSizeBytes);
309   EXPECT_EQ(OkStatus(), blob.Init());
310 
311   BlobStore::BlobWriterWithBuffer writer(blob);
312   EXPECT_EQ(OkStatus(), writer.Open());
313 
314   size_t test_write_size[] = {1, 1, 2, 4, 32, 128};
315 
316   for (size_t size : test_write_size) {
317     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(size)));
318     write_data = write_data.subspan(size);
319   }
320 
321   while (write_data.size_bytes() > 0) {
322     const size_t finish_write_size = 8;
323     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(finish_write_size)));
324     write_data = write_data.subspan(finish_write_size);
325   }
326   EXPECT_EQ(write_data.size_bytes(), 0U);
327   EXPECT_EQ(OkStatus(), writer.Close());
328 
329   VerifyBlob(blob, original_source.size_bytes());
330 }
331 
332 // Write to the blob using no write buffer size. Write operations must be
333 // multiples of flash_write_size_bytes.
TEST_F(BlobStoreTest,NoWriteBuffer_16Alignment)334 TEST_F(BlobStoreTest, NoWriteBuffer_16Alignment) {
335   if (kFlashAlignment > 16) {
336     // Test not valid for flash alignments greater than 16.
337     return;
338   }
339 
340   const size_t kWriteSizeBytes = 16;
341   kvs::ChecksumCrc16 checksum;
342 
343   InitSourceBufferToRandom(0x6745d123);
344 
345   ConstByteSpan write_data = span(source_buffer_);
346   ConstByteSpan original_source = span(source_buffer_);
347 
348   EXPECT_EQ(OkStatus(), partition_.Erase());
349 
350   BlobStore blob(kBlobTitle,
351                  partition_,
352                  &checksum,
353                  kvs::TestKvs(),
354                  span<std::byte>(),
355                  kWriteSizeBytes);
356   EXPECT_EQ(OkStatus(), blob.Init());
357 
358   BlobStore::BlobWriterWithBuffer writer(blob);
359   EXPECT_EQ(OkStatus(), writer.Open());
360   ASSERT_EQ(Status::InvalidArgument(), writer.Write(write_data.first(1)));
361   ASSERT_EQ(Status::InvalidArgument(),
362             writer.Write(write_data.first(kWriteSizeBytes / 2)));
363 
364   ASSERT_EQ(OkStatus(), writer.Write(write_data.first(4 * kWriteSizeBytes)));
365   write_data = write_data.subspan(4 * kWriteSizeBytes);
366 
367   ASSERT_EQ(Status::InvalidArgument(), writer.Write(write_data.first(1)));
368   ASSERT_EQ(Status::InvalidArgument(),
369             writer.Write(write_data.first(kWriteSizeBytes / 2)));
370 
371   while (write_data.size_bytes() > 0) {
372     ASSERT_EQ(OkStatus(), writer.Write(write_data.first(kWriteSizeBytes)));
373     write_data = write_data.subspan(kWriteSizeBytes);
374   }
375   EXPECT_EQ(OkStatus(), writer.Close());
376 
377   VerifyBlob(blob, original_source.size_bytes());
378 }
379 
TEST_F(BlobStoreTest,FileName)380 TEST_F(BlobStoreTest, FileName) {
381   InitSourceBufferToRandom(0x8675309);
382   WriteTestBlock();
383   constexpr std::string_view kFileName("my_file_1.bin");
384   std::array<std::byte, 64> tmp_buffer = {};
385   static_assert(kFileName.size() <= tmp_buffer.size());
386   kvs::ChecksumCrc16 checksum;
387   constexpr size_t kBufferSize = 256;
388   {
389     // Create/init a blob store in a nested scope so it can be re-initialized
390     // later when checking the read.
391     BlobStoreBuffer<kBufferSize> blob(
392         kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
393     EXPECT_EQ(OkStatus(), blob.Init());
394 
395     BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
396 
397     EXPECT_EQ(OkStatus(), writer.Open());
398     EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
399     EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
400 
401     // Read back filename from the open writer.
402     memset(tmp_buffer.data(), 0, tmp_buffer.size());
403     StatusWithSize sws = writer.GetFileName(
404         {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
405     EXPECT_EQ(OkStatus(), sws.status());
406     ASSERT_EQ(kFileName.size(), sws.size());
407     EXPECT_EQ(memcmp(kFileName.data(), tmp_buffer.data(), kFileName.size()), 0);
408     // END of read back filename from the open writer.
409 
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   std::array<std::byte, 64> tmp_buffer = {};
468   std::array<std::byte, 64> zero_buffer = {};
469   static_assert(kFileName.size() <= tmp_buffer.size());
470 
471   kvs::ChecksumCrc16 checksum;
472   constexpr size_t kBufferSize = 256;
473   BlobStoreBuffer<kBufferSize> blob(
474       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
475   EXPECT_EQ(OkStatus(), blob.Init());
476 
477   BlobStore::BlobWriterWithBuffer<2> writer(blob);
478 
479   EXPECT_EQ(OkStatus(), writer.Open());
480   EXPECT_EQ(Status::ResourceExhausted(), writer.SetFileName(kFileName));
481 
482   // Read back filename from the open writer.
483   memset(tmp_buffer.data(), 0, tmp_buffer.size());
484   memset(zero_buffer.data(), 0, tmp_buffer.size());
485   StatusWithSize sws = writer.GetFileName(
486       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
487   EXPECT_EQ(Status::NotFound(), sws.status());
488   ASSERT_EQ(0U, sws.size());
489   EXPECT_EQ(memcmp(zero_buffer.data(), tmp_buffer.data(), tmp_buffer.size()),
490             0);
491   // END of read back filename from the open writer.
492 
493   EXPECT_EQ(OkStatus(), writer.Close());
494 }
495 
TEST_F(BlobStoreTest,FileNameInvalidation)496 TEST_F(BlobStoreTest, FileNameInvalidation) {
497   InitSourceBufferToRandom(0x8675309);
498   WriteTestBlock();
499 
500   constexpr std::string_view kFileName("sliced_cheese.png");
501   std::array<std::byte, 64> tmp_buffer = {};
502   static_assert(kFileName.size() <= tmp_buffer.size());
503 
504   kvs::ChecksumCrc16 checksum;
505   constexpr size_t kBufferSize = 256;
506   BlobStoreBuffer<kBufferSize> blob(
507       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
508   EXPECT_EQ(OkStatus(), blob.Init());
509 
510   BlobStore::BlobWriterWithBuffer<kFileName.size()> writer(blob);
511 
512   EXPECT_EQ(OkStatus(), writer.Open());
513   EXPECT_EQ(OkStatus(), writer.SetFileName(kFileName));
514   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
515   EXPECT_EQ(OkStatus(), writer.Discard());
516   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
517 
518   // Read back filename from the open writer.
519   {
520     StatusWithSize sws = writer.GetFileName(
521         {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
522     EXPECT_EQ(Status::NotFound(), sws.status());
523     ASSERT_EQ(0u, sws.size());
524   }
525 
526   EXPECT_EQ(OkStatus(), writer.Close());
527 
528   // Check that the file name was discarded by Discard().
529   memset(tmp_buffer.data(), 0, tmp_buffer.size());
530   BlobStore::BlobReader reader(blob);
531   ASSERT_EQ(OkStatus(), reader.Open());
532   StatusWithSize sws = reader.GetFileName(
533       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
534   EXPECT_EQ(Status::NotFound(), sws.status());
535   ASSERT_EQ(0u, sws.size());
536 }
537 
TEST_F(BlobStoreTest,NoFileName)538 TEST_F(BlobStoreTest, NoFileName) {
539   InitSourceBufferToRandom(0x8675309);
540   WriteTestBlock();
541 
542   std::array<std::byte, 64> tmp_buffer = {};
543   kvs::ChecksumCrc16 checksum;
544   constexpr size_t kBufferSize = 256;
545   BlobStoreBuffer<kBufferSize> blob(
546       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
547   EXPECT_EQ(OkStatus(), blob.Init());
548 
549   // Ensure blobs with no file names work as expected.
550   BlobStore::BlobReader reader(blob);
551   ASSERT_EQ(OkStatus(), reader.Open());
552 
553   StatusWithSize sws = reader.GetFileName(
554       {reinterpret_cast<char*>(tmp_buffer.data()), tmp_buffer.size()});
555   EXPECT_EQ(Status::NotFound(), sws.status());
556   ASSERT_EQ(0u, sws.size());
557 }
558 
TEST_F(BlobStoreTest,V1MetadataBackwardsCompatible)559 TEST_F(BlobStoreTest, V1MetadataBackwardsCompatible) {
560   constexpr size_t kWriteSize = 25;
561   WriteTestBlock(kWriteSize);
562 
563   kvs::ChecksumCrc16 checksum;
564   constexpr size_t kBufferSize = 16;
565   BlobStoreBuffer<kBufferSize> blob(
566       kBlobTitle, partition_, &checksum, kvs::TestKvs(), kBufferSize);
567   EXPECT_EQ(OkStatus(), blob.Init());
568 
569   // Read the written data in the current format.
570   internal::BlobMetadataHeader current_metadata;
571   ASSERT_EQ(OkStatus(),
572             kvs::TestKvs().acquire()->Get(kBlobTitle, &current_metadata));
573 
574   // Re-save only the V1 metadata contents.
575   ASSERT_EQ(
576       OkStatus(),
577       kvs::TestKvs().acquire()->Put(kBlobTitle, current_metadata.v1_metadata));
578 
579   // Ensure the BlobStore's contents aren't invalid.
580   BlobStore::BlobReader reader(blob);
581   ASSERT_EQ(OkStatus(), reader.Open());
582   ASSERT_EQ(kWriteSize, reader.ConservativeReadLimit());
583   ASSERT_EQ(current_metadata.v1_metadata.data_size_bytes,
584             reader.ConservativeReadLimit());
585 }
586 
TEST_F(BlobStoreTest,Discard)587 TEST_F(BlobStoreTest, Discard) {
588   InitSourceBufferToRandom(0x8675309);
589   WriteTestBlock();
590   constexpr char blob_title[] = "TestBlobBlock";
591   std::array<std::byte, 64> tmp_buffer = {};
592 
593   kvs::ChecksumCrc16 checksum;
594 
595   // TODO(davidrogers): Do this test with flash/kvs in the different entry state
596   // combinations.
597 
598   constexpr size_t kBufferSize = 256;
599   BlobStoreBuffer<kBufferSize> blob(
600       blob_title, partition_, &checksum, kvs::TestKvs(), kBufferSize);
601   EXPECT_EQ(OkStatus(), blob.Init());
602 
603   EXPECT_TRUE(blob.HasData());
604 
605   BlobStore::BlobWriterWithBuffer writer(blob);
606 
607   EXPECT_EQ(OkStatus(), writer.Open());
608   EXPECT_EQ(OkStatus(), writer.Write(tmp_buffer));
609 
610   // Blob should NOT be valid to read, because the write data was only buffered,
611   // and has not been written to flash yet.
612   EXPECT_FALSE(blob.HasData());
613 
614   // The write does an implicit erase so there should be no key for this blob.
615   EXPECT_EQ(Status::NotFound(),
616             kvs::TestKvs().acquire()->Get(blob_title, tmp_buffer).status());
617   EXPECT_EQ(OkStatus(), writer.Close());
618 
619   EXPECT_TRUE(blob.HasData());
620 
621   EXPECT_EQ(OkStatus(),
622             kvs::TestKvs().acquire()->Get(blob_title, tmp_buffer).status());
623 
624   EXPECT_EQ(OkStatus(), writer.Open());
625   EXPECT_EQ(OkStatus(), writer.Discard());
626   EXPECT_EQ(OkStatus(), writer.Close());
627 
628   EXPECT_FALSE(blob.HasData());
629 
630   EXPECT_EQ(Status::NotFound(),
631             kvs::TestKvs().acquire()->Get(blob_title, tmp_buffer).status());
632 }
633 
TEST_F(BlobStoreTest,MultipleErase)634 TEST_F(BlobStoreTest, MultipleErase) {
635   constexpr size_t kBufferSize = 256;
636   BlobStoreBuffer<kBufferSize> blob(
637       "Blob_OK", partition_, nullptr, kvs::TestKvs(), kBufferSize);
638   EXPECT_EQ(OkStatus(), blob.Init());
639 
640   BlobStore::BlobWriterWithBuffer writer(blob);
641   EXPECT_EQ(OkStatus(), writer.Open());
642 
643   EXPECT_EQ(OkStatus(), writer.Erase());
644   EXPECT_EQ(OkStatus(), writer.Erase());
645   EXPECT_EQ(OkStatus(), writer.Erase());
646 }
647 
TEST_F(BlobStoreTest,OffsetRead)648 TEST_F(BlobStoreTest, OffsetRead) {
649   InitSourceBufferToRandom(0x11309);
650   WriteTestBlock();
651 
652   constexpr size_t kOffset = 10;
653   ASSERT_LT(kOffset, kBlobDataSize);
654 
655   kvs::ChecksumCrc16 checksum;
656 
657   char name[16] = "TestBlobBlock";
658   constexpr size_t kBufferSize = 16;
659   BlobStoreBuffer<kBufferSize> blob(
660       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
661   EXPECT_EQ(OkStatus(), blob.Init());
662   BlobStore::BlobReader reader(blob);
663   ASSERT_EQ(OkStatus(), reader.Open(kOffset));
664 
665   std::array<std::byte, kBlobDataSize - kOffset> read_buffer;
666   ByteSpan read_span = read_buffer;
667   ASSERT_EQ(read_span.size_bytes(), reader.ConservativeReadLimit());
668 
669   auto result = reader.Read(read_span);
670   ASSERT_EQ(result.status(), OkStatus());
671   EXPECT_EQ(OkStatus(), reader.Close());
672   VerifyFlash(read_buffer, kOffset);
673 }
674 
TEST_F(BlobStoreTest,SeekOffsetRead)675 TEST_F(BlobStoreTest, SeekOffsetRead) {
676   InitSourceBufferToRandom(0x11309);
677   WriteTestBlock();
678 
679   constexpr size_t kOffset = 10;
680   ASSERT_LT(kOffset, kBlobDataSize);
681 
682   kvs::ChecksumCrc16 checksum;
683 
684   char name[16] = "TestBlobBlock";
685   constexpr size_t kBufferSize = 16;
686   BlobStoreBuffer<kBufferSize> blob(
687       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
688   EXPECT_EQ(OkStatus(), blob.Init());
689   BlobStore::BlobReader reader(blob);
690   ASSERT_EQ(OkStatus(), reader.Open());
691   ASSERT_EQ(OkStatus(), reader.Seek(kOffset));
692 
693   std::array<std::byte, kBlobDataSize - kOffset> read_buffer;
694   ByteSpan read_span = read_buffer;
695   ASSERT_EQ(read_span.size_bytes(), reader.ConservativeReadLimit());
696 
697   auto result = reader.Read(read_span);
698   ASSERT_EQ(result.status(), OkStatus());
699   EXPECT_EQ(OkStatus(), reader.Close());
700   VerifyFlash(read_buffer, kOffset);
701 }
702 
TEST_F(BlobStoreTest,InvalidReadOffset)703 TEST_F(BlobStoreTest, InvalidReadOffset) {
704   InitSourceBufferToRandom(0x11309);
705   WriteTestBlock();
706 
707   constexpr size_t kOffset = kBlobDataSize;
708 
709   kvs::ChecksumCrc16 checksum;
710 
711   char name[16] = "TestBlobBlock";
712   constexpr size_t kBufferSize = 16;
713   BlobStoreBuffer<kBufferSize> blob(
714       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
715   EXPECT_EQ(OkStatus(), blob.Init());
716   BlobStore::BlobReader reader(blob);
717   ASSERT_EQ(Status::InvalidArgument(), reader.Open(kOffset));
718 }
719 
TEST_F(BlobStoreTest,ReadSeekClosedReader)720 TEST_F(BlobStoreTest, ReadSeekClosedReader) {
721   InitSourceBufferToRandom(0x11309);
722   WriteTestBlock();
723 
724   kvs::ChecksumCrc16 checksum;
725 
726   char name[16] = "TestBlobBlock";
727   constexpr size_t kBufferSize = 16;
728   BlobStoreBuffer<kBufferSize> blob(
729       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
730   EXPECT_EQ(OkStatus(), blob.Init());
731   BlobStore::BlobReader reader(blob);
732   ASSERT_EQ(OkStatus(), reader.Open());
733   ASSERT_EQ(OkStatus(), reader.Close());
734 
735   EXPECT_EQ(Status::FailedPrecondition(), reader.Seek(0));
736 
737   std::byte read_buffer[32];
738   EXPECT_EQ(Status::FailedPrecondition(), reader.Read(read_buffer).status());
739 }
740 
TEST_F(BlobStoreTest,InvalidSeekOffset)741 TEST_F(BlobStoreTest, InvalidSeekOffset) {
742   InitSourceBufferToRandom(0x11309);
743   WriteTestBlock();
744 
745   constexpr size_t kOffset = kBlobDataSize;
746 
747   kvs::ChecksumCrc16 checksum;
748 
749   char name[16] = "TestBlobBlock";
750   constexpr size_t kBufferSize = 16;
751   BlobStoreBuffer<kBufferSize> blob(
752       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
753   EXPECT_EQ(OkStatus(), blob.Init());
754   BlobStore::BlobReader reader(blob);
755   ASSERT_EQ(OkStatus(), reader.Open());
756   ASSERT_EQ(Status::OutOfRange(), reader.Seek(kOffset));
757 }
758 
759 // Write a block to blob and close with part of a write buffer with unflushed
760 // data.
TEST_F(BlobStoreTest,WriteBufferWithRemainderInBuffer)761 TEST_F(BlobStoreTest, WriteBufferWithRemainderInBuffer) {
762   InitSourceBufferToRandom(0x11309);
763 
764   kvs::ChecksumCrc16 checksum;
765   constexpr size_t kBufferSize = 256;
766   BlobStoreBuffer<kBufferSize> blob(
767       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
768   EXPECT_EQ(OkStatus(), blob.Init());
769 
770   const size_t write_size_bytes = kBlobDataSize - 10;
771   ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
772 
773   BlobStore::BlobWriterWithBuffer writer(blob);
774   EXPECT_EQ(OkStatus(), writer.Open());
775   ASSERT_EQ(OkStatus(), writer.Write(write_data));
776   EXPECT_EQ(OkStatus(), writer.Close());
777 
778   BlobStore::BlobReader reader(blob);
779   ASSERT_EQ(OkStatus(), reader.Open());
780   EXPECT_EQ(write_size_bytes, reader.ConservativeReadLimit());
781 }
782 
783 // Test reading with a read buffer larger than the available data in the blob.
TEST_F(BlobStoreTest,ReadBufferIsLargerThanData)784 TEST_F(BlobStoreTest, ReadBufferIsLargerThanData) {
785   InitSourceBufferToRandom(0x57326);
786 
787   constexpr size_t kWriteBytes = 64;
788   WriteTestBlock(kWriteBytes);
789 
790   kvs::ChecksumCrc16 checksum;
791 
792   char name[16] = "TestBlobBlock";
793   constexpr size_t kBufferSize = 16;
794   BlobStoreBuffer<kBufferSize> blob(
795       name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
796   EXPECT_EQ(OkStatus(), blob.Init());
797   BlobStore::BlobReader reader(blob);
798   ASSERT_EQ(OkStatus(), reader.Open());
799   EXPECT_EQ(kWriteBytes, reader.ConservativeReadLimit());
800 
801   std::array<std::byte, kWriteBytes + 10> read_buffer;
802   ByteSpan read_span = read_buffer;
803 
804   auto result = reader.Read(read_span);
805   ASSERT_EQ(result.status(), OkStatus());
806   EXPECT_EQ(OkStatus(), reader.Close());
807 }
808 
TEST_F(BlobStoreTest,ChunkRead1)809 TEST_F(BlobStoreTest, ChunkRead1) {
810   InitSourceBufferToRandom(0x8675309);
811   WriteTestBlock();
812   ChunkReadTest(1);
813 }
814 
TEST_F(BlobStoreTest,ChunkRead3)815 TEST_F(BlobStoreTest, ChunkRead3) {
816   InitSourceBufferToFill(0);
817   WriteTestBlock();
818   ChunkReadTest(3);
819 }
820 
TEST_F(BlobStoreTest,ChunkRead4)821 TEST_F(BlobStoreTest, ChunkRead4) {
822   InitSourceBufferToFill(1);
823   WriteTestBlock();
824   ChunkReadTest(4);
825 }
826 
TEST_F(BlobStoreTest,ChunkRead5)827 TEST_F(BlobStoreTest, ChunkRead5) {
828   InitSourceBufferToFill(0xff);
829   WriteTestBlock();
830   ChunkReadTest(5);
831 }
832 
TEST_F(BlobStoreTest,ChunkRead16)833 TEST_F(BlobStoreTest, ChunkRead16) {
834   InitSourceBufferToRandom(0x86);
835   WriteTestBlock();
836   ChunkReadTest(16);
837 }
838 
TEST_F(BlobStoreTest,ChunkRead64)839 TEST_F(BlobStoreTest, ChunkRead64) {
840   InitSourceBufferToRandom(0x9);
841   WriteTestBlock();
842   ChunkReadTest(64);
843 }
844 
TEST_F(BlobStoreTest,ChunkReadFull)845 TEST_F(BlobStoreTest, ChunkReadFull) {
846   InitSourceBufferToRandom(0x9);
847   WriteTestBlock();
848   ChunkReadTest(kBlobDataSize);
849 }
850 
TEST_F(BlobStoreTest,PartialBufferThenClose)851 TEST_F(BlobStoreTest, PartialBufferThenClose) {
852   // Do write of only a partial chunk, which will only have bytes in buffer
853   // (none written to flash) at close.
854   size_t data_bytes = 12;
855   InitSourceBufferToRandom(0x111, data_bytes);
856   WriteTestBlock(data_bytes);
857 
858   // Do write with several full chunks and then some partial.
859   data_bytes = 158;
860   InitSourceBufferToRandom(0x3222, data_bytes);
861 }
862 
863 // Test to do write/close, write/close multiple times.
TEST_F(BlobStoreTest,MultipleWrites)864 TEST_F(BlobStoreTest, MultipleWrites) {
865   InitSourceBufferToRandom(0x1121);
866   WriteTestBlock();
867   InitSourceBufferToRandom(0x515);
868   WriteTestBlock();
869   InitSourceBufferToRandom(0x4321);
870   WriteTestBlock();
871 }
872 
TEST_F(BlobStoreTest,ResumeEmptyAbandonBlob)873 TEST_F(BlobStoreTest, ResumeEmptyAbandonBlob) {
874   InitSourceBufferToRandom(0x11309);
875 
876   kvs::ChecksumCrc16 checksum;
877   constexpr size_t kBufferSize = 256;
878   BlobStoreBuffer<kBufferSize> blob(
879       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
880   EXPECT_EQ(OkStatus(), blob.Init());
881 
882   BlobStore::BlobWriterWithBuffer writer(blob);
883   BlobStore::BlobWriterWithBuffer second_writer(blob);
884   BlobStore::BlobReader reader(blob);
885 
886   EXPECT_EQ(OkStatus(), writer.Open());
887 
888   EXPECT_EQ(OkStatus(), writer.Erase());
889   EXPECT_EQ(OkStatus(), writer.Abandon());
890   EXPECT_FALSE(blob.HasData());
891 
892   StatusWithSize resume_sws = writer.Resume();
893   EXPECT_EQ(OkStatus(), resume_sws.status());
894   EXPECT_EQ(0U, resume_sws.size());
895 
896   EXPECT_EQ(Status::Unavailable(), reader.Open());
897   EXPECT_EQ(Status::Unavailable(), second_writer.Open());
898   EXPECT_EQ(Status::Unavailable(), second_writer.Resume().status());
899 
900   EXPECT_EQ(OkStatus(), writer.Abandon());
901 }
902 
TEST_F(BlobStoreTest,ResumePartialAbandonBlob)903 TEST_F(BlobStoreTest, ResumePartialAbandonBlob) {
904   InitSourceBufferToRandom(0x11309);
905 
906   kvs::ChecksumCrc16 checksum;
907   constexpr size_t kBufferSize = 16;
908   BlobStoreBuffer<kBufferSize> blob(
909       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
910   EXPECT_EQ(OkStatus(), blob.Init());
911 
912   // The test fills 1/2 sector size less than the full blob size. Resume will
913   // backup 1.5 sectors for the resume point. Make sure the blob has enough
914   // sectors to do all that.
915   ASSERT_GE(kSectorCount, 3U);
916 
917   const size_t write_size_bytes = kBlobDataSize - (kSectorSize / 2);
918   ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
919 
920   BlobStore::BlobWriterWithBuffer writer(blob);
921   EXPECT_EQ(OkStatus(), writer.Open());
922   EXPECT_EQ(OkStatus(), writer.Write(write_data));
923   EXPECT_EQ(OkStatus(), writer.Abandon());
924   EXPECT_FALSE(blob.HasData());
925 
926   StatusWithSize resume_sws = writer.Resume();
927   EXPECT_EQ(OkStatus(), resume_sws.status());
928   const size_t expected_resume_size = (kSectorCount - 2) * kSectorSize;
929   EXPECT_EQ(expected_resume_size, resume_sws.size());
930   StatusWithSize eod_sws = partition_.EndOfWrittenData();
931   EXPECT_EQ(OkStatus(), eod_sws.status());
932   EXPECT_EQ(eod_sws.size(), resume_sws.size());
933   VerifyFlash(flash_.buffer().first(resume_sws.size()));
934 
935   EXPECT_EQ(OkStatus(),
936             writer.Write(span(source_buffer_).subspan(resume_sws.size())));
937   EXPECT_EQ(kBlobDataSize, writer.CurrentSizeBytes());
938   EXPECT_EQ(OkStatus(), writer.Close());
939 
940   VerifyBlob(blob, kBlobDataSize);
941 }
942 
TEST_F(BlobStoreTest,ResumePartialAfterRebootBlob)943 TEST_F(BlobStoreTest, ResumePartialAfterRebootBlob) {
944   InitSourceBufferToRandom(0x11309);
945 
946   kvs::ChecksumCrc16 first_checksum;
947   constexpr size_t kBufferSize = 16;
948   BlobStoreBuffer<kBufferSize> first_blob("TestBlobBlock",
949                                           partition_,
950                                           &first_checksum,
951                                           kvs::TestKvs(),
952                                           kBufferSize);
953   EXPECT_EQ(OkStatus(), first_blob.Init());
954 
955   // The test fills 1/2 sector size less than the full blob size. Resume will
956   // backup 1.5 sectors for the resume point. Make sure the blob has enough
957   // sectors to do all that.
958   ASSERT_GE(kSectorCount, 3U);
959 
960   const size_t write_size_bytes = kBlobDataSize - (kSectorSize / 2);
961   ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
962 
963   // Write the data using
964   BlobStore::BlobWriterWithBuffer writer(first_blob);
965   EXPECT_EQ(OkStatus(), writer.Open());
966   EXPECT_EQ(OkStatus(), writer.Write(write_data));
967 
968   // Use a new blob and writer for the resume to simulate having
969   // crashed/rebooted and starting fresh.
970 
971   kvs::ChecksumCrc16 second_checksum;
972   BlobStoreBuffer<kBufferSize> second_blob("TestBlobBlock",
973                                            partition_,
974                                            &second_checksum,
975                                            kvs::TestKvs(),
976                                            kBufferSize);
977   EXPECT_EQ(OkStatus(), second_blob.Init());
978   BlobStore::BlobWriterWithBuffer second_writer(second_blob);
979 
980   StatusWithSize resume_sws = second_writer.Resume();
981   EXPECT_EQ(OkStatus(), resume_sws.status());
982   const size_t expected_resume_size = (kSectorCount - 2) * kSectorSize;
983   EXPECT_EQ(expected_resume_size, resume_sws.size());
984   StatusWithSize eod_sws = partition_.EndOfWrittenData();
985   EXPECT_EQ(OkStatus(), eod_sws.status());
986   EXPECT_EQ(eod_sws.size(), resume_sws.size());
987   VerifyFlash(flash_.buffer().first(resume_sws.size()));
988 
989   EXPECT_EQ(
990       OkStatus(),
991       second_writer.Write(span(source_buffer_).subspan(resume_sws.size())));
992   EXPECT_EQ(kBlobDataSize, second_writer.CurrentSizeBytes());
993   EXPECT_EQ(OkStatus(), second_writer.Close());
994 
995   VerifyBlob(second_blob, kBlobDataSize);
996 }
997 
TEST_F(BlobStoreTest,ResumeAbandonBlobBackedUpToZero)998 TEST_F(BlobStoreTest, ResumeAbandonBlobBackedUpToZero) {
999   InitSourceBufferToRandom(0x11309);
1000 
1001   kvs::ChecksumCrc16 checksum;
1002   constexpr size_t kBufferSize = 256;
1003   BlobStoreBuffer<kBufferSize> blob(
1004       "TestBlobBlock", partition_, &checksum, kvs::TestKvs(), kBufferSize);
1005   EXPECT_EQ(OkStatus(), blob.Init());
1006 
1007   // The test fills 1/2 sector size less than the full blob size. Resume will
1008   // backup 1.5 sectors for the resume point. Make sure the blob has enough
1009   // sectors to do all that.
1010   ASSERT_GE(kSectorCount, 3U);
1011 
1012   const size_t write_size_bytes = kSectorSize + (kSectorSize / 2);
1013   ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
1014 
1015   // Write 1.5 sectors, should resume with zero bytes.
1016   BlobStore::BlobWriterWithBuffer writer(blob);
1017   EXPECT_EQ(OkStatus(), writer.Open());
1018   EXPECT_EQ(OkStatus(), writer.Write(write_data));
1019   EXPECT_EQ(OkStatus(), writer.Abandon());
1020   StatusWithSize resume_sws = writer.Resume();
1021   EXPECT_EQ(OkStatus(), resume_sws.status());
1022   EXPECT_EQ(0U, resume_sws.size());
1023 
1024   // Write 0.5 sectors, should resume with zero bytes.
1025   EXPECT_EQ(OkStatus(), writer.Erase());
1026   EXPECT_EQ(OkStatus(), writer.Write(write_data.first(kSectorSize / 2)));
1027   EXPECT_EQ(OkStatus(), writer.Abandon());
1028   resume_sws = writer.Resume();
1029   EXPECT_EQ(OkStatus(), resume_sws.status());
1030   EXPECT_EQ(0U, resume_sws.size());
1031 }
1032 }  // namespace
1033 }  // namespace pw::blob_store
1034