1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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
65  
    consume_provide(MutableBufferSequence const& buffers) noexcept;
65  
    consume_provide(MutableBufferSequence const& buffers) noexcept;
66  

66  

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

72  

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

76  

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

79  

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

83  

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

85  

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

96  

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

100  

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

104  

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

107  

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

109  

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

117  

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

119  

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

127  

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

129  

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

133  

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

135  

136  
        @pre All coroutines using this mocket must be suspended.
136  
        @pre All coroutines using this mocket must be suspended.
137  
    */
137  
    */
138  
    void provide(std::string s);
138  
    void provide(std::string s);
139  

139  

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

141  

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

146  

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

148  

149  
        @pre All coroutines using this mocket must be suspended.
149  
        @pre All coroutines using this mocket must be suspended.
150  
    */
150  
    */
151  
    void expect(std::string s);
151  
    void expect(std::string s);
152  

152  

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

154  

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

159  

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

164  

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

166  

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

171  

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

173  

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

177  

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

179  

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

183  

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

185  

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

193  

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

195  

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

199  

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

201  

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

210  

211  
//------------------------------------------------------------------------------
211  
//------------------------------------------------------------------------------
212  

212  

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

222  

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

232  

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

240  

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

249  

250  
    // Consume matched portion
250  
    // Consume matched portion
251  
    expect_.erase(0, match_size);
251  
    expect_.erase(0, match_size);
252  
    bytes_written = written.size();
252  
    bytes_written = written.size();
253  
    return true;
253  
    return true;
254  
}
254  
}
255  

255  

256  
//------------------------------------------------------------------------------
256  
//------------------------------------------------------------------------------
257  

257  

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

264  

265  
    mocket* m_;
265  
    mocket* m_;
266  
    MutableBufferSequence buffers_;
266  
    MutableBufferSequence buffers_;
267  
    std::size_t n_ = 0;
267  
    std::size_t n_ = 0;
268  
    union {
268  
    union {
269  
        char dummy_;
269  
        char dummy_;
270  
        sock_awaitable underlying_;
270  
        sock_awaitable underlying_;
271  
    };
271  
    };
272  
    bool sync_ = true;
272  
    bool sync_ = true;
273  

273  

274  
public:
274  
public:
275  
    read_some_awaitable(
275  
    read_some_awaitable(
276  
        mocket& m,
276  
        mocket& m,
277  
        MutableBufferSequence buffers) noexcept
277  
        MutableBufferSequence buffers) noexcept
278  
        : m_(&m)
278  
        : m_(&m)
279  
        , buffers_(std::move(buffers))
279  
        , buffers_(std::move(buffers))
280  
    {
280  
    {
281  
    }
281  
    }
282  

282  

283  
    ~read_some_awaitable()
283  
    ~read_some_awaitable()
284  
    {
284  
    {
285  
        if (!sync_)
285  
        if (!sync_)
286  
            underlying_.~sock_awaitable();
286  
            underlying_.~sock_awaitable();
287  
    }
287  
    }
288  

288  

289  
    read_some_awaitable(read_some_awaitable&& other) noexcept
289  
    read_some_awaitable(read_some_awaitable&& other) noexcept
290  
        : m_(other.m_)
290  
        : m_(other.m_)
291  
        , buffers_(std::move(other.buffers_))
291  
        , buffers_(std::move(other.buffers_))
292  
        , n_(other.n_)
292  
        , n_(other.n_)
293  
        , sync_(other.sync_)
293  
        , sync_(other.sync_)
294  
    {
294  
    {
295  
        if (!sync_)
295  
        if (!sync_)
296  
        {
296  
        {
297  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
297  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
298  
            other.underlying_.~sock_awaitable();
298  
            other.underlying_.~sock_awaitable();
299  
            other.sync_ = true;
299  
            other.sync_ = true;
300  
        }
300  
        }
301  
    }
301  
    }
302  

302  

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

306  

307  
    bool await_ready()
307  
    bool await_ready()
308  
    {
308  
    {
309  
        if (!m_->provide_.empty())
309  
        if (!m_->provide_.empty())
310  
        {
310  
        {
311  
            n_ = m_->consume_provide(buffers_);
311  
            n_ = m_->consume_provide(buffers_);
312  
            return true;
312  
            return true;
313  
        }
313  
        }
314  
        new (&underlying_) sock_awaitable(m_->sock_.read_some(buffers_));
314  
        new (&underlying_) sock_awaitable(m_->sock_.read_some(buffers_));
315  
        sync_ = false;
315  
        sync_ = false;
316  
        return underlying_.await_ready();
316  
        return underlying_.await_ready();
317  
    }
317  
    }
318  

318  

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

