1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_TEST_MOCKET_HPP
10  
#ifndef BOOST_COROSIO_TEST_MOCKET_HPP
11  
#define BOOST_COROSIO_TEST_MOCKET_HPP
11  
#define BOOST_COROSIO_TEST_MOCKET_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/tcp_socket.hpp>
14  
#include <boost/corosio/tcp_socket.hpp>
15  
#include <boost/capy/buffers/buffer_copy.hpp>
15  
#include <boost/capy/buffers/buffer_copy.hpp>
16  
#include <boost/capy/buffers/make_buffer.hpp>
16  
#include <boost/capy/buffers/make_buffer.hpp>
17  
#include <boost/capy/error.hpp>
17  
#include <boost/capy/error.hpp>
18  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/io_result.hpp>
19  
#include <boost/capy/test/fuse.hpp>
19  
#include <boost/capy/test/fuse.hpp>
20  
#include <system_error>
20  
#include <system_error>
21  

21  

22  
#include <cstddef>
22  
#include <cstddef>
23  
#include <new>
23  
#include <new>
24  
#include <string>
24  
#include <string>
25  
#include <utility>
25  
#include <utility>
26  

26  

27  
namespace boost::capy {
27  
namespace boost::capy {
28  
class execution_context;
28  
class execution_context;
29  
} // namespace boost::capy
29  
} // namespace boost::capy
30  

30  

