LCOV - code coverage report
Current view: top level - capy/test - read_source.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 92.5 % 53 49 4
Test Date: 2026-02-17 18:14:47 Functions: 78.6 % 28 22 6

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/capy
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_CAPY_TEST_READ_SOURCE_HPP
      11                 : #define BOOST_CAPY_TEST_READ_SOURCE_HPP
      12                 : 
      13                 : #include <boost/capy/detail/config.hpp>
      14                 : #include <boost/capy/buffers.hpp>
      15                 : #include <boost/capy/buffers/buffer_copy.hpp>
      16                 : #include <boost/capy/buffers/make_buffer.hpp>
      17                 : #include <coroutine>
      18                 : #include <boost/capy/ex/io_env.hpp>
      19                 : #include <boost/capy/io_result.hpp>
      20                 : #include <boost/capy/error.hpp>
      21                 : #include <boost/capy/test/fuse.hpp>
      22                 : 
      23                 : #include <string>
      24                 : #include <string_view>
      25                 : 
      26                 : namespace boost {
      27                 : namespace capy {
      28                 : namespace test {
      29                 : 
      30                 : /** A mock source for testing read operations.
      31                 : 
      32                 :     Use this to verify code that performs complete reads without needing
      33                 :     real I/O. Call @ref provide to supply data, then @ref read
      34                 :     to consume it. The associated @ref fuse enables error injection
      35                 :     at controlled points.
      36                 : 
      37                 :     This class satisfies the @ref ReadSource concept by providing both
      38                 :     partial reads via `read_some` (satisfying @ref ReadStream) and
      39                 :     complete reads via `read` that fill the entire buffer sequence
      40                 :     before returning.
      41                 : 
      42                 :     @par Thread Safety
      43                 :     Not thread-safe.
      44                 : 
      45                 :     @par Example
      46                 :     @code
      47                 :     fuse f;
      48                 :     read_source rs( f );
      49                 :     rs.provide( "Hello, " );
      50                 :     rs.provide( "World!" );
      51                 : 
      52                 :     auto r = f.armed( [&]( fuse& ) -> task<void> {
      53                 :         char buf[32];
      54                 :         auto [ec, n] = co_await rs.read(
      55                 :             mutable_buffer( buf, sizeof( buf ) ) );
      56                 :         if( ec )
      57                 :             co_return;
      58                 :         // buf contains "Hello, World!"
      59                 :     } );
      60                 :     @endcode
      61                 : 
      62                 :     @see fuse, ReadSource
      63                 : */
      64                 : class read_source
      65                 : {
      66                 :     fuse f_;
      67                 :     std::string data_;
      68                 :     std::size_t pos_ = 0;
      69                 :     std::size_t max_read_size_;
      70                 : 
      71                 : public:
      72                 :     /** Construct a read source.
      73                 : 
      74                 :         @param f The fuse used to inject errors during reads.
      75                 : 
      76                 :         @param max_read_size Maximum bytes returned per read.
      77                 :         Use to simulate chunked delivery.
      78                 :     */
      79 HIT         443 :     explicit read_source(
      80                 :         fuse f = {},
      81                 :         std::size_t max_read_size = std::size_t(-1)) noexcept
      82             443 :         : f_(std::move(f))
      83             443 :         , max_read_size_(max_read_size)
      84                 :     {
      85             443 :     }
      86                 : 
      87                 :     /** Append data to be returned by subsequent reads.
      88                 : 
      89                 :         Multiple calls accumulate data that @ref read returns.
      90                 : 
      91                 :         @param sv The data to append.
      92                 :     */
      93                 :     void
      94             404 :     provide(std::string_view sv)
      95                 :     {
      96             404 :         data_.append(sv);
      97             404 :     }
      98                 : 
      99                 :     /// Clear all data and reset the read position.
     100                 :     void
     101               2 :     clear() noexcept
     102                 :     {
     103               2 :         data_.clear();
     104               2 :         pos_ = 0;
     105               2 :     }
     106                 : 
     107                 :     /// Return the number of bytes available for reading.
     108                 :     std::size_t
     109              30 :     available() const noexcept
     110                 :     {
     111              30 :         return data_.size() - pos_;
     112                 :     }
     113                 : 
     114                 :     /** Asynchronously read some data from the source.
     115                 : 
     116                 :         Transfers up to `buffer_size( buffers )` bytes from the internal
     117                 :         buffer to the provided mutable buffer sequence. If no data
     118                 :         remains, returns `error::eof`. Before every read, the attached
     119                 :         @ref fuse is consulted to possibly inject an error for testing
     120                 :         fault scenarios.
     121                 : 
     122                 :         @param buffers The mutable buffer sequence to receive data.
     123                 : 
     124                 :         @return An awaitable yielding `(error_code,std::size_t)`.
     125                 : 
     126                 :         @see fuse
     127                 :     */
     128                 :     template<MutableBufferSequence MB>
     129                 :     auto
     130             116 :     read_some(MB buffers)
     131                 :     {
     132                 :         struct awaitable
     133                 :         {
     134                 :             read_source* self_;
     135                 :             MB buffers_;
     136                 : 
     137             116 :             bool await_ready() const noexcept { return true; }
     138                 : 
     139 MIS           0 :             void await_suspend(
     140                 :                 std::coroutine_handle<>,
     141                 :                 io_env const*) const noexcept
     142                 :             {
     143               0 :             }
     144                 : 
     145                 :             io_result<std::size_t>
     146 HIT         116 :             await_resume()
     147                 :             {
     148             116 :                 if(buffer_empty(buffers_))
     149               4 :                     return {{}, 0};
     150                 : 
     151             112 :                 auto ec = self_->f_.maybe_fail();
     152              80 :                 if(ec)
     153              32 :                     return {ec, 0};
     154                 : 
     155              48 :                 if(self_->pos_ >= self_->data_.size())
     156               4 :                     return {error::eof, 0};
     157                 : 
     158              44 :                 std::size_t avail = self_->data_.size() - self_->pos_;
     159              44 :                 if(avail > self_->max_read_size_)
     160              14 :                     avail = self_->max_read_size_;
     161              44 :                 auto src = make_buffer(self_->data_.data() + self_->pos_, avail);
     162              44 :                 std::size_t const n = buffer_copy(buffers_, src);
     163              44 :                 self_->pos_ += n;
     164              44 :                 return {{}, n};
     165                 :             }
     166                 :         };
     167             116 :         return awaitable{this, buffers};
     168                 :     }
     169                 : 
     170                 :     /** Asynchronously read data from the source.
     171                 : 
     172                 :         Fills the entire buffer sequence from the internal data.
     173                 :         If the available data is less than the buffer size, returns
     174                 :         `error::eof` with the number of bytes transferred. Before
     175                 :         every read, the attached @ref fuse is consulted to possibly
     176                 :         inject an error for testing fault scenarios.
     177                 : 
     178                 :         Unlike @ref read_some, this ignores `max_read_size` and
     179                 :         transfers all available data in a single operation, matching
     180                 :         the @ref ReadSource semantic contract.
     181                 : 
     182                 :         @param buffers The mutable buffer sequence to receive data.
     183                 : 
     184                 :         @return An awaitable yielding `(error_code,std::size_t)`.
     185                 : 
     186                 :         @see fuse
     187                 :     */
     188                 :     template<MutableBufferSequence MB>
     189                 :     auto
     190             434 :     read(MB buffers)
     191                 :     {
     192                 :         struct awaitable
     193                 :         {
     194                 :             read_source* self_;
     195                 :             MB buffers_;
     196                 : 
     197             434 :             bool await_ready() const noexcept { return true; }
     198                 : 
     199 MIS           0 :             void await_suspend(
     200                 :                 std::coroutine_handle<>,
     201                 :                 io_env const*) const noexcept
     202                 :             {
     203               0 :             }
     204                 : 
     205                 :             io_result<std::size_t>
     206 HIT         434 :             await_resume()
     207                 :             {
     208             434 :                 if(buffer_empty(buffers_))
     209               2 :                     return {{}, 0};
     210                 : 
     211             432 :                 auto ec = self_->f_.maybe_fail();
     212             334 :                 if(ec)
     213              98 :                     return {ec, 0};
     214                 : 
     215             236 :                 if(self_->pos_ >= self_->data_.size())
     216              18 :                     return {error::eof, 0};
     217                 : 
     218             218 :                 std::size_t avail = self_->data_.size() - self_->pos_;
     219             218 :                 auto src = make_buffer(self_->data_.data() + self_->pos_, avail);
     220             218 :                 std::size_t const n = buffer_copy(buffers_, src);
     221             218 :                 self_->pos_ += n;
     222                 : 
     223             218 :                 if(n < buffer_size(buffers_))
     224              84 :                     return {error::eof, n};
     225             134 :                 return {{}, n};
     226                 :             }
     227                 :         };
     228             434 :         return awaitable{this, buffers};
     229                 :     }
     230                 : };
     231                 : 
     232                 : } // test
     233                 : } // capy
     234                 : } // boost
     235                 : 
     236                 : #endif
        

Generated by: LCOV version 2.3