blob: fca1a7fc16d8bfa475a924a42a60eeb3c013e7d4 [file] [log] [blame]
Wyatt Hepler1927c282020-02-11 16:45:02 -08001// 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
15#include "pw_kvs/alignment.h"
16
Wyatt Hepler28904602020-03-09 08:45:51 -070017#include <cstring>
Wyatt Hepler1927c282020-02-11 16:45:02 -080018#include <string_view>
19
20#include "gtest/gtest.h"
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -080021#include "pw_status/status_with_size.h"
Wyatt Hepler1927c282020-02-11 16:45:02 -080022
23namespace pw::kvs {
24namespace {
25
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -080026using namespace std::string_view_literals;
Wyatt Hepler1927c282020-02-11 16:45:02 -080027using std::byte;
28
29TEST(AlignUp, Zero) {
30 EXPECT_EQ(0u, AlignUp(0, 1));
31 EXPECT_EQ(0u, AlignUp(0, 2));
32 EXPECT_EQ(0u, AlignUp(0, 15));
33}
34
35TEST(AlignUp, Aligned) {
36 for (size_t i = 1; i < 130; ++i) {
37 EXPECT_EQ(i, AlignUp(i, i));
38 EXPECT_EQ(2 * i, AlignUp(2 * i, i));
39 EXPECT_EQ(3 * i, AlignUp(3 * i, i));
40 }
41}
42
43TEST(AlignUp, NonAligned_PowerOf2) {
44 EXPECT_EQ(32u, AlignUp(1, 32));
45 EXPECT_EQ(32u, AlignUp(31, 32));
46 EXPECT_EQ(64u, AlignUp(33, 32));
47 EXPECT_EQ(64u, AlignUp(45, 32));
48 EXPECT_EQ(64u, AlignUp(63, 32));
49 EXPECT_EQ(128u, AlignUp(127, 32));
50}
51
52TEST(AlignUp, NonAligned_NonPowerOf2) {
53 EXPECT_EQ(2u, AlignUp(1, 2));
54
55 EXPECT_EQ(15u, AlignUp(1, 15));
56 EXPECT_EQ(15u, AlignUp(14, 15));
57 EXPECT_EQ(30u, AlignUp(16, 15));
58}
59
60TEST(AlignDown, Zero) {
61 EXPECT_EQ(0u, AlignDown(0, 1));
62 EXPECT_EQ(0u, AlignDown(0, 2));
63 EXPECT_EQ(0u, AlignDown(0, 15));
64}
65
66TEST(AlignDown, Aligned) {
67 for (size_t i = 1; i < 130; ++i) {
68 EXPECT_EQ(i, AlignDown(i, i));
69 EXPECT_EQ(2 * i, AlignDown(2 * i, i));
70 EXPECT_EQ(3 * i, AlignDown(3 * i, i));
71 }
72}
73
74TEST(AlignDown, NonAligned_PowerOf2) {
75 EXPECT_EQ(0u, AlignDown(1, 32));
76 EXPECT_EQ(0u, AlignDown(31, 32));
77 EXPECT_EQ(32u, AlignDown(33, 32));
78 EXPECT_EQ(32u, AlignDown(45, 32));
79 EXPECT_EQ(32u, AlignDown(63, 32));
80 EXPECT_EQ(96u, AlignDown(127, 32));
81}
82
83TEST(AlignDown, NonAligned_NonPowerOf2) {
84 EXPECT_EQ(0u, AlignDown(1, 2));
85
86 EXPECT_EQ(0u, AlignDown(1, 15));
87 EXPECT_EQ(0u, AlignDown(14, 15));
88 EXPECT_EQ(15u, AlignDown(16, 15));
89}
90
Wyatt Hepler38cfa982020-02-12 11:01:49 -080091TEST(Padding, Zero) {
92 EXPECT_EQ(0u, Padding(0, 1));
93 EXPECT_EQ(0u, Padding(0, 2));
94 EXPECT_EQ(0u, Padding(0, 15));
95}
96
97TEST(Padding, Aligned) {
98 for (size_t i = 1; i < 130; ++i) {
99 EXPECT_EQ(0u, Padding(i, i));
100 EXPECT_EQ(0u, Padding(2 * i, i));
101 EXPECT_EQ(0u, Padding(3 * i, i));
102 }
103}
104
105TEST(Padding, NonAligned_PowerOf2) {
106 EXPECT_EQ(31u, Padding(1, 32));
107 EXPECT_EQ(1u, Padding(31, 32));
108 EXPECT_EQ(31u, Padding(33, 32));
109 EXPECT_EQ(19u, Padding(45, 32));
110 EXPECT_EQ(1u, Padding(63, 32));
111 EXPECT_EQ(1u, Padding(127, 32));
112}
113
114TEST(Padding, NonAligned_NonPowerOf2) {
115 EXPECT_EQ(1u, Padding(1, 2));
116
117 EXPECT_EQ(14u, Padding(1, 15));
118 EXPECT_EQ(1u, Padding(14, 15));
119 EXPECT_EQ(14u, Padding(16, 15));
120}
121
Wyatt Hepler28904602020-03-09 08:45:51 -0700122constexpr size_t kAlignment = 10;
123
Wyatt Hepler1927c282020-02-11 16:45:02 -0800124constexpr std::string_view kData =
125 "123456789_123456789_123456789_123456789_123456789_" // 50
126 "123456789_123456789_123456789_123456789_123456789_"; // 100
127
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700128const std::span<const byte> kBytes = std::as_bytes(std::span(kData));
Wyatt Hepler1927c282020-02-11 16:45:02 -0800129
Wyatt Hepler28904602020-03-09 08:45:51 -0700130// The output function checks that the data is properly aligned and matches
131// the expected value (should always be 123456789_...).
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700132OutputToFunction check_against_data([](std::span<const byte> data) {
Wyatt Hepler28904602020-03-09 08:45:51 -0700133 EXPECT_EQ(data.size() % kAlignment, 0u);
134 EXPECT_EQ(kData.substr(0, data.size()),
135 std::string_view(reinterpret_cast<const char*>(data.data()),
136 data.size()));
137 return StatusWithSize(data.size());
138});
Wyatt Hepler1927c282020-02-11 16:45:02 -0800139
Wyatt Hepler28904602020-03-09 08:45:51 -0700140TEST(AlignedWriter, Write_VaryingLengths) {
141 AlignedWriterBuffer<32> writer(kAlignment, check_against_data);
Wyatt Hepler1927c282020-02-11 16:45:02 -0800142
Wyatt Heplerc656af22020-02-12 14:49:14 -0800143 // Write values smaller than the alignment.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800144 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(0, 1)).status());
145 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(1, 9)).status());
Wyatt Heplerc656af22020-02-12 14:49:14 -0800146
147 // Write values larger than the alignment but smaller than the buffer.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800148 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(10, 11)).status());
Wyatt Heplerc656af22020-02-12 14:49:14 -0800149
150 // Exactly fill the remainder of the buffer.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800151 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(21, 11)).status());
Wyatt Heplerc656af22020-02-12 14:49:14 -0800152
153 // Fill the buffer more than once.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800154 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(32, 66)).status());
Wyatt Heplerc656af22020-02-12 14:49:14 -0800155
156 // Write nothing.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800157 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(98, 0)).status());
Wyatt Heplerc656af22020-02-12 14:49:14 -0800158
159 // Write the remaining data.
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800160 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(98, 2)).status());
Wyatt Hepler1927c282020-02-11 16:45:02 -0800161
162 auto result = writer.Flush();
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800163 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler1927c282020-02-11 16:45:02 -0800164 EXPECT_EQ(kData.size(), result.size());
165}
166
167TEST(AlignedWriter, DestructorFlushes) {
168 static size_t called_with_bytes;
Wyatt Hepler1927c282020-02-11 16:45:02 -0800169 called_with_bytes = 0;
170
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700171 OutputToFunction output([](std::span<const byte> data) {
Wyatt Hepler1927c282020-02-11 16:45:02 -0800172 called_with_bytes += data.size();
173 return StatusWithSize(data.size());
174 });
175
176 {
177 AlignedWriterBuffer<64> writer(3, output);
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700178 writer.Write(std::as_bytes(std::span("What is this?")));
Wyatt Hepler1927c282020-02-11 16:45:02 -0800179 EXPECT_EQ(called_with_bytes, 0u); // Buffer not full; no output yet.
180 }
181
182 EXPECT_EQ(called_with_bytes, AlignUp(sizeof("What is this?"), 3));
183}
184
Wyatt Hepler28904602020-03-09 08:45:51 -0700185// Output class that can be programmed to fail for testing purposes.
186// TODO(hepler): If we create a general pw_io / pw_stream module, this and
187// InputWithErrorInjection should be made into generic test utility classes,
David Rogersd64cc012020-05-26 12:37:37 -0700188// similar to FakeFlashMemory.
Wyatt Hepler28904602020-03-09 08:45:51 -0700189struct OutputWithErrorInjection final : public Output {
190 public:
191 enum { kKeepGoing, kBreakOnNext, kBroken } state = kKeepGoing;
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800192
Wyatt Hepler28904602020-03-09 08:45:51 -0700193 private:
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700194 StatusWithSize DoWrite(std::span<const byte> data) override {
Wyatt Hepler28904602020-03-09 08:45:51 -0700195 switch (state) {
196 case kKeepGoing:
197 return StatusWithSize(data.size());
198 case kBreakOnNext:
199 state = kBroken;
200 break;
201 case kBroken:
202 ADD_FAILURE();
203 break;
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800204 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700205 return StatusWithSize::Unknown(data.size());
Wyatt Hepler28904602020-03-09 08:45:51 -0700206 }
207};
208
209TEST(AlignedWriter, Write_NoFurtherWritesOnFailure) {
210 OutputWithErrorInjection output;
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800211
212 {
213 AlignedWriterBuffer<4> writer(3, output);
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700214 writer.Write(std::as_bytes(std::span("Everything is fine.")));
Wyatt Hepler28904602020-03-09 08:45:51 -0700215 output.state = OutputWithErrorInjection::kBreakOnNext;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700216 EXPECT_EQ(Status::Unknown(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700217 writer.Write(std::as_bytes(std::span("No more writes, okay?")))
218 .status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800219 writer.Flush();
220 }
221}
222
223TEST(AlignedWriter, Write_ReturnsTotalBytesWritten) {
224 static Status return_status;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800225 return_status = OkStatus();
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800226
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700227 OutputToFunction output([](std::span<const byte> data) {
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800228 return StatusWithSize(return_status, data.size());
229 });
230
231 AlignedWriterBuffer<22> writer(10, output);
232
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700233 StatusWithSize result =
234 writer.Write(std::as_bytes(std::span("12345678901"sv)));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800235 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800236 EXPECT_EQ(0u, result.size()); // No writes; haven't filled buffer.
237
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700238 result = writer.Write(std::as_bytes(std::span("2345678901"sv)));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800239 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800240 EXPECT_EQ(20u, result.size());
241
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700242 return_status = Status::PermissionDenied();
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800243
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700244 result = writer.Write(std::as_bytes(std::span("2345678901234567890"sv)));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700245 EXPECT_EQ(Status::PermissionDenied(), result.status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800246 EXPECT_EQ(40u, result.size());
247}
248
249TEST(AlignedWriter, Flush_Ok_ReturnsTotalBytesWritten) {
250 OutputToFunction output(
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700251 [](std::span<const byte> data) { return StatusWithSize(data.size()); });
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800252
253 AlignedWriterBuffer<4> writer(2, output);
254
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800255 EXPECT_EQ(OkStatus(),
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700256 writer.Write(std::as_bytes(std::span("12345678901"sv))).status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800257
258 StatusWithSize result = writer.Flush();
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800259 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800260 EXPECT_EQ(12u, result.size());
261}
262
263TEST(AlignedWriter, Flush_Error_ReturnsTotalBytesWritten) {
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700264 OutputToFunction output([](std::span<const byte> data) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700265 return StatusWithSize::Aborted(data.size());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800266 });
267
268 AlignedWriterBuffer<20> writer(10, output);
269
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700270 EXPECT_EQ(0u, writer.Write(std::as_bytes(std::span("12345678901"sv))).size());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800271
272 StatusWithSize result = writer.Flush();
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700273 EXPECT_EQ(Status::Aborted(), result.status());
Wyatt Hepler0f2ad9f2020-02-25 16:58:55 -0800274 EXPECT_EQ(20u, result.size());
275}
276
Wyatt Hepler28904602020-03-09 08:45:51 -0700277// Input class that can be programmed to fail for testing purposes.
278class InputWithErrorInjection final : public Input {
279 public:
280 void BreakOnIndex(size_t index) { break_on_index_ = index; }
281
282 private:
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700283 StatusWithSize DoRead(std::span<byte> data) override {
Wyatt Hepler28904602020-03-09 08:45:51 -0700284 EXPECT_LE(index_ + data.size(), kBytes.size());
285
286 if (index_ + data.size() > kBytes.size()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700287 return StatusWithSize::Internal();
Wyatt Hepler28904602020-03-09 08:45:51 -0700288 }
289
290 // Check if reading from the index that was programmed to cause an error.
291 if (index_ <= break_on_index_ && break_on_index_ <= index_ + data.size()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700292 return StatusWithSize::Aborted();
Wyatt Hepler28904602020-03-09 08:45:51 -0700293 }
294
295 std::memcpy(data.data(), kBytes.data(), data.size());
296 index_ += data.size();
297 return StatusWithSize(data.size());
298 }
299
300 size_t index_ = 0;
301 size_t break_on_index_ = size_t(-1);
302};
303
304TEST(AlignedWriter, WriteFromInput_Successful) {
305 AlignedWriterBuffer<32> writer(kAlignment, check_against_data);
306
307 InputWithErrorInjection input;
308 StatusWithSize result = writer.Write(input, kData.size());
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800309 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler28904602020-03-09 08:45:51 -0700310 EXPECT_LE(result.size(), kData.size()); // May not have written it all yet.
311
312 result = writer.Flush();
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800313 EXPECT_EQ(OkStatus(), result.status());
Wyatt Hepler28904602020-03-09 08:45:51 -0700314 EXPECT_EQ(kData.size(), result.size());
315}
316
317TEST(AlignedWriter, WriteFromInput_InputError) {
318 AlignedWriterBuffer<kAlignment> writer(kAlignment, check_against_data);
319
320 InputWithErrorInjection input;
321 input.BreakOnIndex(kAlignment + 2);
322
323 StatusWithSize result = writer.Write(input, kData.size());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700324 EXPECT_EQ(Status::Aborted(), result.status());
Wyatt Hepler28904602020-03-09 08:45:51 -0700325 EXPECT_LE(result.size(), kAlignment); // Wrote the first chunk, nothing more.
326}
327
328TEST(AlignedWriter, WriteFromInput_OutputError) {
329 InputWithErrorInjection input;
330 OutputWithErrorInjection output;
331
332 AlignedWriterBuffer<4> writer(3, output);
333 output.state = OutputWithErrorInjection::kBreakOnNext;
334
335 StatusWithSize result = writer.Write(input, kData.size());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700336 EXPECT_EQ(Status::Unknown(), result.status());
Wyatt Hepler28904602020-03-09 08:45:51 -0700337 EXPECT_EQ(3u, result.size()); // Attempted to write 3 bytes.
338}
339
Wyatt Hepler1927c282020-02-11 16:45:02 -0800340} // namespace
341} // namespace pw::kvs