aboutsummaryrefslogtreecommitdiff
path: root/pw_kvs/flash_partition_stream_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'pw_kvs/flash_partition_stream_test.cc')
-rw-r--r--pw_kvs/flash_partition_stream_test.cc396
1 files changed, 396 insertions, 0 deletions
diff --git a/pw_kvs/flash_partition_stream_test.cc b/pw_kvs/flash_partition_stream_test.cc
new file mode 100644
index 000000000..47fd974db
--- /dev/null
+++ b/pw_kvs/flash_partition_stream_test.cc
@@ -0,0 +1,396 @@
+// Copyright 2022 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 <algorithm>
+#include <array>
+#include <cstddef>
+#include <cstring>
+#include <span>
+
+#include "gtest/gtest.h"
+#include "public/pw_kvs/flash_memory.h"
+#include "pw_kvs/fake_flash_memory.h"
+#include "pw_kvs/flash_memory.h"
+#include "pw_kvs_private/config.h"
+#include "pw_log/log.h"
+#include "pw_random/xor_shift.h"
+
+#if PW_CXX_STANDARD_IS_SUPPORTED(17)
+
+#ifndef PW_FLASH_TEST_ALIGNMENT
+#define PW_FLASH_TEST_ALIGNMENT 1
+#endif
+
+namespace pw::kvs {
+namespace {
+
+class FlashStreamTest : public ::testing::Test {
+ protected:
+ FlashStreamTest() : flash_(kFlashAlignment), partition_(&flash_) {}
+
+ void InitBufferToFill(ByteSpan buffer_span, char fill) {
+ std::memset(buffer_span.data(), fill, buffer_span.size_bytes());
+ }
+
+ void InitBufferToRandom(ByteSpan buffer_span, uint64_t seed) {
+ random::XorShiftStarRng64 rng(seed);
+
+ std::memset(buffer_span.data(),
+ static_cast<int>(flash_.erased_memory_content()),
+ buffer_span.size());
+ ASSERT_EQ(OkStatus(), rng.Get(buffer_span).status());
+ }
+
+ void VerifyFlash(ConstByteSpan verify_bytes, size_t offset = 0) {
+ // 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() + offset), source_buffer_.size());
+
+ for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
+ ASSERT_EQ(source_buffer_[i + offset], verify_bytes[i]);
+ }
+ }
+
+ void VerifyFlashContent(ConstByteSpan verify_bytes, size_t offset = 0) {
+ // Can't allow it to march off the end of source_buffer_.
+ ASSERT_LE((verify_bytes.size_bytes() + offset),
+ flash_.buffer().size_bytes());
+
+ for (size_t i = 0; i < verify_bytes.size_bytes(); i++) {
+ ASSERT_EQ(flash_.buffer()[i + offset], verify_bytes[i]);
+ }
+ }
+
+ void DoWriteInChunks(size_t chunk_write_size_bytes, uint64_t seed) {
+ InitBufferToRandom(std::span(source_buffer_), seed);
+ ConstByteSpan write_data = std::span(source_buffer_);
+
+ ASSERT_EQ(OkStatus(), partition_.Erase());
+
+ FlashPartition::Writer writer(partition_);
+
+ while (write_data.size_bytes() > 0) {
+ size_t offset_before_write = writer.Tell();
+ size_t write_chunk_size =
+ std::min(chunk_write_size_bytes, write_data.size_bytes());
+
+ ConstByteSpan write_chunk = write_data.first(write_chunk_size);
+ ASSERT_EQ(OkStatus(), writer.Write(write_chunk));
+ VerifyFlashContent(write_chunk, offset_before_write);
+
+ write_data = write_data.subspan(write_chunk_size);
+
+ ASSERT_EQ(writer.ConservativeWriteLimit(), write_data.size_bytes());
+ }
+
+ VerifyFlashContent(std::span(source_buffer_));
+ }
+
+ void DoReadInChunks(size_t chunk_read_size_bytes,
+ uint64_t seed,
+ size_t start_offset,
+ size_t bytes_to_read) {
+ InitBufferToRandom(flash_.buffer(), seed);
+
+ ASSERT_LE((start_offset + bytes_to_read), flash_.buffer().size_bytes());
+
+ FlashPartition::Reader reader(partition_);
+ ASSERT_EQ(reader.ConservativeReadLimit(), flash_.buffer().size_bytes());
+
+ ASSERT_EQ(reader.Seek(start_offset), OkStatus());
+ ASSERT_EQ(reader.ConservativeReadLimit(),
+ flash_.buffer().size_bytes() - start_offset);
+
+ while (bytes_to_read > 0) {
+ ASSERT_EQ(start_offset, reader.Tell());
+
+ size_t chunk_size = std::min(chunk_read_size_bytes, bytes_to_read);
+
+ ByteSpan read_chunk = std::span(source_buffer_).first(chunk_size);
+ InitBufferToFill(read_chunk, 0);
+ ASSERT_EQ(read_chunk.size_bytes(), chunk_size);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), chunk_size);
+ VerifyFlashContent(read_chunk, start_offset);
+
+ start_offset += chunk_size;
+ bytes_to_read -= chunk_size;
+
+ ASSERT_EQ(reader.ConservativeReadLimit(),
+ flash_.buffer().size_bytes() - start_offset);
+ }
+ }
+
+ static constexpr size_t kFlashAlignment = PW_FLASH_TEST_ALIGNMENT;
+ static constexpr size_t kSectorSize = 2048;
+ static constexpr size_t kSectorCount = 2;
+ static constexpr size_t kFPDataSize = (kSectorCount * kSectorSize);
+
+ FakeFlashMemoryBuffer<kSectorSize, kSectorCount> flash_;
+ FlashPartition partition_;
+ std::array<std::byte, kFPDataSize> source_buffer_;
+ size_t size_bytes_;
+};
+
+TEST_F(FlashStreamTest, Write_1_Byte_Chunks) {
+ // Write in 1 byte chunks.
+ DoWriteInChunks(1, 0xab1234);
+}
+
+TEST_F(FlashStreamTest, Write_5_Byte_Chunks) {
+ // Write in 5 byte chunks.
+ DoWriteInChunks(5, 0xdc2274);
+}
+
+TEST_F(FlashStreamTest, Write_16_Byte_Chunks) {
+ // Write in 16 byte chunks.
+ DoWriteInChunks(16, 0xef8224);
+}
+
+TEST_F(FlashStreamTest, Write_255_Byte_Chunks) {
+ // Write in 255 byte chunks.
+ DoWriteInChunks(255, 0xffe1348);
+}
+
+TEST_F(FlashStreamTest, Write_256_Byte_Chunks) {
+ // Write in 256 byte chunks.
+ DoWriteInChunks(256, 0xe11234);
+}
+
+TEST_F(FlashStreamTest, Read_1_Byte_Chunks) {
+ // Read in 1 byte chunks.
+ DoReadInChunks(1, 0x7643ff, 0, flash_.buffer().size_bytes());
+}
+
+TEST_F(FlashStreamTest, Read_16_Byte_Chunks) {
+ // Read in 16 byte chunks.
+ DoReadInChunks(16, 0x61e234, 0, flash_.buffer().size_bytes());
+}
+
+TEST_F(FlashStreamTest, Read_255_Byte_Chunks) {
+ // Read in 256 byte chunks.
+ DoReadInChunks(255, 0xe13514, 0, flash_.buffer().size_bytes());
+}
+
+TEST_F(FlashStreamTest, Read_256_Byte_Chunks) {
+ // Read in 256 byte chunks.
+ DoReadInChunks(256, 0xe11234, 0, flash_.buffer().size_bytes());
+}
+
+TEST_F(FlashStreamTest, Read_256_Byte_Chunks_With_Offset) {
+ // Read in 256 byte chunks.
+ DoReadInChunks(256, 0xfffe34, 1024, (flash_.buffer().size_bytes() - 1024));
+}
+
+TEST_F(FlashStreamTest, Read_Multiple_Seeks) {
+ static const size_t kSeekReadSizeBytes = 512;
+ static const size_t kSeekReadIterations = 4;
+ ASSERT_GE(flash_.buffer().size_bytes(),
+ (kSeekReadIterations * (2 * kSeekReadSizeBytes)));
+
+ InitBufferToRandom(flash_.buffer(), 0xffde176);
+ FlashPartition::Reader reader(partition_);
+
+ for (size_t i = 0; i < kSeekReadIterations; i++) {
+ size_t start_offset = kSeekReadSizeBytes + (i * 2 * kSeekReadSizeBytes);
+ ASSERT_EQ(reader.Seek(start_offset), OkStatus());
+ ASSERT_EQ(start_offset, reader.Tell());
+
+ ByteSpan read_chunk = std::span(source_buffer_).first(kSeekReadSizeBytes);
+ InitBufferToFill(read_chunk, 0);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), kSeekReadSizeBytes);
+ VerifyFlashContent(read_chunk, start_offset);
+ ASSERT_EQ(start_offset + kSeekReadSizeBytes, reader.Tell());
+ }
+}
+
+TEST_F(FlashStreamTest, Read_Seek_Forward_and_Back) {
+ static const size_t kSeekReadSizeBytes = 256;
+ static const size_t kTotalIterations = 3;
+ static const size_t kSeekReadIterations =
+ flash_.buffer().size_bytes() / (2 * kSeekReadSizeBytes);
+
+ InitBufferToRandom(flash_.buffer(), 0xffde176);
+ FlashPartition::Reader reader(partition_);
+
+ for (size_t outer_count = 0; outer_count < kTotalIterations; outer_count++) {
+ // Seek and read going forward.
+ for (size_t i = 0; i < kSeekReadIterations; i++) {
+ size_t start_offset = kSeekReadSizeBytes + (i * 2 * kSeekReadSizeBytes);
+ ASSERT_EQ(reader.Seek(start_offset), OkStatus());
+ ASSERT_EQ(start_offset, reader.Tell());
+
+ ByteSpan read_chunk = std::span(source_buffer_).first(kSeekReadSizeBytes);
+ InitBufferToFill(read_chunk, 0);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), kSeekReadSizeBytes);
+ VerifyFlashContent(read_chunk, start_offset);
+ ASSERT_EQ(start_offset + kSeekReadSizeBytes, reader.Tell());
+ }
+
+ // Seek and read going backward.
+ for (size_t j = (kSeekReadIterations * 2); j > 0; j--) {
+ size_t start_offset = (j - 1) * kSeekReadSizeBytes;
+ ASSERT_EQ(reader.Seek(start_offset), OkStatus());
+ ASSERT_EQ(start_offset, reader.Tell());
+ ASSERT_GE(reader.ConservativeReadLimit(), kSeekReadSizeBytes);
+
+ ByteSpan read_chunk = std::span(source_buffer_).first(kSeekReadSizeBytes);
+ InitBufferToFill(read_chunk, 0);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), kSeekReadSizeBytes);
+ VerifyFlashContent(read_chunk, start_offset);
+ ASSERT_EQ(start_offset + kSeekReadSizeBytes, reader.Tell());
+ }
+ }
+}
+
+TEST_F(FlashStreamTest, Read_Past_End) {
+ InitBufferToRandom(flash_.buffer(), 0xcccde176);
+ FlashPartition::Reader reader(partition_);
+
+ static const size_t kBytesForFinalRead = 50;
+
+ ByteSpan read_chunk = std::span(source_buffer_)
+ .first(source_buffer_.size() - kBytesForFinalRead);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), read_chunk.size_bytes());
+ ASSERT_EQ(reader.Tell(), read_chunk.size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), kBytesForFinalRead);
+ ASSERT_EQ(result.value().data(), read_chunk.data());
+ VerifyFlashContent(read_chunk);
+
+ result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), kBytesForFinalRead);
+ ASSERT_EQ(reader.Tell(), flash_.buffer().size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+ ASSERT_EQ(result.value().data(), read_chunk.data());
+ VerifyFlashContent(result.value(), read_chunk.size_bytes());
+}
+
+TEST_F(FlashStreamTest, Read_Past_End_After_Seek) {
+ InitBufferToRandom(flash_.buffer(), 0xddcde176);
+ FlashPartition::Reader reader(partition_);
+
+ static const size_t kBytesForFinalRead = 50;
+ size_t start_offset = flash_.buffer().size_bytes() - kBytesForFinalRead;
+ ASSERT_EQ(reader.Seek(start_offset), OkStatus());
+
+ ASSERT_EQ(start_offset, reader.Tell());
+ ASSERT_EQ(reader.ConservativeReadLimit(), kBytesForFinalRead);
+
+ ByteSpan read_chunk = std::span(source_buffer_);
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), kBytesForFinalRead);
+ ASSERT_EQ(reader.Tell(), flash_.buffer().size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+ ASSERT_EQ(result.value().data(), read_chunk.data());
+ VerifyFlashContent(result.value(), start_offset);
+}
+
+TEST_F(FlashStreamTest, Read_Out_Of_Range) {
+ InitBufferToRandom(flash_.buffer(), 0x531de176);
+ FlashPartition::Reader reader(partition_);
+
+ ByteSpan read_chunk = std::span(source_buffer_);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), OkStatus());
+ ASSERT_EQ(result.value().size_bytes(), read_chunk.size_bytes());
+ ASSERT_EQ(reader.Tell(), read_chunk.size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+ ASSERT_EQ(result.value().data(), read_chunk.data());
+ VerifyFlashContent(read_chunk);
+
+ result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), Status::OutOfRange());
+ ASSERT_EQ(reader.Tell(), flash_.buffer().size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+}
+
+TEST_F(FlashStreamTest, Read_Out_Of_Range_After_Seek) {
+ InitBufferToRandom(flash_.buffer(), 0x8c94566);
+ FlashPartition::Reader reader(partition_);
+
+ ByteSpan read_chunk = std::span(source_buffer_);
+
+ ASSERT_EQ(reader.Seek(flash_.buffer().size_bytes()), OkStatus());
+ ASSERT_EQ(reader.Tell(), flash_.buffer().size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+
+ auto result = reader.Read(read_chunk);
+ ASSERT_EQ(result.status(), Status::OutOfRange());
+ ASSERT_EQ(reader.Tell(), flash_.buffer().size_bytes());
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+}
+
+TEST_F(FlashStreamTest, Reader_Seek_Ops) {
+ size_t kPartitionSizeBytes = flash_.buffer().size_bytes();
+ FlashPartition::Reader reader(partition_);
+
+ // Seek from 0 to past end.
+ ASSERT_EQ(reader.Seek(kPartitionSizeBytes + 5), Status::OutOfRange());
+ ASSERT_EQ(reader.Tell(), 0U);
+
+ // Seek to end then seek again going past end.
+ ASSERT_EQ(reader.Seek(0), OkStatus());
+ ASSERT_EQ(reader.Tell(), 0U);
+ ASSERT_EQ(reader.ConservativeReadLimit(), kPartitionSizeBytes);
+
+ ASSERT_EQ(reader.Seek(kPartitionSizeBytes,
+ FlashPartition::Reader::Whence::kCurrent),
+ OkStatus());
+ ASSERT_EQ(reader.Tell(), kPartitionSizeBytes);
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+
+ ASSERT_EQ(reader.Seek(5, FlashPartition::Reader::Whence::kCurrent),
+ Status::OutOfRange());
+ ASSERT_EQ(reader.Tell(), kPartitionSizeBytes);
+ ASSERT_EQ(reader.ConservativeReadLimit(), 0U);
+
+ // Seek to beginning then seek backwards going past start.
+ ASSERT_EQ(reader.Seek(0), OkStatus());
+ ASSERT_EQ(reader.Seek(-5, FlashPartition::Reader::Whence::kCurrent),
+ Status::OutOfRange());
+ ASSERT_EQ(reader.Tell(), 0U);
+ ASSERT_EQ(reader.ConservativeReadLimit(), kPartitionSizeBytes);
+}
+
+TEST_F(FlashStreamTest, Invald_Ops) {
+ FlashPartition::Reader reader(partition_);
+ ASSERT_EQ(reader.ConservativeWriteLimit(), 0U);
+
+ FlashPartition::Writer writer(partition_);
+ ASSERT_EQ(writer.ConservativeReadLimit(), 0U);
+}
+
+} // namespace
+} // namespace pw::kvs
+
+#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)