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_ring_buffer/prefixed_entry_ring_buffer.h"
16 
17 #include <cstddef>
18 #include <cstdint>
19 
20 #include "pw_assert/assert.h"
21 #include "pw_containers/vector.h"
22 #include "pw_unit_test/framework.h"
23 
24 using std::byte;
25 
26 namespace pw {
27 namespace ring_buffer {
28 namespace {
29 
TEST(PrefixedEntryRingBuffer,NoBuffer)30 TEST(PrefixedEntryRingBuffer, NoBuffer) {
31   PrefixedEntryRingBuffer ring(false);
32 
33   byte buf[32];
34   size_t count;
35 
36   EXPECT_EQ(ring.EntryCount(), 0u);
37   EXPECT_EQ(ring.SetBuffer(std::span<byte>(nullptr, 10u)),
38             Status::InvalidArgument());
39   EXPECT_EQ(ring.SetBuffer(std::span(buf, 0u)), Status::InvalidArgument());
40   EXPECT_EQ(ring.FrontEntryDataSizeBytes(), 0u);
41 
42   EXPECT_EQ(ring.PushBack(buf), Status::FailedPrecondition());
43   EXPECT_EQ(ring.EntryCount(), 0u);
44   EXPECT_EQ(ring.PeekFront(buf, &count), Status::FailedPrecondition());
45   EXPECT_EQ(count, 0u);
46   EXPECT_EQ(ring.EntryCount(), 0u);
47   EXPECT_EQ(ring.PeekFrontWithPreamble(buf, &count),
48             Status::FailedPrecondition());
49   EXPECT_EQ(count, 0u);
50   EXPECT_EQ(ring.EntryCount(), 0u);
51   EXPECT_EQ(ring.PopFront(), Status::FailedPrecondition());
52   EXPECT_EQ(ring.EntryCount(), 0u);
53 }
54 
55 // Single entry to write/read/pop over and over again.
56 constexpr byte single_entry_data[] = {byte(1),
57                                       byte(2),
58                                       byte(3),
59                                       byte(4),
60                                       byte(5),
61                                       byte(6),
62                                       byte(7),
63                                       byte(8),
64                                       byte(9)};
65 constexpr size_t single_entry_total_size = sizeof(single_entry_data) + 1;
66 constexpr size_t single_entry_test_buffer_size =
67     (single_entry_total_size * 7) / 2;
68 
69 // Make sure the single_entry_size is even so single_entry_buffer_Size gets the
70 // proper wrap/even behavior when getting to the end of the buffer.
71 static_assert((single_entry_total_size % 2) == 0u);
72 constexpr size_t kSingleEntryCycles = 300u;
73 
74 // Repeatedly write the same data, read it, and pop it, done over and over
75 // again.
SingleEntryWriteReadTest(bool user_data)76 void SingleEntryWriteReadTest(bool user_data) {
77   PrefixedEntryRingBuffer ring(user_data);
78   byte test_buffer[single_entry_test_buffer_size];
79 
80   byte read_buffer[single_entry_total_size];
81 
82   // Set read_size to an unexpected value to make sure result checks don't luck
83   // out and happen to see a previous value.
84   size_t read_size = 500U;
85   uint32_t user_preamble = 0U;
86 
87   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
88 
89   EXPECT_EQ(ring.EntryCount(), 0u);
90   EXPECT_EQ(ring.PopFront(), Status::OutOfRange());
91   EXPECT_EQ(ring.EntryCount(), 0u);
92   EXPECT_EQ(ring.PushBack(std::span(single_entry_data, 0u)),
93             Status::InvalidArgument());
94   EXPECT_EQ(ring.EntryCount(), 0u);
95   EXPECT_EQ(
96       ring.PushBack(std::span(single_entry_data, sizeof(test_buffer) + 5)),
97       Status::OutOfRange());
98   EXPECT_EQ(ring.EntryCount(), 0u);
99   EXPECT_EQ(ring.PeekFront(read_buffer, &read_size), Status::OutOfRange());
100   EXPECT_EQ(read_size, 0u);
101   read_size = 500U;
102   EXPECT_EQ(ring.PeekFrontWithPreamble(read_buffer, &read_size),
103             Status::OutOfRange());
104   EXPECT_EQ(read_size, 0u);
105 
106   size_t user_preamble_bytes = (user_data ? 1 : 0);
107   size_t data_size = sizeof(single_entry_data) - user_preamble_bytes;
108   size_t data_offset = single_entry_total_size - data_size;
109 
110   byte expect_buffer[single_entry_total_size] = {};
111   expect_buffer[user_preamble_bytes] = byte(data_size);
112   memcpy(expect_buffer + data_offset, single_entry_data, data_size);
113 
114   for (size_t i = 0; i < kSingleEntryCycles; i++) {
115     ASSERT_EQ(ring.FrontEntryDataSizeBytes(), 0u);
116     ASSERT_EQ(ring.FrontEntryTotalSizeBytes(), 0u);
117 
118     // Limit the value of the preamble to a single byte, to ensure that we
119     // retain a static `single_entry_buffer_size` during the test. Single
120     // bytes are varint-encoded to the same value.
121     uint32_t preamble_byte = i % 128;
122     ASSERT_EQ(
123         ring.PushBack(std::span(single_entry_data, data_size), preamble_byte),
124         OkStatus());
125     ASSERT_EQ(ring.FrontEntryDataSizeBytes(), data_size);
126     ASSERT_EQ(ring.FrontEntryTotalSizeBytes(), single_entry_total_size);
127 
128     read_size = 500U;
129     ASSERT_EQ(ring.PeekFront(read_buffer, &read_size), OkStatus());
130     ASSERT_EQ(read_size, data_size);
131 
132     // ASSERT_THAT(std::span(expect_buffer).last(data_size),
133     //            testing::ElementsAreArray(std::span(read_buffer, data_size)));
134     ASSERT_EQ(memcmp(std::span(expect_buffer).last(data_size).data(),
135                      read_buffer,
136                      data_size),
137               0);
138 
139     read_size = 500U;
140     ASSERT_EQ(ring.PeekFrontWithPreamble(read_buffer, &read_size), OkStatus());
141     ASSERT_EQ(read_size, single_entry_total_size);
142 
143     if (user_data) {
144       expect_buffer[0] = byte(preamble_byte);
145     }
146 
147     // ASSERT_THAT(std::span(expect_buffer),
148     //            testing::ElementsAreArray(std::span(read_buffer)));
149     ASSERT_EQ(memcmp(expect_buffer, read_buffer, single_entry_total_size), 0);
150 
151     if (user_data) {
152       user_preamble = 0U;
153       ASSERT_EQ(
154           ring.PeekFrontWithPreamble(read_buffer, user_preamble, read_size),
155           OkStatus());
156       ASSERT_EQ(read_size, data_size);
157       ASSERT_EQ(user_preamble, preamble_byte);
158       ASSERT_EQ(memcmp(std::span(expect_buffer).last(data_size).data(),
159                        read_buffer,
160                        data_size),
161                 0);
162     }
163 
164     ASSERT_EQ(ring.PopFront(), OkStatus());
165   }
166 }
167 
TEST(PrefixedEntryRingBuffer,SingleEntryWriteReadNoUserData)168 TEST(PrefixedEntryRingBuffer, SingleEntryWriteReadNoUserData) {
169   SingleEntryWriteReadTest(false);
170 }
171 
TEST(PrefixedEntryRingBuffer,SingleEntryWriteReadYesUserData)172 TEST(PrefixedEntryRingBuffer, SingleEntryWriteReadYesUserData) {
173   SingleEntryWriteReadTest(true);
174 }
175 
176 // TODO(pwbug/196): Increase this to 5000 once we have a way to detect targets
177 // with more computation and memory oomph.
178 constexpr size_t kOuterCycles = 50u;
179 constexpr size_t kCountingUpMaxExpectedEntries =
180     single_entry_test_buffer_size / single_entry_total_size;
181 
182 // Write data that is filled with a byte value that increments each write. Write
183 // many times without read/pop and then check to make sure correct contents are
184 // in the ring buffer.
185 template <bool kUserData>
CountingUpWriteReadTest()186 void CountingUpWriteReadTest() {
187   PrefixedEntryRingBuffer ring(kUserData);
188   byte test_buffer[single_entry_test_buffer_size];
189 
190   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
191   EXPECT_EQ(ring.EntryCount(), 0u);
192 
193   constexpr size_t kDataSize = sizeof(single_entry_data) - (kUserData ? 1 : 0);
194 
195   for (size_t i = 0; i < kOuterCycles; i++) {
196     size_t seed = i;
197 
198     byte write_buffer[kDataSize];
199 
200     size_t j;
201     for (j = 0; j < kSingleEntryCycles; j++) {
202       memset(write_buffer, j + seed, sizeof(write_buffer));
203 
204       ASSERT_EQ(ring.PushBack(write_buffer), OkStatus());
205 
206       size_t expected_count = (j < kCountingUpMaxExpectedEntries)
207                                   ? j + 1
208                                   : kCountingUpMaxExpectedEntries;
209       ASSERT_EQ(ring.EntryCount(), expected_count);
210     }
211     size_t final_write_j = j;
212     size_t fill_val = seed + final_write_j - kCountingUpMaxExpectedEntries;
213 
214     for (j = 0; j < kCountingUpMaxExpectedEntries; j++) {
215       byte read_buffer[sizeof(write_buffer)];
216       size_t read_size;
217       memset(write_buffer, fill_val + j, sizeof(write_buffer));
218       ASSERT_EQ(ring.PeekFront(read_buffer, &read_size), OkStatus());
219 
220       ASSERT_EQ(memcmp(write_buffer, read_buffer, kDataSize), 0);
221 
222       ASSERT_EQ(ring.PopFront(), OkStatus());
223     }
224   }
225 }
226 
TEST(PrefixedEntryRingBuffer,CountingUpWriteReadNoUserData)227 TEST(PrefixedEntryRingBuffer, CountingUpWriteReadNoUserData) {
228   CountingUpWriteReadTest<false>();
229 }
230 
TEST(PrefixedEntryRingBuffer,CountingUpWriteReadYesUserData)231 TEST(PrefixedEntryRingBuffer, CountingUpWriteReadYesUserData) {
232   CountingUpWriteReadTest<true>();
233 }
234 
235 // Create statically to prevent allocating a capture in the lambda below.
236 static pw::Vector<byte, single_entry_total_size> read_buffer;
237 
238 // Repeatedly write the same data, read it, and pop it, done over and over
239 // again.
SingleEntryWriteReadWithSectionWriterTest(bool user_data)240 void SingleEntryWriteReadWithSectionWriterTest(bool user_data) {
241   PrefixedEntryRingBuffer ring(user_data);
242   byte test_buffer[single_entry_test_buffer_size];
243 
244   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
245 
246   auto output = [](std::span<const byte> src) -> Status {
247     for (byte b : src) {
248       read_buffer.push_back(b);
249     }
250     return OkStatus();
251   };
252 
253   size_t user_preamble_bytes = (user_data ? 1 : 0);
254   size_t data_size = sizeof(single_entry_data) - user_preamble_bytes;
255   size_t data_offset = single_entry_total_size - data_size;
256 
257   byte expect_buffer[single_entry_total_size] = {};
258   expect_buffer[user_preamble_bytes] = byte(data_size);
259   memcpy(expect_buffer + data_offset, single_entry_data, data_size);
260 
261   for (size_t i = 0; i < kSingleEntryCycles; i++) {
262     ASSERT_EQ(ring.FrontEntryDataSizeBytes(), 0u);
263     ASSERT_EQ(ring.FrontEntryTotalSizeBytes(), 0u);
264 
265     // Limit the value of the preamble to a single byte, to ensure that we
266     // retain a static `single_entry_buffer_size` during the test. Single
267     // bytes are varint-encoded to the same value.
268     uint32_t preamble_byte = i % 128;
269     ASSERT_EQ(
270         ring.PushBack(std::span(single_entry_data, data_size), preamble_byte),
271         OkStatus());
272     ASSERT_EQ(ring.FrontEntryDataSizeBytes(), data_size);
273     ASSERT_EQ(ring.FrontEntryTotalSizeBytes(), single_entry_total_size);
274 
275     read_buffer.clear();
276     ASSERT_EQ(ring.PeekFront(output), OkStatus());
277     ASSERT_EQ(read_buffer.size(), data_size);
278 
279     ASSERT_EQ(memcmp(std::span(expect_buffer).last(data_size).data(),
280                      read_buffer.data(),
281                      data_size),
282               0);
283 
284     read_buffer.clear();
285     ASSERT_EQ(ring.PeekFrontWithPreamble(output), OkStatus());
286     ASSERT_EQ(read_buffer.size(), single_entry_total_size);
287     ASSERT_EQ(ring.PopFront(), OkStatus());
288 
289     if (user_data) {
290       expect_buffer[0] = byte(preamble_byte);
291     }
292 
293     ASSERT_EQ(
294         memcmp(expect_buffer, read_buffer.data(), single_entry_total_size), 0);
295   }
296 }
297 
TEST(PrefixedEntryRingBuffer,SingleEntryWriteReadWithSectionWriterNoUserData)298 TEST(PrefixedEntryRingBuffer, SingleEntryWriteReadWithSectionWriterNoUserData) {
299   SingleEntryWriteReadWithSectionWriterTest(false);
300 }
301 
TEST(PrefixedEntryRingBuffer,SingleEntryWriteReadWithSectionWriterYesUserData)302 TEST(PrefixedEntryRingBuffer,
303      SingleEntryWriteReadWithSectionWriterYesUserData) {
304   SingleEntryWriteReadWithSectionWriterTest(true);
305 }
306 
307 constexpr size_t kEntrySizeBytes = 8u;
308 constexpr size_t kTotalEntryCount = 20u;
309 constexpr size_t kBufferExtraBytes = 5u;
310 constexpr size_t kTestBufferSize =
311     (kEntrySizeBytes * kTotalEntryCount) + kBufferExtraBytes;
312 
313 // Create statically to prevent allocating a capture in the lambda below.
314 static pw::Vector<byte, kTestBufferSize> actual_result;
315 
DeringTest(bool preload)316 void DeringTest(bool preload) {
317   PrefixedEntryRingBuffer ring;
318 
319   byte test_buffer[kTestBufferSize];
320   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
321 
322   // Entry data is entry size - preamble (single byte in this case).
323   byte single_entry_buffer[kEntrySizeBytes - 1u];
324   auto entry_data = std::span(single_entry_buffer);
325   size_t i;
326 
327   // TODO(pwbug/196): Increase this to 500 once we have a way to detect targets
328   // with more computation and memory oomph.
329   size_t loop_goal = preload ? 50 : 1;
330 
331   for (size_t main_loop_count = 0; main_loop_count < loop_goal;
332        main_loop_count++) {
333     if (preload) {
334       // Prime the ringbuffer with some junk data to get the buffer
335       // wrapped.
336       for (i = 0; i < (kTotalEntryCount * (main_loop_count % 64u)); i++) {
337         memset(single_entry_buffer, i, sizeof(single_entry_buffer));
338         ring.PushBack(single_entry_buffer);
339       }
340     }
341 
342     // Build up the expected buffer and fill the ring buffer with the test data.
343     pw::Vector<byte, kTestBufferSize> expected_result;
344     for (i = 0; i < kTotalEntryCount; i++) {
345       // First component of the entry: the varint size.
346       static_assert(sizeof(single_entry_buffer) < 127);
347       expected_result.push_back(byte(sizeof(single_entry_buffer)));
348 
349       // Second component of the entry: the raw data.
350       memset(single_entry_buffer, 'a' + i, sizeof(single_entry_buffer));
351       for (byte b : entry_data) {
352         expected_result.push_back(b);
353       }
354 
355       // The ring buffer internally pushes the varint size byte.
356       ring.PushBack(single_entry_buffer);
357     }
358 
359     // Check values before doing the dering.
360     EXPECT_EQ(ring.EntryCount(), kTotalEntryCount);
361     EXPECT_EQ(expected_result.size(), ring.TotalUsedBytes());
362 
363     ASSERT_EQ(ring.Dering(), OkStatus());
364 
365     // Check values after doing the dering.
366     EXPECT_EQ(ring.EntryCount(), kTotalEntryCount);
367     EXPECT_EQ(expected_result.size(), ring.TotalUsedBytes());
368 
369     // Read out the entries of the ring buffer.
370     actual_result.clear();
371     auto output = [](std::span<const byte> src) -> Status {
372       for (byte b : src) {
373         actual_result.push_back(b);
374       }
375       return OkStatus();
376     };
377     while (ring.EntryCount()) {
378       ASSERT_EQ(ring.PeekFrontWithPreamble(output), OkStatus());
379       ASSERT_EQ(ring.PopFront(), OkStatus());
380     }
381 
382     // Ensure the actual result out of the ring buffer matches our manually
383     // computed result.
384     EXPECT_EQ(expected_result.size(), actual_result.size());
385     ASSERT_EQ(memcmp(test_buffer, actual_result.data(), actual_result.size()),
386               0);
387     ASSERT_EQ(
388         memcmp(
389             expected_result.data(), actual_result.data(), actual_result.size()),
390         0);
391   }
392 }
393 
TEST(PrefixedEntryRingBuffer,Dering)394 TEST(PrefixedEntryRingBuffer, Dering) { DeringTest(true); }
TEST(PrefixedEntryRingBuffer,DeringNoPreload)395 TEST(PrefixedEntryRingBuffer, DeringNoPreload) { DeringTest(false); }
396 
397 template <typename T>
PushBack(PrefixedEntryRingBufferMulti & ring,T element)398 Status PushBack(PrefixedEntryRingBufferMulti& ring, T element) {
399   union {
400     std::array<byte, sizeof(element)> buffer;
401     T item;
402   } aliased;
403   aliased.item = element;
404   return ring.PushBack(aliased.buffer);
405 }
406 
407 template <typename T>
TryPushBack(PrefixedEntryRingBufferMulti & ring,T element)408 Status TryPushBack(PrefixedEntryRingBufferMulti& ring, T element) {
409   union {
410     std::array<byte, sizeof(element)> buffer;
411     T item;
412   } aliased;
413   aliased.item = element;
414   return ring.TryPushBack(aliased.buffer);
415 }
416 
417 template <typename T>
PeekFront(PrefixedEntryRingBufferMulti::Reader & reader)418 T PeekFront(PrefixedEntryRingBufferMulti::Reader& reader) {
419   union {
420     std::array<byte, sizeof(T)> buffer;
421     T item;
422   } aliased;
423   size_t bytes_read = 0;
424   PW_CHECK_OK(reader.PeekFront(aliased.buffer, &bytes_read));
425   PW_CHECK_INT_EQ(bytes_read, sizeof(T));
426   return aliased.item;
427 }
428 
TEST(PrefixedEntryRingBuffer,TryPushBack)429 TEST(PrefixedEntryRingBuffer, TryPushBack) {
430   PrefixedEntryRingBuffer ring;
431   byte test_buffer[kTestBufferSize];
432   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
433 
434   // Fill up the ring buffer with a constant.
435   int total_items = 0;
436   while (true) {
437     Status status = TryPushBack<int>(ring, 5);
438     if (status.ok()) {
439       total_items++;
440     } else {
441       EXPECT_EQ(status, Status::ResourceExhausted());
442       break;
443     }
444   }
445   EXPECT_EQ(PeekFront<int>(ring), 5);
446 
447   // Should be unable to push more items.
448   for (int i = 0; i < total_items; ++i) {
449     EXPECT_EQ(TryPushBack<int>(ring, 100), Status::ResourceExhausted());
450     EXPECT_EQ(PeekFront<int>(ring), 5);
451   }
452 
453   // Fill up the ring buffer with a constant.
454   for (int i = 0; i < total_items; ++i) {
455     EXPECT_EQ(PushBack<int>(ring, 100), OkStatus());
456   }
457   EXPECT_EQ(PeekFront<int>(ring), 100);
458 }
459 
TEST(PrefixedEntryRingBufferMulti,TryPushBack)460 TEST(PrefixedEntryRingBufferMulti, TryPushBack) {
461   PrefixedEntryRingBufferMulti ring;
462   byte test_buffer[kTestBufferSize];
463   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
464 
465   PrefixedEntryRingBufferMulti::Reader fast_reader;
466   PrefixedEntryRingBufferMulti::Reader slow_reader;
467 
468   EXPECT_EQ(ring.AttachReader(fast_reader), OkStatus());
469   EXPECT_EQ(ring.AttachReader(slow_reader), OkStatus());
470 
471   // Fill up the ring buffer with an increasing count.
472   int total_items = 0;
473   while (true) {
474     Status status = TryPushBack<int>(ring, total_items);
475     if (status.ok()) {
476       total_items++;
477     } else {
478       EXPECT_EQ(status, Status::ResourceExhausted());
479       break;
480     }
481   }
482 
483   // Run fast reader twice as fast as the slow reader.
484   for (int i = 0; i < total_items; ++i) {
485     if (i % 2 == 0) {
486       EXPECT_EQ(PeekFront<int>(slow_reader), i / 2);
487       EXPECT_EQ(slow_reader.PopFront(), OkStatus());
488     }
489     EXPECT_EQ(PeekFront<int>(fast_reader), i);
490     EXPECT_EQ(fast_reader.PopFront(), OkStatus());
491   }
492   EXPECT_EQ(fast_reader.PopFront(), Status::OutOfRange());
493 
494   // Fill the buffer again, expect that the fast reader
495   // only sees half the entries as the slow reader.
496   size_t max_items = total_items;
497   while (true) {
498     Status status = TryPushBack<int>(ring, total_items);
499     if (status.ok()) {
500       total_items++;
501     } else {
502       EXPECT_EQ(status, Status::ResourceExhausted());
503       break;
504     }
505   }
506   EXPECT_EQ(slow_reader.EntryCount(), max_items);
507   EXPECT_EQ(fast_reader.EntryCount(), total_items - max_items);
508 
509   for (int i = total_items - max_items; i < total_items; ++i) {
510     EXPECT_EQ(PeekFront<int>(slow_reader), i);
511     EXPECT_EQ(slow_reader.PopFront(), OkStatus());
512     if (static_cast<size_t>(i) >= max_items) {
513       EXPECT_EQ(PeekFront<int>(fast_reader), i);
514       EXPECT_EQ(fast_reader.PopFront(), OkStatus());
515     }
516   }
517   EXPECT_EQ(slow_reader.PopFront(), Status::OutOfRange());
518   EXPECT_EQ(fast_reader.PopFront(), Status::OutOfRange());
519 }
520 
TEST(PrefixedEntryRingBufferMulti,PushBack)521 TEST(PrefixedEntryRingBufferMulti, PushBack) {
522   PrefixedEntryRingBufferMulti ring;
523   byte test_buffer[kTestBufferSize];
524   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
525 
526   PrefixedEntryRingBufferMulti::Reader fast_reader;
527   PrefixedEntryRingBufferMulti::Reader slow_reader;
528 
529   EXPECT_EQ(ring.AttachReader(fast_reader), OkStatus());
530   EXPECT_EQ(ring.AttachReader(slow_reader), OkStatus());
531 
532   // Fill up the ring buffer with an increasing count.
533   size_t total_items = 0;
534   while (true) {
535     Status status = TryPushBack<uint32_t>(ring, total_items);
536     if (status.ok()) {
537       total_items++;
538     } else {
539       EXPECT_EQ(status, Status::ResourceExhausted());
540       break;
541     }
542   }
543   EXPECT_EQ(slow_reader.EntryCount(), total_items);
544 
545   // The following test:
546   //  - Moves the fast reader forward by one entry.
547   //  - Writes a single entry that is guaranteed to be larger than the size of a
548   //    single entry in the buffer (uint64_t entry > uint32_t entry).
549   //  - Checks to see that both readers were moved forward.
550   EXPECT_EQ(fast_reader.PopFront(), OkStatus());
551   EXPECT_EQ(PushBack<uint64_t>(ring, 5u), OkStatus());
552   // The readers have moved past values 0 and 1.
553   EXPECT_EQ(PeekFront<uint32_t>(slow_reader), 2u);
554   EXPECT_EQ(PeekFront<uint32_t>(fast_reader), 2u);
555   // The readers have lost two entries, but gained an entry.
556   EXPECT_EQ(slow_reader.EntryCount(), total_items - 1);
557   EXPECT_EQ(fast_reader.EntryCount(), total_items - 1);
558 }
559 
TEST(PrefixedEntryRingBufferMulti,ReaderAddRemove)560 TEST(PrefixedEntryRingBufferMulti, ReaderAddRemove) {
561   PrefixedEntryRingBufferMulti ring;
562   byte test_buffer[kTestBufferSize];
563   EXPECT_EQ(ring.SetBuffer(test_buffer), OkStatus());
564 
565   PrefixedEntryRingBufferMulti::Reader reader;
566   PrefixedEntryRingBufferMulti::Reader transient_reader;
567 
568   EXPECT_EQ(ring.AttachReader(reader), OkStatus());
569 
570   // Fill up the ring buffer with a constant value.
571   int total_items = 0;
572   while (true) {
573     Status status = TryPushBack<int>(ring, 5);
574     if (status.ok()) {
575       total_items++;
576     } else {
577       EXPECT_EQ(status, Status::ResourceExhausted());
578       break;
579     }
580   }
581   EXPECT_EQ(reader.EntryCount(), static_cast<size_t>(total_items));
582 
583   // Add new reader after filling the buffer.
584   EXPECT_EQ(ring.AttachReader(transient_reader), OkStatus());
585   EXPECT_EQ(transient_reader.EntryCount(), 0u);
586 
587   // Push a value into the buffer and confirm the transient reader
588   // sees that value, and only that value.
589   EXPECT_EQ(PushBack<int>(ring, 1), OkStatus());
590   EXPECT_EQ(PeekFront<int>(transient_reader), 1);
591   EXPECT_EQ(transient_reader.EntryCount(), 1u);
592 
593   // Confirm that detaching and attaching a reader resets its state.
594   EXPECT_EQ(ring.DetachReader(transient_reader), OkStatus());
595   EXPECT_EQ(ring.AttachReader(transient_reader), OkStatus());
596   EXPECT_EQ(transient_reader.EntryCount(), 0u);
597 }
598 
TEST(PrefixedEntryRingBufferMulti,SingleBufferPerReader)599 TEST(PrefixedEntryRingBufferMulti, SingleBufferPerReader) {
600   PrefixedEntryRingBufferMulti ring_one;
601   PrefixedEntryRingBufferMulti ring_two;
602   byte test_buffer[kTestBufferSize];
603   EXPECT_EQ(ring_one.SetBuffer(test_buffer), OkStatus());
604 
605   PrefixedEntryRingBufferMulti::Reader reader;
606   EXPECT_EQ(ring_one.AttachReader(reader), OkStatus());
607   EXPECT_EQ(ring_two.AttachReader(reader), Status::InvalidArgument());
608 
609   EXPECT_EQ(ring_one.DetachReader(reader), OkStatus());
610   EXPECT_EQ(ring_two.AttachReader(reader), OkStatus());
611   EXPECT_EQ(ring_one.AttachReader(reader), Status::InvalidArgument());
612 }
613 
614 }  // namespace
615 }  // namespace ring_buffer
616 }  // namespace pw
617