xref: /aosp_15_r20/external/pigweed/pw_blob_store/blob_store_deferred_write_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 <array>
16 #include <cstddef>
17 #include <cstring>
18 
19 #include "pw_blob_store/blob_store.h"
20 #include "pw_kvs/crc16_checksum.h"
21 #include "pw_kvs/fake_flash_memory.h"
22 #include "pw_kvs/flash_memory.h"
23 #include "pw_kvs/test_key_value_store.h"
24 #include "pw_log/log.h"
25 #include "pw_random/xor_shift.h"
26 #include "pw_span/span.h"
27 #include "pw_unit_test/framework.h"
28 
29 namespace pw::blob_store {
30 namespace {
31 
32 class DeferredWriteTest : public ::testing::Test {
33  protected:
DeferredWriteTest()34   DeferredWriteTest() : flash_(kFlashAlignment), partition_(&flash_) {}
35 
InitFlashToErased()36   void InitFlashToErased() { ASSERT_EQ(OkStatus(), partition_.Erase()); }
37 
InitFlashToRandom(uint64_t seed)38   void InitFlashToRandom(uint64_t seed) {
39     random::XorShiftStarRng64 rng(seed);
40     rng.Get(flash_.buffer());
41   }
42 
InitBufferToRandom(uint64_t seed)43   void InitBufferToRandom(uint64_t seed) {
44     random::XorShiftStarRng64 rng(seed);
45     rng.Get(buffer_);
46   }
47 
InitBufferToFill(char fill)48   void InitBufferToFill(char fill) {
49     ASSERT_EQ(OkStatus(), partition_.Erase());
50     std::memset(buffer_.data(), fill, buffer_.size());
51   }
52 
53   // Fill the source buffer with random pattern based on given seed, written to
54   // BlobStore in specified chunk size.
ChunkWriteTest(size_t chunk_size,size_t flush_interval,bool explicit_discard=false,bool explicit_erase=false)55   void ChunkWriteTest(size_t chunk_size,
56                       size_t flush_interval,
57                       bool explicit_discard = false,
58                       bool explicit_erase = false) {
59     constexpr size_t kWriteSize = 64;
60     kvs::ChecksumCrc16 checksum;
61 
62     size_t bytes_since_flush = 0;
63 
64     char name[16] = {};
65     snprintf(name, sizeof(name), "Blob%u", static_cast<unsigned>(chunk_size));
66 
67     BlobStoreBuffer<kBufferSize> blob(
68         name, partition_, &checksum, kvs::TestKvs(), kWriteSize);
69     EXPECT_EQ(OkStatus(), blob.Init());
70 
71     BlobStore::DeferredWriterWithBuffer writer(blob);
72     EXPECT_EQ(OkStatus(), writer.Open());
73 
74     if (explicit_discard) {
75       EXPECT_EQ(OkStatus(), writer.Discard());
76     }
77 
78     if (explicit_erase) {
79       EXPECT_EQ(OkStatus(), writer.Erase());
80     }
81 
82     ByteSpan source = buffer_;
83     while (source.size_bytes() > 0) {
84       const size_t write_size = std::min(source.size_bytes(), chunk_size);
85 
86       PW_LOG_DEBUG("Do write of %u bytes, %u bytes remain",
87                    static_cast<unsigned>(write_size),
88                    static_cast<unsigned>(source.size_bytes()));
89 
90       ASSERT_EQ(OkStatus(), writer.Write(source.first(write_size)));
91       // TODO(davidrogers): Add check that the write did not go to flash yet.
92 
93       source = source.subspan(write_size);
94       bytes_since_flush += write_size;
95 
96       if (bytes_since_flush >= flush_interval) {
97         bytes_since_flush = 0;
98         ASSERT_EQ(OkStatus(), writer.Flush());
99       }
100     }
101 
102     EXPECT_EQ(OkStatus(), writer.Close());
103 
104     // Use reader to check for valid data.
105     BlobStore::BlobReader reader(blob);
106     ASSERT_EQ(OkStatus(), reader.Open());
107     Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
108     ASSERT_TRUE(result.ok());
109     VerifyFlash(result.value());
110     EXPECT_EQ(OkStatus(), reader.Close());
111   }
112 
VerifyFlash(ConstByteSpan verify_bytes)113   void VerifyFlash(ConstByteSpan verify_bytes) {
114     // Should be defined as same size.
115     EXPECT_EQ(buffer_.size(), flash_.buffer().size_bytes());
116 
117     // Can't allow it to march off the end of buffer_.
118     ASSERT_LE(verify_bytes.size_bytes(), buffer_.size());
119 
120     for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
121       EXPECT_EQ(buffer_[i], verify_bytes[i]);
122     }
123   }
124 
125   static constexpr size_t kFlashAlignment = 16;
126   static constexpr size_t kSectorSize = 1024;
127   static constexpr size_t kSectorCount = 4;
128   static constexpr size_t kBufferSize = 2 * kSectorSize;
129 
130   kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
131   kvs::FlashPartition partition_;
132   std::array<std::byte, kSectorCount * kSectorSize> buffer_;
133 };
134 
TEST_F(DeferredWriteTest,ChunkWrite1)135 TEST_F(DeferredWriteTest, ChunkWrite1) {
136   InitFlashToErased();
137   InitBufferToRandom(0x8675309);
138   ChunkWriteTest(1, 16);
139 }
140 
TEST_F(DeferredWriteTest,ChunkWrite2)141 TEST_F(DeferredWriteTest, ChunkWrite2) {
142   InitFlashToRandom(0x2283);
143   InitBufferToRandom(0x8675);
144   ChunkWriteTest(2, 16);
145 }
146 
TEST_F(DeferredWriteTest,ChunkWrite3)147 TEST_F(DeferredWriteTest, ChunkWrite3) {
148   InitFlashToErased();
149   InitBufferToFill(0);
150   ChunkWriteTest(3, 16);
151 }
152 
TEST_F(DeferredWriteTest,ChunkWrite4)153 TEST_F(DeferredWriteTest, ChunkWrite4) {
154   InitFlashToErased();
155   InitBufferToFill(1);
156   ChunkWriteTest(4, 64);
157 }
158 
TEST_F(DeferredWriteTest,ChunkWrite5)159 TEST_F(DeferredWriteTest, ChunkWrite5) {
160   InitFlashToErased();
161   InitBufferToFill(0xff);
162   ChunkWriteTest(5, 64);
163 }
164 
TEST_F(DeferredWriteTest,ChunkWrite16)165 TEST_F(DeferredWriteTest, ChunkWrite16) {
166   InitFlashToErased();
167   InitBufferToRandom(0x86);
168   ChunkWriteTest(16, 128);
169 }
170 
TEST_F(DeferredWriteTest,ChunkWrite64)171 TEST_F(DeferredWriteTest, ChunkWrite64) {
172   InitFlashToRandom(0x9223);
173   InitBufferToRandom(0x9);
174   ChunkWriteTest(64, 128);
175 }
176 
TEST_F(DeferredWriteTest,ChunkWrite64FullBufferFill)177 TEST_F(DeferredWriteTest, ChunkWrite64FullBufferFill) {
178   InitFlashToErased();
179   InitBufferToRandom(0x9);
180   ChunkWriteTest(64, kBufferSize);
181 }
182 
TEST_F(DeferredWriteTest,ChunkWrite256)183 TEST_F(DeferredWriteTest, ChunkWrite256) {
184   InitFlashToErased();
185   InitBufferToRandom(0x12345678);
186   ChunkWriteTest(256, 256);
187 }
188 
TEST_F(DeferredWriteTest,ChunkWriteDiscard16)189 TEST_F(DeferredWriteTest, ChunkWriteDiscard16) {
190   InitFlashToErased();
191   InitBufferToRandom(0x86);
192 
193   // Test with a discard of an invalid blob and erased flash.
194   ChunkWriteTest(16, 128, true);
195 
196   // Test with a discard of a valid blob.
197   ChunkWriteTest(16, 128, true);
198 
199   // Test with a discard of an current blob with corrupted date.
200   InitFlashToRandom(0x9223);
201   ChunkWriteTest(16, 128, true);
202 }
203 
TEST_F(DeferredWriteTest,ChunkWriteErase16)204 TEST_F(DeferredWriteTest, ChunkWriteErase16) {
205   InitFlashToErased();
206   InitBufferToRandom(0x1286);
207 
208   // Test with an erase of an invalid blob and erased flash.
209   ChunkWriteTest(16, 128, false, true);
210 
211   // Test with an erase of a valid blob.
212   ChunkWriteTest(16, 128, false, true);
213 
214   // Test with an erase of an current blob with corrupted date.
215   InitFlashToRandom(0x9223);
216   ChunkWriteTest(16, 128, false, true);
217 }
218 
219 }  // namespace
220 }  // namespace pw::blob_store
221