• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 <cstring>
17 
18 #include "pw_checksum/crc16_ccitt.h"
19 #include "pw_kvs/crc16_checksum.h"
20 #include "pw_kvs/flash_memory.h"
21 #include "pw_kvs/flash_test_partition.h"
22 #include "pw_kvs/key_value_store.h"
23 #include "pw_kvs_private/config.h"
24 #include "pw_log/log.h"
25 #include "pw_span/span.h"
26 #include "pw_status/status.h"
27 #include "pw_string/string_builder.h"
28 #include "pw_unit_test/framework.h"
29 
30 namespace pw::kvs {
31 namespace {
32 
33 using std::byte;
34 
35 constexpr size_t kMaxEntries = 256;
36 constexpr size_t kMaxUsableSectors = 1024;
37 
38 constexpr std::array<const char*, 3> keys{"TestKey1", "Key2", "TestKey3"};
39 
40 ChecksumCrc16 checksum;
41 // For KVS magic value always use a random 32 bit integer rather than a
42 // human readable 4 bytes. See pw_kvs/format.h for more information.
43 constexpr EntryFormat default_format{.magic = 0x749c361e,
44                                      .checksum = &checksum};
45 }  // namespace
46 
TEST(KvsFuzz,FuzzTest)47 TEST(KvsFuzz, FuzzTest) {
48   FlashPartition& test_partition = FlashTestPartition();
49   ASSERT_EQ(OkStatus(), test_partition.Erase());
50 
51   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_(&test_partition,
52                                                            default_format);
53 
54   ASSERT_EQ(OkStatus(), kvs_.Init());
55 
56   if (test_partition.sector_size_bytes() < 4 * 1024 ||
57       test_partition.sector_count() < 4) {
58     PW_LOG_INFO("Sectors too small, skipping test.");
59     return;  // TODO(davidrogers): Test could be generalized
60   }
61   const char* key1 = "Buf1";
62   const char* key2 = "Buf2";
63   const size_t kLargestBufSize = 3 * 1024;
64   static byte buf1[kLargestBufSize];
65   static byte buf2[kLargestBufSize];
66   std::memset(buf1, 1, sizeof(buf1));
67   std::memset(buf2, 2, sizeof(buf2));
68 
69   // Start with things in KVS
70   ASSERT_EQ(OkStatus(), kvs_.Put(key1, buf1));
71   ASSERT_EQ(OkStatus(), kvs_.Put(key2, buf2));
72   for (size_t j = 0; j < keys.size(); j++) {
73     ASSERT_EQ(OkStatus(), kvs_.Put(keys[j], j));
74   }
75 
76   for (size_t i = 0; i < 100; i++) {
77     // Vary two sizes
78     size_t size1 = (kLargestBufSize) / (i + 1);
79     size_t size2 = (kLargestBufSize) / (100 - i);
80     for (size_t j = 0; j < 50; j++) {
81       // Rewrite a single key many times, can fill up a sector
82       ASSERT_EQ(OkStatus(), kvs_.Put("some_data", j));
83     }
84 
85     // Delete and re-add everything except "some_data"
86     ASSERT_EQ(OkStatus(), kvs_.Delete(key1));
87     ASSERT_EQ(OkStatus(), kvs_.Put(key1, span(buf1, size1)));
88     ASSERT_EQ(OkStatus(), kvs_.Delete(key2));
89     ASSERT_EQ(OkStatus(), kvs_.Put(key2, span(buf2, size2)));
90     for (size_t j = 0; j < keys.size(); j++) {
91       ASSERT_EQ(OkStatus(), kvs_.Delete(keys[j]));
92       ASSERT_EQ(OkStatus(), kvs_.Put(keys[j], j));
93     }
94 
95     // Re-enable and verify
96     ASSERT_EQ(OkStatus(), kvs_.Init());
97     static byte buf[4 * 1024];
98     ASSERT_EQ(OkStatus(), kvs_.Get(key1, span(buf, size1)).status());
99     ASSERT_EQ(std::memcmp(buf, buf1, size1), 0);
100     ASSERT_EQ(OkStatus(), kvs_.Get(key2, span(buf, size2)).status());
101     ASSERT_EQ(std::memcmp(buf2, buf2, size2), 0);
102     for (size_t j = 0; j < keys.size(); j++) {
103       size_t ret = 1000;
104       ASSERT_EQ(OkStatus(), kvs_.Get(keys[j], &ret));
105       ASSERT_EQ(ret, j);
106     }
107   }
108 }
109 
TEST(KvsFuzz,FuzzTestWithGC)110 TEST(KvsFuzz, FuzzTestWithGC) {
111   FlashPartition& test_partition = FlashTestPartition();
112   ASSERT_EQ(OkStatus(), test_partition.Erase());
113 
114   KeyValueStoreBuffer<kMaxEntries, kMaxUsableSectors> kvs_(&test_partition,
115                                                            default_format);
116 
117   ASSERT_EQ(OkStatus(), kvs_.Init());
118 
119   if (test_partition.sector_size_bytes() < 4 * 1024 ||
120       test_partition.sector_count() < 4) {
121     PW_LOG_INFO("Sectors too small, skipping test.");
122     return;  // TODO(drempel): Test could be generalized
123   }
124   const char* key1 = "Buf1";
125   const char* key2 = "Buf2";
126   const size_t kLargestBufSize = 3 * 1024;
127   static byte buf1[kLargestBufSize];
128   static byte buf2[kLargestBufSize];
129   std::memset(buf1, 1, sizeof(buf1));
130   std::memset(buf2, 2, sizeof(buf2));
131 
132   // Start with things in KVS
133   ASSERT_EQ(OkStatus(), kvs_.Put(key1, buf1));
134   ASSERT_EQ(OkStatus(), kvs_.Put(key2, buf2));
135   for (size_t j = 0; j < keys.size(); j++) {
136     ASSERT_EQ(OkStatus(), kvs_.Put(keys[j], j));
137   }
138 
139   for (size_t i = 0; i < 100; i++) {
140     // Vary two sizes
141     size_t size1 = (kLargestBufSize) / (i + 1);
142     size_t size2 = (kLargestBufSize) / (100 - i);
143     for (size_t j = 0; j < 50; j++) {
144       // Rewrite a single key many times, can fill up a sector
145       ASSERT_EQ(OkStatus(), kvs_.Put("some_data", j));
146     }
147 
148     // Delete and re-add everything except "some_data".
149     ASSERT_EQ(OkStatus(), kvs_.Delete(key1));
150     ASSERT_EQ(OkStatus(), kvs_.Put(key1, span(buf1, size1)));
151     ASSERT_EQ(OkStatus(), kvs_.Delete(key2));
152 
153     // Throw some heavy maintenance in the middle to trigger some GC before
154     // moving forward.
155     EXPECT_EQ(OkStatus(), kvs_.HeavyMaintenance());
156 
157     // check for expected stats
158     KeyValueStore::StorageStats stats = kvs_.GetStorageStats();
159     EXPECT_GT(stats.sector_erase_count, 1u);
160     EXPECT_EQ(stats.reclaimable_bytes, 0u);
161 
162     if (!PW_KVS_REMOVE_DELETED_KEYS_IN_HEAVY_MAINTENANCE) {
163       PW_LOG_INFO(
164           "PW_KVS_REMOVE_DELETED_KEYS_IN_HEAVY_MAINTENANCE is "
165           "disabled; skipping remainder of test");
166       return;
167     }
168 
169     // Write out rotating keyvalue, read it, and delete kMaxEntries * 4.
170     // This tests whether garbage collection is working on write.
171     for (size_t j = 0; j < kMaxEntries * 4; j++) {
172       size_t readj;
173       StringBuffer<6> keyVal;
174       keyVal << j;
175       ASSERT_EQ(OkStatus(), kvs_.Put(keyVal.c_str(), j));
176       ASSERT_EQ(OkStatus(), kvs_.Get(keyVal.c_str(), &readj));
177       ASSERT_EQ(j, readj);
178       ASSERT_EQ(OkStatus(), kvs_.Delete(keyVal.c_str()));
179       ASSERT_EQ(Status::NotFound(), kvs_.Get(keyVal.c_str(), &readj));
180     }
181 
182     // The KVS should contain key1, "some_data", and all of keys[].
183     ASSERT_EQ(kvs_.size(), 2u + keys.size());
184 
185     ASSERT_EQ(OkStatus(), kvs_.Put(key2, span(buf2, size2)));
186     for (size_t j = 0; j < keys.size(); j++) {
187       ASSERT_EQ(OkStatus(), kvs_.Delete(keys[j]));
188       ASSERT_EQ(OkStatus(), kvs_.Put(keys[j], j));
189     }
190 
191     // Do some more heavy maintenance, ensure we have the right number
192     // of keys.
193     EXPECT_EQ(OkStatus(), kvs_.HeavyMaintenance());
194     // The KVS should contain key1, key2, "some_data", and all of keys[].
195     ASSERT_EQ(kvs_.size(), 3u + keys.size());
196 
197     // Re-enable and verify (final check on store).
198     ASSERT_EQ(OkStatus(), kvs_.Init());
199     static byte buf[4 * 1024];
200     ASSERT_EQ(OkStatus(), kvs_.Get(key1, span(buf, size1)).status());
201     ASSERT_EQ(std::memcmp(buf, buf1, size1), 0);
202     ASSERT_EQ(OkStatus(), kvs_.Get(key2, span(buf, size2)).status());
203     ASSERT_EQ(std::memcmp(buf2, buf2, size2), 0);
204     for (size_t j = 0; j < keys.size(); j++) {
205       size_t ret = 1000;
206       ASSERT_EQ(OkStatus(), kvs_.Get(keys[j], &ret));
207       ASSERT_EQ(ret, j);
208     }
209   }
210 }
211 }  // namespace pw::kvs
212