// 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 #include #include #include #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 contents) { partition_.Erase() .IgnoreError(); // TODO(pwbug/387): Handle Status properly std::memcpy(flash_.buffer().data(), contents.data(), contents.size()); } void InitSourceBufferToRandom(uint64_t seed) { partition_.Erase() .IgnoreError(); // TODO(pwbug/387): Handle Status properly random::XorShiftStarRng64 rng(seed); rng.Get(source_buffer_) .IgnoreError(); // TODO(pwbug/387): Handle Status properly } void InitSourceBufferToFill(char fill) { partition_.Erase() .IgnoreError(); // TODO(pwbug/387): Handle Status properly 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(chunk_size)); BlobStoreBuffer blob( name, partition_, &checksum, kvs::TestKvs(), kBufferSize); EXPECT_EQ(OkStatus(), blob.Init()); BlobStore::BlobWriter writer(blob, metadata_buffer_); EXPECT_EQ(OkStatus(), writer.Open()); EXPECT_EQ(OkStatus(), 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(write_size), static_cast(source.size_bytes())); ASSERT_EQ(OkStatus(), writer.Write(source.first(write_size))); source = source.subspan(write_size); } EXPECT_EQ(OkStatus(), writer.Close()); // Use reader to check for valid data. BlobStore::BlobReader reader(blob); ASSERT_EQ(OkStatus(), reader.Open()); Result result = reader.GetMemoryMappedBlob(); ASSERT_TRUE(result.ok()); VerifyFlash(result.value()); EXPECT_EQ(OkStatus(), reader.Close()); } void VerifyFlash(ConstByteSpan 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); static constexpr size_t kMetadataBufferSize = BlobStore::BlobWriter::RequiredMetadataBufferSize(0); kvs::FakeFlashMemoryBuffer flash_; kvs::FlashPartition partition_; std::array metadata_buffer_; std::array 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