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