• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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