1/*
2 * Distributed under the Boost Software License, Version 1.0.
3 * (See accompanying file LICENSE_1_0.txt or copy at
4 * https://www.boost.org/LICENSE_1_0.txt)
5 *
6 * Copyright (c) 2023 Andrey Semashev
7 */
8/*!
9 * \file scope_exit.cpp
10 * \author Andrey Semashev
11 *
12 * \brief This file contains tests for \c scope_exit.
13 */
14
15#include <boost/scope/scope_exit.hpp>
16#include <boost/scope/exception_checker.hpp>
17#include <boost/scope/error_code_checker.hpp>
18#include <boost/core/lightweight_test.hpp>
19#include <boost/core/lightweight_test_trait.hpp>
20#include <boost/config.hpp>
21#include <utility>
22#include <stdexcept>
23#include <system_error>
24#include "function_types.hpp"
25
26#if defined(_MSC_VER) && !defined(__clang__)
27// warning C4702: unreachable code
28// This warning is triggered by tests that unconditionally throw exception at some point
29// and have code after that (e.g. parts of scope guard constructor and a check that verifies
30// that the following code is not reached).
31#pragma warning(disable: 4702)
32#endif
33
34int g_n = 0, g_c = 0;
35
36void check_normal_default_cond()
37{
38 int n = 0;
39 {
40 boost::scope::scope_exit< normal_func > guard{ normal_func(n) };
41 BOOST_TEST(guard.active());
42 }
43 BOOST_TEST_EQ(n, 1);
44
45 n = 0;
46 {
47 boost::scope::scope_exit< moveable_only_func > guard{ moveable_only_func(n) };
48 BOOST_TEST(guard.active());
49 guard.set_active(false);
50 BOOST_TEST(!guard.active());
51 guard.set_active(true);
52 BOOST_TEST(guard.active());
53 }
54 BOOST_TEST_EQ(n, 1);
55
56 n = 0;
57 {
58 boost::scope::scope_exit< normal_func > guard(normal_func(n), false);
59 BOOST_TEST(!guard.active());
60 }
61 BOOST_TEST_EQ(n, 0);
62
63 n = 0;
64 {
65 boost::scope::scope_exit< normal_func > guard(normal_func(n), false);
66 BOOST_TEST(!guard.active());
67 guard.set_active(true);
68 BOOST_TEST(guard.active());
69 }
70 BOOST_TEST_EQ(n, 1);
71
72 n = 0;
73 {
74 boost::scope::scope_exit< moveable_only_func > guard1{ moveable_only_func(n) };
75 BOOST_TEST(guard1.active());
76 boost::scope::scope_exit< moveable_only_func > guard2 = std::move(guard1);
77 BOOST_TEST(!guard1.active());
78 BOOST_TEST(guard2.active());
79 boost::scope::scope_exit< moveable_only_func > guard3 = std::move(guard1);
80 BOOST_TEST(!guard3.active());
81 }
82 BOOST_TEST_EQ(n, 1);
83
84 n = 0;
85 {
86 normal_func func(n);
87 boost::scope::scope_exit< normal_func& > guard(func);
88 BOOST_TEST(guard.active());
89 }
90 BOOST_TEST_EQ(n, 1);
91
92 n = 0;
93 {
94 normal_func func(n);
95 boost::scope::scope_exit< normal_func& > guard1(func);
96 BOOST_TEST(guard1.active());
97 boost::scope::scope_exit< normal_func& > guard2 = std::move(guard1);
98 BOOST_TEST(!guard1.active());
99 BOOST_TEST(guard2.active());
100 boost::scope::scope_exit< normal_func& > guard3 = std::move(guard1);
101 BOOST_TEST(!guard3.active());
102 }
103 BOOST_TEST_EQ(n, 1);
104
105 struct local
106 {
107 static void raw_func()
108 {
109 ++g_n;
110 }
111 };
112
113 g_n = 0;
114 {
115 boost::scope::scope_exit< void (&)() > guard(local::raw_func);
116 BOOST_TEST(guard.active());
117 }
118 BOOST_TEST_EQ(g_n, 1);
119
120 g_n = 0;
121 {
122 boost::scope::scope_exit< void (&)() > guard1(local::raw_func);
123 BOOST_TEST(guard1.active());
124 boost::scope::scope_exit< void (&)() > guard2 = std::move(guard1);
125 BOOST_TEST(!guard1.active());
126 BOOST_TEST(guard2.active());
127 boost::scope::scope_exit< void (&)() > guard3 = std::move(guard1);
128 BOOST_TEST(!guard3.active());
129 }
130 BOOST_TEST_EQ(g_n, 1);
131}
132
133struct always_true
134{
135 bool operator()() const noexcept
136 {
137 return true;
138 }
139};
140
141struct always_false
142{
143 bool operator()() const noexcept
144 {
145 return false;
146 }
147};
148
149void check_normal()
150{
151 int n = 0;
152 {
153 boost::scope::scope_exit< normal_func, always_true > guard{ normal_func(n), always_true() };
154 BOOST_TEST(guard.active());
155 }
156 BOOST_TEST_EQ(n, 1);
157
158 n = 0;
159 {
160 boost::scope::scope_exit< normal_func, always_false > guard{ normal_func(n), always_false() };
161 BOOST_TEST(guard.active());
162 }
163 BOOST_TEST_EQ(n, 0);
164
165 n = 0;
166 {
167 boost::scope::scope_exit< moveable_only_func, always_true > guard{ moveable_only_func(n) };
168 BOOST_TEST(guard.active());
169 guard.set_active(false);
170 BOOST_TEST(!guard.active());
171 guard.set_active(true);
172 BOOST_TEST(guard.active());
173 }
174 BOOST_TEST_EQ(n, 1);
175
176 n = 0;
177 {
178 boost::scope::scope_exit< normal_func, always_true > guard(normal_func(n), always_true(), false);
179 BOOST_TEST(!guard.active());
180 }
181 BOOST_TEST_EQ(n, 0);
182
183 n = 0;
184 {
185 boost::scope::scope_exit< normal_func, always_true > guard(normal_func(n), false);
186 BOOST_TEST(!guard.active());
187 }
188 BOOST_TEST_EQ(n, 0);
189
190 n = 0;
191 {
192 boost::scope::scope_exit< normal_func, always_true > guard(normal_func(n), false);
193 BOOST_TEST(!guard.active());
194 guard.set_active(true);
195 BOOST_TEST(guard.active());
196 }
197 BOOST_TEST_EQ(n, 1);
198
199 n = 0;
200 {
201 boost::scope::scope_exit< moveable_only_func, always_true > guard1{ moveable_only_func(n) };
202 BOOST_TEST(guard1.active());
203 boost::scope::scope_exit< moveable_only_func, always_true > guard2 = std::move(guard1);
204 BOOST_TEST(!guard1.active());
205 BOOST_TEST(guard2.active());
206 boost::scope::scope_exit< moveable_only_func, always_true > guard3 = std::move(guard1);
207 BOOST_TEST(!guard3.active());
208 }
209 BOOST_TEST_EQ(n, 1);
210
211 n = 0;
212 {
213 normal_func func(n);
214 always_true cond;
215 boost::scope::scope_exit< normal_func&, always_true& > guard(func, cond);
216 BOOST_TEST(guard.active());
217 }
218 BOOST_TEST_EQ(n, 1);
219
220 n = 0;
221 {
222 normal_func func(n);
223 always_true cond;
224 boost::scope::scope_exit< normal_func&, always_true& > guard1(func, cond);
225 BOOST_TEST(guard1.active());
226 boost::scope::scope_exit< normal_func&, always_true& > guard2 = std::move(guard1);
227 BOOST_TEST(!guard1.active());
228 BOOST_TEST(guard2.active());
229 boost::scope::scope_exit< normal_func&, always_true& > guard3 = std::move(guard1);
230 BOOST_TEST(!guard3.active());
231 }
232 BOOST_TEST_EQ(n, 1);
233
234 struct local
235 {
236 static void raw_func()
237 {
238 ++g_n;
239 }
240
241 static bool raw_cond()
242 {
243 ++g_c;
244 return true;
245 }
246 };
247
248 g_n = 0;
249 g_c = 0;
250 {
251 boost::scope::scope_exit< void (&)(), bool (&)() > guard(local::raw_func, local::raw_cond);
252 BOOST_TEST(guard.active());
253 }
254 BOOST_TEST_EQ(g_c, 1);
255 BOOST_TEST_EQ(g_n, 1);
256
257 g_n = 0;
258 g_c = 0;
259 {
260 boost::scope::scope_exit< void (&)(), bool (&)() > guard1(local::raw_func, local::raw_cond);
261 BOOST_TEST(guard1.active());
262 boost::scope::scope_exit< void (&)(), bool (&)() > guard2 = std::move(guard1);
263 BOOST_TEST(!guard1.active());
264 BOOST_TEST(guard2.active());
265 boost::scope::scope_exit< void (&)(), bool (&)() > guard3 = std::move(guard1);
266 BOOST_TEST(!guard3.active());
267 }
268 BOOST_TEST_EQ(g_c, 1);
269 BOOST_TEST_EQ(g_n, 1);
270}
271
272void check_throw_default_cond()
273{
274 int n = 0;
275 try
276 {
277 boost::scope::scope_exit< normal_func > guard{ normal_func(n) };
278 BOOST_TEST(guard.active());
279 throw std::runtime_error("error");
280 }
281 catch (...) {}
282 BOOST_TEST_EQ(n, 1);
283
284 n = 0;
285 try
286 {
287 boost::scope::scope_exit< throw_on_copy_func > guard{ throw_on_copy_func(n) };
288 BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_func");
289 }
290 catch (...) {}
291 BOOST_TEST_EQ(n, 1);
292
293 n = 0;
294 try
295 {
296 boost::scope::scope_exit< throw_on_move_func > guard{ throw_on_move_func(n) };
297 }
298 catch (...)
299 {
300 BOOST_ERROR("An exception is not expected to be thrown by throw_on_move_func (copy ctor should be used)");
301 }
302 BOOST_TEST_EQ(n, 1);
303
304 n = 0;
305 bool scope_ended = false, exception_thrown = false, func_destroyed = false;
306 try
307 {
308 boost::scope::scope_exit< throw_on_call_func > guard{ throw_on_call_func(n, func_destroyed) };
309 func_destroyed = false;
310 scope_ended = true;
311 }
312 catch (...)
313 {
314 exception_thrown = true;
315 }
316 BOOST_TEST_EQ(n, 1);
317 BOOST_TEST(scope_ended);
318 BOOST_TEST(exception_thrown);
319 BOOST_TEST(func_destroyed);
320}
321
322void check_throw()
323{
324 int n = 0;
325 try
326 {
327 boost::scope::scope_exit< throw_on_copy_func, boost::scope::exception_checker > guard{ throw_on_copy_func(n) };
328 BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_func");
329 }
330 catch (...) {}
331 BOOST_TEST_EQ(n, 1);
332
333 n = 0;
334 try
335 {
336 boost::scope::scope_exit< throw_on_move_func, boost::scope::exception_checker > guard{ throw_on_move_func(n) };
337 }
338 catch (...)
339 {
340 BOOST_ERROR("An exception is not expected to be thrown by throw_on_move_func (copy ctor should be used)");
341 }
342 BOOST_TEST_EQ(n, 0);
343
344 n = 0;
345 bool scope_ended = false, exception_thrown = false, func_destroyed = false;
346 try
347 {
348 boost::scope::scope_exit< throw_on_call_func, boost::scope::exception_checker > guard{ throw_on_call_func(n, func_destroyed) };
349 func_destroyed = false;
350 scope_ended = true;
351 }
352 catch (...)
353 {
354 exception_thrown = true;
355 }
356 BOOST_TEST_EQ(n, 0);
357 BOOST_TEST(scope_ended);
358 BOOST_TEST(!exception_thrown);
359 BOOST_TEST(func_destroyed);
360}
361
362void check_cond()
363{
364 int n = 0;
365 {
366 int err = 0;
367 boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(ec&: err) };
368 BOOST_TEST(guard.active());
369 err = -1;
370 }
371 BOOST_TEST_EQ(n, 1);
372
373 n = 0;
374 {
375 int err = 0;
376 boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(ec&: err) };
377 BOOST_TEST(guard.active());
378 }
379 BOOST_TEST_EQ(n, 0);
380
381 n = 0;
382 {
383 int err = 0;
384 boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(ec&: err), false };
385 BOOST_TEST(!guard.active());
386 err = -1;
387 }
388 BOOST_TEST_EQ(n, 0);
389
390 n = 0;
391 {
392 std::error_code err{};
393 boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(ec&: err) };
394 BOOST_TEST(guard.active());
395 err = std::make_error_code(e: std::errc::invalid_argument);
396 }
397 BOOST_TEST_EQ(n, 1);
398
399 n = 0;
400 {
401 std::error_code err{};
402 boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(ec&: err) };
403 BOOST_TEST(guard.active());
404 }
405 BOOST_TEST_EQ(n, 0);
406
407 n = 0;
408 try
409 {
410 int err = 0;
411 boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(ec&: err) };
412 BOOST_TEST(guard.active());
413 throw std::runtime_error("error");
414 }
415 catch (...) {}
416 BOOST_TEST_EQ(n, 0); // exception is not the failure condition, err was still 0 when the scope guard was destroyed
417}
418
419void check_deduction()
420{
421 int n = 0;
422 {
423 auto guard = boost::scope::make_scope_exit(func: normal_func(n));
424 BOOST_TEST(guard.active());
425 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func >);
426 }
427 BOOST_TEST_EQ(n, 1);
428
429 n = 0;
430 {
431 auto guard = boost::scope::make_scope_exit(func: normal_func(n), active: false);
432 BOOST_TEST(!guard.active());
433 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func >);
434 }
435 BOOST_TEST_EQ(n, 0);
436
437 n = 0;
438 {
439 const normal_func func{ n };
440 auto guard = boost::scope::make_scope_exit(func, active: true);
441 BOOST_TEST(guard.active());
442 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func >);
443 }
444 BOOST_TEST_EQ(n, 1);
445
446 n = 0;
447 {
448 int err = 0;
449 auto guard = boost::scope::make_scope_exit(func: normal_func(n), cond: boost::scope::check_error_code(ec&: err));
450 BOOST_TEST(guard.active());
451 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >);
452 err = -1;
453 }
454 BOOST_TEST_EQ(n, 1);
455
456 n = 0;
457 {
458 int err = 0;
459 auto guard = boost::scope::make_scope_exit(func: normal_func(n), cond: boost::scope::check_error_code(ec&: err), active: false);
460 BOOST_TEST(!guard.active());
461 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >);
462 err = -1;
463 }
464 BOOST_TEST_EQ(n, 0);
465
466 n = 0;
467 {
468 int err = 0;
469 const normal_func func{ n };
470 const auto cond = boost::scope::check_error_code(ec&: err);
471 auto guard = boost::scope::make_scope_exit(func, cond, active: true);
472 BOOST_TEST(guard.active());
473 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >);
474 err = -1;
475 }
476 BOOST_TEST_EQ(n, 1);
477
478 struct local
479 {
480 static void raw_func()
481 {
482 ++g_n;
483 }
484
485 static bool raw_cond()
486 {
487 ++g_c;
488 return true;
489 }
490
491#if !defined(BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES)
492 static void raw_func_noexcept() noexcept
493 {
494 ++g_n;
495 }
496
497 static bool raw_cond_noexcept() noexcept
498 {
499 ++g_c;
500 return true;
501 }
502#endif
503 };
504
505 g_n = 0;
506 g_c = 0;
507 {
508 auto guard = boost::scope::make_scope_exit(func&: local::raw_func, cond&: local::raw_cond);
509 BOOST_TEST(guard.active());
510 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< void (*)(), bool (*)() >);
511 }
512 BOOST_TEST_EQ(g_n, 1);
513 BOOST_TEST_EQ(g_c, 1);
514
515#if !defined(BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES)
516 g_n = 0;
517 g_c = 0;
518 {
519 auto guard = boost::scope::make_scope_exit(func&: local::raw_func_noexcept, cond&: local::raw_cond_noexcept);
520 BOOST_TEST(guard.active());
521 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< void (*)() noexcept, bool (*)() noexcept >);
522 }
523 BOOST_TEST_EQ(g_n, 1);
524 BOOST_TEST_EQ(g_c, 1);
525#endif
526
527#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
528 n = 0;
529 {
530 boost::scope::scope_exit guard{ normal_func(n) };
531 BOOST_TEST(guard.active());
532 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func >);
533 }
534 BOOST_TEST_EQ(n, 1);
535
536 n = 0;
537 {
538 boost::scope::scope_exit guard{ normal_func(n), true };
539 BOOST_TEST(guard.active());
540 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func >);
541 }
542 BOOST_TEST_EQ(n, 1);
543
544 n = 0;
545 {
546 boost::scope::scope_exit guard([&n] { ++n; });
547 BOOST_TEST(guard.active());
548 }
549 BOOST_TEST_EQ(n, 1);
550
551 n = 0;
552 {
553 boost::scope::scope_exit guard1{ normal_func(n) };
554 boost::scope::scope_exit guard2 = std::move(guard1);
555 BOOST_TEST(guard2.active());
556 BOOST_TEST_TRAIT_SAME(decltype(guard2), boost::scope::scope_exit< normal_func >);
557 }
558 BOOST_TEST_EQ(n, 1);
559
560 n = 0;
561 {
562 int err = 0;
563 boost::scope::scope_exit guard{ normal_func(n), boost::scope::check_error_code(ec&: err) };
564 BOOST_TEST(guard.active());
565 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >);
566 err = -1;
567 }
568 BOOST_TEST_EQ(n, 1);
569
570 n = 0;
571 {
572 int err = 0;
573 boost::scope::scope_exit guard{ normal_func(n), boost::scope::error_code_checker(err), false };
574 BOOST_TEST(!guard.active());
575 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< normal_func, boost::scope::error_code_checker< int > >);
576 err = -1;
577 }
578 BOOST_TEST_EQ(n, 0);
579
580 g_n = 0;
581 g_c = 0;
582 {
583 boost::scope::scope_exit guard{ local::raw_func, local::raw_cond };
584 BOOST_TEST(guard.active());
585 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< void (*)(), bool (*)() >);
586 }
587 BOOST_TEST_EQ(g_n, 1);
588 BOOST_TEST_EQ(g_c, 1);
589
590#if !defined(BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES)
591 g_n = 0;
592 g_c = 0;
593 {
594 boost::scope::scope_exit guard{ local::raw_func_noexcept, local::raw_cond_noexcept };
595 BOOST_TEST(guard.active());
596 BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_exit< void (*)() noexcept, bool (*)() noexcept >);
597 }
598 BOOST_TEST_EQ(g_n, 1);
599 BOOST_TEST_EQ(g_c, 1);
600#endif
601
602 n = 0;
603 {
604 int err = -1;
605 boost::scope::scope_exit guard([&n] { ++n; }, [&err]() noexcept { return err < 0; });
606 BOOST_TEST(guard.active());
607 err = -1;
608 }
609 BOOST_TEST_EQ(n, 1);
610
611 n = 0;
612 {
613 boost::scope::scope_exit guard1{ normal_func(n), always_true() };
614 boost::scope::scope_exit guard2 = std::move(guard1);
615 BOOST_TEST(guard2.active());
616 BOOST_TEST_TRAIT_SAME(decltype(guard2), boost::scope::scope_exit< normal_func, always_true >);
617 }
618 BOOST_TEST_EQ(n, 1);
619#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
620}
621
622int main()
623{
624 check_normal_default_cond();
625 check_normal();
626 check_throw_default_cond();
627 check_throw();
628 check_deduction();
629
630 return boost::report_errors();
631}
632

source code of boost/libs/scope/test/run/scope_exit.cpp