1//
2// Copyright (c) 2022 Seth Heeren (sgheeren at gmail dot 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/boostorg/beast
8//
9
10// Test that header file is self-contained.
11#include <boost/beast/core/buffers_generator.hpp>
12
13#include "test_buffer.hpp"
14
15#include <boost/asio/error.hpp>
16#include <boost/beast/_experimental/unit_test/suite.hpp>
17#include <boost/beast/core/buffers_suffix.hpp>
18#include <boost/beast/core/detail/static_ostream.hpp>
19#include <boost/beast/core/flat_buffer.hpp>
20#include <boost/beast/_experimental/test/stream.hpp>
21
22#include <iostream>
23#include <numeric>
24
25namespace boost {
26namespace beast {
27
28namespace detail {
29
30struct test_buffers_generator
31{
32 using underlying_buffer_sequence = std::array<net::const_buffer, 2>;
33 using const_buffers_type = buffers_suffix<underlying_buffer_sequence>;
34 std::size_t iterations_ = 5;
35 bool verbose_ = false;
36 error_code emulate_error_;
37
38 test_buffers_generator(
39 error_code emulate_error = {}, bool verbose = false)
40 : verbose_(verbose)
41 , emulate_error_(emulate_error)
42 {
43 }
44
45 const_buffers_type cur_{};
46
47 bool
48 is_done() const
49 {
50 return iterations_ == 0 && ! buffer_bytes(cur_);
51 }
52
53 const_buffers_type
54 prepare(error_code& ec)
55 {
56 ec = {};
57 BEAST_EXPECT(! is_done());
58 if(verbose_)
59 std::clog
60 << "prepare, iterations_:" << iterations_
61 << " '" << buffers_to_string(buffers: cur_) << "' ";
62 if (!buffer_bytes(cur_)) {
63 if (iterations_) {
64 cur_ = const_buffers_type(
65 underlying_buffer_sequence{
66 net::buffer(data: "abcde", max_size_in_bytes: iterations_),
67 net::buffer(data: "12345", max_size_in_bytes: iterations_),
68 });
69 iterations_ -= 1;
70 }
71 if(emulate_error_ && iterations_ == 3)
72 {
73 ec = emulate_error_; // generate the specified error
74 }
75 }
76
77 if (verbose_)
78 std::clog << " -> '" << buffers_to_string(buffers: cur_)
79 << "'\n";
80 return const_buffers_type{ cur_ };
81 }
82
83 void consume(std::size_t n) {
84 cur_.consume(amount: n);
85 }
86};
87
88} // namespace detail
89
90class buffers_generator_test : public unit_test::suite
91{
92public:
93 void
94 testMinimalGenerator(error_code emulate_error)
95 {
96 static_assert(
97 is_buffers_generator<
98 detail::test_buffers_generator>::value,
99 "buffers_generator not modeled");
100
101 detail::test_buffers_generator gen{emulate_error};
102 error_code ec;
103
104 std::vector<std::string> actual;
105
106 while(! gen.is_done())
107 {
108 detail::test_buffers_generator::const_buffers_type b =
109 gen.prepare(ec);
110
111 if (ec) {
112 BEAST_EXPECT(emulate_error == ec);
113 // In test we ignore the error because we know that's okay.
114 //
115 // For general models of BuffersGenerator behaviour is
116 // unspecified when using a generator after receiving an error.
117 }
118
119 actual.push_back(x: buffers_to_string(buffers: b));
120
121 gen.consume(n: 3); // okay if > buffer_bytes
122 }
123 BEAST_EXPECT(! ec.failed());
124
125 if(! emulate_error)
126 {
127 BEAST_EXPECT(
128 (actual ==
129 std::vector<std::string>{
130 "abcde12345", "de12345", "2345", "5",
131 "abcd1234", "d1234", "34", "abc123", "123",
132 "ab12", "2", "a1" }));
133 }
134 }
135
136 void
137 testWrite(error_code emulate_error)
138 {
139 net::io_context ioc;
140 test::stream out(ioc), in(ioc);
141 test::connect(s1&: out, s2&: in);
142
143 {
144 detail::test_buffers_generator gen{emulate_error};
145
146 beast::error_code ec;
147 auto total = write(stream&: out, generator&: gen, ec);
148
149 BEAST_EXPECT(ec == emulate_error);
150
151 if(! emulate_error)
152 {
153 BEAST_EXPECT(total == 30);
154
155 BEAST_EXPECT(5 == out.nwrite());
156 BEAST_EXPECT(30 == in.nwrite_bytes());
157 BEAST_EXPECT(
158 "abcde12345abcd1234abc123ab12a1" == in.str());
159 } else
160 {
161 BEAST_EXPECT(total == 10);
162
163 BEAST_EXPECT(1 == out.nwrite());
164 BEAST_EXPECT(10 == in.nwrite_bytes());
165 BEAST_EXPECT("abcde12345" == in.str());
166 }
167 }
168
169 in.clear();
170
171 {
172 error_code ec;
173 auto total = write(stream&: out, generator: detail::test_buffers_generator{emulate_error}, ec);
174
175 BEAST_EXPECT(ec == emulate_error);
176
177 if(! emulate_error)
178 {
179 BEAST_EXPECT(total == 30);
180 BEAST_EXPECT("abcde12345abcd1234abc123ab12a1" == in.str());
181 } else
182 {
183 BEAST_EXPECT(total == 10);
184 BEAST_EXPECT("abcde12345" == in.str());
185 }
186 }
187 }
188
189 void
190 testWriteException(error_code emulate_error)
191 {
192 net::io_context ioc;
193 test::stream out(ioc), in(ioc);
194 {
195 test::connect(s1&: out, s2&: in);
196
197 detail::test_buffers_generator gen{emulate_error};
198
199 try {
200 auto total = write(stream&: out, generator&: gen);
201 if (emulate_error)
202 BEAST_EXPECT(!"unreachable");
203 BEAST_EXPECT(total == 30);
204 } catch(system_error const& se)
205 {
206 BEAST_EXPECT(se.code() == emulate_error);
207 }
208 }
209
210 if(! emulate_error)
211 {
212 BEAST_EXPECT(5 == out.nwrite());
213 BEAST_EXPECT(30 == in.nwrite_bytes());
214 BEAST_EXPECT(
215 "abcde12345abcd1234abc123ab12a1" == in.str());
216 }
217 else
218 {
219 BEAST_EXPECT(1 == out.nwrite());
220 BEAST_EXPECT(10 == in.nwrite_bytes());
221 BEAST_EXPECT("abcde12345" == in.str());
222 }
223 }
224
225 void
226 testAsyncWrite(error_code emulate_error)
227 {
228 net::io_context ioc;
229 test::stream out(ioc), in(ioc);
230 {
231 test::connect(s1&: out, s2&: in);
232
233 detail::test_buffers_generator gen{emulate_error};
234
235 async_write(
236 stream&: out,
237 generator: gen,
238 token: [&](error_code ec, std::size_t total)
239 {
240 BEAST_EXPECT(ec == emulate_error);
241 if(! emulate_error)
242 {
243 BEAST_EXPECT(total == 30);
244 } else
245 {
246 BEAST_EXPECT(total == 10);
247 }
248 });
249
250 ioc.run();
251 }
252
253 if(! emulate_error)
254 {
255 BEAST_EXPECT(5 == out.nwrite());
256 BEAST_EXPECT(30 == in.nwrite_bytes());
257 BEAST_EXPECT(
258 "abcde12345abcd1234abc123ab12a1" == in.str());
259 }
260 else
261 {
262 BEAST_EXPECT(1 == out.nwrite());
263 BEAST_EXPECT(10 == in.nwrite_bytes());
264 BEAST_EXPECT("abcde12345" == in.str());
265 }
266 }
267
268 void
269 testWriteFail()
270 {
271 net::io_context ioc;
272 test::fail_count fc { 3 };
273 test::stream out(ioc, fc), in(ioc);
274 {
275 test::connect(s1&: out, s2&: in);
276
277 detail::test_buffers_generator gen;
278
279 try {
280 /*auto total =*/ write(stream&: out, generator&: gen);
281 BEAST_EXPECT(! "unreachable");
282 } catch(system_error const& se)
283 {
284 BEAST_EXPECT(se.code() == test::error::test_failure);
285 }
286 }
287
288 BEAST_EXPECT(3 == out.nwrite());
289 BEAST_EXPECT(18 == in.nwrite_bytes()); // first two writes 10+8
290 BEAST_EXPECT("abcde12345abcd1234" == in.str());
291 }
292
293 void
294 testAsyncWriteFail()
295 {
296 net::io_context ioc;
297 test::fail_count fc { 3 };
298 test::stream out(ioc, fc), in(ioc);
299 {
300 test::connect(s1&: out, s2&: in);
301
302 detail::test_buffers_generator gen;
303
304 async_write(
305 stream&: out, generator: gen, token: [&](error_code ec, std::size_t total) {
306 BEAST_EXPECT(total == 18);
307 BEAST_EXPECT(ec == test::error::test_failure);
308 });
309
310 ioc.run();
311 }
312
313 BEAST_EXPECT(3 == out.nwrite());
314
315 BEAST_EXPECT(18 == in.nwrite_bytes()); // first two writes 10+8
316 BEAST_EXPECT("abcde12345abcd1234" == in.str());
317 }
318
319 void
320 run() override
321 {
322 for(error_code emulate_error :
323 {error_code{}, error_code{error::timeout}})
324 {
325 testMinimalGenerator(emulate_error);
326 testWrite(emulate_error);
327 testWriteException(emulate_error);
328 testAsyncWrite(emulate_error);
329 }
330
331 testWriteFail();
332 testAsyncWriteFail();
333 }
334};
335
336BEAST_DEFINE_TESTSUITE(beast,core,buffers_generator);
337
338} // beast
339} // boost
340

source code of boost/libs/beast/test/beast/core/buffers_generator.cpp