94.12% Lines (32/34) 88.89% Functions (8/9)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2 - // Copyright (c) 2026 Michael Vandeberg  
3   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
4   // 3   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 6   //
8   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
9   // 8   //
10   9  
11   #ifndef BOOST_CAPY_TEST_BUFFER_SOURCE_HPP 10   #ifndef BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
12   #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP 11   #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
13   12  
14   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
15   #include <boost/capy/buffers.hpp> 14   #include <boost/capy/buffers.hpp>
16   #include <boost/capy/buffers/make_buffer.hpp> 15   #include <boost/capy/buffers/make_buffer.hpp>
17   #include <coroutine> 16   #include <coroutine>
18   #include <boost/capy/error.hpp> 17   #include <boost/capy/error.hpp>
19   #include <boost/capy/ex/io_env.hpp> 18   #include <boost/capy/ex/io_env.hpp>
20   #include <boost/capy/io_result.hpp> 19   #include <boost/capy/io_result.hpp>
21   #include <boost/capy/test/fuse.hpp> 20   #include <boost/capy/test/fuse.hpp>
22   21  
23   #include <algorithm> 22   #include <algorithm>
24   #include <span> 23   #include <span>
25   #include <string> 24   #include <string>
26   #include <string_view> 25   #include <string_view>
27   26  
28   namespace boost { 27   namespace boost {
29   namespace capy { 28   namespace capy {
30   namespace test { 29   namespace test {
31   30  
32 - /** A mock buffer source for testing push operations. 31 + /** A mock buffer source for testing pull (BufferSource) operations.
33   32  
34   Use this to verify code that transfers data from a buffer source to 33   Use this to verify code that transfers data from a buffer source to
35   a sink without needing real I/O. Call @ref provide to supply data, 34   a sink without needing real I/O. Call @ref provide to supply data,
36   then @ref pull to retrieve buffer descriptors. The associated 35   then @ref pull to retrieve buffer descriptors. The associated
37   @ref fuse enables error injection at controlled points. 36   @ref fuse enables error injection at controlled points.
38   37  
39   This class satisfies the @ref BufferSource concept by providing 38   This class satisfies the @ref BufferSource concept by providing
40   a pull interface that fills an array of buffer descriptors and 39   a pull interface that fills an array of buffer descriptors and
41   a consume interface to indicate bytes used. 40   a consume interface to indicate bytes used.
42   41  
43   @par Thread Safety 42   @par Thread Safety
44   Not thread-safe. 43   Not thread-safe.
45   44  
46   @par Example 45   @par Example
47   @code 46   @code
48   fuse f; 47   fuse f;
49   buffer_source bs( f ); 48   buffer_source bs( f );
50   bs.provide( "Hello, " ); 49   bs.provide( "Hello, " );
51   bs.provide( "World!" ); 50   bs.provide( "World!" );
52   51  
53   auto r = f.armed( [&]( fuse& ) -> task<void> { 52   auto r = f.armed( [&]( fuse& ) -> task<void> {
54   const_buffer arr[16]; 53   const_buffer arr[16];
55   auto [ec, bufs] = co_await bs.pull( arr ); 54   auto [ec, bufs] = co_await bs.pull( arr );
56   if( ec ) 55   if( ec )
57   co_return; 56   co_return;
58   // bufs contains buffer descriptors 57   // bufs contains buffer descriptors
59   std::size_t n = buffer_size( bufs ); 58   std::size_t n = buffer_size( bufs );
60   bs.consume( n ); 59   bs.consume( n );
61   } ); 60   } );
62   @endcode 61   @endcode
63   62  
64   @see fuse, BufferSource 63   @see fuse, BufferSource
65   */ 64   */
66   class buffer_source 65   class buffer_source
67   { 66   {
68   fuse f_; 67   fuse f_;
69   std::string data_; 68   std::string data_;
70   std::size_t pos_ = 0; 69   std::size_t pos_ = 0;
71   std::size_t max_pull_size_; 70   std::size_t max_pull_size_;
72   71  
73   public: 72   public:
74   /** Construct a buffer source. 73   /** Construct a buffer source.
75   74  
76   @param f The fuse used to inject errors during pulls. 75   @param f The fuse used to inject errors during pulls.
77   76  
78   @param max_pull_size Maximum bytes returned per pull. 77   @param max_pull_size Maximum bytes returned per pull.
79   Use to simulate chunked delivery. 78   Use to simulate chunked delivery.
80   */ 79   */
HITCBC 81   377 explicit buffer_source( 80   376 explicit buffer_source(
82   fuse f = {}, 81   fuse f = {},
83   std::size_t max_pull_size = std::size_t(-1)) noexcept 82   std::size_t max_pull_size = std::size_t(-1)) noexcept
HITCBC 84   377 : f_(std::move(f)) 83   376 : f_(std::move(f))
HITCBC 85   377 , max_pull_size_(max_pull_size) 84   376 , max_pull_size_(max_pull_size)
86   { 85   {
HITCBC 87   377 } 86   376 }
88   87  
89   /** Append data to be returned by subsequent pulls. 88   /** Append data to be returned by subsequent pulls.
90   89  
91   Multiple calls accumulate data that @ref pull returns. 90   Multiple calls accumulate data that @ref pull returns.
92   91  
93   @param sv The data to append. 92   @param sv The data to append.
94   */ 93   */
95   void 94   void
HITCBC 96   389 provide(std::string_view sv) 95   388 provide(std::string_view sv)
97   { 96   {
HITCBC 98   389 data_.append(sv); 97   388 data_.append(sv);
HITCBC 99   389 } 98   388 }
100   99  
101   /// Clear all data and reset the read position. 100   /// Clear all data and reset the read position.
102   void 101   void
HITCBC 103   6 clear() noexcept 102   6 clear() noexcept
104   { 103   {
HITCBC 105   6 data_.clear(); 104   6 data_.clear();
HITCBC 106   6 pos_ = 0; 105   6 pos_ = 0;
HITCBC 107   6 } 106   6 }
108   107  
109   /// Return the number of bytes available for pulling. 108   /// Return the number of bytes available for pulling.
110   std::size_t 109   std::size_t
HITCBC 111   18 available() const noexcept 110   18 available() const noexcept
112   { 111   {
HITCBC 113   18 return data_.size() - pos_; 112   18 return data_.size() - pos_;
114   } 113   }
115   114  
116   /** Consume bytes from the source. 115   /** Consume bytes from the source.
117   116  
118   Advances the internal read position by the specified number 117   Advances the internal read position by the specified number
119   of bytes. The next call to @ref pull returns data starting 118   of bytes. The next call to @ref pull returns data starting
120   after the consumed bytes. 119   after the consumed bytes.
121   120  
122   @param n The number of bytes to consume. Must not exceed the 121   @param n The number of bytes to consume. Must not exceed the
123   total size of buffers returned by the previous @ref pull. 122   total size of buffers returned by the previous @ref pull.
124   */ 123   */
125   void 124   void
HITCBC 126   319 consume(std::size_t n) noexcept 125   319 consume(std::size_t n) noexcept
127   { 126   {
HITCBC 128   319 pos_ += n; 127   319 pos_ += n;
HITCBC 129   319 } 128   319 }
130   129  
131   /** Pull buffer data from the source. 130   /** Pull buffer data from the source.
132   131  
133   Fills the provided span with buffer descriptors pointing to 132   Fills the provided span with buffer descriptors pointing to
134   internal data starting from the current unconsumed position. 133   internal data starting from the current unconsumed position.
135   Returns a span of filled buffers. When no data remains, 134   Returns a span of filled buffers. When no data remains,
136   returns an empty span to signal completion. 135   returns an empty span to signal completion.
137   136  
138   Calling pull multiple times without intervening @ref consume 137   Calling pull multiple times without intervening @ref consume
139   returns the same data. Use consume to advance past processed 138   returns the same data. Use consume to advance past processed
140   bytes. 139   bytes.
141   140  
142   @param dest Span of const_buffer to fill. 141   @param dest Span of const_buffer to fill.
143   142  
144   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`. 143   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`.
145 - @par Cancellation  
146 - If the environment's stop token has been requested, the pull  
147 - completes immediately with `error::canceled` and an empty span.  
148 -  
149   144  
150   @see consume, fuse 145   @see consume, fuse
151   */ 146   */
152   auto 147   auto
HITCBC 153   657 pull(std::span<const_buffer> dest) 148   656 pull(std::span<const_buffer> dest)
154   { 149   {
155   struct awaitable 150   struct awaitable
156   { 151   {
157   buffer_source* self_; 152   buffer_source* self_;
158 - bool canceled_ = false;  
159   std::span<const_buffer> dest_; 153   std::span<const_buffer> dest_;
160   154  
HITCBC 161 - 657 bool await_ready() const noexcept { return false; } 155 + 656 bool await_ready() const noexcept { return true; }
162   156  
163 - // The operation completes synchronously, but await_suspend is 157 + // This method is required to satisfy Capy's IoAwaitable concept,
164 - // the only place io_env is delivered (the promise's 158 + // but is never called because await_ready() returns true.
165 - // transform_awaiter forwards it here). Returning false means 159 + //
166 - // the coroutine does not actually suspend; it resumes 160 + // Capy uses a two-layer awaitable system: the promise's
167 - // immediately, having observed the stop token. See io_env, 161 + // await_transform wraps awaitables in a transform_awaiter whose
168 - // IoAwaitable. 162 + // standard await_suspend(coroutine_handle) calls this custom
169 - bool 163 + // 2-argument overload, passing the io_env from the coroutine's
ECB 170 - 657 await_suspend( 164 + // context. For synchronous test awaitables like this one, the
  165 + // coroutine never suspends, so this is not invoked. The signature
  166 + // exists to allow the same awaitable type to work with both
  167 + // synchronous (test) and asynchronous (real I/O) code.
MISUNC   168 + void await_suspend(
171   std::coroutine_handle<>, 169   std::coroutine_handle<>,
172 - io_env const* env) noexcept 170 + io_env const*) const noexcept
173 - canceled_ = env->stop_token.stop_requested();  
DCB 174 - 657 return false;  
ECB 175   657 { 171   {
MISUIC 176   } 172   }
177   173  
178   io_result<std::span<const_buffer>> 174   io_result<std::span<const_buffer>>
HITCBC 179   657 await_resume() 175   656 await_resume()
180 - if(canceled_)  
DCB 181 - 657 return {error::canceled, {}};  
DCB 182 - 1  
183   { 176   {
HITCBC 184   656 auto ec = self_->f_.maybe_fail(); 177   656 auto ec = self_->f_.maybe_fail();
HITCBC 185   546 if(ec) 178   546 if(ec)
HITCBC 186   110 return {ec, {}}; 179   110 return {ec, {}};
187   180  
HITCBC 188   436 if(self_->pos_ >= self_->data_.size()) 181   436 if(self_->pos_ >= self_->data_.size())
HITCBC 189   72 return {error::eof, {}}; 182   72 return {error::eof, {}};
190   183  
HITCBC 191   364 std::size_t avail = self_->data_.size() - self_->pos_; 184   364 std::size_t avail = self_->data_.size() - self_->pos_;
HITCBC 192   364 std::size_t to_return = (std::min)(avail, self_->max_pull_size_); 185   364 std::size_t to_return = (std::min)(avail, self_->max_pull_size_);
193   186  
HITCBC 194   364 if(dest_.empty()) 187   364 if(dest_.empty())
HITCBC 195   2 return {{}, {}}; 188   2 return {{}, {}};
196   189  
197   // Fill a single buffer descriptor 190   // Fill a single buffer descriptor
HITCBC 198   362 dest_[0] = make_buffer( 191   362 dest_[0] = make_buffer(
HITCBC 199   362 self_->data_.data() + self_->pos_, 192   362 self_->data_.data() + self_->pos_,
200   to_return); 193   to_return);
201   194  
HITCBC 202   362 return {{}, dest_.first(1)}; 195   362 return {{}, dest_.first(1)};
203   } 196   }
204   }; 197   };
HITCBC 205   657 return awaitable{this, dest}; 198   656 return awaitable{this, dest};
206   } 199   }
207   }; 200   };
208   201  
209   } // test 202   } // test
210   } // capy 203   } // capy
211   } // boost 204   } // boost
212   205  
213   #endif 206   #endif