Add insert_exception_tests

This commit is contained in:
Christian Mazakas
2023-05-19 12:33:14 -07:00
parent f28527c4d8
commit ffcae204ee
3 changed files with 1015 additions and 0 deletions

View File

@ -190,6 +190,7 @@ local CFOA_TESTS =
rehash_tests
equality_tests
fwd_tests
exception_insert_tests
;
for local test in $(CFOA_TESTS)

View File

@ -0,0 +1,448 @@
// Copyright (C) 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "latch.hpp"
#include "../helpers/generators.hpp"
#include "../helpers/test.hpp"
#include <boost/container_hash/hash.hpp>
#include <boost/core/span.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <algorithm>
#include <atomic>
#include <cmath>
#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <mutex>
#include <random>
#include <thread>
#include <type_traits>
#include <vector>
static std::size_t const num_threads =
std::max(2u, std::thread::hardware_concurrency());
std::atomic_bool should_throw{false};
constexpr std::uint32_t threshold = 2500;
void enable_exceptions() { should_throw = true; }
void disable_exceptions() { should_throw = false; }
struct exception_tag
{
};
struct stateful_hash
{
int x_ = -1;
static std::atomic<std::uint32_t> c;
void throw_helper() const
{
++c;
if (should_throw && (c % threshold == 0)) {
throw exception_tag{};
}
}
stateful_hash() {}
stateful_hash(stateful_hash const& rhs) : x_(rhs.x_) {}
stateful_hash(stateful_hash&& rhs) noexcept
{
auto tmp = x_;
x_ = rhs.x_;
rhs.x_ = tmp;
}
stateful_hash(int const x) : x_{x} {}
template <class T> std::size_t operator()(T const& t) const
{
throw_helper();
std::size_t h = static_cast<std::size_t>(x_);
boost::hash_combine(h, t);
return h;
}
bool operator==(stateful_hash const& rhs) const { return x_ == rhs.x_; }
friend std::ostream& operator<<(std::ostream& os, stateful_hash const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
friend void swap(stateful_hash& lhs, stateful_hash& rhs) noexcept
{
if (&lhs != &rhs) {
std::swap(lhs.x_, rhs.x_);
}
}
};
std::atomic<std::uint32_t> stateful_hash::c{0};
struct stateful_key_equal
{
int x_ = -1;
static std::atomic<std::uint32_t> c;
void throw_helper() const
{
++c;
if (should_throw && (c % threshold == 0)) {
throw exception_tag{};
}
}
stateful_key_equal() = default;
stateful_key_equal(stateful_key_equal const&) = default;
stateful_key_equal(stateful_key_equal&& rhs) noexcept
{
auto tmp = x_;
x_ = rhs.x_;
rhs.x_ = tmp;
}
stateful_key_equal(int const x) : x_{x} {}
template <class T, class U> bool operator()(T const& t, U const& u) const
{
throw_helper();
return t == u;
}
bool operator==(stateful_key_equal const& rhs) const { return x_ == rhs.x_; }
friend std::ostream& operator<<(
std::ostream& os, stateful_key_equal const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
friend void swap(stateful_key_equal& lhs, stateful_key_equal& rhs) noexcept
{
if (&lhs != &rhs) {
std::swap(lhs.x_, rhs.x_);
}
}
};
std::atomic<std::uint32_t> stateful_key_equal::c{0};
template <class T> struct stateful_allocator
{
int x_ = -1;
static std::atomic<std::uint32_t> c;
void throw_helper() const
{
++c;
if (should_throw && (c % threshold == 0)) {
throw exception_tag{};
}
}
using value_type = T;
stateful_allocator() = default;
stateful_allocator(stateful_allocator const&) = default;
stateful_allocator(stateful_allocator&&) = default;
stateful_allocator(int const x) : x_{x} {}
template <class U>
stateful_allocator(stateful_allocator<U> const& rhs) : x_{rhs.x_}
{
}
T* allocate(std::size_t n)
{
throw_helper();
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
bool operator==(stateful_allocator const& rhs) const { return x_ == rhs.x_; }
bool operator!=(stateful_allocator const& rhs) const { return x_ != rhs.x_; }
};
template <class T> std::atomic<std::uint32_t> stateful_allocator<T>::c{0};
struct raii
{
static std::atomic<std::uint32_t> default_constructor;
static std::atomic<std::uint32_t> copy_constructor;
static std::atomic<std::uint32_t> move_constructor;
static std::atomic<std::uint32_t> destructor;
static std::atomic<std::uint32_t> copy_assignment;
static std::atomic<std::uint32_t> move_assignment;
static std::atomic<std::uint32_t> c;
void throw_helper() const
{
++c;
if (should_throw && (c % threshold == 0)) {
throw exception_tag{};
}
}
int x_ = -1;
raii()
{
throw_helper();
++default_constructor;
}
raii(int const x) : x_{x}
{
throw_helper();
++default_constructor;
}
raii(raii const& rhs) : x_{rhs.x_}
{
throw_helper();
++copy_constructor;
}
raii(raii&& rhs) noexcept : x_{rhs.x_}
{
rhs.x_ = -1;
++move_constructor;
}
~raii() { ++destructor; }
raii& operator=(raii const& rhs)
{
throw_helper();
++copy_assignment;
if (this != &rhs) {
x_ = rhs.x_;
}
return *this;
}
raii& operator=(raii&& rhs) noexcept
{
++move_assignment;
if (this != &rhs) {
x_ = rhs.x_;
rhs.x_ = -1;
}
return *this;
}
friend bool operator==(raii const& lhs, raii const& rhs)
{
return lhs.x_ == rhs.x_;
}
friend bool operator!=(raii const& lhs, raii const& rhs)
{
return !(lhs == rhs);
}
friend bool operator==(raii const& lhs, int const x) { return lhs.x_ == x; }
friend bool operator!=(raii const& lhs, int const x)
{
return !(lhs.x_ == x);
}
friend bool operator==(int const x, raii const& rhs) { return rhs.x_ == x; }
friend bool operator!=(int const x, raii const& rhs)
{
return !(rhs.x_ == x);
}
friend std::ostream& operator<<(std::ostream& os, raii const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
friend std::ostream& operator<<(
std::ostream& os, std::pair<raii const, raii> const& rhs)
{
os << "pair<" << rhs.first << ", " << rhs.second << ">";
return os;
}
static void reset_counts()
{
default_constructor = 0;
copy_constructor = 0;
move_constructor = 0;
destructor = 0;
copy_assignment = 0;
move_assignment = 0;
c = 0;
stateful_hash::c = 0;
stateful_key_equal::c = 0;
stateful_allocator<void>::c = 0;
}
friend void swap(raii& lhs, raii& rhs) { std::swap(lhs.x_, rhs.x_); }
};
std::atomic<std::uint32_t> raii::default_constructor{0};
std::atomic<std::uint32_t> raii::copy_constructor{0};
std::atomic<std::uint32_t> raii::move_constructor{0};
std::atomic<std::uint32_t> raii::destructor{0};
std::atomic<std::uint32_t> raii::copy_assignment{0};
std::atomic<std::uint32_t> raii::move_assignment{0};
std::atomic<std::uint32_t> raii::c{0};
std::size_t hash_value(raii const& r) noexcept
{
boost::hash<int> hasher;
return hasher(r.x_);
}
struct exception_value_type_generator_type
{
std::pair<raii const, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} exception_value_type_generator;
struct exception_init_type_generator_type
{
std::pair<raii, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} exception_init_type_generator;
template <class T>
std::vector<boost::span<T> > split(
boost::span<T> s, std::size_t const nt /* num threads*/)
{
std::vector<boost::span<T> > subslices;
subslices.reserve(nt);
auto a = s.size() / nt;
auto b = a;
if (s.size() % nt != 0) {
++b;
}
auto num_a = nt;
auto num_b = std::size_t{0};
if (nt * b > s.size()) {
num_a = nt * b - s.size();
num_b = nt - num_a;
}
auto sub_b = s.subspan(0, num_b * b);
auto sub_a = s.subspan(num_b * b);
for (std::size_t i = 0; i < num_b; ++i) {
subslices.push_back(sub_b.subspan(i * b, b));
}
for (std::size_t i = 0; i < num_a; ++i) {
auto const is_last = i == (num_a - 1);
subslices.push_back(
sub_a.subspan(i * a, is_last ? boost::dynamic_extent : a));
}
return subslices;
}
template <class T, class F> void thread_runner(std::vector<T>& values, F f)
{
boost::latch latch(static_cast<std::ptrdiff_t>(num_threads));
std::vector<std::thread> threads;
auto subslices = split<T>(values, num_threads);
for (std::size_t i = 0; i < num_threads; ++i) {
threads.emplace_back([&f, &subslices, i, &latch] {
latch.arrive_and_wait();
auto s = subslices[i];
f(s);
});
}
for (auto& t : threads) {
t.join();
}
}
template <class X, class Y>
void test_matches_reference(X const& x, Y const& reference_map)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}));
}
template <class X, class Y>
void test_fuzzy_matches_reference(
X const& x, Y const& reference_map, test::random_generator rg)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}
}));
}
template <class T> using span_value_type = typename T::value_type;
void check_raii_counts()
{
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(
raii::default_constructor + raii::copy_constructor + raii::move_constructor,
raii::destructor);
}
template <class T> void shuffle_values(std::vector<T>& v)
{
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(v.begin(), v.end(), g);
}
template <class F>
auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
{
using vector_type = std::vector<decltype(f())>;
vector_type v;
v.reserve(count);
for (std::size_t i = 0; i < count; ++i) {
v.emplace_back(f());
}
return v;
}

