1 // 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
17 #include <cstring>
18 #include <string_view>
19
20 #include "pw_status/status_with_size.h"
21 #include "pw_unit_test/framework.h"
22
23 namespace pw::kvs {
24 namespace {
25
26 using namespace std::string_view_literals;
27 using std::byte;
28
29 constexpr size_t kAlignment = 10;
30
31 constexpr std::string_view kData =
32 "123456789_123456789_123456789_123456789_123456789_" // 50
33 "123456789_123456789_123456789_123456789_123456789_"; // 100
34
35 const span<const byte> kBytes = as_bytes(span(kData));
36
37 // The output function checks that the data is properly aligned and matches
38 // the expected value (should always be 123456789_...).
__anond81da1560202(span<const byte> data) 39 OutputToFunction check_against_data([](span<const byte> data) {
40 EXPECT_EQ(data.size() % kAlignment, 0u);
41 EXPECT_EQ(kData.substr(0, data.size()),
42 std::string_view(reinterpret_cast<const char*>(data.data()),
43 data.size()));
44 return StatusWithSize(data.size());
45 });
46
TEST(AlignedWriter,Write_VaryingLengths)47 TEST(AlignedWriter, Write_VaryingLengths) {
48 AlignedWriterBuffer<32> writer(kAlignment, check_against_data);
49
50 // Write values smaller than the alignment.
51 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(0, 1)).status());
52 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(1, 9)).status());
53
54 // Write values larger than the alignment but smaller than the buffer.
55 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(10, 11)).status());
56
57 // Exactly fill the remainder of the buffer.
58 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(21, 11)).status());
59
60 // Fill the buffer more than once.
61 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(32, 66)).status());
62
63 // Write nothing.
64 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(98, 0)).status());
65
66 // Write the remaining data.
67 EXPECT_EQ(OkStatus(), writer.Write(kBytes.subspan(98, 2)).status());
68
69 auto result = writer.Flush();
70 EXPECT_EQ(OkStatus(), result.status());
71 EXPECT_EQ(kData.size(), result.size());
72 }
73
TEST(AlignedWriter,DestructorFlushes)74 TEST(AlignedWriter, DestructorFlushes) {
75 static size_t called_with_bytes;
76 called_with_bytes = 0;
77
78 OutputToFunction output([](span<const byte> data) {
79 called_with_bytes += data.size();
80 return StatusWithSize(data.size());
81 });
82
83 {
84 AlignedWriterBuffer<64> writer(3, output);
85 ASSERT_EQ(OkStatus(),
86 writer.Write(as_bytes(span("What is this?"))).status());
87 EXPECT_EQ(called_with_bytes, 0u); // Buffer not full; no output yet.
88 }
89
90 EXPECT_EQ(called_with_bytes, AlignUp(sizeof("What is this?"), 3));
91 }
92
93 // Output class that can be programmed to fail for testing purposes.
94 // TODO(hepler): If we create a general pw_io / pw_stream module, this and
95 // InputWithErrorInjection should be made into generic test utility classes,
96 // similar to FakeFlashMemory.
97 struct OutputWithErrorInjection final : public Output {
98 public:
99 enum { kKeepGoing, kBreakOnNext, kBroken } state = kKeepGoing;
100
101 private:
DoWritepw::kvs::__anond81da1560111::OutputWithErrorInjection102 StatusWithSize DoWrite(span<const byte> data) override {
103 switch (state) {
104 case kKeepGoing:
105 return StatusWithSize(data.size());
106 case kBreakOnNext:
107 state = kBroken;
108 break;
109 case kBroken:
110 ADD_FAILURE();
111 break;
112 }
113 return StatusWithSize::Unknown(data.size());
114 }
115 };
116
TEST(AlignedWriter,Write_NoFurtherWritesOnFailure)117 TEST(AlignedWriter, Write_NoFurtherWritesOnFailure) {
118 OutputWithErrorInjection output;
119
120 {
121 AlignedWriterBuffer<4> writer(3, output);
122 ASSERT_EQ(OkStatus(),
123 writer.Write(as_bytes(span("Everything is fine."))).status());
124 output.state = OutputWithErrorInjection::kBreakOnNext;
125 EXPECT_EQ(Status::Unknown(),
126 writer.Write(as_bytes(span("No more writes, okay?"))).status());
127 }
128 }
129
TEST(AlignedWriter,Write_ReturnsTotalBytesWritten)130 TEST(AlignedWriter, Write_ReturnsTotalBytesWritten) {
131 static Status return_status;
132 return_status = OkStatus();
133
134 OutputToFunction output([](span<const byte> data) {
135 return StatusWithSize(return_status, data.size());
136 });
137
138 AlignedWriterBuffer<22> writer(10, output);
139
140 StatusWithSize result = writer.Write(as_bytes(span("12345678901"sv)));
141 EXPECT_EQ(OkStatus(), result.status());
142 EXPECT_EQ(0u, result.size()); // No writes; haven't filled buffer.
143
144 result = writer.Write(as_bytes(span("2345678901"sv)));
145 EXPECT_EQ(OkStatus(), result.status());
146 EXPECT_EQ(20u, result.size());
147
148 return_status = Status::PermissionDenied();
149
150 result = writer.Write(as_bytes(span("2345678901234567890"sv)));
151 EXPECT_EQ(Status::PermissionDenied(), result.status());
152 EXPECT_EQ(40u, result.size());
153 }
154
TEST(AlignedWriter,Flush_Ok_ReturnsTotalBytesWritten)155 TEST(AlignedWriter, Flush_Ok_ReturnsTotalBytesWritten) {
156 OutputToFunction output(
157 [](span<const byte> data) { return StatusWithSize(data.size()); });
158
159 AlignedWriterBuffer<4> writer(2, output);
160
161 EXPECT_EQ(OkStatus(), writer.Write(as_bytes(span("12345678901"sv))).status());
162
163 StatusWithSize result = writer.Flush();
164 EXPECT_EQ(OkStatus(), result.status());
165 EXPECT_EQ(12u, result.size());
166 }
167
TEST(AlignedWriter,Flush_Error_ReturnsTotalBytesWritten)168 TEST(AlignedWriter, Flush_Error_ReturnsTotalBytesWritten) {
169 OutputToFunction output([](span<const byte> data) {
170 return StatusWithSize::Aborted(data.size());
171 });
172
173 AlignedWriterBuffer<20> writer(10, output);
174
175 EXPECT_EQ(0u, writer.Write(as_bytes(span("12345678901"sv))).size());
176
177 StatusWithSize result = writer.Flush();
178 EXPECT_EQ(Status::Aborted(), result.status());
179 EXPECT_EQ(20u, result.size());
180 }
181
182 // Input class that can be programmed to fail for testing purposes.
183 class InputWithErrorInjection final : public Input {
184 public:
BreakOnIndex(size_t index)185 void BreakOnIndex(size_t index) { break_on_index_ = index; }
186
187 private:
DoRead(span<byte> data)188 StatusWithSize DoRead(span<byte> data) override {
189 EXPECT_LE(index_ + data.size(), kBytes.size());
190
191 if (index_ + data.size() > kBytes.size()) {
192 return StatusWithSize::Internal();
193 }
194
195 // Check if reading from the index that was programmed to cause an error.
196 if (index_ <= break_on_index_ && break_on_index_ <= index_ + data.size()) {
197 return StatusWithSize::Aborted();
198 }
199
200 std::memcpy(data.data(), kBytes.data(), data.size());
201 index_ += data.size();
202 return StatusWithSize(data.size());
203 }
204
205 size_t index_ = 0;
206 size_t break_on_index_ = size_t(-1);
207 };
208
TEST(AlignedWriter,WriteFromInput_Successful)209 TEST(AlignedWriter, WriteFromInput_Successful) {
210 AlignedWriterBuffer<32> writer(kAlignment, check_against_data);
211
212 InputWithErrorInjection input;
213 StatusWithSize result = writer.Write(input, kData.size());
214 EXPECT_EQ(OkStatus(), result.status());
215 EXPECT_LE(result.size(), kData.size()); // May not have written it all yet.
216
217 result = writer.Flush();
218 EXPECT_EQ(OkStatus(), result.status());
219 EXPECT_EQ(kData.size(), result.size());
220 }
221
TEST(AlignedWriter,WriteFromInput_InputError)222 TEST(AlignedWriter, WriteFromInput_InputError) {
223 AlignedWriterBuffer<kAlignment> writer(kAlignment, check_against_data);
224
225 InputWithErrorInjection input;
226 input.BreakOnIndex(kAlignment + 2);
227
228 StatusWithSize result = writer.Write(input, kData.size());
229 EXPECT_EQ(Status::Aborted(), result.status());
230 EXPECT_LE(result.size(), kAlignment); // Wrote the first chunk, nothing more.
231 }
232
TEST(AlignedWriter,WriteFromInput_OutputError)233 TEST(AlignedWriter, WriteFromInput_OutputError) {
234 InputWithErrorInjection input;
235 OutputWithErrorInjection output;
236
237 AlignedWriterBuffer<4> writer(3, output);
238 output.state = OutputWithErrorInjection::kBreakOnNext;
239
240 StatusWithSize result = writer.Write(input, kData.size());
241 EXPECT_EQ(Status::Unknown(), result.status());
242 EXPECT_EQ(3u, result.size()); // Attempted to write 3 bytes.
243 }
244
245 } // namespace
246 } // namespace pw::kvs
247