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