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