blob: 76def54436b56caf166682d602c579a801dbafa5 [file] [log] [blame]
Wyatt Hepler97fc7942020-02-06 15:55:45 -08001// 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
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080015#include "pw_kvs/internal/entry.h"
Wyatt Heplerd31d9702020-02-14 08:46:36 -080016
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070017#include <span>
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080018#include <string_view>
19
Wyatt Hepler97fc7942020-02-06 15:55:45 -080020#include "gtest/gtest.h"
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -070021#include "pw_bytes/array.h"
Wyatt Heplere541e072020-02-14 09:10:53 -080022#include "pw_kvs/alignment.h"
Wyatt Hepler1d594562020-03-10 18:26:02 -070023#include "pw_kvs/checksum.h"
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080024#include "pw_kvs/crc16_checksum.h"
David Rogersd64cc012020-05-26 12:37:37 -070025#include "pw_kvs/fake_flash_memory.h"
Wyatt Hepler97fc7942020-02-06 15:55:45 -080026#include "pw_kvs/flash_memory.h"
Wyatt Hepler88adfe82020-02-20 19:33:27 -080027#include "pw_kvs/format.h"
Wyatt Hepler97fc7942020-02-06 15:55:45 -080028
Wyatt Hepler1fc11042020-02-19 17:17:51 -080029namespace pw::kvs::internal {
Wyatt Hepler97fc7942020-02-06 15:55:45 -080030namespace {
31
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080032using std::byte;
33using std::string_view;
Wyatt Heplere541e072020-02-14 09:10:53 -080034
David Rogers436b3aa2020-08-03 08:44:10 -070035// For magic value always use a random 32 bit integer rather than a human
36// readable 4 bytes. See pw_kvs/format.h for more information.
37constexpr EntryFormat kFormat{0x961c2ff9, nullptr};
Wyatt Hepler88adfe82020-02-20 19:33:27 -080038
Wyatt Heplere541e072020-02-14 09:10:53 -080039TEST(Entry, Size_RoundsUpToAlignment) {
David Rogersa149d642020-07-08 09:25:41 -070040 // Use FakeFlashMemory, rather than FakeFlashMemoryBuffer, so the class gets
41 // tested/used directly.
42 std::array<std::byte, 64 * 2> buffer;
David Rogers02892d22020-07-30 00:33:00 -070043
44 // Flash alignment needs to be 1 due to how the partition is used in this
45 // test.
46 FakeFlashMemory flash(buffer, 64, 2, 1);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080047
Wyatt Hepler97fc7942020-02-06 15:55:45 -080048 for (size_t alignment_bytes = 1; alignment_bytes <= 4096; ++alignment_bytes) {
Wyatt Hepler7465be32020-02-21 15:30:53 -080049 FlashPartition partition(&flash, 0, flash.sector_count(), alignment_bytes);
Wyatt Heplere541e072020-02-14 09:10:53 -080050 const size_t align = AlignUp(alignment_bytes, Entry::kMinAlignmentBytes);
51
52 for (size_t value : {size_t(0), align - 1, align, align + 1, 2 * align}) {
Wyatt Hepler7465be32020-02-21 15:30:53 -080053 Entry entry =
54 Entry::Valid(partition, 0, kFormat, "k", {nullptr, value}, 0);
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080055
Wyatt Heplere541e072020-02-14 09:10:53 -080056 ASSERT_EQ(AlignUp(sizeof(EntryHeader) + 1 /* key */ + value, align),
57 entry.size());
58 }
59
Wyatt Hepler7465be32020-02-21 15:30:53 -080060 Entry entry = Entry::Tombstone(partition, 0, kFormat, "k", 0);
Wyatt Heplere541e072020-02-14 09:10:53 -080061 ASSERT_EQ(AlignUp(sizeof(EntryHeader) + 1 /* key */, align), entry.size());
Wyatt Hepler97fc7942020-02-06 15:55:45 -080062 }
63}
64
Wyatt Heplere541e072020-02-14 09:10:53 -080065TEST(Entry, Construct_ValidEntry) {
David Rogersd64cc012020-05-26 12:37:37 -070066 FakeFlashMemoryBuffer<64, 2> flash(16);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080067 FlashPartition partition(&flash, 0, flash.sector_count());
68
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070069 auto entry = Entry::Valid(
70 partition, 1, kFormat, "k", std::as_bytes(std::span("123")), 9876);
Wyatt Hepler97fc7942020-02-06 15:55:45 -080071
72 EXPECT_FALSE(entry.deleted());
Wyatt Hepler88adfe82020-02-20 19:33:27 -080073 EXPECT_EQ(entry.magic(), kFormat.magic);
Wyatt Heplere541e072020-02-14 09:10:53 -080074 EXPECT_EQ(entry.value_size(), sizeof("123"));
Wyatt Hepler1fc11042020-02-19 17:17:51 -080075 EXPECT_EQ(entry.transaction_id(), 9876u);
Wyatt Hepler97fc7942020-02-06 15:55:45 -080076}
77
Wyatt Heplere541e072020-02-14 09:10:53 -080078TEST(Entry, Construct_Tombstone) {
David Rogersd64cc012020-05-26 12:37:37 -070079 FakeFlashMemoryBuffer<64, 2> flash(16);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080080 FlashPartition partition(&flash, 0, flash.sector_count());
81
Wyatt Hepler7465be32020-02-21 15:30:53 -080082 auto entry = Entry::Tombstone(partition, 1, kFormat, "key", 123);
Wyatt Hepler97fc7942020-02-06 15:55:45 -080083
84 EXPECT_TRUE(entry.deleted());
Wyatt Hepler88adfe82020-02-20 19:33:27 -080085 EXPECT_EQ(entry.magic(), kFormat.magic);
Wyatt Heplere541e072020-02-14 09:10:53 -080086 EXPECT_EQ(entry.value_size(), 0u);
Wyatt Hepler1fc11042020-02-19 17:17:51 -080087 EXPECT_EQ(entry.transaction_id(), 123u);
Wyatt Hepler97fc7942020-02-06 15:55:45 -080088}
89
David Rogers436b3aa2020-08-03 08:44:10 -070090// For magic value always use a unique random 32 bit integer rather than a human
91// readable 4 bytes. See pw_kvs/format.h for more information.
92constexpr uint32_t kMagicWithChecksum = 0xad165142;
Wyatt Hepler1d594562020-03-10 18:26:02 -070093constexpr uint32_t kTransactionId1 = 0x96979899;
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080094
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -070095constexpr auto kKey1 = bytes::String("key45");
96constexpr auto kValue1 = bytes::String("VALUE!");
97constexpr auto kPadding1 = bytes::String("\0\0\0\0\0");
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080098
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -070099constexpr auto kHeader1 = bytes::Concat(kMagicWithChecksum,
100 uint32_t(0x23aa), // checksum (CRC16)
101 uint8_t(1), // alignment (32 B)
102 uint8_t(kKey1.size()), // key length
103 uint16_t(kValue1.size()), // value size
104 kTransactionId1 // transaction ID
Wyatt Hepler1d594562020-03-10 18:26:02 -0700105);
106
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700107constexpr auto kEntryWithoutPadding1 = bytes::Concat(kHeader1, kKey1, kValue1);
108constexpr auto kEntry1 = bytes::Concat(kEntryWithoutPadding1, kPadding1);
Wyatt Hepler7465be32020-02-21 15:30:53 -0800109static_assert(kEntry1.size() == 32);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800110
Armando Montanez888370d2020-05-01 18:29:22 -0700111ChecksumCrc16 default_checksum;
112constexpr EntryFormat kFormatWithChecksum{kMagicWithChecksum,
113 &default_checksum};
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800114constexpr internal::EntryFormats kFormats(kFormatWithChecksum);
115
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800116class ValidEntryInFlash : public ::testing::Test {
117 protected:
118 ValidEntryInFlash() : flash_(kEntry1), partition_(&flash_) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800119 EXPECT_EQ(OkStatus(), Entry::Read(partition_, 0, kFormats, &entry_));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800120 }
121
David Rogersd64cc012020-05-26 12:37:37 -0700122 FakeFlashMemoryBuffer<1024, 4> flash_;
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800123 FlashPartition partition_;
124 Entry entry_;
125};
126
127TEST_F(ValidEntryInFlash, PassesChecksumVerification) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800128 EXPECT_EQ(OkStatus(), entry_.VerifyChecksumInFlash());
129 EXPECT_EQ(OkStatus(), entry_.VerifyChecksum("key45", kValue1));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800130}
131
132TEST_F(ValidEntryInFlash, HeaderContents) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800133 EXPECT_EQ(entry_.magic(), kMagicWithChecksum);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800134 EXPECT_EQ(entry_.key_length(), 5u);
135 EXPECT_EQ(entry_.value_size(), 6u);
Wyatt Hepler1d594562020-03-10 18:26:02 -0700136 EXPECT_EQ(entry_.transaction_id(), kTransactionId1);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800137 EXPECT_FALSE(entry_.deleted());
138}
139
140TEST_F(ValidEntryInFlash, ReadKey) {
141 Entry::KeyBuffer key = {};
142 auto result = entry_.ReadKey(key);
143
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800144 ASSERT_EQ(OkStatus(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800145 EXPECT_EQ(result.size(), entry_.key_length());
146 EXPECT_STREQ(key.data(), "key45");
147}
148
149TEST_F(ValidEntryInFlash, ReadValue) {
150 char value[32] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700151 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800152
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800153 ASSERT_EQ(OkStatus(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800154 EXPECT_EQ(result.size(), entry_.value_size());
155 EXPECT_STREQ(value, "VALUE!");
156}
157
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800158TEST_F(ValidEntryInFlash, ReadValue_BufferTooSmall) {
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800159 char value[3] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700160 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800161
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700162 ASSERT_EQ(Status::ResourceExhausted(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800163 EXPECT_EQ(3u, result.size());
164 EXPECT_EQ(value[0], 'V');
165 EXPECT_EQ(value[1], 'A');
166 EXPECT_EQ(value[2], 'L');
167}
168
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800169TEST_F(ValidEntryInFlash, ReadValue_WithOffset) {
170 char value[3] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700171 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)), 3);
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800172
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800173 ASSERT_EQ(OkStatus(), result.status());
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800174 EXPECT_EQ(3u, result.size());
175 EXPECT_EQ(value[0], 'U');
176 EXPECT_EQ(value[1], 'E');
177 EXPECT_EQ(value[2], '!');
178}
179
180TEST_F(ValidEntryInFlash, ReadValue_WithOffset_BufferTooSmall) {
181 char value[1] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700182 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)), 4);
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800183
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700184 ASSERT_EQ(Status::ResourceExhausted(), result.status());
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800185 EXPECT_EQ(1u, result.size());
186 EXPECT_EQ(value[0], 'E');
187}
188
189TEST_F(ValidEntryInFlash, ReadValue_WithOffset_EmptyRead) {
190 char value[16] = {'?'};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700191 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)), 6);
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800192
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800193 ASSERT_EQ(OkStatus(), result.status());
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800194 EXPECT_EQ(0u, result.size());
195 EXPECT_EQ(value[0], '?');
196}
197
198TEST_F(ValidEntryInFlash, ReadValue_WithOffset_PastEnd) {
199 char value[16] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700200 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)), 7);
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800201
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700202 EXPECT_EQ(Status::OutOfRange(), result.status());
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800203 EXPECT_EQ(0u, result.size());
204}
205
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800206TEST(ValidEntry, Write) {
David Rogersd64cc012020-05-26 12:37:37 -0700207 FakeFlashMemoryBuffer<1024, 4> flash;
Wyatt Hepler7465be32020-02-21 15:30:53 -0800208 FlashPartition partition(&flash, 0, flash.sector_count(), 32);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800209
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800210 Entry entry = Entry::Valid(
David Rogers907570b2020-08-06 12:44:27 -0700211 partition, 64, kFormatWithChecksum, "key45", kValue1, kTransactionId1);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800212
213 auto result = entry.Write("key45", kValue1);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800214 EXPECT_EQ(OkStatus(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800215 EXPECT_EQ(32u, result.size());
David Rogers907570b2020-08-06 12:44:27 -0700216 EXPECT_EQ(std::memcmp(&flash.buffer()[64], kEntry1.data(), kEntry1.size()),
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800217 0);
218}
219
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700220constexpr auto kHeader2 = bytes::String(
David Rogers436b3aa2020-08-03 08:44:10 -0700221 "\x42\x51\x16\xad" // magic
222 "\xba\xb3\x00\x00" // checksum (CRC16)
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800223 "\x00" // alignment
224 "\x01" // key length
225 "\xff\xff" // value size
Wyatt Hepler1d594562020-03-10 18:26:02 -0700226 "\x00\x01\x02\x03" // transaction ID
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800227);
228
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700229constexpr auto kKeyAndPadding2 =
230 bytes::String("K\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800231
232class TombstoneEntryInFlash : public ::testing::Test {
233 protected:
234 TombstoneEntryInFlash()
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700235 : flash_(bytes::Concat(kHeader2, kKeyAndPadding2)), partition_(&flash_) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800236 EXPECT_EQ(OkStatus(), Entry::Read(partition_, 0, kFormats, &entry_));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800237 }
238
David Rogersd64cc012020-05-26 12:37:37 -0700239 FakeFlashMemoryBuffer<1024, 4> flash_;
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800240 FlashPartition partition_;
241 Entry entry_;
242};
243
244TEST_F(TombstoneEntryInFlash, PassesChecksumVerification) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800245 EXPECT_EQ(OkStatus(), entry_.VerifyChecksumInFlash());
246 EXPECT_EQ(OkStatus(), entry_.VerifyChecksum("K", {}));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800247}
248
249TEST_F(TombstoneEntryInFlash, HeaderContents) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800250 EXPECT_EQ(entry_.magic(), kMagicWithChecksum);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800251 EXPECT_EQ(entry_.key_length(), 1u);
252 EXPECT_EQ(entry_.value_size(), 0u);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800253 EXPECT_EQ(entry_.transaction_id(), 0x03020100u);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800254 EXPECT_TRUE(entry_.deleted());
255}
256
257TEST_F(TombstoneEntryInFlash, ReadKey) {
258 Entry::KeyBuffer key = {};
259 auto result = entry_.ReadKey(key);
260
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800261 ASSERT_EQ(OkStatus(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800262 EXPECT_EQ(result.size(), entry_.key_length());
263 EXPECT_STREQ(key.data(), "K");
264}
265
266TEST_F(TombstoneEntryInFlash, ReadValue) {
267 char value[32] = {};
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700268 auto result = entry_.ReadValue(std::as_writable_bytes(std::span(value)));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800269
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800270 ASSERT_EQ(OkStatus(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800271 EXPECT_EQ(0u, result.size());
272}
273
274TEST(TombstoneEntry, Write) {
David Rogersd64cc012020-05-26 12:37:37 -0700275 FakeFlashMemoryBuffer<1024, 4> flash;
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800276 FlashPartition partition(&flash);
277 ChecksumCrc16 checksum;
278
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800279 Entry entry =
280 Entry::Tombstone(partition, 16, kFormatWithChecksum, "K", 0x03020100);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800281
282 auto result = entry.Write("K", {});
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800283 EXPECT_EQ(OkStatus(), result.status());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800284 EXPECT_EQ(32u, result.size());
285 EXPECT_EQ(std::memcmp(&flash.buffer()[16],
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700286 bytes::Concat(kHeader2, kKeyAndPadding2).data(),
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800287 kEntry1.size()),
288 0);
289}
290
291TEST(Entry, Checksum_NoChecksumRequiresZero) {
David Rogersd64cc012020-05-26 12:37:37 -0700292 FakeFlashMemoryBuffer<1024, 4> flash(kEntry1);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800293 FlashPartition partition(&flash);
294 Entry entry;
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800295
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800296 const EntryFormat format{kMagicWithChecksum, nullptr};
297 const internal::EntryFormats formats(format);
298
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800299 ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, formats, &entry));
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800300
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700301 EXPECT_EQ(Status::DataLoss(), entry.VerifyChecksumInFlash());
302 EXPECT_EQ(Status::DataLoss(), entry.VerifyChecksum({}, {}));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800303
304 std::memset(&flash.buffer()[4], 0, 4); // set the checksum field to 0
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800305 ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, formats, &entry));
306 EXPECT_EQ(OkStatus(), entry.VerifyChecksumInFlash());
307 EXPECT_EQ(OkStatus(), entry.VerifyChecksum({}, {}));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800308}
309
310TEST(Entry, Checksum_ChecksPadding) {
David Rogersd64cc012020-05-26 12:37:37 -0700311 FakeFlashMemoryBuffer<1024, 4> flash(
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700312 bytes::Concat(kHeader1, kKey1, kValue1, bytes::String("\0\0\0\0\1")));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800313 FlashPartition partition(&flash);
314 Entry entry;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800315 ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, kFormats, &entry));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800316
317 // Last byte in padding is a 1; should fail.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700318 EXPECT_EQ(Status::DataLoss(), entry.VerifyChecksumInFlash());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800319
320 // The in-memory verification fills in 0s for the padding.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800321 EXPECT_EQ(OkStatus(), entry.VerifyChecksum("key45", kValue1));
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800322
323 flash.buffer()[kEntry1.size() - 1] = byte{0};
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800324 EXPECT_EQ(OkStatus(), entry.VerifyChecksumInFlash());
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800325}
Wyatt Hepler1d594562020-03-10 18:26:02 -0700326
Wyatt Hepler50070492020-04-23 21:50:34 -0700327TEST_F(ValidEntryInFlash, Update_SameFormat_TransactionIdIsUpdated) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800328 ASSERT_EQ(OkStatus(),
Wyatt Hepler1d594562020-03-10 18:26:02 -0700329 entry_.Update(kFormatWithChecksum, kTransactionId1 + 3));
330
331 EXPECT_EQ(kFormatWithChecksum.magic, entry_.magic());
332 EXPECT_EQ(0u, entry_.address());
333 EXPECT_EQ(kTransactionId1 + 3, entry_.transaction_id());
334 EXPECT_FALSE(entry_.deleted());
335}
336
Wyatt Hepler50070492020-04-23 21:50:34 -0700337TEST_F(ValidEntryInFlash,
338 Update_DifferentFormat_MagicAndTransactionIdAreUpdated) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800339 ASSERT_EQ(OkStatus(), entry_.Update(kFormat, kTransactionId1 + 6));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700340
341 EXPECT_EQ(kFormat.magic, entry_.magic());
342 EXPECT_EQ(0u, entry_.address());
343 EXPECT_EQ(kTransactionId1 + 6, entry_.transaction_id());
344 EXPECT_FALSE(entry_.deleted());
345}
346
Wyatt Hepler50070492020-04-23 21:50:34 -0700347TEST_F(ValidEntryInFlash, Update_ReadError_WithChecksumIsError) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700348 flash_.InjectReadError(FlashError::Unconditional(Status::Aborted()));
Wyatt Hepler486ac572020-03-12 15:15:22 -0700349
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700350 EXPECT_EQ(Status::Aborted(),
Wyatt Hepler486ac572020-03-12 15:15:22 -0700351 entry_.Update(kFormatWithChecksum, kTransactionId1 + 1));
352}
353
David Rogers436b3aa2020-08-03 08:44:10 -0700354// For magic value always use a random 32 bit integer rather than a human
355// readable 4 bytes. See pw_kvs/format.h for more information.
356constexpr EntryFormat kNoChecksumFormat{.magic = 0x721bad24,
Wyatt Hepler50070492020-04-23 21:50:34 -0700357 .checksum = nullptr};
358
359TEST_F(ValidEntryInFlash, Update_ReadError_NoChecksumIsOkay) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700360 flash_.InjectReadError(FlashError::Unconditional(Status::Aborted()));
Wyatt Hepler486ac572020-03-12 15:15:22 -0700361
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800362 EXPECT_EQ(OkStatus(), entry_.Update(kNoChecksumFormat, kTransactionId1 + 1));
Wyatt Hepler486ac572020-03-12 15:15:22 -0700363}
364
Wyatt Hepler50070492020-04-23 21:50:34 -0700365TEST_F(ValidEntryInFlash, Copy) {
Wyatt Hepler486ac572020-03-12 15:15:22 -0700366 auto result = entry_.Copy(123);
367
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800368 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler486ac572020-03-12 15:15:22 -0700369 EXPECT_EQ(entry_.size(), result.size());
370 EXPECT_EQ(0,
371 std::memcmp(
372 &flash_.buffer().data()[123], kEntry1.data(), kEntry1.size()));
373}
374
Wyatt Hepler50070492020-04-23 21:50:34 -0700375TEST_F(ValidEntryInFlash, Copy_ReadError) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700376 flash_.InjectReadError(FlashError::Unconditional(Status::Unimplemented()));
Wyatt Hepler486ac572020-03-12 15:15:22 -0700377 auto result = entry_.Copy(kEntry1.size());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700378 EXPECT_EQ(Status::Unimplemented(), result.status());
Wyatt Hepler486ac572020-03-12 15:15:22 -0700379 EXPECT_EQ(0u, result.size());
380}
381
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700382constexpr uint32_t ByteSum(std::span<const byte> bytes, uint32_t value = 0) {
Wyatt Hepler1d594562020-03-10 18:26:02 -0700383 for (byte b : bytes) {
384 value += unsigned(b);
385 }
386 return value;
387}
388
Wyatt Hepler50070492020-04-23 21:50:34 -0700389// Sums the bytes, adding one to each byte so that zeroes change the checksum.
David Rogers31b358b2020-04-15 05:00:50 -0700390class ChecksumSummation final : public ChecksumAlgorithm {
391 public:
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700392 ChecksumSummation()
393 : ChecksumAlgorithm(std::as_bytes(std::span(&sum_, 1))), sum_(0) {}
Wyatt Hepler1d594562020-03-10 18:26:02 -0700394
David Rogers31b358b2020-04-15 05:00:50 -0700395 void Reset() override { sum_ = 0; }
396
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700397 void Update(std::span<const byte> data) override {
Wyatt Hepler50070492020-04-23 21:50:34 -0700398 for (byte b : data) {
399 sum_ += unsigned(b) + 1; // Add 1 so zero-value bytes affect checksum.
Wyatt Hepler1d594562020-03-10 18:26:02 -0700400 }
David Rogers31b358b2020-04-15 05:00:50 -0700401 }
Wyatt Hepler1d594562020-03-10 18:26:02 -0700402
David Rogers31b358b2020-04-15 05:00:50 -0700403 private:
404 uint32_t sum_;
405} sum_checksum;
Wyatt Hepler1d594562020-03-10 18:26:02 -0700406
David Rogers436b3aa2020-08-03 08:44:10 -0700407// For magic value always use a random 32 bit integer rather than a human
408// readable 4 bytes. See pw_kvs/format.h for more information.
409constexpr uint32_t kMagicWithSum = 0x6093aadb;
David Rogers31b358b2020-04-15 05:00:50 -0700410constexpr EntryFormat kFormatWithSum{kMagicWithSum, &sum_checksum};
411constexpr internal::EntryFormats kFormatsWithSum(kFormatWithSum);
Wyatt Hepler1d594562020-03-10 18:26:02 -0700412
Wyatt Heplerb8c67ac2021-08-12 09:44:21 -0700413template <size_t kAlignment>
Wyatt Hepler50070492020-04-23 21:50:34 -0700414constexpr auto MakeNewFormatWithSumEntry() {
Wyatt Heplerb8c67ac2021-08-12 09:44:21 -0700415 constexpr uint8_t alignment_units = (kAlignment + 15) / 16 - 1;
416 constexpr size_t size = AlignUp(kEntryWithoutPadding1.size(), kAlignment);
Wyatt Hepler1d594562020-03-10 18:26:02 -0700417
418 constexpr uint32_t checksum =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700419 ByteSum(bytes::Concat(kFormatWithSum.magic)) + 0 /* checksum */ +
Wyatt Hepler50070492020-04-23 21:50:34 -0700420 alignment_units + kKey1.size() + kValue1.size() +
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700421 ByteSum(bytes::Concat(kTransactionId1 + 1)) + ByteSum(kKey1) +
Wyatt Hepler50070492020-04-23 21:50:34 -0700422 ByteSum(kValue1) + size /* +1 for each byte in the checksum */;
Wyatt Hepler1d594562020-03-10 18:26:02 -0700423
424 constexpr auto kNewHeader1 =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700425 bytes::Concat(kFormatWithSum.magic, // magic
426 checksum, // checksum (byte sum)
427 alignment_units, // alignment (in 16 B units)
428 uint8_t(kKey1.size()), // key length
429 uint16_t(kValue1.size()), // value size
430 kTransactionId1 + 1); // transaction ID
Wyatt Heplerb8c67ac2021-08-12 09:44:21 -0700431 constexpr size_t padding = Padding(kEntryWithoutPadding1.size(), kAlignment);
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700432 return bytes::Concat(
433 kNewHeader1, kKey1, kValue1, bytes::Initialized<padding>(0));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700434}
435
Wyatt Hepler50070492020-04-23 21:50:34 -0700436TEST_F(ValidEntryInFlash, UpdateAndCopy_DifferentFormatSmallerAlignment) {
437 // Uses 16-bit alignment, smaller than the original entry's alignment.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800438 ASSERT_EQ(OkStatus(), entry_.Update(kFormatWithSum, kTransactionId1 + 1));
Wyatt Hepler50070492020-04-23 21:50:34 -0700439
440 StatusWithSize result = entry_.Copy(kEntry1.size());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800441 ASSERT_EQ(OkStatus(), result.status());
Wyatt Hepler50070492020-04-23 21:50:34 -0700442 EXPECT_EQ(kEntry1.size(), result.size());
443
444 constexpr auto new_data = MakeNewFormatWithSumEntry<16>();
445 static_assert(new_data.size() == 32);
446
447 EXPECT_EQ(
448 0,
449 std::memcmp(
450 &flash_.buffer()[kEntry1.size()], new_data.data(), new_data.size()));
451 Entry new_entry;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800452 ASSERT_EQ(OkStatus(),
Wyatt Hepler50070492020-04-23 21:50:34 -0700453 Entry::Read(partition_, 32, kFormatsWithSum, &new_entry));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800454 EXPECT_EQ(OkStatus(), new_entry.VerifyChecksumInFlash());
Wyatt Hepler50070492020-04-23 21:50:34 -0700455 EXPECT_EQ(kFormatWithSum.magic, new_entry.magic());
456 EXPECT_EQ(kTransactionId1 + 1, new_entry.transaction_id());
457}
458
459TEST(ValidEntryInFlash, UpdateAndCopy_DifferentFormatSameAlignment) {
460 // Use 32-bit alignment, the same as the original entry's alignment.
David Rogersd64cc012020-05-26 12:37:37 -0700461 FakeFlashMemoryBuffer<1024, 4> flash(kEntry1);
Wyatt Hepler50070492020-04-23 21:50:34 -0700462 FlashPartition partition(&flash, 0, 4, 32);
463 Entry entry;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800464 ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, kFormats, &entry));
Wyatt Hepler50070492020-04-23 21:50:34 -0700465
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800466 ASSERT_EQ(OkStatus(), entry.Update(kFormatWithSum, kTransactionId1 + 1));
Wyatt Hepler50070492020-04-23 21:50:34 -0700467
468 StatusWithSize result = entry.Copy(32);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800469 ASSERT_EQ(OkStatus(), result.status());
Wyatt Hepler50070492020-04-23 21:50:34 -0700470 EXPECT_EQ(AlignUp(kEntry1.size(), 32), result.size());
471
472 constexpr auto new_data = MakeNewFormatWithSumEntry<32>();
473 static_assert(new_data.size() == 32);
474
475 EXPECT_EQ(0,
476 std::memcmp(&flash.buffer()[32], new_data.data(), new_data.size()));
477
478 Entry new_entry;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800479 ASSERT_EQ(OkStatus(),
Wyatt Hepler50070492020-04-23 21:50:34 -0700480 Entry::Read(partition, 32, kFormatsWithSum, &new_entry));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800481 EXPECT_EQ(OkStatus(), new_entry.VerifyChecksumInFlash());
Wyatt Hepler50070492020-04-23 21:50:34 -0700482 EXPECT_EQ(kTransactionId1 + 1, new_entry.transaction_id());
483}
484
485TEST(ValidEntryInFlash, UpdateAndCopy_DifferentFormatLargerAlignment) {
486 // Use 64-bit alignment, larger than the original entry's alignment.
David Rogersd64cc012020-05-26 12:37:37 -0700487 FakeFlashMemoryBuffer<1024, 4> flash(kEntry1);
Wyatt Hepler50070492020-04-23 21:50:34 -0700488 FlashPartition partition(&flash, 0, 4, 64);
489 Entry entry;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800490 ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, kFormats, &entry));
Wyatt Hepler50070492020-04-23 21:50:34 -0700491
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800492 ASSERT_EQ(OkStatus(), entry.Update(kFormatWithSum, kTransactionId1 + 1));
Wyatt Hepler50070492020-04-23 21:50:34 -0700493
494 StatusWithSize result = entry.Copy(64);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800495 ASSERT_EQ(OkStatus(), result.status());
Wyatt Hepler50070492020-04-23 21:50:34 -0700496 EXPECT_EQ(AlignUp(kEntry1.size(), 64), result.size());
497
498 constexpr auto new_data = MakeNewFormatWithSumEntry<64>();
499 static_assert(new_data.size() == 64);
500
501 EXPECT_EQ(0,
502 std::memcmp(&flash.buffer()[64], new_data.data(), new_data.size()));
503
504 Entry new_entry;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800505 ASSERT_EQ(OkStatus(),
Wyatt Hepler50070492020-04-23 21:50:34 -0700506 Entry::Read(partition, 64, kFormatsWithSum, &new_entry));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800507 EXPECT_EQ(OkStatus(), new_entry.VerifyChecksumInFlash());
Wyatt Hepler50070492020-04-23 21:50:34 -0700508 EXPECT_EQ(kTransactionId1 + 1, new_entry.transaction_id());
509}
510
511TEST_F(ValidEntryInFlash, UpdateAndCopy_NoChecksum_UpdatesToNewFormat) {
David Rogers436b3aa2020-08-03 08:44:10 -0700512 // For magic value always use a random 32 bit integer rather than a human
513 // readable 4 bytes. See pw_kvs/format.h for more information.
514 constexpr EntryFormat no_checksum{.magic = 0x43fae18f, .checksum = nullptr};
Wyatt Hepler1d594562020-03-10 18:26:02 -0700515
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800516 ASSERT_EQ(OkStatus(), entry_.Update(no_checksum, kTransactionId1 + 1));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700517
518 auto result = entry_.Copy(kEntry1.size());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800519 ASSERT_EQ(OkStatus(), result.status());
Wyatt Hepler1d594562020-03-10 18:26:02 -0700520 EXPECT_EQ(kEntry1.size(), result.size());
521
522 constexpr auto kNewHeader1 =
Wyatt Hepler6b3a6c92020-08-03 10:15:56 -0700523 bytes::Concat(no_checksum.magic, // magic
524 uint32_t(0), // checksum (none)
525 uint8_t(0), // alignment (changed to 16 B from 32)
526 uint8_t(kKey1.size()), // key length
527 uint16_t(kValue1.size()), // value size
528 kTransactionId1 + 1); // transaction ID
529 constexpr auto kNewEntry1 =
530 bytes::Concat(kNewHeader1, kKey1, kValue1, kPadding1);
Wyatt Hepler1d594562020-03-10 18:26:02 -0700531
532 EXPECT_EQ(0,
533 std::memcmp(&flash_.buffer()[kEntry1.size()],
534 kNewEntry1.data(),
535 kNewEntry1.size()));
536}
537
Wyatt Hepler50070492020-04-23 21:50:34 -0700538TEST_F(ValidEntryInFlash, UpdateAndCopyMultple_DifferentFormat) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800539 ASSERT_EQ(OkStatus(), entry_.Update(kFormatWithSum, kTransactionId1 + 6));
David Rogers31b358b2020-04-15 05:00:50 -0700540
541 FlashPartition::Address new_address = entry_.size();
542
543 for (int i = 0; i < 10; i++) {
544 StatusWithSize copy_result = entry_.Copy(new_address + (i * entry_.size()));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800545 ASSERT_EQ(OkStatus(), copy_result.status());
David Rogers31b358b2020-04-15 05:00:50 -0700546 ASSERT_EQ(kEntry1.size(), copy_result.size());
547 }
548
549 for (int j = 0; j < 10; j++) {
550 Entry entry;
551 FlashPartition::Address read_address = (new_address + (j * entry_.size()));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800552 ASSERT_EQ(OkStatus(),
David Rogers31b358b2020-04-15 05:00:50 -0700553 Entry::Read(partition_, read_address, kFormatsWithSum, &entry));
554
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800555 EXPECT_EQ(OkStatus(), entry.VerifyChecksumInFlash());
David Rogers31b358b2020-04-15 05:00:50 -0700556 EXPECT_EQ(kFormatWithSum.magic, entry.magic());
557 EXPECT_EQ(read_address, entry.address());
558 EXPECT_EQ(kTransactionId1 + 6, entry.transaction_id());
559 EXPECT_FALSE(entry.deleted());
560 }
561}
562
Wyatt Hepler50070492020-04-23 21:50:34 -0700563TEST_F(ValidEntryInFlash, DifferentFormat_UpdatedCopy_FailsWithWrongMagic) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800564 ASSERT_EQ(OkStatus(), entry_.Update(kFormatWithSum, kTransactionId1 + 6));
David Rogers31b358b2020-04-15 05:00:50 -0700565
566 FlashPartition::Address new_address = entry_.size();
567
568 StatusWithSize copy_result = entry_.Copy(new_address);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800569 ASSERT_EQ(OkStatus(), copy_result.status());
David Rogers31b358b2020-04-15 05:00:50 -0700570 ASSERT_EQ(kEntry1.size(), copy_result.size());
571
572 Entry entry;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700573 ASSERT_EQ(Status::DataLoss(),
David Rogers31b358b2020-04-15 05:00:50 -0700574 Entry::Read(partition_, new_address, kFormats, &entry));
575}
576
Wyatt Hepler50070492020-04-23 21:50:34 -0700577TEST_F(ValidEntryInFlash, UpdateAndCopy_WriteError) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700578 flash_.InjectWriteError(FlashError::Unconditional(Status::Cancelled()));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700579
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800580 ASSERT_EQ(OkStatus(), entry_.Update(kNoChecksumFormat, kTransactionId1 + 1));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700581
582 auto result = entry_.Copy(kEntry1.size());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700583 EXPECT_EQ(Status::Cancelled(), result.status());
Wyatt Hepler1d594562020-03-10 18:26:02 -0700584 EXPECT_EQ(kEntry1.size(), result.size());
585}
586
Wyatt Hepler97fc7942020-02-06 15:55:45 -0800587} // namespace
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800588} // namespace pw::kvs::internal