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 "pw_blob_store/flat_file_system_entry.h"
16
17 #include <array>
18 #include <cstddef>
19 #include <cstring>
20
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_random/xor_shift.h"
27 #include "pw_span/span.h"
28 #include "pw_sync/mutex.h"
29 #include "pw_unit_test/framework.h"
30
31 namespace pw::blob_store {
32 namespace {
33
34 class FlatFileSystemBlobStoreEntryTest : public ::testing::Test {
35 protected:
36 static constexpr char kBlobTitle[] = "TestBlobBlock";
37 static constexpr size_t kBufferSize = 64;
38
FlatFileSystemBlobStoreEntryTest()39 FlatFileSystemBlobStoreEntryTest()
40 : flash_(kFlashAlignment),
41 partition_(&flash_),
42 metadata_buffer_(),
43 source_buffer_(),
44 checksum_(),
45 blob_(kBlobTitle, partition_, &checksum_, kvs::TestKvs(), kBufferSize) {
46 }
47
SetUp()48 void SetUp() override { ASSERT_EQ(OkStatus(), blob_.Init()); }
49
InitSourceBufferToRandom(uint64_t seed,size_t init_size_bytes=kBlobDataSize)50 void InitSourceBufferToRandom(uint64_t seed,
51 size_t init_size_bytes = kBlobDataSize) {
52 ASSERT_LE(init_size_bytes, source_buffer_.size());
53 random::XorShiftStarRng64 rng(seed);
54
55 std::memset(source_buffer_.data(),
56 static_cast<int>(flash_.erased_memory_content()),
57 source_buffer_.size());
58 rng.Get(span(source_buffer_).first(init_size_bytes));
59 }
60
61 // Fill the source buffer with random pattern based on given seed, written to
62 // BlobStore in specified chunk size.
WriteTestBlock(std::string_view file_name,size_t write_size_bytes)63 void WriteTestBlock(std::string_view file_name, size_t write_size_bytes) {
64 ASSERT_LE(write_size_bytes, source_buffer_.size());
65
66 ConstByteSpan write_data = span(source_buffer_).first(write_size_bytes);
67
68 BlobStore::BlobWriter writer(blob_, metadata_buffer_);
69 EXPECT_EQ(OkStatus(), writer.Open());
70 ASSERT_EQ(OkStatus(), writer.SetFileName(file_name));
71 ASSERT_EQ(OkStatus(), writer.Write(write_data));
72 EXPECT_EQ(OkStatus(), writer.Close());
73
74 // Use reader to check for valid data.
75 BlobStore::BlobReader reader(blob_);
76 ASSERT_EQ(OkStatus(), reader.Open());
77 Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
78 ASSERT_TRUE(result.ok());
79 EXPECT_EQ(write_size_bytes, result.value().size_bytes());
80 EXPECT_EQ(OkStatus(), reader.Close());
81 }
82
83 static constexpr size_t kFlashAlignment = 16;
84 static constexpr size_t kSectorSize = 2048;
85 static constexpr size_t kSectorCount = 2;
86 static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
87 static constexpr size_t kMaxFileNameLength = 32;
88 static constexpr size_t kMetadataBufferSize =
89 BlobStore::BlobWriter::RequiredMetadataBufferSize(kMaxFileNameLength);
90
91 kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
92 kvs::FlashPartition partition_;
93 std::array<std::byte, kMetadataBufferSize> metadata_buffer_;
94 std::array<std::byte, kBlobDataSize> source_buffer_;
95 kvs::ChecksumCrc16 checksum_;
96 BlobStoreBuffer<kBufferSize> blob_;
97 };
98
TEST_F(FlatFileSystemBlobStoreEntryTest,BasicProperties)99 TEST_F(FlatFileSystemBlobStoreEntryTest, BasicProperties) {
100 constexpr size_t kWrittenDataSizeBytes = 104;
101 constexpr uint32_t kExpectedFileId = 0x731ACAC0;
102 constexpr FlatFileSystemBlobStoreEntry::FilePermissions kExpectedPermissions =
103 FlatFileSystemBlobStoreEntry::FilePermissions::READ;
104
105 constexpr std::string_view kFileName("my_file_1.bin");
106 InitSourceBufferToRandom(0x5C4CA189);
107 WriteTestBlock(kFileName, kWrittenDataSizeBytes);
108 std::array<char, kMaxFileNameLength> tmp_buffer = {};
109 static_assert(kFileName.size() <= tmp_buffer.size());
110
111 sync::VirtualMutex blob_store_mutex;
112 FlatFileSystemBlobStoreEntry blob_store_file(
113 kExpectedFileId, kExpectedPermissions, blob_, blob_store_mutex);
114
115 StatusWithSize sws = blob_store_file.Name(tmp_buffer);
116 ASSERT_EQ(OkStatus(), sws.status());
117
118 const int comparison =
119 memcmp(tmp_buffer.data(), kFileName.data(), sws.size());
120 EXPECT_EQ(0, comparison);
121 EXPECT_EQ(kWrittenDataSizeBytes, blob_store_file.SizeBytes());
122 EXPECT_EQ(kExpectedPermissions, blob_store_file.Permissions());
123 EXPECT_EQ(kExpectedFileId, blob_store_file.FileId());
124 }
125
TEST_F(FlatFileSystemBlobStoreEntryTest,Delete)126 TEST_F(FlatFileSystemBlobStoreEntryTest, Delete) {
127 constexpr size_t kWrittenDataSizeBytes = 104;
128 constexpr uint32_t kExpectedFileId = 0x87ED0EF2;
129 constexpr FlatFileSystemBlobStoreEntry::FilePermissions kExpectedPermissions =
130 FlatFileSystemBlobStoreEntry::FilePermissions::READ;
131
132 constexpr std::string_view kFileName("my_file_1.bin");
133 InitSourceBufferToRandom(0x5C4CA189);
134 WriteTestBlock(kFileName, kWrittenDataSizeBytes);
135
136 sync::VirtualMutex blob_store_mutex;
137 FlatFileSystemBlobStoreEntry blob_store_file(
138 kExpectedFileId, kExpectedPermissions, blob_, blob_store_mutex);
139
140 ASSERT_EQ(OkStatus(), blob_store_file.Delete());
141
142 BlobStore::BlobReader reader(blob_);
143 // Failed precondition is the expected return value when a BlobStore is opened
144 // for reading and is empty.
145 ASSERT_EQ(Status::FailedPrecondition(), reader.Open());
146 }
147
TEST_F(FlatFileSystemBlobStoreEntryTest,NoData)148 TEST_F(FlatFileSystemBlobStoreEntryTest, NoData) {
149 constexpr uint32_t kExpectedFileId = 0x1;
150 constexpr FlatFileSystemBlobStoreEntry::FilePermissions kExpectedPermissions =
151 FlatFileSystemBlobStoreEntry::FilePermissions::READ;
152
153 // Ensure the BlobStore is erased.
154 ASSERT_EQ(OkStatus(), partition_.Erase());
155
156 sync::VirtualMutex blob_store_mutex;
157 FlatFileSystemBlobStoreEntry blob_store_file(
158 kExpectedFileId, kExpectedPermissions, blob_, blob_store_mutex);
159
160 std::array<char, kMaxFileNameLength> tmp_buffer = {};
161 StatusWithSize sws = blob_store_file.Name(tmp_buffer);
162 EXPECT_EQ(Status::NotFound(), sws.status());
163 EXPECT_EQ(0u, sws.size());
164 }
165
166 } // namespace
167 } // namespace pw::blob_store
168