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