xref: /aosp_15_r20/external/pigweed/pw_kvs/alignment_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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