123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- /* 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
- }
|