blob: 193f3d2ab4105c6baeae558b4e5a29a381dde02d [file] [log] [blame]
David Rogersca592962020-07-01 09:21:54 -07001// Copyright 2020 The Pigweed Authors
2//
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
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
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
13// the License.
14
David Rogersca592962020-07-01 09:21:54 -070015#include <span>
16
17#include "gtest/gtest.h"
18#include "pw_kvs/flash_memory.h"
David Rogers6a262b42020-07-09 03:27:41 -070019#include "pw_kvs/flash_test_partition.h"
David Rogersca592962020-07-01 09:21:54 -070020#include "pw_kvs_private/config.h"
21#include "pw_log/log.h"
22
23namespace pw::kvs::PartitionTest {
David Rogers6a262b42020-07-09 03:27:41 -070024namespace {
25
26#ifndef PW_FLASH_TEST_ITERATIONS
27#define PW_FLASH_TEST_ITERATIONS 2
28#endif // PW_FLASH_TEST_ITERATIONS
29
30constexpr size_t kTestIterations = PW_FLASH_TEST_ITERATIONS;
David Rogersca592962020-07-01 09:21:54 -070031
David Rogers6a6dae62020-07-10 01:13:38 -070032size_t error_count = 0;
David Rogersca592962020-07-01 09:21:54 -070033
David Rogersca592962020-07-01 09:21:54 -070034void WriteData(FlashPartition& partition, uint8_t fill_byte) {
David Rogers6a6dae62020-07-10 01:13:38 -070035 uint8_t test_data[kMaxFlashAlignment];
David Rogersca592962020-07-01 09:21:54 -070036 memset(test_data, fill_byte, sizeof(test_data));
37
David Rogers6a6dae62020-07-10 01:13:38 -070038 const size_t alignment = partition.alignment_bytes();
David Rogersca592962020-07-01 09:21:54 -070039
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080040 ASSERT_EQ(OkStatus(), partition.Erase(0, partition.sector_count()));
David Rogersca592962020-07-01 09:21:54 -070041
David Rogers6a6dae62020-07-10 01:13:38 -070042 const size_t chunks_per_sector = partition.sector_size_bytes() / alignment;
David Rogersca592962020-07-01 09:21:54 -070043
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();
48 sector_index++) {
49 FlashPartition::Address address =
50 sector_index * partition.sector_size_bytes();
51
52 for (size_t chunk_index = 0; chunk_index < chunks_per_sector;
53 chunk_index++) {
David Rogers6a6dae62020-07-10 01:13:38 -070054 StatusWithSize status =
55 partition.Write(address, as_bytes(std::span(test_data, alignment)));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080056 ASSERT_EQ(OkStatus(), status.status());
David Rogers6a6dae62020-07-10 01:13:38 -070057 ASSERT_EQ(alignment, status.size());
58 address += alignment;
David Rogersca592962020-07-01 09:21:54 -070059 }
60 }
61
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.
David Rogersca592962020-07-01 09:21:54 -070064 for (size_t sector_index = 0; sector_index < partition.sector_count();
65 sector_index++) {
66 FlashPartition::Address address =
67 sector_index * partition.sector_size_bytes();
68
69 for (size_t chunk_index = 0; chunk_index < chunks_per_sector;
70 chunk_index++) {
71 memset(test_data, 0, sizeof(test_data));
David Rogers6a6dae62020-07-10 01:13:38 -070072 StatusWithSize status = partition.Read(address, alignment, test_data);
David Rogersca592962020-07-01 09:21:54 -070073
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080074 EXPECT_EQ(OkStatus(), status.status());
David Rogers6a6dae62020-07-10 01:13:38 -070075 EXPECT_EQ(alignment, status.size());
76 if (!status.ok() || (alignment != status.size())) {
David Rogersca592962020-07-01 09:21:54 -070077 error_count++;
David Rogers6a6dae62020-07-10 01:13:38 -070078 PW_LOG_DEBUG(" Read Error [%s], %u of %u",
79 status.status().str(),
80 unsigned(status.size()),
81 unsigned(alignment));
David Rogersca592962020-07-01 09:21:54 -070082 continue;
83 }
84
David Rogers6a6dae62020-07-10 01:13:38 -070085 for (size_t i = 0; i < alignment; i++) {
David Rogersca592962020-07-01 09:21:54 -070086 if (test_data[i] != fill_byte) {
87 error_count++;
David Rogers6a6dae62020-07-10 01:13:38 -070088 PW_LOG_DEBUG(
89 " Error %u, Read compare @ address %x, got 0x%02x, "
90 "expected 0x%02x",
91 unsigned(error_count),
92 unsigned(address + i),
93 unsigned(test_data[i]),
94 unsigned(fill_byte));
David Rogersca592962020-07-01 09:21:54 -070095 }
96 }
97
David Rogers6a6dae62020-07-10 01:13:38 -070098 address += alignment;
David Rogersca592962020-07-01 09:21:54 -070099 }
100 }
101
102 EXPECT_EQ(error_count, 0U);
103 if (error_count != 0) {
104 PW_LOG_ERROR("Partition test, fill '%c', %u errors found",
105 fill_byte,
106 unsigned(error_count));
107 }
108}
109
David Rogers6a262b42020-07-09 03:27:41 -0700110TEST(FlashPartitionTest, FillTest) {
111 FlashPartition& test_partition = FlashTestPartition();
David Rogersca592962020-07-01 09:21:54 -0700112
David Rogers6a6dae62020-07-10 01:13:38 -0700113 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
David Rogersca592962020-07-01 09:21:54 -0700114
David Rogers6a262b42020-07-09 03:27:41 -0700115 for (size_t i = 0; i < kTestIterations; i++) {
David Rogers6a6dae62020-07-10 01:13:38 -0700116 PW_LOG_DEBUG("FillTest iteration %u, write '0'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700117 WriteData(test_partition, 0);
David Rogers6a6dae62020-07-10 01:13:38 -0700118 PW_LOG_DEBUG("FillTest iteration %u, write '0xff'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700119 WriteData(test_partition, 0xff);
David Rogers6a6dae62020-07-10 01:13:38 -0700120 PW_LOG_DEBUG("FillTest iteration %u, write '0x55'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700121 WriteData(test_partition, 0x55);
David Rogers6a6dae62020-07-10 01:13:38 -0700122 PW_LOG_DEBUG("FillTest iteration %u, write '0xa3'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700123 WriteData(test_partition, 0xa3);
David Rogers6a6dae62020-07-10 01:13:38 -0700124 PW_LOG_DEBUG("Completed iterations %u, Total errors %u",
125 unsigned(i),
126 unsigned(error_count));
David Rogersca592962020-07-01 09:21:54 -0700127 }
128}
129
David Rogers6a262b42020-07-09 03:27:41 -0700130TEST(FlashPartitionTest, EraseTest) {
131 FlashPartition& test_partition = FlashTestPartition();
132
David Rogersca592962020-07-01 09:21:54 -0700133 static const uint8_t fill_byte = 0x55;
David Rogers6a6dae62020-07-10 01:13:38 -0700134 uint8_t test_data[kMaxFlashAlignment];
David Rogersca592962020-07-01 09:21:54 -0700135 memset(test_data, fill_byte, sizeof(test_data));
136
David Rogers6a6dae62020-07-10 01:13:38 -0700137 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
David Rogersca592962020-07-01 09:21:54 -0700138
139 const size_t block_size =
David Rogers6a262b42020-07-09 03:27:41 -0700140 std::min(sizeof(test_data), test_partition.sector_size_bytes());
David Rogersca592962020-07-01 09:21:54 -0700141 auto data_span = std::span(test_data, block_size);
142
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800143 ASSERT_EQ(OkStatus(), test_partition.Erase(0, test_partition.sector_count()));
David Rogersca592962020-07-01 09:21:54 -0700144
145 // Write to the first page of each sector.
David Rogers6a262b42020-07-09 03:27:41 -0700146 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
David Rogersca592962020-07-01 09:21:54 -0700147 sector_index++) {
148 FlashPartition::Address address =
David Rogers6a262b42020-07-09 03:27:41 -0700149 sector_index * test_partition.sector_size_bytes();
David Rogersca592962020-07-01 09:21:54 -0700150
David Rogers6a262b42020-07-09 03:27:41 -0700151 StatusWithSize status = test_partition.Write(address, as_bytes(data_span));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800152 ASSERT_EQ(OkStatus(), status.status());
David Rogersca592962020-07-01 09:21:54 -0700153 ASSERT_EQ(block_size, status.size());
154 }
155
David Rogersa5661ef2020-07-09 15:15:12 -0700156 // Preset the flag to make sure the check actually sets it.
157 bool is_erased = true;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800158 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
David Rogersa5661ef2020-07-09 15:15:12 -0700159 ASSERT_EQ(false, is_erased);
160
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800161 ASSERT_EQ(OkStatus(), test_partition.Erase());
David Rogersca592962020-07-01 09:21:54 -0700162
David Rogersa5661ef2020-07-09 15:15:12 -0700163 // Preset the flag to make sure the check actually sets it.
164 is_erased = false;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800165 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
David Rogersca592962020-07-01 09:21:54 -0700166 ASSERT_EQ(true, is_erased);
167
168 // Read the first page of each sector and make sure it has been erased.
David Rogers6a262b42020-07-09 03:27:41 -0700169 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
David Rogersca592962020-07-01 09:21:54 -0700170 sector_index++) {
171 FlashPartition::Address address =
David Rogers6a262b42020-07-09 03:27:41 -0700172 sector_index * test_partition.sector_size_bytes();
David Rogersca592962020-07-01 09:21:54 -0700173
174 StatusWithSize status =
David Rogers6a262b42020-07-09 03:27:41 -0700175 test_partition.Read(address, data_span.size_bytes(), data_span.data());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800176 EXPECT_EQ(OkStatus(), status.status());
David Rogers6a6dae62020-07-10 01:13:38 -0700177 EXPECT_EQ(data_span.size_bytes(), status.size());
David Rogersca592962020-07-01 09:21:54 -0700178
David Rogers6a6dae62020-07-10 01:13:38 -0700179 EXPECT_EQ(true, test_partition.AppearsErased(as_bytes(data_span)));
David Rogersca592962020-07-01 09:21:54 -0700180 }
181}
182
David Rogers6a6dae62020-07-10 01:13:38 -0700183TEST(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();
187
188 EXPECT_LE(alignment, kMaxFlashAlignment);
David Rogers907570b2020-08-06 12:44:27 -0700189 EXPECT_GT(alignment, 0u);
David Rogers6a6dae62020-07-10 01:13:38 -0700190 EXPECT_EQ(kMaxFlashAlignment % alignment, 0U);
191 EXPECT_LE(kMaxFlashAlignment, sector_size_bytes);
192 EXPECT_LE(sector_size_bytes % kMaxFlashAlignment, 0U);
193}
194
Ewout van Bekkume4d7b692020-10-15 13:12:30 -0700195#define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
196#if TESTING_CHECK_FAILURES_IS_SUPPORTED
David Rogers907570b2020-08-06 12:44:27 -0700197// TODO: Ensure that this test triggers an assert.
198TEST(FlashPartitionTest, BadWriteAddressAlignment) {
199 FlashPartition& test_partition = FlashTestPartition();
200
201 // Can't get bad alignment with alignment of 1.
202 if (test_partition.alignment_bytes() == 1) {
203 return;
204 }
205
206 std::array<std::byte, kMaxFlashAlignment> source_data;
207 test_partition.Write(1, source_data);
208}
209
210// TODO: Ensure that this test triggers an assert.
211TEST(FlashPartitionTest, BadWriteSizeAlignment) {
212 FlashPartition& test_partition = FlashTestPartition();
213
214 // Can't get bad alignment with alignment of 1.
215 if (test_partition.alignment_bytes() == 1) {
216 return;
217 }
218
219 std::array<std::byte, 1> source_data;
220 test_partition.Write(0, source_data);
221}
222
223// TODO: Ensure that this test triggers an assert.
224TEST(FlashPartitionTest, BadEraseAddressAlignment) {
225 FlashPartition& test_partition = FlashTestPartition();
226
227 // Can't get bad alignment with sector size of 1.
228 if (test_partition.sector_size_bytes() == 1) {
229 return;
230 }
231
232 // Try Erase at address 1 for 1 sector.
233 test_partition.Erase(1, 1);
234}
235
Ewout van Bekkume4d7b692020-10-15 13:12:30 -0700236#endif // TESTING_CHECK_FAILURES_IS_SUPPORTED
David Rogers907570b2020-08-06 12:44:27 -0700237
David Rogers6a6dae62020-07-10 01:13:38 -0700238TEST(FlashPartitionTest, IsErased) {
239 FlashPartition& test_partition = FlashTestPartition();
240 const size_t alignment = test_partition.alignment_bytes();
241
242 // Make sure the partition is big enough to do this test.
243 ASSERT_GE(test_partition.size_bytes(), 3 * kMaxFlashAlignment);
244
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800245 ASSERT_EQ(OkStatus(), test_partition.Erase());
David Rogers6a6dae62020-07-10 01:13:38 -0700246
247 bool is_erased = true;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800248 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
David Rogers6a6dae62020-07-10 01:13:38 -0700249 ASSERT_EQ(true, is_erased);
250
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);
255
256 // Write the chunk with fill byte.
257 StatusWithSize status = test_partition.Write(alignment, as_bytes(data_span));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800258 ASSERT_EQ(OkStatus(), status.status());
David Rogers6a6dae62020-07-10 01:13:38 -0700259 ASSERT_EQ(data_span.size_bytes(), status.size());
260
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800261 EXPECT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
David Rogers6a6dae62020-07-10 01:13:38 -0700262 EXPECT_EQ(false, is_erased);
263
264 // Check the chunk that was written.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800265 EXPECT_EQ(OkStatus(),
David Rogers6a6dae62020-07-10 01:13:38 -0700266 test_partition.IsRegionErased(
267 alignment, data_span.size_bytes(), &is_erased));
268 EXPECT_EQ(false, is_erased);
269
270 // Check a region that starts erased but later has been written.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800271 EXPECT_EQ(OkStatus(),
David Rogers6a6dae62020-07-10 01:13:38 -0700272 test_partition.IsRegionErased(0, 2 * alignment, &is_erased));
273 EXPECT_EQ(false, is_erased);
274
275 // Check erased for a region smaller than kMaxFlashAlignment. This has been a
276 // bug in the past.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800277 EXPECT_EQ(OkStatus(),
David Rogers6a6dae62020-07-10 01:13:38 -0700278 test_partition.IsRegionErased(0, alignment, &is_erased));
279 EXPECT_EQ(true, is_erased);
280}
281
David Rogers6a262b42020-07-09 03:27:41 -0700282} // namespace
David Rogersca592962020-07-01 09:21:54 -0700283} // namespace pw::kvs::PartitionTest