1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
17 #include "gtest/gtest.h"
18 #include "pw_kvs/flash_memory.h"
19 #include "pw_kvs/flash_test_partition.h"
20 #include "pw_kvs_private/config.h"
21 #include "pw_log/log.h"
23 namespace pw::kvs::PartitionTest {
26 #ifndef PW_FLASH_TEST_ITERATIONS
27 #define PW_FLASH_TEST_ITERATIONS 2
28 #endif // PW_FLASH_TEST_ITERATIONS
30 constexpr size_t kTestIterations = PW_FLASH_TEST_ITERATIONS;
32 size_t error_count = 0;
34 void WriteData(FlashPartition& partition, uint8_t fill_byte) {
35 uint8_t test_data[kMaxFlashAlignment];
36 memset(test_data, fill_byte, sizeof(test_data));
38 const size_t alignment = partition.alignment_bytes();
40 ASSERT_EQ(OkStatus(), partition.Erase(0, partition.sector_count()));
42 const size_t chunks_per_sector = partition.sector_size_bytes() / alignment;
44 // Fill partition sector by sector. Fill the sector with an integer number
45 // of alignment-size chunks. If the sector is not evenly divisible by
46 // alignment-size, the remainder is not written.
47 for (size_t sector_index = 0; sector_index < partition.sector_count();
49 FlashPartition::Address address =
50 sector_index * partition.sector_size_bytes();
52 for (size_t chunk_index = 0; chunk_index < chunks_per_sector;
54 StatusWithSize status =
55 partition.Write(address, as_bytes(std::span(test_data, alignment)));
56 ASSERT_EQ(OkStatus(), status.status());
57 ASSERT_EQ(alignment, status.size());
62 // Check the fill result. Use expect so the test doesn't bail on error.
63 // Count the errors and print if any errors are found.
64 for (size_t sector_index = 0; sector_index < partition.sector_count();
66 FlashPartition::Address address =
67 sector_index * partition.sector_size_bytes();
69 for (size_t chunk_index = 0; chunk_index < chunks_per_sector;
71 memset(test_data, 0, sizeof(test_data));
72 StatusWithSize status = partition.Read(address, alignment, test_data);
74 EXPECT_EQ(OkStatus(), status.status());
75 EXPECT_EQ(alignment, status.size());
76 if (!status.ok() || (alignment != status.size())) {
78 PW_LOG_DEBUG(" Read Error [%s], %u of %u",
79 status.status().str(),
80 unsigned(status.size()),
85 for (size_t i = 0; i < alignment; i++) {
86 if (test_data[i] != fill_byte) {
89 " Error %u, Read compare @ address %x, got 0x%02x, "
91 unsigned(error_count),
92 unsigned(address + i),
93 unsigned(test_data[i]),
102 EXPECT_EQ(error_count, 0U);
103 if (error_count != 0) {
104 PW_LOG_ERROR("Partition test, fill '%c', %u errors found",
106 unsigned(error_count));
110 TEST(FlashPartitionTest, FillTest) {
111 FlashPartition& test_partition = FlashTestPartition();
113 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
115 for (size_t i = 0; i < kTestIterations; i++) {
116 PW_LOG_DEBUG("FillTest iteration %u, write '0'", unsigned(i));
117 WriteData(test_partition, 0);
118 PW_LOG_DEBUG("FillTest iteration %u, write '0xff'", unsigned(i));
119 WriteData(test_partition, 0xff);
120 PW_LOG_DEBUG("FillTest iteration %u, write '0x55'", unsigned(i));
121 WriteData(test_partition, 0x55);
122 PW_LOG_DEBUG("FillTest iteration %u, write '0xa3'", unsigned(i));
123 WriteData(test_partition, 0xa3);
124 PW_LOG_DEBUG("Completed iterations %u, Total errors %u",
126 unsigned(error_count));
130 TEST(FlashPartitionTest, EraseTest) {
131 FlashPartition& test_partition = FlashTestPartition();
133 static const uint8_t fill_byte = 0x55;
134 uint8_t test_data[kMaxFlashAlignment];
135 memset(test_data, fill_byte, sizeof(test_data));
137 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
139 const size_t block_size =
140 std::min(sizeof(test_data), test_partition.sector_size_bytes());
141 auto data_span = std::span(test_data, block_size);
143 ASSERT_EQ(OkStatus(), test_partition.Erase(0, test_partition.sector_count()));
145 // Write to the first page of each sector.
146 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
148 FlashPartition::Address address =
149 sector_index * test_partition.sector_size_bytes();
151 StatusWithSize status = test_partition.Write(address, as_bytes(data_span));
152 ASSERT_EQ(OkStatus(), status.status());
153 ASSERT_EQ(block_size, status.size());
156 // Preset the flag to make sure the check actually sets it.
157 bool is_erased = true;
158 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
159 ASSERT_EQ(false, is_erased);
161 ASSERT_EQ(OkStatus(), test_partition.Erase());
163 // Preset the flag to make sure the check actually sets it.
165 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
166 ASSERT_EQ(true, is_erased);
168 // Read the first page of each sector and make sure it has been erased.
169 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
171 FlashPartition::Address address =
172 sector_index * test_partition.sector_size_bytes();
174 StatusWithSize status =
175 test_partition.Read(address, data_span.size_bytes(), data_span.data());
176 EXPECT_EQ(OkStatus(), status.status());
177 EXPECT_EQ(data_span.size_bytes(), status.size());
179 EXPECT_EQ(true, test_partition.AppearsErased(as_bytes(data_span)));
183 TEST(FlashPartitionTest, AlignmentCheck) {
184 FlashPartition& test_partition = FlashTestPartition();
185 const size_t alignment = test_partition.alignment_bytes();
186 const size_t sector_size_bytes = test_partition.sector_size_bytes();
188 EXPECT_LE(alignment, kMaxFlashAlignment);
189 EXPECT_GT(alignment, 0u);
190 EXPECT_EQ(kMaxFlashAlignment % alignment, 0U);
191 EXPECT_LE(kMaxFlashAlignment, sector_size_bytes);
192 EXPECT_LE(sector_size_bytes % kMaxFlashAlignment, 0U);
195 #define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
196 #if TESTING_CHECK_FAILURES_IS_SUPPORTED
197 // TODO: Ensure that this test triggers an assert.
198 TEST(FlashPartitionTest, BadWriteAddressAlignment) {
199 FlashPartition& test_partition = FlashTestPartition();
201 // Can't get bad alignment with alignment of 1.
202 if (test_partition.alignment_bytes() == 1) {
206 std::array<std::byte, kMaxFlashAlignment> source_data;
207 test_partition.Write(1, source_data);
210 // TODO: Ensure that this test triggers an assert.
211 TEST(FlashPartitionTest, BadWriteSizeAlignment) {
212 FlashPartition& test_partition = FlashTestPartition();
214 // Can't get bad alignment with alignment of 1.
215 if (test_partition.alignment_bytes() == 1) {
219 std::array<std::byte, 1> source_data;
220 test_partition.Write(0, source_data);
223 // TODO: Ensure that this test triggers an assert.
224 TEST(FlashPartitionTest, BadEraseAddressAlignment) {
225 FlashPartition& test_partition = FlashTestPartition();
227 // Can't get bad alignment with sector size of 1.
228 if (test_partition.sector_size_bytes() == 1) {
232 // Try Erase at address 1 for 1 sector.
233 test_partition.Erase(1, 1);
236 #endif // TESTING_CHECK_FAILURES_IS_SUPPORTED
238 TEST(FlashPartitionTest, IsErased) {
239 FlashPartition& test_partition = FlashTestPartition();
240 const size_t alignment = test_partition.alignment_bytes();
242 // Make sure the partition is big enough to do this test.
243 ASSERT_GE(test_partition.size_bytes(), 3 * kMaxFlashAlignment);
245 ASSERT_EQ(OkStatus(), test_partition.Erase());
247 bool is_erased = true;
248 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
249 ASSERT_EQ(true, is_erased);
251 static const uint8_t fill_byte = 0x55;
252 uint8_t test_data[kMaxFlashAlignment];
253 memset(test_data, fill_byte, sizeof(test_data));
254 auto data_span = std::span(test_data);
256 // Write the chunk with fill byte.
257 StatusWithSize status = test_partition.Write(alignment, as_bytes(data_span));
258 ASSERT_EQ(OkStatus(), status.status());
259 ASSERT_EQ(data_span.size_bytes(), status.size());
261 EXPECT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
262 EXPECT_EQ(false, is_erased);
264 // Check the chunk that was written.
265 EXPECT_EQ(OkStatus(),
266 test_partition.IsRegionErased(
267 alignment, data_span.size_bytes(), &is_erased));
268 EXPECT_EQ(false, is_erased);
270 // Check a region that starts erased but later has been written.
271 EXPECT_EQ(OkStatus(),
272 test_partition.IsRegionErased(0, 2 * alignment, &is_erased));
273 EXPECT_EQ(false, is_erased);
275 // Check erased for a region smaller than kMaxFlashAlignment. This has been a
277 EXPECT_EQ(OkStatus(),
278 test_partition.IsRegionErased(0, alignment, &is_erased));
279 EXPECT_EQ(true, is_erased);
283 } // namespace pw::kvs::PartitionTest