2
0
mirror of https://github.com/boostorg/scope.git synced 2026-01-28 07:32:09 +00:00
Files
scope/test/run/scope_success.cpp
Andrey Semashev c5635f21cc Implemented scope_check and added support for arbitrary fail conditions.
The new scope_check scope guard is a generalization of scope_success and
scope_fail and allows specifying arbitrary conditions for executing the
scope exit action. The scope_success and scope_fail have been
reimplemented in terms of scope_check and now also support arbitrary
fail conditions.

Added exception_checker and error_code_checker conditions for testing
for an exception being thrown and for an error code, respectively.
scope_success and scope_fail use exception_checker by default.

Added tests for the new components.
2023-03-26 21:02:11 +03:00

384 lines
11 KiB
C++

/*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* https://www.boost.org/LICENSE_1_0.txt)
*
* Copyright (c) 2023 Andrey Semashev
*/
/*!
* \file scope_success.cpp
* \author Andrey Semashev
*
* \brief This file contains tests for \c scope_success.
*/
#include <boost/scope/scope_success.hpp>
#include <boost/scope/error_code_checker.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/config.hpp>
#include <utility>
#include <stdexcept>
#include <system_error>
#include "function_types.hpp"
int g_n = 0;
void check_normal()
{
int n = 0;
{
boost::scope::scope_success< normal_func > guard{ normal_func(n) };
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
boost::scope::scope_success< normal_func > guard{ normal_func(n) };
BOOST_TEST(guard.active());
guard.release();
BOOST_TEST(!guard.active());
}
BOOST_TEST_EQ(n, 0);
n = 0;
{
boost::scope::scope_success< moveable_only_func > guard{ moveable_only_func(n) };
BOOST_TEST(guard.active());
guard.set_active(false);
BOOST_TEST(!guard.active());
guard.set_active(true);
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
boost::scope::scope_success< normal_func > guard(normal_func(n), false);
BOOST_TEST(!guard.active());
}
BOOST_TEST_EQ(n, 0);
n = 0;
{
boost::scope::scope_success< normal_func > guard(normal_func(n), false);
BOOST_TEST(!guard.active());
guard.set_active(true);
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
boost::scope::scope_success< moveable_only_func > guard1{ moveable_only_func(n) };
BOOST_TEST(guard1.active());
boost::scope::scope_success< moveable_only_func > guard2 = std::move(guard1);
BOOST_TEST(!guard1.active());
BOOST_TEST(guard2.active());
boost::scope::scope_success< moveable_only_func > guard3 = std::move(guard1);
BOOST_TEST(!guard3.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
normal_func func(n);
boost::scope::scope_success< normal_func& > guard(func);
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
normal_func func(n);
boost::scope::scope_success< normal_func& > guard1(func);
BOOST_TEST(guard1.active());
boost::scope::scope_success< normal_func& > guard2 = std::move(guard1);
BOOST_TEST(!guard1.active());
BOOST_TEST(guard2.active());
boost::scope::scope_success< normal_func& > guard3 = std::move(guard1);
BOOST_TEST(!guard3.active());
}
BOOST_TEST_EQ(n, 1);
struct local
{
static void raw_func()
{
++g_n;
}
};
g_n = 0;
{
boost::scope::scope_success< void (&)() > guard(local::raw_func);
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(g_n, 1);
g_n = 0;
{
boost::scope::scope_success< void (&)() > guard1(local::raw_func);
BOOST_TEST(guard1.active());
boost::scope::scope_success< void (&)() > guard2 = std::move(guard1);
BOOST_TEST(!guard1.active());
BOOST_TEST(guard2.active());
boost::scope::scope_success< void (&)() > guard3 = std::move(guard1);
BOOST_TEST(!guard3.active());
}
BOOST_TEST_EQ(g_n, 1);
}
void check_throw()
{
int n = 0;
try
{
boost::scope::scope_success< normal_func > guard{ normal_func(n) };
BOOST_TEST(guard.active());
throw std::runtime_error("error");
}
catch (...) {}
BOOST_TEST_EQ(n, 0);
n = 0;
try
{
boost::scope::scope_success< normal_func > guard1{ normal_func(n) };
BOOST_TEST(guard1.active());
throw std::runtime_error("error");
}
catch (...)
{
BOOST_TEST_EQ(n, 0);
boost::scope::scope_success< normal_func > guard2{ normal_func(n) };
BOOST_TEST(guard2.active());
}
BOOST_TEST_EQ(n, 1);
#if !defined(BOOST_CORE_UNCAUGHT_EXCEPTIONS_EMULATED)
n = 0;
try
{
boost::scope::scope_success< normal_func > guard1{ normal_func(n) };
BOOST_TEST(guard1.active());
throw std::runtime_error("error 1");
}
catch (...)
{
BOOST_TEST_EQ(n, 0);
try
{
boost::scope::scope_success< normal_func > guard2{ normal_func(n) };
BOOST_TEST(guard2.active());
throw std::runtime_error("error 2");
}
catch (...) {}
}
BOOST_TEST_EQ(n, 0);
#endif // !defined(BOOST_CORE_UNCAUGHT_EXCEPTIONS_EMULATED)
n = 0;
try
{
boost::scope::scope_success< throw_on_copy_func > guard{ throw_on_copy_func(n) };
BOOST_ERROR("An exception is expected to be thrown by throw_on_copy_func");
}
catch (...) {}
BOOST_TEST_EQ(n, 0);
n = 0;
try
{
boost::scope::scope_success< throw_on_move_func > guard{ throw_on_move_func(n) };
}
catch (...)
{
BOOST_ERROR("An exception is not expected to be thrown by throw_on_move_func (copy ctor should be used)");
}
BOOST_TEST_EQ(n, 1);
n = 0;
bool scope_ended = false, exception_thrown = false, func_destroyed = false;
try
{
boost::scope::scope_success< throw_on_call_func > guard{ throw_on_call_func(n, func_destroyed) };
func_destroyed = false;
scope_ended = true;
}
catch (...)
{
exception_thrown = true;
}
BOOST_TEST_EQ(n, 1);
BOOST_TEST(scope_ended);
BOOST_TEST(exception_thrown);
BOOST_TEST(func_destroyed);
}
void check_cond()
{
int n = 0;
{
int err = 0;
boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) };
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
int err = 0;
boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) };
BOOST_TEST(guard.active());
err = -1;
}
BOOST_TEST_EQ(n, 0);
n = 0;
{
int err = 0;
boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err), false };
BOOST_TEST(!guard.active());
}
BOOST_TEST_EQ(n, 0);
n = 0;
{
std::error_code err{};
boost::scope::scope_success< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(err) };
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
std::error_code err{};
boost::scope::scope_success< normal_func, boost::scope::error_code_checker< std::error_code > > guard{ normal_func(n), boost::scope::check_error_code(err) };
BOOST_TEST(guard.active());
err = std::make_error_code(std::errc::invalid_argument);
}
BOOST_TEST_EQ(n, 0);
n = 0;
try
{
int err = 0;
boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > > guard{ normal_func(n), boost::scope::check_error_code(err) };
BOOST_TEST(guard.active());
throw std::runtime_error("error");
}
catch (...) {}
BOOST_TEST_EQ(n, 1); // exception is not the failure condition, err was still 0 when the scope guard was destroyed
}
void check_deduction()
{
int n = 0;
{
auto guard = boost::scope::make_scope_success(normal_func(n));
BOOST_TEST(guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func >);
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
auto guard = boost::scope::make_scope_success(normal_func(n), false);
BOOST_TEST(!guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func >);
}
BOOST_TEST_EQ(n, 0);
n = 0;
{
const normal_func func{ n };
auto guard = boost::scope::make_scope_success(func, true);
BOOST_TEST(guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func >);
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
int err = 0;
auto guard = boost::scope::make_scope_success(normal_func(n), boost::scope::check_error_code(err));
BOOST_TEST(guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > >);
}
BOOST_TEST_EQ(n, 1);
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
n = 0;
{
boost::scope::scope_success guard{ normal_func(n) };
BOOST_TEST(guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func >);
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
boost::scope::scope_success guard{ normal_func(n), false };
BOOST_TEST(!guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func >);
}
BOOST_TEST_EQ(n, 0);
n = 0;
{
int err = 0;
boost::scope::scope_success guard{ normal_func(n), boost::scope::check_error_code(err) };
BOOST_TEST(guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > >);
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
int err = 0;
boost::scope::scope_success guard{ normal_func(n), boost::scope::error_code_checker(err), true };
BOOST_TEST(guard.active());
BOOST_TEST_TRAIT_SAME(decltype(guard), boost::scope::scope_success< normal_func, boost::scope::error_code_checker< int > >);
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
boost::scope::scope_success guard([&n] { ++n; });
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
int err = 10;
boost::scope::scope_success guard([&n] { ++n; }, [&err]() noexcept { return err < 0; });
BOOST_TEST(guard.active());
}
BOOST_TEST_EQ(n, 1);
n = 0;
{
boost::scope::scope_success guard1{ normal_func(n) };
boost::scope::scope_success guard2 = std::move(guard1);
BOOST_TEST(guard2.active());
BOOST_TEST_TRAIT_SAME(decltype(guard2), boost::scope::scope_success< normal_func >);
}
BOOST_TEST_EQ(n, 1);
#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
}
int main()
{
check_normal();
check_throw();
check_cond();
check_deduction();
return boost::report_errors();
}