TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_TEST_FUSE_HPP
11 : #define BOOST_CAPY_TEST_FUSE_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/concept/io_runnable.hpp>
15 : #include <boost/capy/error.hpp>
16 : #include <boost/capy/test/run_blocking.hpp>
17 : #include <system_error>
18 : #include <cstddef>
19 : #include <exception>
20 : #include <limits>
21 : #include <memory>
22 : #include <source_location>
23 : #include <type_traits>
24 :
25 : /*
26 : LLM/AI Instructions for fuse-based test patterns:
27 :
28 : When f.armed() runs a test, it injects errors at successive points
29 : via maybe_fail(). Operations like read_stream::read_some() and
30 : write_stream::write_some() call maybe_fail() internally.
31 :
32 : CORRECT pattern - early return on injected error:
33 :
34 : auto [ec, n] = co_await rs.read_some(buf);
35 : if(ec)
36 : co_return; // fuse injected error, exit gracefully
37 : // ... continue with success path
38 :
39 : WRONG pattern - asserting success unconditionally:
40 :
41 : auto [ec, n] = co_await rs.read_some(buf);
42 : BOOST_TEST(! ec); // FAILS when fuse injects error!
43 :
44 : The fuse mechanism tests error handling by failing at each point
45 : in sequence. Tests must handle injected errors by returning early,
46 : not by asserting that operations always succeed.
47 : */
48 :
49 : namespace boost {
50 : namespace capy {
51 : namespace test {
52 :
53 : /** A test utility for systematic error injection.
54 :
55 : This class enables exhaustive testing of error handling
56 : paths by injecting failures at successive points in code.
57 : Each iteration fails at a later point until the code path
58 : completes without encountering a failure. The @ref armed
59 : method runs in two phases: first with error codes, then
60 : with exceptions. The @ref inert method runs once without
61 : automatic failure injection.
62 :
63 : @par Thread Safety
64 :
65 : @b Not @b thread @b safe. Instances must not be accessed
66 : from different logical threads of operation concurrently.
67 : This includes coroutines - accessing the same fuse from
68 : multiple concurrent coroutines causes non-deterministic
69 : test behavior.
70 :
71 : @par Basic Inline Usage
72 :
73 : @code
74 : fuse()([](fuse& f) {
75 : auto ec = f.maybe_fail();
76 : if(ec)
77 : return;
78 :
79 : ec = f.maybe_fail();
80 : if(ec)
81 : return;
82 : });
83 : @endcode
84 :
85 : @par Named Fuse with armed()
86 :
87 : @code
88 : fuse f;
89 : MyObject obj(f);
90 : auto r = f.armed([&](fuse&) {
91 : obj.do_something();
92 : });
93 : @endcode
94 :
95 : @par Using inert() for Single-Run Tests
96 :
97 : @code
98 : fuse f;
99 : auto r = f.inert([](fuse& f) {
100 : auto ec = f.maybe_fail(); // Always succeeds
101 : if(some_condition)
102 : f.fail(); // Only way to signal failure
103 : });
104 : @endcode
105 :
106 : @par Dependency Injection (Standalone Usage)
107 :
108 : A default-constructed fuse is a no-op when used outside
109 : of @ref armed or @ref inert. This enables passing a fuse
110 : to classes for dependency injection without affecting
111 : normal operation.
112 :
113 : @code
114 : class MyService
115 : {
116 : fuse& f_;
117 : public:
118 : explicit MyService(fuse& f) : f_(f) {}
119 :
120 : std::error_code do_work()
121 : {
122 : auto ec = f_.maybe_fail(); // No-op outside armed/inert
123 : if(ec)
124 : return ec;
125 : // ... actual work ...
126 : return {};
127 : }
128 : };
129 :
130 : // Production usage - fuse is no-op
131 : fuse f;
132 : MyService svc(f);
133 : svc.do_work(); // maybe_fail() returns {} always
134 :
135 : // Test usage - failures are injected
136 : auto r = f.armed([&](fuse&) {
137 : svc.do_work(); // maybe_fail() triggers failures
138 : });
139 : @endcode
140 :
141 : @par Custom Error Code
142 :
143 : @code
144 : auto custom_ec = make_error_code(
145 : std::errc::operation_canceled);
146 : fuse f(custom_ec);
147 : auto r = f.armed([](fuse& f) {
148 : auto ec = f.maybe_fail();
149 : if(ec)
150 : return;
151 : });
152 : @endcode
153 :
154 : @par Checking the Result
155 :
156 : @code
157 : fuse f;
158 : auto r = f([](fuse& f) {
159 : auto ec = f.maybe_fail();
160 : if(ec)
161 : return;
162 : });
163 :
164 : if(!r)
165 : {
166 : std::cerr << "Failure at "
167 : << r.loc.file_name() << ":"
168 : << r.loc.line() << "\n";
169 : }
170 : @endcode
171 :
172 : @par Test Framework Integration
173 :
174 : @code
175 : fuse f;
176 : auto r = f([](fuse& f) {
177 : auto ec = f.maybe_fail();
178 : if(ec)
179 : return;
180 : });
181 :
182 : // Boost.Test
183 : BOOST_TEST(r.success);
184 : if(!r)
185 : BOOST_TEST_MESSAGE("Failed at " << r.loc.file_name()
186 : << ":" << r.loc.line());
187 :
188 : // Catch2
189 : REQUIRE(r.success);
190 : if(!r)
191 : INFO("Failed at " << r.loc.file_name()
192 : << ":" << r.loc.line());
193 : @endcode
194 : */
195 : class fuse
196 : {
197 : struct state
198 : {
199 : std::size_t n = (std::numeric_limits<std::size_t>::max)();
200 : std::size_t i = 0;
201 : bool triggered = false;
202 : bool throws = false;
203 : bool stopped = false;
204 : bool inert = true;
205 : std::error_code ec;
206 : std::source_location loc;
207 : std::exception_ptr ep;
208 : };
209 :
210 : std::shared_ptr<state> p_;
211 :
212 : /** Return true if testing should continue.
213 :
214 : On the first call, initializes the failure point to 0.
215 : After a triggered failure, increments the failure point
216 : and resets for the next iteration. Returns false when
217 : the test completes without triggering a failure.
218 : */
219 HIT 3551 : explicit operator bool() const noexcept
220 : {
221 3551 : auto& s = *p_;
222 3551 : if(s.n == (std::numeric_limits<std::size_t>::max)())
223 : {
224 : // First call: start round 0
225 742 : s.n = 0;
226 742 : return true;
227 : }
228 2809 : if(s.triggered)
229 : {
230 : // Previous round triggered, try next failure point
231 2073 : s.n++;
232 2073 : s.i = 0;
233 2073 : s.triggered = false;
234 2073 : return true;
235 : }
236 : // Test completed without trigger: success
237 736 : return false;
238 : }
239 :
240 : public:
241 : /** Result of a fuse operation.
242 :
243 : Contains the outcome of @ref armed or @ref inert
244 : and, on failure, the source location of the failing
245 : point. Converts to `bool` for convenient success
246 : checking.
247 :
248 : @par Example
249 :
250 : @code
251 : fuse f;
252 : auto r = f([](fuse& f) {
253 : auto ec = f.maybe_fail();
254 : if(ec)
255 : return;
256 : });
257 :
258 : if(!r)
259 : {
260 : std::cerr << "Failure at "
261 : << r.loc.file_name() << ":"
262 : << r.loc.line() << "\n";
263 : }
264 : @endcode
265 : */
266 : struct result
267 : {
268 : /// Source location of the failing point, set only on failure.
269 : std::source_location loc = {};
270 :
271 : /// Exception captured by @ref fail, or null if none.
272 : std::exception_ptr ep = nullptr;
273 :
274 : /// True if the test completed without a failure.
275 : bool success = true;
276 :
277 : /// Return @ref success.
278 64 : constexpr explicit operator bool() const noexcept
279 : {
280 64 : return success;
281 : }
282 : };
283 :
284 : /** Construct a fuse with a custom error code.
285 :
286 : @par Example
287 :
288 : @code
289 : auto custom_ec = make_error_code(
290 : std::errc::operation_canceled);
291 : fuse f(custom_ec);
292 :
293 : std::error_code captured_ec;
294 : auto r = f([&](fuse& f) {
295 : auto ec = f.maybe_fail();
296 : if(ec)
297 : {
298 : captured_ec = ec;
299 : return;
300 : }
301 : });
302 :
303 : assert(captured_ec == custom_ec);
304 : @endcode
305 :
306 : @param ec The error code to deliver at failure points.
307 : */
308 453 : explicit fuse(std::error_code ec)
309 453 : : p_(std::make_shared<state>())
310 : {
311 453 : p_->ec = ec;
312 453 : }
313 :
314 : /** Construct a fuse with the default error code.
315 :
316 : The default error code is `error::test_failure`.
317 :
318 : @par Example
319 :
320 : @code
321 : fuse f;
322 : std::error_code captured_ec;
323 :
324 : auto r = f([&](fuse& f) {
325 : auto ec = f.maybe_fail();
326 : if(ec)
327 : {
328 : captured_ec = ec;
329 : return;
330 : }
331 : });
332 :
333 : assert(captured_ec == error::test_failure);
334 : @endcode
335 : */
336 451 : fuse()
337 451 : : fuse(error::test_failure)
338 : {
339 451 : }
340 :
341 : /** Return an error or throw at the current failure point.
342 :
343 : When running under @ref armed, increments the internal
344 : counter. When the counter reaches the current failure
345 : point, returns the stored error code (or throws
346 : `std::system_error` in exception mode) and records
347 : the source location.
348 :
349 : When called outside of @ref armed or @ref inert (standalone
350 : usage), or when running under @ref inert, always returns
351 : an empty error code. This enables dependency injection
352 : where the fuse is a no-op in production code.
353 :
354 : @par Example
355 :
356 : @code
357 : fuse f;
358 : auto r = f([](fuse& f) {
359 : // Error code mode: returns the error
360 : auto ec = f.maybe_fail();
361 : if(ec)
362 : return;
363 :
364 : // Exception mode: throws system_error
365 : ec = f.maybe_fail();
366 : if(ec)
367 : return;
368 : });
369 : @endcode
370 :
371 : @par Standalone Usage
372 :
373 : @code
374 : fuse f;
375 : auto ec = f.maybe_fail(); // Always returns {} (no-op)
376 : @endcode
377 :
378 : @param loc The source location of the call site,
379 : captured automatically.
380 :
381 : @return The stored error code if at the failure point,
382 : otherwise an empty error code. In exception mode,
383 : throws instead of returning an error. When called
384 : outside @ref armed, or when running under @ref inert,
385 : always returns an empty error code.
386 :
387 : @throws std::system_error When in exception mode
388 : and at the failure point (not thrown outside @ref armed).
389 : */
390 : std::error_code
391 6435 : maybe_fail(
392 : std::source_location loc = std::source_location::current())
393 : {
394 6435 : auto& s = *p_;
395 6435 : if(s.inert)
396 233 : return {};
397 6202 : if(s.i < s.n)
398 5470 : ++s.i;
399 6202 : if(s.i == s.n)
400 : {
401 2151 : s.triggered = true;
402 2151 : s.loc = loc;
403 2151 : if(s.throws)
404 1031 : throw std::system_error(s.ec);
405 1120 : return s.ec;
406 : }
407 4051 : return {};
408 : }
409 :
410 : /** Signal a test failure and stop execution.
411 :
412 : Call this from the test function to indicate a failure
413 : condition. Both @ref armed and @ref inert will return
414 : a failed @ref result immediately.
415 :
416 : @par Example
417 :
418 : @code
419 : fuse f;
420 : auto r = f([](fuse& f) {
421 : auto ec = f.maybe_fail();
422 : if(ec)
423 : return;
424 :
425 : // Explicit failure when a condition is not met
426 : if(some_value != expected)
427 : {
428 : f.fail();
429 : return;
430 : }
431 : });
432 :
433 : if(!r)
434 : {
435 : std::cerr << "Test failed at "
436 : << r.loc.file_name() << ":"
437 : << r.loc.line() << "\n";
438 : }
439 : @endcode
440 :
441 : @param loc The source location of the call site,
442 : captured automatically.
443 : */
444 : void
445 3 : fail(
446 : std::source_location loc =
447 : std::source_location::current()) noexcept
448 : {
449 3 : p_->loc = loc;
450 3 : p_->stopped = true;
451 3 : }
452 :
453 : /** Signal a test failure with an exception and stop execution.
454 :
455 : Call this from the test function to indicate a failure
456 : condition with an associated exception. Both @ref armed
457 : and @ref inert will return a failed @ref result with
458 : the captured exception pointer.
459 :
460 : @par Example
461 :
462 : @code
463 : fuse f;
464 : auto r = f([](fuse& f) {
465 : try
466 : {
467 : do_something();
468 : }
469 : catch(...)
470 : {
471 : f.fail(std::current_exception());
472 : return;
473 : }
474 : });
475 :
476 : if(!r)
477 : {
478 : try
479 : {
480 : if(r.ep)
481 : std::rethrow_exception(r.ep);
482 : }
483 : catch(std::exception const& e)
484 : {
485 : std::cerr << "Exception: " << e.what() << "\n";
486 : }
487 : }
488 : @endcode
489 :
490 : @param ep The exception pointer to capture.
491 :
492 : @param loc The source location of the call site,
493 : captured automatically.
494 : */
495 : void
496 2 : fail(
497 : std::exception_ptr ep,
498 : std::source_location loc =
499 : std::source_location::current()) noexcept
500 : {
501 2 : p_->ep = ep;
502 2 : p_->loc = loc;
503 2 : p_->stopped = true;
504 2 : }
505 :
506 : /** Run a test function with systematic failure injection.
507 :
508 : Repeatedly invokes the provided function, failing at
509 : successive points until the function completes without
510 : encountering a failure. First runs the complete loop
511 : using error codes, then runs using exceptions.
512 :
513 : @par Example
514 :
515 : @code
516 : fuse f;
517 : auto r = f.armed([](fuse& f) {
518 : auto ec = f.maybe_fail();
519 : if(ec)
520 : return;
521 :
522 : ec = f.maybe_fail();
523 : if(ec)
524 : return;
525 : });
526 :
527 : if(!r)
528 : {
529 : std::cerr << "Failure at "
530 : << r.loc.file_name() << ":"
531 : << r.loc.line() << "\n";
532 : }
533 : @endcode
534 :
535 : @param fn The test function to invoke. It receives
536 : a reference to the fuse and should call @ref maybe_fail
537 : at each potential failure point.
538 :
539 : @return A @ref result indicating success or failure.
540 : On failure, `result::loc` contains the source location
541 : of the last @ref maybe_fail or @ref fail call.
542 : */
543 : template<class F>
544 : result
545 32 : armed(F&& fn)
546 : {
547 32 : result r;
548 :
549 : // Phase 1: error code mode
550 32 : p_->throws = false;
551 32 : p_->inert = false;
552 32 : p_->n = (std::numeric_limits<std::size_t>::max)();
553 97 : while(*this)
554 : {
555 : try
556 : {
557 71 : fn(*this);
558 : }
559 6 : catch(...)
560 : {
561 3 : r.success = false;
562 3 : r.loc = p_->loc;
563 3 : r.ep = p_->ep;
564 3 : p_->inert = true;
565 3 : return r;
566 : }
567 68 : if(p_->stopped)
568 : {
569 3 : r.success = false;
570 3 : r.loc = p_->loc;
571 3 : r.ep = p_->ep;
572 3 : p_->inert = true;
573 3 : return r;
574 : }
575 : }
576 :
577 : // Phase 2: exception mode
578 26 : p_->throws = true;
579 26 : p_->n = (std::numeric_limits<std::size_t>::max)();
580 26 : p_->i = 0;
581 26 : p_->triggered = false;
582 80 : while(*this)
583 : {
584 : try
585 : {
586 54 : fn(*this);
587 : }
588 56 : catch(std::system_error const& ex)
589 : {
590 28 : if(ex.code() != p_->ec)
591 : {
592 MIS 0 : r.success = false;
593 0 : r.loc = p_->loc;
594 0 : r.ep = p_->ep;
595 0 : p_->inert = true;
596 0 : return r;
597 : }
598 : }
599 0 : catch(...)
600 : {
601 0 : r.success = false;
602 0 : r.loc = p_->loc;
603 0 : r.ep = p_->ep;
604 0 : p_->inert = true;
605 0 : return r;
606 : }
607 HIT 54 : if(p_->stopped)
608 : {
609 MIS 0 : r.success = false;
610 0 : r.loc = p_->loc;
611 0 : r.ep = p_->ep;
612 0 : p_->inert = true;
613 0 : return r;
614 : }
615 : }
616 HIT 26 : p_->inert = true;
617 26 : return r;
618 MIS 0 : }
619 :
620 : /** Run a coroutine test function with systematic failure injection.
621 :
622 : Repeatedly invokes the provided coroutine function, failing at
623 : successive points until the function completes without
624 : encountering a failure. First runs the complete loop
625 : using error codes, then runs using exceptions.
626 :
627 : This overload handles lambdas that return an @ref IoRunnable
628 : (such as `task<void>`), executing them synchronously via
629 : @ref run_blocking.
630 :
631 : @par Example
632 :
633 : @code
634 : fuse f;
635 : auto r = f.armed([&](fuse&) -> task<void> {
636 : auto ec = f.maybe_fail();
637 : if(ec)
638 : co_return;
639 :
640 : ec = f.maybe_fail();
641 : if(ec)
642 : co_return;
643 : });
644 :
645 : if(!r)
646 : {
647 : std::cerr << "Failure at "
648 : << r.loc.file_name() << ":"
649 : << r.loc.line() << "\n";
650 : }
651 : @endcode
652 :
653 : @param fn The coroutine test function to invoke. It receives
654 : a reference to the fuse and should call @ref maybe_fail
655 : at each potential failure point.
656 :
657 : @return A @ref result indicating success or failure.
658 : On failure, `result::loc` contains the source location
659 : of the last @ref maybe_fail or @ref fail call.
660 : */
661 : template<class F>
662 : requires IoRunnable<std::invoke_result_t<F, fuse&>>
663 : result
664 HIT 342 : armed(F&& fn)
665 : {
666 342 : result r;
667 :
668 : // Phase 1: error code mode
669 342 : p_->throws = false;
670 342 : p_->inert = false;
671 342 : p_->n = (std::numeric_limits<std::size_t>::max)();
672 1687 : while(*this)
673 : {
674 : try
675 : {
676 1345 : run_blocking()(fn(*this));
677 : }
678 MIS 0 : catch(...)
679 : {
680 0 : r.success = false;
681 0 : r.loc = p_->loc;
682 0 : r.ep = p_->ep;
683 0 : p_->inert = true;
684 0 : return r;
685 : }
686 HIT 1345 : if(p_->stopped)
687 : {
688 MIS 0 : r.success = false;
689 0 : r.loc = p_->loc;
690 0 : r.ep = p_->ep;
691 0 : p_->inert = true;
692 0 : return r;
693 : }
694 : }
695 :
696 : // Phase 2: exception mode
697 HIT 342 : p_->throws = true;
698 342 : p_->n = (std::numeric_limits<std::size_t>::max)();
699 342 : p_->i = 0;
700 342 : p_->triggered = false;
701 1687 : while(*this)
702 : {
703 : try
704 : {
705 3351 : run_blocking()(fn(*this));
706 : }
707 2006 : catch(std::system_error const& ex)
708 : {
709 1003 : if(ex.code() != p_->ec)
710 : {
711 MIS 0 : r.success = false;
712 0 : r.loc = p_->loc;
713 0 : r.ep = p_->ep;
714 0 : p_->inert = true;
715 0 : return r;
716 : }
717 : }
718 0 : catch(...)
719 : {
720 0 : r.success = false;
721 0 : r.loc = p_->loc;
722 0 : r.ep = p_->ep;
723 0 : p_->inert = true;
724 0 : return r;
725 : }
726 HIT 1345 : if(p_->stopped)
727 : {
728 MIS 0 : r.success = false;
729 0 : r.loc = p_->loc;
730 0 : r.ep = p_->ep;
731 0 : p_->inert = true;
732 0 : return r;
733 : }
734 : }
735 HIT 342 : p_->inert = true;
736 342 : return r;
737 MIS 0 : }
738 :
739 : /** Alias for @ref armed.
740 :
741 : Allows the fuse to be invoked directly as a function
742 : object for more concise syntax.
743 :
744 : @par Example
745 :
746 : @code
747 : // These are equivalent:
748 : fuse f;
749 : auto r1 = f.armed([](fuse& f) { ... });
750 : auto r2 = f([](fuse& f) { ... });
751 :
752 : // Inline usage:
753 : auto r3 = fuse()([](fuse& f) {
754 : auto ec = f.maybe_fail();
755 : if(ec)
756 : return;
757 : });
758 : @endcode
759 :
760 : @see armed
761 : */
762 : template<class F>
763 : result
764 HIT 15 : operator()(F&& fn)
765 : {
766 15 : return armed(std::forward<F>(fn));
767 : }
768 :
769 : /** Alias for @ref armed (coroutine overload).
770 :
771 : @see armed
772 : */
773 : template<class F>
774 : requires IoRunnable<std::invoke_result_t<F, fuse&>>
775 : result
776 : operator()(F&& fn)
777 : {
778 : return armed(std::forward<F>(fn));
779 : }
780 :
781 : /** Run a test function once without failure injection.
782 :
783 : Invokes the provided function exactly once. Calls to
784 : @ref maybe_fail always return an empty error code and
785 : never throw. Only explicit calls to @ref fail can
786 : signal a test failure.
787 :
788 : This is useful for running tests where you want to
789 : manually control failures, or for quick single-run
790 : tests without systematic error injection.
791 :
792 : @par Example
793 :
794 : @code
795 : fuse f;
796 : auto r = f.inert([](fuse& f) {
797 : auto ec = f.maybe_fail(); // Always succeeds
798 : assert(!ec);
799 :
800 : // Only way to signal failure:
801 : if(some_condition)
802 : {
803 : f.fail();
804 : return;
805 : }
806 : });
807 :
808 : if(!r)
809 : {
810 : std::cerr << "Test failed at "
811 : << r.loc.file_name() << ":"
812 : << r.loc.line() << "\n";
813 : }
814 : @endcode
815 :
816 : @param fn The test function to invoke. It receives
817 : a reference to the fuse. Calls to @ref maybe_fail
818 : will always succeed.
819 :
820 : @return A @ref result indicating success or failure.
821 : On failure, `result::loc` contains the source location
822 : of the @ref fail call.
823 : */
824 : template<class F>
825 : result
826 8 : inert(F&& fn)
827 : {
828 8 : result r;
829 8 : p_->inert = true;
830 : try
831 : {
832 8 : fn(*this);
833 : }
834 2 : catch(...)
835 : {
836 1 : r.success = false;
837 1 : r.loc = p_->loc;
838 1 : r.ep = std::current_exception();
839 1 : return r;
840 : }
841 7 : if(p_->stopped)
842 : {
843 2 : r.success = false;
844 2 : r.loc = p_->loc;
845 2 : r.ep = p_->ep;
846 : }
847 7 : return r;
848 MIS 0 : }
849 :
850 : /** Run a coroutine test function once without failure injection.
851 :
852 : Invokes the provided coroutine function exactly once using
853 : @ref run_blocking. Calls to @ref maybe_fail always return
854 : an empty error code and never throw. Only explicit calls
855 : to @ref fail can signal a test failure.
856 :
857 : @par Example
858 :
859 : @code
860 : fuse f;
861 : auto r = f.inert([](fuse& f) -> task<void> {
862 : auto ec = f.maybe_fail(); // Always succeeds
863 : assert(!ec);
864 :
865 : // Only way to signal failure:
866 : if(some_condition)
867 : {
868 : f.fail();
869 : co_return;
870 : }
871 : });
872 :
873 : if(!r)
874 : {
875 : std::cerr << "Test failed at "
876 : << r.loc.file_name() << ":"
877 : << r.loc.line() << "\n";
878 : }
879 : @endcode
880 :
881 : @param fn The coroutine test function to invoke. It receives
882 : a reference to the fuse. Calls to @ref maybe_fail
883 : will always succeed.
884 :
885 : @return A @ref result indicating success or failure.
886 : On failure, `result::loc` contains the source location
887 : of the @ref fail call.
888 : */
889 : template<class F>
890 : requires IoRunnable<std::invoke_result_t<F, fuse&>>
891 : result
892 HIT 22 : inert(F&& fn)
893 : {
894 22 : result r;
895 22 : p_->inert = true;
896 : try
897 : {
898 22 : run_blocking()(fn(*this));
899 : }
900 MIS 0 : catch(...)
901 : {
902 0 : r.success = false;
903 0 : r.loc = p_->loc;
904 0 : r.ep = std::current_exception();
905 0 : return r;
906 : }
907 HIT 22 : if(p_->stopped)
908 : {
909 MIS 0 : r.success = false;
910 0 : r.loc = p_->loc;
911 0 : r.ep = p_->ep;
912 : }
913 HIT 22 : return r;
914 MIS 0 : }
915 : };
916 :
917 : } // test
918 : } // capy
919 : } // boost
920 :
921 : #endif
|