Skip to content

Commit

Permalink
More unit tests for unexpected
Browse files Browse the repository at this point in the history
  • Loading branch information
Bronek committed Feb 1, 2025
1 parent 57291ca commit 1ec1b3a
Showing 1 changed file with 272 additions and 21 deletions.
293 changes: 272 additions & 21 deletions tests/pfn/expected.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
// Distributed under the ISC License. See accompanying file LICENSE.md
// or copy at https://opensource.org/licenses/ISC

#include "catch2/catch_test_macros.hpp"
#include <pfn/expected.hpp>

#include <catch2/catch_all.hpp>

#include <stdexcept>
#include <type_traits>

enum Error { unknown, secret = 142, mystery = 176 };
Expand Down Expand Up @@ -131,8 +133,87 @@ TEST_CASE("unexpect", "[expected][polyfill][unexpect]")
}
}

namespace unxp {
static int witness = 0;

struct Foo {
int v = {};

Foo() = delete;
Foo(Foo &&) = default;

Foo &operator=(Foo &o) noexcept
{
v = o.v;
witness *= 53;
return *this;
}

Foo &operator=(Foo const &o) noexcept
{
v = o.v;
witness *= 59;
return *this;
}

Foo &operator=(Foo &&o) noexcept
{
v = o.v;
witness *= 61;
return *this;
}

Foo &operator=(Foo const &&o) noexcept
{
v = o.v;
witness *= 67;
return *this;
}

Foo(int a) noexcept : v(a) { witness += a; }

Foo(auto &&...a) noexcept
requires(sizeof...(a) > 1 && (std::is_same_v<std::remove_cvref_t<decltype(a)>, int> && ...))
: v((1 * ... * a))
{
witness += v;
}

Foo(std::initializer_list<double> l, auto... a) noexcept(false)
requires(std::is_same_v<std::remove_cvref_t<decltype(a)>, int> && ...)
: v(init(l, a...))
{
witness += v;
}

bool operator==(Foo const &) const noexcept = default;

static int init(std::initializer_list<double> l, auto &&...a) noexcept(false)
{
double ret = (1 * ... * a);
for (auto d : l) {
if (d == 0.0)
throw std::runtime_error("invalid input");
ret *= d;
}
return static_cast<int>(ret);
}
};

void swap(Foo &l, Foo &r)
{
std::swap(l.v, r.v);
witness *= 97;
}

} // namespace unxp

