bbd4e31869
Merged from develop branch as CDash reports all green
375 lines
14 KiB
C++
375 lines
14 KiB
C++
/* Unit testing for outcomes
|
|
(C) 2013-2019 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
|
|
|
|
|
|
Boost Software License - Version 1.0 - August 17th, 2003
|
|
|
|
Permission is hereby granted, free of charge, to any person or organization
|
|
obtaining a copy of the software and accompanying documentation covered by
|
|
this license (the "Software") to use, reproduce, display, distribute,
|
|
execute, and transmit the Software, and to prepare derivative works of the
|
|
Software, and to permit third-parties to whom the Software is furnished to
|
|
do so, all subject to the following:
|
|
|
|
The copyright notices in the Software and this entire statement, including
|
|
the above license grant, this restriction and the following disclaimer,
|
|
must be included in all copies of the Software, in whole or in part, and
|
|
all derivative works of the Software, unless such copies or derivative
|
|
works are solely in the form of machine-executable object code generated by
|
|
a source language processor.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <boost/outcome/outcome.hpp>
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <boost/test/unit_test_monitor.hpp>
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4297) // function assumed not to throw an exception but does
|
|
#endif
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wterminate"
|
|
#endif
|
|
template <bool mc, bool ma> struct Throwy
|
|
{
|
|
int count{0}, inc{0}, id{0};
|
|
Throwy() = default;
|
|
Throwy(int c, int d, int i = 1) noexcept
|
|
: count(c)
|
|
, inc(i)
|
|
, id(d)
|
|
{
|
|
}
|
|
Throwy(const Throwy &) = delete;
|
|
Throwy &operator=(const Throwy &) = delete;
|
|
Throwy(Throwy &&o) noexcept(!mc)
|
|
: count(o.count - o.inc)
|
|
, inc(o.inc)
|
|
, id(o.id) // NOLINT
|
|
{
|
|
if(mc)
|
|
{
|
|
std::cout << id << " move constructor count = " << count << std::endl;
|
|
if(!count)
|
|
{
|
|
throw std::bad_alloc();
|
|
}
|
|
}
|
|
o.count = 0;
|
|
o.inc = 0;
|
|
o.id = 0;
|
|
}
|
|
Throwy &operator=(Throwy &&o) noexcept(!ma)
|
|
{
|
|
count = o.count - o.inc;
|
|
if(ma)
|
|
{
|
|
std::cout << o.id << " move assignment count = " << count << std::endl;
|
|
if(!count)
|
|
{
|
|
throw std::bad_alloc();
|
|
}
|
|
}
|
|
inc = o.inc;
|
|
id = o.id;
|
|
o.count = 0;
|
|
o.inc = 0;
|
|
o.id = 0;
|
|
return *this;
|
|
}
|
|
~Throwy() = default;
|
|
};
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
enum class ErrorCode
|
|
{
|
|
dummy
|
|
};
|
|
enum class ErrorCode2
|
|
{
|
|
dummy
|
|
};
|
|
template <bool mc, bool ma> using resulty1 = BOOST_OUTCOME_V2_NAMESPACE::result<Throwy<mc, ma>, ErrorCode, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
|
|
template <bool mc, bool ma> using resulty2 = BOOST_OUTCOME_V2_NAMESPACE::result<ErrorCode, Throwy<mc, ma>, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
|
|
template <bool mc, bool ma> using outcomey1 = BOOST_OUTCOME_V2_NAMESPACE::outcome<ErrorCode, Throwy<mc, ma>, ErrorCode2, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
|
|
template <bool mc, bool ma> using outcomey2 = BOOST_OUTCOME_V2_NAMESPACE::outcome<ErrorCode, ErrorCode2, Throwy<mc, ma>, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
|
|
#endif
|
|
|
|
BOOST_OUTCOME_AUTO_TEST_CASE(works_outcome_swap, "Tests that the outcome swaps as intended")
|
|
{
|
|
using namespace BOOST_OUTCOME_V2_NAMESPACE;
|
|
{ // Does swap actually swap?
|
|
outcome<std::string> a("niall"), b("douglas");
|
|
BOOST_CHECK(a.value() == "niall");
|
|
BOOST_CHECK(b.value() == "douglas");
|
|
swap(a, b);
|
|
BOOST_CHECK(a.value() == "douglas");
|
|
BOOST_CHECK(b.value() == "niall");
|
|
a = boost::system::errc::not_enough_memory;
|
|
swap(a, b);
|
|
BOOST_CHECK(a.value() == "niall");
|
|
BOOST_CHECK(b.error() == boost::system::errc::not_enough_memory);
|
|
BOOST_CHECK(!a.has_lost_consistency());
|
|
BOOST_CHECK(!b.has_lost_consistency());
|
|
}
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
{ // Is noexcept propagated?
|
|
using nothrow_t = Throwy<false, false>;
|
|
using nothrow = resulty1<false, false>;
|
|
static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
|
|
|
|
static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
|
|
nothrow a(1, 78), b(1, 65);
|
|
a.swap(b);
|
|
static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
|
|
}
|
|
{ // Is noexcept propagated?
|
|
using nothrow_t = Throwy<false, false>;
|
|
using nothrow = resulty2<false, false>;
|
|
static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
|
|
|
|
static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
|
|
nothrow a(1, 78), b(1, 65);
|
|
a.swap(b);
|
|
static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
|
|
}
|
|
|
|
{ // Does swap implement the strong guarantee?
|
|
using throwy_t = Throwy<true, true>;
|
|
using throwy = resulty1<true, true>;
|
|
static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
|
|
|
|
static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
{
|
|
throwy a(3, 78), b(4, 65);
|
|
a.swap(b);
|
|
static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
|
|
BOOST_CHECK(a.value().id == 65);
|
|
BOOST_CHECK(b.value().id == 78);
|
|
try
|
|
{
|
|
a.swap(b); // fails on first assignment
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.value().id == 65); // ensure it is perfectly restored
|
|
BOOST_CHECK(b.value().id == 78);
|
|
}
|
|
BOOST_CHECK(!a.has_lost_consistency());
|
|
BOOST_CHECK(!b.has_lost_consistency());
|
|
}
|
|
std::cout << std::endl;
|
|
{
|
|
throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
|
|
try
|
|
{
|
|
a.swap(b);
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
|
|
BOOST_CHECK(b.has_lost_consistency());
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
{ // Does swap implement the strong guarantee?
|
|
using throwy_t = Throwy<true, true>;
|
|
using throwy = resulty2<true, true>;
|
|
static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
|
|
|
|
static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
{
|
|
throwy a(3, 78), b(4, 65);
|
|
a.swap(b);
|
|
static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
|
|
BOOST_CHECK(a.error().id == 65);
|
|
BOOST_CHECK(b.error().id == 78);
|
|
try
|
|
{
|
|
a.swap(b); // fails on first assignment
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.error().id == 65); // ensure it is perfectly restored
|
|
BOOST_CHECK(b.error().id == 78);
|
|
}
|
|
BOOST_CHECK(!a.has_lost_consistency());
|
|
BOOST_CHECK(!b.has_lost_consistency());
|
|
}
|
|
std::cout << std::endl;
|
|
{
|
|
throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
|
|
try
|
|
{
|
|
a.swap(b);
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
|
|
BOOST_CHECK(b.has_lost_consistency());
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
{ // Is noexcept propagated?
|
|
using nothrow_t = Throwy<false, false>;
|
|
using nothrow = outcomey1<false, false>;
|
|
static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
|
|
|
|
static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
|
|
nothrow a(1, 78), b(1, 65);
|
|
a.swap(b);
|
|
static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
|
|
}
|
|
{ // Is noexcept propagated?
|
|
using nothrow_t = Throwy<false, false>;
|
|
using nothrow = outcomey1<false, false>;
|
|
static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
|
|
static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
|
|
static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
|
|
|
|
static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
|
|
nothrow a(1, 78), b(1, 65);
|
|
a.swap(b);
|
|
static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
|
|
}
|
|
|
|
{ // Does swap implement the strong guarantee?
|
|
using throwy_t = Throwy<true, true>;
|
|
using throwy = outcomey1<true, true>;
|
|
static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
|
|
|
|
static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
{
|
|
throwy a(3, 78), b(4, 65);
|
|
a.swap(b);
|
|
static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
|
|
BOOST_CHECK(a.error().id == 65);
|
|
BOOST_CHECK(b.error().id == 78);
|
|
try
|
|
{
|
|
a.swap(b); // fails on first assignment
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.error().id == 65); // ensure it is perfectly restored
|
|
BOOST_CHECK(b.error().id == 78);
|
|
}
|
|
BOOST_CHECK(!a.has_lost_consistency());
|
|
BOOST_CHECK(!b.has_lost_consistency());
|
|
}
|
|
std::cout << std::endl;
|
|
{
|
|
throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
|
|
try
|
|
{
|
|
a.swap(b);
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
|
|
BOOST_CHECK(b.has_lost_consistency());
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
{ // Does swap implement the strong guarantee?
|
|
using throwy_t = Throwy<true, true>;
|
|
using throwy = outcomey2<true, true>;
|
|
static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
|
|
static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
|
|
static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
|
|
|
|
static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
|
|
|
|
{
|
|
throwy a(3, 78), b(4, 65);
|
|
a.swap(b);
|
|
static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
|
|
BOOST_CHECK(a.exception().id == 65);
|
|
BOOST_CHECK(b.exception().id == 78);
|
|
try
|
|
{
|
|
a.swap(b); // fails on first assignment
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.exception().id == 65); // ensure it is perfectly restored
|
|
BOOST_CHECK(b.exception().id == 78);
|
|
}
|
|
BOOST_CHECK(!a.has_lost_consistency());
|
|
BOOST_CHECK(!b.has_lost_consistency());
|
|
}
|
|
std::cout << std::endl;
|
|
{
|
|
throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
|
|
try
|
|
{
|
|
a.swap(b);
|
|
BOOST_REQUIRE(false);
|
|
}
|
|
catch(const std::bad_alloc & /*unused*/)
|
|
{
|
|
BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
|
|
BOOST_CHECK(b.has_lost_consistency());
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
#endif
|
|
}
|