blob: bc49e19a83ea3fa0087574745518783e8c976d4f [file] [log] [blame]
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001// 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_kvs/internal/entry_cache.h"
16
17#include "gtest/gtest.h"
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -070018#include "pw_bytes/array.h"
David Rogersd64cc012020-05-26 12:37:37 -070019#include "pw_kvs/fake_flash_memory.h"
Wyatt Hepler02946272020-03-18 10:36:22 -070020#include "pw_kvs/flash_memory.h"
Wyatt Hepler02946272020-03-18 10:36:22 -070021#include "pw_kvs/internal/hash.h"
22#include "pw_kvs/internal/key_descriptor.h"
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070023
24namespace pw::kvs::internal {
Wyatt Hepler02946272020-03-18 10:36:22 -070025namespace {
26
27using std::byte;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070028
29class EmptyEntryCache : public ::testing::Test {
30 protected:
Wyatt Hepler02946272020-03-18 10:36:22 -070031 static constexpr size_t kMaxEntries = 32;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070032 static constexpr size_t kRedundancy = 3;
33
34 EmptyEntryCache() : entries_(descriptors_, addresses_, kRedundancy) {}
35
Wyatt Hepler02946272020-03-18 10:36:22 -070036 Vector<KeyDescriptor, kMaxEntries> descriptors_;
37 EntryCache::AddressList<kMaxEntries, kRedundancy> addresses_;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070038
39 EntryCache entries_;
40};
41
Wyatt Hepler02946272020-03-18 10:36:22 -070042constexpr char kTheKey[] = "The Key";
43
44constexpr KeyDescriptor kDescriptor = {.key_hash = Hash(kTheKey),
45 .transaction_id = 123,
46 .state = EntryState::kValid};
47
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070048TEST_F(EmptyEntryCache, AddNew) {
Wyatt Hepler02946272020-03-18 10:36:22 -070049 EntryMetadata metadata = entries_.AddNew(kDescriptor, 5);
50 EXPECT_EQ(kDescriptor.key_hash, metadata.hash());
51 EXPECT_EQ(kDescriptor.transaction_id, metadata.transaction_id());
52 EXPECT_EQ(kDescriptor.state, metadata.state());
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070053
54 EXPECT_EQ(5u, metadata.first_address());
55 EXPECT_EQ(1u, metadata.addresses().size());
56}
57
Wyatt Hepler02946272020-03-18 10:36:22 -070058TEST_F(EmptyEntryCache, EntryMetadata_AddNewAddress) {
59 EntryMetadata metadata = entries_.AddNew(kDescriptor, 100);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070060
Wyatt Hepler02946272020-03-18 10:36:22 -070061 metadata.AddNewAddress(999);
62
63 EXPECT_EQ(2u, metadata.addresses().size());
64 EXPECT_EQ(100u, metadata.first_address());
65 EXPECT_EQ(100u, metadata.addresses()[0]);
66 EXPECT_EQ(999u, metadata.addresses()[1]);
67}
68
69TEST_F(EmptyEntryCache, EntryMetadata_Reset) {
70 EntryMetadata metadata = entries_.AddNew(kDescriptor, 100);
71 metadata.AddNewAddress(999);
72
73 metadata.Reset(
74 {.key_hash = 987, .transaction_id = 5, .state = EntryState::kDeleted},
75 8888);
76
77 EXPECT_EQ(987u, metadata.hash());
78 EXPECT_EQ(5u, metadata.transaction_id());
79 EXPECT_EQ(EntryState::kDeleted, metadata.state());
80 EXPECT_EQ(1u, metadata.addresses().size());
81 EXPECT_EQ(8888u, metadata.first_address());
82 EXPECT_EQ(8888u, metadata.addresses()[0]);
83}
84
85TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_NewEntry) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080086 ASSERT_EQ(OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -070087 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 2000));
88
89 EXPECT_EQ(1u, entries_.present_entries());
90
91 for (const EntryMetadata& entry : entries_) {
92 EXPECT_EQ(1000u, entry.first_address());
93 EXPECT_EQ(kDescriptor.key_hash, entry.hash());
94 EXPECT_EQ(kDescriptor.transaction_id, entry.transaction_id());
95 }
96}
97
98TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_NewEntry_Full) {
99 for (uint32_t i = 0; i < kMaxEntries; ++i) {
100 ASSERT_EQ( // Fill up the cache
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800101 OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700102 entries_.AddNewOrUpdateExisting({i, i, EntryState::kValid}, i, 1));
103 }
104 ASSERT_EQ(kMaxEntries, entries_.total_entries());
105 ASSERT_TRUE(entries_.full());
106
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700107 EXPECT_EQ(Status::ResourceExhausted(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700108 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 1));
109 EXPECT_EQ(kMaxEntries, entries_.total_entries());
110}
111
112TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_UpdatedEntry) {
113 KeyDescriptor kd = kDescriptor;
114 kd.transaction_id += 3;
115
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800116 ASSERT_EQ(OkStatus(), entries_.AddNewOrUpdateExisting(kd, 3210, 2000));
Wyatt Hepler02946272020-03-18 10:36:22 -0700117
118 EXPECT_EQ(1u, entries_.present_entries());
119
120 for (const EntryMetadata& entry : entries_) {
121 EXPECT_EQ(3210u, entry.first_address());
122 EXPECT_EQ(kDescriptor.key_hash, entry.hash());
123 EXPECT_EQ(kDescriptor.transaction_id + 3, entry.transaction_id());
124 }
125}
126
127TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_AddDuplicateEntry) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800128 ASSERT_EQ(OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700129 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 2000));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800130 ASSERT_EQ(OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700131 entries_.AddNewOrUpdateExisting(kDescriptor, 3000, 2000));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800132 ASSERT_EQ(OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700133 entries_.AddNewOrUpdateExisting(kDescriptor, 7000, 2000));
134
135 // Duplicates beyond the redundancy are ignored.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800136 ASSERT_EQ(OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700137 entries_.AddNewOrUpdateExisting(kDescriptor, 9000, 2000));
138
139 EXPECT_EQ(1u, entries_.present_entries());
140
141 for (const EntryMetadata& entry : entries_) {
142 EXPECT_EQ(3u, entry.addresses().size());
143 EXPECT_EQ(1000u, entry.addresses()[0]);
144 EXPECT_EQ(3000u, entry.addresses()[1]);
145 EXPECT_EQ(7000u, entry.addresses()[2]);
146
147 EXPECT_EQ(kDescriptor.key_hash, entry.hash());
148 EXPECT_EQ(kDescriptor.transaction_id, entry.transaction_id());
149 }
150}
151
152TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_AddDuplicateEntryInSameSector) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800153 ASSERT_EQ(OkStatus(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700154 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 1000));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700155 EXPECT_EQ(Status::DataLoss(),
Wyatt Hepler02946272020-03-18 10:36:22 -0700156 entries_.AddNewOrUpdateExisting(kDescriptor, 1950, 1000));
157
158 EXPECT_EQ(1u, entries_.present_entries());
159
160 for (const EntryMetadata& entry : entries_) {
161 EXPECT_EQ(1u, entry.addresses().size());
162 EXPECT_EQ(1000u, entry.addresses()[0]);
163
164 EXPECT_EQ(kDescriptor.key_hash, entry.hash());
165 EXPECT_EQ(kDescriptor.transaction_id, entry.transaction_id());
166 }
167}
168
Wyatt Hepler883e7402020-04-06 09:31:18 -0700169TEST_F(EmptyEntryCache, Iterator_MutableFromConst_CanModify) {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700170 entries_.AddNew(kDescriptor, 1);
Wyatt Hepler883e7402020-04-06 09:31:18 -0700171 EntryCache::iterator it = static_cast<const EntryCache&>(entries_).begin();
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700172
173 static_assert(kRedundancy > 1);
174 it->AddNewAddress(1234);
175
176 EXPECT_EQ(1u, it->first_address());
177 EXPECT_EQ(1u, (*it).addresses()[0]);
178 EXPECT_EQ(1234u, it->addresses()[1]);
179}
180
181TEST_F(EmptyEntryCache, Iterator_Const) {
182 entries_.AddNew(kDescriptor, 99);
183 EntryCache::const_iterator it = entries_.cbegin();
184
185 EXPECT_EQ(99u, (*it).first_address());
186 EXPECT_EQ(99u, it->first_address());
187}
188
189TEST_F(EmptyEntryCache, Iterator_Const_CanBeAssignedFromMutable) {
190 entries_.AddNew(kDescriptor, 99);
191 EntryCache::const_iterator it = entries_.begin();
192
193 EXPECT_EQ(99u, (*it).first_address());
194 EXPECT_EQ(99u, it->first_address());
195}
196
David Rogers98fea472020-04-01 15:43:48 -0700197constexpr size_t kSectorSize = 64;
David Rogers436b3aa2020-08-03 08:44:10 -0700198constexpr uint32_t kMagic = 0xa14ae726;
199// For KVS entry magic value always use a random 32 bit integer rather than a
200// human readable 4 bytes. See pw_kvs/format.h for more information.
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700201constexpr auto kTheEntry =
202 bytes::Concat(uint32_t(kMagic), // magic
203 uint32_t(0), // checksum
204 uint8_t(0), // alignment (16 B)
205 uint8_t(sizeof(kTheKey) - 1), // key length
206 uint16_t(0), // value size
207 uint32_t(123), // transaction ID
208 bytes::String(kTheKey));
David Rogers98fea472020-04-01 15:43:48 -0700209constexpr std::array<byte, kSectorSize - kTheEntry.size() % kSectorSize>
210 kPadding1{};
211constexpr size_t kSize1 = kTheEntry.size() + kPadding1.size();
Wyatt Hepler02946272020-03-18 10:36:22 -0700212
213constexpr char kCollision1[] = "9FDC";
214constexpr char kCollision2[] = "axzzK";
215
David Rogers436b3aa2020-08-03 08:44:10 -0700216// For KVS entry magic value always use a random 32 bit integer rather than a
217// human readable 4 bytes. See pw_kvs/format.h for more information.
Wyatt Hepler02946272020-03-18 10:36:22 -0700218constexpr auto kCollisionEntry =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700219 bytes::Concat(uint32_t(kMagic), // magic
220 uint32_t(0), // checksum
221 uint8_t(0), // alignment (16 B)
222 uint8_t(sizeof(kCollision1) - 1), // key length
223 uint16_t(0), // value size
224 uint32_t(123), // transaction ID
225 bytes::String(kCollision1));
David Rogers98fea472020-04-01 15:43:48 -0700226constexpr std::array<byte, kSectorSize - kCollisionEntry.size() % kSectorSize>
227 kPadding2{};
228constexpr size_t kSize2 = kCollisionEntry.size() + kPadding2.size();
Wyatt Hepler02946272020-03-18 10:36:22 -0700229
David Rogers436b3aa2020-08-03 08:44:10 -0700230// For KVS entry magic value always use a random 32 bit integer rather than a
231// human readable 4 bytes. See pw_kvs/format.h for more information.
Wyatt Hepler02946272020-03-18 10:36:22 -0700232constexpr auto kDeletedEntry =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700233 bytes::Concat(uint32_t(kMagic), // magic
234 uint32_t(0), // checksum
235 uint8_t(0), // alignment (16 B)
236 uint8_t(sizeof("delorted") - 1), // key length
237 uint16_t(0xffff), // value size (deleted)
238 uint32_t(123), // transaction ID
239 bytes::String("delorted"));
David Rogers98fea472020-04-01 15:43:48 -0700240constexpr std::array<byte, kSectorSize - kDeletedEntry.size() % kSectorSize>
241 kPadding3{};
242
David Rogers436b3aa2020-08-03 08:44:10 -0700243// For KVS entry magic value always use a random 32 bit integer rather than a
244// human readable 4 bytes. See pw_kvs/format.h for more information.
245constexpr EntryFormat kFormat{.magic = uint32_t(kMagic), .checksum = nullptr};
Wyatt Hepler02946272020-03-18 10:36:22 -0700246
247class InitializedEntryCache : public EmptyEntryCache {
248 protected:
249 static_assert(Hash(kCollision1) == Hash(kCollision2));
250
251 InitializedEntryCache()
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700252 : flash_(bytes::Concat(kTheEntry,
253 kPadding1,
254 kTheEntry,
255 kPadding1,
256 kCollisionEntry,
257 kPadding2,
258 kDeletedEntry,
259 kPadding3)),
David Rogers98fea472020-04-01 15:43:48 -0700260 partition_(&flash_),
261 sectors_(sector_descriptors_, partition_, nullptr),
262 format_(kFormat) {
263 sectors_.Reset();
264 size_t address = 0;
265 auto entry = entries_.AddNew(kDescriptor, address);
266
267 address += kSize1;
268 entry.AddNewAddress(kSize1);
269
270 address += kSize1;
Wyatt Hepler02946272020-03-18 10:36:22 -0700271 entries_.AddNew({.key_hash = Hash(kCollision1),
272 .transaction_id = 125,
273 .state = EntryState::kDeleted},
David Rogers98fea472020-04-01 15:43:48 -0700274 address);
275
276 address += kSize2;
Wyatt Hepler02946272020-03-18 10:36:22 -0700277 entries_.AddNew({.key_hash = Hash("delorted"),
278 .transaction_id = 256,
279 .state = EntryState::kDeleted},
David Rogers98fea472020-04-01 15:43:48 -0700280 address);
Wyatt Hepler02946272020-03-18 10:36:22 -0700281 }
282
David Rogers98fea472020-04-01 15:43:48 -0700283 void CheckForCorruptSectors(SectorDescriptor* sector1 = nullptr,
284 SectorDescriptor* sector2 = nullptr) {
Prashanth Swaminathan79254302021-02-02 22:35:56 -0800285 for (const auto& sector : sectors_) {
David Rogers98fea472020-04-01 15:43:48 -0700286 bool expect_corrupt =
Prashanth Swaminathan79254302021-02-02 22:35:56 -0800287 ((sector1 && &sector == sector1) || (sector2 && &sector == sector2));
David Rogers98fea472020-04-01 15:43:48 -0700288 EXPECT_EQ(expect_corrupt, sector.corrupt());
289 }
290 }
291
292 static constexpr size_t kTotalSectors = 128;
David Rogersd64cc012020-05-26 12:37:37 -0700293 FakeFlashMemoryBuffer<kSectorSize, kTotalSectors> flash_;
Wyatt Hepler02946272020-03-18 10:36:22 -0700294 FlashPartition partition_;
David Rogers98fea472020-04-01 15:43:48 -0700295
296 Vector<SectorDescriptor, kTotalSectors> sector_descriptors_;
297 Sectors sectors_;
298
299 EntryFormats format_;
Wyatt Hepler02946272020-03-18 10:36:22 -0700300};
301
302TEST_F(InitializedEntryCache, EntryCounts) {
303 EXPECT_EQ(3u, entries_.total_entries());
304 EXPECT_EQ(1u, entries_.present_entries());
305 EXPECT_EQ(kMaxEntries, entries_.max_entries());
306}
307
308TEST_F(InitializedEntryCache, Reset_ClearsEntryCounts) {
309 entries_.Reset();
310
311 EXPECT_EQ(0u, entries_.total_entries());
312 EXPECT_EQ(0u, entries_.present_entries());
313 EXPECT_EQ(kMaxEntries, entries_.max_entries());
314}
315
316TEST_F(InitializedEntryCache, Find_PresentEntry) {
317 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700318
319 StatusWithSize result =
320 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
321
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800322 ASSERT_EQ(OkStatus(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700323 EXPECT_EQ(0u, result.size());
Wyatt Hepler02946272020-03-18 10:36:22 -0700324 EXPECT_EQ(Hash(kTheKey), metadata.hash());
325 EXPECT_EQ(EntryState::kValid, metadata.state());
David Rogers98fea472020-04-01 15:43:48 -0700326 CheckForCorruptSectors();
327}
328
329TEST_F(InitializedEntryCache, Find_PresentEntryWithSingleReadError) {
330 // Inject 2 read errors so that the initial key read and the follow-up full
331 // read of the first entry fail.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700332 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 2));
David Rogers98fea472020-04-01 15:43:48 -0700333
334 EntryMetadata metadata;
335
336 StatusWithSize result =
337 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
338
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800339 ASSERT_EQ(OkStatus(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700340 EXPECT_EQ(1u, result.size());
341 EXPECT_EQ(Hash(kTheKey), metadata.hash());
342 EXPECT_EQ(EntryState::kValid, metadata.state());
343 CheckForCorruptSectors(&sectors_.FromAddress(0));
344}
345
346TEST_F(InitializedEntryCache, Find_PresentEntryWithMultiReadError) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700347 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 4));
David Rogers98fea472020-04-01 15:43:48 -0700348
349 EntryMetadata metadata;
350
351 StatusWithSize result =
352 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
353
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700354 ASSERT_EQ(Status::DataLoss(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700355 EXPECT_EQ(1u, result.size());
356 CheckForCorruptSectors(&sectors_.FromAddress(0),
357 &sectors_.FromAddress(kSize1));
Wyatt Hepler02946272020-03-18 10:36:22 -0700358}
359
360TEST_F(InitializedEntryCache, Find_DeletedEntry) {
361 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700362
363 StatusWithSize result =
364 entries_.Find(partition_, sectors_, format_, "delorted", &metadata);
365
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800366 ASSERT_EQ(OkStatus(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700367 EXPECT_EQ(0u, result.size());
Wyatt Hepler02946272020-03-18 10:36:22 -0700368 EXPECT_EQ(Hash("delorted"), metadata.hash());
369 EXPECT_EQ(EntryState::kDeleted, metadata.state());
David Rogers98fea472020-04-01 15:43:48 -0700370 CheckForCorruptSectors();
Wyatt Hepler02946272020-03-18 10:36:22 -0700371}
372
373TEST_F(InitializedEntryCache, Find_MissingEntry) {
374 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700375
376 StatusWithSize result =
377 entries_.Find(partition_, sectors_, format_, "3.141", &metadata);
378
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700379 ASSERT_EQ(Status::NotFound(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700380 EXPECT_EQ(0u, result.size());
381 CheckForCorruptSectors();
Wyatt Hepler02946272020-03-18 10:36:22 -0700382}
383
384TEST_F(InitializedEntryCache, Find_Collision) {
385 EntryMetadata metadata;
Wyatt Hepler02946272020-03-18 10:36:22 -0700386
David Rogers98fea472020-04-01 15:43:48 -0700387 StatusWithSize result =
388 entries_.Find(partition_, sectors_, format_, kCollision2, &metadata);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700389 EXPECT_EQ(Status::AlreadyExists(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700390 EXPECT_EQ(0u, result.size());
391 CheckForCorruptSectors();
Wyatt Hepler02946272020-03-18 10:36:22 -0700392}
393
394} // namespace
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700395} // namespace pw::kvs::internal