]>
Commit | Line | Data |
---|---|---|
b32b8144 | 1 | // |
92f5a8d4 | 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
b32b8144 FG |
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/bind_handler.hpp> | |
12 | ||
92f5a8d4 TL |
13 | #include "test_handler.hpp" |
14 | ||
b32b8144 | 15 | #include <boost/beast/core/detail/type_traits.hpp> |
92f5a8d4 TL |
16 | #include <boost/beast/_experimental/unit_test/suite.hpp> |
17 | #include <boost/beast/_experimental/test/stream.hpp> | |
18 | #include <boost/asio/io_context.hpp> | |
19 | #include <boost/asio/bind_executor.hpp> | |
20 | #include <boost/asio/executor_work_guard.hpp> | |
21 | #include <boost/asio/dispatch.hpp> | |
22 | #include <boost/asio/defer.hpp> | |
23 | #include <boost/asio/post.hpp> | |
11fdf7f2 | 24 | #include <boost/asio/strand.hpp> |
92f5a8d4 TL |
25 | #include <boost/bind/placeholders.hpp> |
26 | #include <boost/core/exchange.hpp> | |
27 | #include <memory> | |
b32b8144 FG |
28 | #include <string> |
29 | ||
30 | namespace boost { | |
31 | namespace beast { | |
32 | ||
33 | class bind_handler_test : public unit_test::suite | |
34 | { | |
35 | public: | |
36 | template<class... Args> | |
37 | struct handler | |
38 | { | |
39 | void | |
40 | operator()(Args const&...) const | |
41 | { | |
42 | } | |
43 | }; | |
44 | ||
92f5a8d4 TL |
45 | struct copyable |
46 | { | |
47 | template<class... Args> | |
48 | void | |
49 | operator()(Args&&...) const | |
50 | { | |
51 | } | |
52 | }; | |
53 | ||
54 | struct move_only | |
55 | { | |
56 | move_only() = default; | |
57 | move_only(move_only&&) = default; | |
58 | move_only(move_only const&) = delete; | |
59 | void operator()() const{}; | |
60 | }; | |
61 | ||
62 | // A move-only parameter | |
63 | template<std::size_t I> | |
64 | struct move_arg | |
65 | { | |
66 | move_arg() = default; | |
67 | move_arg(move_arg&&) = default; | |
68 | move_arg(move_arg const&) = delete; | |
69 | void operator()() const | |
70 | { | |
71 | }; | |
72 | }; | |
73 | ||
74 | void | |
75 | callback(int v) | |
76 | { | |
77 | BEAST_EXPECT(v == 42); | |
78 | } | |
79 | ||
80 | class test_executor | |
81 | { | |
82 | bind_handler_test& s_; | |
92f5a8d4 | 83 | |
20effc67 TL |
84 | #if defined(BOOST_ASIO_NO_TS_EXECUTORS) |
85 | net::any_io_executor ex_; | |
86 | ||
87 | // Storing the blocking property as a member is not strictly necessary, | |
88 | // as we could simply forward the calls | |
89 | // require(ex_, blocking.possibly) | |
90 | // and | |
91 | // require(ex_, blocking.never) | |
92 | // to the underlying executor, and then | |
93 | // query(ex_, blocking) | |
94 | // when required. This forwarding approach is used here for the | |
95 | // outstanding_work property. | |
96 | net::execution::blocking_t blocking_; | |
97 | ||
98 | #else // defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
99 | net::io_context::executor_type ex_; | |
100 | #endif // defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
92f5a8d4 TL |
101 | public: |
102 | test_executor( | |
103 | test_executor const&) = default; | |
104 | ||
105 | test_executor( | |
106 | bind_handler_test& s, | |
107 | net::io_context& ioc) | |
108 | : s_(s) | |
109 | , ex_(ioc.get_executor()) | |
20effc67 TL |
110 | #if defined(BOOST_ASIO_NO_TS_EXECUTORS) |
111 | , blocking_(net::execution::blocking.possibly) | |
112 | #endif | |
92f5a8d4 TL |
113 | { |
114 | } | |
115 | ||
116 | bool operator==( | |
117 | test_executor const& other) const noexcept | |
118 | { | |
119 | return ex_ == other.ex_; | |
120 | } | |
121 | ||
122 | bool operator!=( | |
123 | test_executor const& other) const noexcept | |
124 | { | |
125 | return ex_ != other.ex_; | |
126 | } | |
127 | ||
20effc67 TL |
128 | #if defined(BOOST_ASIO_NO_TS_EXECUTORS) |
129 | ||
130 | net::execution_context& | |
131 | query( | |
132 | net::execution::context_t c) const noexcept | |
133 | { | |
134 | return net::query(ex_, c); | |
135 | } | |
136 | ||
137 | net::execution::blocking_t | |
138 | query( | |
139 | net::execution::blocking_t) const noexcept | |
140 | { | |
141 | return blocking_; | |
142 | } | |
143 | ||
144 | net::execution::outstanding_work_t | |
145 | query( | |
146 | net::execution::outstanding_work_t w) const noexcept | |
147 | { | |
148 | return net::query(ex_, w); | |
149 | } | |
150 | ||
151 | test_executor | |
152 | require( | |
153 | net::execution::blocking_t::possibly_t b) const | |
154 | { | |
155 | test_executor new_ex(*this); | |
156 | new_ex.blocking_ = b; | |
157 | return new_ex; | |
158 | } | |
159 | ||
160 | test_executor | |
161 | require( | |
162 | net::execution::blocking_t::never_t b) const | |
163 | { | |
164 | test_executor new_ex(*this); | |
165 | new_ex.blocking_ = b; | |
166 | return new_ex; | |
167 | } | |
168 | ||
169 | test_executor prefer(net::execution::outstanding_work_t::untracked_t w) const | |
170 | { | |
171 | test_executor new_ex(*this); | |
172 | new_ex.ex_ = net::prefer(ex_, w); | |
173 | return new_ex; | |
174 | } | |
175 | ||
176 | test_executor prefer(net::execution::outstanding_work_t::tracked_t w) const | |
177 | { | |
178 | test_executor new_ex(*this); | |
179 | new_ex.ex_ = net::prefer(ex_, w); | |
180 | return new_ex; | |
181 | } | |
182 | ||
183 | template<class F> | |
184 | void execute(F&& f) const | |
185 | { | |
186 | if (blocking_ == net::execution::blocking.possibly) | |
187 | { | |
188 | s_.on_invoke(); | |
189 | net::execution::execute(ex_, std::forward<F>(f)); | |
190 | } | |
191 | else | |
192 | { | |
193 | // shouldn't be called since the enclosing | |
194 | // networking wrapper only uses dispatch | |
195 | BEAST_FAIL(); | |
196 | } | |
197 | } | |
198 | #endif | |
199 | #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) | |
200 | net::execution_context& | |
92f5a8d4 TL |
201 | context() const noexcept |
202 | { | |
203 | return ex_.context(); | |
204 | } | |
205 | ||
20effc67 TL |
206 | void |
207 | on_work_started() const noexcept | |
92f5a8d4 TL |
208 | { |
209 | ex_.on_work_started(); | |
210 | } | |
211 | ||
20effc67 TL |
212 | void |
213 | on_work_finished() const noexcept | |
92f5a8d4 TL |
214 | { |
215 | ex_.on_work_finished(); | |
216 | } | |
217 | ||
218 | template<class F, class Alloc> | |
20effc67 TL |
219 | void |
220 | dispatch(F&& f, Alloc const& a) | |
92f5a8d4 TL |
221 | { |
222 | s_.on_invoke(); | |
20effc67 TL |
223 | net::execution::execute( |
224 | net::prefer(ex_, | |
225 | net::execution::blocking.possibly, | |
226 | net::execution::allocator(a)), | |
227 | std::forward<F>(f)); | |
228 | // previously equivalent to | |
229 | // ex_.dispatch(std::forward<F>(f), a); | |
92f5a8d4 TL |
230 | } |
231 | ||
232 | template<class F, class Alloc> | |
20effc67 TL |
233 | void |
234 | post(F&& f, Alloc const& a) | |
92f5a8d4 TL |
235 | { |
236 | // shouldn't be called since the enclosing | |
237 | // networking wrapper only uses dispatch | |
238 | BEAST_FAIL(); | |
239 | } | |
240 | ||
241 | template<class F, class Alloc> | |
20effc67 TL |
242 | void |
243 | defer(F&& f, Alloc const& a) | |
92f5a8d4 TL |
244 | { |
245 | // shouldn't be called since the enclosing | |
246 | // networking wrapper only uses dispatch | |
247 | BEAST_FAIL(); | |
248 | } | |
20effc67 | 249 | #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
92f5a8d4 TL |
250 | }; |
251 | ||
20effc67 TL |
252 | #if defined(BOOST_ASIO_NO_TS_EXECUTORS) |
253 | BOOST_STATIC_ASSERT(net::execution::is_executor<test_executor>::value); | |
254 | #else | |
255 | BOOST_STATIC_ASSERT(net::is_executor<test_executor>::value); | |
256 | #endif | |
257 | ||
92f5a8d4 TL |
258 | class test_cb |
259 | { | |
260 | bool fail_ = true; | |
261 | ||
262 | public: | |
263 | test_cb() = default; | |
264 | test_cb(test_cb const&) = delete; | |
265 | test_cb(test_cb&& other) | |
266 | : fail_(boost::exchange( | |
267 | other.fail_, false)) | |
268 | { | |
269 | } | |
270 | ||
271 | ~test_cb() | |
272 | { | |
273 | BEAST_EXPECT(! fail_); | |
274 | } | |
275 | ||
276 | void | |
277 | operator()() | |
278 | { | |
279 | fail_ = false; | |
280 | BEAST_EXPECT(true); | |
281 | } | |
282 | ||
283 | void | |
284 | operator()(int v) | |
285 | { | |
286 | fail_ = false; | |
287 | BEAST_EXPECT(v == 42); | |
288 | } | |
289 | ||
290 | void | |
291 | operator()(int v, string_view s) | |
292 | { | |
293 | fail_ = false; | |
294 | BEAST_EXPECT(v == 42); | |
295 | BEAST_EXPECT(s == "s"); | |
296 | } | |
297 | ||
298 | void | |
299 | operator()(int v, string_view s, move_arg<1>) | |
300 | { | |
301 | fail_ = false; | |
302 | BEAST_EXPECT(v == 42); | |
303 | BEAST_EXPECT(s == "s"); | |
304 | } | |
305 | ||
306 | void | |
307 | operator()(int v, string_view s, move_arg<1>, move_arg<2>) | |
308 | { | |
309 | fail_ = false; | |
310 | BEAST_EXPECT(v == 42); | |
311 | BEAST_EXPECT(s == "s"); | |
312 | } | |
313 | ||
314 | void | |
315 | operator()(error_code, std::size_t n) | |
316 | { | |
317 | fail_ = false; | |
318 | BEAST_EXPECT(n == 256); | |
319 | } | |
320 | ||
321 | void | |
322 | operator()( | |
323 | error_code, std::size_t n, string_view s) | |
324 | { | |
325 | boost::ignore_unused(s); | |
326 | fail_ = false; | |
327 | BEAST_EXPECT(n == 256); | |
328 | } | |
329 | ||
330 | void | |
331 | operator()(std::shared_ptr<int> const& sp) | |
332 | { | |
333 | fail_ = false; | |
334 | BEAST_EXPECT(sp.get() != nullptr); | |
335 | } | |
336 | }; | |
337 | ||
b32b8144 | 338 | #if 0 |
92f5a8d4 TL |
339 | // These functions should fail to compile |
340 | ||
b32b8144 FG |
341 | void |
342 | failStdBind() | |
343 | { | |
92f5a8d4 TL |
344 | std::bind(bind_handler(test_cb{})); |
345 | } | |
346 | ||
347 | void | |
348 | failStdBindFront() | |
349 | { | |
350 | std::bind(bind_front_handler(test_cb{})); | |
b32b8144 FG |
351 | } |
352 | #endif | |
353 | ||
92f5a8d4 TL |
354 | //-------------------------------------------------------------------------- |
355 | ||
356 | bool invoked_; | |
357 | ||
b32b8144 | 358 | void |
92f5a8d4 | 359 | on_invoke() |
b32b8144 | 360 | { |
92f5a8d4 | 361 | invoked_ = true; |
b32b8144 | 362 | } |
92f5a8d4 TL |
363 | |
364 | template<class F> | |
b32b8144 | 365 | void |
92f5a8d4 | 366 | testHooks(net::io_context& ioc, F&& f) |
b32b8144 | 367 | { |
92f5a8d4 TL |
368 | invoked_ = false; |
369 | net::post(ioc, std::forward<F>(f)); | |
370 | ioc.run(); | |
371 | ioc.restart(); | |
372 | BEAST_EXPECT(invoked_); | |
b32b8144 FG |
373 | } |
374 | ||
92f5a8d4 TL |
375 | //-------------------------------------------------------------------------- |
376 | ||
377 | void | |
378 | testBindHandler() | |
11fdf7f2 | 379 | { |
92f5a8d4 TL |
380 | using m1 = move_arg<1>; |
381 | using m2 = move_arg<2>; | |
382 | ||
11fdf7f2 | 383 | { |
92f5a8d4 TL |
384 | using namespace std::placeholders; |
385 | ||
386 | // 0-ary | |
387 | bind_handler(test_cb{})(); | |
388 | ||
389 | // 1-ary | |
390 | bind_handler(test_cb{}, 42)(); | |
391 | bind_handler(test_cb{}, _1)(42); | |
392 | bind_handler(test_cb{}, _2)(0, 42); | |
393 | ||
394 | // 2-ary | |
395 | bind_handler(test_cb{}, 42, "s")(); | |
396 | bind_handler(test_cb{}, 42, "s")(0); | |
397 | bind_handler(test_cb{}, _1, "s")(42); | |
398 | bind_handler(test_cb{}, 42, _1) ("s"); | |
399 | bind_handler(test_cb{}, _1, _2)(42, "s"); | |
400 | bind_handler(test_cb{}, _1, _2)(42, "s", "X"); | |
401 | bind_handler(test_cb{}, _2, _1)("s", 42); | |
402 | bind_handler(test_cb{}, _3, _2)("X", "s", 42); | |
403 | ||
404 | // 3-ary | |
405 | bind_handler(test_cb{}, 42, "s")(m1{}); | |
406 | bind_handler(test_cb{}, 42, "s", _1)(m1{}); | |
407 | bind_handler(test_cb{}, 42, _1, m1{})("s"); | |
408 | ||
409 | // 4-ary | |
410 | bind_handler(test_cb{}, 42, "s")(m1{}, m2{}); | |
411 | bind_handler(test_cb{}, 42, "s", m1{})(m2{}); | |
412 | bind_handler(test_cb{}, 42, "s", m1{}, m2{})(); | |
413 | bind_handler(test_cb{}, 42, _1, m1{})("s", m2{}); | |
414 | bind_handler(test_cb{}, _3, _1, m1{})("s", m2{}, 42); | |
11fdf7f2 | 415 | } |
11fdf7f2 | 416 | |
92f5a8d4 TL |
417 | { |
418 | using namespace boost::placeholders; | |
419 | ||
420 | // 0-ary | |
421 | bind_handler(test_cb{})(); | |
422 | ||
423 | // 1-ary | |
424 | bind_handler(test_cb{}, 42)(); | |
425 | bind_handler(test_cb{}, _1)(42); | |
426 | bind_handler(test_cb{}, _2)(0, 42); | |
427 | ||
428 | // 2-ary | |
429 | bind_handler(test_cb{}, 42, "s")(); | |
430 | bind_handler(test_cb{}, 42, "s")(0); | |
431 | bind_handler(test_cb{}, _1, "s")(42); | |
432 | bind_handler(test_cb{}, 42, _1) ("s"); | |
433 | bind_handler(test_cb{}, _1, _2)(42, "s"); | |
434 | bind_handler(test_cb{}, _1, _2)(42, "s", "X"); | |
435 | bind_handler(test_cb{}, _2, _1)("s", 42); | |
436 | bind_handler(test_cb{}, _3, _2)("X", "s", 42); | |
437 | ||
438 | // 3-ary | |
439 | bind_handler(test_cb{}, 42, "s")(m1{}); | |
440 | bind_handler(test_cb{}, 42, "s", _1)(m1{}); | |
441 | bind_handler(test_cb{}, 42, _1, m1{})("s"); | |
442 | ||
443 | // 4-ary | |
444 | bind_handler(test_cb{}, 42, "s")(m1{}, m2{}); | |
445 | bind_handler(test_cb{}, 42, "s", m1{})(m2{}); | |
446 | bind_handler(test_cb{}, 42, "s", m1{}, m2{})(); | |
447 | bind_handler(test_cb{}, 42, _1, m1{})("s", m2{}); | |
448 | bind_handler(test_cb{}, _3, _1, m1{})("s", m2{}, 42); | |
449 | } | |
450 | ||
451 | // perfect forwarding | |
452 | { | |
453 | std::shared_ptr<int> const sp = | |
454 | std::make_shared<int>(42); | |
455 | { | |
456 | bind_handler(test_cb{}, sp)(); | |
457 | BEAST_EXPECT(sp.get() != nullptr); | |
458 | } | |
459 | { | |
460 | bind_handler(test_cb{})(sp); | |
461 | BEAST_EXPECT(sp.get() != nullptr); | |
462 | } | |
463 | } | |
464 | ||
465 | // associated executor | |
466 | { | |
467 | net::io_context ioc; | |
468 | testHooks(ioc, bind_handler(net::bind_executor( | |
469 | test_executor(*this, ioc), test_cb{}))); | |
470 | } | |
471 | ||
472 | // asio_handler_invoke | |
473 | { | |
474 | // make sure things compile, also can set a | |
475 | // breakpoint in asio_handler_invoke to make sure | |
476 | // it is instantiated. | |
477 | net::io_context ioc; | |
478 | net::strand< | |
479 | net::io_context::executor_type> s{ | |
480 | ioc.get_executor()}; | |
481 | net::post(s, | |
482 | bind_handler(test_cb{}, 42)); | |
483 | ioc.run(); | |
484 | } | |
485 | ||
486 | // legacy hooks | |
487 | legacy_handler::test( | |
488 | [](legacy_handler h) | |
489 | { | |
490 | return bind_handler(h); | |
491 | }); | |
492 | } | |
493 | ||
494 | void | |
495 | testBindFrontHandler() | |
496 | { | |
497 | using m1 = move_arg<1>; | |
498 | using m2 = move_arg<2>; | |
499 | ||
500 | // 0-ary | |
501 | bind_front_handler(test_cb{})(); | |
502 | ||
503 | // 1-ary | |
504 | bind_front_handler(test_cb{}, 42)(); | |
505 | bind_front_handler(test_cb{})(42); | |
506 | ||
507 | // 2-ary | |
508 | bind_front_handler(test_cb{}, 42, "s")(); | |
509 | bind_front_handler(test_cb{}, 42)("s"); | |
510 | bind_front_handler(test_cb{})(42, "s"); | |
511 | ||
512 | // 3-ary | |
513 | bind_front_handler(test_cb{}, 42, "s", m1{})(); | |
514 | bind_front_handler(test_cb{}, 42, "s")(m1{}); | |
515 | bind_front_handler(test_cb{}, 42)("s", m1{}); | |
516 | bind_front_handler(test_cb{})(42, "s", m1{}); | |
517 | ||
518 | // 4-ary | |
519 | bind_front_handler(test_cb{}, 42, "s", m1{}, m2{})(); | |
520 | bind_front_handler(test_cb{}, 42, "s", m1{})(m2{}); | |
521 | bind_front_handler(test_cb{}, 42, "s")(m1{}, m2{}); | |
522 | bind_front_handler(test_cb{}, 42)("s", m1{}, m2{}); | |
523 | bind_front_handler(test_cb{})(42, "s", m1{}, m2{}); | |
524 | ||
525 | error_code ec; | |
526 | std::size_t n = 256; | |
527 | ||
528 | // void(error_code, size_t) | |
529 | bind_front_handler(test_cb{}, ec, n)(); | |
530 | ||
531 | // void(error_code, size_t)(string_view) | |
532 | bind_front_handler(test_cb{}, ec, n)("s"); | |
533 | ||
534 | // perfect forwarding | |
535 | { | |
536 | std::shared_ptr<int> const sp = | |
537 | std::make_shared<int>(42); | |
538 | bind_front_handler(test_cb{}, sp)(); | |
539 | BEAST_EXPECT(sp.get() != nullptr); | |
540 | } | |
541 | ||
542 | // associated executor | |
543 | { | |
544 | net::io_context ioc; | |
545 | ||
546 | testHooks(ioc, bind_front_handler(net::bind_executor( | |
547 | test_executor(*this, ioc), test_cb{}) | |
548 | )); | |
549 | testHooks(ioc, bind_front_handler(net::bind_executor( | |
550 | test_executor(*this, ioc), test_cb{}), | |
551 | 42)); | |
552 | testHooks(ioc, bind_front_handler(net::bind_executor( | |
553 | test_executor(*this, ioc), test_cb{}), | |
554 | 42, "s")); | |
555 | testHooks(ioc, bind_front_handler(net::bind_executor( | |
556 | test_executor(*this, ioc), test_cb{}), | |
557 | 42, "s", m1{})); | |
558 | testHooks(ioc, bind_front_handler(net::bind_executor( | |
559 | test_executor(*this, ioc), test_cb{}), | |
560 | 42, "s", m1{}, m2{})); | |
561 | testHooks(ioc, bind_front_handler(net::bind_executor( | |
562 | test_executor(*this, ioc), test_cb{}), | |
563 | ec, n)); | |
564 | } | |
565 | ||
566 | // legacy hooks | |
567 | legacy_handler::test( | |
568 | [](legacy_handler h) | |
569 | { | |
570 | return bind_front_handler(h); | |
571 | }); | |
572 | legacy_handler::test( | |
573 | [](legacy_handler h) | |
574 | { | |
575 | return bind_front_handler( | |
576 | h, error_code{}, std::size_t{}); | |
577 | }); | |
578 | } | |
579 | ||
580 | ||
581 | //-------------------------------------------------------------------------- | |
582 | ||
583 | template <class AsyncReadStream, class ReadHandler> | |
11fdf7f2 | 584 | void |
92f5a8d4 TL |
585 | signal_aborted (AsyncReadStream& stream, ReadHandler&& handler) |
586 | { | |
587 | net::post( | |
588 | stream.get_executor(), | |
589 | bind_handler (std::forward <ReadHandler> (handler), | |
590 | net::error::operation_aborted, 0)); | |
591 | } | |
592 | ||
593 | template <class AsyncReadStream, class ReadHandler> | |
594 | void | |
595 | signal_eof (AsyncReadStream& stream, ReadHandler&& handler) | |
596 | { | |
597 | net::post( | |
598 | stream.get_executor(), | |
599 | bind_front_handler (std::forward<ReadHandler> (handler), | |
600 | net::error::eof, 0)); | |
11fdf7f2 TL |
601 | } |
602 | ||
92f5a8d4 TL |
603 | void |
604 | testJavadocs() | |
605 | { | |
606 | BEAST_EXPECT(( | |
607 | &bind_handler_test::signal_aborted< | |
608 | test::stream, handler<error_code, std::size_t>>)); | |
609 | ||
610 | BEAST_EXPECT(( | |
611 | &bind_handler_test::signal_eof< | |
612 | test::stream, handler<error_code, std::size_t>>)); | |
613 | } | |
614 | ||
615 | //-------------------------------------------------------------------------- | |
616 | ||
b32b8144 FG |
617 | void |
618 | run() override | |
619 | { | |
92f5a8d4 TL |
620 | testBindHandler(); |
621 | testBindFrontHandler(); | |
622 | testJavadocs(); | |
b32b8144 FG |
623 | } |
624 | }; | |
625 | ||
626 | BEAST_DEFINE_TESTSUITE(beast,core,bind_handler); | |
627 | ||
628 | } // beast | |
629 | } // boost |