1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include <array>
16 #include <cstdio>
17 #include <cstring>
18
19 #include "pw_assert/check.h"
20 #include "pw_bytes/array.h"
21 #include "pw_checksum/crc16_ccitt.h"
22 #include "pw_kvs/crc16_checksum.h"
23 #include "pw_kvs/flash_memory.h"
24 #include "pw_kvs/flash_test_partition.h"
25 #include "pw_kvs/internal/entry.h"
26 #include "pw_kvs/key_value_store.h"
27 #include "pw_log/log.h"
28 #include "pw_span/span.h"
29 #include "pw_status/status.h"
30 #include "pw_string/string_builder.h"
31 #include "pw_unit_test/framework.h"
32
33 namespace pw::kvs {
34 namespace {
35
36 using internal::EntryHeader;
37 using std::byte;
38
39 constexpr size_t kMaxEntries = 256;
40 constexpr size_t kMaxUsableSectors = 1024;
41
42 FlashPartition& test_partition = FlashTestPartition();
43
44 std::array<byte, 512> buffer;
45
RoundUpForAlignment(size_t size)46 size_t RoundUpForAlignment(size_t size) {
47 return AlignUp(size, test_partition.alignment_bytes());
48 }
49
50 // This class gives attributes of KVS that we are testing against
51 class KvsAttributes {
52 public:
KvsAttributes(size_t key_size,size_t data_size)53 KvsAttributes(size_t key_size, size_t data_size)
54 : chunk_header_size_(RoundUpForAlignment(sizeof(EntryHeader))),
55 data_size_(RoundUpForAlignment(data_size)),
56 key_size_(RoundUpForAlignment(key_size)),
57 erase_size_(chunk_header_size_ + key_size_),
58 min_put_size_(
59 RoundUpForAlignment(chunk_header_size_ + key_size_ + data_size_)) {}
60
ChunkHeaderSize()61 size_t ChunkHeaderSize() { return chunk_header_size_; }
DataSize()62 size_t DataSize() { return data_size_; }
KeySize()63 size_t KeySize() { return key_size_; }
EraseSize()64 size_t EraseSize() { return erase_size_; }
MinPutSize()65 size_t MinPutSize() { return min_put_size_; }
66
67 private:
68 const size_t chunk_header_size_;
69 const size_t data_size_;
70 const size_t key_size_;
71 const size_t erase_size_;
72 const size_t min_put_size_;
73 };
74
75 constexpr std::array<const char*, 3> keys{"TestKey1", "Key2", "TestKey3"};
76
77 ChecksumCrc16 checksum;
78 // For KVS magic value always use a random 32 bit integer rather than a
79 // human readable 4 bytes. See pw_kvs/format.h for more information.
80 constexpr EntryFormat default_format{.magic = 0x5b9a341e,
81 .checksum = &checksum};
82
83 class EmptyInitializedKvs : public ::testing::Test {
84 protected:
EmptyInitializedKvs()85 EmptyInitializedKvs() : kvs_(&test_partition, default_format) {
86 EXPECT_EQ(OkStatus(), test_partition.Erase());
87 PW_CHECK_OK(kvs_.Init());
88 }
89
90 // Intention of this is to put and erase key-val to fill up sectors. It's a
91 // helper function in testing how KVS handles cases where flash sector is full
92 // or near full.
FillKvs(const char * key,size_t size_to_fill)93 void FillKvs(const char* key, size_t size_to_fill) {
94 constexpr size_t kTestDataSize = 8;
95 KvsAttributes kvs_attr(std::strlen(key), kTestDataSize);
96 const size_t kMaxPutSize =
97 buffer.size() + kvs_attr.ChunkHeaderSize() + kvs_attr.KeySize();
98
99 ASSERT_GE(size_to_fill, kvs_attr.MinPutSize() + kvs_attr.EraseSize());
100
101 // Saving enough space to perform erase after loop
102 size_to_fill -= kvs_attr.EraseSize();
103 // We start with possible small chunk to prevent too small of a Kvs.Put() at
104 // the end.
105 size_t chunk_len =
106 std::max(kvs_attr.MinPutSize(), size_to_fill % buffer.size());
107 std::memset(buffer.data(), 0, buffer.size());
108 while (size_to_fill > 0) {
109 // Changing buffer value so put actually does something
110 buffer[0] = static_cast<byte>(static_cast<uint8_t>(buffer[0]) + 1);
111 ASSERT_EQ(OkStatus(),
112 kvs_.Put(key,
113 span(buffer.data(),
114 chunk_len - kvs_attr.ChunkHeaderSize() -
115 kvs_attr.KeySize())));
116 size_to_fill -= chunk_len;
117 chunk_len = std::min(size_to_fill, kMaxPutSize);
118 }
119 ASSERT_EQ(OkStatus(), kvs_.Delete(key));
120 }
121
122 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_;
123 };
124
125 } // namespace
126
TEST_F(EmptyInitializedKvs,Put_SameKeySameValueRepeatedly_AlignedEntries)127 TEST_F(EmptyInitializedKvs, Put_SameKeySameValueRepeatedly_AlignedEntries) {
128 std::array<char, 8> value{'v', 'a', 'l', 'u', 'e', '6', '7', '\0'};
129
130 for (int i = 0; i < 1000; ++i) {
131 ASSERT_EQ(OkStatus(), kvs_.Put("The Key!", as_bytes(span(value))));
132 }
133 }
134
TEST_F(EmptyInitializedKvs,Put_SameKeySameValueRepeatedly_UnalignedEntries)135 TEST_F(EmptyInitializedKvs, Put_SameKeySameValueRepeatedly_UnalignedEntries) {
136 std::array<char, 7> value{'v', 'a', 'l', 'u', 'e', '6', '\0'};
137
138 for (int i = 0; i < 1000; ++i) {
139 ASSERT_EQ(OkStatus(), kvs_.Put("The Key!", as_bytes(span(value))));
140 }
141 }
142
TEST_F(EmptyInitializedKvs,Put_SameKeyDifferentValuesRepeatedly)143 TEST_F(EmptyInitializedKvs, Put_SameKeyDifferentValuesRepeatedly) {
144 std::array<char, 10> value{'v', 'a', 'l', 'u', 'e', '6', '7', '8', '9', '\0'};
145
146 for (int i = 0; i < 100; ++i) {
147 for (unsigned size = 0; size < value.size(); ++size) {
148 ASSERT_EQ(OkStatus(), kvs_.Put("The Key!", i));
149 }
150 }
151 }
152
TEST_F(EmptyInitializedKvs,PutAndGetByValue_ConvertibleToSpan)153 TEST_F(EmptyInitializedKvs, PutAndGetByValue_ConvertibleToSpan) {
154 constexpr float input[] = {1.0, -3.5};
155 ASSERT_EQ(OkStatus(), kvs_.Put("key", input));
156
157 float output[2] = {};
158 ASSERT_EQ(OkStatus(), kvs_.Get("key", &output));
159 EXPECT_EQ(input[0], output[0]);
160 EXPECT_EQ(input[1], output[1]);
161 }
162
TEST_F(EmptyInitializedKvs,PutAndGetByValue_Span)163 TEST_F(EmptyInitializedKvs, PutAndGetByValue_Span) {
164 float input[] = {1.0, -3.5};
165 ASSERT_EQ(OkStatus(), kvs_.Put("key", span(input)));
166
167 float output[2] = {};
168 ASSERT_EQ(OkStatus(), kvs_.Get("key", &output));
169 EXPECT_EQ(input[0], output[0]);
170 EXPECT_EQ(input[1], output[1]);
171 }
172
TEST_F(EmptyInitializedKvs,PutAndGetByValue_NotConvertibleToSpan)173 TEST_F(EmptyInitializedKvs, PutAndGetByValue_NotConvertibleToSpan) {
174 struct TestStruct {
175 float a;
176 bool b;
177 };
178 const TestStruct input{-1234.5, true};
179
180 ASSERT_EQ(OkStatus(), kvs_.Put("key", input));
181
182 TestStruct output;
183 ASSERT_EQ(OkStatus(), kvs_.Get("key", &output));
184 EXPECT_EQ(input.a, output.a);
185 EXPECT_EQ(input.b, output.b);
186 }
187
TEST_F(EmptyInitializedKvs,Get_Simple)188 TEST_F(EmptyInitializedKvs, Get_Simple) {
189 ASSERT_EQ(OkStatus(), kvs_.Put("Charles", as_bytes(span("Mingus"))));
190
191 char value[16];
192 auto result = kvs_.Get("Charles", as_writable_bytes(span(value)));
193 EXPECT_EQ(OkStatus(), result.status());
194 EXPECT_EQ(sizeof("Mingus"), result.size());
195 EXPECT_STREQ("Mingus", value);
196 }
197
TEST_F(EmptyInitializedKvs,Get_WithOffset)198 TEST_F(EmptyInitializedKvs, Get_WithOffset) {
199 ASSERT_EQ(OkStatus(), kvs_.Put("Charles", as_bytes(span("Mingus"))));
200
201 char value[16];
202 auto result = kvs_.Get("Charles", as_writable_bytes(span(value)), 4);
203 EXPECT_EQ(OkStatus(), result.status());
204 EXPECT_EQ(sizeof("Mingus") - 4, result.size());
205 EXPECT_STREQ("us", value);
206 }
207
TEST_F(EmptyInitializedKvs,Get_WithOffset_FillBuffer)208 TEST_F(EmptyInitializedKvs, Get_WithOffset_FillBuffer) {
209 ASSERT_EQ(OkStatus(), kvs_.Put("Charles", as_bytes(span("Mingus"))));
210
211 char value[4] = {};
212 auto result = kvs_.Get("Charles", as_writable_bytes(span(value, 3)), 1);
213 EXPECT_EQ(Status::ResourceExhausted(), result.status());
214 EXPECT_EQ(3u, result.size());
215 EXPECT_STREQ("ing", value);
216 }
217
TEST_F(EmptyInitializedKvs,Get_WithOffset_PastEnd)218 TEST_F(EmptyInitializedKvs, Get_WithOffset_PastEnd) {
219 ASSERT_EQ(OkStatus(), kvs_.Put("Charles", as_bytes(span("Mingus"))));
220
221 char value[16];
222 auto result =
223 kvs_.Get("Charles", as_writable_bytes(span(value)), sizeof("Mingus") + 1);
224 EXPECT_EQ(Status::OutOfRange(), result.status());
225 EXPECT_EQ(0u, result.size());
226 }
227
TEST_F(EmptyInitializedKvs,GetValue)228 TEST_F(EmptyInitializedKvs, GetValue) {
229 ASSERT_EQ(OkStatus(), kvs_.Put("key", uint32_t(0xfeedbeef)));
230
231 uint32_t value = 0;
232 EXPECT_EQ(OkStatus(), kvs_.Get("key", &value));
233 EXPECT_EQ(uint32_t(0xfeedbeef), value);
234 }
235
TEST_F(EmptyInitializedKvs,GetValue_TooSmall)236 TEST_F(EmptyInitializedKvs, GetValue_TooSmall) {
237 ASSERT_EQ(OkStatus(), kvs_.Put("key", uint32_t(0xfeedbeef)));
238
239 uint8_t value = 0;
240 EXPECT_EQ(Status::InvalidArgument(), kvs_.Get("key", &value));
241 EXPECT_EQ(0u, value);
242 }
243
TEST_F(EmptyInitializedKvs,GetValue_TooLarge)244 TEST_F(EmptyInitializedKvs, GetValue_TooLarge) {
245 ASSERT_EQ(OkStatus(), kvs_.Put("key", uint32_t(0xfeedbeef)));
246
247 uint64_t value = 0;
248 EXPECT_EQ(Status::InvalidArgument(), kvs_.Get("key", &value));
249 EXPECT_EQ(0u, value);
250 }
251
TEST_F(EmptyInitializedKvs,Delete_GetDeletedKey_ReturnsNotFound)252 TEST_F(EmptyInitializedKvs, Delete_GetDeletedKey_ReturnsNotFound) {
253 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
254 ASSERT_EQ(OkStatus(), kvs_.Delete("kEy"));
255
256 EXPECT_EQ(Status::NotFound(), kvs_.Get("kEy", {}).status());
257 EXPECT_EQ(Status::NotFound(), kvs_.ValueSize("kEy").status());
258 }
259
TEST_F(EmptyInitializedKvs,Delete_AddBackKey_PersistsAfterInitialization)260 TEST_F(EmptyInitializedKvs, Delete_AddBackKey_PersistsAfterInitialization) {
261 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
262 ASSERT_EQ(OkStatus(), kvs_.Delete("kEy"));
263
264 EXPECT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("45678"))));
265 char data[6] = {};
266 ASSERT_EQ(OkStatus(), kvs_.Get("kEy", &data));
267 EXPECT_STREQ(data, "45678");
268
269 // Ensure that the re-added key is still present after reinitialization.
270 KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> new_kvs(&test_partition,
271 default_format);
272 ASSERT_EQ(OkStatus(), new_kvs.Init());
273
274 EXPECT_EQ(OkStatus(), new_kvs.Put("kEy", as_bytes(span("45678"))));
275 char new_data[6] = {};
276 EXPECT_EQ(OkStatus(), new_kvs.Get("kEy", &new_data));
277 EXPECT_STREQ(data, "45678");
278 }
279
TEST_F(EmptyInitializedKvs,Delete_AllItems_KvsIsEmpty)280 TEST_F(EmptyInitializedKvs, Delete_AllItems_KvsIsEmpty) {
281 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
282 ASSERT_EQ(OkStatus(), kvs_.Delete("kEy"));
283
284 EXPECT_EQ(0u, kvs_.size());
285 EXPECT_TRUE(kvs_.empty());
286 }
287
TEST_F(EmptyInitializedKvs,Collision_WithPresentKey)288 TEST_F(EmptyInitializedKvs, Collision_WithPresentKey) {
289 // Both hash to 0x19df36f0.
290 constexpr std::string_view key1 = "D4";
291 constexpr std::string_view key2 = "dFU6S";
292
293 ASSERT_EQ(OkStatus(), kvs_.Put(key1, 1000));
294
295 EXPECT_EQ(Status::AlreadyExists(), kvs_.Put(key2, 999));
296
297 int value = 0;
298 EXPECT_EQ(OkStatus(), kvs_.Get(key1, &value));
299 EXPECT_EQ(1000, value);
300
301 EXPECT_EQ(Status::NotFound(), kvs_.Get(key2, &value));
302 EXPECT_EQ(Status::NotFound(), kvs_.ValueSize(key2).status());
303 EXPECT_EQ(Status::NotFound(), kvs_.Delete(key2));
304 }
305
TEST_F(EmptyInitializedKvs,Collision_WithDeletedKey)306 TEST_F(EmptyInitializedKvs, Collision_WithDeletedKey) {
307 // Both hash to 0x4060f732.
308 constexpr std::string_view key1 = "1U2";
309 constexpr std::string_view key2 = "ahj9d";
310
311 ASSERT_EQ(OkStatus(), kvs_.Put(key1, 1000));
312 ASSERT_EQ(OkStatus(), kvs_.Delete(key1));
313
314 // key2 collides with key1's tombstone.
315 EXPECT_EQ(Status::AlreadyExists(), kvs_.Put(key2, 999));
316
317 int value = 0;
318 EXPECT_EQ(Status::NotFound(), kvs_.Get(key1, &value));
319
320 EXPECT_EQ(Status::NotFound(), kvs_.Get(key2, &value));
321 EXPECT_EQ(Status::NotFound(), kvs_.ValueSize(key2).status());
322 EXPECT_EQ(Status::NotFound(), kvs_.Delete(key2));
323 }
324
TEST_F(EmptyInitializedKvs,Iteration_Empty_ByReference)325 TEST_F(EmptyInitializedKvs, Iteration_Empty_ByReference) {
326 for (const KeyValueStore::Item& entry : kvs_) {
327 FAIL(); // The KVS is empty; this shouldn't execute.
328 static_cast<void>(entry);
329 }
330 }
331
TEST_F(EmptyInitializedKvs,Iteration_Empty_ByValue)332 TEST_F(EmptyInitializedKvs, Iteration_Empty_ByValue) {
333 for (KeyValueStore::Item entry : kvs_) {
334 FAIL(); // The KVS is empty; this shouldn't execute.
335 static_cast<void>(entry);
336 }
337 }
338
TEST_F(EmptyInitializedKvs,Iteration_OneItem)339 TEST_F(EmptyInitializedKvs, Iteration_OneItem) {
340 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
341
342 for (KeyValueStore::Item entry : kvs_) {
343 EXPECT_STREQ(entry.key(), "kEy"); // Make sure null-terminated.
344
345 char temp[sizeof("123")] = {};
346 EXPECT_EQ(OkStatus(), entry.Get(&temp));
347 EXPECT_STREQ("123", temp);
348 }
349 }
350
TEST_F(EmptyInitializedKvs,Iteration_GetWithOffset)351 TEST_F(EmptyInitializedKvs, Iteration_GetWithOffset) {
352 ASSERT_EQ(OkStatus(), kvs_.Put("key", as_bytes(span("not bad!"))));
353
354 for (KeyValueStore::Item entry : kvs_) {
355 char temp[5];
356 auto result = entry.Get(as_writable_bytes(span(temp)), 4);
357 EXPECT_EQ(OkStatus(), result.status());
358 EXPECT_EQ(5u, result.size());
359 EXPECT_STREQ("bad!", temp);
360 }
361 }
362
TEST_F(EmptyInitializedKvs,Iteration_GetValue)363 TEST_F(EmptyInitializedKvs, Iteration_GetValue) {
364 ASSERT_EQ(OkStatus(), kvs_.Put("key", uint32_t(0xfeedbeef)));
365
366 for (KeyValueStore::Item entry : kvs_) {
367 uint32_t value = 0;
368 EXPECT_EQ(OkStatus(), entry.Get(&value));
369 EXPECT_EQ(uint32_t(0xfeedbeef), value);
370 }
371 }
372
TEST_F(EmptyInitializedKvs,Iteration_GetValue_TooSmall)373 TEST_F(EmptyInitializedKvs, Iteration_GetValue_TooSmall) {
374 ASSERT_EQ(OkStatus(), kvs_.Put("key", uint32_t(0xfeedbeef)));
375
376 for (KeyValueStore::Item entry : kvs_) {
377 uint8_t value = 0;
378 EXPECT_EQ(Status::InvalidArgument(), entry.Get(&value));
379 EXPECT_EQ(0u, value);
380 }
381 }
382
TEST_F(EmptyInitializedKvs,Iteration_GetValue_TooLarge)383 TEST_F(EmptyInitializedKvs, Iteration_GetValue_TooLarge) {
384 ASSERT_EQ(OkStatus(), kvs_.Put("key", uint32_t(0xfeedbeef)));
385
386 for (KeyValueStore::Item entry : kvs_) {
387 uint64_t value = 0;
388 EXPECT_EQ(Status::InvalidArgument(), entry.Get(&value));
389 EXPECT_EQ(0u, value);
390 }
391 }
392
TEST_F(EmptyInitializedKvs,Iteration_EmptyAfterDeletion)393 TEST_F(EmptyInitializedKvs, Iteration_EmptyAfterDeletion) {
394 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
395 ASSERT_EQ(OkStatus(), kvs_.Delete("kEy"));
396
397 for (KeyValueStore::Item entry : kvs_) {
398 static_cast<void>(entry);
399 FAIL();
400 }
401 }
402
TEST_F(EmptyInitializedKvs,Iterator)403 TEST_F(EmptyInitializedKvs, Iterator) {
404 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
405
406 for (KeyValueStore::iterator it = kvs_.begin(); it != kvs_.end(); ++it) {
407 EXPECT_STREQ(it->key(), "kEy");
408
409 char temp[sizeof("123")] = {};
410 EXPECT_EQ(OkStatus(), it->Get(&temp));
411 EXPECT_STREQ("123", temp);
412 }
413 }
414
TEST_F(EmptyInitializedKvs,Iterator_PostIncrement)415 TEST_F(EmptyInitializedKvs, Iterator_PostIncrement) {
416 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
417
418 KeyValueStore::iterator it = kvs_.begin();
419 EXPECT_EQ(it++, kvs_.begin());
420 EXPECT_EQ(it, kvs_.end());
421 }
422
TEST_F(EmptyInitializedKvs,Iterator_PreIncrement)423 TEST_F(EmptyInitializedKvs, Iterator_PreIncrement) {
424 ASSERT_EQ(OkStatus(), kvs_.Put("kEy", as_bytes(span("123"))));
425
426 KeyValueStore::iterator it = kvs_.begin();
427 EXPECT_EQ(++it, kvs_.end());
428 EXPECT_EQ(it, kvs_.end());
429 }
430
TEST_F(EmptyInitializedKvs,Basic)431 TEST_F(EmptyInitializedKvs, Basic) {
432 // Add some data
433 uint8_t value1 = 0xDA;
434 ASSERT_EQ(OkStatus(),
435 kvs_.Put(keys[0], as_bytes(span(&value1, sizeof(value1)))));
436
437 uint32_t value2 = 0xBAD0301f;
438 ASSERT_EQ(OkStatus(), kvs_.Put(keys[1], value2));
439
440 // Verify data
441 uint32_t test2;
442 EXPECT_EQ(OkStatus(), kvs_.Get(keys[1], &test2));
443 uint8_t test1;
444 ASSERT_EQ(OkStatus(), kvs_.Get(keys[0], &test1));
445
446 EXPECT_EQ(test1, value1);
447 EXPECT_EQ(test2, value2);
448
449 // Delete a key
450 EXPECT_EQ(OkStatus(), kvs_.Delete(keys[0]));
451
452 // Verify it was erased
453 EXPECT_EQ(kvs_.Get(keys[0], &test1), Status::NotFound());
454 test2 = 0;
455 ASSERT_EQ(
456 OkStatus(),
457 kvs_.Get(keys[1], span(reinterpret_cast<byte*>(&test2), sizeof(test2)))
458 .status());
459 EXPECT_EQ(test2, value2);
460
461 // Delete other key
462 ASSERT_EQ(OkStatus(), kvs_.Delete(keys[1]));
463
464 // Verify it was erased
465 EXPECT_EQ(kvs_.size(), 0u);
466 }
467
468 } // namespace pw::kvs
469