pw_blob_store: Add blob open and read
- Add support to open blobs saved to flash
- Add support to stream read blobs
Change-Id: I97c587df34c98b6be03dba582837cd637e8c6448
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/18300
Commit-Queue: David Rogers <davidrogers@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_blob_store/blob_store_chunk_write_test.cc b/pw_blob_store/blob_store_chunk_write_test.cc
new file mode 100644
index 0000000..42dac62
--- /dev/null
+++ b/pw_blob_store/blob_store_chunk_write_test.cc
@@ -0,0 +1,171 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <array>
+#include <cstddef>
+#include <cstring>
+#include <span>
+
+#include "gtest/gtest.h"
+#include "pw_blob_store/blob_store.h"
+#include "pw_kvs/crc16_checksum.h"
+#include "pw_kvs/fake_flash_memory.h"
+#include "pw_kvs/flash_memory.h"
+#include "pw_kvs/test_key_value_store.h"
+#include "pw_log/log.h"
+#include "pw_random/xor_shift.h"
+
+namespace pw::blob_store {
+namespace {
+
+class BlobStoreChunkTest : public ::testing::Test {
+ protected:
+ BlobStoreChunkTest() : flash_(kFlashAlignment), partition_(&flash_) {}
+
+ void InitFlashTo(std::span<const std::byte> contents) {
+ partition_.Erase();
+ std::memcpy(flash_.buffer().data(), contents.data(), contents.size());
+ }
+
+ void InitSourceBufferToRandom(uint64_t seed) {
+ partition_.Erase();
+ random::XorShiftStarRng64 rng(seed);
+ rng.Get(source_buffer_);
+ }
+
+ void InitSourceBufferToFill(char fill) {
+ partition_.Erase();
+ std::memset(source_buffer_.data(), fill, source_buffer_.size());
+ }
+
+ // Fill the source buffer with random pattern based on given seed, written to
+ // BlobStore in specified chunk size.
+ void ChunkWriteTest(size_t chunk_size) {
+ constexpr size_t kBufferSize = 256;
+ kvs::ChecksumCrc16 checksum;
+
+ char name[16] = {};
+ snprintf(name, sizeof(name), "Blob%u", static_cast<unsigned>(chunk_size));
+
+ BlobStoreBuffer<kBufferSize> blob(
+ name, &partition_, &checksum, kvs::TestKvs());
+ EXPECT_EQ(Status::OK, blob.Init());
+
+ BlobStore::BlobWriter writer(blob);
+ EXPECT_EQ(Status::OK, writer.Open());
+ EXPECT_EQ(Status::OK, writer.Erase());
+
+ ByteSpan source = source_buffer_;
+ while (source.size_bytes() > 0) {
+ const size_t write_size = std::min(source.size_bytes(), chunk_size);
+
+ PW_LOG_DEBUG("Do write of %u bytes, %u bytes remain",
+ static_cast<unsigned>(write_size),
+ static_cast<unsigned>(source.size_bytes()));
+
+ ASSERT_EQ(Status::OK, writer.Write(source.first(write_size)));
+
+ source = source.subspan(write_size);
+ }
+
+ EXPECT_EQ(Status::OK, writer.Close());
+
+ // Use reader to check for valid data.
+ BlobStore::BlobReader reader(blob);
+ ASSERT_EQ(Status::OK, reader.Open());
+ Result<ByteSpan> result = reader.GetMemoryMappedBlob();
+ ASSERT_TRUE(result.ok());
+ VerifyFlash(result.value());
+ EXPECT_EQ(Status::OK, reader.Close());
+ }
+
+ void VerifyFlash(ByteSpan verify_bytes) {
+ // Should be defined as same size.
+ EXPECT_EQ(source_buffer_.size(), flash_.buffer().size_bytes());
+
+ // Can't allow it to march off the end of source_buffer_.
+ ASSERT_LE(verify_bytes.size_bytes(), source_buffer_.size());
+
+ for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
+ EXPECT_EQ(source_buffer_[i], verify_bytes[i]);
+ }
+ }
+
+ static constexpr size_t kFlashAlignment = 16;
+ static constexpr size_t kSectorSize = 2048;
+ static constexpr size_t kSectorCount = 2;
+ static constexpr size_t kBlobDataSize = (kSectorCount * kSectorSize);
+
+ kvs::FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
+ kvs::FlashPartition partition_;
+ std::array<std::byte, kBlobDataSize> source_buffer_;
+};
+
+TEST_F(BlobStoreChunkTest, ChunkWrite1) {
+ InitSourceBufferToRandom(0x8675309);
+ ChunkWriteTest(1);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite2) {
+ InitSourceBufferToRandom(0x8675);
+ ChunkWriteTest(2);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite3) {
+ InitSourceBufferToFill(0);
+ ChunkWriteTest(3);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite4) {
+ InitSourceBufferToFill(1);
+ ChunkWriteTest(4);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite5) {
+ InitSourceBufferToFill(0xff);
+ ChunkWriteTest(5);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite16) {
+ InitSourceBufferToRandom(0x86);
+ ChunkWriteTest(16);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite64) {
+ InitSourceBufferToRandom(0x9);
+ ChunkWriteTest(64);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite256) {
+ InitSourceBufferToRandom(0x12345678);
+ ChunkWriteTest(256);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite512) {
+ InitSourceBufferToRandom(0x42);
+ ChunkWriteTest(512);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWrite4096) {
+ InitSourceBufferToRandom(0x89);
+ ChunkWriteTest(4096);
+}
+
+TEST_F(BlobStoreChunkTest, ChunkWriteSingleFull) {
+ InitSourceBufferToRandom(0x98765);
+ ChunkWriteTest(kBlobDataSize);
+}
+
+} // namespace
+} // namespace pw::blob_store