• 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 #define DUMP_KVS_STATE_TO_FILE 0
16 #define USE_MEMORY_BUFFER 1
17 
18 #include "pw_kvs/key_value_store.h"
19 
20 #include <array>
21 #include <cstdio>
22 #include <cstring>
23 
24 #include "pw_span/span.h"
25 
26 #if DUMP_KVS_STATE_TO_FILE
27 #include <vector>
28 #endif  // DUMP_KVS_STATE_TO_FILE
29 
30 #include "pw_assert/check.h"
31 #include "pw_bytes/array.h"
32 #include "pw_checksum/crc16_ccitt.h"
33 #include "pw_kvs/crc16_checksum.h"
34 #include "pw_kvs/fake_flash_memory.h"
35 #include "pw_kvs/flash_memory.h"
36 #include "pw_kvs/internal/entry.h"
37 #include "pw_kvs_private/config.h"
38 #include "pw_log/log.h"
39 #include "pw_status/status.h"
40 #include "pw_string/string_builder.h"
41 #include "pw_unit_test/framework.h"
42 
43 namespace pw::kvs {
44 namespace {
45 
46 using internal::EntryHeader;
47 
48 constexpr size_t kMaxEntries = 256;
49 constexpr size_t kMaxUsableSectors = 256;
50 
51 // This is a self contained flash unit with both memory and a single partition.
52 template <uint32_t kSectorSizeBytes, uint16_t kSectorCount>
53 struct FlashWithPartitionFake {
54   // Default to 16 byte alignment, which is common in practice.
FlashWithPartitionFakepw::kvs::__anon4de83e880111::FlashWithPartitionFake55   FlashWithPartitionFake() : FlashWithPartitionFake(16) {}
FlashWithPartitionFakepw::kvs::__anon4de83e880111::FlashWithPartitionFake56   FlashWithPartitionFake(size_t alignment_bytes)
57       : memory(alignment_bytes), partition(&memory, 0, memory.sector_count()) {}
58 
59   FakeFlashMemoryBuffer<kSectorSizeBytes, kSectorCount> memory;
60   FlashPartition partition;
61 
62  public:
63 #if DUMP_KVS_STATE_TO_FILE
Dumppw::kvs::__anon4de83e880111::FlashWithPartitionFake64   Status Dump(const char* filename) {
65     std::FILE* out_file = std::fopen(filename, "w+");
66     if (out_file == nullptr) {
67       PW_LOG_ERROR("Failed to dump to %s", filename);
68       return Status::DataLoss();
69     }
70     std::vector<std::byte> out_vec(memory.size_bytes());
71     Status status =
72         memory.Read(0, span<std::byte>(out_vec.data(), out_vec.size()));
73     if (status != OkStatus()) {
74       fclose(out_file);
75       return status;
76     }
77 
78     size_t written =
79         std::fwrite(out_vec.data(), 1, memory.size_bytes(), out_file);
80     if (written != memory.size_bytes()) {
81       PW_LOG_ERROR("Failed to dump to %s, written=%u",
82                    filename,
83                    static_cast<unsigned>(written));
84       status = Status::DataLoss();
85     } else {
86       PW_LOG_INFO("Dumped to %s", filename);
87       status = OkStatus();
88     }
89 
90     fclose(out_file);
91     return status;
92   }
93 #else
94   Status Dump(const char*) { return OkStatus(); }
95 #endif  // DUMP_KVS_STATE_TO_FILE
96 };
97 
98 typedef FlashWithPartitionFake<4 * 128 /*sector size*/, 6 /*sectors*/> Flash;
99 
100 FakeFlashMemoryBuffer<1024, 60> large_test_flash(8);
101 FlashPartition large_test_partition(&large_test_flash,
102                                     0,
103                                     large_test_flash.sector_count());
104 
105 constexpr std::array<const char*, 3> keys{"TestKey1", "Key2", "TestKey3"};
106 
107 ChecksumCrc16 checksum;
108 // For KVS magic value always use a random 32 bit integer rather than a
109 // human readable 4 bytes. See pw_kvs/format.h for more information.
110 constexpr EntryFormat default_format{.magic = 0xa6cb3c16,
111                                      .checksum = &checksum};
112 
113 }  // namespace
114 
TEST(InitCheck,TooFewSectors)115 TEST(InitCheck, TooFewSectors) {
116   // Use test flash with 1 x 4k sectors, 16 byte alignment
117   FakeFlashMemoryBuffer<4 * 1024, 1> test_flash(16);
118   FlashPartition test_partition(&test_flash, 0, test_flash.sector_count());
119 
120   // For KVS magic value always use a random 32 bit integer rather than a
121   // human readable 4 bytes. See pw_kvs/format.h for more information.
122   constexpr EntryFormat format{.magic = 0x89bb14d2, .checksum = nullptr};
123   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&test_partition,
124                                                           format);
125 
126   EXPECT_EQ(kvs.Init(), Status::FailedPrecondition());
127 }
128 
TEST(InitCheck,ZeroSectors)129 TEST(InitCheck, ZeroSectors) {
130   // Use test flash with 1 x 4k sectors, 16 byte alignment
131   FakeFlashMemoryBuffer<4 * 1024, 1> test_flash(16);
132 
133   // Set FlashPartition to have 0 sectors.
134   FlashPartition test_partition(&test_flash, 0, 0);
135 
136   // For KVS magic value always use a random 32 bit integer rather than a
137   // human readable 4 bytes. See pw_kvs/format.h for more information.
138   constexpr EntryFormat format{.magic = 0xd1da57c1, .checksum = nullptr};
139   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&test_partition,
140                                                           format);
141 
142   EXPECT_EQ(kvs.Init(), Status::FailedPrecondition());
143 }
144 
TEST(InitCheck,TooManySectors)145 TEST(InitCheck, TooManySectors) {
146   // Use test flash with 1 x 4k sectors, 16 byte alignment
147   FakeFlashMemoryBuffer<4 * 1024, 5> test_flash(16);
148 
149   // Set FlashPartition to have 0 sectors.
150   FlashPartition test_partition(&test_flash, 0, test_flash.sector_count());
151 
152   // For KVS magic value always use a random 32 bit integer rather than a
153   // human readable 4 bytes. See pw_kvs/format.h for more information.
154   constexpr EntryFormat format{.magic = 0x610f6d17, .checksum = nullptr};
155   KeyValueStoreBuffer<kMaxEntries, 2> kvs(&test_partition, format);
156 
157   EXPECT_EQ(kvs.Init(), Status::FailedPrecondition());
158 }
159 
TEST(InMemoryKvs,WriteOneKeyMultipleTimes)160 TEST(InMemoryKvs, WriteOneKeyMultipleTimes) {
161   // Create and erase the fake flash. It will persist across reloads.
162   Flash flash;
163   PW_TEST_ASSERT_OK(flash.partition.Erase());
164 
165   int num_reloads = 2;
166   for (int reload = 0; reload < num_reloads; ++reload) {
167     PW_LOG_DEBUG("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
168     PW_LOG_DEBUG("xxx                                      xxxx");
169     PW_LOG_DEBUG("xxx               Reload %2d              xxxx", reload);
170     PW_LOG_DEBUG("xxx                                      xxxx");
171     PW_LOG_DEBUG("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
172 
173     // Create and initialize the KVS. For KVS magic value always use a random 32
174     // bit integer rather than a human readable 4 bytes. See pw_kvs/format.h for
175     // more information.
176     constexpr EntryFormat format{.magic = 0x83a9257, .checksum = nullptr};
177     KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
178                                                             format);
179     PW_TEST_ASSERT_OK(kvs.Init());
180 
181     // Write the same entry many times.
182     const char* key = "abcd";
183     const size_t num_writes = 99;
184     uint32_t written_value;
185     EXPECT_EQ(kvs.size(), (reload == 0) ? 0 : 1u);
186     for (uint32_t i = 0; i < num_writes; ++i) {
187       PW_LOG_DEBUG(
188           "PUT #%zu for key %s with value %zu", size_t(i), key, size_t(i));
189 
190       written_value = i + 0xfc;  // Prevent accidental pass with zero.
191       PW_TEST_EXPECT_OK(kvs.Put(key, written_value));
192       EXPECT_EQ(kvs.size(), 1u);
193     }
194 
195     // Verify that we can read the value back.
196     PW_LOG_DEBUG("GET final value for key: %s", key);
197     uint32_t actual_value;
198     PW_TEST_EXPECT_OK(kvs.Get(key, &actual_value));
199     EXPECT_EQ(actual_value, written_value);
200 
201     char fname_buf[64] = {'\0'};
202     snprintf(&fname_buf[0],
203              sizeof(fname_buf),
204              "WriteOneKeyMultipleTimes_%d.bin",
205              reload);
206     ASSERT_EQ(OkStatus(), flash.Dump(fname_buf));
207   }
208 }
209 
TEST(InMemoryKvs,WritingMultipleKeysIncreasesSize)210 TEST(InMemoryKvs, WritingMultipleKeysIncreasesSize) {
211   // Create and erase the fake flash.
212   Flash flash;
213   PW_TEST_ASSERT_OK(flash.partition.Erase());
214 
215   // Create and initialize the KVS. For KVS magic value always use a random 32
216   // bit integer rather than a human readable 4 bytes. See pw_kvs/format.h for
217   // more information.
218   constexpr EntryFormat format{.magic = 0x2ed3a058, .checksum = nullptr};
219   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
220                                                           format);
221   PW_TEST_ASSERT_OK(kvs.Init());
222 
223   // Write the same entry many times.
224   const size_t num_writes = 10;
225   EXPECT_EQ(kvs.size(), 0u);
226   for (size_t i = 0; i < num_writes; ++i) {
227     StringBuffer<150> key;
228     key << "key_" << i;
229     PW_LOG_DEBUG("PUT #%zu for key %s with value %zu", i, key.c_str(), i);
230 
231     size_t value = i + 77;  // Prevent accidental pass with zero.
232     PW_TEST_EXPECT_OK(kvs.Put(key.view(), value));
233     EXPECT_EQ(kvs.size(), i + 1);
234   }
235   ASSERT_EQ(OkStatus(), flash.Dump("WritingMultipleKeysIncreasesSize.bin"));
236 }
237 
TEST(InMemoryKvs,WriteAndReadOneKey)238 TEST(InMemoryKvs, WriteAndReadOneKey) {
239   // Create and erase the fake flash.
240   Flash flash;
241   PW_TEST_ASSERT_OK(flash.partition.Erase());
242 
243   // Create and initialize the KVS.
244   // For KVS magic value always use a random 32 bit integer rather than a
245   // human readable 4 bytes. See pw_kvs/format.h for more information.
246   constexpr EntryFormat format{.magic = 0x5d70896, .checksum = nullptr};
247   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
248                                                           format);
249   PW_TEST_ASSERT_OK(kvs.Init());
250 
251   // Add one entry.
252   const char* key = "Key1";
253   PW_LOG_DEBUG("PUT value for key: %s", key);
254   uint8_t written_value = 0xDA;
255   PW_TEST_ASSERT_OK(kvs.Put(key, written_value));
256   EXPECT_EQ(kvs.size(), 1u);
257 
258   PW_LOG_DEBUG("GET value for key: %s", key);
259   uint8_t actual_value;
260   PW_TEST_ASSERT_OK(kvs.Get(key, &actual_value));
261   EXPECT_EQ(actual_value, written_value);
262 
263   EXPECT_EQ(kvs.size(), 1u);
264 }
265 
TEST(InMemoryKvs,WriteOneKeyValueMultipleTimes)266 TEST(InMemoryKvs, WriteOneKeyValueMultipleTimes) {
267   // Create and erase the fake flash.
268   Flash flash;
269   PW_TEST_ASSERT_OK(flash.partition.Erase());
270 
271   // Create and initialize the KVS.
272   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
273                                                           default_format);
274   PW_TEST_ASSERT_OK(kvs.Init());
275 
276   // Add one entry, with the same key and value, multiple times.
277   const char* key = "Key1";
278   uint8_t written_value = 0xDA;
279   for (int i = 0; i < 50; i++) {
280     PW_LOG_DEBUG("PUT [%d] value for key: %s", i, key);
281     PW_TEST_ASSERT_OK(kvs.Put(key, written_value));
282     EXPECT_EQ(kvs.size(), 1u);
283   }
284 
285   PW_LOG_DEBUG("GET value for key: %s", key);
286   uint8_t actual_value;
287   PW_TEST_ASSERT_OK(kvs.Get(key, &actual_value));
288   EXPECT_EQ(actual_value, written_value);
289 
290   // Verify that only one entry was written to the KVS.
291   EXPECT_EQ(kvs.size(), 1u);
292   EXPECT_EQ(kvs.transaction_count(), 1u);
293   KeyValueStore::StorageStats stats = kvs.GetStorageStats();
294   EXPECT_EQ(stats.reclaimable_bytes, 0u);
295 }
296 
TEST(InMemoryKvs,Basic)297 TEST(InMemoryKvs, Basic) {
298   const char* key1 = "Key1";
299   const char* key2 = "Key2";
300 
301   // Create and erase the fake flash.
302   Flash flash;
303   ASSERT_EQ(OkStatus(), flash.partition.Erase());
304 
305   // Create and initialize the KVS.
306   // For KVS magic value always use a random 32 bit integer rather than a
307   // human readable 4 bytes. See pw_kvs/format.h for more information.
308   constexpr EntryFormat format{.magic = 0x7bf19895, .checksum = nullptr};
309   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
310                                                           format);
311   PW_TEST_ASSERT_OK(kvs.Init());
312 
313   // Add two entries with different keys and values.
314   uint8_t value1 = 0xDA;
315   PW_TEST_ASSERT_OK(kvs.Put(key1, as_bytes(span(&value1, sizeof(value1)))));
316   EXPECT_EQ(kvs.size(), 1u);
317 
318   uint32_t value2 = 0xBAD0301f;
319   PW_TEST_ASSERT_OK(kvs.Put(key2, value2));
320   EXPECT_EQ(kvs.size(), 2u);
321 
322   // Verify data
323   uint32_t test2;
324   PW_TEST_EXPECT_OK(kvs.Get(key2, &test2));
325 
326   uint8_t test1;
327   PW_TEST_ASSERT_OK(kvs.Get(key1, &test1));
328 
329   EXPECT_EQ(test1, value1);
330   EXPECT_EQ(test2, value2);
331 
332   EXPECT_EQ(kvs.size(), 2u);
333 }
334 
TEST(InMemoryKvs,CallingEraseTwice_NothingWrittenToFlash)335 TEST(InMemoryKvs, CallingEraseTwice_NothingWrittenToFlash) {
336   // Create and erase the fake flash.
337   Flash flash;
338   ASSERT_EQ(OkStatus(), flash.partition.Erase());
339 
340   // Create and initialize the KVS.
341   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
342                                                           default_format);
343   PW_TEST_ASSERT_OK(kvs.Init());
344 
345   const uint8_t kValue = 0xDA;
346   ASSERT_EQ(OkStatus(), kvs.Put(keys[0], kValue));
347   ASSERT_EQ(OkStatus(), kvs.Delete(keys[0]));
348 
349   // Compare before / after checksums to verify that nothing was written.
350   const uint16_t crc = checksum::Crc16Ccitt::Calculate(flash.memory.buffer());
351 
352   EXPECT_EQ(kvs.Delete(keys[0]), Status::NotFound());
353 
354   EXPECT_EQ(crc, checksum::Crc16Ccitt::Calculate(flash.memory.buffer()));
355 }
356 
357 class LargeEmptyInitializedKvs : public ::testing::Test {
358  protected:
LargeEmptyInitializedKvs()359   LargeEmptyInitializedKvs() : kvs_(&large_test_partition, default_format) {
360     PW_CHECK_OK(large_test_partition.Erase());
361     PW_CHECK_OK(kvs_.Init());
362   }
363 
364   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_;
365 };
366 
TEST_F(LargeEmptyInitializedKvs,Basic)367 TEST_F(LargeEmptyInitializedKvs, Basic) {
368   const uint8_t kValue1 = 0xDA;
369   const uint8_t kValue2 = 0x12;
370   uint8_t value;
371   ASSERT_EQ(OkStatus(), kvs_.Put(keys[0], kValue1));
372   EXPECT_EQ(kvs_.size(), 1u);
373   ASSERT_EQ(OkStatus(), kvs_.Delete(keys[0]));
374   EXPECT_EQ(kvs_.Get(keys[0], &value), Status::NotFound());
375   ASSERT_EQ(OkStatus(), kvs_.Put(keys[1], kValue1));
376   ASSERT_EQ(OkStatus(), kvs_.Put(keys[2], kValue2));
377   ASSERT_EQ(OkStatus(), kvs_.Delete(keys[1]));
378   EXPECT_EQ(OkStatus(), kvs_.Get(keys[2], &value));
379   EXPECT_EQ(kValue2, value);
380   ASSERT_EQ(kvs_.Get(keys[1], &value), Status::NotFound());
381   EXPECT_EQ(kvs_.size(), 1u);
382 }
383 
TEST_F(LargeEmptyInitializedKvs,FullMaintenance)384 TEST_F(LargeEmptyInitializedKvs, FullMaintenance) {
385   const uint8_t kValue1 = 0xDA;
386   const uint8_t kValue2 = 0x12;
387 
388   // Write a key and write again with a different value, resulting in a stale
389   // entry from the first write.
390   ASSERT_EQ(OkStatus(), kvs_.Put(keys[0], kValue1));
391   ASSERT_EQ(OkStatus(), kvs_.Put(keys[0], kValue2));
392   EXPECT_EQ(kvs_.size(), 1u);
393 
394   KeyValueStore::StorageStats stats = kvs_.GetStorageStats();
395   EXPECT_EQ(stats.sector_erase_count, 0u);
396   EXPECT_GT(stats.reclaimable_bytes, 0u);
397 
398   // Do regular FullMaintenance, which should not touch the sector with valid
399   // data.
400   EXPECT_EQ(OkStatus(), kvs_.FullMaintenance());
401   stats = kvs_.GetStorageStats();
402   EXPECT_EQ(stats.sector_erase_count, 0u);
403   EXPECT_GT(stats.reclaimable_bytes, 0u);
404 
405   // Do aggressive FullMaintenance, which should GC the sector with valid data,
406   // resulting in no reclaimable bytes and an erased sector.
407   EXPECT_EQ(OkStatus(), kvs_.HeavyMaintenance());
408   stats = kvs_.GetStorageStats();
409   EXPECT_EQ(stats.sector_erase_count, 1u);
410   EXPECT_EQ(stats.reclaimable_bytes, 0u);
411 }
412 
TEST_F(LargeEmptyInitializedKvs,KeyDeletionMaintenance)413 TEST_F(LargeEmptyInitializedKvs, KeyDeletionMaintenance) {
414   const uint8_t kValue1 = 0xDA;
415   const uint8_t kValue2 = 0x12;
416   uint8_t val = 0;
417 
418   // Write and delete a key. The key should be gone, but the size should be 1.
419   ASSERT_EQ(OkStatus(), kvs_.Put(keys[0], kValue1));
420   ASSERT_EQ(kvs_.size(), 1u);
421   ASSERT_EQ(OkStatus(), kvs_.Delete(keys[0]));
422 
423   // Ensure the key is indeed gone and the size is 1 before continuing.
424   ASSERT_EQ(kvs_.Get(keys[0], &val), Status::NotFound());
425   ASSERT_EQ(kvs_.size(), 0u);
426   ASSERT_EQ(kvs_.total_entries_with_deleted(), 1u);
427 
428   KeyValueStore::StorageStats stats = kvs_.GetStorageStats();
429   EXPECT_EQ(stats.sector_erase_count, 0u);
430   EXPECT_GT(stats.reclaimable_bytes, 0u);
431 
432   // Do aggressive FullMaintenance, which should GC the sector with valid data,
433   // resulting in no reclaimable bytes and an erased sector.
434   EXPECT_EQ(OkStatus(), kvs_.HeavyMaintenance());
435   stats = kvs_.GetStorageStats();
436   EXPECT_EQ(stats.reclaimable_bytes, 0u);
437   ASSERT_EQ(kvs_.size(), 0u);
438 
439   if (PW_KVS_REMOVE_DELETED_KEYS_IN_HEAVY_MAINTENANCE) {
440     EXPECT_GT(stats.sector_erase_count, 1u);
441     ASSERT_EQ(kvs_.total_entries_with_deleted(), 0u);
442   } else {  // The deleted entries are only removed if that feature is enabled.
443     EXPECT_EQ(stats.sector_erase_count, 1u);
444     ASSERT_EQ(kvs_.total_entries_with_deleted(), 1u);
445   }
446 
447   // Do it again but with 2 keys and keep one.
448   ASSERT_EQ(OkStatus(), kvs_.Put(keys[0], kValue1));
449   ASSERT_EQ(OkStatus(), kvs_.Put(keys[1], kValue2));
450   ASSERT_EQ(kvs_.size(), 2u);
451   ASSERT_EQ(OkStatus(), kvs_.Delete(keys[0]));
452 
453   // Ensure the key is indeed gone and the size is 1 before continuing.
454   ASSERT_EQ(kvs_.Get(keys[0], &val), Status::NotFound());
455   ASSERT_EQ(kvs_.size(), 1u);
456   ASSERT_EQ(kvs_.total_entries_with_deleted(), 2u);
457 
458   // Do aggressive FullMaintenance, which should GC the sector with valid data,
459   // resulting in no reclaimable bytes and an erased sector.
460   EXPECT_EQ(OkStatus(), kvs_.HeavyMaintenance());
461   stats = kvs_.GetStorageStats();
462   ASSERT_EQ(kvs_.size(), 1u);
463 
464   if (PW_KVS_REMOVE_DELETED_KEYS_IN_HEAVY_MAINTENANCE) {
465     ASSERT_EQ(kvs_.total_entries_with_deleted(), 1u);
466   } else {  // The deleted entries are only removed if that feature is enabled.
467     ASSERT_EQ(kvs_.total_entries_with_deleted(), 2u);
468   }
469 
470   // Read back the second key to make sure it is still valid.
471   ASSERT_EQ(kvs_.Get(keys[1], &val), OkStatus());
472   ASSERT_EQ(val, kValue2);
473 }
474 
TEST(InMemoryKvs,Put_MaxValueSize)475 TEST(InMemoryKvs, Put_MaxValueSize) {
476   // Create and erase the fake flash.
477   Flash flash;
478   ASSERT_EQ(OkStatus(), flash.partition.Erase());
479 
480   // Create and initialize the KVS.
481   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs(&flash.partition,
482                                                           default_format);
483   PW_TEST_ASSERT_OK(kvs.Init());
484 
485   size_t max_key_value_size = kvs.max_key_value_size_bytes();
486   EXPECT_EQ(max_key_value_size,
487             KeyValueStore::max_key_value_size_bytes(
488                 flash.partition.sector_size_bytes()));
489 
490   size_t max_value_size =
491       flash.partition.sector_size_bytes() - sizeof(EntryHeader) - 1;
492   EXPECT_EQ(max_key_value_size, (max_value_size + 1));
493 
494   // Use the large_test_flash as a big chunk of data for the Put statement.
495   ASSERT_GT(sizeof(large_test_flash), max_value_size + 2 * sizeof(EntryHeader));
496   auto big_data = as_bytes(span(&large_test_flash, 1));
497 
498   EXPECT_EQ(OkStatus(), kvs.Put("K", big_data.subspan(0, max_value_size)));
499 
500   // Larger than maximum is rejected.
501   EXPECT_EQ(Status::InvalidArgument(),
502             kvs.Put("K", big_data.subspan(0, max_value_size + 1)));
503   EXPECT_EQ(Status::InvalidArgument(), kvs.Put("K", big_data));
504 }
505 
506 }  // namespace pw::kvs
507