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 <array>
16 #include <cstddef>
17 #include <cstring>
18 #include <span>
19
20 #include "gtest/gtest.h"
21 #include "pw_blob_store/blob_store.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/test_key_value_store.h"
26 #include "pw_log/log.h"
27 #include "pw_random/xor_shift.h"
28
29 namespace pw::blob_store {
30 namespace {
31
32 class BlobStoreChunkTest : public ::testing::Test {
33 protected:
BlobStoreChunkTest()34 BlobStoreChunkTest() : flash_(kFlashAlignment), partition_(&flash_) {}
35
InitFlashTo(std::span<const std::byte> contents)36 void InitFlashTo(std::span<const std::byte> contents) {
37 partition_.Erase();
38 std::memcpy(flash_.buffer().data(), contents.data(), contents.size());
39 }
40
InitSourceBufferToRandom(uint64_t seed)41 void InitSourceBufferToRandom(uint64_t seed) {
42 partition_.Erase();
43 random::XorShiftStarRng64 rng(seed);
44 rng.Get(source_buffer_);
45 }
46
InitSourceBufferToFill(char fill)47 void InitSourceBufferToFill(char fill) {
48 partition_.Erase();
49 std::memset(source_buffer_.data(), fill, source_buffer_.size());
50 }
51
52 // Fill the source buffer with random pattern based on given seed, written to
53 // BlobStore in specified chunk size.
ChunkWriteTest(size_t chunk_size)54 void ChunkWriteTest(size_t chunk_size) {
55 constexpr size_t kBufferSize = 256;
56 kvs::ChecksumCrc16 checksum;
57
58 char name[16] = {};
59 snprintf(name, sizeof(name), "Blob%u", static_cast<unsigned>(chunk_size));
60
61 BlobStoreBuffer<kBufferSize> blob(
62 name, partition_, &checksum, kvs::TestKvs(), kBufferSize);
63 EXPECT_EQ(OkStatus(), blob.Init());
64
65 BlobStore::BlobWriter writer(blob);
66 EXPECT_EQ(OkStatus(), writer.Open());
67 EXPECT_EQ(OkStatus(), writer.Erase());
68
69 ByteSpan source = source_buffer_;
70 while (source.size_bytes() > 0) {
71 const size_t write_size = std::min(source.size_bytes(), chunk_size);
72
73 PW_LOG_DEBUG("Do write of %u bytes, %u bytes remain",
74 static_cast<unsigned>(write_size),
75 static_cast<unsigned>(source.size_bytes()));
76
77 ASSERT_EQ(OkStatus(), writer.Write(source.first(write_size)));
78
79 source = source.subspan(write_size);
80 }
81
82 EXPECT_EQ(OkStatus(), writer.Close());
83
84 // Use reader to check for valid data.
85 BlobStore::BlobReader reader(blob);
86 ASSERT_EQ(OkStatus(), reader.Open());
87 Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
88 ASSERT_TRUE(result.ok());
89 VerifyFlash(result.value());
90 EXPECT_EQ(OkStatus(), reader.Close());
91 }
92
VerifyFlash(ConstByteSpan verify_bytes)93 void VerifyFlash(ConstByteSpan verify_bytes) {
94 // Should be defined as same size.
95 EXPECT_EQ(source_buffer_.size(), flash_.buffer().size_bytes());
96
97 // Can't allow it to march off the end of source_buffer_.
98 ASSERT_LE(verify_bytes.size_bytes(), source_buffer_.size());
99
100 for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
101 EXPECT_EQ(source_buffer_[i], verify_bytes[i]);
102 }
103 }
104
105 static constexpr size_t kFlashAlignment = 16;
106 static constexpr size_t kSectorSize = 2048;
107 static constexpr size_t kSectorCount = 2;
108 static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
109
110 kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
111 kvs::FlashPartition partition_;
112 std::array<std::byte, kBlobDataSize> source_buffer_;
113 };
114
TEST_F(BlobStoreChunkTest,ChunkWrite1)115 TEST_F(BlobStoreChunkTest, ChunkWrite1) {
116 InitSourceBufferToRandom(0x8675309);
117 ChunkWriteTest(1);
118 }
119
TEST_F(BlobStoreChunkTest,ChunkWrite2)120 TEST_F(BlobStoreChunkTest, ChunkWrite2) {
121 InitSourceBufferToRandom(0x8675);
122 ChunkWriteTest(2);
123 }
124
TEST_F(BlobStoreChunkTest,ChunkWrite3)125 TEST_F(BlobStoreChunkTest, ChunkWrite3) {
126 InitSourceBufferToFill(0);
127 ChunkWriteTest(3);
128 }
129
TEST_F(BlobStoreChunkTest,ChunkWrite4)130 TEST_F(BlobStoreChunkTest, ChunkWrite4) {
131 InitSourceBufferToFill(1);
132 ChunkWriteTest(4);
133 }
134
TEST_F(BlobStoreChunkTest,ChunkWrite5)135 TEST_F(BlobStoreChunkTest, ChunkWrite5) {
136 InitSourceBufferToFill(0xff);
137 ChunkWriteTest(5);
138 }
139
TEST_F(BlobStoreChunkTest,ChunkWrite16)140 TEST_F(BlobStoreChunkTest, ChunkWrite16) {
141 InitSourceBufferToRandom(0x86);
142 ChunkWriteTest(16);
143 }
144
TEST_F(BlobStoreChunkTest,ChunkWrite64)145 TEST_F(BlobStoreChunkTest, ChunkWrite64) {
146 InitSourceBufferToRandom(0x9);
147 ChunkWriteTest(64);
148 }
149
TEST_F(BlobStoreChunkTest,ChunkWrite256)150 TEST_F(BlobStoreChunkTest, ChunkWrite256) {
151 InitSourceBufferToRandom(0x12345678);
152 ChunkWriteTest(256);
153 }
154
TEST_F(BlobStoreChunkTest,ChunkWrite512)155 TEST_F(BlobStoreChunkTest, ChunkWrite512) {
156 InitSourceBufferToRandom(0x42);
157 ChunkWriteTest(512);
158 }
159
TEST_F(BlobStoreChunkTest,ChunkWrite4096)160 TEST_F(BlobStoreChunkTest, ChunkWrite4096) {
161 InitSourceBufferToRandom(0x89);
162 ChunkWriteTest(4096);
163 }
164
TEST_F(BlobStoreChunkTest,ChunkWriteSingleFull)165 TEST_F(BlobStoreChunkTest, ChunkWriteSingleFull) {
166 InitSourceBufferToRandom(0x98765);
167 ChunkWriteTest(kBlobDataSize);
168 }
169
170 } // namespace
171 } // namespace pw::blob_store
172