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