1 // Copyright 2023 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_multibuf/multibuf.h"
16
17 #include "pw_assert/check.h"
18 #include "pw_bytes/suffix.h"
19 #include "pw_multibuf_private/test_utils.h"
20 #include "pw_span/span.h"
21 #include "pw_unit_test/framework.h"
22
23 namespace pw::multibuf {
24 namespace {
25
26 using namespace pw::multibuf::test_utils;
27
28 #if __cplusplus >= 202002L
29 static_assert(std::forward_iterator<MultiBuf::iterator>);
30 static_assert(std::forward_iterator<MultiBuf::const_iterator>);
31 static_assert(std::forward_iterator<MultiBuf::ChunkIterator>);
32 static_assert(std::forward_iterator<MultiBuf::ConstChunkIterator>);
33 #endif // __cplusplus >= 202002L
34
TEST(MultiBuf,IsDefaultConstructible)35 TEST(MultiBuf, IsDefaultConstructible) { [[maybe_unused]] MultiBuf buf; }
36
TEST(MultiBuf,WithOneChunkReleases)37 TEST(MultiBuf, WithOneChunkReleases) {
38 AllocatorForTest<kArbitraryAllocatorSize> allocator;
39 const auto& metrics = allocator.metrics();
40 MultiBuf buf;
41 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
42 EXPECT_EQ(metrics.num_allocations.value(), 2U);
43 buf.Release();
44 EXPECT_EQ(metrics.num_deallocations.value(), 2U);
45 }
46
TEST(MultiBuf,WithOneChunkReleasesOnDestruction)47 TEST(MultiBuf, WithOneChunkReleasesOnDestruction) {
48 AllocatorForTest<kArbitraryAllocatorSize> allocator;
49 const auto& metrics = allocator.metrics();
50 {
51 MultiBuf buf;
52 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
53 EXPECT_EQ(metrics.num_allocations.value(), 2U);
54 }
55 EXPECT_EQ(metrics.num_deallocations.value(), 2U);
56 }
57
TEST(MultiBuf,WithMultipleChunksReleasesAllOnDestruction)58 TEST(MultiBuf, WithMultipleChunksReleasesAllOnDestruction) {
59 AllocatorForTest<kArbitraryAllocatorSize> allocator;
60 const auto& metrics = allocator.metrics();
61 {
62 MultiBuf buf;
63 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
64 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
65 EXPECT_EQ(metrics.num_allocations.value(), 4U);
66 }
67 EXPECT_EQ(metrics.num_deallocations.value(), 4U);
68 }
69
TEST(MultiBuf,SizeReturnsNumberOfBytes)70 TEST(MultiBuf, SizeReturnsNumberOfBytes) {
71 AllocatorForTest<kArbitraryAllocatorSize> allocator;
72 MultiBuf buf;
73 EXPECT_EQ(buf.size(), 0U);
74 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
75 EXPECT_EQ(buf.size(), kArbitraryChunkSize);
76 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
77 EXPECT_EQ(buf.size(), kArbitraryChunkSize * 2);
78 }
79
TEST(MultiBuf,EmptyIfNoChunks)80 TEST(MultiBuf, EmptyIfNoChunks) {
81 AllocatorForTest<kArbitraryAllocatorSize> allocator;
82 MultiBuf buf;
83 EXPECT_EQ(buf.size(), 0U);
84 EXPECT_TRUE(buf.empty());
85 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
86 EXPECT_NE(buf.size(), 0U);
87 EXPECT_FALSE(buf.empty());
88 }
89
TEST(MultiBuf,EmptyIfOnlyEmptyChunks)90 TEST(MultiBuf, EmptyIfOnlyEmptyChunks) {
91 AllocatorForTest<kArbitraryAllocatorSize> allocator;
92 MultiBuf buf;
93 EXPECT_TRUE(buf.empty());
94 buf.PushFrontChunk(MakeChunk(allocator, 0));
95 EXPECT_TRUE(buf.empty());
96 buf.PushFrontChunk(MakeChunk(allocator, 0));
97 EXPECT_TRUE(buf.empty());
98 EXPECT_EQ(buf.size(), 0U);
99 }
100
TEST(MultiBuf,EmptyIsFalseIfAnyNonEmptyChunks)101 TEST(MultiBuf, EmptyIsFalseIfAnyNonEmptyChunks) {
102 AllocatorForTest<kArbitraryAllocatorSize> allocator;
103 MultiBuf buf;
104 buf.PushFrontChunk(MakeChunk(allocator, 0));
105 EXPECT_TRUE(buf.empty());
106 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
107 EXPECT_FALSE(buf.empty());
108 EXPECT_EQ(buf.size(), kArbitraryChunkSize);
109 }
110
TEST(MultiBuf,ClaimPrefixReclaimsFirstChunkPrefix)111 TEST(MultiBuf, ClaimPrefixReclaimsFirstChunkPrefix) {
112 AllocatorForTest<kArbitraryAllocatorSize> allocator;
113 MultiBuf buf;
114 OwnedChunk chunk = MakeChunk(allocator, 16);
115 chunk->DiscardPrefix(7);
116 buf.PushFrontChunk(std::move(chunk));
117 EXPECT_EQ(buf.size(), 9U);
118 EXPECT_EQ(buf.ClaimPrefix(7), true);
119 EXPECT_EQ(buf.size(), 16U);
120 }
121
TEST(MultiBuf,ClaimPrefixOnFirstChunkWithoutPrefixReturnsFalse)122 TEST(MultiBuf, ClaimPrefixOnFirstChunkWithoutPrefixReturnsFalse) {
123 AllocatorForTest<kArbitraryAllocatorSize> allocator;
124 MultiBuf buf;
125 buf.PushFrontChunk(MakeChunk(allocator, 16));
126 EXPECT_EQ(buf.size(), 16U);
127 EXPECT_EQ(buf.ClaimPrefix(7), false);
128 EXPECT_EQ(buf.size(), 16U);
129 }
130
TEST(MultiBuf,ClaimPrefixWithoutChunksReturnsFalse)131 TEST(MultiBuf, ClaimPrefixWithoutChunksReturnsFalse) {
132 AllocatorForTest<kArbitraryAllocatorSize> allocator;
133 MultiBuf buf;
134 EXPECT_EQ(buf.size(), 0U);
135 EXPECT_EQ(buf.ClaimPrefix(7), false);
136 EXPECT_EQ(buf.size(), 0U);
137 }
138
TEST(MultiBuf,ClaimSuffixReclaimsLastChunkSuffix)139 TEST(MultiBuf, ClaimSuffixReclaimsLastChunkSuffix) {
140 AllocatorForTest<kArbitraryAllocatorSize> allocator;
141 MultiBuf buf;
142 OwnedChunk chunk = MakeChunk(allocator, 16U);
143 chunk->Truncate(9U);
144 buf.PushFrontChunk(std::move(chunk));
145 buf.PushFrontChunk(MakeChunk(allocator, 4U));
146 EXPECT_EQ(buf.size(), 13U);
147 EXPECT_EQ(buf.ClaimSuffix(7U), true);
148 EXPECT_EQ(buf.size(), 20U);
149 }
150
TEST(MultiBuf,ClaimSuffixOnLastChunkWithoutSuffixReturnsFalse)151 TEST(MultiBuf, ClaimSuffixOnLastChunkWithoutSuffixReturnsFalse) {
152 AllocatorForTest<kArbitraryAllocatorSize> allocator;
153 MultiBuf buf;
154 buf.PushFrontChunk(MakeChunk(allocator, 16U));
155 EXPECT_EQ(buf.size(), 16U);
156 EXPECT_EQ(buf.ClaimPrefix(7U), false);
157 EXPECT_EQ(buf.size(), 16U);
158 }
159
TEST(MultiBuf,ClaimSuffixWithoutChunksReturnsFalse)160 TEST(MultiBuf, ClaimSuffixWithoutChunksReturnsFalse) {
161 AllocatorForTest<kArbitraryAllocatorSize> allocator;
162 MultiBuf buf;
163 EXPECT_EQ(buf.size(), 0U);
164 EXPECT_EQ(buf.ClaimSuffix(7U), false);
165 EXPECT_EQ(buf.size(), 0U);
166 }
167
TEST(MultiBuf,DiscardPrefixWithZeroDoesNothing)168 TEST(MultiBuf, DiscardPrefixWithZeroDoesNothing) {
169 AllocatorForTest<kArbitraryAllocatorSize> allocator;
170 MultiBuf buf;
171 buf.DiscardPrefix(0);
172 EXPECT_EQ(buf.size(), 0U);
173 }
174
TEST(MultiBuf,DiscardPrefixDiscardsPartialChunk)175 TEST(MultiBuf, DiscardPrefixDiscardsPartialChunk) {
176 AllocatorForTest<kArbitraryAllocatorSize> allocator;
177 MultiBuf buf;
178 buf.PushFrontChunk(MakeChunk(allocator, 16U));
179 buf.DiscardPrefix(5U);
180 EXPECT_EQ(buf.size(), 11U);
181 }
182
TEST(MultiBuf,DiscardPrefixDiscardsWholeChunk)183 TEST(MultiBuf, DiscardPrefixDiscardsWholeChunk) {
184 AllocatorForTest<kArbitraryAllocatorSize> allocator;
185 MultiBuf buf;
186 buf.PushFrontChunk(MakeChunk(allocator, 16U));
187 buf.PushFrontChunk(MakeChunk(allocator, 3U));
188 buf.DiscardPrefix(16U);
189 EXPECT_EQ(buf.size(), 3U);
190 }
191
TEST(MultiBuf,DiscardPrefixDiscardsMultipleChunks)192 TEST(MultiBuf, DiscardPrefixDiscardsMultipleChunks) {
193 AllocatorForTest<kArbitraryAllocatorSize> allocator;
194 MultiBuf buf;
195 buf.PushFrontChunk(MakeChunk(allocator, 16U));
196 buf.PushFrontChunk(MakeChunk(allocator, 4U));
197 buf.PushFrontChunk(MakeChunk(allocator, 3U));
198 buf.DiscardPrefix(21U);
199 EXPECT_EQ(buf.size(), 2U);
200 }
201
TEST(MultiBuf,SliceDiscardsPrefixAndSuffixWholeAndPartialChunks)202 TEST(MultiBuf, SliceDiscardsPrefixAndSuffixWholeAndPartialChunks) {
203 AllocatorForTest<kArbitraryAllocatorSize> allocator;
204 MultiBuf buf;
205 buf.PushBackChunk(MakeChunk(allocator, {1_b, 1_b, 1_b}));
206 buf.PushBackChunk(MakeChunk(allocator, {2_b, 2_b, 2_b}));
207 buf.PushBackChunk(MakeChunk(allocator, {3_b, 3_b, 3_b}));
208 buf.PushBackChunk(MakeChunk(allocator, {4_b, 4_b, 4_b}));
209 buf.Slice(4, 7);
210 ExpectElementsEqual(buf, {2_b, 2_b, 3_b});
211 }
212
TEST(MultiBuf,SliceDoesNotModifyChunkMemory)213 TEST(MultiBuf, SliceDoesNotModifyChunkMemory) {
214 AllocatorForTest<kArbitraryAllocatorSize> allocator;
215 MultiBuf buf;
216 std::array<std::byte, 4> kBytes = {1_b, 2_b, 3_b, 4_b};
217 OwnedChunk chunk = MakeChunk(allocator, kBytes);
218 ConstByteSpan span(chunk);
219 buf.PushFrontChunk(std::move(chunk));
220 buf.Slice(2, 3);
221 ExpectElementsEqual(span, kBytes);
222 }
223
TEST(MultiBuf,TruncateRemovesWholeAndPartialChunks)224 TEST(MultiBuf, TruncateRemovesWholeAndPartialChunks) {
225 AllocatorForTest<kArbitraryAllocatorSize> allocator;
226 MultiBuf buf;
227 buf.PushFrontChunk(MakeChunk(allocator, 3U));
228 buf.PushFrontChunk(MakeChunk(allocator, 3U));
229 buf.Truncate(2U);
230 EXPECT_EQ(buf.size(), 2U);
231 }
232
TEST(MultiBuf,TruncateEmptyBuffer)233 TEST(MultiBuf, TruncateEmptyBuffer) {
234 MultiBuf buf;
235 buf.Truncate(0);
236 EXPECT_TRUE(buf.empty());
237 }
238
TEST(MultiBuf,TakePrefixWithNoBytesDoesNothing)239 TEST(MultiBuf, TakePrefixWithNoBytesDoesNothing) {
240 AllocatorForTest<kArbitraryAllocatorSize> allocator;
241 MultiBuf buf;
242 std::optional<MultiBuf> empty_front = buf.TakePrefix(0);
243 ASSERT_TRUE(empty_front.has_value());
244 EXPECT_EQ(buf.size(), 0U);
245 EXPECT_EQ(empty_front->size(), 0U);
246 }
247
TEST(MultiBuf,TakePrefixReturnsPartialChunk)248 TEST(MultiBuf, TakePrefixReturnsPartialChunk) {
249 AllocatorForTest<kArbitraryAllocatorSize> allocator;
250 MultiBuf buf;
251 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
252 std::optional<MultiBuf> old_front = buf.TakePrefix(2);
253 ASSERT_TRUE(old_front.has_value());
254 ExpectElementsEqual(*old_front, {1_b, 2_b});
255 ExpectElementsEqual(buf, {3_b});
256 }
257
TEST(MultiBuf,TakePrefixReturnsWholeAndPartialChunks)258 TEST(MultiBuf, TakePrefixReturnsWholeAndPartialChunks) {
259 AllocatorForTest<kArbitraryAllocatorSize> allocator;
260 MultiBuf buf;
261 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
262 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
263 std::optional<MultiBuf> old_front = buf.TakePrefix(4);
264 ASSERT_TRUE(old_front.has_value());
265 ExpectElementsEqual(*old_front, {1_b, 2_b, 3_b, 4_b});
266 ExpectElementsEqual(buf, {5_b, 6_b});
267 }
268
TEST(MultiBuf,TakeSuffixReturnsWholeAndPartialChunks)269 TEST(MultiBuf, TakeSuffixReturnsWholeAndPartialChunks) {
270 AllocatorForTest<kArbitraryAllocatorSize> allocator;
271 MultiBuf buf;
272 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
273 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
274 std::optional<MultiBuf> old_tail = buf.TakeSuffix(4);
275 ASSERT_TRUE(old_tail.has_value());
276 ExpectElementsEqual(buf, {1_b, 2_b});
277 ExpectElementsEqual(*old_tail, {3_b, 4_b, 5_b, 6_b});
278 }
279
TEST(MultiBuf,PushPrefixPrependsData)280 TEST(MultiBuf, PushPrefixPrependsData) {
281 AllocatorForTest<kArbitraryAllocatorSize> allocator;
282 MultiBuf buf;
283 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
284 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
285 MultiBuf buf2;
286 buf2.PushBackChunk(MakeChunk(allocator, {7_b, 8_b}));
287 buf2.PushPrefix(std::move(buf));
288 ExpectElementsEqual(buf2, {1_b, 2_b, 3_b, 4_b, 5_b, 6_b, 7_b, 8_b});
289 }
290
TEST(MultiBuf,PushSuffixAppendsData)291 TEST(MultiBuf, PushSuffixAppendsData) {
292 AllocatorForTest<kArbitraryAllocatorSize> allocator;
293 MultiBuf buf;
294 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
295 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
296 MultiBuf buf2;
297 buf2.PushBackChunk(MakeChunk(allocator, {7_b, 8_b}));
298 buf2.PushSuffix(std::move(buf));
299 ExpectElementsEqual(buf2, {7_b, 8_b, 1_b, 2_b, 3_b, 4_b, 5_b, 6_b});
300 }
301
TEST(MultiBuf,PushFrontChunkAddsBytesToFront)302 TEST(MultiBuf, PushFrontChunkAddsBytesToFront) {
303 AllocatorForTest<kArbitraryAllocatorSize> allocator;
304 MultiBuf buf;
305
306 const std::array<std::byte, 3> kBytesOne = {0_b, 1_b, 2_b};
307 auto chunk_one = MakeChunk(allocator, kBytesOne);
308 buf.PushFrontChunk(std::move(chunk_one));
309 ExpectElementsEqual(buf, kBytesOne);
310
311 const std::array<std::byte, 4> kBytesTwo = {9_b, 10_b, 11_b, 12_b};
312 auto chunk_two = MakeChunk(allocator, kBytesTwo);
313 buf.PushFrontChunk(std::move(chunk_two));
314
315 // clang-format off
316 ExpectElementsEqual(buf, {
317 9_b, 10_b, 11_b, 12_b,
318 0_b, 1_b, 2_b,
319 });
320 // clang-format on
321 }
322
TEST(MultiBuf,InsertChunkOnEmptyBufAddsFirstChunk)323 TEST(MultiBuf, InsertChunkOnEmptyBufAddsFirstChunk) {
324 AllocatorForTest<kArbitraryAllocatorSize> allocator;
325 MultiBuf buf;
326
327 const std::array<std::byte, 3> kBytes = {0_b, 1_b, 2_b};
328 auto chunk = MakeChunk(allocator, kBytes);
329 auto inserted_iter = buf.InsertChunk(buf.Chunks().begin(), std::move(chunk));
330 EXPECT_EQ(inserted_iter, buf.Chunks().begin());
331 ExpectElementsEqual(buf, kBytes);
332 EXPECT_EQ(++inserted_iter, buf.Chunks().end());
333 }
334
TEST(MultiBuf,InsertChunkAtEndOfBufAddsLastChunk)335 TEST(MultiBuf, InsertChunkAtEndOfBufAddsLastChunk) {
336 AllocatorForTest<kArbitraryAllocatorSize> allocator;
337 MultiBuf buf;
338
339 // Add a chunk to the beginning
340 buf.PushFrontChunk(MakeChunk(allocator, kArbitraryChunkSize));
341
342 const std::array<std::byte, 3> kBytes = {0_b, 1_b, 2_b};
343 auto chunk = MakeChunk(allocator, kBytes);
344 auto inserted_iter = buf.InsertChunk(buf.Chunks().end(), std::move(chunk));
345 EXPECT_EQ(inserted_iter, ++buf.Chunks().begin());
346 EXPECT_EQ(++inserted_iter, buf.Chunks().end());
347 const Chunk& second_chunk = *(++buf.Chunks().begin());
348 ExpectElementsEqual(second_chunk, kBytes);
349 }
350
TEST(MultiBuf,TakeChunkAtBeginRemovesAndReturnsFirstChunk)351 TEST(MultiBuf, TakeChunkAtBeginRemovesAndReturnsFirstChunk) {
352 AllocatorForTest<kArbitraryAllocatorSize> allocator;
353 MultiBuf buf;
354 auto insert_iter = buf.Chunks().begin();
355 insert_iter = buf.InsertChunk(insert_iter, MakeChunk(allocator, 2));
356 insert_iter = buf.InsertChunk(++insert_iter, MakeChunk(allocator, 4));
357
358 auto [chunk_iter, chunk] = buf.TakeChunk(buf.Chunks().begin());
359 EXPECT_EQ(chunk.size(), 2U);
360 EXPECT_EQ(chunk_iter->size(), 4U);
361 ++chunk_iter;
362 EXPECT_EQ(chunk_iter, buf.Chunks().end());
363 }
364
TEST(MultiBuf,TakeChunkOnLastInsertedIterReturnsLastInserted)365 TEST(MultiBuf, TakeChunkOnLastInsertedIterReturnsLastInserted) {
366 AllocatorForTest<kArbitraryAllocatorSize> allocator;
367 MultiBuf buf;
368 auto iter = buf.Chunks().begin();
369 iter = buf.InsertChunk(iter, MakeChunk(allocator, 42));
370 iter = buf.InsertChunk(++iter, MakeChunk(allocator, 11));
371 iter = buf.InsertChunk(++iter, MakeChunk(allocator, 65));
372 OwnedChunk chunk;
373 std::tie(iter, chunk) = buf.TakeChunk(iter);
374 EXPECT_EQ(iter, buf.Chunks().end());
375 EXPECT_EQ(chunk.size(), 65U);
376 }
377
TEST(MultiBuf,RangeBasedForLoopsCompile)378 TEST(MultiBuf, RangeBasedForLoopsCompile) {
379 MultiBuf buf;
380 for ([[maybe_unused]] std::byte& byte : buf) {
381 }
382 for ([[maybe_unused]] const std::byte& byte : buf) {
383 }
384 for ([[maybe_unused]] Chunk& chunk : buf.Chunks()) {
385 }
386 for ([[maybe_unused]] const Chunk& chunk : buf.Chunks()) {
387 }
388
389 const MultiBuf const_buf;
390 for ([[maybe_unused]] const std::byte& byte : const_buf) {
391 }
392 for ([[maybe_unused]] const Chunk& chunk : const_buf.Chunks()) {
393 }
394 }
395
TEST(MultiBuf,IteratorAdvancesNAcrossChunks)396 TEST(MultiBuf, IteratorAdvancesNAcrossChunks) {
397 AllocatorForTest<kArbitraryAllocatorSize> allocator;
398 MultiBuf buf;
399 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
400 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
401
402 MultiBuf::iterator iter = buf.begin();
403 iter += 4;
404 EXPECT_EQ(*iter, 5_b);
405 }
406
TEST(MultiBuf,IteratorAdvancesNAcrossZeroLengthChunk)407 TEST(MultiBuf, IteratorAdvancesNAcrossZeroLengthChunk) {
408 AllocatorForTest<kArbitraryAllocatorSize> allocator;
409 MultiBuf buf;
410 buf.PushBackChunk(MakeChunk(allocator, 0));
411 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
412 buf.PushBackChunk(MakeChunk(allocator, 0));
413 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
414
415 MultiBuf::iterator iter = buf.begin();
416 iter += 4;
417 EXPECT_EQ(*iter, 5_b);
418 }
419
TEST(MultiBuf,ConstIteratorAdvancesNAcrossChunks)420 TEST(MultiBuf, ConstIteratorAdvancesNAcrossChunks) {
421 AllocatorForTest<kArbitraryAllocatorSize> allocator;
422 MultiBuf buf;
423 buf.PushBackChunk(MakeChunk(allocator, {1_b, 2_b, 3_b}));
424 buf.PushBackChunk(MakeChunk(allocator, {4_b, 5_b, 6_b}));
425
426 MultiBuf::const_iterator iter = buf.cbegin();
427 iter += 4;
428 EXPECT_EQ(*iter, 5_b);
429 }
430
TEST(MultiBuf,IteratorSkipsEmptyChunks)431 TEST(MultiBuf, IteratorSkipsEmptyChunks) {
432 AllocatorForTest<kArbitraryAllocatorSize> allocator;
433 MultiBuf buf;
434 buf.PushBackChunk(MakeChunk(allocator, 0));
435 buf.PushBackChunk(MakeChunk(allocator, 0));
436 buf.PushBackChunk(MakeChunk(allocator, {1_b}));
437 buf.PushBackChunk(MakeChunk(allocator, 0));
438 buf.PushBackChunk(MakeChunk(allocator, {2_b, 3_b}));
439 buf.PushBackChunk(MakeChunk(allocator, 0));
440
441 MultiBuf::iterator it = buf.begin();
442 ASSERT_EQ(*it++, 1_b);
443 ASSERT_EQ(*it++, 2_b);
444 ASSERT_EQ(*it++, 3_b);
445 ASSERT_EQ(it, buf.end());
446 }
447
448 } // namespace
449 } // namespace pw::multibuf
450