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