blob: 6b7c9fa877acff02533e1583cdf62e1d7756aa6f [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) {
285 for (auto& sector : sectors_) {
286 bool expect_corrupt =
287 (&sector == sector1 || &sector == sector2) ? true : false;
288
289 EXPECT_EQ(expect_corrupt, sector.corrupt());
290 }
291 }
292
293 static constexpr size_t kTotalSectors = 128;
David Rogersd64cc012020-05-26 12:37:37 -0700294 FakeFlashMemoryBuffer<kSectorSize, kTotalSectors> flash_;
Wyatt Hepler02946272020-03-18 10:36:22 -0700295 FlashPartition partition_;
David Rogers98fea472020-04-01 15:43:48 -0700296
297 Vector<SectorDescriptor, kTotalSectors> sector_descriptors_;
298 Sectors sectors_;
299
300 EntryFormats format_;
Wyatt Hepler02946272020-03-18 10:36:22 -0700301};
302
303TEST_F(InitializedEntryCache, EntryCounts) {
304 EXPECT_EQ(3u, entries_.total_entries());
305 EXPECT_EQ(1u, entries_.present_entries());
306 EXPECT_EQ(kMaxEntries, entries_.max_entries());
307}
308
309TEST_F(InitializedEntryCache, Reset_ClearsEntryCounts) {
310 entries_.Reset();
311
312 EXPECT_EQ(0u, entries_.total_entries());
313 EXPECT_EQ(0u, entries_.present_entries());
314 EXPECT_EQ(kMaxEntries, entries_.max_entries());
315}
316
317TEST_F(InitializedEntryCache, Find_PresentEntry) {
318 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700319
320 StatusWithSize result =
321 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
322
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800323 ASSERT_EQ(OkStatus(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700324 EXPECT_EQ(0u, result.size());
Wyatt Hepler02946272020-03-18 10:36:22 -0700325 EXPECT_EQ(Hash(kTheKey), metadata.hash());
326 EXPECT_EQ(EntryState::kValid, metadata.state());
David Rogers98fea472020-04-01 15:43:48 -0700327 CheckForCorruptSectors();
328}
329
330TEST_F(InitializedEntryCache, Find_PresentEntryWithSingleReadError) {
331 // Inject 2 read errors so that the initial key read and the follow-up full
332 // read of the first entry fail.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700333 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 2));
David Rogers98fea472020-04-01 15:43:48 -0700334
335 EntryMetadata metadata;
336
337 StatusWithSize result =
338 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
339
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800340 ASSERT_EQ(OkStatus(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700341 EXPECT_EQ(1u, result.size());
342 EXPECT_EQ(Hash(kTheKey), metadata.hash());
343 EXPECT_EQ(EntryState::kValid, metadata.state());
344 CheckForCorruptSectors(&sectors_.FromAddress(0));
345}
346
347TEST_F(InitializedEntryCache, Find_PresentEntryWithMultiReadError) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700348 flash_.InjectReadError(FlashError::Unconditional(Status::Internal(), 4));
David Rogers98fea472020-04-01 15:43:48 -0700349
350 EntryMetadata metadata;
351
352 StatusWithSize result =
353 entries_.Find(partition_, sectors_, format_, kTheKey, &metadata);
354
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700355 ASSERT_EQ(Status::DataLoss(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700356 EXPECT_EQ(1u, result.size());
357 CheckForCorruptSectors(&sectors_.FromAddress(0),
358 &sectors_.FromAddress(kSize1));
Wyatt Hepler02946272020-03-18 10:36:22 -0700359}
360
361TEST_F(InitializedEntryCache, Find_DeletedEntry) {
362 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700363
364 StatusWithSize result =
365 entries_.Find(partition_, sectors_, format_, "delorted", &metadata);
366
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800367 ASSERT_EQ(OkStatus(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700368 EXPECT_EQ(0u, result.size());
Wyatt Hepler02946272020-03-18 10:36:22 -0700369 EXPECT_EQ(Hash("delorted"), metadata.hash());
370 EXPECT_EQ(EntryState::kDeleted, metadata.state());
David Rogers98fea472020-04-01 15:43:48 -0700371 CheckForCorruptSectors();
Wyatt Hepler02946272020-03-18 10:36:22 -0700372}
373
374TEST_F(InitializedEntryCache, Find_MissingEntry) {
375 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700376
377 StatusWithSize result =
378 entries_.Find(partition_, sectors_, format_, "3.141", &metadata);
379
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700380 ASSERT_EQ(Status::NotFound(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700381 EXPECT_EQ(0u, result.size());
382 CheckForCorruptSectors();
Wyatt Hepler02946272020-03-18 10:36:22 -0700383}
384
385TEST_F(InitializedEntryCache, Find_Collision) {
386 EntryMetadata metadata;
Wyatt Hepler02946272020-03-18 10:36:22 -0700387
David Rogers98fea472020-04-01 15:43:48 -0700388 StatusWithSize result =
389 entries_.Find(partition_, sectors_, format_, kCollision2, &metadata);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700390 EXPECT_EQ(Status::AlreadyExists(), result.status());
David Rogers98fea472020-04-01 15:43:48 -0700391 EXPECT_EQ(0u, result.size());
392 CheckForCorruptSectors();
Wyatt Hepler02946272020-03-18 10:36:22 -0700393}
394
395} // namespace
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700396} // namespace pw::kvs::internal