LCOV - code coverage report
Current view: top level - boost/capy/test - read_source.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 92.6 % 27 25
Test Date: 2026-01-30 23:43:15 Functions: 80.0 % 15 12

            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 <boost/capy/coro.hpp>
      18              : #include <boost/capy/ex/executor_ref.hpp>
      19              : #include <boost/capy/io_result.hpp>
      20              : #include <boost/capy/error.hpp>
      21              : #include <boost/capy/test/fuse.hpp>
      22              : 
      23              : #include <stop_token>
      24              : #include <string>
      25              : #include <string_view>
      26              : 
      27              : namespace boost {
      28              : namespace capy {
      29              : namespace test {
      30              : 
      31              : /** A mock source for testing read operations.
      32              : 
      33              :     Use this to verify code that performs complete reads without needing
      34              :     real I/O. Call @ref provide to supply data, then @ref read
      35              :     to consume it. The associated @ref fuse enables error injection
      36              :     at controlled points.
      37              : 
      38              :     Unlike @ref read_stream which provides partial reads via `read_some`,
      39              :     this class satisfies the @ref ReadSource concept by providing complete
      40              :     reads that fill the entire buffer sequence 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          322 :     explicit read_source(
      80              :         fuse& f,
      81              :         std::size_t max_read_size = std::size_t(-1)) noexcept
      82          322 :         : f_(&f)
      83          322 :         , max_read_size_(max_read_size)
      84              :     {
      85          322 :     }
      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          310 :     provide(std::string_view sv)
      95              :     {
      96          310 :         data_.append(sv);
      97          310 :     }
      98              : 
      99              :     /// Clear all data and reset the read position.
     100              :     void
     101              :     clear() noexcept
     102              :     {
     103              :         data_.clear();
     104              :         pos_ = 0;
     105              :     }
     106              : 
     107              :     /// Return the number of bytes available for reading.
     108              :     std::size_t
     109            4 :     available() const noexcept
     110              :     {
     111            4 :         return data_.size() - pos_;
     112              :     }
     113              : 
     114              :     /** Asynchronously read data from the source.
     115              : 
     116              :         Transfers up to `buffer_size( buffers )` bytes from the internal
     117              :         buffer to the provided mutable buffer sequence, filling buffers
     118              :         completely before returning. If no data remains, returns
     119              :         `error::eof`. Before every read, the attached @ref fuse is
     120              :         consulted to possibly inject an error for testing fault scenarios.
     121              :         The returned `std::size_t` is the number of bytes transferred.
     122              : 
     123              :         @par Effects
     124              :         On success, advances the internal read position by the number of
     125              :         bytes copied. If an error is injected by the fuse, the read position
     126              :         remains unchanged.
     127              : 
     128              :         @par Exception Safety
     129              :         No-throw guarantee.
     130              : 
     131              :         @param buffers The mutable buffer sequence to receive data.
     132              : 
     133              :         @return An awaitable yielding `(error_code,std::size_t)`.
     134              : 
     135              :         @see fuse
     136              :     */
     137              :     template<MutableBufferSequence MB>
     138              :     auto
     139          568 :     read(MB buffers)
     140              :     {
     141              :         struct awaitable
     142              :         {
     143              :             read_source* self_;
     144              :             MB buffers_;
     145              : 
     146          568 :             bool await_ready() const noexcept { return true; }
     147              : 
     148            0 :             void await_suspend(
     149              :                 coro,
     150              :                 executor_ref,
     151              :                 std::stop_token) const noexcept
     152              :             {
     153            0 :             }
     154              : 
     155              :             io_result<std::size_t>
     156          568 :             await_resume()
     157              :             {
     158          568 :                 auto ec = self_->f_->maybe_fail();
     159          470 :                 if(ec)
     160           98 :                     return {ec, 0};
     161              : 
     162          372 :                 if(self_->pos_ >= self_->data_.size())
     163           70 :                     return {error::eof, 0};
     164              : 
     165          302 :                 std::size_t avail = self_->data_.size() - self_->pos_;
     166          302 :                 if(avail > self_->max_read_size_)
     167           84 :                     avail = self_->max_read_size_;
     168          302 :                 auto src = make_buffer(self_->data_.data() + self_->pos_, avail);
     169          302 :                 std::size_t const n = buffer_copy(buffers_, src);
     170          302 :                 self_->pos_ += n;
     171          302 :                 return {{}, n};
     172              :             }
     173              :         };
     174          568 :         return awaitable{this, buffers};
     175              :     }
     176              : };
     177              : 
     178              : } // test
     179              : } // capy
     180              : } // boost
     181              : 
     182              : #endif
        

Generated by: LCOV version 2.3