blob: 1fb046a97bf5803e5031f9394d39d234c3a1dc03 [file] [log] [blame]
Wyatt Hepler118fc3c2020-02-21 14:23:19 -08001// 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
Wyatt Hepler8e94ed62020-03-05 16:45:32 -080015// Tests that directly work with the KVS's binary format and flash layer.
16
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080017#include <string_view>
18
19#include "gtest/gtest.h"
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -070020#include "pw_bytes/array.h"
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080021#include "pw_kvs/crc16_checksum.h"
David Rogersd64cc012020-05-26 12:37:37 -070022#include "pw_kvs/fake_flash_memory.h"
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080023#include "pw_kvs/format.h"
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080024#include "pw_kvs/internal/hash.h"
25#include "pw_kvs/key_value_store.h"
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080026
27namespace pw::kvs {
28namespace {
29
30using std::byte;
31using std::string_view;
32
33constexpr size_t kMaxEntries = 256;
34constexpr size_t kMaxUsableSectors = 256;
35
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070036constexpr uint32_t SimpleChecksum(std::span<const byte> data, uint32_t state) {
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080037 for (byte b : data) {
38 state += uint32_t(b);
39 }
40 return state;
41}
42
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080043template <typename State>
44class ChecksumFunction final : public ChecksumAlgorithm {
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080045 public:
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070046 ChecksumFunction(State (&algorithm)(std::span<const byte>, State))
47 : ChecksumAlgorithm(std::as_bytes(std::span(&state_, 1))),
48 algorithm_(algorithm) {}
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080049
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080050 void Reset() override { state_ = {}; }
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080051
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070052 void Update(std::span<const byte> data) override {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080053 state_ = algorithm_(data, state_);
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080054 }
55
56 private:
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080057 State state_;
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070058 State (&algorithm_)(std::span<const byte>, State);
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080059};
60
Armando Montanez888370d2020-05-01 18:29:22 -070061ChecksumFunction<uint32_t> default_checksum(SimpleChecksum);
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080062
63// Returns a buffer containing the necessary padding for an entry.
Armando Montanez344814b2020-04-24 15:05:42 -070064template <size_t kAlignmentBytes, size_t kKeyLength, size_t kValueSize = 0>
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080065constexpr auto EntryPadding() {
66 constexpr size_t content =
67 sizeof(internal::EntryHeader) + kKeyLength + kValueSize;
68 return std::array<byte, Padding(content, kAlignmentBytes)>{};
69}
70
71// Creates a buffer containing a valid entry at compile time.
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070072template <uint32_t (*kChecksum)(std::span<const byte>,
73 uint32_t) = &SimpleChecksum,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080074 size_t kAlignmentBytes = sizeof(internal::EntryHeader),
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080075 size_t kKeyLengthWithNull,
76 size_t kValueSize>
77constexpr auto MakeValidEntry(uint32_t magic,
78 uint32_t id,
79 const char (&key)[kKeyLengthWithNull],
80 const std::array<byte, kValueSize>& value) {
81 constexpr size_t kKeyLength = kKeyLengthWithNull - 1;
82
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -070083 auto data =
84 bytes::Concat(magic,
85 uint32_t(0),
86 uint8_t(kAlignmentBytes / 16 - 1),
87 uint8_t(kKeyLength),
88 uint16_t(kValueSize),
89 id,
90 bytes::String(key),
91 std::span(value),
92 EntryPadding<kAlignmentBytes, kKeyLength, kValueSize>());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080093
94 // Calculate the checksum
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080095 uint32_t checksum = kChecksum(data, 0);
Wyatt Hepler118fc3c2020-02-21 14:23:19 -080096 for (size_t i = 0; i < sizeof(checksum); ++i) {
97 data[4 + i] = byte(checksum & 0xff);
98 checksum >>= 8;
99 }
100
101 return data;
102}
103
Armando Montanez344814b2020-04-24 15:05:42 -0700104// Creates a buffer containing a deleted entry at compile time.
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700105template <uint32_t (*kChecksum)(std::span<const byte>,
106 uint32_t) = &SimpleChecksum,
Armando Montanez344814b2020-04-24 15:05:42 -0700107 size_t kAlignmentBytes = sizeof(internal::EntryHeader),
108 size_t kKeyLengthWithNull>
109constexpr auto MakeDeletedEntry(uint32_t magic,
110 uint32_t id,
111 const char (&key)[kKeyLengthWithNull]) {
112 constexpr size_t kKeyLength = kKeyLengthWithNull - 1;
113
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700114 auto data = bytes::Concat(magic,
115 uint32_t(0),
116 uint8_t(kAlignmentBytes / 16 - 1),
117 uint8_t(kKeyLength),
118 uint16_t(0xFFFF),
119 id,
120 bytes::String(key),
121 EntryPadding<kAlignmentBytes, kKeyLength>());
Armando Montanez344814b2020-04-24 15:05:42 -0700122
123 // Calculate the checksum
124 uint32_t checksum = kChecksum(data, 0);
125 for (size_t i = 0; i < sizeof(checksum); ++i) {
126 data[4 + i] = byte(checksum & 0xff);
127 checksum >>= 8;
128 }
129
130 return data;
131}
132
David Rogers436b3aa2020-08-03 08:44:10 -0700133// For KVS magic value always use a random 32 bit integer rather than a
134// human readable 4 bytes. See pw_kvs/format.h for more information.
135constexpr uint32_t kMagic = 0x5ab2f0b5;
David Rogers9abe3c72020-03-24 19:03:13 -0700136
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800137constexpr Options kNoGcOptions{
David Rogers890acb52020-02-28 09:06:50 -0800138 .gc_on_write = GargbageCollectOnWrite::kDisabled,
David Rogers9abe3c72020-03-24 19:03:13 -0700139 .recovery = ErrorRecovery::kManual,
140 .verify_on_read = true,
141 .verify_on_write = true,
142};
143
144constexpr Options kRecoveryNoGcOptions{
145 .gc_on_write = GargbageCollectOnWrite::kDisabled,
146 .recovery = ErrorRecovery::kLazy,
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800147 .verify_on_read = true,
148 .verify_on_write = true,
149};
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800150
David Rogersfcea3252020-04-07 14:56:35 -0700151constexpr Options kRecoveryLazyGcOptions{
152 .gc_on_write = GargbageCollectOnWrite::kOneSector,
153 .recovery = ErrorRecovery::kLazy,
154 .verify_on_read = true,
155 .verify_on_write = true,
156};
157
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700158constexpr auto kEntry1 =
159 MakeValidEntry(kMagic, 1, "key1", bytes::String("value1"));
160constexpr auto kEntry2 =
161 MakeValidEntry(kMagic, 3, "k2", bytes::String("value2"));
162constexpr auto kEntry3 =
163 MakeValidEntry(kMagic, 4, "k3y", bytes::String("value3"));
164constexpr auto kEntry4 =
165 MakeValidEntry(kMagic, 5, "4k", bytes::String("value4"));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800166
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700167constexpr auto kEmpty32Bytes = bytes::Initialized<32>(0xff);
David Rogers39d2a032020-04-10 15:00:35 -0700168static_assert(sizeof(kEmpty32Bytes) == 32);
169
David Rogers178002a2020-06-16 13:52:54 -0700170EntryFormat default_format = {.magic = kMagic, .checksum = &default_checksum};
171
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800172class KvsErrorHandling : public ::testing::Test {
173 protected:
174 KvsErrorHandling()
175 : flash_(internal::Entry::kMinAlignmentBytes),
176 partition_(&flash_),
David Rogers178002a2020-06-16 13:52:54 -0700177 kvs_(&partition_, default_format, kNoGcOptions) {}
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800178
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700179 void InitFlashTo(std::span<const byte> contents) {
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800180 partition_.Erase();
181 std::memcpy(flash_.buffer().data(), contents.data(), contents.size());
182 }
183
David Rogersd64cc012020-05-26 12:37:37 -0700184 FakeFlashMemoryBuffer<512, 4> flash_;
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800185 FlashPartition partition_;
186 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_;
187};
188
189TEST_F(KvsErrorHandling, Init_Ok) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700190 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800191
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800192 EXPECT_EQ(OkStatus(), kvs_.Init());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800193 byte buffer[64];
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800194 EXPECT_EQ(OkStatus(), kvs_.Get("key1", buffer).status());
195 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800196}
197
198TEST_F(KvsErrorHandling, Init_DuplicateEntries_ReturnsDataLossButReadsEntry) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700199 InitFlashTo(bytes::Concat(kEntry1, kEntry1));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800200
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700201 EXPECT_EQ(Status::DataLoss(), kvs_.Init());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800202 byte buffer[64];
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800203 EXPECT_EQ(OkStatus(), kvs_.Get("key1", buffer).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700204 EXPECT_EQ(Status::NotFound(), kvs_.Get("k2", buffer).status());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800205}
206
207TEST_F(KvsErrorHandling, Init_CorruptEntry_FindsSubsequentValidEntry) {
208 // Corrupt each byte in the first entry once.
209 for (size_t i = 0; i < kEntry1.size(); ++i) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700210 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800211 flash_.buffer()[i] = byte(int(flash_.buffer()[i]) + 1);
212
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700213 ASSERT_EQ(Status::DataLoss(), kvs_.Init());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800214 byte buffer[64];
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700215 ASSERT_EQ(Status::NotFound(), kvs_.Get("key1", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800216 ASSERT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800217
218 auto stats = kvs_.GetStorageStats();
219 // One valid entry.
220 ASSERT_EQ(32u, stats.in_use_bytes);
221 // Rest of space is reclaimable as the sector is corrupt.
222 ASSERT_EQ(480u, stats.reclaimable_bytes);
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800223 }
224}
225
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800226TEST_F(KvsErrorHandling, Init_CorruptEntry_CorrectlyAccountsForSectorSize) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700227 InitFlashTo(bytes::Concat(kEntry1, kEntry2, kEntry3, kEntry4));
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800228
229 // Corrupt the first and third entries.
230 flash_.buffer()[9] = byte(0xef);
231 flash_.buffer()[67] = byte(0xef);
232
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700233 ASSERT_EQ(Status::DataLoss(), kvs_.Init());
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800234
235 EXPECT_EQ(2u, kvs_.size());
236
237 byte buffer[64];
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700238 EXPECT_EQ(Status::NotFound(), kvs_.Get("key1", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800239 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700240 EXPECT_EQ(Status::NotFound(), kvs_.Get("k3y", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800241 EXPECT_EQ(OkStatus(), kvs_.Get("4k", buffer).status());
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800242
243 auto stats = kvs_.GetStorageStats();
244 ASSERT_EQ(64u, stats.in_use_bytes);
245 ASSERT_EQ(448u, stats.reclaimable_bytes);
246 ASSERT_EQ(1024u, stats.writable_bytes);
247}
248
David Rogersfcea3252020-04-07 14:56:35 -0700249TEST_F(KvsErrorHandling, Init_ReadError_InitializedWithSingleEntryError) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700250 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800251
252 flash_.InjectReadError(
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700253 FlashError::InRange(Status::Unauthenticated(), kEntry1.size()));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800254
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700255 EXPECT_EQ(Status::DataLoss(), kvs_.Init());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800256 EXPECT_FALSE(kvs_.initialized());
257}
258
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800259TEST_F(KvsErrorHandling, Init_CorruptSectors_ShouldBeUnwritable) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700260 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800261
262 // Corrupt 3 of the 4 512-byte flash sectors. Corrupt sectors should be
263 // unwritable, and the KVS must maintain one empty sector at all times.
264 // As GC on write is disabled through KVS options, writes should no longer be
265 // possible due to lack of space.
266 flash_.buffer()[1] = byte(0xef);
267 flash_.buffer()[513] = byte(0xef);
268 flash_.buffer()[1025] = byte(0xef);
269
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700270 ASSERT_EQ(Status::DataLoss(), kvs_.Init());
271 EXPECT_EQ(Status::FailedPrecondition(),
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700272 kvs_.Put("hello", bytes::String("world")));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700273 EXPECT_EQ(Status::FailedPrecondition(), kvs_.Put("a", bytes::String("b")));
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800274
275 // Existing valid entries should still be readable.
276 EXPECT_EQ(1u, kvs_.size());
277 byte buffer[64];
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700278 EXPECT_EQ(Status::NotFound(), kvs_.Get("key1", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800279 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800280
281 auto stats = kvs_.GetStorageStats();
282 EXPECT_EQ(32u, stats.in_use_bytes);
283 EXPECT_EQ(480u + 2 * 512u, stats.reclaimable_bytes);
284 EXPECT_EQ(0u, stats.writable_bytes);
285}
286
David Rogers91627482020-02-27 17:38:12 -0800287TEST_F(KvsErrorHandling, Init_CorruptSectors_ShouldRecoverOne) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700288 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
David Rogers91627482020-02-27 17:38:12 -0800289
290 // Corrupt all of the 4 512-byte flash sectors. Leave the pre-init entries
David Rogers9abe3c72020-03-24 19:03:13 -0700291 // intact. The KVS should be unavailable because recovery is set to full
292 // manual, and it does not have the required one empty sector at all times.
David Rogers91627482020-02-27 17:38:12 -0800293 flash_.buffer()[64] = byte(0xef);
294 flash_.buffer()[513] = byte(0xef);
295 flash_.buffer()[1025] = byte(0xef);
296 flash_.buffer()[1537] = byte(0xef);
297
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700298 ASSERT_EQ(Status::DataLoss(), kvs_.Init());
David Rogers91627482020-02-27 17:38:12 -0800299
300 auto stats = kvs_.GetStorageStats();
301 EXPECT_EQ(64u, stats.in_use_bytes);
David Rogers9abe3c72020-03-24 19:03:13 -0700302 EXPECT_EQ(4 * 512u - 64u, stats.reclaimable_bytes);
David Rogers91627482020-02-27 17:38:12 -0800303 EXPECT_EQ(0u, stats.writable_bytes);
304}
305
David Rogers39d2a032020-04-10 15:00:35 -0700306// Currently disabled due to KVS failing the test. KVS fails due to Init bailing
307// out when it sees a small patch of "erased" looking flash space, which could
308// result in missing keys that are actually written after a write error in
309// flash.
310TEST_F(KvsErrorHandling, DISABLED_Init_OkWithWriteErrorOnFlash) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700311 InitFlashTo(bytes::Concat(kEntry1, kEmpty32Bytes, kEntry2));
David Rogers39d2a032020-04-10 15:00:35 -0700312
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700313 EXPECT_EQ(Status::DataLoss(), kvs_.Init());
David Rogers39d2a032020-04-10 15:00:35 -0700314 byte buffer[64];
315 EXPECT_EQ(2u, kvs_.size());
316 EXPECT_EQ(true, kvs_.error_detected());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800317 EXPECT_EQ(OkStatus(), kvs_.Get("key1", buffer).status());
318 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
David Rogers39d2a032020-04-10 15:00:35 -0700319
320 auto stats = kvs_.GetStorageStats();
321 EXPECT_EQ(64u, stats.in_use_bytes);
322 EXPECT_EQ(512u - 64u, stats.reclaimable_bytes);
323 EXPECT_EQ(2 * 512u, stats.writable_bytes);
324}
325
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800326TEST_F(KvsErrorHandling, Init_CorruptKey_RevertsToPreviousVersion) {
327 constexpr auto kVersion7 =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700328 MakeValidEntry(kMagic, 7, "my_key", bytes::String("version 7"));
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800329 constexpr auto kVersion8 =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700330 MakeValidEntry(kMagic, 8, "my_key", bytes::String("version 8"));
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800331
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700332 InitFlashTo(bytes::Concat(kVersion7, kVersion8));
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800333
334 // Corrupt a byte of entry version 8 (addresses 32-63).
335 flash_.buffer()[34] = byte(0xef);
336
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700337 ASSERT_EQ(Status::DataLoss(), kvs_.Init());
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800338
339 char buffer[64] = {};
340
341 EXPECT_EQ(1u, kvs_.size());
342
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700343 auto result = kvs_.Get("my_key", std::as_writable_bytes(std::span(buffer)));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800344 EXPECT_EQ(OkStatus(), result.status());
Alexei Frolovb43a5e62020-02-24 14:52:35 -0800345 EXPECT_EQ(sizeof("version 7") - 1, result.size());
346 EXPECT_STREQ("version 7", buffer);
347
348 EXPECT_EQ(32u, kvs_.GetStorageStats().in_use_bytes);
349}
350
David Rogers98fea472020-04-01 15:43:48 -0700351// The Put_WriteFailure_EntryNotAddedButBytesMarkedWritten test is run with both
352// the KvsErrorRecovery and KvsErrorHandling test fixtures (different KVS
353// configurations).
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800354TEST_F(KvsErrorHandling, Put_WriteFailure_EntryNotAddedButBytesMarkedWritten) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800355 ASSERT_EQ(OkStatus(), kvs_.Init());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700356 flash_.InjectWriteError(FlashError::Unconditional(Status::Unavailable(), 1));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800357
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700358 EXPECT_EQ(Status::Unavailable(), kvs_.Put("key1", bytes::String("value1")));
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800359
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700360 EXPECT_EQ(Status::NotFound(), kvs_.Get("key1", std::span<byte>()).status());
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800361 ASSERT_TRUE(kvs_.empty());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800362
363 auto stats = kvs_.GetStorageStats();
364 EXPECT_EQ(stats.in_use_bytes, 0u);
David Rogers9abe3c72020-03-24 19:03:13 -0700365 EXPECT_EQ(stats.reclaimable_bytes, 512u);
366 EXPECT_EQ(stats.writable_bytes, 512u * 2);
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800367
368 // The bytes were marked used, so a new key should not overlap with the bytes
369 // from the failed Put.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800370 EXPECT_EQ(OkStatus(), kvs_.Put("key1", bytes::String("value1")));
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800371
372 stats = kvs_.GetStorageStats();
David Rogersf3884eb2020-03-08 19:21:40 -0700373 EXPECT_EQ(stats.in_use_bytes, (32u * kvs_.redundancy()));
David Rogers9abe3c72020-03-24 19:03:13 -0700374 EXPECT_EQ(stats.reclaimable_bytes, 512u);
375 EXPECT_EQ(stats.writable_bytes, 512u * 2 - (32 * kvs_.redundancy()));
376}
377
378class KvsErrorRecovery : public ::testing::Test {
379 protected:
380 KvsErrorRecovery()
381 : flash_(internal::Entry::kMinAlignmentBytes),
382 partition_(&flash_),
383 kvs_(&partition_,
Armando Montanez888370d2020-05-01 18:29:22 -0700384 {.magic = kMagic, .checksum = &default_checksum},
David Rogers9abe3c72020-03-24 19:03:13 -0700385 kRecoveryNoGcOptions) {}
386
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700387 void InitFlashTo(std::span<const byte> contents) {
David Rogers9abe3c72020-03-24 19:03:13 -0700388 partition_.Erase();
389 std::memcpy(flash_.buffer().data(), contents.data(), contents.size());
390 }
391
David Rogersd64cc012020-05-26 12:37:37 -0700392 FakeFlashMemoryBuffer<512, 4> flash_;
David Rogers9abe3c72020-03-24 19:03:13 -0700393 FlashPartition partition_;
394 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_;
395};
396
397TEST_F(KvsErrorRecovery, Init_Ok) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700398 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
David Rogers9abe3c72020-03-24 19:03:13 -0700399
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800400 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogers9abe3c72020-03-24 19:03:13 -0700401 byte buffer[64];
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800402 EXPECT_EQ(OkStatus(), kvs_.Get("key1", buffer).status());
403 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
David Rogers9abe3c72020-03-24 19:03:13 -0700404}
405
406TEST_F(KvsErrorRecovery, Init_DuplicateEntries_RecoversDuringInit) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700407 InitFlashTo(bytes::Concat(kEntry1, kEntry1));
David Rogers9abe3c72020-03-24 19:03:13 -0700408
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800409 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogers9abe3c72020-03-24 19:03:13 -0700410 auto stats = kvs_.GetStorageStats();
411 EXPECT_EQ(stats.corrupt_sectors_recovered, 1u);
412
413 byte buffer[64];
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800414 EXPECT_EQ(OkStatus(), kvs_.Get("key1", buffer).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700415 EXPECT_EQ(Status::NotFound(), kvs_.Get("k2", buffer).status());
David Rogers9abe3c72020-03-24 19:03:13 -0700416}
417
418TEST_F(KvsErrorRecovery, Init_CorruptEntry_FindsSubsequentValidEntry) {
419 // Corrupt each byte in the first entry once.
420 for (size_t i = 0; i < kEntry1.size(); ++i) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700421 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
David Rogers9abe3c72020-03-24 19:03:13 -0700422 flash_.buffer()[i] = byte(int(flash_.buffer()[i]) + 1);
423
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800424 ASSERT_EQ(OkStatus(), kvs_.Init());
David Rogers9abe3c72020-03-24 19:03:13 -0700425 byte buffer[64];
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700426 ASSERT_EQ(Status::NotFound(), kvs_.Get("key1", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800427 ASSERT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
David Rogers9abe3c72020-03-24 19:03:13 -0700428
429 auto stats = kvs_.GetStorageStats();
430 // One valid entry.
431 ASSERT_EQ(32u, stats.in_use_bytes);
432 // The sector with corruption should have been recovered.
433 ASSERT_EQ(0u, stats.reclaimable_bytes);
David Rogers98fea472020-04-01 15:43:48 -0700434 ASSERT_EQ(i + 1u, stats.corrupt_sectors_recovered);
David Rogers9abe3c72020-03-24 19:03:13 -0700435 }
436}
437
438TEST_F(KvsErrorRecovery, Init_CorruptEntry_CorrectlyAccountsForSectorSize) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700439 InitFlashTo(bytes::Concat(kEntry1, kEntry2, kEntry3, kEntry4));
David Rogers9abe3c72020-03-24 19:03:13 -0700440
441 // Corrupt the first and third entries.
442 flash_.buffer()[9] = byte(0xef);
443 flash_.buffer()[67] = byte(0xef);
444
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800445 ASSERT_EQ(OkStatus(), kvs_.Init());
David Rogers9abe3c72020-03-24 19:03:13 -0700446
447 EXPECT_EQ(2u, kvs_.size());
448
449 byte buffer[64];
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700450 EXPECT_EQ(Status::NotFound(), kvs_.Get("key1", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800451 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700452 EXPECT_EQ(Status::NotFound(), kvs_.Get("k3y", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800453 EXPECT_EQ(OkStatus(), kvs_.Get("4k", buffer).status());
David Rogers9abe3c72020-03-24 19:03:13 -0700454
455 auto stats = kvs_.GetStorageStats();
456 ASSERT_EQ(64u, stats.in_use_bytes);
457 ASSERT_EQ(0u, stats.reclaimable_bytes);
458 ASSERT_EQ(1472u, stats.writable_bytes);
459 ASSERT_EQ(1u, stats.corrupt_sectors_recovered);
460}
461
David Rogersfcea3252020-04-07 14:56:35 -0700462TEST_F(KvsErrorRecovery, Init_ReadError_InitializedWithSingleEntryError) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700463 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
David Rogers9abe3c72020-03-24 19:03:13 -0700464
465 flash_.InjectReadError(
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700466 FlashError::InRange(Status::Unauthenticated(), kEntry1.size()));
David Rogers9abe3c72020-03-24 19:03:13 -0700467
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800468 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogersfcea3252020-04-07 14:56:35 -0700469 EXPECT_TRUE(kvs_.initialized());
470 auto stats = kvs_.GetStorageStats();
471 ASSERT_EQ(32u, stats.in_use_bytes);
472 ASSERT_EQ(0u, stats.reclaimable_bytes);
473 ASSERT_EQ(3 * 512u - 32u, stats.writable_bytes);
474 ASSERT_EQ(1u, stats.corrupt_sectors_recovered);
475 ASSERT_EQ(0u, stats.missing_redundant_entries_recovered);
David Rogers9abe3c72020-03-24 19:03:13 -0700476}
477
478TEST_F(KvsErrorRecovery, Init_CorruptSectors_ShouldBeUnwritable) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700479 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
David Rogers9abe3c72020-03-24 19:03:13 -0700480
481 // Corrupt 3 of the 4 512-byte flash sectors. Corrupt sectors should be
482 // recovered via garbage collection.
483 flash_.buffer()[1] = byte(0xef);
484 flash_.buffer()[513] = byte(0xef);
485 flash_.buffer()[1025] = byte(0xef);
486
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800487 ASSERT_EQ(OkStatus(), kvs_.Init());
488 EXPECT_EQ(OkStatus(), kvs_.Put("hello", bytes::String("world")));
489 EXPECT_EQ(OkStatus(), kvs_.Put("a", bytes::String("b")));
David Rogers9abe3c72020-03-24 19:03:13 -0700490
491 // Existing valid entries should still be readable.
492 EXPECT_EQ(3u, kvs_.size());
493 byte buffer[64];
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700494 EXPECT_EQ(Status::NotFound(), kvs_.Get("key1", buffer).status());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800495 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
David Rogers9abe3c72020-03-24 19:03:13 -0700496
497 auto stats = kvs_.GetStorageStats();
498 EXPECT_EQ(96u, stats.in_use_bytes);
499 EXPECT_EQ(0u, stats.reclaimable_bytes);
500 EXPECT_EQ(1440u, stats.writable_bytes);
David Rogersfcea3252020-04-07 14:56:35 -0700501 EXPECT_EQ(3u, stats.corrupt_sectors_recovered);
David Rogers9abe3c72020-03-24 19:03:13 -0700502}
503
504TEST_F(KvsErrorRecovery, Init_CorruptSectors_ShouldRecoverOne) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700505 InitFlashTo(bytes::Concat(kEntry1, kEntry2));
David Rogers9abe3c72020-03-24 19:03:13 -0700506
507 // Corrupt all of the 4 512-byte flash sectors. Leave the pre-init entries
508 // intact. As part of recovery all corrupt sectors should get garbage
509 // collected.
510 flash_.buffer()[64] = byte(0xef);
511 flash_.buffer()[513] = byte(0xef);
512 flash_.buffer()[1025] = byte(0xef);
513 flash_.buffer()[1537] = byte(0xef);
514
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800515 ASSERT_EQ(OkStatus(), kvs_.Init());
David Rogers9abe3c72020-03-24 19:03:13 -0700516
517 auto stats = kvs_.GetStorageStats();
518 EXPECT_EQ(64u, stats.in_use_bytes);
519 EXPECT_EQ(0u, stats.reclaimable_bytes);
520 EXPECT_EQ(3 * 512u - 64u, stats.writable_bytes);
David Rogersfcea3252020-04-07 14:56:35 -0700521 EXPECT_EQ(4u, stats.corrupt_sectors_recovered);
David Rogers9abe3c72020-03-24 19:03:13 -0700522}
523
David Rogers39d2a032020-04-10 15:00:35 -0700524// Currently disabled due to KVS failing the test. KVS fails due to Init bailing
525// out when it sees a small patch of "erased" looking flash space, which could
526// result in missing keys that are actually written after a write error in
527// flash.
528TEST_F(KvsErrorRecovery, DISABLED_Init_OkWithWriteErrorOnFlash) {
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700529 InitFlashTo(bytes::Concat(kEntry1, kEmpty32Bytes, kEntry2));
David Rogers39d2a032020-04-10 15:00:35 -0700530
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800531 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogers39d2a032020-04-10 15:00:35 -0700532 byte buffer[64];
533 EXPECT_EQ(2u, kvs_.size());
534 EXPECT_EQ(false, kvs_.error_detected());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800535 EXPECT_EQ(OkStatus(), kvs_.Get("key1", buffer).status());
536 EXPECT_EQ(OkStatus(), kvs_.Get("k2", buffer).status());
David Rogers39d2a032020-04-10 15:00:35 -0700537
538 auto stats = kvs_.GetStorageStats();
539 EXPECT_EQ(64u, stats.in_use_bytes);
540 EXPECT_EQ(0u, stats.reclaimable_bytes);
541 EXPECT_EQ(3 * 512u - 64u, stats.writable_bytes);
542 EXPECT_EQ(1u, stats.corrupt_sectors_recovered);
543 EXPECT_EQ(0u, stats.missing_redundant_entries_recovered);
544}
545
David Rogers9abe3c72020-03-24 19:03:13 -0700546TEST_F(KvsErrorRecovery, Init_CorruptKey_RevertsToPreviousVersion) {
547 constexpr auto kVersion7 =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700548 MakeValidEntry(kMagic, 7, "my_key", bytes::String("version 7"));
David Rogers9abe3c72020-03-24 19:03:13 -0700549 constexpr auto kVersion8 =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700550 MakeValidEntry(kMagic, 8, "my_key", bytes::String("version 8"));
David Rogers9abe3c72020-03-24 19:03:13 -0700551
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700552 InitFlashTo(bytes::Concat(kVersion7, kVersion8));
David Rogers9abe3c72020-03-24 19:03:13 -0700553
554 // Corrupt a byte of entry version 8 (addresses 32-63).
555 flash_.buffer()[34] = byte(0xef);
556
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800557 ASSERT_EQ(OkStatus(), kvs_.Init());
David Rogers9abe3c72020-03-24 19:03:13 -0700558
559 char buffer[64] = {};
560
561 EXPECT_EQ(1u, kvs_.size());
562
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700563 auto result = kvs_.Get("my_key", std::as_writable_bytes(std::span(buffer)));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800564 EXPECT_EQ(OkStatus(), result.status());
David Rogers9abe3c72020-03-24 19:03:13 -0700565 EXPECT_EQ(sizeof("version 7") - 1, result.size());
566 EXPECT_STREQ("version 7", buffer);
567
568 EXPECT_EQ(32u, kvs_.GetStorageStats().in_use_bytes);
569}
570
David Rogers98fea472020-04-01 15:43:48 -0700571// The Put_WriteFailure_EntryNotAddedButBytesMarkedWritten test is run with both
572// the KvsErrorRecovery and KvsErrorHandling test fixtures (different KVS
573// configurations).
David Rogers9abe3c72020-03-24 19:03:13 -0700574TEST_F(KvsErrorRecovery, Put_WriteFailure_EntryNotAddedButBytesMarkedWritten) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800575 ASSERT_EQ(OkStatus(), kvs_.Init());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700576 flash_.InjectWriteError(FlashError::Unconditional(Status::Unavailable(), 1));
David Rogers9abe3c72020-03-24 19:03:13 -0700577
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700578 EXPECT_EQ(Status::Unavailable(), kvs_.Put("key1", bytes::String("value1")));
David Rogers9abe3c72020-03-24 19:03:13 -0700579 EXPECT_EQ(true, kvs_.error_detected());
580
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700581 EXPECT_EQ(Status::NotFound(), kvs_.Get("key1", std::span<byte>()).status());
David Rogers9abe3c72020-03-24 19:03:13 -0700582 ASSERT_TRUE(kvs_.empty());
583
584 auto stats = kvs_.GetStorageStats();
585 EXPECT_EQ(stats.in_use_bytes, 0u);
586 EXPECT_EQ(stats.reclaimable_bytes, 512u);
587 EXPECT_EQ(stats.writable_bytes, 512u * 2);
588 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
589 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
590
591 // The bytes were marked used, so a new key should not overlap with the bytes
592 // from the failed Put.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800593 EXPECT_EQ(OkStatus(), kvs_.Put("key1", bytes::String("value1")));
David Rogers9abe3c72020-03-24 19:03:13 -0700594
595 stats = kvs_.GetStorageStats();
596 EXPECT_EQ(stats.in_use_bytes, (32u * kvs_.redundancy()));
597 EXPECT_EQ(stats.reclaimable_bytes, 512u);
598 EXPECT_EQ(stats.writable_bytes, 512u * 2 - (32 * kvs_.redundancy()));
599 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
600 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
Wyatt Hepler118fc3c2020-02-21 14:23:19 -0800601}
602
David Rogers436b3aa2020-08-03 08:44:10 -0700603// For KVS magic value always use a random 32 bit integer rather than a
604// human readable 4 bytes. See pw_kvs/format.h for more information.
605constexpr uint32_t kAltMagic = 0x41a2db83;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800606
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700607constexpr uint32_t AltChecksum(std::span<const byte> data, uint32_t state) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800608 for (byte b : data) {
609 state = (state << 8) | uint32_t(byte(state >> 24) ^ b);
610 }
611 return state;
612}
613
614ChecksumFunction<uint32_t> alt_checksum(AltChecksum);
615
616constexpr auto kAltEntry =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700617 MakeValidEntry<AltChecksum>(kAltMagic, 32, "A Key", bytes::String("XD"));
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800618
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700619constexpr uint32_t NoChecksum(std::span<const byte>, uint32_t) { return 0; }
David Rogers436b3aa2020-08-03 08:44:10 -0700620// For KVS magic value always use a random 32 bit integer rather than a
621// human readable 4 bytes. See pw_kvs/format.h for more information.
622constexpr uint32_t kNoChecksumMagic = 0xd49ba138;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800623
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700624constexpr auto kNoChecksumEntry = MakeValidEntry<NoChecksum>(
625 kNoChecksumMagic, 64, "kee", bytes::String("O_o"));
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800626
Armando Montanez344814b2020-04-24 15:05:42 -0700627constexpr auto kDeletedEntry =
628 MakeDeletedEntry<AltChecksum>(kAltMagic, 128, "gone");
629
David Rogers31b358b2020-04-15 05:00:50 -0700630class InitializedRedundantMultiMagicKvs : public ::testing::Test {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800631 protected:
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700632 static constexpr auto kInitialContents = bytes::Concat(
Armando Montanez344814b2020-04-24 15:05:42 -0700633 kNoChecksumEntry, kEntry1, kAltEntry, kEntry2, kEntry3, kDeletedEntry);
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800634
David Rogers31b358b2020-04-15 05:00:50 -0700635 InitializedRedundantMultiMagicKvs()
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800636 : flash_(internal::Entry::kMinAlignmentBytes),
637 partition_(&flash_),
638 kvs_(&partition_,
639 {{
Armando Montanez888370d2020-05-01 18:29:22 -0700640 {.magic = kMagic, .checksum = &default_checksum},
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800641 {.magic = kAltMagic, .checksum = &alt_checksum},
642 {.magic = kNoChecksumMagic, .checksum = nullptr},
643 }},
David Rogers9abe3c72020-03-24 19:03:13 -0700644 kRecoveryNoGcOptions) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800645 partition_.Erase();
646 std::memcpy(flash_.buffer().data(),
647 kInitialContents.data(),
648 kInitialContents.size());
649
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800650 EXPECT_EQ(OkStatus(), kvs_.Init());
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800651 }
652
David Rogersd64cc012020-05-26 12:37:37 -0700653 FakeFlashMemoryBuffer<512, 4, 3> flash_;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800654 FlashPartition partition_;
David Rogersf3884eb2020-03-08 19:21:40 -0700655 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors, 2, 3> kvs_;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800656};
657
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700658#define ASSERT_CONTAINS_ENTRY(key, str_value) \
659 do { \
660 char val[sizeof(str_value)] = {}; \
661 StatusWithSize stat = \
662 kvs_.Get(key, std::as_writable_bytes(std::span(val))); \
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800663 ASSERT_EQ(OkStatus(), stat.status()); \
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700664 ASSERT_EQ(sizeof(str_value) - 1, stat.size()); \
665 ASSERT_STREQ(str_value, val); \
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800666 } while (0)
667
David Rogers31b358b2020-04-15 05:00:50 -0700668TEST_F(InitializedRedundantMultiMagicKvs, AllEntriesArePresent) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800669 ASSERT_CONTAINS_ENTRY("key1", "value1");
670 ASSERT_CONTAINS_ENTRY("k2", "value2");
671 ASSERT_CONTAINS_ENTRY("k3y", "value3");
672 ASSERT_CONTAINS_ENTRY("A Key", "XD");
673 ASSERT_CONTAINS_ENTRY("kee", "O_o");
674}
675
David Rogers31b358b2020-04-15 05:00:50 -0700676TEST_F(InitializedRedundantMultiMagicKvs, RecoversLossOfFirstSector) {
David Rogers98fea472020-04-01 15:43:48 -0700677 auto stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700678 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700679 EXPECT_EQ(stats.reclaimable_bytes, 0u);
Armando Montanez344814b2020-04-24 15:05:42 -0700680 EXPECT_EQ(stats.writable_bytes, 512u * 3 - (192 * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700681 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700682 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700683
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800684 EXPECT_EQ(OkStatus(), partition_.Erase(0, 1));
David Rogers98fea472020-04-01 15:43:48 -0700685
686 ASSERT_CONTAINS_ENTRY("key1", "value1");
687 ASSERT_CONTAINS_ENTRY("k2", "value2");
688 ASSERT_CONTAINS_ENTRY("k3y", "value3");
689 ASSERT_CONTAINS_ENTRY("A Key", "XD");
690 ASSERT_CONTAINS_ENTRY("kee", "O_o");
691
692 EXPECT_EQ(true, kvs_.error_detected());
693
694 stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700695 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
696 EXPECT_EQ(stats.reclaimable_bytes, 320u);
697 EXPECT_EQ(stats.writable_bytes, 512u * 2 - (192 * (kvs_.redundancy() - 1)));
David Rogers98fea472020-04-01 15:43:48 -0700698 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700699 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700700
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800701 EXPECT_EQ(OkStatus(), kvs_.FullMaintenance());
David Rogers98fea472020-04-01 15:43:48 -0700702 stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700703 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700704 EXPECT_EQ(stats.reclaimable_bytes, 0u);
Armando Montanez344814b2020-04-24 15:05:42 -0700705 EXPECT_EQ(stats.writable_bytes, 512u * 3 - (192 * kvs_.redundancy()));
David Rogersfcea3252020-04-07 14:56:35 -0700706 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanez344814b2020-04-24 15:05:42 -0700707 EXPECT_EQ(stats.missing_redundant_entries_recovered, 6u);
David Rogers98fea472020-04-01 15:43:48 -0700708}
709
David Rogers31b358b2020-04-15 05:00:50 -0700710TEST_F(InitializedRedundantMultiMagicKvs, RecoversLossOfSecondSector) {
David Rogers98fea472020-04-01 15:43:48 -0700711 auto stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700712 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700713 EXPECT_EQ(stats.reclaimable_bytes, 0u);
Armando Montanez344814b2020-04-24 15:05:42 -0700714 EXPECT_EQ(stats.writable_bytes, 512u * 3 - (192 * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700715 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700716 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700717
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800718 EXPECT_EQ(OkStatus(), partition_.Erase(partition_.sector_size_bytes(), 1));
David Rogers98fea472020-04-01 15:43:48 -0700719
720 ASSERT_CONTAINS_ENTRY("key1", "value1");
721 ASSERT_CONTAINS_ENTRY("k2", "value2");
722 ASSERT_CONTAINS_ENTRY("k3y", "value3");
723 ASSERT_CONTAINS_ENTRY("A Key", "XD");
724 ASSERT_CONTAINS_ENTRY("kee", "O_o");
725
726 EXPECT_EQ(false, kvs_.error_detected());
727
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800728 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogers98fea472020-04-01 15:43:48 -0700729 stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700730 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700731 EXPECT_EQ(stats.reclaimable_bytes, 0u);
Armando Montanez344814b2020-04-24 15:05:42 -0700732 EXPECT_EQ(stats.writable_bytes, 512u * 3 - (192 * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700733 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700734 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700735}
736
David Rogers31b358b2020-04-15 05:00:50 -0700737TEST_F(InitializedRedundantMultiMagicKvs, SingleReadErrors) {
David Rogers98fea472020-04-01 15:43:48 -0700738 // Inject 2 read errors, so the first read attempt fully fails.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700739 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 2));
David Rogers98fea472020-04-01 15:43:48 -0700740
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700741 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 1, 7));
David Rogers98fea472020-04-01 15:43:48 -0700742
743 ASSERT_CONTAINS_ENTRY("key1", "value1");
744 ASSERT_CONTAINS_ENTRY("k2", "value2");
745 ASSERT_CONTAINS_ENTRY("k3y", "value3");
746 ASSERT_CONTAINS_ENTRY("A Key", "XD");
747 ASSERT_CONTAINS_ENTRY("kee", "O_o");
748
749 EXPECT_EQ(true, kvs_.error_detected());
750
751 auto stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700752 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
753 EXPECT_EQ(stats.reclaimable_bytes, 320u);
754 EXPECT_EQ(stats.writable_bytes, 512u * 2 - (192 * (kvs_.redundancy() - 1)));
David Rogers98fea472020-04-01 15:43:48 -0700755 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700756 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700757}
758
David Rogers31b358b2020-04-15 05:00:50 -0700759TEST_F(InitializedRedundantMultiMagicKvs, SingleWriteError) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700760 flash_.InjectWriteError(FlashError::Unconditional(Status::Internal(), 1, 1));
David Rogers98fea472020-04-01 15:43:48 -0700761
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700762 EXPECT_EQ(Status::Internal(), kvs_.Put("new key", bytes::String("abcd?")));
David Rogers98fea472020-04-01 15:43:48 -0700763
764 EXPECT_EQ(true, kvs_.error_detected());
765
766 auto stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700767 EXPECT_EQ(stats.in_use_bytes, 32 + (192u * kvs_.redundancy()));
768 EXPECT_EQ(stats.reclaimable_bytes, 320u);
David Rogers98fea472020-04-01 15:43:48 -0700769 EXPECT_EQ(stats.writable_bytes,
Armando Montanez344814b2020-04-24 15:05:42 -0700770 512u * 2 - 32 - (192 * (kvs_.redundancy() - 1)));
David Rogers98fea472020-04-01 15:43:48 -0700771 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700772 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700773
774 char val[20] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700775 EXPECT_EQ(
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800776 OkStatus(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700777 kvs_.Get("new key", std::as_writable_bytes(std::span(val))).status());
David Rogers98fea472020-04-01 15:43:48 -0700778
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800779 EXPECT_EQ(OkStatus(), kvs_.FullMaintenance());
David Rogers98fea472020-04-01 15:43:48 -0700780 stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700781 EXPECT_EQ(stats.in_use_bytes, (224u * kvs_.redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700782 EXPECT_EQ(stats.reclaimable_bytes, 0u);
Armando Montanez344814b2020-04-24 15:05:42 -0700783 EXPECT_EQ(stats.writable_bytes, 512u * 3 - (224 * kvs_.redundancy()));
David Rogersfcea3252020-04-07 14:56:35 -0700784 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700785 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700786
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700787 EXPECT_EQ(
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800788 OkStatus(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700789 kvs_.Get("new key", std::as_writable_bytes(std::span(val))).status());
David Rogers98fea472020-04-01 15:43:48 -0700790}
791
David Rogers31b358b2020-04-15 05:00:50 -0700792TEST_F(InitializedRedundantMultiMagicKvs, DataLossAfterLosingBothCopies) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800793 EXPECT_EQ(OkStatus(), partition_.Erase(0, 2));
David Rogers98fea472020-04-01 15:43:48 -0700794
795 char val[20] = {};
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700796 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700797 kvs_.Get("key1", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700798 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700799 kvs_.Get("k2", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700800 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700801 kvs_.Get("k3y", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700802 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700803 kvs_.Get("A Key", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700804 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700805 kvs_.Get("kee", std::as_writable_bytes(std::span(val))).status());
David Rogers98fea472020-04-01 15:43:48 -0700806
807 EXPECT_EQ(true, kvs_.error_detected());
808
809 auto stats = kvs_.GetStorageStats();
Armando Montanez344814b2020-04-24 15:05:42 -0700810 EXPECT_EQ(stats.in_use_bytes, (192u * kvs_.redundancy()));
811 EXPECT_EQ(stats.reclaimable_bytes, 2 * 320u);
David Rogers98fea472020-04-01 15:43:48 -0700812 EXPECT_EQ(stats.writable_bytes, 512u);
813 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700814 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogers98fea472020-04-01 15:43:48 -0700815}
816
David Rogers31b358b2020-04-15 05:00:50 -0700817TEST_F(InitializedRedundantMultiMagicKvs, PutNewEntry_UsesFirstFormat) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800818 EXPECT_EQ(OkStatus(), kvs_.Put("new key", bytes::String("abcd?")));
David Rogers31b358b2020-04-15 05:00:50 -0700819
820 constexpr auto kNewEntry =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700821 MakeValidEntry(kMagic, 129, "new key", bytes::String("abcd?"));
David Rogers31b358b2020-04-15 05:00:50 -0700822 EXPECT_EQ(0,
823 std::memcmp(kNewEntry.data(),
824 flash_.buffer().data() + kInitialContents.size(),
825 kNewEntry.size()));
826 ASSERT_CONTAINS_ENTRY("new key", "abcd?");
827}
828
829TEST_F(InitializedRedundantMultiMagicKvs, PutExistingEntry_UsesFirstFormat) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800830 EXPECT_EQ(OkStatus(), kvs_.Put("A Key", bytes::String("New value!")));
David Rogers31b358b2020-04-15 05:00:50 -0700831
832 constexpr auto kNewEntry =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700833 MakeValidEntry(kMagic, 129, "A Key", bytes::String("New value!"));
David Rogers31b358b2020-04-15 05:00:50 -0700834 EXPECT_EQ(0,
835 std::memcmp(kNewEntry.data(),
836 flash_.buffer().data() + kInitialContents.size(),
837 kNewEntry.size()));
838 ASSERT_CONTAINS_ENTRY("A Key", "New value!");
839}
840
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700841#define ASSERT_KVS_CONTAINS_ENTRY(kvs, key, str_value) \
842 do { \
843 char val[sizeof(str_value)] = {}; \
844 StatusWithSize stat = \
845 kvs.Get(key, std::as_writable_bytes(std::span(val))); \
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800846 ASSERT_EQ(OkStatus(), stat.status()); \
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700847 ASSERT_EQ(sizeof(str_value) - 1, stat.size()); \
848 ASSERT_STREQ(str_value, val); \
David Rogers31b358b2020-04-15 05:00:50 -0700849 } while (0)
850
851TEST_F(InitializedRedundantMultiMagicKvs, UpdateEntryFormat) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800852 ASSERT_EQ(OkStatus(), kvs_.FullMaintenance());
David Rogers31b358b2020-04-15 05:00:50 -0700853
854 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors, 2, 1> local_kvs(
Armando Montanez888370d2020-05-01 18:29:22 -0700855 &partition_,
856 {.magic = kMagic, .checksum = &default_checksum},
857 kNoGcOptions);
David Rogers31b358b2020-04-15 05:00:50 -0700858
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800859 ASSERT_EQ(OkStatus(), local_kvs.Init());
David Rogers31b358b2020-04-15 05:00:50 -0700860 EXPECT_EQ(false, local_kvs.error_detected());
861 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "key1", "value1");
862 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "k2", "value2");
863 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "k3y", "value3");
864 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "A Key", "XD");
865 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "kee", "O_o");
866}
867
868class InitializedMultiMagicKvs : public ::testing::Test {
869 protected:
870 static constexpr auto kInitialContents =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700871 bytes::Concat(kNoChecksumEntry, kEntry1, kAltEntry, kEntry2, kEntry3);
David Rogers31b358b2020-04-15 05:00:50 -0700872
873 InitializedMultiMagicKvs()
874 : flash_(internal::Entry::kMinAlignmentBytes),
875 partition_(&flash_),
876 kvs_(&partition_,
877 {{
Armando Montanez888370d2020-05-01 18:29:22 -0700878 {.magic = kMagic, .checksum = &default_checksum},
David Rogers31b358b2020-04-15 05:00:50 -0700879 {.magic = kAltMagic, .checksum = &alt_checksum},
880 {.magic = kNoChecksumMagic, .checksum = nullptr},
881 }},
882 kRecoveryNoGcOptions) {
883 partition_.Erase();
884 std::memcpy(flash_.buffer().data(),
885 kInitialContents.data(),
886 kInitialContents.size());
887
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800888 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogers31b358b2020-04-15 05:00:50 -0700889 }
890
David Rogersd64cc012020-05-26 12:37:37 -0700891 FakeFlashMemoryBuffer<512, 4, 3> flash_;
David Rogers31b358b2020-04-15 05:00:50 -0700892 FlashPartition partition_;
893 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors, 1, 3> kvs_;
894};
895
896// Similar to test for InitializedRedundantMultiMagicKvs. Doing similar test
897// with different KVS configuration.
898TEST_F(InitializedMultiMagicKvs, AllEntriesArePresent) {
899 ASSERT_CONTAINS_ENTRY("key1", "value1");
900 ASSERT_CONTAINS_ENTRY("k2", "value2");
901 ASSERT_CONTAINS_ENTRY("k3y", "value3");
902 ASSERT_CONTAINS_ENTRY("A Key", "XD");
903 ASSERT_CONTAINS_ENTRY("kee", "O_o");
904}
905
906// Similar to test for InitializedRedundantMultiMagicKvs. Doing similar test
907// with different KVS configuration.
908TEST_F(InitializedMultiMagicKvs, UpdateEntryFormat) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800909 ASSERT_EQ(OkStatus(), kvs_.FullMaintenance());
David Rogers31b358b2020-04-15 05:00:50 -0700910
911 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors, 1, 1> local_kvs(
Armando Montanez888370d2020-05-01 18:29:22 -0700912 &partition_,
913 {.magic = kMagic, .checksum = &default_checksum},
914 kNoGcOptions);
David Rogers31b358b2020-04-15 05:00:50 -0700915
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800916 ASSERT_EQ(OkStatus(), local_kvs.Init());
David Rogers31b358b2020-04-15 05:00:50 -0700917 EXPECT_EQ(false, local_kvs.error_detected());
918 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "key1", "value1");
919 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "k2", "value2");
920 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "k3y", "value3");
921 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "A Key", "XD");
922 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "kee", "O_o");
923}
924
925class InitializedRedundantLazyRecoveryKvs : public ::testing::Test {
David Rogersfcea3252020-04-07 14:56:35 -0700926 protected:
927 static constexpr auto kInitialContents =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700928 bytes::Concat(kEntry1, kEntry2, kEntry3, kEntry4);
David Rogersfcea3252020-04-07 14:56:35 -0700929
David Rogers31b358b2020-04-15 05:00:50 -0700930 InitializedRedundantLazyRecoveryKvs()
David Rogersfcea3252020-04-07 14:56:35 -0700931 : flash_(internal::Entry::kMinAlignmentBytes),
932 partition_(&flash_),
933 kvs_(&partition_,
Armando Montanez888370d2020-05-01 18:29:22 -0700934 {.magic = kMagic, .checksum = &default_checksum},
David Rogersfcea3252020-04-07 14:56:35 -0700935 kRecoveryLazyGcOptions) {
936 partition_.Erase();
937 std::memcpy(flash_.buffer().data(),
938 kInitialContents.data(),
939 kInitialContents.size());
940
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800941 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogersfcea3252020-04-07 14:56:35 -0700942 }
943
David Rogersd64cc012020-05-26 12:37:37 -0700944 FakeFlashMemoryBuffer<512, 4, 3> flash_;
David Rogersfcea3252020-04-07 14:56:35 -0700945 FlashPartition partition_;
946 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors, 2> kvs_;
947};
948
David Rogers31b358b2020-04-15 05:00:50 -0700949TEST_F(InitializedRedundantLazyRecoveryKvs, WriteAfterDataLoss) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800950 EXPECT_EQ(OkStatus(), partition_.Erase(0, 4));
David Rogersfcea3252020-04-07 14:56:35 -0700951
952 char val[20] = {};
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700953 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700954 kvs_.Get("key1", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700955 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700956 kvs_.Get("k2", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700957 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700958 kvs_.Get("k3y", std::as_writable_bytes(std::span(val))).status());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700959 EXPECT_EQ(Status::DataLoss(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700960 kvs_.Get("4k", std::as_writable_bytes(std::span(val))).status());
David Rogersfcea3252020-04-07 14:56:35 -0700961
962 EXPECT_EQ(true, kvs_.error_detected());
963
964 auto stats = kvs_.GetStorageStats();
965 EXPECT_EQ(stats.in_use_bytes, (128u * kvs_.redundancy()));
966 EXPECT_EQ(stats.reclaimable_bytes, 2 * 384u);
967 EXPECT_EQ(stats.writable_bytes, 512u);
968 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700969 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogersfcea3252020-04-07 14:56:35 -0700970
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700971 ASSERT_EQ(Status::DataLoss(), kvs_.Put("key1", 1000));
David Rogersfcea3252020-04-07 14:56:35 -0700972
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800973 EXPECT_EQ(OkStatus(), kvs_.FullMaintenance());
David Rogersfcea3252020-04-07 14:56:35 -0700974 stats = kvs_.GetStorageStats();
975 EXPECT_EQ(stats.in_use_bytes, 0u);
976 EXPECT_EQ(stats.reclaimable_bytes, 0u);
977 EXPECT_EQ(stats.writable_bytes, 3 * 512u);
978 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700979 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogersfcea3252020-04-07 14:56:35 -0700980}
981
David Rogers31b358b2020-04-15 05:00:50 -0700982TEST_F(InitializedRedundantLazyRecoveryKvs, TwoSectorsCorruptWithGoodEntries) {
David Rogersfcea3252020-04-07 14:56:35 -0700983 ASSERT_CONTAINS_ENTRY("key1", "value1");
984 ASSERT_CONTAINS_ENTRY("k2", "value2");
985 ASSERT_CONTAINS_ENTRY("k3y", "value3");
986 ASSERT_CONTAINS_ENTRY("4k", "value4");
987
988 EXPECT_EQ(false, kvs_.error_detected());
989
990 auto stats = kvs_.GetStorageStats();
991 EXPECT_EQ(stats.in_use_bytes, (128u * kvs_.redundancy()));
992 EXPECT_EQ(stats.reclaimable_bytes, 0u);
993 EXPECT_EQ(stats.writable_bytes, 3 * 512u - (128u * kvs_.redundancy()));
994 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
Armando Montanezf8419ae2020-04-21 10:03:05 -0700995 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
David Rogersfcea3252020-04-07 14:56:35 -0700996
997 // Corrupt all the keys, alternating which copy gets corrupted.
998 flash_.buffer()[0x10] = byte(0xef);
999 flash_.buffer()[0x230] = byte(0xef);
1000 flash_.buffer()[0x50] = byte(0xef);
1001 flash_.buffer()[0x270] = byte(0xef);
1002
1003 ASSERT_CONTAINS_ENTRY("key1", "value1");
1004 ASSERT_CONTAINS_ENTRY("k2", "value2");
1005 ASSERT_CONTAINS_ENTRY("k3y", "value3");
1006 ASSERT_CONTAINS_ENTRY("4k", "value4");
1007
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001008 EXPECT_EQ(OkStatus(), kvs_.FullMaintenance());
David Rogersfcea3252020-04-07 14:56:35 -07001009 stats = kvs_.GetStorageStats();
1010 EXPECT_EQ(stats.in_use_bytes, (128u * kvs_.redundancy()));
1011 EXPECT_EQ(stats.reclaimable_bytes, 0u);
1012 EXPECT_EQ(stats.writable_bytes, 3 * 512u - (128u * kvs_.redundancy()));
1013 EXPECT_EQ(stats.corrupt_sectors_recovered, 2u);
Armando Montanezf8419ae2020-04-21 10:03:05 -07001014 EXPECT_EQ(stats.missing_redundant_entries_recovered, 4u);
David Rogersfcea3252020-04-07 14:56:35 -07001015}
1016
David Rogers0f8a1bb2020-04-21 18:50:19 -07001017class InitializedLazyRecoveryKvs : public ::testing::Test {
1018 protected:
1019 static constexpr auto kInitialContents =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -07001020 bytes::Concat(kEntry1, kEntry2, kEntry3, kEntry4);
David Rogers0f8a1bb2020-04-21 18:50:19 -07001021
1022 InitializedLazyRecoveryKvs()
1023 : flash_(internal::Entry::kMinAlignmentBytes),
1024 partition_(&flash_),
1025 kvs_(&partition_,
Armando Montanez888370d2020-05-01 18:29:22 -07001026 {.magic = kMagic, .checksum = &default_checksum},
David Rogers0f8a1bb2020-04-21 18:50:19 -07001027 kRecoveryLazyGcOptions) {
1028 partition_.Erase();
1029 std::memcpy(flash_.buffer().data(),
1030 kInitialContents.data(),
1031 kInitialContents.size());
1032
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001033 EXPECT_EQ(OkStatus(), kvs_.Init());
David Rogers0f8a1bb2020-04-21 18:50:19 -07001034 }
1035
David Rogersd64cc012020-05-26 12:37:37 -07001036 FakeFlashMemoryBuffer<512, 8> flash_;
David Rogers0f8a1bb2020-04-21 18:50:19 -07001037 FlashPartition partition_;
1038 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_;
1039};
1040
David Rogers0f8a1bb2020-04-21 18:50:19 -07001041// Test a KVS with a number of entries, several sectors that are nearly full
1042// of stale (reclaimable) space, and not enough writable (free) space to add a
1043// redundant copy for any of the entries. Tests that the add redundancy step of
1044// repair is able to use garbage collection to free up space needed for the new
1045// copies.
1046TEST_F(InitializedLazyRecoveryKvs, AddRedundancyToKvsFullOfStaleData) {
1047 // Verify the pre-initialized key are present in the KVS.
1048 ASSERT_CONTAINS_ENTRY("key1", "value1");
1049 ASSERT_CONTAINS_ENTRY("k2", "value2");
1050 ASSERT_CONTAINS_ENTRY("k3y", "value3");
1051 ASSERT_CONTAINS_ENTRY("4k", "value4");
1052
1053 EXPECT_EQ(false, kvs_.error_detected());
1054
1055 auto stats = kvs_.GetStorageStats();
1056 EXPECT_EQ(stats.in_use_bytes, (128u * kvs_.redundancy()));
1057 EXPECT_EQ(stats.reclaimable_bytes, 0u);
1058 EXPECT_EQ(stats.writable_bytes, 7 * 512u - (128u * kvs_.redundancy()));
1059 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
1060 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
1061
David Rogersd50eb1c2020-05-12 17:46:36 -07001062 // Block of data to use for entry value. Sized to 470 so the total entry
1063 // results in the 512 byte sector having 16 bytes remaining.
1064 uint8_t test_data[470] = {1, 2, 3, 4, 5, 6};
David Rogers0f8a1bb2020-04-21 18:50:19 -07001065
David Rogersd50eb1c2020-05-12 17:46:36 -07001066 // Add a near-sector size key entry to fill the KVS with a valid large entry
1067 // and stale data. Modify the value in between Puts so it actually writes
1068 // (identical value writes are skipped).
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001069 EXPECT_EQ(OkStatus(), kvs_.Put("big_key", test_data));
David Rogersd50eb1c2020-05-12 17:46:36 -07001070 test_data[0]++;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001071 EXPECT_EQ(OkStatus(), kvs_.Put("big_key", test_data));
David Rogersd50eb1c2020-05-12 17:46:36 -07001072 test_data[0]++;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001073 EXPECT_EQ(OkStatus(), kvs_.Put("big_key", test_data));
David Rogersd50eb1c2020-05-12 17:46:36 -07001074 test_data[0]++;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001075 EXPECT_EQ(OkStatus(), kvs_.Put("big_key", test_data));
David Rogersd50eb1c2020-05-12 17:46:36 -07001076 test_data[0]++;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001077 EXPECT_EQ(OkStatus(), kvs_.Put("big_key", test_data));
David Rogersd50eb1c2020-05-12 17:46:36 -07001078 test_data[0]++;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001079 EXPECT_EQ(OkStatus(), kvs_.Put("big_key", test_data));
David Rogers0f8a1bb2020-04-21 18:50:19 -07001080
1081 // Instantiate a new KVS with redundancy of 2. This KVS should add an extra
1082 // copy of each valid key as part of the init process. Because there is not
1083 // enough writable space, the adding redundancy will need to garbage collect
1084 // two sectors.
1085 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors, 2> local_kvs(
1086 &partition_,
Armando Montanez888370d2020-05-01 18:29:22 -07001087 {.magic = kMagic, .checksum = &default_checksum},
David Rogers0f8a1bb2020-04-21 18:50:19 -07001088 kRecoveryLazyGcOptions);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001089 ASSERT_EQ(OkStatus(), local_kvs.Init());
David Rogers0f8a1bb2020-04-21 18:50:19 -07001090
1091 // Verify no errors found in the new KVS and all the entries are present.
1092 EXPECT_EQ(false, local_kvs.error_detected());
1093 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "key1", "value1");
1094 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "k2", "value2");
1095 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "k3y", "value3");
1096 ASSERT_KVS_CONTAINS_ENTRY(local_kvs, "4k", "value4");
1097 StatusWithSize big_key_size = local_kvs.ValueSize("big_key");
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -08001098 EXPECT_EQ(OkStatus(), big_key_size.status());
David Rogers0f8a1bb2020-04-21 18:50:19 -07001099 EXPECT_EQ(sizeof(test_data), big_key_size.size());
1100
1101 // Verify that storage stats of the new redundant KVS match expected values.
1102 stats = local_kvs.GetStorageStats();
1103
1104 // Expected in-use bytes is size of (pre-init keys + big key) * redundancy.
1105 EXPECT_EQ(stats.in_use_bytes, ((128u + 496u) * local_kvs.redundancy()));
1106
1107 // Expected reclaimable space is number of stale entries remaining for big_key
1108 // (3 after GC to add redundancy) * total sizeof big_key entry (496 bytes).
1109
1110 EXPECT_EQ(stats.reclaimable_bytes, 496u * 3u);
1111 // Expected writable bytes is total writable size (512 * 7) - valid bytes (in
1112 // use) - reclaimable bytes.
1113 EXPECT_EQ(stats.writable_bytes, 848u);
1114 EXPECT_EQ(stats.corrupt_sectors_recovered, 0u);
1115 EXPECT_EQ(stats.missing_redundant_entries_recovered, 0u);
1116}
1117
Wyatt Hepler118fc3c2020-02-21 14:23:19 -08001118} // namespace
1119} // namespace pw::kvs