TEST_CASE("unexpected", "[expected][polyfill][unexpected]")
{
using pfn::unexpected;
using unxp::Foo;
using unxp::witness;

SECTION("is_valid_unexpected")
{
using pfn::detail::_is_valid_unexpected;
Expand All @@ -156,39 +237,209 @@ TEST_CASE("unexpected", "[expected][polyfill][unexpected]")

SECTION("constructors")
{
using pfn::unexpected;
SECTION("constexpr, CTAD")
{
constexpr unexpected c{Error::mystery};
static_assert(c.error() == Error::mystery);
static_assert(std::is_same_v<decltype(c), unexpected<Error> const>);
static_assert(std::is_nothrow_constructible_v<decltype(c), Error>);
SUCCEED();
}

SECTION("explicit single parameter")
SECTION("constexpr, no CTAD")
{
SECTION("constexpr, CTAD")
{
constexpr unexpected c1{Error::mystery};
static_assert(std::is_same_v<decltype(c1), unexpected<Error> const>);
static_assert(c1.error() == Error::mystery);
}
constexpr unexpected<int> c{42};
static_assert(c.error() == 42);
static_assert(std::is_nothrow_constructible_v<decltype(c), int>);
SUCCEED();
}

SECTION("no conversion, CTAD")
{
auto const before = witness;
unexpected c{Foo{2}};
CHECK(witness == before + 2);
CHECK(c.error() == Foo{2});
CHECK(c == unexpected<Foo>{2});
static_assert(std::is_same_v<decltype(c), unexpected<Foo>>);
static_assert(std::is_nothrow_constructible_v<decltype(c), Foo>);
}

SECTION("conversion, no CTAD")
{
auto const before = witness;
unexpected<Foo> c(3);
CHECK(witness == before + 3);
CHECK(c.error().v == 3);
CHECK(c == unexpected<Foo>{3});
static_assert(std::is_nothrow_constructible_v<decltype(c), int>);
}

SECTION("in-place, no CTAD")
{
auto const before = witness;
unexpected<Foo> c(std::in_place, 3, 5);
CHECK(witness == before + 3 * 5);
CHECK(c.error() == Foo{3, 5});
CHECK(c == unexpected<Foo>{15});
static_assert(std::is_nothrow_constructible_v<decltype(c), std::in_place_t, int, int>);
}

SECTION("constexpr, no CTAD")
SECTION("in_place, not CTAD, initializer_list, noexcept(false)")
{
SECTION("forwarded args")
{
constexpr unexpected<int> c1{42};
static_assert(std::is_same_v<decltype(c1), unexpected<int> const>);
static_assert(c1.error() == 42);
auto const before = witness;
unexpected<Foo> c(std::in_place, {3.0, 5.0}, 7, 11);
auto const d = 3 * 5 * 7 * 11;
CHECK(witness == before + d);
CHECK(c.error() == Foo{d});
CHECK(c == unexpected{Foo{d}});
static_assert(
not std::is_nothrow_constructible_v<decltype(c), std::in_place_t, std::initializer_list<double>, int, int>);
static_assert(std::is_constructible_v<decltype(c), std::in_place_t, std::initializer_list<double>, int, int>);
}

SECTION("no conversion, CTAD")
SECTION("no forwarded args")
{
unexpected const c2{Error::secret};
static_assert(std::is_same_v<decltype(c2), unexpected<Error> const>);
CHECK(c2.error() == Error::secret);
auto const before = witness;
unexpected<Foo> c(std::in_place, {2.0, 2.5});
CHECK(witness == before + 5);
CHECK(c.error() == Foo{5});
CHECK(c == unexpected<Foo>{5});
static_assert(not std::is_nothrow_constructible_v<decltype(c), std::in_place_t, std::initializer_list<double>>);
static_assert(std::is_constructible_v<decltype(c), std::in_place_t, std::initializer_list<double>>);
}

SECTION("conversion, no CTAD")
SECTION("exception thrown")
{
unexpected<double> c3(12);
static_assert(std::is_same_v<decltype(c3), unexpected<double>>);
CHECK(c3.error() == 12.0);
unexpected<Foo> t{13};
auto const before = witness;
try {
t = unexpected<Foo>{std::in_place, {2.0, 1.0, 0.0}, 5};
FAIL();
} catch (std::runtime_error const &) {
SUCCEED();
}
CHECK(t.error().v == 13);
CHECK(witness == before);
}
}
}

SECTION("accessor")
{
Foo v{1};

SECTION("lval")
{
unexpected<Foo> t{13};
auto before = witness;
v = t.error();
CHECK(witness == before * 53);
CHECK(v == Foo{13});
}

SECTION("lval const")
{
unexpected<Foo> const t{17};
auto before = witness;
v = t.error();
CHECK(witness == before * 59);
CHECK(v == Foo{17});
}

SECTION("rval")
{
unexpected<Foo> t{19};
auto before = witness;
v = std::move(t.error());
CHECK(witness == before * 61);
CHECK(v == Foo{19});
}

SECTION("rval const")
{
unexpected<Foo> const t{23};
auto before = witness;
v = std::move(t.error());
CHECK(witness == before * 67);
CHECK(v == Foo{23});
}
}

SECTION("assignment")
{
unexpected<Foo> v{0};

SECTION("lval")
{
unexpected<Foo> t{13};
auto before = witness;
v = t; // t binds to unexpected<Foo> const &
CHECK(witness == before * 59);
CHECK(v.error() == Foo{13});
}

SECTION("lval const")
{
unexpected<Foo> const t{17};
auto before = witness;
v = t; // t binds to unexpected<Foo> const &
CHECK(witness == before * 59);
CHECK(v.error() == Foo{17});
}

SECTION("rval")
{
unexpected<Foo> t{19};
auto before = witness;
v = std::move(t); // t binds to unexpected<Foo> &&
CHECK(witness == before * 61);
CHECK(v.error() == Foo{19});
}

SECTION("in_place") {}
SECTION("rval const")
{
unexpected<Foo> const t{23};
auto before = witness;
v = std::move(t); // t binds to unexpected<Foo> const &
CHECK(witness == before * 59);
CHECK(v.error() == Foo{23});
}
}

SECTION("swap")
{
unexpected<Foo> v{2};
unexpected w{Foo{3}};
auto before = witness;
v.swap(w);
CHECK(witness == before * 97);
CHECK(v == unexpected{Foo{3}});
CHECK(w == unexpected{Foo{2}});
w.error() = Foo{11};
before = witness;
swap(v, w);
CHECK(v.error() == Foo{11});
CHECK(w.error() == Foo(3));
}

SECTION("constexpr all, CTAD")
{
constexpr auto test = [](auto i) constexpr {
unexpected a{i};
unexpected b{i * 5};
swap(a, b);
unexpected c{0};
c = b;
b.swap(c);
return unexpected{b.error() * a.error() * 7};
};
constexpr auto c = test(21);
static_assert(std::is_same_v<decltype(c), unexpected<int> const>);
static_assert(c.error() == 21 * 21 * 5 * 7);

SUCCEED();
}
}

0 comments on commit 1ec1b3a

Please sign in to comment.