1 -
//
1 +
//
2 -
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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
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