View File

@ -0,0 +1,566 @@
// Copyright (C) 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "exception_helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/ignore_unused.hpp>
namespace {
test::seed_t initialize_seed(73987);
struct lvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
enable_exceptions();
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
try {
bool b = x.insert(r);
if (b) {
++num_inserts;
}
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_inserter;
struct norehash_lvalue_inserter_type : public lvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
lvalue_inserter_type::operator()(values, x);
BOOST_TEST_GT(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
} norehash_lvalue_inserter;
struct rvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
BOOST_TEST_EQ(raii::copy_constructor, 0u);
enable_exceptions();
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto& r : s) {
try {
bool b = x.insert(std::move(r));
if (b) {
++num_inserts;
}
} catch (...) {
}
}
});
disable_exceptions();
if (!std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} rvalue_inserter;
struct norehash_rvalue_inserter_type : public rvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
rvalue_inserter_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
}
} norehash_rvalue_inserter;
struct iterator_range_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
enable_exceptions();
thread_runner(values, [&x](boost::span<T> s) {
try {
x.insert(s.begin(), s.end());
} catch (...) {
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} iterator_range_inserter;
struct lvalue_insert_or_assign_copy_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
enable_exceptions();
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
try {
x.insert_or_assign(r.first, r.second);
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_assign_copy_assign;
struct lvalue_insert_or_assign_move_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
enable_exceptions();
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
try {
x.insert_or_assign(r.first, std::move(r.second));
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_insert_or_assign_move_assign;
struct rvalue_insert_or_assign_copy_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
enable_exceptions();
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
try {
x.insert_or_assign(std::move(r.first), r.second);
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} rvalue_insert_or_assign_copy_assign;
struct rvalue_insert_or_assign_move_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
enable_exceptions();
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
try {
x.insert_or_assign(std::move(r.first), std::move(r.second));
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} rvalue_insert_or_assign_move_assign;
struct lvalue_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
enable_exceptions();
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
try {
bool b = x.insert_or_cvisit(
r, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_GT(num_inserts, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_cvisit;
struct lvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
enable_exceptions();
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
try {
bool b =
x.insert_or_visit(r, [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_GT(num_inserts, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_visit;
struct rvalue_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
enable_exceptions();
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
try {
bool b = x.insert_or_cvisit(
std::move(r), [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_GT(num_inserts, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
}
} rvalue_insert_or_cvisit;
struct rvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
enable_exceptions();
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
try {
bool b = x.insert_or_visit(
std::move(r), [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
} catch (...) {
}
}
});
disable_exceptions();
BOOST_TEST_GT(num_inserts, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (!std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
}
} rvalue_insert_or_visit;
struct iterator_range_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_invokes{0};
enable_exceptions();
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
try {
x.insert_or_cvisit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
} catch (...) {
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_or_cvisit;
struct iterator_range_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_invokes{0};
enable_exceptions();
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
try {
x.insert_or_visit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
} catch (...) {
}
});
disable_exceptions();
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_or_visit;
template <class X, class G, class F>
void insert(X*, G gen, F inserter, test::random_generator rg)
{
disable_exceptions();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
X x;
inserter(values, x);
test_fuzzy_matches_reference(x, reference_map, rg);
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
}
template <class X> void insert_initializer_list(X*)
{
using value_type = typename X::value_type;
std::initializer_list<value_type> values{
value_type{raii{0}, raii{0}},
value_type{raii{1}, raii{1}},
value_type{raii{2}, raii{2}},
value_type{raii{3}, raii{3}},
value_type{raii{4}, raii{4}},
value_type{raii{5}, raii{5}},
value_type{raii{6}, raii{6}},
value_type{raii{6}, raii{6}},
value_type{raii{7}, raii{7}},
value_type{raii{8}, raii{8}},
value_type{raii{9}, raii{9}},
value_type{raii{10}, raii{10}},
value_type{raii{9}, raii{9}},
value_type{raii{8}, raii{8}},
value_type{raii{7}, raii{7}},
value_type{raii{6}, raii{6}},
value_type{raii{5}, raii{5}},
value_type{raii{4}, raii{4}},
value_type{raii{3}, raii{3}},
value_type{raii{2}, raii{2}},
value_type{raii{1}, raii{1}},
value_type{raii{0}, raii{0}},
};
std::vector<raii> dummy;
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
{
X x;
thread_runner(
dummy, [&x, &values](boost::span<raii>) { x.insert(values); });
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
{
{
std::atomic<std::uint64_t> num_invokes{0};
X x;
thread_runner(dummy, [&x, &values, &num_invokes](boost::span<raii>) {
x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
x.insert_or_cvisit(
values, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, (values.size() - x.size()) +
(num_threads - 1) * values.size() +
num_threads * values.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
}
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
} // namespace
using test::default_generator;
using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map)))
UNORDERED_TEST(
insert,
((map))
((exception_value_type_generator)(exception_init_type_generator))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
(rvalue_insert_or_cvisit)(rvalue_insert_or_visit)
(iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((map))
((exception_init_type_generator))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()