31  
namespace boost::corosio::test {
31  
namespace boost::corosio::test {
32  

32  

33  
/** A mock socket for testing I/O operations.
33  
/** A mock socket for testing I/O operations.
34  

34  

35  
    This class provides a testable socket-like interface where data
35  
    This class provides a testable socket-like interface where data
36  
    can be staged for reading and expected data can be validated on
36  
    can be staged for reading and expected data can be validated on
37  
    writes. A mocket is paired with a regular tcp_socket using
37  
    writes. A mocket is paired with a regular tcp_socket using
38  
    @ref make_mocket_pair, allowing bidirectional communication testing.
38  
    @ref make_mocket_pair, allowing bidirectional communication testing.
39  

39  

40  
    When reading, data comes from the `provide()` buffer first.
40  
    When reading, data comes from the `provide()` buffer first.
41  
    When writing, data is validated against the `expect()` buffer.
41  
    When writing, data is validated against the `expect()` buffer.
42  
    Once buffers are exhausted, I/O passes through to the underlying
42  
    Once buffers are exhausted, I/O passes through to the underlying
43  
    socket connection.
43  
    socket connection.
44  

44  

45  
    Satisfies the `capy::Stream` concept.
45  
    Satisfies the `capy::Stream` concept.
46  

46  

47  
    @par Thread Safety
47  
    @par Thread Safety
48  
    Not thread-safe. All operations must occur on a single thread.
48  
    Not thread-safe. All operations must occur on a single thread.
49  
    All coroutines using the mocket must be suspended when calling
49  
    All coroutines using the mocket must be suspended when calling
50  
    `expect()` or `provide()`.
50  
    `expect()` or `provide()`.
51  

51  

52  
    @see make_mocket_pair
52  
    @see make_mocket_pair
53  
*/
53  
*/
54  
class BOOST_COROSIO_DECL mocket
54  
class BOOST_COROSIO_DECL mocket
55  
{
55  
{
56  
    tcp_socket sock_;
56  
    tcp_socket sock_;
57  
    std::string provide_;
57  
    std::string provide_;
58  
    std::string expect_;
58  
    std::string expect_;
59  
    capy::test::fuse fuse_;
59  
    capy::test::fuse fuse_;
60  
    std::size_t max_read_size_;
60  
    std::size_t max_read_size_;
61  
    std::size_t max_write_size_;
61  
    std::size_t max_write_size_;
62  

62  

63  
    template<class MutableBufferSequence>
63  
    template<class MutableBufferSequence>
64 -
    std::size_t
64 +
    std::size_t consume_provide(MutableBufferSequence const& buffers) noexcept;
65 -
    consume_provide(MutableBufferSequence const& buffers) noexcept;
 
66  

65  

67  
    template<class ConstBufferSequence>
66  
    template<class ConstBufferSequence>
68 -
    bool
67 +
    bool validate_expect(
69 -
    validate_expect(
68 +
        ConstBufferSequence const& buffers, std::size_t& bytes_written);
70 -
        ConstBufferSequence const& buffers,
 
71 -
        std::size_t& bytes_written);
 
72  

69  

73  
public:
70  
public:
74  
    template<class MutableBufferSequence>
71  
    template<class MutableBufferSequence>
75  
    class read_some_awaitable;
72  
    class read_some_awaitable;
76  

73  

77  
    template<class ConstBufferSequence>
74  
    template<class ConstBufferSequence>
78  
    class write_some_awaitable;
75  
    class write_some_awaitable;
79  

76  

80  
    /** Destructor.
77  
    /** Destructor.
81  
    */
78  
    */
82  
    ~mocket();
79  
    ~mocket();
83  

80  

84  
    /** Construct a mocket.
81  
    /** Construct a mocket.
85  

82  

86  
        @param ctx The execution context for the socket.
83  
        @param ctx The execution context for the socket.
87  
        @param f The fuse for error injection testing.
84  
        @param f The fuse for error injection testing.
88  
        @param max_read_size Maximum bytes per read operation.
85  
        @param max_read_size Maximum bytes per read operation.
89  
        @param max_write_size Maximum bytes per write operation.
86  
        @param max_write_size Maximum bytes per write operation.
90  
    */
87  
    */
91  
    mocket(
88  
    mocket(
92  
        capy::execution_context& ctx,
89  
        capy::execution_context& ctx,
93  
        capy::test::fuse f = {},
90  
        capy::test::fuse f = {},
94  
        std::size_t max_read_size = std::size_t(-1),
91  
        std::size_t max_read_size = std::size_t(-1),
95  
        std::size_t max_write_size = std::size_t(-1));
92  
        std::size_t max_write_size = std::size_t(-1));
96  

93  

97  
    /** Move constructor.
94  
    /** Move constructor.
98  
    */
95  
    */
99  
    mocket(mocket&& other) noexcept;
96  
    mocket(mocket&& other) noexcept;
100  

97  

101  
    /** Move assignment.
98  
    /** Move assignment.
102  
    */
99  
    */
103  
    mocket& operator=(mocket&& other) noexcept;
100  
    mocket& operator=(mocket&& other) noexcept;
104  

101  

105  
    mocket(mocket const&) = delete;
102  
    mocket(mocket const&) = delete;
106  
    mocket& operator=(mocket const&) = delete;
103  
    mocket& operator=(mocket const&) = delete;
107  

104  

108  
    /** Return the execution context.
105  
    /** Return the execution context.
109  

106  

110  
        @return Reference to the execution context that owns this mocket.
107  
        @return Reference to the execution context that owns this mocket.
111  
    */
108  
    */
112 -
    capy::execution_context&
109 +
    capy::execution_context& context() const noexcept
113 -
    context() const noexcept
 
114  
    {
110  
    {
115  
        return sock_.context();
111  
        return sock_.context();
116  
    }
112  
    }
117  

113  

118  
    /** Return the underlying socket.
114  
    /** Return the underlying socket.
119  

115  

120  
        @return Reference to the underlying tcp_socket.
116  
        @return Reference to the underlying tcp_socket.
121  
    */
117  
    */
122 -
    tcp_socket&
118 +
    tcp_socket& socket() noexcept
123 -
    socket() noexcept
 
124  
    {
119  
    {
125  
        return sock_;
120  
        return sock_;
126  
    }
121  
    }
127  

122  

128  
    /** Stage data for reads.
123  
    /** Stage data for reads.
129  

124  

130  
        Appends the given string to this mocket's provide buffer.
125  
        Appends the given string to this mocket's provide buffer.
131  
        When `read_some` is called, it will receive this data first
126  
        When `read_some` is called, it will receive this data first
132  
        before reading from the underlying socket.
127  
        before reading from the underlying socket.
133  

128  

134  
        @param s The data to provide.
129  
        @param s The data to provide.
135  

130  

136  
        @pre All coroutines using this mocket must be suspended.
131  
        @pre All coroutines using this mocket must be suspended.
137  
    */
132  
    */
138 -
    void provide(std::string s);
133 +
    void provide(std::string const& s);
139  

134  

140  
    /** Set expected data for writes.
135  
    /** Set expected data for writes.
141  

136  

142  
        Appends the given string to this mocket's expect buffer.
137  
        Appends the given string to this mocket's expect buffer.
143  
        When the caller writes to this mocket, the written data
138  
        When the caller writes to this mocket, the written data
144  
        must match the expected data. On mismatch, `fuse::fail()`
139  
        must match the expected data. On mismatch, `fuse::fail()`
145  
        is called.
140  
        is called.
146  

141  

147  
        @param s The expected data.
142  
        @param s The expected data.
148  

143  

149  
        @pre All coroutines using this mocket must be suspended.
144  
        @pre All coroutines using this mocket must be suspended.
150  
    */
145  
    */
151 -
    void expect(std::string s);
146 +
    void expect(std::string const& s);
152  

147  

153  
    /** Close the mocket and verify test expectations.
148  
    /** Close the mocket and verify test expectations.
154  

149  

155  
        Closes the underlying socket and verifies that both the
150  
        Closes the underlying socket and verifies that both the
156  
        `expect()` and `provide()` buffers are empty. If either
151  
        `expect()` and `provide()` buffers are empty. If either
157  
        buffer contains unconsumed data, returns `test_failure`
152  
        buffer contains unconsumed data, returns `test_failure`
158  
        and calls `fuse::fail()`.
153  
        and calls `fuse::fail()`.
159  

154  

160  
        @return An error code indicating success or failure.
155  
        @return An error code indicating success or failure.
161  
            Returns `error::test_failure` if buffers are not empty.
156  
            Returns `error::test_failure` if buffers are not empty.
162  
    */
157  
    */
163  
    std::error_code close();
158  
    std::error_code close();
164  

159  

165  
    /** Cancel pending I/O operations.
160  
    /** Cancel pending I/O operations.
166  

161  

167  
        Cancels any pending asynchronous operations on the underlying
162  
        Cancels any pending asynchronous operations on the underlying
168  
        socket. Outstanding operations complete with `cond::canceled`.
163  
        socket. Outstanding operations complete with `cond::canceled`.
169  
    */
164  
    */
170  
    void cancel();
165  
    void cancel();
171  

166  

172  
    /** Check if the mocket is open.
167  
    /** Check if the mocket is open.
173  

168  

174  
        @return `true` if the mocket is open.
169  
        @return `true` if the mocket is open.
175  
    */
170  
    */
176  
    bool is_open() const noexcept;
171  
    bool is_open() const noexcept;
177  

172  

178  
    /** Initiate an asynchronous read operation.
173  
    /** Initiate an asynchronous read operation.
179  

174  

180  
        Reads available data into the provided buffer sequence. If the
175  
        Reads available data into the provided buffer sequence. If the
181  
        provide buffer has data, it is consumed first. Otherwise, the
176  
        provide buffer has data, it is consumed first. Otherwise, the
182  
        operation delegates to the underlying socket.
177  
        operation delegates to the underlying socket.
183  

178  

184  
        @param buffers The buffer sequence to read data into.
179  
        @param buffers The buffer sequence to read data into.
185  

180  

186  
        @return An awaitable yielding `(error_code, std::size_t)`.
181  
        @return An awaitable yielding `(error_code, std::size_t)`.
187  
    */
182  
    */
188  
    template<class MutableBufferSequence>
183  
    template<class MutableBufferSequence>
189  
    auto read_some(MutableBufferSequence const& buffers)
184  
    auto read_some(MutableBufferSequence const& buffers)
190  
    {
185  
    {
191  
        return read_some_awaitable<MutableBufferSequence>(*this, buffers);
186  
        return read_some_awaitable<MutableBufferSequence>(*this, buffers);
192  
    }
187  
    }
193  

188  

194  
    /** Initiate an asynchronous write operation.
189  
    /** Initiate an asynchronous write operation.
195  

190  

196  
        Writes data from the provided buffer sequence. If the expect
191  
        Writes data from the provided buffer sequence. If the expect
197  
        buffer has data, it is validated. Otherwise, the operation
192  
        buffer has data, it is validated. Otherwise, the operation
198  
        delegates to the underlying socket.
193  
        delegates to the underlying socket.
199  

194  

200  
        @param buffers The buffer sequence containing data to write.
195  
        @param buffers The buffer sequence containing data to write.
201  

196  

202  
        @return An awaitable yielding `(error_code, std::size_t)`.
197  
        @return An awaitable yielding `(error_code, std::size_t)`.
203  
    */
198  
    */
204  
    template<class ConstBufferSequence>
199  
    template<class ConstBufferSequence>
205  
    auto write_some(ConstBufferSequence const& buffers)
200  
    auto write_some(ConstBufferSequence const& buffers)
206  
    {
201  
    {
207  
        return write_some_awaitable<ConstBufferSequence>(*this, buffers);
202  
        return write_some_awaitable<ConstBufferSequence>(*this, buffers);
208  
    }
203  
    }
209  
};
204  
};
210 -
//------------------------------------------------------------------------------
 
211  

205  

212  

206  

213  
template<class MutableBufferSequence>
207  
template<class MutableBufferSequence>
214  
std::size_t
208  
std::size_t
215 -
mocket::
209 +
mocket::consume_provide(MutableBufferSequence const& buffers) noexcept
216 -
consume_provide(MutableBufferSequence const& buffers) noexcept
 
217  
{
210  
{
218 -
    auto n = capy::buffer_copy(buffers, capy::make_buffer(provide_), max_read_size_);
211 +
    auto n =
 
212 +
        capy::buffer_copy(buffers, capy::make_buffer(provide_), max_read_size_);
219  
    provide_.erase(0, n);
213  
    provide_.erase(0, n);
220  
    return n;
214  
    return n;
221  
}
215  
}
222  

216  

223  
template<class ConstBufferSequence>
217  
template<class ConstBufferSequence>
224  
bool
218  
bool
225 -
mocket::
219 +
mocket::validate_expect(
226 -
validate_expect(
220 +
    ConstBufferSequence const& buffers, std::size_t& bytes_written)
227 -
    ConstBufferSequence const& buffers,
 
228 -
    std::size_t& bytes_written)
 
229  
{
221  
{
230  
    if (expect_.empty())
222  
    if (expect_.empty())
231  
        return true;
223  
        return true;
232  

224  

233  
    // Build the write data up to max_write_size_
225  
    // Build the write data up to max_write_size_
234  
    std::string written;
226  
    std::string written;
235  
    auto total = capy::buffer_size(buffers);
227  
    auto total = capy::buffer_size(buffers);
236  
    if (total > max_write_size_)
228  
    if (total > max_write_size_)
237  
        total = max_write_size_;
229  
        total = max_write_size_;
238  
    written.resize(total);
230  
    written.resize(total);
239  
    capy::buffer_copy(capy::make_buffer(written), buffers, max_write_size_);
231  
    capy::buffer_copy(capy::make_buffer(written), buffers, max_write_size_);
240  

232  

241  
    // Check if written data matches expect prefix
233  
    // Check if written data matches expect prefix
242  
    auto const match_size = (std::min)(written.size(), expect_.size());
234  
    auto const match_size = (std::min)(written.size(), expect_.size());
243  
    if (std::memcmp(written.data(), expect_.data(), match_size) != 0)
235  
    if (std::memcmp(written.data(), expect_.data(), match_size) != 0)
244  
    {
236  
    {
245  
        fuse_.fail();
237  
        fuse_.fail();
246  
        bytes_written = 0;
238  
        bytes_written = 0;
247  
        return false;
239  
        return false;
248  
    }
240  
    }
249  

241  

250  
    // Consume matched portion
242  
    // Consume matched portion
251  
    expect_.erase(0, match_size);
243  
    expect_.erase(0, match_size);
252  
    bytes_written = written.size();
244  
    bytes_written = written.size();
253  
    return true;
245  
    return true;
254  
}
246  
}
255 -
//------------------------------------------------------------------------------
 
256  

247  

257  

248  

258  
template<class MutableBufferSequence>
249  
template<class MutableBufferSequence>
259  
class mocket::read_some_awaitable
250  
class mocket::read_some_awaitable
260  
{
251  
{
261 -
    using sock_awaitable =
252 +
    using sock_awaitable = decltype(std::declval<tcp_socket&>().read_some(
262 -
        decltype(std::declval<tcp_socket&>().read_some(
253 +
        std::declval<MutableBufferSequence>()));
263 -
            std::declval<MutableBufferSequence>()));
 
264  

254  

265  
    mocket* m_;
255  
    mocket* m_;
266  
    MutableBufferSequence buffers_;
256  
    MutableBufferSequence buffers_;
267  
    std::size_t n_ = 0;
257  
    std::size_t n_ = 0;
268 -
    union {
258 +
    union
 
259 +
    {
269  
        char dummy_;
260  
        char dummy_;
270  
        sock_awaitable underlying_;
261  
        sock_awaitable underlying_;
271  
    };
262  
    };
272  
    bool sync_ = true;
263  
    bool sync_ = true;
273  

264  

274  
public:
265  
public:
275 -
    read_some_awaitable(
266 +
    read_some_awaitable(mocket& m, MutableBufferSequence buffers) noexcept
276 -
        mocket& m,
 
277 -
        MutableBufferSequence buffers) noexcept
 
278  
        : m_(&m)
267  
        : m_(&m)
279  
        , buffers_(std::move(buffers))
268  
        , buffers_(std::move(buffers))
280  
    {
269  
    {
281  
    }
270  
    }
282  

271  

283  
    ~read_some_awaitable()
272  
    ~read_some_awaitable()
284  
    {
273  
    {
285  
        if (!sync_)
274  
        if (!sync_)
286  
            underlying_.~sock_awaitable();
275  
            underlying_.~sock_awaitable();
287  
    }
276  
    }
288  

277  

289  
    read_some_awaitable(read_some_awaitable&& other) noexcept
278  
    read_some_awaitable(read_some_awaitable&& other) noexcept
290  
        : m_(other.m_)
279  
        : m_(other.m_)
291  
        , buffers_(std::move(other.buffers_))
280  
        , buffers_(std::move(other.buffers_))
292  
        , n_(other.n_)
281  
        , n_(other.n_)
293  
        , sync_(other.sync_)
282  
        , sync_(other.sync_)
294  
    {
283  
    {
295  
        if (!sync_)
284  
        if (!sync_)
296  
        {
285  
        {
297  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
286  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
298  
            other.underlying_.~sock_awaitable();
287  
            other.underlying_.~sock_awaitable();
299  
            other.sync_ = true;
288  
            other.sync_ = true;
300  
        }
289  
        }
301  
    }
290  
    }
302  

291  

303  
    read_some_awaitable(read_some_awaitable const&) = delete;
292  
    read_some_awaitable(read_some_awaitable const&) = delete;
304  
    read_some_awaitable& operator=(read_some_awaitable const&) = delete;
293  
    read_some_awaitable& operator=(read_some_awaitable const&) = delete;
305  
    read_some_awaitable& operator=(read_some_awaitable&&) = delete;
294  
    read_some_awaitable& operator=(read_some_awaitable&&) = delete;
306  

295  

307  
    bool await_ready()
296  
    bool await_ready()
308  
    {
297  
    {
309  
        if (!m_->provide_.empty())
298  
        if (!m_->provide_.empty())
310  
        {
299  
        {
311  
            n_ = m_->consume_provide(buffers_);
300  
            n_ = m_->consume_provide(buffers_);
312  
            return true;
301  
            return true;
313  
        }
302  
        }
314  
        new (&underlying_) sock_awaitable(m_->sock_.read_some(buffers_));
303  
        new (&underlying_) sock_awaitable(m_->sock_.read_some(buffers_));
315  
        sync_ = false;
304  
        sync_ = false;
316  
        return underlying_.await_ready();
305  
        return underlying_.await_ready();
317  
    }
306  
    }
318  

307  

319  
    template<class... Args>
308  
    template<class... Args>
320  
    auto await_suspend(Args&&... args)
309  
    auto await_suspend(Args&&... args)
321  
    {
310  
    {
322  
        return underlying_.await_suspend(std::forward<Args>(args)...);
311  
        return underlying_.await_suspend(std::forward<Args>(args)...);
323  
    }
312  
    }
324  

313  

325  
    capy::io_result<std::size_t> await_resume()
314  
    capy::io_result<std::size_t> await_resume()
326  
    {
315  
    {
327  
        if (sync_)
316  
        if (sync_)
328  
            return {{}, n_};
317  
            return {{}, n_};
329  
        return underlying_.await_resume();
318  
        return underlying_.await_resume();
330  
    }
319  
    }
331  
};
320  
};
332 -
//------------------------------------------------------------------------------
 
333  

321  

334  

322  

335  
template<class ConstBufferSequence>
323  
template<class ConstBufferSequence>
336  
class mocket::write_some_awaitable
324  
class mocket::write_some_awaitable
337  
{
325  
{
338 -
    using sock_awaitable =
326 +
    using sock_awaitable = decltype(std::declval<tcp_socket&>().write_some(
339 -
        decltype(std::declval<tcp_socket&>().write_some(
327 +
        std::declval<ConstBufferSequence>()));
340 -
            std::declval<ConstBufferSequence>()));
 
341  

328  

342  
    mocket* m_;
329  
    mocket* m_;
343  
    ConstBufferSequence buffers_;
330  
    ConstBufferSequence buffers_;
344  
    std::size_t n_ = 0;
331  
    std::size_t n_ = 0;
345  
    std::error_code ec_;
332  
    std::error_code ec_;
346 -
    union {
333 +
    union
 
334 +
    {
347  
        char dummy_;
335  
        char dummy_;
348  
        sock_awaitable underlying_;
336  
        sock_awaitable underlying_;
349  
    };
337  
    };
350  
    bool sync_ = true;
338  
    bool sync_ = true;
351  

339  

352  
public:
340  
public:
353 -
    write_some_awaitable(
341 +
    write_some_awaitable(mocket& m, ConstBufferSequence buffers) noexcept
354 -
        mocket& m,
 
355 -
        ConstBufferSequence buffers) noexcept
 
356  
        : m_(&m)
342  
        : m_(&m)
357  
        , buffers_(std::move(buffers))
343  
        , buffers_(std::move(buffers))
358  
    {
344  
    {
359  
    }
345  
    }
360  

346  

361  
    ~write_some_awaitable()
347  
    ~write_some_awaitable()
362  
    {
348  
    {
363  
        if (!sync_)
349  
        if (!sync_)
364  
            underlying_.~sock_awaitable();
350  
            underlying_.~sock_awaitable();
365  
    }
351  
    }
366  

352  

367  
    write_some_awaitable(write_some_awaitable&& other) noexcept
353  
    write_some_awaitable(write_some_awaitable&& other) noexcept
368  
        : m_(other.m_)
354  
        : m_(other.m_)
369  
        , buffers_(std::move(other.buffers_))
355  
        , buffers_(std::move(other.buffers_))
370  
        , n_(other.n_)
356  
        , n_(other.n_)
371  
        , ec_(other.ec_)
357  
        , ec_(other.ec_)
372  
        , sync_(other.sync_)
358  
        , sync_(other.sync_)
373  
    {
359  
    {
374  
        if (!sync_)
360  
        if (!sync_)
375  
        {
361  
        {
376  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
362  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
377  
            other.underlying_.~sock_awaitable();
363  
            other.underlying_.~sock_awaitable();
378  
            other.sync_ = true;
364  
            other.sync_ = true;
379  
        }
365  
        }
380  
    }
366  
    }
381  

367  

382  
    write_some_awaitable(write_some_awaitable const&) = delete;
368  
    write_some_awaitable(write_some_awaitable const&) = delete;
383  
    write_some_awaitable& operator=(write_some_awaitable const&) = delete;
369  
    write_some_awaitable& operator=(write_some_awaitable const&) = delete;
384  
    write_some_awaitable& operator=(write_some_awaitable&&) = delete;
370  
    write_some_awaitable& operator=(write_some_awaitable&&) = delete;
385  

371  

386  
    bool await_ready()
372  
    bool await_ready()
387  
    {
373  
    {
388  
        if (!m_->expect_.empty())
374  
        if (!m_->expect_.empty())
389  
        {
375  
        {
390  
            if (!m_->validate_expect(buffers_, n_))
376  
            if (!m_->validate_expect(buffers_, n_))
391  
            {
377  
            {
392  
                ec_ = capy::error::test_failure;
378  
                ec_ = capy::error::test_failure;
393  
                n_ = 0;
379  
                n_ = 0;
394  
            }
380  
            }
395  
            return true;
381  
            return true;
396  
        }
382  
        }
397  
        new (&underlying_) sock_awaitable(m_->sock_.write_some(buffers_));
383  
        new (&underlying_) sock_awaitable(m_->sock_.write_some(buffers_));
398  
        sync_ = false;
384  
        sync_ = false;
399  
        return underlying_.await_ready();
385  
        return underlying_.await_ready();
400  
    }
386  
    }
401  

387  

402  
    template<class... Args>
388  
    template<class... Args>
403  
    auto await_suspend(Args&&... args)
389  
    auto await_suspend(Args&&... args)
404  
    {
390  
    {
405  
        return underlying_.await_suspend(std::forward<Args>(args)...);
391  
        return underlying_.await_suspend(std::forward<Args>(args)...);
406  
    }
392  
    }
407  

393  

408  
    capy::io_result<std::size_t> await_resume()
394  
    capy::io_result<std::size_t> await_resume()
409  
    {
395  
    {
410  
        if (sync_)
396  
        if (sync_)
411  
            return {ec_, n_};
397  
            return {ec_, n_};
412  
        return underlying_.await_resume();
398  
        return underlying_.await_resume();
413  
    }
399  
    }
414  
};
400  
};
415 -
//------------------------------------------------------------------------------
 
416  

401  

417  

402  

418  
/** Create a mocket paired with a socket.
403  
/** Create a mocket paired with a socket.
419  

404  

420  
    Creates a mocket and a tcp_socket connected via loopback.
405  
    Creates a mocket and a tcp_socket connected via loopback.
421  
    Data written to one can be read from the other.
406  
    Data written to one can be read from the other.
422  

407  

423  
    The mocket has fuse checks enabled via `maybe_fail()` and
408  
    The mocket has fuse checks enabled via `maybe_fail()` and
424  
    supports provide/expect buffers for test instrumentation.
409  
    supports provide/expect buffers for test instrumentation.
425  
    The tcp_socket is the "peer" end with no test instrumentation.
410  
    The tcp_socket is the "peer" end with no test instrumentation.
426  

411  

427  
    Optional max_read_size and max_write_size parameters limit the
412  
    Optional max_read_size and max_write_size parameters limit the
428  
    number of bytes transferred per I/O operation on the mocket,
413  
    number of bytes transferred per I/O operation on the mocket,
429  
    simulating chunked network delivery for testing purposes.
414  
    simulating chunked network delivery for testing purposes.
430  

415  

431  
    @param ctx The execution context for the sockets.
416  
    @param ctx The execution context for the sockets.
432  
    @param f The fuse for error injection testing.
417  
    @param f The fuse for error injection testing.
433  
    @param max_read_size Maximum bytes per read operation (default unlimited).
418  
    @param max_read_size Maximum bytes per read operation (default unlimited).
434  
    @param max_write_size Maximum bytes per write operation (default unlimited).
419  
    @param max_write_size Maximum bytes per write operation (default unlimited).
435  

420  

436  
    @return A pair of (mocket, tcp_socket).
421  
    @return A pair of (mocket, tcp_socket).
437  

422  

438  
    @note Mockets are not thread-safe and must be used in a
423  
    @note Mockets are not thread-safe and must be used in a
439  
        single-threaded, deterministic context.
424  
        single-threaded, deterministic context.
440  
*/
425  
*/
441  
BOOST_COROSIO_DECL
426  
BOOST_COROSIO_DECL
442 -
std::pair<mocket, tcp_socket>
427 +
std::pair<mocket, tcp_socket> make_mocket_pair(
443 -
make_mocket_pair(
 
444  
    capy::execution_context& ctx,
428  
    capy::execution_context& ctx,
445  
    capy::test::fuse f = {},
429  
    capy::test::fuse f = {},
446  
    std::size_t max_read_size = std::size_t(-1),
430  
    std::size_t max_read_size = std::size_t(-1),
447  
    std::size_t max_write_size = std::size_t(-1));
431  
    std::size_t max_write_size = std::size_t(-1));
448  

432  

449  
} // namespace boost::corosio::test
433  
} // namespace boost::corosio::test
450  

434  

451  
#endif
435  
#endif