psemek/libs/util/tests/function.cpp

211 lines
3.3 KiB
C++

#include <psemek/test/test.hpp>
#include <psemek/util/function.hpp>
#include <psemek/util/unused.hpp>
#include <memory>
#include <array>
using namespace psemek::util;
test_case(util_function_empty_call)
{
function<void()> f;
expect_throw(f(), empty_function_error);
}
using heavy = std::array<int, 256>;
static void util_function_assign_test(bool light)
{
function<void()> f;
expect(!f);
int counter = 0;
if (light)
f = [&counter]{ ++counter; };
else
f = [&counter, h = heavy{}]{ unused(h); ++counter; };
expect(f);
expect_equal(counter, 0);
f();
expect_equal(counter, 1);
f();
expect_equal(counter, 2);
}
static void util_function_move_test(bool light)
{
function<void()> f;
expect(!f);
int counter = 0;
if (light)
f = [&counter]{ ++counter; };
else
f = [&counter, h = heavy{}]{ unused(h); ++counter; };
expect(f);
function<void()> g;
expect(!g);
g = std::move(f);
expect(!f);
expect(g);
expect_equal(counter, 0);
g();
expect_equal(counter, 1);
g();
expect_equal(counter, 2);
function<void()> h = std::move(g);
expect(!f);
expect(!g);
expect(h);
expect_equal(counter, 2);
h();
expect_equal(counter, 3);
h();
expect_equal(counter, 4);
}
static void util_function_destroy_test(bool light)
{
std::shared_ptr<int> p = std::make_shared<int>(0);
auto wp = std::weak_ptr(p);
{
function<void()> f, g;
expect(!f);
expect(!g);
if (light)
{
f = [p]{ ++*p; };
g = [p]{ --*p; };
}
else
{
f = [p, h = heavy{}]{ unused(h); ++*p; };
g = [p, h = heavy{}]{ unused(h); --*p; };
}
expect(f);
expect(g);
expect(p);
expect_equal(p.use_count(), 3);
expect_equal(*p, 0);
p.reset();
expect(wp.lock());
expect_equal(wp.use_count(), 2);
expect_equal(*wp.lock(), 0);
f();
expect_equal(*wp.lock(), 1);
f();
expect_equal(*wp.lock(), 2);
g();
expect_equal(*wp.lock(), 1);
g();
expect_equal(*wp.lock(), 0);
f.reset();
expect(!f);
expect(wp.lock());
expect_equal(wp.use_count(), 1);
}
expect(!wp.lock());
expect_equal(wp.use_count(), 0);
}
namespace
{
struct fake_exception
: std::exception
{
char const * what() const noexcept { return "fake"; }
};
template <typename Heavy>
struct throw_on_move
{
Heavy heavy;
throw_on_move() = default;
throw_on_move(throw_on_move &&)
{
throw fake_exception{};
}
void operator()()
{}
};
}
static void util_function_guarantee_test(bool light)
{
function<void()> f;
expect(!f);
int counter = 0;
f = [&counter]{ ++counter; };
expect(f);
expect_equal(counter, 0);
if (light)
expect_throw(f = throw_on_move<int>{}, fake_exception);
else
expect_throw(f = throw_on_move<heavy>{}, fake_exception);
expect(f);
expect_equal(counter, 0);
f();
expect_equal(counter, 1);
}
test_case(util_function_light_assign)
{
util_function_assign_test(true);
}
test_case(util_function_light_move)
{
util_function_move_test(true);
}
test_case(util_function_light_destroy)
{
util_function_destroy_test(true);
}
test_case(util_function_light_guarantee)
{
util_function_guarantee_test(true);
}
test_case(util_function_heavy_assign)
{
util_function_assign_test(false);
}
test_case(util_function_heavy_move)
{
util_function_move_test(false);
}
test_case(util_function_heavy_destroy)
{
util_function_destroy_test(false);
}
test_case(util_function_heavy_guarantee)
{
util_function_guarantee_test(false);
}