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 #include <span>
21
22 #include "gtest/gtest.h"
23 #include "pw_blob_store/blob_store.h"
24 #include "pw_kvs/crc16_checksum.h"
25 #include "pw_kvs/fake_flash_memory.h"
26 #include "pw_kvs/flash_memory.h"
27 #include "pw_kvs/test_key_value_store.h"
28 #include "pw_random/xor_shift.h"
29 #include "pw_sync/mutex.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(std::span(source_buffer_).first(init_size_bytes))
59 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
60 }
61
62 // Fill the source buffer with random pattern based on given seed, written to
63 // BlobStore in specified chunk size.
WriteTestBlock(std::string_view file_name,size_t write_size_bytes)64 void WriteTestBlock(std::string_view file_name, size_t write_size_bytes) {
65 ASSERT_LE(write_size_bytes, source_buffer_.size());
66
67 ConstByteSpan write_data =
68 std::span(source_buffer_).first(write_size_bytes);
69
70 BlobStore::BlobWriter writer(blob_, metadata_buffer_);
71 EXPECT_EQ(OkStatus(), writer.Open());
72 ASSERT_EQ(OkStatus(), writer.SetFileName(file_name));
73 ASSERT_EQ(OkStatus(), writer.Write(write_data));
74 EXPECT_EQ(OkStatus(), writer.Close());
75
76 // Use reader to check for valid data.
77 BlobStore::BlobReader reader(blob_);
78 ASSERT_EQ(OkStatus(), reader.Open());
79 Result<ConstByteSpan> result = reader.GetMemoryMappedBlob();
80 ASSERT_TRUE(result.ok());
81 EXPECT_EQ(write_size_bytes, result.value().size_bytes());
82 EXPECT_EQ(OkStatus(), reader.Close());
83 }
84
85 static constexpr size_t kFlashAlignment = 16;
86 static constexpr size_t kSectorSize = 2048;
87 static constexpr size_t kSectorCount = 2;
88 static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
89 static constexpr size_t kMaxFileNameLength = 32;
90 static constexpr size_t kMetadataBufferSize =
91 BlobStore::BlobWriter::RequiredMetadataBufferSize(kMaxFileNameLength);
92
93 kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
94 kvs::FlashPartition partition_;
95 std::array<std::byte, kMetadataBufferSize> metadata_buffer_;
96 std::array<std::byte, kBlobDataSize> source_buffer_;
97 kvs::ChecksumCrc16 checksum_;
98 BlobStoreBuffer<kBufferSize> blob_;
99 };
100
TEST_F(FlatFileSystemBlobStoreEntryTest,BasicProperties)101 TEST_F(FlatFileSystemBlobStoreEntryTest, BasicProperties) {
102 constexpr size_t kWrittenDataSizeBytes = 104;
103 constexpr uint32_t kExpectedFileId = 0x731ACAC0;
104 constexpr FlatFileSystemBlobStoreEntry::FilePermissions kExpectedPermissions =
105 FlatFileSystemBlobStoreEntry::FilePermissions::READ;
106
107 constexpr std::string_view kFileName("my_file_1.bin");
108 InitSourceBufferToRandom(0x5C4CA189);
109 WriteTestBlock(kFileName, kWrittenDataSizeBytes);
110 std::array<char, kMaxFileNameLength> tmp_buffer = {};
111 static_assert(kFileName.size() <= tmp_buffer.size());
112
113 sync::VirtualMutex blob_store_mutex;
114 FlatFileSystemBlobStoreEntry blob_store_file(
115 kExpectedFileId, kExpectedPermissions, blob_, blob_store_mutex);
116
117 StatusWithSize sws = blob_store_file.Name(tmp_buffer);
118 ASSERT_EQ(OkStatus(), sws.status());
119
120 const int comparison =
121 memcmp(tmp_buffer.data(), kFileName.data(), sws.size());
122 EXPECT_EQ(0, comparison);
123 EXPECT_EQ(kWrittenDataSizeBytes, blob_store_file.SizeBytes());
124 EXPECT_EQ(kExpectedPermissions, blob_store_file.Permissions());
125 EXPECT_EQ(kExpectedFileId, blob_store_file.FileId());
126 }
127
TEST_F(FlatFileSystemBlobStoreEntryTest,Delete)128 TEST_F(FlatFileSystemBlobStoreEntryTest, Delete) {
129 constexpr size_t kWrittenDataSizeBytes = 104;
130 constexpr uint32_t kExpectedFileId = 0x87ED0EF2;
131 constexpr FlatFileSystemBlobStoreEntry::FilePermissions kExpectedPermissions =
132 FlatFileSystemBlobStoreEntry::FilePermissions::READ;
133
134 constexpr std::string_view kFileName("my_file_1.bin");
135 InitSourceBufferToRandom(0x5C4CA189);
136 WriteTestBlock(kFileName, kWrittenDataSizeBytes);
137
138 sync::VirtualMutex blob_store_mutex;
139 FlatFileSystemBlobStoreEntry blob_store_file(
140 kExpectedFileId, kExpectedPermissions, blob_, blob_store_mutex);
141
142 ASSERT_EQ(OkStatus(), blob_store_file.Delete());
143
144 BlobStore::BlobReader reader(blob_);
145 // Failed precondition is the expected return value when a BlobStore is opened
146 // for reading and is empty.
147 ASSERT_EQ(Status::FailedPrecondition(), reader.Open());
148 }
149
TEST_F(FlatFileSystemBlobStoreEntryTest,NoData)150 TEST_F(FlatFileSystemBlobStoreEntryTest, NoData) {
151 constexpr uint32_t kExpectedFileId = 0x1;
152 constexpr FlatFileSystemBlobStoreEntry::FilePermissions kExpectedPermissions =
153 FlatFileSystemBlobStoreEntry::FilePermissions::READ;
154
155 // Ensure the BlobStore is erased.
156 ASSERT_EQ(OkStatus(), partition_.Erase());
157
158 sync::VirtualMutex blob_store_mutex;
159 FlatFileSystemBlobStoreEntry blob_store_file(
160 kExpectedFileId, kExpectedPermissions, blob_, blob_store_mutex);
161
162 std::array<char, kMaxFileNameLength> tmp_buffer = {};
163 StatusWithSize sws = blob_store_file.Name(tmp_buffer);
164 EXPECT_EQ(Status::NotFound(), sws.status());
165 EXPECT_EQ(0u, sws.size());
166 }
167
168 } // namespace
169 } // namespace pw::blob_store
170