xref: /aosp_15_r20/external/pigweed/pw_kvs/entry_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 "pw_kvs/internal/entry.h"
16 
17 #include <string_view>
18 
19 #include "pw_bytes/array.h"
20 #include "pw_kvs/alignment.h"
21 #include "pw_kvs/checksum.h"
22 #include "pw_kvs/crc16_checksum.h"
23 #include "pw_kvs/fake_flash_memory.h"
24 #include "pw_kvs/flash_memory.h"
25 #include "pw_kvs/format.h"
26 #include "pw_span/span.h"
27 #include "pw_unit_test/framework.h"
28 
29 namespace pw::kvs::internal {
30 namespace {
31 
32 using std::byte;
33 using std::string_view;
34 
35 // 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.
37 constexpr EntryFormat kFormat{0x961c2ff9, nullptr};
38 
TEST(Entry,Size_RoundsUpToAlignment)39 TEST(Entry, Size_RoundsUpToAlignment) {
40   // Use FakeFlashMemory, rather than FakeFlashMemoryBuffer, so the class gets
41   // tested/used directly.
42   std::array<std::byte, 64 * 2> buffer;
43 
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);
47 
48   for (size_t alignment_bytes = 1; alignment_bytes <= 4096; ++alignment_bytes) {
49     FlashPartition partition(&flash, 0, flash.sector_count(), alignment_bytes);
50     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}) {
53       Entry entry = Entry::Valid(
54           partition, 0, kFormat, "k", {static_cast<byte*>(nullptr), value}, 0);
55 
56       ASSERT_EQ(AlignUp(sizeof(EntryHeader) + 1 /* key */ + value, align),
57                 entry.size());
58     }
59 
60     Entry entry = Entry::Tombstone(partition, 0, kFormat, "k", 0);
61     ASSERT_EQ(AlignUp(sizeof(EntryHeader) + 1 /* key */, align), entry.size());
62   }
63 }
64 
TEST(Entry,Construct_ValidEntry)65 TEST(Entry, Construct_ValidEntry) {
66   FakeFlashMemoryBuffer<64, 2> flash(16);
67   FlashPartition partition(&flash, 0, flash.sector_count());
68 
69   auto entry =
70       Entry::Valid(partition, 1, kFormat, "k", as_bytes(span("123")), 9876);
71 
72   EXPECT_FALSE(entry.deleted());
73   EXPECT_EQ(entry.magic(), kFormat.magic);
74   EXPECT_EQ(entry.value_size(), sizeof("123"));
75   EXPECT_EQ(entry.transaction_id(), 9876u);
76 }
77 
TEST(Entry,Construct_Tombstone)78 TEST(Entry, Construct_Tombstone) {
79   FakeFlashMemoryBuffer<64, 2> flash(16);
80   FlashPartition partition(&flash, 0, flash.sector_count());
81 
82   auto entry = Entry::Tombstone(partition, 1, kFormat, "key", 123);
83 
84   EXPECT_TRUE(entry.deleted());
85   EXPECT_EQ(entry.magic(), kFormat.magic);
86   EXPECT_EQ(entry.value_size(), 0u);
87   EXPECT_EQ(entry.transaction_id(), 123u);
88 }
89 
90 // 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.
92 constexpr uint32_t kMagicWithChecksum = 0xad165142;
93 constexpr uint32_t kTransactionId1 = 0x96979899;
94 
95 constexpr auto kKey1 = bytes::String("key45");
96 constexpr auto kValue1 = bytes::String("VALUE!");
97 constexpr auto kPadding1 = bytes::String("\0\0\0\0\0");
98 
99 constexpr 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
105 );
106 
107 constexpr auto kEntryWithoutPadding1 = bytes::Concat(kHeader1, kKey1, kValue1);
108 constexpr auto kEntry1 = bytes::Concat(kEntryWithoutPadding1, kPadding1);
109 static_assert(kEntry1.size() == 32);
110 
111 ChecksumCrc16 default_checksum;
112 constexpr EntryFormat kFormatWithChecksum{kMagicWithChecksum,
113                                           &default_checksum};
114 constexpr internal::EntryFormats kFormats(kFormatWithChecksum);
115 
116 class ValidEntryInFlash : public ::testing::Test {
117  protected:
ValidEntryInFlash()118   ValidEntryInFlash() : flash_(kEntry1), partition_(&flash_) {
119     EXPECT_EQ(OkStatus(), Entry::Read(partition_, 0, kFormats, &entry_));
120   }
121 
122   FakeFlashMemoryBuffer<1024, 4> flash_;
123   FlashPartition partition_;
124   Entry entry_;
125 };
126 
TEST_F(ValidEntryInFlash,PassesChecksumVerification)127 TEST_F(ValidEntryInFlash, PassesChecksumVerification) {
128   EXPECT_EQ(OkStatus(), entry_.VerifyChecksumInFlash());
129   EXPECT_EQ(OkStatus(), entry_.VerifyChecksum("key45", kValue1));
130 }
131 
TEST_F(ValidEntryInFlash,HeaderContents)132 TEST_F(ValidEntryInFlash, HeaderContents) {
133   EXPECT_EQ(entry_.magic(), kMagicWithChecksum);
134   EXPECT_EQ(entry_.key_length(), 5u);
135   EXPECT_EQ(entry_.value_size(), 6u);
136   EXPECT_EQ(entry_.transaction_id(), kTransactionId1);
137   EXPECT_FALSE(entry_.deleted());
138 }
139 
TEST_F(ValidEntryInFlash,ReadKey)140 TEST_F(ValidEntryInFlash, ReadKey) {
141   Entry::KeyBuffer key = {};
142   auto result = entry_.ReadKey(key);
143 
144   ASSERT_EQ(OkStatus(), result.status());
145   EXPECT_EQ(result.size(), entry_.key_length());
146   EXPECT_STREQ(key.data(), "key45");
147 }
148 
TEST_F(ValidEntryInFlash,ReadValue)149 TEST_F(ValidEntryInFlash, ReadValue) {
150   char value[32] = {};
151   auto result = entry_.ReadValue(as_writable_bytes(span(value)));
152 
153   ASSERT_EQ(OkStatus(), result.status());
154   EXPECT_EQ(result.size(), entry_.value_size());
155   EXPECT_STREQ(value, "VALUE!");
156 }
157 
TEST_F(ValidEntryInFlash,ReadValue_BufferTooSmall)158 TEST_F(ValidEntryInFlash, ReadValue_BufferTooSmall) {
159   char value[3] = {};
160   auto result = entry_.ReadValue(as_writable_bytes(span(value)));
161 
162   ASSERT_EQ(Status::ResourceExhausted(), result.status());
163   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 
TEST_F(ValidEntryInFlash,ReadValue_WithOffset)169 TEST_F(ValidEntryInFlash, ReadValue_WithOffset) {
170   char value[3] = {};
171   auto result = entry_.ReadValue(as_writable_bytes(span(value)), 3);
172 
173   ASSERT_EQ(OkStatus(), result.status());
174   EXPECT_EQ(3u, result.size());
175   EXPECT_EQ(value[0], 'U');
176   EXPECT_EQ(value[1], 'E');
177   EXPECT_EQ(value[2], '!');
178 }
179 
TEST_F(ValidEntryInFlash,ReadValue_WithOffset_BufferTooSmall)180 TEST_F(ValidEntryInFlash, ReadValue_WithOffset_BufferTooSmall) {
181   char value[1] = {};
182   auto result = entry_.ReadValue(as_writable_bytes(span(value)), 4);
183 
184   ASSERT_EQ(Status::ResourceExhausted(), result.status());
185   EXPECT_EQ(1u, result.size());
186   EXPECT_EQ(value[0], 'E');
187 }
188 
TEST_F(ValidEntryInFlash,ReadValue_WithOffset_EmptyRead)189 TEST_F(ValidEntryInFlash, ReadValue_WithOffset_EmptyRead) {
190   char value[16] = {'?'};
191   auto result = entry_.ReadValue(as_writable_bytes(span(value)), 6);
192 
193   ASSERT_EQ(OkStatus(), result.status());
194   EXPECT_EQ(0u, result.size());
195   EXPECT_EQ(value[0], '?');
196 }
197 
TEST_F(ValidEntryInFlash,ReadValue_WithOffset_PastEnd)198 TEST_F(ValidEntryInFlash, ReadValue_WithOffset_PastEnd) {
199   char value[16] = {};
200   auto result = entry_.ReadValue(as_writable_bytes(span(value)), 7);
201 
202   EXPECT_EQ(Status::OutOfRange(), result.status());
203   EXPECT_EQ(0u, result.size());
204 }
205 
TEST(ValidEntry,Write)206 TEST(ValidEntry, Write) {
207   FakeFlashMemoryBuffer<1024, 4> flash;
208   FlashPartition partition(&flash, 0, flash.sector_count(), 32);
209 
210   Entry entry = Entry::Valid(
211       partition, 64, kFormatWithChecksum, "key45", kValue1, kTransactionId1);
212 
213   auto result = entry.Write("key45", kValue1);
214   EXPECT_EQ(OkStatus(), result.status());
215   EXPECT_EQ(32u, result.size());
216   EXPECT_EQ(std::memcmp(&flash.buffer()[64], kEntry1.data(), kEntry1.size()),
217             0);
218 }
219 
220 constexpr auto kHeader2 = bytes::String(
221     "\x42\x51\x16\xad"  // magic
222     "\xba\xb3\x00\x00"  // checksum (CRC16)
223     "\x00"              // alignment
224     "\x01"              // key length
225     "\xff\xff"          // value size
226     "\x00\x01\x02\x03"  // transaction ID
227 );
228 
229 constexpr auto kKeyAndPadding2 =
230     bytes::String("K\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
231 
232 class TombstoneEntryInFlash : public ::testing::Test {
233  protected:
TombstoneEntryInFlash()234   TombstoneEntryInFlash()
235       : flash_(bytes::Concat(kHeader2, kKeyAndPadding2)), partition_(&flash_) {
236     EXPECT_EQ(OkStatus(), Entry::Read(partition_, 0, kFormats, &entry_));
237   }
238 
239   FakeFlashMemoryBuffer<1024, 4> flash_;
240   FlashPartition partition_;
241   Entry entry_;
242 };
243 
TEST_F(TombstoneEntryInFlash,PassesChecksumVerification)244 TEST_F(TombstoneEntryInFlash, PassesChecksumVerification) {
245   EXPECT_EQ(OkStatus(), entry_.VerifyChecksumInFlash());
246   EXPECT_EQ(OkStatus(), entry_.VerifyChecksum("K", {}));
247 }
248 
TEST_F(TombstoneEntryInFlash,HeaderContents)249 TEST_F(TombstoneEntryInFlash, HeaderContents) {
250   EXPECT_EQ(entry_.magic(), kMagicWithChecksum);
251   EXPECT_EQ(entry_.key_length(), 1u);
252   EXPECT_EQ(entry_.value_size(), 0u);
253   EXPECT_EQ(entry_.transaction_id(), 0x03020100u);
254   EXPECT_TRUE(entry_.deleted());
255 }
256 
TEST_F(TombstoneEntryInFlash,ReadKey)257 TEST_F(TombstoneEntryInFlash, ReadKey) {
258   Entry::KeyBuffer key = {};
259   auto result = entry_.ReadKey(key);
260 
261   ASSERT_EQ(OkStatus(), result.status());
262   EXPECT_EQ(result.size(), entry_.key_length());
263   EXPECT_STREQ(key.data(), "K");
264 }
265 
TEST_F(TombstoneEntryInFlash,ReadValue)266 TEST_F(TombstoneEntryInFlash, ReadValue) {
267   char value[32] = {};
268   auto result = entry_.ReadValue(as_writable_bytes(span(value)));
269 
270   ASSERT_EQ(OkStatus(), result.status());
271   EXPECT_EQ(0u, result.size());
272 }
273 
TEST(TombstoneEntry,Write)274 TEST(TombstoneEntry, Write) {
275   FakeFlashMemoryBuffer<1024, 4> flash;
276   FlashPartition partition(&flash);
277   ChecksumCrc16 checksum;
278 
279   Entry entry =
280       Entry::Tombstone(partition, 16, kFormatWithChecksum, "K", 0x03020100);
281 
282   auto result = entry.Write("K", {});
283   EXPECT_EQ(OkStatus(), result.status());
284   EXPECT_EQ(32u, result.size());
285   EXPECT_EQ(std::memcmp(&flash.buffer()[16],
286                         bytes::Concat(kHeader2, kKeyAndPadding2).data(),
287                         kEntry1.size()),
288             0);
289 }
290 
TEST(Entry,Checksum_NoChecksumRequiresZero)291 TEST(Entry, Checksum_NoChecksumRequiresZero) {
292   FakeFlashMemoryBuffer<1024, 4> flash(kEntry1);
293   FlashPartition partition(&flash);
294   Entry entry;
295 
296   const EntryFormat format{kMagicWithChecksum, nullptr};
297   const internal::EntryFormats formats(format);
298 
299   ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, formats, &entry));
300 
301   EXPECT_EQ(Status::DataLoss(), entry.VerifyChecksumInFlash());
302   EXPECT_EQ(Status::DataLoss(), entry.VerifyChecksum({}, {}));
303 
304   std::memset(&flash.buffer()[4], 0, 4);  // set the checksum field to 0
305   ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, formats, &entry));
306   EXPECT_EQ(OkStatus(), entry.VerifyChecksumInFlash());
307   EXPECT_EQ(OkStatus(), entry.VerifyChecksum({}, {}));
308 }
309 
TEST(Entry,Checksum_ChecksPadding)310 TEST(Entry, Checksum_ChecksPadding) {
311   FakeFlashMemoryBuffer<1024, 4> flash(
312       bytes::Concat(kHeader1, kKey1, kValue1, bytes::String("\0\0\0\0\1")));
313   FlashPartition partition(&flash);
314   Entry entry;
315   ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, kFormats, &entry));
316 
317   // Last byte in padding is a 1; should fail.
318   EXPECT_EQ(Status::DataLoss(), entry.VerifyChecksumInFlash());
319 
320   // The in-memory verification fills in 0s for the padding.
321   EXPECT_EQ(OkStatus(), entry.VerifyChecksum("key45", kValue1));
322 
323   flash.buffer()[kEntry1.size() - 1] = byte{0};
324   EXPECT_EQ(OkStatus(), entry.VerifyChecksumInFlash());
325 }
326 
TEST_F(ValidEntryInFlash,Update_SameFormat_TransactionIdIsUpdated)327 TEST_F(ValidEntryInFlash, Update_SameFormat_TransactionIdIsUpdated) {
328   ASSERT_EQ(OkStatus(),
329             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 
TEST_F(ValidEntryInFlash,Update_DifferentFormat_MagicAndTransactionIdAreUpdated)337 TEST_F(ValidEntryInFlash,
338        Update_DifferentFormat_MagicAndTransactionIdAreUpdated) {
339   ASSERT_EQ(OkStatus(), entry_.Update(kFormat, kTransactionId1 + 6));
340 
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 
TEST_F(ValidEntryInFlash,Update_ReadError_WithChecksumIsError)347 TEST_F(ValidEntryInFlash, Update_ReadError_WithChecksumIsError) {
348   flash_.InjectReadError(FlashError::Unconditional(Status::Aborted()));
349 
350   EXPECT_EQ(Status::Aborted(),
351             entry_.Update(kFormatWithChecksum, kTransactionId1 + 1));
352 }
353 
354 // 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.
356 constexpr EntryFormat kNoChecksumFormat{.magic = 0x721bad24,
357                                         .checksum = nullptr};
358 
TEST_F(ValidEntryInFlash,Update_ReadError_NoChecksumIsOkay)359 TEST_F(ValidEntryInFlash, Update_ReadError_NoChecksumIsOkay) {
360   flash_.InjectReadError(FlashError::Unconditional(Status::Aborted()));
361 
362   EXPECT_EQ(OkStatus(), entry_.Update(kNoChecksumFormat, kTransactionId1 + 1));
363 }
364 
TEST_F(ValidEntryInFlash,Copy)365 TEST_F(ValidEntryInFlash, Copy) {
366   auto result = entry_.Copy(123);
367 
368   EXPECT_EQ(OkStatus(), result.status());
369   EXPECT_EQ(entry_.size(), result.size());
370   EXPECT_EQ(0,
371             std::memcmp(
372                 &flash_.buffer().data()[123], kEntry1.data(), kEntry1.size()));
373 }
374 
TEST_F(ValidEntryInFlash,Copy_ReadError)375 TEST_F(ValidEntryInFlash, Copy_ReadError) {
376   flash_.InjectReadError(FlashError::Unconditional(Status::Unimplemented()));
377   auto result = entry_.Copy(kEntry1.size());
378   EXPECT_EQ(Status::Unimplemented(), result.status());
379   EXPECT_EQ(0u, result.size());
380 }
381 
ByteSum(span<const byte> bytes,uint32_t value=0)382 constexpr uint32_t ByteSum(span<const byte> bytes, uint32_t value = 0) {
383   for (byte b : bytes) {
384     value += unsigned(b);
385   }
386   return value;
387 }
388 
389 // Sums the bytes, adding one to each byte so that zeroes change the checksum.
390 class ChecksumSummation final : public ChecksumAlgorithm {
391  public:
ChecksumSummation()392   ChecksumSummation() : ChecksumAlgorithm(as_bytes(span(&sum_, 1))), sum_(0) {}
393 
Reset()394   void Reset() override { sum_ = 0; }
395 
Update(span<const byte> data)396   void Update(span<const byte> data) override {
397     for (byte b : data) {
398       sum_ += unsigned(b) + 1;  // Add 1 so zero-value bytes affect checksum.
399     }
400   }
401 
402  private:
403   uint32_t sum_;
404 } sum_checksum;
405 
406 // For magic value always use a random 32 bit integer rather than a human
407 // readable 4 bytes. See pw_kvs/format.h for more information.
408 constexpr uint32_t kMagicWithSum = 0x6093aadb;
409 constexpr EntryFormat kFormatWithSum{kMagicWithSum, &sum_checksum};
410 constexpr internal::EntryFormats kFormatsWithSum(kFormatWithSum);
411 
412 template <size_t kAlignment>
MakeNewFormatWithSumEntry()413 constexpr auto MakeNewFormatWithSumEntry() {
414   constexpr uint8_t alignment_units = (kAlignment + 15) / 16 - 1;
415   constexpr size_t size = AlignUp(kEntryWithoutPadding1.size(), kAlignment);
416 
417   constexpr uint32_t checksum =
418       ByteSum(bytes::Concat(kFormatWithSum.magic)) + 0 /* checksum */ +
419       alignment_units + kKey1.size() + kValue1.size() +
420       ByteSum(bytes::Concat(kTransactionId1 + 1)) + ByteSum(kKey1) +
421       ByteSum(kValue1) + size /* +1 for each byte in the checksum */;
422 
423   constexpr auto kNewHeader1 =
424       bytes::Concat(kFormatWithSum.magic,      // magic
425                     checksum,                  // checksum (byte sum)
426                     alignment_units,           // alignment (in 16 B units)
427                     uint8_t(kKey1.size()),     // key length
428                     uint16_t(kValue1.size()),  // value size
429                     kTransactionId1 + 1);      // transaction ID
430   constexpr size_t padding = Padding(kEntryWithoutPadding1.size(), kAlignment);
431   return bytes::Concat(
432       kNewHeader1, kKey1, kValue1, bytes::Initialized<padding>(0));
433 }
434 
TEST_F(ValidEntryInFlash,UpdateAndCopy_DifferentFormatSmallerAlignment)435 TEST_F(ValidEntryInFlash, UpdateAndCopy_DifferentFormatSmallerAlignment) {
436   // Uses 16-bit alignment, smaller than the original entry's alignment.
437   ASSERT_EQ(OkStatus(), entry_.Update(kFormatWithSum, kTransactionId1 + 1));
438 
439   StatusWithSize result = entry_.Copy(kEntry1.size());
440   ASSERT_EQ(OkStatus(), result.status());
441   EXPECT_EQ(kEntry1.size(), result.size());
442 
443   constexpr auto new_data = MakeNewFormatWithSumEntry<16>();
444   static_assert(new_data.size() == 32);
445 
446   EXPECT_EQ(
447       0,
448       std::memcmp(
449           &flash_.buffer()[kEntry1.size()], new_data.data(), new_data.size()));
450   Entry new_entry;
451   ASSERT_EQ(OkStatus(),
452             Entry::Read(partition_, 32, kFormatsWithSum, &new_entry));
453   EXPECT_EQ(OkStatus(), new_entry.VerifyChecksumInFlash());
454   EXPECT_EQ(kFormatWithSum.magic, new_entry.magic());
455   EXPECT_EQ(kTransactionId1 + 1, new_entry.transaction_id());
456 }
457 
TEST(Entry,UpdateAndCopy_DifferentFormatSameAlignment)458 TEST(Entry, UpdateAndCopy_DifferentFormatSameAlignment) {
459   // Use 32-bit alignment, the same as the original entry's alignment.
460   FakeFlashMemoryBuffer<1024, 4> flash(kEntry1);
461   FlashPartition partition(&flash, 0, 4, 32);
462   Entry entry;
463   ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, kFormats, &entry));
464 
465   ASSERT_EQ(OkStatus(), entry.Update(kFormatWithSum, kTransactionId1 + 1));
466 
467   StatusWithSize result = entry.Copy(32);
468   ASSERT_EQ(OkStatus(), result.status());
469   EXPECT_EQ(AlignUp(kEntry1.size(), 32), result.size());
470 
471   constexpr auto new_data = MakeNewFormatWithSumEntry<32>();
472   static_assert(new_data.size() == 32);
473 
474   EXPECT_EQ(0,
475             std::memcmp(&flash.buffer()[32], new_data.data(), new_data.size()));
476 
477   Entry new_entry;
478   ASSERT_EQ(OkStatus(),
479             Entry::Read(partition, 32, kFormatsWithSum, &new_entry));
480   EXPECT_EQ(OkStatus(), new_entry.VerifyChecksumInFlash());
481   EXPECT_EQ(kTransactionId1 + 1, new_entry.transaction_id());
482 }
483 
TEST(Entry,UpdateAndCopy_DifferentFormatLargerAlignment)484 TEST(Entry, UpdateAndCopy_DifferentFormatLargerAlignment) {
485   // Use 64-bit alignment, larger than the original entry's alignment.
486   FakeFlashMemoryBuffer<1024, 4> flash(kEntry1);
487   FlashPartition partition(&flash, 0, 4, 64);
488   Entry entry;
489   ASSERT_EQ(OkStatus(), Entry::Read(partition, 0, kFormats, &entry));
490 
491   ASSERT_EQ(OkStatus(), entry.Update(kFormatWithSum, kTransactionId1 + 1));
492 
493   StatusWithSize result = entry.Copy(64);
494   ASSERT_EQ(OkStatus(), result.status());
495   EXPECT_EQ(AlignUp(kEntry1.size(), 64), result.size());
496 
497   constexpr auto new_data = MakeNewFormatWithSumEntry<64>();
498   static_assert(new_data.size() == 64);
499 
500   EXPECT_EQ(0,
501             std::memcmp(&flash.buffer()[64], new_data.data(), new_data.size()));
502 
503   Entry new_entry;
504   ASSERT_EQ(OkStatus(),
505             Entry::Read(partition, 64, kFormatsWithSum, &new_entry));
506   EXPECT_EQ(OkStatus(), new_entry.VerifyChecksumInFlash());
507   EXPECT_EQ(kTransactionId1 + 1, new_entry.transaction_id());
508 }
509 
TEST_F(ValidEntryInFlash,UpdateAndCopy_NoChecksum_UpdatesToNewFormat)510 TEST_F(ValidEntryInFlash, UpdateAndCopy_NoChecksum_UpdatesToNewFormat) {
511   // For magic value always use a random 32 bit integer rather than a human
512   // readable 4 bytes. See pw_kvs/format.h for more information.
513   constexpr EntryFormat no_checksum{.magic = 0x43fae18f, .checksum = nullptr};
514 
515   ASSERT_EQ(OkStatus(), entry_.Update(no_checksum, kTransactionId1 + 1));
516 
517   auto result = entry_.Copy(kEntry1.size());
518   ASSERT_EQ(OkStatus(), result.status());
519   EXPECT_EQ(kEntry1.size(), result.size());
520 
521   constexpr auto kNewHeader1 =
522       bytes::Concat(no_checksum.magic,  // magic
523                     uint32_t(0),        // checksum (none)
524                     uint8_t(0),         // alignment (changed to 16 B from 32)
525                     uint8_t(kKey1.size()),     // key length
526                     uint16_t(kValue1.size()),  // value size
527                     kTransactionId1 + 1);      // transaction ID
528   constexpr auto kNewEntry1 =
529       bytes::Concat(kNewHeader1, kKey1, kValue1, kPadding1);
530 
531   EXPECT_EQ(0,
532             std::memcmp(&flash_.buffer()[kEntry1.size()],
533                         kNewEntry1.data(),
534                         kNewEntry1.size()));
535 }
536 
TEST_F(ValidEntryInFlash,UpdateAndCopyMultple_DifferentFormat)537 TEST_F(ValidEntryInFlash, UpdateAndCopyMultple_DifferentFormat) {
538   ASSERT_EQ(OkStatus(), entry_.Update(kFormatWithSum, kTransactionId1 + 6));
539 
540   FlashPartition::Address new_address = entry_.size();
541 
542   for (int i = 0; i < 10; i++) {
543     StatusWithSize copy_result = entry_.Copy(new_address + (i * entry_.size()));
544     ASSERT_EQ(OkStatus(), copy_result.status());
545     ASSERT_EQ(kEntry1.size(), copy_result.size());
546   }
547 
548   for (int j = 0; j < 10; j++) {
549     Entry entry;
550     FlashPartition::Address read_address = (new_address + (j * entry_.size()));
551     ASSERT_EQ(OkStatus(),
552               Entry::Read(partition_, read_address, kFormatsWithSum, &entry));
553 
554     EXPECT_EQ(OkStatus(), entry.VerifyChecksumInFlash());
555     EXPECT_EQ(kFormatWithSum.magic, entry.magic());
556     EXPECT_EQ(read_address, entry.address());
557     EXPECT_EQ(kTransactionId1 + 6, entry.transaction_id());
558     EXPECT_FALSE(entry.deleted());
559   }
560 }
561 
TEST_F(ValidEntryInFlash,DifferentFormat_UpdatedCopy_FailsWithWrongMagic)562 TEST_F(ValidEntryInFlash, DifferentFormat_UpdatedCopy_FailsWithWrongMagic) {
563   ASSERT_EQ(OkStatus(), entry_.Update(kFormatWithSum, kTransactionId1 + 6));
564 
565   FlashPartition::Address new_address = entry_.size();
566 
567   StatusWithSize copy_result = entry_.Copy(new_address);
568   ASSERT_EQ(OkStatus(), copy_result.status());
569   ASSERT_EQ(kEntry1.size(), copy_result.size());
570 
571   Entry entry;
572   ASSERT_EQ(Status::DataLoss(),
573             Entry::Read(partition_, new_address, kFormats, &entry));
574 }
575 
TEST_F(ValidEntryInFlash,UpdateAndCopy_WriteError)576 TEST_F(ValidEntryInFlash, UpdateAndCopy_WriteError) {
577   flash_.InjectWriteError(FlashError::Unconditional(Status::Cancelled()));
578 
579   ASSERT_EQ(OkStatus(), entry_.Update(kNoChecksumFormat, kTransactionId1 + 1));
580 
581   auto result = entry_.Copy(kEntry1.size());
582   EXPECT_EQ(Status::Cancelled(), result.status());
583   EXPECT_EQ(kEntry1.size(), result.size());
584 }
585 
586 }  // namespace
587 }  // namespace pw::kvs::internal
588