1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
15 #include "pw_kvs/internal/entry_cache.h"
17 #include "gtest/gtest.h"
18 #include "pw_bytes/array.h"
19 #include "pw_kvs/fake_flash_memory.h"
20 #include "pw_kvs/flash_memory.h"
21 #include "pw_kvs/internal/hash.h"
22 #include "pw_kvs/internal/key_descriptor.h"
24 namespace pw::kvs::internal {
29 class EmptyEntryCache : public ::testing::Test {
31 static constexpr size_t kMaxEntries = 32;
32 static constexpr size_t kRedundancy = 3;
34 EmptyEntryCache() : entries_(descriptors_, addresses_, kRedundancy) {}
36 Vector<KeyDescriptor, kMaxEntries> descriptors_;
37 EntryCache::AddressList<kMaxEntries, kRedundancy> addresses_;
42 constexpr char kTheKey[] = "The Key";
44 constexpr KeyDescriptor kDescriptor = {.key_hash = Hash(kTheKey),
45 .transaction_id = 123,
46 .state = EntryState::kValid};
48 TEST_F(EmptyEntryCache, AddNew) {
49 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());
54 EXPECT_EQ(5u, metadata.first_address());
55 EXPECT_EQ(1u, metadata.addresses().size());
58 TEST_F(EmptyEntryCache, EntryMetadata_AddNewAddress) {
59 EntryMetadata metadata = entries_.AddNew(kDescriptor, 100);
61 metadata.AddNewAddress(999);
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]);
69 TEST_F(EmptyEntryCache, EntryMetadata_Reset) {
70 EntryMetadata metadata = entries_.AddNew(kDescriptor, 100);
71 metadata.AddNewAddress(999);
74 {.key_hash = 987, .transaction_id = 5, .state = EntryState::kDeleted},
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]);
85 TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_NewEntry) {
87 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 2000));
89 EXPECT_EQ(1u, entries_.present_entries());
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());
98 TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_NewEntry_Full) {
99 for (uint32_t i = 0; i < kMaxEntries; ++i) {
100 ASSERT_EQ( // Fill up the cache
102 entries_.AddNewOrUpdateExisting({i, i, EntryState::kValid}, i, 1));
104 ASSERT_EQ(kMaxEntries, entries_.total_entries());
105 ASSERT_TRUE(entries_.full());
107 EXPECT_EQ(Status::ResourceExhausted(),
108 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 1));
109 EXPECT_EQ(kMaxEntries, entries_.total_entries());
112 TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_UpdatedEntry) {
113 KeyDescriptor kd = kDescriptor;
114 kd.transaction_id += 3;
116 ASSERT_EQ(OkStatus(), entries_.AddNewOrUpdateExisting(kd, 3210, 2000));
118 EXPECT_EQ(1u, entries_.present_entries());
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());
127 TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_AddDuplicateEntry) {
128 ASSERT_EQ(OkStatus(),
129 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 2000));
130 ASSERT_EQ(OkStatus(),
131 entries_.AddNewOrUpdateExisting(kDescriptor, 3000, 2000));
132 ASSERT_EQ(OkStatus(),
133 entries_.AddNewOrUpdateExisting(kDescriptor, 7000, 2000));
135 // Duplicates beyond the redundancy are ignored.
136 ASSERT_EQ(OkStatus(),
137 entries_.AddNewOrUpdateExisting(kDescriptor, 9000, 2000));
139 EXPECT_EQ(1u, entries_.present_entries());
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]);
147 EXPECT_EQ(kDescriptor.key_hash, entry.hash());
148 EXPECT_EQ(kDescriptor.transaction_id, entry.transaction_id());
152 TEST_F(EmptyEntryCache, AddNewOrUpdateExisting_AddDuplicateEntryInSameSector) {
153 ASSERT_EQ(OkStatus(),
154 entries_.AddNewOrUpdateExisting(kDescriptor, 1000, 1000));
155 EXPECT_EQ(Status::DataLoss(),
156 entries_.AddNewOrUpdateExisting(kDescriptor, 1950, 1000));
158 EXPECT_EQ(1u, entries_.present_entries());
160 for (const EntryMetadata& entry : entries_) {
161 EXPECT_EQ(1u, entry.addresses().size());
162 EXPECT_EQ(1000u, entry.addresses()[0]);
164 EXPECT_EQ(kDescriptor.key_hash, entry.hash());
165 EXPECT_EQ(kDescriptor.transaction_id, entry.transaction_id());
169 TEST_F(EmptyEntryCache, Iterator_MutableFromConst_CanModify) {
170 entries_.AddNew(kDescriptor, 1);
171 EntryCache::iterator it = static_cast<const EntryCache&>(entries_).begin();
173 static_assert(kRedundancy > 1);
174 it->AddNewAddress(1234);
176 EXPECT_EQ(1u, it->first_address());
177 EXPECT_EQ(1u, (*it).addresses()[0]);
178 EXPECT_EQ(1234u, it->addresses()[1]);
181 TEST_F(EmptyEntryCache, Iterator_Const) {
182 entries_.AddNew(kDescriptor, 99);
183 EntryCache::const_iterator it = entries_.cbegin();
185 EXPECT_EQ(99u, (*it).first_address());
186 EXPECT_EQ(99u, it->first_address());
189 TEST_F(EmptyEntryCache, Iterator_Const_CanBeAssignedFromMutable) {
190 entries_.AddNew(kDescriptor, 99);
191 EntryCache::const_iterator it = entries_.begin();
193 EXPECT_EQ(99u, (*it).first_address());
194 EXPECT_EQ(99u, it->first_address());
197 constexpr size_t kSectorSize = 64;
198 constexpr 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.
201 constexpr 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));
209 constexpr std::array<byte, kSectorSize - kTheEntry.size() % kSectorSize>
211 constexpr size_t kSize1 = kTheEntry.size() + kPadding1.size();
213 constexpr char kCollision1[] = "9FDC";
214 constexpr char kCollision2[] = "axzzK";
216 // 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.
218 constexpr auto kCollisionEntry =
219 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));
226 constexpr std::array<byte, kSectorSize - kCollisionEntry.size() % kSectorSize>
228 constexpr size_t kSize2 = kCollisionEntry.size() + kPadding2.size();
230 // 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.
232 constexpr auto kDeletedEntry =
233 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"));
240 constexpr std::array<byte, kSectorSize - kDeletedEntry.size() % kSectorSize>
243 // 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.
245 constexpr EntryFormat kFormat{.magic = uint32_t(kMagic), .checksum = nullptr};
247 class InitializedEntryCache : public EmptyEntryCache {
249 static_assert(Hash(kCollision1) == Hash(kCollision2));
251 InitializedEntryCache()
252 : flash_(bytes::Concat(kTheEntry,
261 sectors_(sector_descriptors_, partition_, nullptr),
265 auto entry = entries_.AddNew(kDescriptor, address);
268 entry.AddNewAddress(kSize1);
271 entries_.AddNew({.key_hash = Hash(kCollision1),
272 .transaction_id = 125,
273 .state = EntryState::kDeleted},
277 entries_.AddNew({.key_hash = Hash("delorted"),
278 .transaction_id = 256,
279 .state = EntryState::kDeleted},
283 void CheckForCorruptSectors(SectorDescriptor* sector1 = nullptr,
284 SectorDescriptor* sector2 = nullptr) {
285 for (const auto& sector : sectors_) {
286 bool expect_corrupt =
287 ((sector1 && §or == sector1) || (sector2 && §or == sector2));
288 EXPECT_EQ(expect_corrupt, sector.corrupt());
292 static constexpr size_t kTotalSectors = 128;
293 FakeFlashMemoryBuffer<kSectorSize, kTotalSectors> flash_;
294 FlashPartition partition_;
296 Vector<SectorDescriptor, kTotalSectors> sector_descriptors_;
299 EntryFormats format_;
302 TEST_F(InitializedEntryCache, EntryCounts) {
303 EXPECT_EQ(3u, entries_.total_entries());
304 EXPECT_EQ(1u, entries_.present_entries());
305 EXPECT_EQ(kMaxEntries, entries_.max_entries());
308 TEST_F(InitializedEntryCache, Reset_ClearsEntryCounts) {
311 EXPECT_EQ(0u, entries_.total_entries());
312 EXPECT_EQ(0u, entries_.present_entries());
313 EXPECT_EQ(kMaxEntries, entries_.max_entries());
316 TEST_F(InitializedEntryCache, Find_PresentEntry) {
317 EntryMetadata metadata;
319 StatusWithSize result =
320 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
322 ASSERT_EQ(OkStatus(), result.status());
323 EXPECT_EQ(0u, result.size());
324 EXPECT_EQ(Hash(kTheKey), metadata.hash());
325 EXPECT_EQ(EntryState::kValid, metadata.state());
326 CheckForCorruptSectors();
329 TEST_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.
332 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 2));
334 EntryMetadata metadata;
336 StatusWithSize result =
337 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
339 ASSERT_EQ(OkStatus(), result.status());
340 EXPECT_EQ(1u, result.size());
341 EXPECT_EQ(Hash(kTheKey), metadata.hash());
342 EXPECT_EQ(EntryState::kValid, metadata.state());
343 CheckForCorruptSectors(§ors_.FromAddress(0));
346 TEST_F(InitializedEntryCache, Find_PresentEntryWithMultiReadError) {
347 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 4));
349 EntryMetadata metadata;
351 StatusWithSize result =
352 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
354 ASSERT_EQ(Status::DataLoss(), result.status());
355 EXPECT_EQ(1u, result.size());
356 CheckForCorruptSectors(§ors_.FromAddress(0),
357 §ors_.FromAddress(kSize1));
360 TEST_F(InitializedEntryCache, Find_DeletedEntry) {
361 EntryMetadata metadata;
363 StatusWithSize result =
364 entries_.Find(partition_, sectors_, format_, "delorted", &metadata);
366 ASSERT_EQ(OkStatus(), result.status());
367 EXPECT_EQ(0u, result.size());
368 EXPECT_EQ(Hash("delorted"), metadata.hash());
369 EXPECT_EQ(EntryState::kDeleted, metadata.state());
370 CheckForCorruptSectors();
373 TEST_F(InitializedEntryCache, Find_MissingEntry) {
374 EntryMetadata metadata;
376 StatusWithSize result =
377 entries_.Find(partition_, sectors_, format_, "3.141", &metadata);
379 ASSERT_EQ(Status::NotFound(), result.status());
380 EXPECT_EQ(0u, result.size());
381 CheckForCorruptSectors();
384 TEST_F(InitializedEntryCache, Find_Collision) {
385 EntryMetadata metadata;
387 StatusWithSize result =
388 entries_.Find(partition_, sectors_, format_, kCollision2, &metadata);
389 EXPECT_EQ(Status::AlreadyExists(), result.status());
390 EXPECT_EQ(0u, result.size());
391 CheckForCorruptSectors();
395 } // namespace pw::kvs::internal