324  

325  
    capy::io_result<std::size_t> await_resume()
325  
    capy::io_result<std::size_t> await_resume()
326  
    {
326  
    {
327  
        if (sync_)
327  
        if (sync_)
328  
            return {{}, n_};
328  
            return {{}, n_};
329  
        return underlying_.await_resume();
329  
        return underlying_.await_resume();
330  
    }
330  
    }
331  
};
331  
};
332  

332  

333  
//------------------------------------------------------------------------------
333  
//------------------------------------------------------------------------------
334  

334  

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

341  

342  
    mocket* m_;
342  
    mocket* m_;
343  
    ConstBufferSequence buffers_;
343  
    ConstBufferSequence buffers_;
344  
    std::size_t n_ = 0;
344  
    std::size_t n_ = 0;
345  
    std::error_code ec_;
345  
    std::error_code ec_;
346  
    union {
346  
    union {
347  
        char dummy_;
347  
        char dummy_;
348  
        sock_awaitable underlying_;
348  
        sock_awaitable underlying_;
349  
    };
349  
    };
350  
    bool sync_ = true;
350  
    bool sync_ = true;
351  

351  

352  
public:
352  
public:
353  
    write_some_awaitable(
353  
    write_some_awaitable(
354  
        mocket& m,
354  
        mocket& m,
355  
        ConstBufferSequence buffers) noexcept
355  
        ConstBufferSequence buffers) noexcept
356  
        : m_(&m)
356  
        : m_(&m)
357  
        , buffers_(std::move(buffers))
357  
        , buffers_(std::move(buffers))
358  
    {
358  
    {
359  
    }
359  
    }
360  

360  

361  
    ~write_some_awaitable()
361  
    ~write_some_awaitable()
362  
    {
362  
    {
363  
        if (!sync_)
363  
        if (!sync_)
364  
            underlying_.~sock_awaitable();
364  
            underlying_.~sock_awaitable();
365  
    }
365  
    }
366  

366  

367  
    write_some_awaitable(write_some_awaitable&& other) noexcept
367  
    write_some_awaitable(write_some_awaitable&& other) noexcept
368  
        : m_(other.m_)
368  
        : m_(other.m_)
369  
        , buffers_(std::move(other.buffers_))
369  
        , buffers_(std::move(other.buffers_))
370  
        , n_(other.n_)
370  
        , n_(other.n_)
371  
        , ec_(other.ec_)
371  
        , ec_(other.ec_)
372  
        , sync_(other.sync_)
372  
        , sync_(other.sync_)
373  
    {
373  
    {
374  
        if (!sync_)
374  
        if (!sync_)
375  
        {
375  
        {
376  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
376  
            new (&underlying_) sock_awaitable(std::move(other.underlying_));
377  
            other.underlying_.~sock_awaitable();
377  
            other.underlying_.~sock_awaitable();
378  
            other.sync_ = true;
378  
            other.sync_ = true;
379  
        }
379  
        }
380  
    }
380  
    }
381  

381  

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

385  

386  
    bool await_ready()
386  
    bool await_ready()
387  
    {
387  
    {
388  
        if (!m_->expect_.empty())
388  
        if (!m_->expect_.empty())
389  
        {
389  
        {
390  
            if (!m_->validate_expect(buffers_, n_))
390  
            if (!m_->validate_expect(buffers_, n_))
391  
            {
391  
            {
392  
                ec_ = capy::error::test_failure;
392  
                ec_ = capy::error::test_failure;
393  
                n_ = 0;
393  
                n_ = 0;
394  
            }
394  
            }
395  
            return true;
395  
            return true;
396  
        }
396  
        }
397  
        new (&underlying_) sock_awaitable(m_->sock_.write_some(buffers_));
397  
        new (&underlying_) sock_awaitable(m_->sock_.write_some(buffers_));
398  
        sync_ = false;
398  
        sync_ = false;
399  
        return underlying_.await_ready();
399  
        return underlying_.await_ready();
400  
    }
400  
    }
401  

401  

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

407  

408  
    capy::io_result<std::size_t> await_resume()
408  
    capy::io_result<std::size_t> await_resume()
409  
    {
409  
    {
410  
        if (sync_)
410  
        if (sync_)
411  
            return {ec_, n_};
411  
            return {ec_, n_};
412  
        return underlying_.await_resume();
412  
        return underlying_.await_resume();
413  
    }
413  
    }
414  
};
414  
};
415  

415  

416  
//------------------------------------------------------------------------------
416  
//------------------------------------------------------------------------------
417  

417  

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

419  

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

422  

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

426  

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

430  

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

435  

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

437  

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

448  

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

450  

451  
#endif
451  
#endif