| // 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 "pw_kvs/flash_partition_test.h" |
| |
| #include <span> |
| |
| #include "gtest/gtest.h" |
| #include "pw_kvs/flash_memory.h" |
| #include "pw_kvs_private/config.h" |
| #include "pw_log/log.h" |
| |
| namespace pw::kvs::PartitionTest { |
| |
| constexpr size_t kTestDataSize = kMaxFlashAlignment; |
| |
| namespace { |
| |
| void WriteData(FlashPartition& partition, uint8_t fill_byte) { |
| uint8_t test_data[kTestDataSize]; |
| memset(test_data, fill_byte, sizeof(test_data)); |
| |
| ASSERT_GE(kTestDataSize, partition.alignment_bytes()); |
| |
| partition.Erase(0, partition.sector_count()); |
| |
| const size_t chunks_per_sector = |
| partition.sector_size_bytes() / partition.alignment_bytes(); |
| |
| // Fill partition sector by sector. Fill the sector with an integer number |
| // of alignment-size chunks. If the sector is not evenly divisible by |
| // alignment-size, the remainder is not written. |
| for (size_t sector_index = 0; sector_index < partition.sector_count(); |
| sector_index++) { |
| FlashPartition::Address address = |
| sector_index * partition.sector_size_bytes(); |
| |
| for (size_t chunk_index = 0; chunk_index < chunks_per_sector; |
| chunk_index++) { |
| StatusWithSize status = partition.Write( |
| address, as_bytes(std::span(test_data, partition.alignment_bytes()))); |
| ASSERT_EQ(Status::OK, status.status()); |
| ASSERT_EQ(partition.alignment_bytes(), status.size()); |
| address += partition.alignment_bytes(); |
| } |
| } |
| |
| // Check the fill result. Use expect so the test doesn't bail on error. |
| // Count the errors and print if any errors are found. |
| size_t error_count = 0; |
| for (size_t sector_index = 0; sector_index < partition.sector_count(); |
| sector_index++) { |
| FlashPartition::Address address = |
| sector_index * partition.sector_size_bytes(); |
| |
| for (size_t chunk_index = 0; chunk_index < chunks_per_sector; |
| chunk_index++) { |
| memset(test_data, 0, sizeof(test_data)); |
| StatusWithSize status = |
| partition.Read(address, partition.alignment_bytes(), test_data); |
| |
| EXPECT_EQ(Status::OK, status.status()); |
| EXPECT_EQ(partition.alignment_bytes(), status.size()); |
| if (!status.ok() || (partition.alignment_bytes() != status.size())) { |
| error_count++; |
| continue; |
| } |
| |
| for (size_t i = 0; i < partition.alignment_bytes(); i++) { |
| if (test_data[i] != fill_byte) { |
| error_count++; |
| } |
| } |
| |
| address += partition.alignment_bytes(); |
| } |
| } |
| |
| EXPECT_EQ(error_count, 0U); |
| if (error_count != 0) { |
| PW_LOG_ERROR("Partition test, fill '%c', %u errors found", |
| fill_byte, |
| unsigned(error_count)); |
| } |
| } |
| |
| } // namespace |
| |
| void WriteTest(FlashPartition& partition, size_t test_iterations) { |
| ASSERT_GE(kTestDataSize, partition.alignment_bytes()); |
| |
| for (size_t i = 0; i < test_iterations; i++) { |
| WriteData(partition, 0); |
| WriteData(partition, 0xff); |
| WriteData(partition, 0x55); |
| WriteData(partition, 0xa3); |
| } |
| } |
| |
| void EraseTest(FlashPartition& partition) { |
| static const uint8_t fill_byte = 0x55; |
| uint8_t test_data[kTestDataSize]; |
| memset(test_data, fill_byte, sizeof(test_data)); |
| |
| ASSERT_GE(kTestDataSize, partition.alignment_bytes()); |
| |
| const size_t block_size = |
| std::min(sizeof(test_data), partition.sector_size_bytes()); |
| auto data_span = std::span(test_data, block_size); |
| |
| ASSERT_EQ(Status::OK, partition.Erase(0, partition.sector_count())); |
| |
| // Write to the first page of each sector. |
| for (size_t sector_index = 0; sector_index < partition.sector_count(); |
| sector_index++) { |
| FlashPartition::Address address = |
| sector_index * partition.sector_size_bytes(); |
| |
| StatusWithSize status = partition.Write(address, as_bytes(data_span)); |
| ASSERT_EQ(Status::OK, status.status()); |
| ASSERT_EQ(block_size, status.size()); |
| } |
| |
| ASSERT_EQ(Status::OK, partition.Erase()); |
| |
| bool is_erased; |
| ASSERT_EQ(Status::OK, |
| partition.IsRegionErased(0, partition.size_bytes(), &is_erased)); |
| ASSERT_EQ(true, is_erased); |
| |
| // Read the first page of each sector and make sure it has been erased. |
| for (size_t sector_index = 0; sector_index < partition.sector_count(); |
| sector_index++) { |
| FlashPartition::Address address = |
| sector_index * partition.sector_size_bytes(); |
| |
| StatusWithSize status = |
| partition.Read(address, data_span.size_bytes(), data_span.data()); |
| ASSERT_EQ(Status::OK, status.status()); |
| ASSERT_EQ(data_span.size_bytes(), status.size()); |
| |
| ASSERT_EQ(true, partition.AppearsErased(as_bytes(data_span))); |
| } |
| } |
| |
| void ReadOnlyTest(FlashPartition& partition) { |
| uint8_t test_data[kTestDataSize]; |
| auto data_span = std::span(test_data); |
| |
| ASSERT_EQ(Status::PERMISSION_DENIED, |
| partition.Erase(0, partition.sector_count())); |
| |
| ASSERT_EQ(Status::PERMISSION_DENIED, |
| partition.Write(0, as_bytes(data_span)).status()); |
| } |
| |
| } // namespace pw::kvs::PartitionTest |