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