Squashed 'external/entt/entt/' content from commit fef92113

git-subtree-dir: external/entt/entt
git-subtree-split: fef921132cae7588213d0f9bcd2fb9c8ffd8b7fc
This commit is contained in:
2023-07-25 11:29:51 +02:00
commit 5c7231b7a3
242 changed files with 146004 additions and 0 deletions

View File

@ -0,0 +1,32 @@
#ifndef ENTT_COMMON_BASIC_TEST_ALLOCATOR_HPP
#define ENTT_COMMON_BASIC_TEST_ALLOCATOR_HPP
#include <memory>
#include <type_traits>
namespace test {
template<typename Type, typename Pocs = std::true_type>
struct basic_test_allocator: std::allocator<Type> {
// basic pocca/pocma/pocs allocator
using base = std::allocator<Type>;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_swap = Pocs;
using std::allocator<Type>::allocator;
basic_test_allocator &operator=(const basic_test_allocator &other) {
// necessary to avoid call suppression
base::operator=(other);
return *this;
}
bool operator==(const basic_test_allocator &other) {
return (this == &other);
}
};
} // namespace test
#endif

18
test/entt/common/config.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef ENTT_COMMON_CONFIG_H
#define ENTT_COMMON_CONFIG_H
namespace test {
#ifdef NDEBUG
# define ENTT_DEBUG_TEST(Case, Test) TEST(Case, DISABLED_##Test)
# define ENTT_DEBUG_TEST_F(Case, Test) TEST_F(Case, DISABLED_##Test)
# define ENTT_DEBUG_TYPED_TEST(Case, Test) TYPED_TEST(Case, DISABLED_##Test)
#else
# define ENTT_DEBUG_TEST(Case, Test) TEST(Case, Test)
# define ENTT_DEBUG_TEST_F(Case, Test) TEST_F(Case, Test)
# define ENTT_DEBUG_TYPED_TEST(Case, Test) TYPED_TEST(Case, Test)
#endif
} // namespace test
#endif

View File

@ -0,0 +1,69 @@
#ifndef ENTT_COMMON_THROWING_ALLOCATOR_HPP
#define ENTT_COMMON_THROWING_ALLOCATOR_HPP
#include <cstddef>
#include <memory>
#include <type_traits>
namespace test {
template<typename Type>
class throwing_allocator: std::allocator<Type> {
template<typename Other>
friend class throwing_allocator;
using base = std::allocator<Type>;
struct test_exception {};
public:
using value_type = Type;
using pointer = value_type *;
using const_pointer = const value_type *;
using void_pointer = void *;
using const_void_pointer = const void *;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
using exception_type = test_exception;
template<class Other>
struct rebind {
using other = throwing_allocator<Other>;
};
throwing_allocator() = default;
template<class Other>
throwing_allocator(const throwing_allocator<Other> &other)
: base{other} {}
pointer allocate(std::size_t length) {
if(trigger_on_allocate) {
trigger_on_allocate = false;
throw test_exception{};
}
trigger_on_allocate = trigger_after_allocate;
trigger_after_allocate = false;
return base::allocate(length);
}
void deallocate(pointer mem, std::size_t length) {
base::deallocate(mem, length);
}
bool operator==(const throwing_allocator<Type> &) const {
return true;
}
bool operator!=(const throwing_allocator<Type> &other) const {
return !(*this == other);
}
static inline bool trigger_on_allocate{};
static inline bool trigger_after_allocate{};
};
} // namespace test
#endif

View File

@ -0,0 +1,46 @@
#ifndef ENTT_COMMON_THROWING_TYPE_HPP
#define ENTT_COMMON_THROWING_TYPE_HPP
namespace test {
class throwing_type {
struct test_exception {};
public:
using exception_type = test_exception;
static constexpr auto moved_from_value = -1;
throwing_type(int value)
: data{value} {}
throwing_type(const throwing_type &other)
: data{other.data} {
if(data == trigger_on_value) {
data = moved_from_value;
throw exception_type{};
}
}
throwing_type &operator=(const throwing_type &other) {
if(other.data == trigger_on_value) {
data = moved_from_value;
throw exception_type{};
}
data = other.data;
return *this;
}
operator int() const {
return data;
}
static inline int trigger_on_value{};
private:
int data{};
};
} // namespace test
#endif

View File

@ -0,0 +1,64 @@
#ifndef ENTT_COMMON_TRACKED_MEMORY_RESOURCE_HPP
#define ENTT_COMMON_TRACKED_MEMORY_RESOURCE_HPP
#ifdef ENTT_HAS_HEADER_VERSION
# include <version>
#
# if defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L
# define ENTT_HAS_TRACKED_MEMORY_RESOURCE
#
# include <cstddef>
# include <memory_resource>
# include <string>
namespace test {
class tracked_memory_resource: public std::pmr::memory_resource {
void *do_allocate(std::size_t bytes, std::size_t alignment) override {
++alloc_counter;
return std::pmr::get_default_resource()->allocate(bytes, alignment);
}
void do_deallocate(void *value, std::size_t bytes, std::size_t alignment) override {
++dealloc_counter;
std::pmr::get_default_resource()->deallocate(value, bytes, alignment);
}
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override {
return (this == &other);
}
public:
using string_type = std::pmr::string;
using size_type = std::size_t;
static constexpr const char *default_value = "a string long enough to force an allocation (hopefully)";
tracked_memory_resource()
: alloc_counter{},
dealloc_counter{} {}
size_type do_allocate_counter() const noexcept {
return alloc_counter;
}
size_type do_deallocate_counter() const noexcept {
return dealloc_counter;
}
void reset() noexcept {
alloc_counter = 0u;
dealloc_counter = 0u;
}
private:
size_type alloc_counter;
size_type dealloc_counter;
};
} // namespace test
# endif
#endif
#endif

View File

@ -0,0 +1,8 @@
#include <regex>
#include <gtest/gtest.h>
#include <entt/config/version.h>
TEST(Version, All) {
ASSERT_STREQ(ENTT_VERSION, ENTT_XSTR(ENTT_VERSION_MAJOR) "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH));
ASSERT_TRUE(std::regex_match(ENTT_VERSION, std::regex{"^[0-9]+\\.[0-9]+\\.[0-9]+$"}));
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,946 @@
#include <cmath>
#include <cstddef>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/container/dense_set.hpp>
#include <entt/core/memory.hpp>
#include <entt/core/utility.hpp>
#include "../common/throwing_allocator.hpp"
#include "../common/tracked_memory_resource.hpp"
struct transparent_equal_to {
using is_transparent = void;
template<typename Type, typename Other>
constexpr std::enable_if_t<std::is_convertible_v<Other, Type>, bool>
operator()(const Type &lhs, const Other &rhs) const {
return lhs == static_cast<Type>(rhs);
}
};
TEST(DenseSet, Functionalities) {
entt::dense_set<int, entt::identity, transparent_equal_to> set;
const auto &cset = set;
ASSERT_NO_FATAL_FAILURE([[maybe_unused]] auto alloc = set.get_allocator());
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.load_factor(), 0.f);
ASSERT_EQ(set.max_load_factor(), .875f);
ASSERT_EQ(set.max_size(), (std::vector<std::pair<std::size_t, int>>{}.max_size()));
set.max_load_factor(.9f);
ASSERT_EQ(set.max_load_factor(), .9f);
ASSERT_EQ(set.begin(), set.end());
ASSERT_EQ(cset.begin(), cset.end());
ASSERT_EQ(set.cbegin(), set.cend());
ASSERT_NE(set.max_bucket_count(), 0u);
ASSERT_EQ(set.bucket_count(), 8u);
ASSERT_EQ(set.bucket_size(3u), 0u);
ASSERT_EQ(set.bucket(0), 0u);
ASSERT_EQ(set.bucket(3), 3u);
ASSERT_EQ(set.bucket(8), 0u);
ASSERT_EQ(set.bucket(10), 2u);
ASSERT_EQ(set.begin(1u), set.end(1u));
ASSERT_EQ(cset.begin(1u), cset.end(1u));
ASSERT_EQ(set.cbegin(1u), set.cend(1u));
ASSERT_FALSE(set.contains(42));
ASSERT_FALSE(set.contains(4.2));
ASSERT_EQ(set.find(42), set.end());
ASSERT_EQ(set.find(4.2), set.end());
ASSERT_EQ(cset.find(42), set.cend());
ASSERT_EQ(cset.find(4.2), set.cend());
ASSERT_EQ(set.hash_function()(42), 42);
ASSERT_TRUE(set.key_eq()(42, 42));
set.emplace(0);
ASSERT_EQ(set.count(0), 1u);
ASSERT_EQ(set.count(4.2), 0u);
ASSERT_EQ(cset.count(0.0), 1u);
ASSERT_EQ(cset.count(42), 0u);
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 1u);
ASSERT_NE(set.begin(), set.end());
ASSERT_NE(cset.begin(), cset.end());
ASSERT_NE(set.cbegin(), set.cend());
ASSERT_TRUE(set.contains(0));
ASSERT_EQ(set.bucket(0), 0u);
set.clear();
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_EQ(cset.begin(), cset.end());
ASSERT_EQ(set.cbegin(), set.cend());
ASSERT_FALSE(set.contains(0));
}
TEST(DenseSet, Constructors) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<int> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
set = entt::dense_set<int>{std::allocator<int>{}};
set = entt::dense_set<int>{2u * minimum_bucket_count, std::allocator<float>{}};
set = entt::dense_set<int>{4u * minimum_bucket_count, std::hash<int>(), std::allocator<double>{}};
set.emplace(3);
entt::dense_set<int> temp{set, set.get_allocator()};
entt::dense_set<int> other{std::move(temp), set.get_allocator()};
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(set.bucket_count(), 4u * minimum_bucket_count);
ASSERT_EQ(other.bucket_count(), 4u * minimum_bucket_count);
}
TEST(DenseSet, Copy) {
entt::dense_set<std::size_t, entt::identity> set;
set.max_load_factor(set.max_load_factor() - .05f);
set.emplace(3u);
entt::dense_set<std::size_t, entt::identity> other{set};
ASSERT_TRUE(set.contains(3u));
ASSERT_TRUE(other.contains(3u));
ASSERT_EQ(set.max_load_factor(), other.max_load_factor());
set.emplace(1u);
set.emplace(11u);
other.emplace(0u);
other = set;
ASSERT_TRUE(other.contains(3u));
ASSERT_TRUE(other.contains(1u));
ASSERT_TRUE(other.contains(11u));
ASSERT_FALSE(other.contains(0u));
ASSERT_EQ(other.bucket(3u), set.bucket(11u));
ASSERT_EQ(other.bucket(3u), other.bucket(11u));
ASSERT_EQ(*other.begin(3u), *set.begin(3u));
ASSERT_EQ(*other.begin(3u), 11u);
ASSERT_EQ((*++other.begin(3u)), 3u);
}
TEST(DenseSet, Move) {
entt::dense_set<std::size_t, entt::identity> set;
set.max_load_factor(set.max_load_factor() - .05f);
set.emplace(3u);
entt::dense_set<std::size_t, entt::identity> other{std::move(set)};
ASSERT_EQ(set.size(), 0u);
ASSERT_TRUE(other.contains(3u));
ASSERT_EQ(set.max_load_factor(), other.max_load_factor());
set = other;
set.emplace(1u);
set.emplace(11u);
other.emplace(0u);
other = std::move(set);
ASSERT_EQ(set.size(), 0u);
ASSERT_TRUE(other.contains(3u));
ASSERT_TRUE(other.contains(1u));
ASSERT_TRUE(other.contains(11u));
ASSERT_FALSE(other.contains(0u));
ASSERT_EQ(other.bucket(3u), other.bucket(11u));
ASSERT_EQ(*other.begin(3u), 11u);
ASSERT_EQ(*++other.begin(3u), 3u);
}
TEST(DenseSet, Iterator) {
using iterator = typename entt::dense_set<int>::iterator;
static_assert(std::is_same_v<iterator::value_type, int>);
static_assert(std::is_same_v<iterator::pointer, const int *>);
static_assert(std::is_same_v<iterator::reference, const int &>);
entt::dense_set<int> set;
set.emplace(3);
iterator end{set.begin()};
iterator begin{};
begin = set.end();
std::swap(begin, end);
ASSERT_EQ(begin, set.begin());
ASSERT_EQ(end, set.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, set.begin());
ASSERT_EQ(begin--, set.end());
ASSERT_EQ(begin + 1, set.end());
ASSERT_EQ(end - 1, set.begin());
ASSERT_EQ(++begin, set.end());
ASSERT_EQ(--begin, set.begin());
ASSERT_EQ(begin += 1, set.end());
ASSERT_EQ(begin -= 1, set.begin());
ASSERT_EQ(begin + (end - begin), set.end());
ASSERT_EQ(begin - (begin - end), set.end());
ASSERT_EQ(end - (end - begin), set.begin());
ASSERT_EQ(end + (begin - end), set.begin());
ASSERT_EQ(begin[0u], *set.begin().operator->());
ASSERT_EQ(begin[0u], *set.begin());
ASSERT_LT(begin, end);
ASSERT_LE(begin, set.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, set.end());
set.emplace(42);
begin = set.begin();
ASSERT_EQ(begin[0u], 3);
ASSERT_EQ(begin[1u], 42);
}
TEST(DenseSet, ConstIterator) {
using iterator = typename entt::dense_set<int>::const_iterator;
static_assert(std::is_same_v<iterator::value_type, int>);
static_assert(std::is_same_v<iterator::pointer, const int *>);
static_assert(std::is_same_v<iterator::reference, const int &>);
entt::dense_set<int> set;
set.emplace(3);
iterator cend{set.cbegin()};
iterator cbegin{};
cbegin = set.cend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, set.cbegin());
ASSERT_EQ(cend, set.cend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, set.cbegin());
ASSERT_EQ(cbegin--, set.cend());
ASSERT_EQ(cbegin + 1, set.cend());
ASSERT_EQ(cend - 1, set.cbegin());
ASSERT_EQ(++cbegin, set.cend());
ASSERT_EQ(--cbegin, set.cbegin());
ASSERT_EQ(cbegin += 1, set.cend());
ASSERT_EQ(cbegin -= 1, set.cbegin());
ASSERT_EQ(cbegin + (cend - cbegin), set.cend());
ASSERT_EQ(cbegin - (cbegin - cend), set.cend());
ASSERT_EQ(cend - (cend - cbegin), set.cbegin());
ASSERT_EQ(cend + (cbegin - cend), set.cbegin());
ASSERT_EQ(cbegin[0u], *set.cbegin().operator->());
ASSERT_EQ(cbegin[0u], *set.cbegin());
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, set.cbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, set.cend());
set.emplace(42);
cbegin = set.cbegin();
ASSERT_EQ(cbegin[0u], 3);
ASSERT_EQ(cbegin[1u], 42);
}
TEST(DenseSet, IteratorConversion) {
entt::dense_set<int> set;
set.emplace(3);
typename entt::dense_set<int, int>::iterator it = set.begin();
typename entt::dense_set<int, int>::const_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), const int &>);
static_assert(std::is_same_v<decltype(*cit), const int &>);
ASSERT_EQ(*it, 3);
ASSERT_EQ(*it.operator->(), 3);
ASSERT_EQ(it.operator->(), cit.operator->());
ASSERT_EQ(*it, *cit);
ASSERT_EQ(it - cit, 0);
ASSERT_EQ(cit - it, 0);
ASSERT_LE(it, cit);
ASSERT_LE(cit, it);
ASSERT_GE(it, cit);
ASSERT_GE(cit, it);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(DenseSet, Insert) {
entt::dense_set<int> set;
typename entt::dense_set<int>::iterator it;
bool result;
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.find(0), set.end());
ASSERT_FALSE(set.contains(0));
int value{1};
std::tie(it, result) = set.insert(std::as_const(value));
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(1));
ASSERT_NE(set.find(1), set.end());
ASSERT_EQ(*it, 1);
std::tie(it, result) = set.insert(value);
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 1);
std::tie(it, result) = set.insert(3);
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(3));
ASSERT_NE(set.find(3), set.end());
ASSERT_EQ(*it, 3);
std::tie(it, result) = set.insert(3);
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 3);
int range[2u]{7, 9};
set.insert(std::begin(range), std::end(range));
ASSERT_EQ(set.size(), 4u);
ASSERT_TRUE(set.contains(7));
ASSERT_NE(set.find(9), set.end());
}
TEST(DenseSet, InsertRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_TRUE(set.insert(next).second);
}
ASSERT_EQ(set.size(), minimum_bucket_count);
ASSERT_GT(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_FALSE(set.contains(minimum_bucket_count));
ASSERT_TRUE(set.insert(minimum_bucket_count).second);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count * 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count));
for(std::size_t next{}; next <= minimum_bucket_count; ++next) {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
}
}
TEST(DenseSet, InsertSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_EQ(set.cbegin(next), set.cend(next));
}
ASSERT_TRUE(set.insert(1u).second);
ASSERT_TRUE(set.insert(9u).second);
ASSERT_EQ(set.size(), 2u);
ASSERT_TRUE(set.contains(1u));
ASSERT_NE(set.find(9u), set.end());
ASSERT_EQ(set.bucket(1u), 1u);
ASSERT_EQ(set.bucket(9u), 1u);
ASSERT_EQ(set.bucket_size(1u), 2u);
ASSERT_EQ(set.cbegin(6u), set.cend(6u));
}
TEST(DenseSet, Emplace) {
entt::dense_set<int> set;
typename entt::dense_set<int>::iterator it;
bool result;
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.find(0), set.end());
ASSERT_FALSE(set.contains(0));
std::tie(it, result) = set.emplace();
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(0));
ASSERT_NE(set.find(0), set.end());
ASSERT_EQ(*it, 0);
std::tie(it, result) = set.emplace();
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 0);
std::tie(it, result) = set.emplace(1);
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(1));
ASSERT_NE(set.find(1), set.end());
ASSERT_EQ(*it, 1);
std::tie(it, result) = set.emplace(1);
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 1);
}
TEST(DenseSet, EmplaceRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_TRUE(set.emplace(next).second);
ASSERT_LE(set.load_factor(), set.max_load_factor());
}
ASSERT_EQ(set.size(), minimum_bucket_count);
ASSERT_GT(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_FALSE(set.contains(minimum_bucket_count));
ASSERT_TRUE(set.emplace(minimum_bucket_count).second);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count * 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count));
for(std::size_t next{}; next <= minimum_bucket_count; ++next) {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
}
}
TEST(DenseSet, EmplaceSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_EQ(set.cbegin(next), set.cend(next));
}
ASSERT_TRUE(set.emplace(1u).second);
ASSERT_TRUE(set.emplace(9u).second);
ASSERT_EQ(set.size(), 2u);
ASSERT_TRUE(set.contains(1u));
ASSERT_NE(set.find(9u), set.end());
ASSERT_EQ(set.bucket(1u), 1u);
ASSERT_EQ(set.bucket(9u), 1u);
ASSERT_EQ(set.bucket_size(1u), 2u);
ASSERT_EQ(set.cbegin(6u), set.cend(6u));
}
TEST(DenseSet, Erase) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
set.emplace(next);
}
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
ASSERT_TRUE(set.contains(next));
}
auto it = set.erase(++set.begin());
it = set.erase(it, it + 1);
ASSERT_EQ(*--set.end(), 6u);
ASSERT_EQ(set.erase(6u), 1u);
ASSERT_EQ(set.erase(6u), 0u);
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u - 3u);
ASSERT_EQ(it, ++set.begin());
ASSERT_EQ(*it, 7u);
ASSERT_EQ(*--set.end(), 5u);
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
if(next == 1u || next == 8u || next == 6u) {
ASSERT_FALSE(set.contains(next));
ASSERT_EQ(set.bucket_size(next), 0u);
} else {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
ASSERT_EQ(set.bucket_size(next), 1u);
}
}
set.erase(set.begin(), set.end());
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
ASSERT_FALSE(set.contains(next));
}
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.size(), 0u);
}
TEST(DenseSet, EraseWithMovableKeyValue) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::string> set;
set.emplace("0");
set.emplace("1");
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_EQ(set.size(), 2u);
auto it = set.erase(set.find("0"));
ASSERT_EQ(*it, "1");
ASSERT_EQ(set.size(), 1u);
ASSERT_FALSE(set.contains("0"));
}
TEST(DenseSet, EraseFromBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_EQ(set.size(), 0u);
for(std::size_t next{}; next < 4u; ++next) {
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next).second);
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next + 2u).second);
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * (next + 1u) - 1u).second);
}
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 12u);
ASSERT_EQ(set.bucket_size(0u), 4u);
ASSERT_EQ(set.bucket_size(2u), 4u);
ASSERT_EQ(set.bucket_size(15u), 4u);
set.erase(set.end() - 3, set.end());
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 9u);
ASSERT_EQ(set.bucket_size(0u), 3u);
ASSERT_EQ(set.bucket_size(2u), 3u);
ASSERT_EQ(set.bucket_size(15u), 3u);
for(std::size_t next{}; next < 3u; ++next) {
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next));
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next), 0u);
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next + 2u));
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next + 2u), 2u);
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * (next + 1u) - 1u));
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * (next + 1u) - 1u), 15u);
}
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u + 2u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * (3u + 1u) - 1u));
set.erase(*++set.begin(0u));
set.erase(*++set.begin(2u));
set.erase(*++set.begin(15u));
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 6u);
ASSERT_EQ(set.bucket_size(0u), 2u);
ASSERT_EQ(set.bucket_size(2u), 2u);
ASSERT_EQ(set.bucket_size(15u), 2u);
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u + 2u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * (1u + 1u) - 1u));
while(set.begin(15) != set.end(15u)) {
set.erase(*set.begin(15));
}
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 4u);
ASSERT_EQ(set.bucket_size(0u), 2u);
ASSERT_EQ(set.bucket_size(2u), 2u);
ASSERT_EQ(set.bucket_size(15u), 0u);
ASSERT_TRUE(set.contains(0u * minimum_bucket_count));
ASSERT_TRUE(set.contains(0u * minimum_bucket_count + 2u));
ASSERT_TRUE(set.contains(4u * minimum_bucket_count));
ASSERT_TRUE(set.contains(4u * minimum_bucket_count + 2u));
set.erase(4u * minimum_bucket_count + 2u);
set.erase(0u * minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(set.bucket_size(0u), 1u);
ASSERT_EQ(set.bucket_size(2u), 1u);
ASSERT_EQ(set.bucket_size(15u), 0u);
ASSERT_FALSE(set.contains(0u * minimum_bucket_count));
ASSERT_TRUE(set.contains(0u * minimum_bucket_count + 2u));
ASSERT_TRUE(set.contains(4u * minimum_bucket_count));
ASSERT_FALSE(set.contains(4u * minimum_bucket_count + 2u));
}
TEST(DenseSet, Swap) {
entt::dense_set<int> set;
entt::dense_set<int> other;
set.emplace(0);
ASSERT_FALSE(set.empty());
ASSERT_TRUE(other.empty());
ASSERT_TRUE(set.contains(0));
ASSERT_FALSE(other.contains(0));
set.swap(other);
ASSERT_TRUE(set.empty());
ASSERT_FALSE(other.empty());
ASSERT_FALSE(set.contains(0));
ASSERT_TRUE(other.contains(0));
}
TEST(DenseSet, EqualRange) {
entt::dense_set<int, entt::identity, transparent_equal_to> set;
const auto &cset = set;
set.emplace(42);
ASSERT_EQ(set.equal_range(0).first, set.end());
ASSERT_EQ(set.equal_range(0).second, set.end());
ASSERT_EQ(cset.equal_range(0).first, cset.cend());
ASSERT_EQ(cset.equal_range(0).second, cset.cend());
ASSERT_EQ(set.equal_range(0.0).first, set.end());
ASSERT_EQ(set.equal_range(0.0).second, set.end());
ASSERT_EQ(cset.equal_range(0.0).first, cset.cend());
ASSERT_EQ(cset.equal_range(0.0).second, cset.cend());
ASSERT_NE(set.equal_range(42).first, set.end());
ASSERT_EQ(*set.equal_range(42).first, 42);
ASSERT_EQ(set.equal_range(42).second, set.end());
ASSERT_NE(cset.equal_range(42).first, cset.cend());
ASSERT_EQ(*cset.equal_range(42).first, 42);
ASSERT_EQ(cset.equal_range(42).second, cset.cend());
ASSERT_NE(set.equal_range(42.0).first, set.end());
ASSERT_EQ(*set.equal_range(42.0).first, 42);
ASSERT_EQ(set.equal_range(42.0).second, set.end());
ASSERT_NE(cset.equal_range(42.0).first, cset.cend());
ASSERT_EQ(*cset.equal_range(42.0).first, 42);
ASSERT_EQ(cset.equal_range(42.0).second, cset.cend());
}
TEST(DenseSet, LocalIterator) {
using iterator = typename entt::dense_set<std::size_t, entt::identity>::local_iterator;
static_assert(std::is_same_v<iterator::value_type, std::size_t>);
static_assert(std::is_same_v<iterator::pointer, const std::size_t *>);
static_assert(std::is_same_v<iterator::reference, const std::size_t &>);
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(3u);
set.emplace(3u + minimum_bucket_count);
iterator end{set.begin(3u)};
iterator begin{};
begin = set.end(3u);
std::swap(begin, end);
ASSERT_EQ(begin, set.begin(3u));
ASSERT_EQ(end, set.end(3u));
ASSERT_NE(begin, end);
ASSERT_EQ(*begin.operator->(), 3u + minimum_bucket_count);
ASSERT_EQ(*begin, 3u + minimum_bucket_count);
ASSERT_EQ(begin++, set.begin(3u));
ASSERT_EQ(++begin, set.end(3u));
}
TEST(DenseSet, ConstLocalIterator) {
using iterator = typename entt::dense_set<std::size_t, entt::identity>::const_local_iterator;
static_assert(std::is_same_v<iterator::value_type, std::size_t>);
static_assert(std::is_same_v<iterator::pointer, const std::size_t *>);
static_assert(std::is_same_v<iterator::reference, const std::size_t &>);
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(3u);
set.emplace(3u + minimum_bucket_count);
iterator cend{set.begin(3u)};
iterator cbegin{};
cbegin = set.end(3u);
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, set.begin(3u));
ASSERT_EQ(cend, set.end(3u));
ASSERT_NE(cbegin, cend);
ASSERT_EQ(*cbegin.operator->(), 3u + minimum_bucket_count);
ASSERT_EQ(*cbegin, 3u + minimum_bucket_count);
ASSERT_EQ(cbegin++, set.begin(3u));
ASSERT_EQ(++cbegin, set.end(3u));
}
TEST(DenseSet, LocalIteratorConversion) {
entt::dense_set<int> set;
set.emplace(3);
typename entt::dense_set<int>::local_iterator it = set.begin(set.bucket(3));
typename entt::dense_set<int>::const_local_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), const int &>);
static_assert(std::is_same_v<decltype(*cit), const int &>);
ASSERT_EQ(*it, 3);
ASSERT_EQ(*it.operator->(), 3);
ASSERT_EQ(it.operator->(), cit.operator->());
ASSERT_EQ(*it, *cit);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(DenseSet, Rehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(32u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
set.rehash(12u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
set.rehash(44u);
ASSERT_EQ(set.bucket_count(), 8u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 32u);
set.rehash(0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
set.emplace(next);
}
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
set.rehash(0u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
set.rehash(55u);
ASSERT_EQ(set.bucket_count(), 8u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
set.rehash(2u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
}
ASSERT_EQ(set.bucket_size(0u), 2u);
ASSERT_EQ(set.bucket_size(3u), 1u);
ASSERT_EQ(*set.begin(0u), 0u);
ASSERT_EQ(*++set.begin(0u), 32u);
set.clear();
set.rehash(2u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_FALSE(set.contains(32u));
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_FALSE(set.contains(next));
}
ASSERT_EQ(set.bucket_size(0u), 0u);
ASSERT_EQ(set.bucket_size(3u), 0u);
}
TEST(DenseSet, Reserve) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<int> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
set.reserve(0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
set.reserve(minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), entt::next_power_of_two(std::ceil(minimum_bucket_count / set.max_load_factor())));
}
TEST(DenseSet, ThrowingAllocator) {
using allocator = test::throwing_allocator<std::size_t>;
using packed_allocator = test::throwing_allocator<std::pair<std::size_t, std::size_t>>;
using packed_exception = typename packed_allocator::exception_type;
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, std::hash<std::size_t>, std::equal_to<std::size_t>, allocator> set{};
packed_allocator::trigger_on_allocate = true;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_THROW(set.reserve(2u * set.bucket_count()), packed_exception);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(set.emplace(), packed_exception);
ASSERT_FALSE(set.contains(0u));
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(set.emplace(std::size_t{}), packed_exception);
ASSERT_FALSE(set.contains(0u));
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(set.insert(0u), packed_exception);
ASSERT_FALSE(set.contains(0u));
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
TEST(DenseSet, NoUsesAllocatorConstruction) {
using allocator = std::pmr::polymorphic_allocator<int>;
test::tracked_memory_resource memory_resource{};
entt::dense_set<int, std::hash<int>, std::equal_to<int>, allocator> set{&memory_resource};
set.reserve(1u);
memory_resource.reset();
set.emplace(0);
ASSERT_TRUE(set.get_allocator().resource()->is_equal(memory_resource));
ASSERT_EQ(memory_resource.do_allocate_counter(), 0u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
TEST(DenseSet, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
using allocator = std::pmr::polymorphic_allocator<string_type>;
test::tracked_memory_resource memory_resource{};
entt::dense_set<string_type, std::hash<string_type>, std::equal_to<string_type>, allocator> set{&memory_resource};
set.reserve(1u);
memory_resource.reset();
set.emplace(test::tracked_memory_resource::default_value);
ASSERT_TRUE(set.get_allocator().resource()->is_equal(memory_resource));
ASSERT_GT(memory_resource.do_allocate_counter(), 0u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
#endif

View File

@ -0,0 +1,98 @@
#include <array>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/algorithm.hpp>
struct boxed_int {
int value;
};
TEST(Algorithm, StdSort) {
// well, I'm pretty sure it works, it's std::sort!!
std::array<int, 5> arr{{4, 1, 3, 2, 0}};
entt::std_sort sort;
sort(arr.begin(), arr.end());
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_LT(arr[i], arr[i + 1u]);
}
}
TEST(Algorithm, StdSortBoxedInt) {
// well, I'm pretty sure it works, it's std::sort!!
std::array<boxed_int, 6> arr{{{4}, {1}, {3}, {2}, {0}, {6}}};
entt::std_sort sort;
sort(arr.begin(), arr.end(), [](const auto &lhs, const auto &rhs) {
return lhs.value > rhs.value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_GT(arr[i].value, arr[i + 1u].value);
}
}
TEST(Algorithm, InsertionSort) {
std::array<int, 5> arr{{4, 1, 3, 2, 0}};
entt::insertion_sort sort;
sort(arr.begin(), arr.end());
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_LT(arr[i], arr[i + 1u]);
}
}
TEST(Algorithm, InsertionSortBoxedInt) {
std::array<boxed_int, 6> arr{{{4}, {1}, {3}, {2}, {0}, {6}}};
entt::insertion_sort sort;
sort(arr.begin(), arr.end(), [](const auto &lhs, const auto &rhs) {
return lhs.value > rhs.value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_GT(arr[i].value, arr[i + 1u].value);
}
}
TEST(Algorithm, InsertionSortEmptyContainer) {
std::vector<int> vec{};
entt::insertion_sort sort;
// this should crash with asan enabled if we break the constraint
sort(vec.begin(), vec.end());
}
TEST(Algorithm, RadixSort) {
std::array<uint32_t, 5> arr{{4, 1, 3, 2, 0}};
entt::radix_sort<8, 32> sort;
sort(arr.begin(), arr.end(), [](const auto &value) {
return value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_LT(arr[i], arr[i + 1u]);
}
}
TEST(Algorithm, RadixSortBoxedInt) {
std::array<boxed_int, 6> arr{{{4}, {1}, {3}, {2}, {0}, {6}}};
entt::radix_sort<2, 6> sort;
sort(arr.rbegin(), arr.rend(), [](const auto &instance) {
return instance.value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_GT(arr[i].value, arr[i + 1u].value);
}
}
TEST(Algorithm, RadixSortEmptyContainer) {
std::vector<int> vec{};
entt::radix_sort<8, 32> sort;
// this should crash with asan enabled if we break the constraint
sort(vec.begin(), vec.end());
}

1466
test/entt/core/any.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/compressed_pair.hpp>
struct empty_type {};
struct move_only_type {
move_only_type()
: value{new int{99}} {}
move_only_type(int v)
: value{new int{v}} {}
~move_only_type() {
delete value;
}
move_only_type(const move_only_type &) = delete;
move_only_type &operator=(const move_only_type &) = delete;
move_only_type(move_only_type &&other) noexcept
: value{std::exchange(other.value, nullptr)} {}
move_only_type &operator=(move_only_type &&other) noexcept {
delete value;
value = std::exchange(other.value, nullptr);
return *this;
}
int *value;
};
struct non_default_constructible {
non_default_constructible(int v)
: value{v} {}
int value;
};
TEST(CompressedPair, Size) {
struct local {
int value;
empty_type empty;
};
static_assert(sizeof(entt::compressed_pair<int, int>) == sizeof(int[2u]));
static_assert(sizeof(entt::compressed_pair<empty_type, int>) == sizeof(int));
static_assert(sizeof(entt::compressed_pair<int, empty_type>) == sizeof(int));
static_assert(sizeof(entt::compressed_pair<int, empty_type>) < sizeof(local));
static_assert(sizeof(entt::compressed_pair<int, empty_type>) < sizeof(std::pair<int, empty_type>));
}
TEST(CompressedPair, ConstructCopyMove) {
static_assert(!std::is_default_constructible_v<entt::compressed_pair<non_default_constructible, empty_type>>);
static_assert(std::is_default_constructible_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_copy_constructible_v<entt::compressed_pair<non_default_constructible, empty_type>>);
static_assert(!std::is_copy_constructible_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_copy_assignable_v<entt::compressed_pair<non_default_constructible, empty_type>>);
static_assert(!std::is_copy_assignable_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_move_constructible_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_move_assignable_v<entt::compressed_pair<move_only_type, empty_type>>);
entt::compressed_pair copyable{non_default_constructible{42}, empty_type{}};
auto by_copy{copyable};
ASSERT_EQ(by_copy.first().value, 42);
by_copy.first().value = 3;
copyable = by_copy;
ASSERT_EQ(copyable.first().value, 3);
entt::compressed_pair<empty_type, move_only_type> movable{};
auto by_move{std::move(movable)};
ASSERT_EQ(*by_move.second().value, 99);
ASSERT_EQ(movable.second().value, nullptr);
*by_move.second().value = 3;
movable = std::move(by_move);
ASSERT_EQ(*movable.second().value, 3);
ASSERT_EQ(by_move.second().value, nullptr);
}
TEST(CompressedPair, PiecewiseConstruct) {
std::vector<int> vec{42};
entt::compressed_pair<empty_type, empty_type> empty{std::piecewise_construct, std::make_tuple(), std::make_tuple()};
entt::compressed_pair<std::vector<int>, std::size_t> pair{std::piecewise_construct, std::forward_as_tuple(std::move(vec)), std::make_tuple(sizeof(empty))};
ASSERT_EQ(pair.first().size(), 1u);
ASSERT_EQ(pair.second(), sizeof(empty));
ASSERT_EQ(vec.size(), 0u);
}
TEST(CompressedPair, DeductionGuide) {
int value = 42;
empty_type empty{};
entt::compressed_pair pair{value, 3};
static_assert(std::is_same_v<decltype(entt::compressed_pair{empty_type{}, empty}), entt::compressed_pair<empty_type, empty_type>>);
ASSERT_TRUE((std::is_same_v<decltype(pair), entt::compressed_pair<int, int>>));
ASSERT_EQ(pair.first(), 42);
ASSERT_EQ(pair.second(), 3);
}
TEST(CompressedPair, Getters) {
entt::compressed_pair pair{3, empty_type{}};
const auto &cpair = pair;
static_assert(std::is_same_v<decltype(pair.first()), int &>);
static_assert(std::is_same_v<decltype(pair.second()), empty_type &>);
static_assert(std::is_same_v<decltype(cpair.first()), const int &>);
static_assert(std::is_same_v<decltype(cpair.second()), const empty_type &>);
ASSERT_EQ(pair.first(), cpair.first());
ASSERT_EQ(&pair.second(), &cpair.second());
}
TEST(CompressedPair, Swap) {
entt::compressed_pair pair{1, 2};
entt::compressed_pair other{3, 4};
swap(pair, other);
ASSERT_EQ(pair.first(), 3);
ASSERT_EQ(pair.second(), 4);
ASSERT_EQ(other.first(), 1);
ASSERT_EQ(other.second(), 2);
pair.swap(other);
ASSERT_EQ(pair.first(), 1);
ASSERT_EQ(pair.second(), 2);
ASSERT_EQ(other.first(), 3);
ASSERT_EQ(other.second(), 4);
}
TEST(CompressedPair, Get) {
entt::compressed_pair pair{1, 2};
ASSERT_EQ(pair.get<0>(), 1);
ASSERT_EQ(pair.get<1>(), 2);
ASSERT_EQ(&pair.get<0>(), &pair.first());
ASSERT_EQ(&pair.get<1>(), &pair.second());
auto &&[first, second] = pair;
ASSERT_EQ(first, 1);
ASSERT_EQ(second, 2);
first = 3;
second = 4;
ASSERT_EQ(pair.first(), 3);
ASSERT_EQ(pair.second(), 4);
auto &[cfirst, csecond] = std::as_const(pair);
ASSERT_EQ(cfirst, 3);
ASSERT_EQ(csecond, 4);
static_assert(std::is_same_v<decltype(cfirst), const int>);
static_assert(std::is_same_v<decltype(csecond), const int>);
auto [tfirst, tsecond] = entt::compressed_pair{9, 99};
ASSERT_EQ(tfirst, 9);
ASSERT_EQ(tsecond, 99);
static_assert(std::is_same_v<decltype(cfirst), const int>);
static_assert(std::is_same_v<decltype(csecond), const int>);
}

72
test/entt/core/enum.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <cstdint>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/enum.hpp>
enum class detected {
foo = 0x01,
bar = 0x02,
quux = 0x04,
_entt_enum_as_bitmask
};
// small type on purpose
enum class registered : std::uint8_t {
foo = 0x01,
bar = 0x02,
quux = 0x04
};
template<>
struct entt::enum_as_bitmask<registered>
: std::true_type {};
template<typename Type>
struct Enum: testing::Test {
using type = Type;
};
using EnumTypes = ::testing::Types<detected, registered>;
TYPED_TEST_SUITE(Enum, EnumTypes, );
TYPED_TEST(Enum, Functionalities) {
using enum_type = typename TestFixture::type;
ASSERT_TRUE(!!((enum_type::foo | enum_type::bar) & enum_type::foo));
ASSERT_TRUE(!!((enum_type::foo | enum_type::bar) & enum_type::bar));
ASSERT_TRUE(!((enum_type::foo | enum_type::bar) & enum_type::quux));
ASSERT_TRUE(!!((enum_type::foo ^ enum_type::bar) & enum_type::foo));
ASSERT_TRUE(!((enum_type::foo ^ enum_type::foo) & enum_type::foo));
ASSERT_TRUE(!(~enum_type::foo & enum_type::foo));
ASSERT_TRUE(!!(~enum_type::foo & enum_type::bar));
ASSERT_TRUE(enum_type::foo == enum_type::foo);
ASSERT_TRUE(enum_type::foo != enum_type::bar);
enum_type value = enum_type::foo;
ASSERT_TRUE(!!(value & enum_type::foo));
ASSERT_TRUE(!(value & enum_type::bar));
ASSERT_TRUE(!(value & enum_type::quux));
value |= (enum_type::bar | enum_type::quux);
ASSERT_TRUE(!!(value & enum_type::foo));
ASSERT_TRUE(!!(value & enum_type::bar));
ASSERT_TRUE(!!(value & enum_type::quux));
value &= (enum_type::bar | enum_type::quux);
ASSERT_TRUE(!(value & enum_type::foo));
ASSERT_TRUE(!!(value & enum_type::bar));
ASSERT_TRUE(!!(value & enum_type::quux));
value ^= enum_type::bar;
ASSERT_TRUE(!(value & enum_type::foo));
ASSERT_TRUE(!(value & enum_type::bar));
ASSERT_TRUE(!!(value & enum_type::quux));
}

22
test/entt/core/family.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <gtest/gtest.h>
#include <entt/core/family.hpp>
using a_family = entt::family<struct a_family_type>;
using another_family = entt::family<struct another_family_type>;
TEST(Family, Functionalities) {
auto t1 = a_family::value<int>;
auto t2 = a_family::value<int>;
auto t3 = a_family::value<char>;
auto t4 = another_family::value<double>;
ASSERT_EQ(t1, t2);
ASSERT_NE(t1, t3);
ASSERT_EQ(t1, t4);
}
TEST(Family, Uniqueness) {
ASSERT_NE(a_family::value<int>, a_family::value<int &>);
ASSERT_NE(a_family::value<int>, a_family::value<int &&>);
ASSERT_NE(a_family::value<int>, a_family::value<const int &>);
}

View File

@ -0,0 +1,224 @@
#include <cstdint>
#include <string>
#include <string_view>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
template<typename>
struct foobar_t;
template<>
struct foobar_t<std::uint32_t> {
static constexpr auto value = 0xbf9cf968;
};
template<>
struct foobar_t<std::uint64_t> {
static constexpr auto value = 0x85944171f73967e8;
};
inline constexpr auto foobar_v = foobar_t<entt::id_type>::value;
TEST(BasicHashedString, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_hashed_string{"foo"}), entt::hashed_string>);
static_assert(std::is_same_v<decltype(entt::basic_hashed_string{L"foo"}), entt::hashed_wstring>);
}
TEST(HashedString, Functionalities) {
using namespace entt::literals;
using hash_type = entt::hashed_string::hash_type;
const char *bar = "bar";
auto foo_hs = entt::hashed_string{"foo"};
auto bar_hs = entt::hashed_string{bar};
ASSERT_NE(static_cast<hash_type>(foo_hs), static_cast<hash_type>(bar_hs));
ASSERT_STREQ(static_cast<const char *>(foo_hs), "foo");
ASSERT_STREQ(static_cast<const char *>(bar_hs), bar);
ASSERT_STREQ(foo_hs.data(), "foo");
ASSERT_STREQ(bar_hs.data(), bar);
ASSERT_EQ(foo_hs.size(), 3u);
ASSERT_EQ(bar_hs.size(), 3u);
ASSERT_EQ(foo_hs, foo_hs);
ASSERT_NE(foo_hs, bar_hs);
entt::hashed_string hs{"foobar"};
ASSERT_EQ(static_cast<hash_type>(hs), foobar_v);
ASSERT_EQ(hs.value(), foobar_v);
ASSERT_EQ(foo_hs, "foo"_hs);
ASSERT_NE(bar_hs, "foo"_hs);
entt::hashed_string empty_hs{};
ASSERT_EQ(empty_hs, entt::hashed_string{});
ASSERT_NE(empty_hs, foo_hs);
empty_hs = foo_hs;
ASSERT_NE(empty_hs, entt::hashed_string{});
ASSERT_EQ(empty_hs, foo_hs);
}
TEST(HashedString, Empty) {
using hash_type = entt::hashed_string::hash_type;
entt::hashed_string hs{};
ASSERT_EQ(hs.size(), 0u);
ASSERT_EQ(static_cast<hash_type>(hs), hash_type{});
ASSERT_EQ(static_cast<const char *>(hs), nullptr);
}
TEST(HashedString, Correctness) {
const char *foobar = "foobar";
std::string_view view{"foobar__", 6};
ASSERT_EQ(entt::hashed_string{foobar}, foobar_v);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), foobar_v);
ASSERT_EQ(entt::hashed_string{"foobar"}, foobar_v);
ASSERT_EQ(entt::hashed_string::value(foobar), foobar_v);
ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), foobar_v);
ASSERT_EQ(entt::hashed_string::value("foobar"), foobar_v);
ASSERT_EQ(entt::hashed_string{foobar}.size(), 6u);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}).size(), 6u);
ASSERT_EQ(entt::hashed_string{"foobar"}.size(), 6u);
}
TEST(HashedString, Order) {
using namespace entt::literals;
const entt::hashed_string lhs = "foo"_hs;
const entt::hashed_string rhs = "bar"_hs;
ASSERT_FALSE(lhs < lhs);
ASSERT_FALSE(rhs < rhs);
ASSERT_LT(rhs, lhs);
ASSERT_LE(rhs, lhs);
ASSERT_GT(lhs, rhs);
ASSERT_GE(lhs, rhs);
}
TEST(HashedString, Constexprness) {
using namespace entt::literals;
constexpr std::string_view view{"foobar__", 6};
static_assert(entt::hashed_string{"quux"} == "quux"_hs);
static_assert(entt::hashed_string{"foobar"} == foobar_v);
static_assert(entt::hashed_string::value("quux") == "quux"_hs);
static_assert(entt::hashed_string::value("foobar") == foobar_v);
static_assert(entt::hashed_string{"quux", 4} == "quux"_hs);
static_assert(entt::hashed_string{view.data(), view.size()} == foobar_v);
static_assert(entt::hashed_string::value("quux", 4) == "quux"_hs);
static_assert(entt::hashed_string::value(view.data(), view.size()) == foobar_v);
static_assert(entt::hashed_string{"bar"} < "foo"_hs);
static_assert(entt::hashed_string{"bar"} <= "bar"_hs);
static_assert(entt::hashed_string{"foo"} > "bar"_hs);
static_assert(entt::hashed_string{"foo"} >= "foo"_hs);
}
TEST(HashedWString, Functionalities) {
using namespace entt::literals;
using hash_type = entt::hashed_wstring::hash_type;
const wchar_t *bar = L"bar";
auto foo_hws = entt::hashed_wstring{L"foo"};
auto bar_hws = entt::hashed_wstring{bar};
ASSERT_NE(static_cast<hash_type>(foo_hws), static_cast<hash_type>(bar_hws));
ASSERT_STREQ(static_cast<const wchar_t *>(foo_hws), L"foo");
ASSERT_STREQ(static_cast<const wchar_t *>(bar_hws), bar);
ASSERT_STREQ(foo_hws.data(), L"foo");
ASSERT_STREQ(bar_hws.data(), bar);
ASSERT_EQ(foo_hws.size(), 3u);
ASSERT_EQ(bar_hws.size(), 3u);
ASSERT_EQ(foo_hws, foo_hws);
ASSERT_NE(foo_hws, bar_hws);
entt::hashed_wstring hws{L"foobar"};
ASSERT_EQ(static_cast<hash_type>(hws), foobar_v);
ASSERT_EQ(hws.value(), foobar_v);
ASSERT_EQ(foo_hws, L"foo"_hws);
ASSERT_NE(bar_hws, L"foo"_hws);
}
TEST(HashedWString, Empty) {
using hash_type = entt::hashed_wstring::hash_type;
entt::hashed_wstring hws{};
ASSERT_EQ(hws.size(), 0u);
ASSERT_EQ(static_cast<hash_type>(hws), hash_type{});
ASSERT_EQ(static_cast<const wchar_t *>(hws), nullptr);
}
TEST(HashedWString, Correctness) {
const wchar_t *foobar = L"foobar";
std::wstring_view view{L"foobar__", 6};
ASSERT_EQ(entt::hashed_wstring{foobar}, foobar_v);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), foobar_v);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}, foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(foobar), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), foobar_v);
ASSERT_EQ(entt::hashed_wstring{foobar}.size(), 6u);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}).size(), 6u);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}.size(), 6u);
}
TEST(HashedWString, Order) {
using namespace entt::literals;
const entt::hashed_wstring lhs = L"foo"_hws;
const entt::hashed_wstring rhs = L"bar"_hws;
ASSERT_FALSE(lhs < lhs);
ASSERT_FALSE(rhs < rhs);
ASSERT_LT(rhs, lhs);
ASSERT_LE(rhs, lhs);
ASSERT_GT(lhs, rhs);
ASSERT_GE(lhs, rhs);
}
TEST(HashedWString, Constexprness) {
using namespace entt::literals;
constexpr std::wstring_view view{L"foobar__", 6};
static_assert(entt::hashed_wstring{L"quux"} == L"quux"_hws);
static_assert(entt::hashed_wstring{L"foobar"} == foobar_v);
static_assert(entt::hashed_wstring::value(L"quux") == L"quux"_hws);
static_assert(entt::hashed_wstring::value(L"foobar") == foobar_v);
static_assert(entt::hashed_wstring{L"quux", 4} == L"quux"_hws);
static_assert(entt::hashed_wstring{view.data(), view.size()} == foobar_v);
static_assert(entt::hashed_wstring::value(L"quux", 4) == L"quux"_hws);
static_assert(entt::hashed_wstring::value(view.data(), view.size()) == foobar_v);
static_assert(entt::hashed_wstring{L"bar"} < L"foo"_hws);
static_assert(entt::hashed_wstring{L"bar"} <= L"bar"_hws);
static_assert(entt::hashed_wstring{L"foo"} > L"bar"_hws);
static_assert(entt::hashed_wstring{L"foo"} >= L"foo"_hws);
}

31
test/entt/core/ident.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/ident.hpp>
struct a_type {};
struct another_type {};
TEST(Ident, Uniqueness) {
using id = entt::ident<a_type, another_type>;
constexpr a_type an_instance;
constexpr another_type another_instance;
ASSERT_NE(id::value<a_type>, id::value<another_type>);
ASSERT_EQ(id::value<a_type>, id::value<decltype(an_instance)>);
ASSERT_NE(id::value<a_type>, id::value<decltype(another_instance)>);
ASSERT_EQ(id::value<a_type>, id::value<a_type>);
ASSERT_EQ(id::value<another_type>, id::value<another_type>);
// test uses in constant expressions
switch(id::value<another_type>) {
case id::value<a_type>:
FAIL();
case id::value<another_type>:
SUCCEED();
}
}
TEST(Identifier, SingleType) {
using id = entt::ident<a_type>;
[[maybe_unused]] std::integral_constant<id::value_type, id::value<a_type>> ic;
}

View File

@ -0,0 +1,55 @@
#include <cstddef>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/iterator.hpp>
struct clazz {
int value{0};
};
TEST(InputIteratorPointer, Functionalities) {
clazz instance{};
entt::input_iterator_pointer ptr{std::move(instance)};
ptr->value = 42;
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(ptr->value, 42);
ASSERT_EQ(ptr->value, (*ptr).value);
ASSERT_EQ(ptr.operator->(), &ptr.operator*());
}
TEST(IotaIterator, Functionalities) {
entt::iota_iterator<std::size_t> first{};
const entt::iota_iterator<std::size_t> last{2u};
ASSERT_NE(first, last);
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_EQ(*first++, 0u);
ASSERT_EQ(*first, 1u);
ASSERT_EQ(*++first, *last);
ASSERT_EQ(*first, 2u);
}
TEST(IterableAdaptor, Functionalities) {
std::vector<int> vec{1, 2};
entt::iterable_adaptor iterable{vec.begin(), vec.end()};
decltype(iterable) other{};
ASSERT_NO_FATAL_FAILURE(other = iterable);
ASSERT_NO_FATAL_FAILURE(std::swap(other, iterable));
ASSERT_EQ(iterable.begin(), vec.begin());
ASSERT_EQ(iterable.end(), vec.end());
ASSERT_EQ(*iterable.cbegin(), 1);
ASSERT_EQ(*++iterable.cbegin(), 2);
ASSERT_EQ(++iterable.cbegin(), --iterable.end());
for(auto value: entt::iterable_adaptor<const int *, const void *>{vec.data(), vec.data() + 1u}) {
ASSERT_EQ(value, 1);
}
}

241
test/entt/core/memory.cpp Normal file
View File

@ -0,0 +1,241 @@
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/memory.hpp>
#include "../common/basic_test_allocator.hpp"
#include "../common/config.h"
#include "../common/throwing_allocator.hpp"
#include "../common/throwing_type.hpp"
#include "../common/tracked_memory_resource.hpp"
TEST(ToAddress, Functionalities) {
std::shared_ptr<int> shared = std::make_shared<int>();
auto *plain = std::addressof(*shared);
ASSERT_EQ(entt::to_address(shared), plain);
ASSERT_EQ(entt::to_address(plain), plain);
}
TEST(PoccaPocmaAndPocs, Functionalities) {
test::basic_test_allocator<int> lhs, rhs;
// honestly, I don't even know how one is supposed to test such a thing :)
entt::propagate_on_container_copy_assignment(lhs, rhs);
entt::propagate_on_container_move_assignment(lhs, rhs);
entt::propagate_on_container_swap(lhs, rhs);
}
ENTT_DEBUG_TEST(PoccaPocmaAndPocsDeathTest, Functionalities) {
using pocs = std::false_type;
test::basic_test_allocator<int, pocs> lhs, rhs;
ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), "");
}
TEST(IsPowerOfTwo, Functionalities) {
// constexpr-ness guaranteed
constexpr auto zero_is_power_of_two = entt::is_power_of_two(0u);
ASSERT_FALSE(zero_is_power_of_two);
ASSERT_TRUE(entt::is_power_of_two(1u));
ASSERT_TRUE(entt::is_power_of_two(2u));
ASSERT_TRUE(entt::is_power_of_two(4u));
ASSERT_FALSE(entt::is_power_of_two(7u));
ASSERT_TRUE(entt::is_power_of_two(128u));
ASSERT_FALSE(entt::is_power_of_two(200u));
}
TEST(NextPowerOfTwo, Functionalities) {
// constexpr-ness guaranteed
constexpr auto next_power_of_two_of_zero = entt::next_power_of_two(0u);
ASSERT_EQ(next_power_of_two_of_zero, 1u);
ASSERT_EQ(entt::next_power_of_two(1u), 1u);
ASSERT_EQ(entt::next_power_of_two(2u), 2u);
ASSERT_EQ(entt::next_power_of_two(3u), 4u);
ASSERT_EQ(entt::next_power_of_two(17u), 32u);
ASSERT_EQ(entt::next_power_of_two(32u), 32u);
ASSERT_EQ(entt::next_power_of_two(33u), 64u);
ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16)), std::pow(2, 16));
ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16) + 1u), std::pow(2, 17));
}
ENTT_DEBUG_TEST(NextPowerOfTwoDeathTest, Functionalities) {
ASSERT_DEATH(static_cast<void>(entt::next_power_of_two((std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)) + 1)), "");
}
TEST(FastMod, Functionalities) {
// constexpr-ness guaranteed
constexpr auto fast_mod_of_zero = entt::fast_mod(0u, 8u);
ASSERT_EQ(fast_mod_of_zero, 0u);
ASSERT_EQ(entt::fast_mod(7u, 8u), 7u);
ASSERT_EQ(entt::fast_mod(8u, 8u), 0u);
}
TEST(AllocateUnique, Functionalities) {
test::throwing_allocator<test::throwing_type> allocator{};
test::throwing_allocator<test::throwing_type>::trigger_on_allocate = true;
test::throwing_type::trigger_on_value = 0;
ASSERT_THROW((entt::allocate_unique<test::throwing_type>(allocator, 0)), test::throwing_allocator<test::throwing_type>::exception_type);
ASSERT_THROW((entt::allocate_unique<test::throwing_type>(allocator, test::throwing_type{0})), test::throwing_type::exception_type);
std::unique_ptr<test::throwing_type, entt::allocation_deleter<test::throwing_allocator<test::throwing_type>>> ptr = entt::allocate_unique<test::throwing_type>(allocator, 42);
ASSERT_TRUE(ptr);
ASSERT_EQ(*ptr, 42);
ptr.reset();
ASSERT_FALSE(ptr);
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
TEST(AllocateUnique, NoUsesAllocatorConstruction) {
test::tracked_memory_resource memory_resource{};
std::pmr::polymorphic_allocator<int> allocator{&memory_resource};
using type = std::unique_ptr<int, entt::allocation_deleter<std::pmr::polymorphic_allocator<int>>>;
type ptr = entt::allocate_unique<int>(allocator, 0);
ASSERT_EQ(memory_resource.do_allocate_counter(), 1u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
TEST(AllocateUnique, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
test::tracked_memory_resource memory_resource{};
std::pmr::polymorphic_allocator<string_type> allocator{&memory_resource};
using type = std::unique_ptr<string_type, entt::allocation_deleter<std::pmr::polymorphic_allocator<string_type>>>;
type ptr = entt::allocate_unique<string_type>(allocator, test::tracked_memory_resource::default_value);
ASSERT_GT(memory_resource.do_allocate_counter(), 1u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
#endif
TEST(UsesAllocatorConstructionArgs, NoUsesAllocatorConstruction) {
const auto value = 42;
const auto args = entt::uses_allocator_construction_args<int>(std::allocator<int>{}, value);
static_assert(std::tuple_size_v<decltype(args)> == 1u);
static_assert(std::is_same_v<decltype(args), const std::tuple<const int &>>);
ASSERT_EQ(std::get<0>(args), value);
}
TEST(UsesAllocatorConstructionArgs, LeadingAllocatorConvention) {
const auto value = 42;
const auto args = entt::uses_allocator_construction_args<std::tuple<int, char>>(std::allocator<int>{}, value, 'c');
static_assert(std::tuple_size_v<decltype(args)> == 4u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::allocator_arg_t, const std::allocator<int> &, const int &, char &&>>);
ASSERT_EQ(std::get<2>(args), value);
}
TEST(UsesAllocatorConstructionArgs, TrailingAllocatorConvention) {
const auto size = 42u;
const auto args = entt::uses_allocator_construction_args<std::vector<int>>(std::allocator<int>{}, size);
static_assert(std::tuple_size_v<decltype(args)> == 2u);
static_assert(std::is_same_v<decltype(args), const std::tuple<const unsigned int &, const std::allocator<int> &>>);
ASSERT_EQ(std::get<0>(args), size);
}
TEST(UsesAllocatorConstructionArgs, PairPiecewiseConstruct) {
const auto size = 42u;
const auto tup = std::make_tuple(size);
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, std::piecewise_construct, std::make_tuple(3), tup);
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<int &&>, std::tuple<const unsigned int &, const std::allocator<int> &>>>);
ASSERT_EQ(std::get<0>(std::get<2>(args)), size);
}
TEST(UsesAllocatorConstructionArgs, PairNoArgs) {
[[maybe_unused]] const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{});
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<>, std::tuple<const std::allocator<int> &>>>);
}
TEST(UsesAllocatorConstructionArgs, PairValues) {
const auto size = 42u;
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, 3, size);
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<int &&>, std::tuple<const unsigned int &, const std::allocator<int> &>>>);
ASSERT_EQ(std::get<0>(std::get<2>(args)), size);
}
TEST(UsesAllocatorConstructionArgs, PairConstLValueReference) {
const auto value = std::make_pair(3, 42u);
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, value);
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<const int &>, std::tuple<const unsigned int &, const std::allocator<int> &>>>);
ASSERT_EQ(std::get<0>(std::get<1>(args)), 3);
ASSERT_EQ(std::get<0>(std::get<2>(args)), 42u);
}
TEST(UsesAllocatorConstructionArgs, PairRValueReference) {
[[maybe_unused]] const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, std::make_pair(3, 42u));
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<int &&>, std::tuple<unsigned int &&, const std::allocator<int> &>>>);
}
TEST(MakeObjUsingAllocator, Functionalities) {
const auto size = 42u;
test::throwing_allocator<int>::trigger_on_allocate = true;
ASSERT_THROW((entt::make_obj_using_allocator<std::vector<int, test::throwing_allocator<int>>>(test::throwing_allocator<int>{}, size)), test::throwing_allocator<int>::exception_type);
const auto vec = entt::make_obj_using_allocator<std::vector<int>>(std::allocator<int>{}, size);
ASSERT_FALSE(vec.empty());
ASSERT_EQ(vec.size(), size);
}
TEST(UninitializedConstructUsingAllocator, NoUsesAllocatorConstruction) {
alignas(int) std::byte storage[sizeof(int)];
std::allocator<int> allocator{};
int *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<int *>(&storage), allocator, 42);
ASSERT_EQ(*value, 42);
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
TEST(UninitializedConstructUsingAllocator, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
test::tracked_memory_resource memory_resource{};
std::pmr::polymorphic_allocator<string_type> allocator{&memory_resource};
alignas(string_type) std::byte storage[sizeof(string_type)];
string_type *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<string_type *>(&storage), allocator, test::tracked_memory_resource::default_value);
ASSERT_GT(memory_resource.do_allocate_counter(), 0u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
ASSERT_EQ(*value, test::tracked_memory_resource::default_value);
value->~string_type();
}
#endif

View File

@ -0,0 +1,22 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/monostate.hpp>
TEST(Monostate, Functionalities) {
using namespace entt::literals;
const bool b_pre = entt::monostate<entt::hashed_string{"foobar"}>{};
const int i_pre = entt::monostate<"foobar"_hs>{};
ASSERT_FALSE(b_pre);
ASSERT_EQ(i_pre, int{});
entt::monostate<"foobar"_hs>{} = true;
entt::monostate_v<"foobar"_hs> = 42;
const bool &b_post = entt::monostate<"foobar"_hs>{};
const int &i_post = entt::monostate_v<entt::hashed_string{"foobar"}>;
ASSERT_TRUE(b_post);
ASSERT_EQ(i_post, 42);
}

38
test/entt/core/tuple.cpp Normal file
View File

@ -0,0 +1,38 @@
#include <tuple>
#include <gtest/gtest.h>
#include <entt/core/tuple.hpp>
TEST(Tuple, IsTuple) {
static_assert(!entt::is_tuple_v<int>);
static_assert(entt::is_tuple_v<std::tuple<>>);
static_assert(entt::is_tuple_v<std::tuple<int>>);
static_assert(entt::is_tuple_v<std::tuple<int, char>>);
}
TEST(Tuple, UnwrapTuple) {
auto single = std::make_tuple(42);
auto multi = std::make_tuple(42, 'c');
auto ref = std::forward_as_tuple(std::get<0>(single));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(single)), int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(multi)), std::tuple<int, char> &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(ref)), int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::move(single))), int &&>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::move(multi))), std::tuple<int, char> &&>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::move(ref))), int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::as_const(single))), const int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::as_const(multi))), const std::tuple<int, char> &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::as_const(ref))), int &>));
ASSERT_EQ(entt::unwrap_tuple(single), 42);
ASSERT_EQ(entt::unwrap_tuple(multi), multi);
ASSERT_EQ(entt::unwrap_tuple(std::move(ref)), 42);
}
TEST(Tuple, ForwardApply) {
ASSERT_EQ(entt::forward_apply{[](auto &&...args) { return sizeof...(args); }}(std::make_tuple()), 0u);
ASSERT_EQ(entt::forward_apply{[](int i) { return i; }}(std::make_tuple(42)), 42);
ASSERT_EQ(entt::forward_apply{[](auto... args) { return (args + ...); }}(std::make_tuple('a', 1u)), 'b');
}

View File

@ -0,0 +1,116 @@
#include <string_view>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/core/type_traits.hpp>
template<>
struct entt::type_name<float> final {
[[nodiscard]] static constexpr std::string_view value() noexcept {
return std::string_view{""};
}
};
TEST(TypeIndex, Functionalities) {
ASSERT_EQ(entt::type_index<int>::value(), entt::type_index<int>::value());
ASSERT_NE(entt::type_index<int>::value(), entt::type_index<char>::value());
ASSERT_NE(entt::type_index<int>::value(), entt::type_index<int &&>::value());
ASSERT_NE(entt::type_index<int &>::value(), entt::type_index<const int &>::value());
ASSERT_EQ(static_cast<entt::id_type>(entt::type_index<int>{}), entt::type_index<int>::value());
}
TEST(TypeHash, Functionalities) {
ASSERT_NE(entt::type_hash<int>::value(), entt::type_hash<const int>::value());
ASSERT_NE(entt::type_hash<int>::value(), entt::type_hash<char>::value());
ASSERT_EQ(entt::type_hash<int>::value(), entt::type_hash<int>::value());
ASSERT_EQ(static_cast<entt::id_type>(entt::type_hash<int>{}), entt::type_hash<int>::value());
}
TEST(TypeName, Functionalities) {
ASSERT_EQ(entt::type_name<int>::value(), std::string_view{"int"});
ASSERT_EQ(entt::type_name<float>{}.value(), std::string_view{""});
ASSERT_TRUE((entt::type_name<entt::integral_constant<3>>::value() == std::string_view{"std::integral_constant<int, 3>"})
|| (entt::type_name<entt::integral_constant<3>>::value() == std::string_view{"std::__1::integral_constant<int, 3>"})
|| (entt::type_name<entt::integral_constant<3>>::value() == std::string_view{"struct std::integral_constant<int,3>"}));
ASSERT_TRUE(((entt::type_name<entt::type_list<entt::type_list<int, char>, double>>::value()) == std::string_view{"entt::type_list<entt::type_list<int, char>, double>"})
|| ((entt::type_name<entt::type_list<entt::type_list<int, char>, double>>::value()) == std::string_view{"struct entt::type_list<struct entt::type_list<int,char>,double>"}));
ASSERT_EQ(static_cast<std::string_view>(entt::type_name<int>{}), entt::type_name<int>::value());
}
TEST(TypeInfo, Functionalities) {
static_assert(std::is_copy_constructible_v<entt::type_info>);
static_assert(std::is_move_constructible_v<entt::type_info>);
static_assert(std::is_copy_assignable_v<entt::type_info>);
static_assert(std::is_move_assignable_v<entt::type_info>);
entt::type_info info{std::in_place_type<int>};
entt::type_info other{std::in_place_type<void>};
ASSERT_EQ(info, entt::type_info{std::in_place_type<int &>});
ASSERT_EQ(info, entt::type_info{std::in_place_type<int &&>});
ASSERT_EQ(info, entt::type_info{std::in_place_type<const int &>});
ASSERT_NE(info, other);
ASSERT_TRUE(info == info);
ASSERT_FALSE(info != info);
ASSERT_EQ(info.index(), entt::type_index<int>::value());
ASSERT_EQ(info.hash(), entt::type_hash<int>::value());
ASSERT_EQ(info.name(), entt::type_name<int>::value());
other = info;
ASSERT_EQ(other.index(), entt::type_index<int>::value());
ASSERT_EQ(other.hash(), entt::type_hash<int>::value());
ASSERT_EQ(other.name(), entt::type_name<int>::value());
ASSERT_EQ(other.index(), info.index());
ASSERT_EQ(other.hash(), info.hash());
ASSERT_EQ(other.name(), info.name());
other = std::move(info);
ASSERT_EQ(other.index(), entt::type_index<int>::value());
ASSERT_EQ(other.hash(), entt::type_hash<int>::value());
ASSERT_EQ(other.name(), entt::type_name<int>::value());
ASSERT_EQ(other.index(), info.index());
ASSERT_EQ(other.hash(), info.hash());
ASSERT_EQ(other.name(), info.name());
}
TEST(TypeInfo, Order) {
entt::type_info rhs = entt::type_id<int>();
entt::type_info lhs = entt::type_id<char>();
// let's adjust the two objects since values are generated at runtime
rhs < lhs ? void() : std::swap(lhs, rhs);
ASSERT_FALSE(lhs < lhs);
ASSERT_FALSE(rhs < rhs);
ASSERT_LT(rhs, lhs);
ASSERT_LE(rhs, lhs);
ASSERT_GT(lhs, rhs);
ASSERT_GE(lhs, rhs);
}
TEST(TypeId, Functionalities) {
const int value = 42;
ASSERT_EQ(entt::type_id(value), entt::type_id<int>());
ASSERT_EQ(entt::type_id(42), entt::type_id<int>());
ASSERT_EQ(entt::type_id<int>(), entt::type_id<int>());
ASSERT_EQ(entt::type_id<int &>(), entt::type_id<int &&>());
ASSERT_EQ(entt::type_id<int &>(), entt::type_id<int>());
ASSERT_NE(entt::type_id<int>(), entt::type_id<char>());
ASSERT_EQ(&entt::type_id<int>(), &entt::type_id<int>());
ASSERT_NE(&entt::type_id<int>(), &entt::type_id<void>());
}

View File

@ -0,0 +1,224 @@
#include <functional>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_traits.hpp>
struct not_comparable {
bool operator==(const not_comparable &) const = delete;
};
struct nlohmann_json_like final {
using value_type = nlohmann_json_like;
bool operator==(const nlohmann_json_like &) const {
return true;
}
};
struct clazz {
char foo(int) {
return {};
}
int bar(double, float) const {
return {};
}
bool quux;
};
int free_function(int, const double &) {
return 42;
}
template<typename, typename Type = void>
struct multi_argument_operation {
using type = Type;
};
TEST(SizeOf, Functionalities) {
static_assert(entt::size_of_v<void> == 0u);
static_assert(entt::size_of_v<char> == sizeof(char));
static_assert(entt::size_of_v<int[]> == 0u);
static_assert(entt::size_of_v<int[3]> == sizeof(int[3]));
}
TEST(UnpackAsType, Functionalities) {
auto test = [](auto &&...args) {
return [](entt::unpack_as_type<int, decltype(args)>... value) {
return (value + ... + 0);
};
};
ASSERT_EQ(test('c', 42., true)(1, 2, 3), 6);
}
TEST(UnpackAsValue, Functionalities) {
auto test = [](auto &&...args) {
return (entt::unpack_as_value<2, decltype(args)> + ... + 0);
};
ASSERT_EQ(test('c', 42., true), 6);
}
TEST(IntegralConstant, Functionalities) {
entt::integral_constant<3> constant{};
static_assert(std::is_same_v<typename entt::integral_constant<3>::value_type, int>);
static_assert(constant.value == 3);
}
TEST(Choice, Functionalities) {
static_assert(std::is_base_of_v<entt::choice_t<0>, entt::choice_t<1>>);
static_assert(!std::is_base_of_v<entt::choice_t<1>, entt::choice_t<0>>);
}
TEST(TypeList, Functionalities) {
using type = entt::type_list<int, char>;
using other = entt::type_list<double>;
static_assert(type::size == 2u);
static_assert(other::size == 1u);
static_assert(std::is_same_v<decltype(type{} + other{}), entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, other, type, other>, entt::type_list<int, char, double, int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, other>, entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, type>, entt::type_list<int, char, int, char>>);
static_assert(std::is_same_v<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, entt::type_list<int, char>>);
static_assert(entt::type_list_contains_v<type, int>);
static_assert(entt::type_list_contains_v<type, char>);
static_assert(!entt::type_list_contains_v<type, double>);
static_assert(std::is_same_v<entt::type_list_element_t<0u, type>, int>);
static_assert(std::is_same_v<entt::type_list_element_t<1u, type>, char>);
static_assert(std::is_same_v<entt::type_list_element_t<0u, other>, double>);
static_assert(entt::type_list_index_v<int, type> == 0u);
static_assert(entt::type_list_index_v<char, type> == 1u);
static_assert(entt::type_list_index_v<double, other> == 0u);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<float, bool>>, entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<int, char, double>>, entt::type_list<>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<int, char>>, entt::type_list<double>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<char, double>>, entt::type_list<int>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<char>>, entt::type_list<int, double>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, entt::type_identity>, entt::type_list<int, char>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, std::add_const>, entt::type_list<const int, const char>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, multi_argument_operation>, entt::type_list<void, void>>);
}
TEST(ValueList, Functionalities) {
using value = entt::value_list<0, 2>;
using other = entt::value_list<1>;
static_assert(value::size == 2u);
static_assert(other::size == 1u);
static_assert(std::is_same_v<decltype(value{} + other{}), entt::value_list<0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, other, value, other>, entt::value_list<0, 2, 1, 0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, other>, entt::value_list<0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, value>, entt::value_list<0, 2, 0, 2>>);
static_assert(entt::value_list_element_v<0u, value> == 0);
static_assert(entt::value_list_element_v<1u, value> == 2);
static_assert(entt::value_list_element_v<0u, other> == 1);
}
TEST(IsApplicable, Functionalities) {
static_assert(entt::is_applicable_v<void(int, char), std::tuple<double, char>>);
static_assert(!entt::is_applicable_v<void(int, char), std::tuple<int>>);
static_assert(entt::is_applicable_r_v<float, int(int, char), std::tuple<double, char>>);
static_assert(!entt::is_applicable_r_v<float, void(int, char), std::tuple<double, char>>);
static_assert(!entt::is_applicable_r_v<int, int(int, char), std::tuple<void>>);
}
TEST(IsComplete, Functionalities) {
static_assert(!entt::is_complete_v<void>);
static_assert(entt::is_complete_v<int>);
}
TEST(IsIterator, Functionalities) {
static_assert(!entt::is_iterator_v<void>);
static_assert(!entt::is_iterator_v<int>);
static_assert(!entt::is_iterator_v<void *>);
static_assert(entt::is_iterator_v<int *>);
static_assert(entt::is_iterator_v<std::vector<int>::iterator>);
static_assert(entt::is_iterator_v<std::vector<int>::const_iterator>);
static_assert(entt::is_iterator_v<std::vector<int>::reverse_iterator>);
}
TEST(IsEBCOEligible, Functionalities) {
static_assert(entt::is_ebco_eligible_v<not_comparable>);
static_assert(!entt::is_ebco_eligible_v<nlohmann_json_like>);
static_assert(!entt::is_ebco_eligible_v<double>);
static_assert(!entt::is_ebco_eligible_v<void>);
}
TEST(IsTransparent, Functionalities) {
static_assert(!entt::is_transparent_v<std::less<int>>);
static_assert(entt::is_transparent_v<std::less<void>>);
static_assert(!entt::is_transparent_v<std::logical_not<double>>);
static_assert(entt::is_transparent_v<std::logical_not<void>>);
}
TEST(IsEqualityComparable, Functionalities) {
static_assert(entt::is_equality_comparable_v<int>);
static_assert(entt::is_equality_comparable_v<const int>);
static_assert(entt::is_equality_comparable_v<std::vector<int>>);
static_assert(entt::is_equality_comparable_v<std::vector<std::vector<int>>>);
static_assert(entt::is_equality_comparable_v<std::unordered_map<int, int>>);
static_assert(entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, char>>>);
static_assert(entt::is_equality_comparable_v<std::pair<const int, int>>);
static_assert(entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, char>>>);
static_assert(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>);
static_assert(entt::is_equality_comparable_v<nlohmann_json_like>);
static_assert(!entt::is_equality_comparable_v<not_comparable>);
static_assert(!entt::is_equality_comparable_v<const not_comparable>);
static_assert(!entt::is_equality_comparable_v<std::vector<not_comparable>>);
static_assert(!entt::is_equality_comparable_v<std::vector<std::vector<not_comparable>>>);
static_assert(!entt::is_equality_comparable_v<std::unordered_map<int, not_comparable>>);
static_assert(!entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, not_comparable>>>);
static_assert(!entt::is_equality_comparable_v<std::pair<const int, not_comparable>>);
static_assert(!entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, not_comparable>>>);
static_assert(!entt::is_equality_comparable_v<void>);
}
TEST(ConstnessAs, Functionalities) {
static_assert(std::is_same_v<entt::constness_as_t<int, char>, int>);
static_assert(std::is_same_v<entt::constness_as_t<const int, char>, int>);
static_assert(std::is_same_v<entt::constness_as_t<int, const char>, const int>);
static_assert(std::is_same_v<entt::constness_as_t<const int, const char>, const int>);
}
TEST(MemberClass, Functionalities) {
static_assert(std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::foo)>>);
static_assert(std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::bar)>>);
static_assert(std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::quux)>>);
}
TEST(NthArgument, Functionalities) {
static_assert(std::is_same_v<entt::nth_argument_t<0u, &free_function>, int>);
static_assert(std::is_same_v<entt::nth_argument_t<1u, &free_function>, const double &>);
static_assert(std::is_same_v<entt::nth_argument_t<0u, &clazz::bar>, double>);
static_assert(std::is_same_v<entt::nth_argument_t<1u, &clazz::bar>, float>);
static_assert(std::is_same_v<entt::nth_argument_t<0u, &clazz::quux>, bool>);
ASSERT_EQ(free_function(entt::nth_argument_t<0u, &free_function>{}, entt::nth_argument_t<1u, &free_function>{}), 42);
}
TEST(Tag, Functionalities) {
using namespace entt::literals;
static_assert(entt::tag<"foobar"_hs>::value == entt::hashed_string::value("foobar"));
static_assert(std::is_same_v<typename entt::tag<"foobar"_hs>::value_type, entt::id_type>);
}

View File

@ -0,0 +1,61 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/core/utility.hpp>
struct functions {
static void foo(int) {}
static void foo() {}
void bar(int) {}
void bar() {}
};
TEST(Identity, Functionalities) {
entt::identity identity;
int value = 42;
ASSERT_TRUE(entt::is_transparent_v<entt::identity>);
ASSERT_EQ(identity(value), value);
ASSERT_EQ(&identity(value), &value);
}
TEST(Overload, Functionalities) {
ASSERT_EQ(entt::overload<void(int)>(&functions::foo), static_cast<void (*)(int)>(&functions::foo));
ASSERT_EQ(entt::overload<void()>(&functions::foo), static_cast<void (*)()>(&functions::foo));
ASSERT_EQ(entt::overload<void(int)>(&functions::bar), static_cast<void (functions::*)(int)>(&functions::bar));
ASSERT_EQ(entt::overload<void()>(&functions::bar), static_cast<void (functions::*)()>(&functions::bar));
functions instance;
ASSERT_NO_FATAL_FAILURE(entt::overload<void(int)>(&functions::foo)(0));
ASSERT_NO_FATAL_FAILURE(entt::overload<void()>(&functions::foo)());
ASSERT_NO_FATAL_FAILURE((instance.*entt::overload<void(int)>(&functions::bar))(0));
ASSERT_NO_FATAL_FAILURE((instance.*entt::overload<void()>(&functions::bar))());
}
TEST(Overloaded, Functionalities) {
int iv = 0;
char cv = '\0';
entt::overloaded func{
[&iv](int value) { iv = value; },
[&cv](char value) { cv = value; }};
func(42);
func('c');
ASSERT_EQ(iv, 42);
ASSERT_EQ(cv, 'c');
}
TEST(YCombinator, Functionalities) {
entt::y_combinator gauss([](const auto &self, auto value) -> unsigned int {
return value ? (value + self(value - 1u)) : 0;
});
ASSERT_EQ(gauss(3u), 3u * 4u / 2u);
ASSERT_EQ(std::as_const(gauss)(7u), 7u * 8u / 2u);
}

View File

@ -0,0 +1,79 @@
#include <gtest/gtest.h>
#include <entt/config/config.h>
#include <entt/entity/component.hpp>
struct empty {};
struct non_empty {
int value;
};
struct non_movable {
non_movable() = default;
non_movable(const non_movable &) = delete;
non_movable &operator=(const non_movable &) = delete;
int value;
};
struct self_contained {
static constexpr auto in_place_delete = true;
static constexpr auto page_size = 4u;
};
struct traits_based {};
template<>
struct entt::component_traits<traits_based> {
using type = traits_based;
static constexpr auto in_place_delete = false;
static constexpr auto page_size = 8u;
};
TEST(Component, VoidType) {
using traits = entt::component_traits<void>;
static_assert(traits::in_place_delete);
static_assert(entt::ignore_as_empty_v<typename traits::type>);
// we don't really care about this thanks to ignore_as_empty_v
static_assert(traits::page_size != 0u);
}
TEST(Component, Empty) {
using traits = entt::component_traits<empty>;
static_assert(!traits::in_place_delete);
static_assert(entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 0u);
}
TEST(Component, NonEmpty) {
using traits = entt::component_traits<non_empty>;
static_assert(!traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == ENTT_PACKED_PAGE);
}
TEST(Component, NonMovable) {
using traits = entt::component_traits<non_movable>;
static_assert(traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == ENTT_PACKED_PAGE);
}
TEST(Component, SelfContained) {
using traits = entt::component_traits<self_contained>;
static_assert(traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 4u);
}
TEST(Component, TraitsBased) {
using traits = entt::component_traits<traits_based>;
static_assert(!traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 8u);
}

View File

@ -0,0 +1,92 @@
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
TEST(Entity, Traits) {
using traits_type = entt::entt_traits<entt::entity>;
constexpr entt::entity tombstone = entt::tombstone;
constexpr entt::entity null = entt::null;
entt::registry registry{};
registry.destroy(registry.create());
const auto entity = registry.create();
const auto other = registry.create();
ASSERT_EQ(entt::to_integral(entity), entt::to_integral(entity));
ASSERT_NE(entt::to_integral(entity), entt::to_integral<entt::entity>(entt::null));
ASSERT_NE(entt::to_integral(entity), entt::to_integral(entt::entity{}));
ASSERT_EQ(entt::to_entity(entity), 0u);
ASSERT_EQ(entt::to_version(entity), 1u);
ASSERT_EQ(entt::to_entity(other), 1u);
ASSERT_EQ(entt::to_version(other), 0u);
ASSERT_EQ(traits_type::construct(entt::to_entity(entity), entt::to_version(entity)), entity);
ASSERT_EQ(traits_type::construct(entt::to_entity(other), entt::to_version(other)), other);
ASSERT_NE(traits_type::construct(entt::to_entity(entity), {}), entity);
ASSERT_EQ(traits_type::construct(entt::to_entity(other), entt::to_version(entity)), traits_type::combine(entt::to_integral(other), entt::to_integral(entity)));
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::null), tombstone);
ASSERT_EQ(traits_type::combine(entt::null, entt::tombstone), null);
}
TEST(Entity, Null) {
using traits_type = entt::entt_traits<entt::entity>;
constexpr entt::entity null = entt::null;
ASSERT_FALSE(entt::entity{} == entt::null);
ASSERT_TRUE(entt::null == entt::null);
ASSERT_FALSE(entt::null != entt::null);
entt::registry registry{};
const auto entity = registry.create();
ASSERT_EQ(traits_type::combine(entt::null, entt::to_integral(entity)), (traits_type::construct(entt::to_entity(null), entt::to_version(entity))));
ASSERT_EQ(traits_type::combine(entt::null, entt::to_integral(null)), null);
ASSERT_EQ(traits_type::combine(entt::null, entt::tombstone), null);
registry.emplace<int>(entity, 42);
ASSERT_FALSE(entity == entt::null);
ASSERT_FALSE(entt::null == entity);
ASSERT_TRUE(entity != entt::null);
ASSERT_TRUE(entt::null != entity);
const entt::entity other = entt::null;
ASSERT_FALSE(registry.valid(other));
ASSERT_NE(registry.create(other), other);
}
TEST(Entity, Tombstone) {
using traits_type = entt::entt_traits<entt::entity>;
constexpr entt::entity tombstone = entt::tombstone;
ASSERT_FALSE(entt::entity{} == entt::tombstone);
ASSERT_TRUE(entt::tombstone == entt::tombstone);
ASSERT_FALSE(entt::tombstone != entt::tombstone);
entt::registry registry{};
const auto entity = registry.create();
ASSERT_EQ(traits_type::combine(entt::to_integral(entity), entt::tombstone), (traits_type::construct(entt::to_entity(entity), entt::to_version(tombstone))));
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::to_integral(tombstone)), tombstone);
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::null), tombstone);
registry.emplace<int>(entity, 42);
ASSERT_FALSE(entity == entt::tombstone);
ASSERT_FALSE(entt::tombstone == entity);
ASSERT_TRUE(entity != entt::tombstone);
ASSERT_TRUE(entt::tombstone != entity);
constexpr auto vers = entt::to_version(tombstone);
const auto other = traits_type::construct(entt::to_entity(entity), vers);
ASSERT_FALSE(registry.valid(entt::tombstone));
ASSERT_NE(registry.destroy(entity, vers), vers);
ASSERT_NE(registry.create(other), other);
}

1468
test/entt/entity/group.cpp Normal file

File diff suppressed because it is too large Load Diff

295
test/entt/entity/handle.cpp Normal file
View File

@ -0,0 +1,295 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/handle.hpp>
#include <entt/entity/registry.hpp>
TEST(BasicHandle, Assumptions) {
static_assert(std::is_trivially_copyable_v<entt::handle>);
static_assert(std::is_trivially_assignable_v<entt::handle, entt::handle>);
static_assert(std::is_trivially_destructible_v<entt::handle>);
static_assert(std::is_trivially_copyable_v<entt::const_handle>);
static_assert(std::is_trivially_assignable_v<entt::const_handle, entt::const_handle>);
static_assert(std::is_trivially_destructible_v<entt::const_handle>);
}
TEST(BasicHandle, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_handle{std::declval<entt::registry &>(), {}}), entt::basic_handle<entt::registry>>);
static_assert(std::is_same_v<decltype(entt::basic_handle{std::declval<const entt::registry &>(), {}}), entt::basic_handle<const entt::registry>>);
}
TEST(BasicHandle, Construction) {
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
entt::const_handle chandle{std::as_const(registry), entity};
ASSERT_FALSE(entt::null == handle.entity());
ASSERT_EQ(entity, handle);
ASSERT_TRUE(handle);
ASSERT_FALSE(entt::null == chandle.entity());
ASSERT_EQ(entity, chandle);
ASSERT_TRUE(chandle);
ASSERT_EQ(handle, chandle);
static_assert(std::is_same_v<entt::registry *, decltype(handle.registry())>);
static_assert(std::is_same_v<const entt::registry *, decltype(chandle.registry())>);
}
TEST(BasicHandle, Invalidation) {
entt::handle handle;
ASSERT_FALSE(handle);
ASSERT_EQ(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
entt::registry registry;
const auto entity = registry.create();
handle = {registry, entity};
ASSERT_TRUE(handle);
ASSERT_NE(handle.registry(), nullptr);
ASSERT_NE(handle.entity(), entt::entity{entt::null});
handle = {};
ASSERT_FALSE(handle);
ASSERT_EQ(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
}
TEST(BasicHandle, Destruction) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
ASSERT_TRUE(handle);
ASSERT_TRUE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
handle.destroy(traits_type::to_version(entity));
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
ASSERT_EQ(registry.current(entity), typename entt::registry::version_type{});
handle = entt::handle{registry, registry.create()};
ASSERT_TRUE(handle);
ASSERT_TRUE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
handle.destroy();
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
ASSERT_NE(registry.current(entity), typename entt::registry::version_type{});
}
TEST(BasicHandle, Comparison) {
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
entt::const_handle chandle = handle;
ASSERT_NE(handle, entt::handle{});
ASSERT_FALSE(handle == entt::handle{});
ASSERT_TRUE(handle != entt::handle{});
ASSERT_NE(chandle, entt::const_handle{});
ASSERT_FALSE(chandle == entt::const_handle{});
ASSERT_TRUE(chandle != entt::const_handle{});
ASSERT_EQ(handle, chandle);
ASSERT_TRUE(handle == chandle);
ASSERT_FALSE(handle != chandle);
ASSERT_EQ(entt::handle{}, entt::const_handle{});
ASSERT_TRUE(entt::handle{} == entt::const_handle{});
ASSERT_FALSE(entt::handle{} != entt::const_handle{});
handle = {};
chandle = {};
ASSERT_EQ(handle, entt::handle{});
ASSERT_TRUE(handle == entt::handle{});
ASSERT_FALSE(handle != entt::handle{});
ASSERT_EQ(chandle, entt::const_handle{});
ASSERT_TRUE(chandle == entt::const_handle{});
ASSERT_FALSE(chandle != entt::const_handle{});
entt::registry other;
const auto entt = other.create();
handle = {registry, entity};
chandle = {other, entt};
ASSERT_NE(handle, chandle);
ASSERT_FALSE(chandle == handle);
ASSERT_TRUE(chandle != handle);
ASSERT_EQ(handle.entity(), chandle.entity());
ASSERT_NE(handle.registry(), chandle.registry());
}
TEST(BasicHandle, Component) {
entt::registry registry;
const auto entity = registry.create();
entt::handle_view<int, char, double> handle{registry, entity};
ASSERT_EQ(3, handle.emplace<int>(3));
ASSERT_EQ('c', handle.emplace_or_replace<char>('c'));
ASSERT_EQ(.3, handle.emplace_or_replace<double>(.3));
const auto &patched = handle.patch<int>([](auto &comp) { comp = 42; });
ASSERT_EQ(42, patched);
ASSERT_EQ('a', handle.replace<char>('a'));
ASSERT_TRUE((handle.all_of<int, char, double>()));
ASSERT_EQ((std::make_tuple(42, 'a', .3)), (handle.get<int, char, double>()));
handle.erase<char, double>();
ASSERT_TRUE(registry.storage<char>().empty());
ASSERT_TRUE(registry.storage<double>().empty());
ASSERT_EQ(0u, (handle.remove<char, double>()));
for(auto [id, pool]: handle.storage()) {
ASSERT_EQ(id, entt::type_id<int>().hash());
ASSERT_TRUE(pool.contains(handle.entity()));
}
ASSERT_TRUE((handle.any_of<int, char, double>()));
ASSERT_FALSE((handle.all_of<int, char, double>()));
ASSERT_FALSE(handle.orphan());
ASSERT_EQ(1u, (handle.remove<int>()));
ASSERT_TRUE(registry.storage<int>().empty());
ASSERT_TRUE(handle.orphan());
ASSERT_EQ(42, handle.get_or_emplace<int>(42));
ASSERT_EQ(42, handle.get_or_emplace<int>(1));
ASSERT_EQ(42, handle.get<int>());
ASSERT_EQ(42, *handle.try_get<int>());
ASSERT_EQ(nullptr, handle.try_get<char>());
ASSERT_EQ(nullptr, std::get<1>(handle.try_get<int, char, double>()));
}
TEST(BasicHandle, FromEntity) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity, 42);
registry.emplace<char>(entity, 'c');
entt::handle handle{registry, entity};
ASSERT_TRUE(handle);
ASSERT_EQ(entity, handle.entity());
ASSERT_TRUE((handle.all_of<int, char>()));
ASSERT_EQ(handle.get<int>(), 42);
ASSERT_EQ(handle.get<char>(), 'c');
}
TEST(BasicHandle, Lifetime) {
entt::registry registry;
const auto entity = registry.create();
auto *handle = new entt::handle{registry, entity};
handle->emplace<int>();
ASSERT_FALSE(registry.storage<int>().empty());
ASSERT_FALSE(registry.empty());
registry.each([handle](const auto e) {
ASSERT_EQ(handle->entity(), e);
});
delete handle;
ASSERT_FALSE(registry.storage<int>().empty());
ASSERT_FALSE(registry.empty());
}
TEST(BasicHandle, ImplicitConversions) {
entt::registry registry;
const entt::handle handle{registry, registry.create()};
const entt::const_handle chandle = handle;
const entt::handle_view<int, char> vhandle = handle;
const entt::const_handle_view<int> cvhandle = vhandle;
handle.emplace<int>(42);
ASSERT_EQ(handle.get<int>(), chandle.get<int>());
ASSERT_EQ(chandle.get<int>(), vhandle.get<int>());
ASSERT_EQ(vhandle.get<int>(), cvhandle.get<int>());
ASSERT_EQ(cvhandle.get<int>(), 42);
}
TEST(BasicHandle, Storage) {
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
entt::const_handle chandle{std::as_const(registry), entity};
static_assert(std::is_same_v<decltype(*handle.storage().begin()), std::pair<entt::id_type, entt::sparse_set &>>);
static_assert(std::is_same_v<decltype(*chandle.storage().begin()), std::pair<entt::id_type, const entt::sparse_set &>>);
ASSERT_EQ(handle.storage().begin(), handle.storage().end());
ASSERT_EQ(chandle.storage().begin(), chandle.storage().end());
registry.storage<double>();
registry.emplace<int>(entity);
ASSERT_NE(handle.storage().begin(), handle.storage().end());
ASSERT_NE(chandle.storage().begin(), chandle.storage().end());
ASSERT_EQ(++handle.storage().begin(), handle.storage().end());
ASSERT_EQ(++chandle.storage().begin(), chandle.storage().end());
ASSERT_EQ(handle.storage().begin()->second.type(), entt::type_id<int>());
ASSERT_EQ(chandle.storage().begin()->second.type(), entt::type_id<int>());
}
TEST(BasicHandle, HandleStorageIterator) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<double>(entity);
auto test = [](auto iterable) {
auto end{iterable.begin()};
decltype(end) begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.cbegin());
ASSERT_EQ(end, iterable.cend());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(++begin, iterable.end());
};
test(entt::handle{registry, entity}.storage());
test(entt::const_handle{std::as_const(registry), entity}.storage());
}

126
test/entt/entity/helper.cpp Normal file
View File

@ -0,0 +1,126 @@
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/helper.hpp>
#include <entt/entity/registry.hpp>
struct clazz {
void func(entt::registry &, entt::entity curr) {
entt = curr;
}
entt::entity entt{entt::null};
};
struct stable_type {
static constexpr auto in_place_delete = true;
int value;
};
TEST(Helper, AsView) {
entt::registry registry;
const entt::registry cregistry;
([](entt::view<entt::get_t<int>>) {})(entt::as_view{registry});
([](entt::view<entt::get_t<char, double>, entt::exclude_t<int>>) {})(entt::as_view{registry});
([](entt::view<entt::get_t<const char, double>, entt::exclude_t<const int>>) {})(entt::as_view{registry});
([](entt::view<entt::get_t<const char, const double>, entt::exclude_t<const int>>) {})(entt::as_view{cregistry});
}
TEST(Helper, AsGroup) {
entt::registry registry;
const entt::registry cregistry;
([](entt::group<entt::owned_t<double>, entt::get_t<char>, entt::exclude_t<int>>) {})(entt::as_group{registry});
([](entt::group<entt::owned_t<double>, entt::get_t<const char>, entt::exclude_t<const int>>) {})(entt::as_group{registry});
([](entt::group<entt::owned_t<const double>, entt::get_t<const char>, entt::exclude_t<const int>>) {})(entt::as_group{cregistry});
}
TEST(Helper, Invoke) {
entt::registry registry;
const auto entity = registry.create();
registry.on_construct<clazz>().connect<entt::invoke<&clazz::func>>();
registry.emplace<clazz>(entity);
ASSERT_EQ(entity, registry.get<clazz>(entity).entt);
}
TEST(Helper, ToEntity) {
entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::component_traits<int>::page_size;
const int value = 42;
ASSERT_EQ(entt::to_entity(registry, 42), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
const auto entity = registry.create();
auto &&storage = registry.storage<int>();
storage.emplace(entity);
while(storage.size() < (page_size - 1u)) {
storage.emplace(registry.create(), value);
}
const auto other = registry.create();
const auto next = registry.create();
registry.emplace<int>(other);
registry.emplace<int>(next);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
ASSERT_EQ(&registry.get<int>(entity) + page_size - 1u, &registry.get<int>(other));
registry.destroy(other);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
ASSERT_EQ(&registry.get<int>(entity) + page_size - 1u, &registry.get<int>(next));
ASSERT_EQ(entt::to_entity(registry, 42), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
}
TEST(Helper, ToEntityStableType) {
entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::component_traits<stable_type>::page_size;
const stable_type value{42};
ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
const auto entity = registry.create();
auto &&storage = registry.storage<stable_type>();
registry.emplace<stable_type>(entity);
while(storage.size() < (page_size - 2u)) {
storage.emplace(registry.create(), value);
}
const auto other = registry.create();
const auto next = registry.create();
registry.emplace<stable_type>(other);
registry.emplace<stable_type>(next);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(next)), next);
ASSERT_EQ(&registry.get<stable_type>(entity) + page_size - 2u, &registry.get<stable_type>(other));
registry.destroy(other);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(next)), next);
ASSERT_EQ(&registry.get<stable_type>(entity) + page_size - 1u, &registry.get<stable_type>(next));
ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
}

View File

@ -0,0 +1,371 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/observer.hpp>
#include <entt/entity/registry.hpp>
TEST(Observer, Functionalities) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>()};
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
ASSERT_EQ(observer.begin(), observer.end());
const auto entity = registry.create();
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
ASSERT_NE(observer.begin(), observer.end());
ASSERT_EQ(++observer.begin(), observer.end());
ASSERT_EQ(*observer.begin(), entity);
observer.clear();
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.erase<int>(entity);
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, AllOf) {
constexpr auto collector =
entt::collector
.group<int, char>(entt::exclude<float>)
.group<int, double>();
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.emplace<double>(entity);
ASSERT_FALSE(observer.empty());
registry.erase<int>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace<float>(entity);
registry.emplace<int>(entity);
ASSERT_FALSE(observer.empty());
registry.erase<double>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace<double>(entity);
observer.clear();
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.emplace_or_replace<int>(entity);
registry.emplace_or_replace<char>(entity);
registry.erase<float>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, AllOfFiltered) {
constexpr auto collector =
entt::collector
.group<int>()
.where<char>(entt::exclude<double>);
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
registry.erase<int>(entity);
registry.emplace<char>(entity);
registry.emplace<double>(entity);
registry.emplace<int>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<int>(entity);
registry.erase<double>(entity);
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.emplace<double>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<double>(entity);
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.erase<int>(entity);
registry.emplace<int>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, Observe) {
entt::registry registry;
entt::observer observer{registry, entt::collector.update<int>().update<char>()};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
observer.clear();
registry.replace<char>(entity);
ASSERT_FALSE(observer.empty());
observer.clear();
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.emplace_or_replace<int>(entity);
registry.emplace_or_replace<char>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, ObserveFiltered) {
constexpr auto collector =
entt::collector
.update<int>()
.where<char>(entt::exclude<double>);
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.replace<int>(entity);
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
registry.emplace<char>(entity);
registry.emplace<double>(entity);
registry.replace<int>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<double>(entity);
registry.replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.emplace<double>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<double>(entity);
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.replace<int>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, AllOfObserve) {
entt::registry registry;
entt::observer observer{};
const auto entity = registry.create();
observer.connect(registry, entt::collector.group<int>().update<char>());
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
registry.replace<char>(entity);
registry.erase<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.erase<char>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(observer.empty());
registry.replace<char>(entity);
observer.clear();
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.emplace_or_replace<int>(entity);
registry.emplace_or_replace<char>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, CrossRulesCornerCase) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>().group<char>()};
const auto entity = registry.create();
registry.emplace<int>(entity);
observer.clear();
ASSERT_TRUE(observer.empty());
registry.emplace<char>(entity);
registry.erase<int>(entity);
ASSERT_FALSE(observer.empty());
}
TEST(Observer, Each) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>()};
const auto entity = registry.create();
registry.emplace<int>(entity);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(observer.size(), 1u);
std::as_const(observer).each([entity](const auto entt) {
ASSERT_EQ(entity, entt);
});
ASSERT_FALSE(observer.empty());
ASSERT_EQ(observer.size(), 1u);
observer.each([entity](const auto entt) {
ASSERT_EQ(entity, entt);
});
ASSERT_TRUE(observer.empty());
ASSERT_EQ(observer.size(), 0u);
}
TEST(Observer, MultipleFilters) {
constexpr auto collector =
entt::collector
.update<int>()
.where<char>()
.update<double>()
.where<float>();
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
observer.clear();
registry.emplace<double>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<double>(entity);
registry.emplace<float>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<double>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.erase<float>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
observer.clear();
observer.disconnect();
registry.emplace_or_replace<int>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, GroupCornerCase) {
constexpr auto add_collector = entt::collector.group<int>(entt::exclude<char>);
constexpr auto remove_collector = entt::collector.group<int, char>();
entt::registry registry;
entt::observer add_observer{registry, add_collector};
entt::observer remove_observer{registry, remove_collector};
const auto entity = registry.create();
registry.emplace<int>(entity);
ASSERT_FALSE(add_observer.empty());
ASSERT_TRUE(remove_observer.empty());
add_observer.clear();
registry.emplace<char>(entity);
ASSERT_TRUE(add_observer.empty());
ASSERT_FALSE(remove_observer.empty());
remove_observer.clear();
registry.erase<char>(entity);
ASSERT_FALSE(add_observer.empty());
ASSERT_TRUE(remove_observer.empty());
}

View File

@ -0,0 +1,432 @@
#include <cstddef>
#include <gtest/gtest.h>
#include <entt/entity/organizer.hpp>
#include <entt/entity/registry.hpp>
void ro_int_rw_char_double(entt::view<entt::get_t<const int, char>>, double &) {}
void ro_char_rw_int(entt::view<entt::get_t<int, const char>>) {}
void ro_char_rw_double(entt::view<entt::get_t<const char>>, double &) {}
void ro_int_double(entt::view<entt::get_t<const int>>, const double &) {}
void sync_point(entt::registry &, entt::view<entt::get_t<const int>>) {}
struct clazz {
void ro_int_char_double(entt::view<entt::get_t<const int, const char>>, const double &) {}
void rw_int(entt::view<entt::get_t<int>>) {}
void rw_int_char(entt::view<entt::get_t<int, char>>) {}
void rw_int_char_double(entt::view<entt::get_t<int, char>>, double &) {}
static void ro_int_with_payload(const clazz &, entt::view<entt::get_t<const int>>) {}
static void ro_char_with_payload(const clazz &, entt::view<entt::get_t<const char>>) {}
static void ro_int_char_with_payload(clazz &, entt::view<entt::get_t<const int, const char>>) {}
};
void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry &registry) {
value = view.size();
}
TEST(Organizer, EmplaceFreeFunction) {
entt::organizer organizer;
entt::registry registry;
organizer.emplace<&ro_int_rw_char_double>("t1");
organizer.emplace<&ro_char_rw_int>("t2");
organizer.emplace<&ro_char_rw_double>("t3");
organizer.emplace<&ro_int_double>("t4");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 4u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_EQ(graph[0u].ro_count(), 1u);
ASSERT_EQ(graph[1u].ro_count(), 1u);
ASSERT_EQ(graph[2u].ro_count(), 1u);
ASSERT_EQ(graph[3u].ro_count(), 2u);
ASSERT_EQ(graph[0u].rw_count(), 2u);
ASSERT_EQ(graph[1u].rw_count(), 1u);
ASSERT_EQ(graph[2u].rw_count(), 1u);
ASSERT_EQ(graph[3u].rw_count(), 0u);
ASSERT_NE(graph[0u].info(), graph[1u].info());
ASSERT_NE(graph[1u].info(), graph[2u].info());
ASSERT_NE(graph[2u].info(), graph[3u].info());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_EQ(graph[0u].children().size(), 2u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[0u].children()[1u], 2u);
ASSERT_EQ(graph[1u].children()[0u], 3u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, EmplaceMemberFunction) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&clazz::ro_int_char_double>(instance, "t1");
organizer.emplace<&clazz::rw_int>(instance, "t2");
organizer.emplace<&clazz::rw_int_char>(instance, "t3");
organizer.emplace<&clazz::rw_int_char_double>(instance, "t4");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 4u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_EQ(graph[0u].ro_count(), 3u);
ASSERT_EQ(graph[1u].ro_count(), 0u);
ASSERT_EQ(graph[2u].ro_count(), 0u);
ASSERT_EQ(graph[3u].ro_count(), 0u);
ASSERT_EQ(graph[0u].rw_count(), 0u);
ASSERT_EQ(graph[1u].rw_count(), 1u);
ASSERT_EQ(graph[2u].rw_count(), 2u);
ASSERT_EQ(graph[3u].rw_count(), 3u);
ASSERT_NE(graph[0u].info(), graph[1u].info());
ASSERT_NE(graph[1u].info(), graph[2u].info());
ASSERT_NE(graph[2u].info(), graph[3u].info());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, EmplaceFreeFunctionWithPayload) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&clazz::ro_int_char_double>(instance, "t1");
organizer.emplace<&clazz::ro_int_with_payload>(instance, "t2");
organizer.emplace<&clazz::ro_char_with_payload, const clazz>(instance, "t3");
organizer.emplace<&clazz::ro_int_char_with_payload, clazz>(instance, "t4");
organizer.emplace<&clazz::rw_int_char>(instance, "t5");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 5u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_STREQ(graph[4u].name(), "t5");
ASSERT_EQ(graph[0u].ro_count(), 3u);
ASSERT_EQ(graph[1u].ro_count(), 1u);
ASSERT_EQ(graph[2u].ro_count(), 2u);
ASSERT_EQ(graph[3u].ro_count(), 2u);
ASSERT_EQ(graph[4u].ro_count(), 0u);
ASSERT_EQ(graph[0u].rw_count(), 0u);
ASSERT_EQ(graph[1u].rw_count(), 0u);
ASSERT_EQ(graph[2u].rw_count(), 0u);
ASSERT_EQ(graph[3u].rw_count(), 1u);
ASSERT_EQ(graph[4u].rw_count(), 2u);
ASSERT_NE(graph[0u].info(), graph[1u].info());
ASSERT_NE(graph[1u].info(), graph[2u].info());
ASSERT_NE(graph[2u].info(), graph[3u].info());
ASSERT_NE(graph[3u].info(), graph[4u].info());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_TRUE(graph[1u].top_level());
ASSERT_TRUE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_FALSE(graph[4u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 1u);
ASSERT_EQ(graph[4u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 4u);
ASSERT_EQ(graph[1u].children()[0u], 4u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
ASSERT_EQ(graph[3u].children()[0u], 4u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, EmplaceDirectFunction) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
// no aggressive comdat
auto t1 = +[](const void *, entt::registry &reg) { reg.clear<int>(); };
auto t2 = +[](const void *, entt::registry &reg) { reg.clear<char>(); };
auto t3 = +[](const void *, entt::registry &reg) { reg.clear<double>(); };
auto t4 = +[](const void *, entt::registry &reg) { reg.clear(); };
organizer.emplace<int>(t1, nullptr, "t1");
organizer.emplace<const int>(t2, &instance, "t2");
organizer.emplace<const int, char>(t3, nullptr, "t3");
organizer.emplace<int, char, double>(t4, &instance, "t4");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 4u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_EQ(graph[0u].ro_count(), 0u);
ASSERT_EQ(graph[1u].ro_count(), 1u);
ASSERT_EQ(graph[2u].ro_count(), 1u);
ASSERT_EQ(graph[3u].ro_count(), 0u);
ASSERT_EQ(graph[0u].rw_count(), 1u);
ASSERT_EQ(graph[1u].rw_count(), 0u);
ASSERT_EQ(graph[2u].rw_count(), 1u);
ASSERT_EQ(graph[3u].rw_count(), 3u);
ASSERT_TRUE(graph[0u].callback() == t1);
ASSERT_TRUE(graph[1u].callback() == t2);
ASSERT_TRUE(graph[2u].callback() == t3);
ASSERT_TRUE(graph[3u].callback() == t4);
ASSERT_EQ(graph[0u].data(), nullptr);
ASSERT_EQ(graph[1u].data(), &instance);
ASSERT_EQ(graph[2u].data(), nullptr);
ASSERT_EQ(graph[3u].data(), &instance);
ASSERT_EQ(graph[0u].info(), entt::type_id<void>());
ASSERT_EQ(graph[1u].info(), entt::type_id<void>());
ASSERT_EQ(graph[2u].info(), entt::type_id<void>());
ASSERT_EQ(graph[3u].info(), entt::type_id<void>());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, SyncPoint) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&ro_int_double>("before");
organizer.emplace<&sync_point>("sync_1");
organizer.emplace<&clazz::ro_int_char_double>(instance, "mid_1");
organizer.emplace<&ro_int_double>("mid_2");
organizer.emplace<&sync_point>("sync_2");
organizer.emplace<&ro_int_double>("after");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 6u);
ASSERT_STREQ(graph[0u].name(), "before");
ASSERT_STREQ(graph[1u].name(), "sync_1");
ASSERT_STREQ(graph[2u].name(), "mid_1");
ASSERT_STREQ(graph[3u].name(), "mid_2");
ASSERT_STREQ(graph[4u].name(), "sync_2");
ASSERT_STREQ(graph[5u].name(), "after");
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_FALSE(graph[4u].top_level());
ASSERT_FALSE(graph[5u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 2u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 1u);
ASSERT_EQ(graph[4u].children().size(), 1u);
ASSERT_EQ(graph[5u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
ASSERT_EQ(graph[1u].children()[1u], 3u);
ASSERT_EQ(graph[2u].children()[0u], 4u);
ASSERT_EQ(graph[3u].children()[0u], 4u);
ASSERT_EQ(graph[4u].children()[0u], 5u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
}
TEST(Organizer, Override) {
entt::organizer organizer;
organizer.emplace<&ro_int_rw_char_double, const char, const double>("t1");
organizer.emplace<&ro_char_rw_double, const double>("t2");
organizer.emplace<&ro_int_double, double>("t3");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 3u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_TRUE(graph[0u].top_level());
ASSERT_TRUE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 2u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
}
TEST(Organizer, Prepare) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&ro_int_double>();
organizer.emplace<&clazz::rw_int_char>(instance);
const auto graph = organizer.graph();
ASSERT_FALSE(registry.ctx().contains<int>());
ASSERT_FALSE(registry.ctx().contains<char>());
ASSERT_FALSE(registry.ctx().contains<double>());
for(auto &&vertex: graph) {
vertex.prepare(registry);
}
ASSERT_FALSE(registry.ctx().contains<int>());
ASSERT_FALSE(registry.ctx().contains<char>());
ASSERT_TRUE(registry.ctx().contains<double>());
}
TEST(Organizer, Dependencies) {
entt::organizer organizer;
clazz instance;
organizer.emplace<&ro_int_double>();
organizer.emplace<&clazz::rw_int_char>(instance);
organizer.emplace<char, const double>(+[](const void *, entt::registry &) {});
const auto graph = organizer.graph();
const entt::type_info *buffer[5u]{};
ASSERT_EQ(graph.size(), 3u);
ASSERT_EQ(graph[0u].ro_count(), 2u);
ASSERT_EQ(graph[0u].rw_count(), 0u);
ASSERT_EQ(graph[0u].ro_dependency(buffer, 0u), 0u);
ASSERT_EQ(graph[0u].rw_dependency(buffer, 2u), 0u);
ASSERT_EQ(graph[0u].ro_dependency(buffer, 5u), 2u);
ASSERT_EQ(*buffer[0u], entt::type_id<int>());
ASSERT_EQ(*buffer[1u], entt::type_id<double>());
ASSERT_EQ(graph[1u].ro_count(), 0u);
ASSERT_EQ(graph[1u].rw_count(), 2u);
ASSERT_EQ(graph[1u].ro_dependency(buffer, 2u), 0u);
ASSERT_EQ(graph[1u].rw_dependency(buffer, 0u), 0u);
ASSERT_EQ(graph[1u].rw_dependency(buffer, 5u), 2u);
ASSERT_EQ(*buffer[0u], entt::type_id<int>());
ASSERT_EQ(*buffer[1u], entt::type_id<char>());
ASSERT_EQ(graph[2u].ro_count(), 1u);
ASSERT_EQ(graph[2u].rw_count(), 1u);
ASSERT_EQ(graph[2u].ro_dependency(buffer, 2u), 1u);
ASSERT_EQ(graph[2u].rw_dependency(buffer, 0u), 0u);
ASSERT_EQ(graph[2u].ro_dependency(buffer, 5u), 1u);
ASSERT_EQ(*buffer[0u], entt::type_id<double>());
ASSERT_EQ(graph[2u].rw_dependency(buffer, 5u), 1u);
ASSERT_EQ(*buffer[0u], entt::type_id<char>());
}
TEST(Organizer, ToArgsIntegrity) {
entt::organizer organizer;
entt::registry registry;
organizer.emplace<&to_args_integrity>();
registry.ctx().emplace<std::size_t>(42u);
auto graph = organizer.graph();
graph[0u].callback()(graph[0u].data(), registry);
ASSERT_EQ(registry.ctx().at<std::size_t>(), 0u);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,409 @@
#include <algorithm>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/runtime_view.hpp>
struct stable_type {
static constexpr auto in_place_delete = true;
int value;
};
template<typename Type>
struct RuntimeView: testing::Test {
using type = Type;
};
using RuntimeViewTypes = ::testing::Types<entt::runtime_view, entt::const_runtime_view>;
TYPED_TEST_SUITE(RuntimeView, RuntimeViewTypes, );
TYPED_TEST(RuntimeView, Functionalities) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
const auto e1 = registry.create();
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_FALSE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
// forces the creation of the pools
static_cast<void>(registry.storage<int>());
static_cast<void>(registry.storage<char>());
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
ASSERT_EQ(view.size_hint(), 0u);
registry.emplace<char>(e0);
registry.emplace<int>(e1);
ASSERT_NE(view.size_hint(), 0u);
registry.emplace<char>(e1);
ASSERT_EQ(view.size_hint(), 1u);
auto it = view.begin();
ASSERT_EQ(*it, e1);
ASSERT_EQ(++it, (view.end()));
ASSERT_NO_FATAL_FAILURE((view.begin()++));
ASSERT_NO_FATAL_FAILURE((++view.begin()));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.size_hint(), 1u);
registry.get<char>(e0) = '1';
registry.get<char>(e1) = '2';
registry.get<int>(e1) = 42;
for(auto entity: view) {
ASSERT_EQ(registry.get<int>(entity), 42);
ASSERT_EQ(registry.get<char>(entity), '2');
}
view.clear();
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(view.begin(), view.end());
}
TYPED_TEST(RuntimeView, Constructors) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view = runtime_view_type{std::allocator<int>{}};
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type temp{view, view.get_allocator()};
runtime_view_type other{std::move(temp), view.get_allocator()};
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(temp.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Copy) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type other{view};
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = view;
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Move) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type other{std::move(view)};
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
view = other;
other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = std::move(view);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Swap) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
runtime_view_type other{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view.iterate(registry.storage<int>());
ASSERT_EQ(view.size_hint(), 1u);
ASSERT_EQ(other.size_hint(), 0u);
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(other.begin(), other.end());
view.swap(other);
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(other.size_hint(), 1u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
ASSERT_EQ(view.begin(), view.end());
ASSERT_NE(other.begin(), other.end());
}
TYPED_TEST(RuntimeView, Iterator) {
using runtime_view_type = typename TestFixture::type;
using iterator = typename runtime_view_type::iterator;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view.iterate(registry.storage<int>());
iterator end{view.begin()};
iterator begin{};
begin = view.end();
std::swap(begin, end);
ASSERT_EQ(begin, view.begin());
ASSERT_EQ(end, view.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, view.begin());
ASSERT_EQ(begin--, view.end());
ASSERT_EQ(++begin, view.end());
ASSERT_EQ(--begin, view.begin());
ASSERT_EQ(*begin, entity);
ASSERT_EQ(*begin.operator->(), entity);
}
TYPED_TEST(RuntimeView, Contains) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<int>(entity);
registry.emplace<int>(other);
registry.destroy(entity);
view.iterate(registry.storage<int>());
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
}
TYPED_TEST(RuntimeView, Empty) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<double>(entity);
registry.emplace<float>(other);
view.iterate(registry.storage<int>());
ASSERT_FALSE(view.contains(entity));
ASSERT_FALSE(view.contains(other));
ASSERT_EQ(view.begin(), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), entity)), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), other)), view.end());
}
TYPED_TEST(RuntimeView, Each) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
registry.emplace<char>(other);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.each([entity](const auto entt) {
ASSERT_EQ(entt, entity);
});
}
TYPED_TEST(RuntimeView, EachWithHoles) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<char>(e0, '0');
registry.emplace<char>(e1, '1');
registry.emplace<int>(e0, 0);
registry.emplace<int>(e2, 2);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.each([e0](auto entt) {
ASSERT_EQ(e0, entt);
});
}
TYPED_TEST(RuntimeView, ExcludedComponents) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
registry.emplace<int>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
view.iterate(registry.storage<int>())
.exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
view.each([e0](auto entt) {
ASSERT_EQ(e0, entt);
});
}
TYPED_TEST(RuntimeView, StableType) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<int>(e0);
registry.emplace<int>(e1);
registry.emplace<int>(e2);
registry.emplace<stable_type>(e0);
registry.emplace<stable_type>(e1);
registry.remove<stable_type>(e1);
view.iterate(registry.storage<int>()).iterate(registry.storage<stable_type>());
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_TRUE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
ASSERT_EQ(*view.begin(), e0);
ASSERT_EQ(++view.begin(), view.end());
view.each([e0](const auto entt) {
ASSERT_EQ(e0, entt);
});
for(auto entt: view) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
ASSERT_EQ(e0, entt);
}
registry.compact();
ASSERT_EQ(view.size_hint(), 1u);
}
TYPED_TEST(RuntimeView, StableTypeWithExcludedComponent) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<stable_type>(entity, 0);
registry.emplace<stable_type>(other, 42);
registry.emplace<int>(entity);
view.iterate(registry.storage<stable_type>()).exclude(registry.storage<int>());
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
registry.destroy(entity);
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
for(auto entt: view) {
constexpr entt::entity tombstone = entt::tombstone;
ASSERT_NE(entt, tombstone);
ASSERT_EQ(entt, other);
}
view.each([other](const auto entt) {
constexpr entt::entity tombstone = entt::tombstone;
ASSERT_NE(entt, tombstone);
ASSERT_EQ(entt, other);
});
}

View File

@ -0,0 +1,593 @@
#include <cstddef>
#include <map>
#include <queue>
#include <tuple>
#include <type_traits>
#include <vector>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/snapshot.hpp>
struct noncopyable_component {
noncopyable_component()
: value{} {}
explicit noncopyable_component(int v)
: value{v} {}
noncopyable_component(const noncopyable_component &) = delete;
noncopyable_component(noncopyable_component &&) = default;
noncopyable_component &operator=(const noncopyable_component &) = delete;
noncopyable_component &operator=(noncopyable_component &&) = default;
int value;
};
template<typename Storage>
struct output_archive {
output_archive(Storage &instance)
: storage{instance} {}
template<typename... Value>
void operator()(const Value &...value) {
(std::get<std::queue<Value>>(storage).push(value), ...);
}
void operator()(const entt::entity &entity, const noncopyable_component &instance) {
(*this)(entity, instance.value);
}
private:
Storage &storage;
};
template<typename Storage>
struct input_archive {
input_archive(Storage &instance)
: storage{instance} {}
template<typename... Value>
void operator()(Value &...value) {
auto assign = [this](auto &val) {
auto &queue = std::get<std::queue<std::decay_t<decltype(val)>>>(storage);
val = queue.front();
queue.pop();
};
(assign(value), ...);
}
void operator()(entt::entity &entity, noncopyable_component &instance) {
(*this)(entity, instance.value);
}
private:
Storage &storage;
};
struct a_component {};
struct another_component {
int key;
int value;
};
struct what_a_component {
entt::entity bar;
std::vector<entt::entity> quux;
};
struct map_component {
std::map<entt::entity, int> keys;
std::map<int, entt::entity> values;
std::map<entt::entity, entt::entity> both;
};
TEST(Snapshot, Dump) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<int>(e0, 42);
registry.emplace<char>(e0, 'c');
registry.emplace<double>(e0, .1);
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<int>(e2, 3);
const auto e3 = registry.create();
registry.emplace<a_component>(e3);
registry.emplace<char>(e3, '0');
registry.destroy(e1);
auto v1 = registry.current(e1);
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<int>,
std::queue<char>,
std::queue<double>,
std::queue<a_component>,
std::queue<another_component>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
entt::snapshot{registry}.entities(output).component<int, char, double, a_component, another_component>(output);
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
entt::snapshot_loader{registry}.entities(input).component<int, char, double, a_component, another_component>(input).orphans();
ASSERT_TRUE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_TRUE(registry.valid(e2));
ASSERT_TRUE(registry.valid(e3));
ASSERT_FALSE(registry.orphan(e0));
ASSERT_FALSE(registry.orphan(e2));
ASSERT_FALSE(registry.orphan(e3));
ASSERT_EQ(registry.get<int>(e0), 42);
ASSERT_EQ(registry.get<char>(e0), 'c');
ASSERT_EQ(registry.get<double>(e0), .1);
ASSERT_EQ(registry.current(e1), v1);
ASSERT_EQ(registry.get<int>(e2), 3);
ASSERT_EQ(registry.get<char>(e3), '0');
ASSERT_TRUE(registry.all_of<a_component>(e3));
ASSERT_TRUE(registry.storage<another_component>().empty());
}
TEST(Snapshot, Partial) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<int>(e0, 42);
registry.emplace<char>(e0, 'c');
registry.emplace<double>(e0, .1);
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<int>(e2, 3);
const auto e3 = registry.create();
registry.emplace<char>(e3, '0');
registry.destroy(e1);
auto v1 = registry.current(e1);
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<int>,
std::queue<char>,
std::queue<double>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
entt::snapshot{registry}.entities(output).component<char, int>(output);
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
entt::snapshot_loader{registry}.entities(input).component<char, int>(input);
ASSERT_TRUE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_TRUE(registry.valid(e2));
ASSERT_TRUE(registry.valid(e3));
ASSERT_EQ(registry.get<int>(e0), 42);
ASSERT_EQ(registry.get<char>(e0), 'c');
ASSERT_FALSE(registry.all_of<double>(e0));
ASSERT_EQ(registry.current(e1), v1);
ASSERT_EQ(registry.get<int>(e2), 3);
ASSERT_EQ(registry.get<char>(e3), '0');
entt::snapshot{registry}.entities(output);
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
entt::snapshot_loader{registry}.entities(input).orphans();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
}
TEST(Snapshot, Iterator) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
for(auto i = 0; i < 50; ++i) {
const auto entity = registry.create();
registry.emplace<another_component>(entity, i, i);
registry.emplace<noncopyable_component>(entity, i);
if(i % 2) {
registry.emplace<a_component>(entity);
}
}
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<another_component>,
std::queue<int>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
const auto view = registry.view<a_component>();
const auto size = view.size();
entt::snapshot{registry}.component<another_component, noncopyable_component>(output, view.begin(), view.end());
registry.clear();
entt::snapshot_loader{registry}.component<another_component, noncopyable_component>(input);
ASSERT_EQ(registry.view<another_component>().size(), size);
registry.view<another_component>().each([](const auto entity, const auto &) {
ASSERT_NE(entt::to_integral(entity) % 2u, 0u);
});
}
TEST(Snapshot, Continuous) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry src;
entt::registry dst;
entt::continuous_loader loader{dst};
std::vector<entt::entity> entities;
entt::entity entity;
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<another_component>,
std::queue<what_a_component>,
std::queue<map_component>,
std::queue<int>,
std::queue<double>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
for(int i = 0; i < 10; ++i) {
static_cast<void>(src.create());
}
src.clear();
for(int i = 0; i < 5; ++i) {
entity = src.create();
entities.push_back(entity);
src.emplace<a_component>(entity);
src.emplace<another_component>(entity, i, i);
src.emplace<noncopyable_component>(entity, i);
if(i % 2) {
src.emplace<what_a_component>(entity, entity);
} else {
src.emplace<map_component>(entity);
}
}
src.view<what_a_component>().each([&entities](auto, auto &what_a_component) {
what_a_component.quux.insert(what_a_component.quux.begin(), entities.begin(), entities.end());
});
src.view<map_component>().each([&entities](auto, auto &map_component) {
for(std::size_t i = 0; i < entities.size(); ++i) {
map_component.keys.insert({entities[i], int(i)});
map_component.values.insert({int(i), entities[i]});
map_component.both.insert({entities[entities.size() - i - 1], entities[i]});
}
});
entity = dst.create();
dst.emplace<a_component>(entity);
dst.emplace<another_component>(entity, -1, -1);
dst.emplace<noncopyable_component>(entity, -1);
entt::snapshot{src}.entities(output).component<a_component, another_component, what_a_component, map_component, noncopyable_component>(output);
loader.entities(input)
.component<a_component, another_component, what_a_component, map_component, noncopyable_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
decltype(dst.size()) a_component_cnt{};
decltype(dst.size()) another_component_cnt{};
decltype(dst.size()) what_a_component_cnt{};
decltype(dst.size()) map_component_cnt{};
decltype(dst.size()) noncopyable_component_cnt{};
dst.each([&dst, &a_component_cnt](auto entt) {
ASSERT_TRUE(dst.all_of<a_component>(entt));
++a_component_cnt;
});
dst.view<another_component>().each([&another_component_cnt](auto, const auto &component) {
ASSERT_EQ(component.value, component.key < 0 ? -1 : component.key);
++another_component_cnt;
});
dst.view<what_a_component>().each([&dst, &what_a_component_cnt](auto entt, const auto &component) {
ASSERT_EQ(entt, component.bar);
for(auto child: component.quux) {
ASSERT_TRUE(dst.valid(child));
}
++what_a_component_cnt;
});
dst.view<map_component>().each([&dst, &map_component_cnt](const auto &component) {
for(auto child: component.keys) {
ASSERT_TRUE(dst.valid(child.first));
}
for(auto child: component.values) {
ASSERT_TRUE(dst.valid(child.second));
}
for(auto child: component.both) {
ASSERT_TRUE(dst.valid(child.first));
ASSERT_TRUE(dst.valid(child.second));
}
++map_component_cnt;
});
dst.view<noncopyable_component>().each([&dst, &noncopyable_component_cnt](auto, const auto &component) {
++noncopyable_component_cnt;
ASSERT_EQ(component.value, static_cast<int>(dst.storage<noncopyable_component>().size() - noncopyable_component_cnt - 1u));
});
src.view<another_component>().each([](auto, auto &component) {
component.value = 2 * component.key;
});
auto size = dst.size();
entt::snapshot{src}.entities(output).component<a_component, what_a_component, map_component, another_component>(output);
loader.entities(input)
.component<a_component, what_a_component, map_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
ASSERT_EQ(size, dst.size());
ASSERT_EQ(dst.storage<a_component>().size(), a_component_cnt);
ASSERT_EQ(dst.storage<another_component>().size(), another_component_cnt);
ASSERT_EQ(dst.storage<what_a_component>().size(), what_a_component_cnt);
ASSERT_EQ(dst.storage<map_component>().size(), map_component_cnt);
ASSERT_EQ(dst.storage<noncopyable_component>().size(), noncopyable_component_cnt);
dst.view<another_component>().each([](auto, auto &component) {
ASSERT_EQ(component.value, component.key < 0 ? -1 : (2 * component.key));
});
entity = src.create();
src.view<what_a_component>().each([entity](auto, auto &component) {
component.bar = entity;
});
entt::snapshot{src}.entities(output).component<what_a_component, map_component, a_component, another_component>(output);
loader.entities(input)
.component<what_a_component, map_component, a_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
dst.view<what_a_component>().each([&loader, entity](auto, auto &component) {
ASSERT_EQ(component.bar, loader.map(entity));
});
entities.clear();
for(auto entt: src.view<a_component>()) {
entities.push_back(entt);
}
src.destroy(entity);
loader.shrink();
entt::snapshot{src}.entities(output).component<a_component, another_component, what_a_component, map_component>(output);
loader.entities(input)
.component<a_component, another_component, what_a_component, map_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans()
.shrink();
dst.view<what_a_component>().each([&dst](auto, auto &component) {
ASSERT_FALSE(dst.valid(component.bar));
});
ASSERT_FALSE(loader.contains(entity));
entity = src.create();
src.view<what_a_component>().each([entity](auto, auto &component) {
component.bar = entity;
});
dst.clear<a_component>();
a_component_cnt = src.storage<a_component>().size();
entt::snapshot{src}.entities(output).component<a_component, what_a_component, map_component, another_component>(output);
loader.entities(input)
.component<a_component, what_a_component, map_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
ASSERT_EQ(dst.storage<a_component>().size(), a_component_cnt);
src.clear<a_component>();
a_component_cnt = {};
entt::snapshot{src}.entities(output).component<what_a_component, map_component, a_component, another_component>(output);
loader.entities(input)
.component<what_a_component, map_component, a_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
ASSERT_EQ(dst.storage<a_component>().size(), a_component_cnt);
}
TEST(Snapshot, MoreOnShrink) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry src;
entt::registry dst;
entt::continuous_loader loader{dst};
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
auto entity = src.create();
entt::snapshot{src}.entities(output);
loader.entities(input).shrink();
ASSERT_TRUE(dst.valid(entity));
loader.shrink();
ASSERT_FALSE(dst.valid(entity));
}
TEST(Snapshot, SyncDataMembers) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry src;
entt::registry dst;
entt::continuous_loader loader{dst};
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<what_a_component>,
std::queue<map_component>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
static_cast<void>(src.create());
static_cast<void>(src.create());
src.clear();
auto parent = src.create();
auto child = src.create();
src.emplace<what_a_component>(parent, entt::null);
src.emplace<what_a_component>(child, parent).quux.push_back(child);
src.emplace<map_component>(
child,
decltype(map_component::keys){{{child, 10}}},
decltype(map_component::values){{{10, child}}},
decltype(map_component::both){{{child, child}}});
entt::snapshot{src}.entities(output).component<what_a_component, map_component>(output);
loader.entities(input).component<what_a_component, map_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both);
ASSERT_FALSE(dst.valid(parent));
ASSERT_FALSE(dst.valid(child));
ASSERT_TRUE(dst.all_of<what_a_component>(loader.map(parent)));
ASSERT_TRUE(dst.all_of<what_a_component>(loader.map(child)));
ASSERT_EQ(dst.get<what_a_component>(loader.map(parent)).bar, static_cast<entt::entity>(entt::null));
const auto &component = dst.get<what_a_component>(loader.map(child));
ASSERT_EQ(component.bar, loader.map(parent));
ASSERT_EQ(component.quux[0], loader.map(child));
const auto &foobar = dst.get<map_component>(loader.map(child));
ASSERT_EQ(foobar.keys.at(loader.map(child)), 10);
ASSERT_EQ(foobar.values.at(10), loader.map(child));
ASSERT_EQ(foobar.both.at(loader.map(child)), loader.map(child));
}

File diff suppressed because it is too large Load Diff

1933
test/entt/entity/storage.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,613 @@
#include <iterator>
#include <gtest/gtest.h>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include "../common/throwing_allocator.hpp"
#include "../common/throwing_type.hpp"
struct empty_type {};
struct stable_type {
static constexpr auto in_place_delete = true;
int value{};
};
struct non_default_constructible {
non_default_constructible() = delete;
non_default_constructible(int v)
: value{v} {}
int value{};
};
struct counter {
int value{};
};
template<typename Registry>
void listener(counter &counter, Registry &, typename Registry::entity_type) {
++counter.value;
}
TEST(SighStorageMixin, GenericType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]), 0);
ASSERT_EQ(pool.get(entities[1u]), 0);
base.erase(entities[0u]);
pool.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
ASSERT_TRUE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(pool.get(entities[0u]), 0);
ASSERT_EQ(pool.get(entities[1u]), 0);
ASSERT_FALSE(pool.empty());
base.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_FALSE(pool.empty());
base.erase(entities[0u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_TRUE(pool.empty());
pool.insert(std::begin(entities), std::end(entities), 3);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]), 3);
ASSERT_EQ(pool.get(entities[1u]), 3);
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 6);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, StableType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<stable_type>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[1u]).value, 0);
base.erase(entities[0u]);
pool.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
ASSERT_FALSE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[1u]).value, 0);
ASSERT_FALSE(pool.empty());
base.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_FALSE(pool.empty());
base.erase(entities[0u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
pool.insert(std::begin(entities), std::end(entities), stable_type{3});
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]).value, 3);
ASSERT_EQ(pool.get(entities[1u]).value, 3);
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 6);
ASSERT_FALSE(pool.empty());
}
TEST(SighStorageMixin, EmptyType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<empty_type>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
base.erase(entities[0u]);
pool.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
ASSERT_TRUE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
ASSERT_FALSE(pool.empty());
base.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_FALSE(pool.empty());
base.erase(entities[0u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_TRUE(pool.empty());
pool.insert(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 6);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, NonDefaultConstructibleType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<non_default_constructible>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_EQ(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u], 3);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(pool.contains(entities[0u]));
ASSERT_EQ(pool.get(entities[1u]).value, 3);
base.erase(entities[1u]);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_TRUE(pool.empty());
ASSERT_EQ(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_FALSE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_TRUE(pool.empty());
pool.insert(std::begin(entities), std::end(entities), 3);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]).value, 3);
ASSERT_EQ(pool.get(entities[1u]).value, 3);
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, VoidType) {
entt::sigh_storage_mixin<entt::storage<void>> pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{99});
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_TRUE(pool.contains(entt::entity{99}));
entt::sigh_storage_mixin<entt::storage<void>> other{std::move(pool)};
ASSERT_FALSE(pool.contains(entt::entity{99}));
ASSERT_TRUE(other.contains(entt::entity{99}));
pool = std::move(other);
ASSERT_TRUE(pool.contains(entt::entity{99}));
ASSERT_FALSE(other.contains(entt::entity{99}));
pool.clear();
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
}
TEST(SighStorageMixin, Move) {
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{3}, 3);
ASSERT_TRUE(std::is_move_constructible_v<decltype(pool)>);
ASSERT_TRUE(std::is_move_assignable_v<decltype(pool)>);
ASSERT_EQ(pool.type(), entt::type_id<int>());
entt::sigh_storage_mixin<entt::storage<int>> other{std::move(pool)};
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<int>());
ASSERT_EQ(pool.at(0u), static_cast<entt::entity>(entt::null));
ASSERT_EQ(other.at(0u), entt::entity{3});
ASSERT_EQ(other.get(entt::entity{3}), 3);
pool = std::move(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(pool.at(0u), entt::entity{3});
ASSERT_EQ(pool.get(entt::entity{3}), 3);
ASSERT_EQ(other.at(0u), static_cast<entt::entity>(entt::null));
other = entt::sigh_storage_mixin<entt::storage<int>>{};
other.bind(entt::forward_as_any(registry));
other.emplace(entt::entity{42}, 42);
other = std::move(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(pool.at(0u), static_cast<entt::entity>(entt::null));
ASSERT_EQ(other.at(0u), entt::entity{3});
ASSERT_EQ(other.get(entt::entity{3}), 3);
other.clear();
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
}
TEST(SighStorageMixin, Swap) {
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::sigh_storage_mixin<entt::storage<int>> other;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
other.bind(entt::forward_as_any(registry));
other.on_construct().connect<&listener<entt::registry>>(on_construct);
other.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{42}, 41);
other.emplace(entt::entity{9}, 8);
other.emplace(entt::entity{3}, 2);
other.erase(entt::entity{9});
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
pool.swap(other);
ASSERT_EQ(pool.type(), entt::type_id<int>());
ASSERT_EQ(other.type(), entt::type_id<int>());
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(pool.at(0u), entt::entity{3});
ASSERT_EQ(pool.get(entt::entity{3}), 2);
ASSERT_EQ(other.at(0u), entt::entity{42});
ASSERT_EQ(other.get(entt::entity{42}), 41);
pool.clear();
other.clear();
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 3);
}
TEST(SighStorageMixin, CustomAllocator) {
auto test = [](auto pool, auto alloc) {
using registry_type = typename decltype(pool)::registry_type;
registry_type registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().template connect<&listener<registry_type>>(on_construct);
pool.on_destroy().template connect<&listener<registry_type>>(on_destroy);
pool.reserve(1u);
ASSERT_NE(pool.capacity(), 0u);
pool.emplace(entt::entity{0});
pool.emplace(entt::entity{1});
decltype(pool) other{std::move(pool), alloc};
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(pool.capacity(), 0u);
ASSERT_NE(other.capacity(), 0u);
ASSERT_EQ(other.size(), 2u);
pool = std::move(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(other.capacity(), 0u);
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
pool.swap(other);
pool = std::move(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(other.capacity(), 0u);
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
pool.clear();
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
};
test::throwing_allocator<entt::entity> allocator{};
test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{allocator}, allocator);
test(entt::sigh_storage_mixin<entt::basic_storage<std::true_type, entt::entity, test::throwing_allocator<std::true_type>>>{allocator}, allocator);
test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{allocator}, allocator);
}
TEST(SighStorageMixin, ThrowingAllocator) {
auto test = [](auto pool) {
using pool_allocator_type = typename decltype(pool)::allocator_type;
using value_type = typename decltype(pool)::value_type;
using registry_type = typename decltype(pool)::registry_type;
typename std::decay_t<decltype(pool)>::base_type &base = pool;
constexpr auto packed_page_size = entt::component_traits<typename decltype(pool)::value_type>::page_size;
constexpr auto sparse_page_size = entt::entt_traits<typename decltype(pool)::entity_type>::page_size;
registry_type registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().template connect<&listener<registry_type>>(on_construct);
pool.on_destroy().template connect<&listener<registry_type>>(on_destroy);
pool_allocator_type::trigger_on_allocate = true;
ASSERT_THROW(pool.reserve(1u), typename pool_allocator_type::exception_type);
ASSERT_EQ(pool.capacity(), 0u);
pool_allocator_type::trigger_after_allocate = true;
ASSERT_THROW(pool.reserve(2 * packed_page_size), typename pool_allocator_type::exception_type);
ASSERT_EQ(pool.capacity(), packed_page_size);
pool.shrink_to_fit();
ASSERT_EQ(pool.capacity(), 0u);
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_FALSE(pool.contains(entt::entity{0}));
ASSERT_TRUE(pool.empty());
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
ASSERT_THROW(base.emplace(entt::entity{0}), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_FALSE(base.contains(entt::entity{0}));
ASSERT_TRUE(base.empty());
pool_allocator_type::trigger_on_allocate = true;
ASSERT_THROW(pool.emplace(entt::entity{0}, 0), typename pool_allocator_type::exception_type);
ASSERT_FALSE(pool.contains(entt::entity{0}));
ASSERT_NO_FATAL_FAILURE(pool.compact());
ASSERT_TRUE(pool.empty());
pool.emplace(entt::entity{0}, 0);
const entt::entity entities[2u]{entt::entity{1}, entt::entity{sparse_page_size}};
test::throwing_allocator<entt::entity>::trigger_after_allocate = true;
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), value_type{0}), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size}));
pool.erase(entt::entity{1});
const value_type components[2u]{value_type{1}, value_type{sparse_page_size}};
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
pool.compact();
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size}));
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
};
test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{});
test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{});
}
TEST(SighStorageMixin, ThrowingComponent) {
entt::sigh_storage_mixin<entt::storage<test::throwing_type>> pool;
using registry_type = typename decltype(pool)::registry_type;
registry_type registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<registry_type>>(on_construct);
pool.on_destroy().connect<&listener<registry_type>>(on_destroy);
test::throwing_type::trigger_on_value = 42;
// strong exception safety
ASSERT_THROW(pool.emplace(entt::entity{0}, test::throwing_type{42}), typename test::throwing_type::exception_type);
ASSERT_TRUE(pool.empty());
const entt::entity entities[2u]{entt::entity{42}, entt::entity{1}};
const test::throwing_type components[2u]{42, 1};
// basic exception safety
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), test::throwing_type{42}), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 0u);
ASSERT_FALSE(pool.contains(entt::entity{1}));
// basic exception safety
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 0u);
ASSERT_FALSE(pool.contains(entt::entity{1}));
// basic exception safety
ASSERT_THROW(pool.insert(std::rbegin(entities), std::rend(entities), std::rbegin(components)), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_EQ(pool.get(entt::entity{1}), 1);
pool.clear();
pool.emplace(entt::entity{1}, 1);
pool.emplace(entt::entity{42}, 42);
// basic exception safety
ASSERT_THROW(pool.erase(entt::entity{1}), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 2u);
ASSERT_TRUE(pool.contains(entt::entity{42}));
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_EQ(pool.at(0u), entt::entity{1});
ASSERT_EQ(pool.at(1u), entt::entity{42});
ASSERT_EQ(pool.get(entt::entity{42}), 42);
// the element may have been moved but it's still there
ASSERT_EQ(pool.get(entt::entity{1}), test::throwing_type::moved_from_value);
test::throwing_type::trigger_on_value = 99;
pool.erase(entt::entity{1});
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entt::entity{42}));
ASSERT_FALSE(pool.contains(entt::entity{1}));
ASSERT_EQ(pool.at(0u), entt::entity{42});
ASSERT_EQ(pool.get(entt::entity{42}), 42);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 3);
}

1290
test/entt/entity/view.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,432 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/graph/adjacency_matrix.hpp>
#include "../common/throwing_allocator.hpp"
TEST(AdjacencyMatrix, Resize) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2};
adjacency_matrix.insert(1u, 0u);
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(1u, 0u));
adjacency_matrix.resize(3u);
ASSERT_EQ(adjacency_matrix.size(), 3u);
ASSERT_TRUE(adjacency_matrix.contains(1u, 0u));
}
TEST(AdjacencyMatrix, Constructors) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
ASSERT_EQ(adjacency_matrix.size(), 0u);
adjacency_matrix = entt::adjacency_matrix<entt::directed_tag>{std::allocator<bool>{}};
adjacency_matrix = entt::adjacency_matrix<entt::directed_tag>{3u, std::allocator<bool>{}};
ASSERT_EQ(adjacency_matrix.size(), 3u);
adjacency_matrix.insert(0u, 1u);
entt::adjacency_matrix<entt::directed_tag> temp{adjacency_matrix, adjacency_matrix.get_allocator()};
entt::adjacency_matrix<entt::directed_tag> other{std::move(adjacency_matrix), adjacency_matrix.get_allocator()};
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
}
TEST(AdjacencyMatrix, Copy) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
entt::adjacency_matrix<entt::directed_tag> other{adjacency_matrix};
ASSERT_EQ(adjacency_matrix.size(), 3u);
ASSERT_EQ(other.size(), 3u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
adjacency_matrix.resize(4u);
adjacency_matrix.insert(0u, 2u);
other.insert(1u, 2u);
other = adjacency_matrix;
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(adjacency_matrix.size(), 4u);
ASSERT_TRUE(other.contains(0u, 1u));
ASSERT_FALSE(other.contains(1u, 2u));
ASSERT_TRUE(other.contains(0u, 2u));
}
TEST(AdjacencyMatrix, Move) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
entt::adjacency_matrix<entt::directed_tag> other{std::move(adjacency_matrix)};
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
adjacency_matrix = {};
adjacency_matrix.resize(4u);
adjacency_matrix.insert(0u, 2u);
other.insert(1u, 2u);
other = std::move(adjacency_matrix);
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_FALSE(other.contains(0u, 1u));
ASSERT_FALSE(other.contains(1u, 2u));
ASSERT_TRUE(other.contains(0u, 2u));
}
TEST(AdjacencyMatrix, Swap) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
entt::adjacency_matrix<entt::directed_tag> other{};
adjacency_matrix.insert(0u, 1u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(adjacency_matrix.size(), 3u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(other.contains(0u, 1u));
adjacency_matrix.swap(other);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
}
TEST(AdjacencyMatrix, InsertDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto first = adjacency_matrix.insert(0u, 1u);
auto second = adjacency_matrix.insert(0u, 2u);
auto other = adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(first.second);
ASSERT_TRUE(second.second);
ASSERT_FALSE(other.second);
ASSERT_NE(first.first, second.first);
ASSERT_EQ(first.first, other.first);
ASSERT_EQ(*first.first, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*second.first, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(2u, 0u));
}
TEST(AdjacencyMatrix, InsertUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto first = adjacency_matrix.insert(0u, 1u);
auto second = adjacency_matrix.insert(0u, 2u);
auto other = adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(first.second);
ASSERT_TRUE(second.second);
ASSERT_FALSE(other.second);
ASSERT_NE(first.first, second.first);
ASSERT_EQ(first.first, other.first);
ASSERT_EQ(*first.first, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*second.first, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(2u, 0u));
}
TEST(AdjacencyMatrix, EraseDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(1u, 0u));
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 1u);
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 0u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(1u, 0u));
}
TEST(AdjacencyMatrix, EraseUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(1u, 0u));
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 1u);
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 0u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(1u, 0u));
}
TEST(AdjacencyMatrix, Clear) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(0u, 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(0u, 2u));
ASSERT_EQ(adjacency_matrix.size(), 3u);
adjacency_matrix.clear();
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(0u, 2u));
ASSERT_EQ(adjacency_matrix.size(), 0u);
}
TEST(AdjacencyMatrix, VertexIterator) {
using iterator = typename entt::adjacency_matrix<entt::directed_tag>::vertex_iterator;
static_assert(std::is_same_v<iterator::value_type, std::size_t>);
static_assert(std::is_same_v<iterator::pointer, void>);
static_assert(std::is_same_v<iterator::reference, std::size_t>);
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2u};
const auto iterable = adjacency_matrix.vertices();
iterator end{iterable.begin()};
iterator begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.begin());
ASSERT_EQ(end, iterable.end());
ASSERT_NE(begin, end);
ASSERT_EQ(*begin, 0u);
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(*begin, 1u);
ASSERT_EQ(++begin, iterable.end());
}
TEST(AdjacencyMatrix, EdgeIterator) {
using iterator = typename entt::adjacency_matrix<entt::directed_tag>::edge_iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<std::size_t, std::size_t>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<std::size_t, std::size_t>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<std::size_t, std::size_t>>);
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(0u, 2u);
const auto iterable = adjacency_matrix.edges();
iterator end{iterable.begin()};
iterator begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.begin());
ASSERT_EQ(end, iterable.end());
ASSERT_NE(begin, end);
ASSERT_EQ(*begin, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(*begin.operator->(), std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_EQ(++begin, iterable.end());
}
TEST(AdjacencyMatrix, Vertices) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
auto iterable = adjacency_matrix.vertices();
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.resize(2u);
iterable = adjacency_matrix.vertices();
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, 0u);
ASSERT_EQ(*it, 1u);
ASSERT_EQ(++it, iterable.end());
}
TEST(AdjacencyMatrix, EdgesDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.edges();
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.edges();
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*it.operator->(), std::make_pair(std::size_t{1u}, std::size_t{2u}));
ASSERT_EQ(++it, iterable.end());
}
TEST(AdjacencyMatrix, EdgesUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.edges();
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.edges();
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*it.operator->(), std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(*(++it).operator->(), std::make_pair(std::size_t{1u}, std::size_t{2u}));
ASSERT_EQ(*++it, std::make_pair(std::size_t{2u}, std::size_t{1u}));
ASSERT_EQ(++it, iterable.end());
}
TEST(AdjacencyMatrix, OutEdgesDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.out_edges(0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.out_edges(0u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.out_edges(2u);
it = iterable.cbegin();
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, OutEdgesUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.out_edges(0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.out_edges(0u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.out_edges(2u);
it = iterable.cbegin();
ASSERT_NE(it, iterable.cend());
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, InEdgesDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.in_edges(1u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.in_edges(1u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.in_edges(0u);
it = iterable.cbegin();
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, InEdgesUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.in_edges(1u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.in_edges(1u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.in_edges(0u);
it = iterable.cbegin();
ASSERT_NE(it, iterable.cend());
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, ThrowingAllocator) {
using allocator = test::throwing_allocator<std::size_t>;
using exception = typename allocator::exception_type;
entt::adjacency_matrix<entt::directed_tag, allocator> adjacency_matrix{2u};
adjacency_matrix.insert(0u, 1u);
allocator::trigger_on_allocate = true;
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_THROW(adjacency_matrix.resize(4u), exception);
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
}

62
test/entt/graph/dot.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <sstream>
#include <string>
#include <gtest/gtest.h>
#include <entt/graph/adjacency_matrix.hpp>
#include <entt/graph/dot.hpp>
TEST(Dot, DirectedGraph) {
std::ostringstream output{};
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
adjacency_matrix.insert(0u, 2u);
entt::dot(output, adjacency_matrix);
const std::string expected = "digraph{0[];1[];2[];0->1;0->2;1->2;}";
const auto str = output.str();
ASSERT_FALSE(str.empty());
ASSERT_EQ(str, expected);
}
TEST(Dot, UndirectedGraph) {
std::ostringstream output{};
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
adjacency_matrix.insert(0u, 2u);
entt::dot(output, adjacency_matrix);
const std::string expected = "graph{0[];1[];2[];0--1;0--2;1--0;1--2;2--0;2--1;}";
const auto str = output.str();
ASSERT_FALSE(str.empty());
ASSERT_EQ(str, expected);
}
TEST(Dot, CustomWriter) {
std::ostringstream output{};
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
adjacency_matrix.insert(0u, 2u);
entt::dot(output, adjacency_matrix, [&adjacency_matrix](std::ostream &out, std::size_t vertex) {
out << "label=\"v" << vertex << "\"";
if(auto in_edges = adjacency_matrix.in_edges(vertex); in_edges.cbegin() == in_edges.cend()) {
out << ",shape=\"box\"";
}
});
const std::string expected = "digraph{0[label=\"v0\",shape=\"box\"];1[label=\"v1\"];2[label=\"v2\"];0->1;0->2;1->2;}";
const auto str = output.str();
ASSERT_FALSE(str.empty());
ASSERT_EQ(str, expected);
}

289
test/entt/graph/flow.cpp Normal file
View File

@ -0,0 +1,289 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/graph/flow.hpp>
#include "../common/throwing_allocator.hpp"
TEST(Flow, Constructors) {
entt::flow flow{};
ASSERT_EQ(flow.size(), 0u);
flow = entt::flow{std::allocator<entt::id_type>{}};
ASSERT_EQ(flow.size(), 0u);
flow.bind(0);
flow.bind(3);
flow.bind(99);
ASSERT_EQ(flow.size(), 3u);
entt::flow temp{flow, flow.get_allocator()};
entt::flow other{std::move(flow), flow.get_allocator()};
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
}
TEST(Flow, Copy) {
entt::flow flow{};
flow.bind(0);
flow.bind(3);
flow.bind(99);
entt::flow other{flow};
ASSERT_EQ(flow.size(), 3u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
flow.bind(1);
other.bind(2);
other = flow;
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(flow.size(), 4u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
ASSERT_EQ(other[3u], 1);
}
TEST(Flow, Move) {
entt::flow flow{};
flow.bind(0);
flow.bind(3);
flow.bind(99);
entt::flow other{std::move(flow)};
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
flow = {};
flow.bind(1);
other.bind(2);
other = std::move(flow);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other[0u], 1);
}
TEST(Flow, Swap) {
entt::flow flow{};
entt::flow other{};
flow.bind(7);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(flow.size(), 1u);
ASSERT_EQ(flow[0u], 7);
flow.swap(other);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other[0u], 7);
}
TEST(Flow, Clear) {
entt::flow flow{};
flow.bind(0);
flow.bind(99);
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow[0u], 0);
ASSERT_EQ(flow[1u], 99);
flow.clear();
ASSERT_EQ(flow.size(), 0u);
}
TEST(Flow, Set) {
entt::flow flow{};
flow.bind(0).set(10, true).bind(1).set(10, true).set(11, false);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_NE(graph.edges().cbegin(), graph.edges().cend());
ASSERT_TRUE(graph.contains(0u, 1u));
ASSERT_FALSE(graph.contains(1u, 0u));
}
TEST(Flow, RO) {
entt::flow flow{};
flow.bind(0).ro(10).bind(1).ro(10).ro(11);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(graph.edges().cbegin(), graph.edges().cend());
}
TEST(Flow, RangeRO) {
entt::flow flow{};
const entt::id_type res[2u]{10, 11};
flow.bind(0).ro(res, res + 1).bind(1).ro(res, res + 2);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(graph.edges().cbegin(), graph.edges().cend());
}
TEST(Flow, RW) {
entt::flow flow{};
flow.bind(0).rw(10).bind(1).rw(10).rw(11);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_NE(graph.edges().cbegin(), graph.edges().cend());
ASSERT_TRUE(graph.contains(0u, 1u));
ASSERT_FALSE(graph.contains(1u, 0u));
}
TEST(Flow, RangeRW) {
entt::flow flow{};
const entt::id_type res[2u]{10, 11};
flow.bind(0).rw(res, res + 1).bind(1).rw(res, res + 2);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_NE(graph.edges().cbegin(), graph.edges().cend());
ASSERT_TRUE(graph.contains(0u, 1u));
ASSERT_FALSE(graph.contains(1u, 0u));
}
TEST(Flow, Graph) {
using namespace entt::literals;
entt::flow flow{};
flow.bind("task_0"_hs)
.ro("resource_0"_hs)
.rw("resource_1"_hs);
flow.bind("task_1"_hs)
.ro("resource_0"_hs)
.rw("resource_2"_hs);
flow.bind("task_2"_hs)
.ro("resource_1"_hs)
.rw("resource_3"_hs);
flow.bind("task_3"_hs)
.rw("resource_1"_hs)
.ro("resource_2"_hs);
flow.bind("task_4"_hs)
.rw("resource_0"_hs);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 5u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(flow[0u], "task_0"_hs);
ASSERT_EQ(flow[1u], "task_1"_hs);
ASSERT_EQ(flow[2u], "task_2"_hs);
ASSERT_EQ(flow[3u], "task_3"_hs);
ASSERT_EQ(flow[4u], "task_4"_hs);
auto it = graph.edges().cbegin();
const auto last = graph.edges().cend();
ASSERT_NE(it, last);
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{4u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{3u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{4u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{3u}));
ASSERT_EQ(it, last);
}
TEST(Flow, Sync) {
using namespace entt::literals;
entt::flow flow{};
flow.bind("task_0"_hs)
.ro("resource_0"_hs);
flow.bind("task_1"_hs)
.rw("resource_1"_hs);
flow.bind("task_2"_hs)
.sync();
flow.bind("task_3"_hs)
.ro("resource_0"_hs)
.rw("resource_2"_hs);
flow.bind("task_4"_hs)
.ro("resource_2"_hs);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 5u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(flow[0u], "task_0"_hs);
ASSERT_EQ(flow[1u], "task_1"_hs);
ASSERT_EQ(flow[2u], "task_2"_hs);
ASSERT_EQ(flow[3u], "task_3"_hs);
ASSERT_EQ(flow[4u], "task_4"_hs);
auto it = graph.edges().cbegin();
const auto last = graph.edges().cend();
ASSERT_NE(it, last);
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{2u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{3u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{3u}, std::size_t{4u}));
ASSERT_EQ(it, last);
}
TEST(Flow, ThrowingAllocator) {
using allocator = test::throwing_allocator<entt::id_type>;
using task_allocator = test::throwing_allocator<std::pair<std::size_t, entt::id_type>>;
using task_exception = typename task_allocator::exception_type;
entt::basic_flow<allocator> flow{};
task_allocator::trigger_on_allocate = true;
ASSERT_EQ(flow.size(), 0u);
ASSERT_THROW(flow.bind(1), task_exception);
ASSERT_EQ(flow.size(), 0u);
flow.bind(1);
ASSERT_EQ(flow.size(), 1u);
}

View File

@ -0,0 +1,78 @@
#include <memory>
#include <gtest/gtest.h>
#include <entt/locator/locator.hpp>
#include "../common/config.h"
struct base_service {
virtual ~base_service() = default;
virtual void invoke() {}
};
struct null_service: base_service {
void invoke() override {
invoked = true;
}
static inline bool invoked{};
};
struct derived_service: base_service {
void invoke() override {
invoked = true;
}
static inline bool invoked{};
};
struct ServiceLocator: ::testing::Test {
void SetUp() override {
null_service::invoked = false;
derived_service::invoked = false;
}
};
using ServiceLocatorDeathTest = ServiceLocator;
TEST(ServiceLocator, Functionalities) {
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_FALSE(derived_service::invoked);
ASSERT_FALSE(null_service::invoked);
entt::locator<base_service>::value_or<null_service>().invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(null_service::invoked);
auto handle = entt::locator<base_service>::handle();
entt::locator<base_service>::reset();
ASSERT_FALSE(entt::locator<base_service>::has_value());
entt::locator<base_service>::reset(handle);
ASSERT_TRUE(entt::locator<base_service>::has_value());
entt::locator<base_service>::reset(decltype(handle){});
ASSERT_FALSE(entt::locator<base_service>::has_value());
entt::locator<base_service>::emplace<derived_service>();
entt::locator<base_service>::value().invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(derived_service::invoked);
derived_service::invoked = false;
entt::locator<base_service>::allocate_emplace<derived_service>(std::allocator<derived_service>{}).invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(derived_service::invoked);
}
ENTT_DEBUG_TEST(ServiceLocatorDeathTest, UninitializedValue) {
ASSERT_NO_FATAL_FAILURE(entt::locator<base_service>::value_or().invoke());
entt::locator<base_service>::reset();
ASSERT_DEATH(entt::locator<base_service>::value().invoke(), "");
}

1371
test/entt/meta/meta_any.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct base_1_t {
base_1_t() = default;
int value_1{};
};
struct base_2_t {
base_2_t() = default;
operator int() const {
return value_2;
}
int value_2{};
};
struct base_3_t: base_2_t {
base_3_t() = default;
int value_3{};
};
struct derived_t: base_1_t, base_3_t {
derived_t() = default;
int value{};
};
struct MetaBase: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<base_1_t>()
.data<&base_1_t::value_1>("value_1"_hs);
entt::meta<base_2_t>()
.conv<int>()
.data<&base_2_t::value_2>("value_2"_hs);
entt::meta<base_3_t>()
.base<base_2_t>()
.data<&base_3_t::value_3>("value_3"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_1_t>()
.base<base_3_t>()
.data<&derived_t::value>("value"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaBase, Functionalities) {
auto any = entt::resolve<derived_t>().construct();
any.cast<derived_t &>().value_1 = 42;
auto as_derived = any.as_ref();
ASSERT_TRUE(any.allow_cast<base_1_t &>());
ASSERT_FALSE(any.allow_cast<char>());
ASSERT_FALSE(as_derived.allow_cast<char>());
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<base_1_t &>().value_1, as_derived.cast<derived_t &>().value_1);
any.cast<base_1_t &>().value_1 = 3;
ASSERT_EQ(any.cast<const base_1_t &>().value_1, as_derived.cast<const derived_t &>().value_1);
}
TEST_F(MetaBase, SetGetWithMutatingThis) {
using namespace entt::literals;
derived_t instance;
auto any = entt::forward_as_meta(instance);
auto as_cref = std::as_const(any).as_ref();
ASSERT_NE(static_cast<const void *>(static_cast<const base_1_t *>(&instance)), static_cast<const void *>(static_cast<const base_2_t *>(&instance)));
ASSERT_NE(static_cast<const void *>(static_cast<const base_1_t *>(&instance)), static_cast<const void *>(static_cast<const base_3_t *>(&instance)));
ASSERT_EQ(static_cast<const void *>(static_cast<const base_2_t *>(&instance)), static_cast<const void *>(static_cast<const base_3_t *>(&instance)));
ASSERT_EQ(static_cast<const void *>(&instance), static_cast<const void *>(static_cast<const base_1_t *>(&instance)));
ASSERT_TRUE(any.set("value"_hs, 42));
ASSERT_TRUE(any.set("value_1"_hs, 1));
ASSERT_TRUE(any.set("value_2"_hs, 2));
ASSERT_TRUE(any.set("value_3"_hs, 3));
ASSERT_FALSE(as_cref.set("value"_hs, 0));
ASSERT_FALSE(as_cref.set("value_1"_hs, 0));
ASSERT_FALSE(as_cref.set("value_2"_hs, 0));
ASSERT_FALSE(as_cref.set("value_3"_hs, 0));
ASSERT_EQ(any.get("value"_hs).cast<int>(), 42);
ASSERT_EQ(any.get("value_1"_hs).cast<const int>(), 1);
ASSERT_EQ(any.get("value_2"_hs).cast<int>(), 2);
ASSERT_EQ(any.get("value_3"_hs).cast<const int>(), 3);
ASSERT_EQ(as_cref.get("value"_hs).cast<const int>(), 42);
ASSERT_EQ(as_cref.get("value_1"_hs).cast<int>(), 1);
ASSERT_EQ(as_cref.get("value_2"_hs).cast<const int>(), 2);
ASSERT_EQ(as_cref.get("value_3"_hs).cast<int>(), 3);
ASSERT_EQ(instance.value, 42);
ASSERT_EQ(instance.value_1, 1);
ASSERT_EQ(instance.value_2, 2);
ASSERT_EQ(instance.value_3, 3);
}
TEST_F(MetaBase, ConvWithMutatingThis) {
entt::meta_any any{derived_t{}};
auto &&ref = any.cast<derived_t &>();
auto as_cref = std::as_const(any).as_ref();
ref.value_2 = 42;
auto conv = std::as_const(any).allow_cast<int>();
auto from_cref = std::as_const(as_cref).allow_cast<int>();
ASSERT_TRUE(conv);
ASSERT_TRUE(from_cref);
ASSERT_EQ(conv.cast<int>(), 42);
ASSERT_EQ(from_cref.cast<int>(), 42);
ASSERT_TRUE(as_cref.allow_cast<int>());
ASSERT_TRUE(any.allow_cast<int>());
ASSERT_EQ(as_cref.cast<int>(), 42);
ASSERT_EQ(any.cast<int>(), 42);
}
TEST_F(MetaBase, OpaqueConvWithMutatingThis) {
entt::meta_any any{derived_t{}};
auto as_cref = std::as_const(any).as_ref();
any.cast<derived_t &>().value_2 = 42;
auto conv = std::as_const(any).allow_cast(entt::resolve<int>());
auto from_cref = std::as_const(as_cref).allow_cast(entt::resolve<int>());
ASSERT_TRUE(conv);
ASSERT_TRUE(from_cref);
ASSERT_EQ(conv.cast<int>(), 42);
ASSERT_EQ(from_cref.cast<int>(), 42);
ASSERT_TRUE(as_cref.allow_cast(entt::resolve<int>()));
ASSERT_TRUE(any.allow_cast(entt::resolve<int>()));
ASSERT_EQ(as_cref.cast<int>(), 42);
ASSERT_EQ(any.cast<int>(), 42);
}
TEST_F(MetaBase, AssignWithMutatingThis) {
using namespace entt::literals;
entt::meta_any dst{base_2_t{}};
entt::meta_any src{derived_t{}};
dst.cast<base_2_t &>().value_2 = 0;
src.cast<derived_t &>().value_2 = 42;
ASSERT_TRUE(dst.assign(src));
ASSERT_EQ(dst.get("value_2"_hs).cast<int>(), 42);
}
TEST_F(MetaBase, TransferWithMutatingThis) {
using namespace entt::literals;
entt::meta_any dst{base_2_t{}};
entt::meta_any src{derived_t{}};
dst.cast<base_2_t &>().value_2 = 0;
src.cast<derived_t &>().value_2 = 42;
ASSERT_TRUE(dst.assign(std::move(src)));
ASSERT_EQ(dst.get("value_2"_hs).cast<int>(), 42);
}
TEST_F(MetaBase, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<derived_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->base.empty());
ASSERT_EQ(node.details->base.size(), 2u);
}

View File

@ -0,0 +1,757 @@
#include <array>
#include <deque>
#include <list>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
struct invalid_type {};
struct MetaContainer: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs);
entt::meta<int>()
.type("int"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaContainerDeathTest = MetaContainer;
TEST_F(MetaContainer, InvalidContainer) {
ASSERT_FALSE(entt::meta_any{42}.as_sequence_container());
ASSERT_FALSE(entt::meta_any{42}.as_associative_container());
ASSERT_FALSE((entt::meta_any{std::map<int, char>{}}.as_sequence_container()));
ASSERT_FALSE(entt::meta_any{std::vector<int>{}}.as_associative_container());
}
TEST_F(MetaContainer, EmptySequenceContainer) {
entt::meta_sequence_container container{};
ASSERT_FALSE(container);
entt::meta_any any{std::vector<int>{}};
container = any.as_sequence_container();
ASSERT_TRUE(container);
}
TEST_F(MetaContainer, EmptyAssociativeContainer) {
entt::meta_associative_container container{};
ASSERT_FALSE(container);
entt::meta_any any{std::map<int, char>{}};
container = any.as_associative_container();
ASSERT_TRUE(container);
}
TEST_F(MetaContainer, SequenceContainerIterator) {
std::vector<int> vec{2, 3, 4};
auto any = entt::forward_as_meta(vec);
entt::meta_sequence_container::iterator first{};
auto view = any.as_sequence_container();
ASSERT_FALSE(first);
first = view.begin();
const auto last = view.end();
ASSERT_TRUE(first);
ASSERT_TRUE(last);
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_EQ((first++)->cast<int>(), 2);
ASSERT_EQ((++first)->cast<int>(), 4);
ASSERT_NE(first++, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
ASSERT_EQ(first--, last);
ASSERT_EQ((first--)->cast<int>(), 4);
ASSERT_EQ((--first)->cast<int>(), 2);
}
TEST_F(MetaContainer, AssociativeContainerIterator) {
std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
auto any = entt::forward_as_meta(map);
entt::meta_associative_container::iterator first{};
auto view = any.as_associative_container();
ASSERT_FALSE(first);
first = view.begin();
const auto last = view.end();
ASSERT_TRUE(first);
ASSERT_TRUE(last);
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_NE(first, last);
ASSERT_EQ((first++)->first.cast<int>(), 2);
ASSERT_EQ((++first)->second.cast<char>(), 'e');
ASSERT_NE(first++, last);
ASSERT_EQ(first, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
}
TEST_F(MetaContainer, StdVector) {
std::vector<int> vec{};
auto any = entt::forward_as_meta(vec);
auto view = any.as_sequence_container();
auto cview = std::as_const(any).as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, 1.));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<int>(), 0);
ASSERT_EQ((++view.begin())->cast<int>(), 1);
ret = view.insert(cview.end(), 42);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 42);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(ret->cast<int>(), 1);
ret = view.erase(cview.begin());
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<int>(), 2);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdArray) {
std::array<int, 3> arr{};
auto any = entt::forward_as_meta(arr);
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_FALSE(view.resize(5u));
ASSERT_EQ(view.size(), 3u);
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_FALSE(ret);
ASSERT_FALSE(view.insert(it, 'c'));
ASSERT_FALSE(view.insert(++it, 1.));
ASSERT_EQ(view.size(), 3u);
ASSERT_EQ(view.begin()->cast<int>(), 2);
ASSERT_EQ((++view.begin())->cast<int>(), 3);
it = view.begin();
ret = view.erase(it);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 3u);
ASSERT_EQ(it->cast<int>(), 2);
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 3u);
}
TEST_F(MetaContainer, StdList) {
std::list<int> list{};
auto any = entt::forward_as_meta(list);
auto view = any.as_sequence_container();
auto cview = std::as_const(any).as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, 1.));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<int>(), 0);
ASSERT_EQ((++view.begin())->cast<int>(), 1);
ret = view.insert(cview.end(), 42);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 42);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(ret->cast<int>(), 1);
ret = view.erase(cview.begin());
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<int>(), 2);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdDeque) {
std::deque<int> deque{};
auto any = entt::forward_as_meta(deque);
auto view = any.as_sequence_container();
auto cview = std::as_const(any).as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, 1.));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<int>(), 0);
ASSERT_EQ((++view.begin())->cast<int>(), 1);
ret = view.insert(cview.end(), 42);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 42);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(ret->cast<int>(), 1);
ret = view.erase(cview.begin());
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<int>(), 2);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdMap) {
std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
auto any = entt::forward_as_meta(map);
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_FALSE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::resolve<char>());
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->second.cast<char>(), 'd');
ASSERT_FALSE(view.insert(invalid_type{}, 'a'));
ASSERT_FALSE(view.insert(1, invalid_type{}));
ASSERT_TRUE(view.insert(0, 'a'));
ASSERT_TRUE(view.insert(1., static_cast<int>('b')));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->second.cast<char>(), 'a');
ASSERT_EQ(view.find(1.)->second.cast<char>(), 'b');
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
view.find(1.)->second.cast<char &>() = 'f';
ASSERT_EQ(view.find(1.f)->second.cast<char>(), 'f');
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdSet) {
std::set<int> set{2, 3, 4};
auto any = entt::forward_as_meta(set);
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_TRUE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::meta_type{});
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->first.cast<int>(), 3);
ASSERT_FALSE(view.insert(invalid_type{}));
ASSERT_TRUE(view.insert(.0));
ASSERT_TRUE(view.insert(1));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->first.cast<int>(), 0);
ASSERT_EQ(view.find(1.)->first.cast<int>(), 1);
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(1.f)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(1.)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(true)->first.cast<const int &>(), 1);
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, DenseMap) {
entt::dense_map<int, char> map{};
auto any = entt::forward_as_meta(map);
auto view = any.as_associative_container();
map.emplace(2, 'c');
map.emplace(3, 'd');
map.emplace(4, '3');
ASSERT_TRUE(view);
ASSERT_FALSE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::resolve<char>());
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->second.cast<char>(), 'd');
ASSERT_FALSE(view.insert(invalid_type{}, 'a'));
ASSERT_FALSE(view.insert(1, invalid_type{}));
ASSERT_TRUE(view.insert(0, 'a'));
ASSERT_TRUE(view.insert(1., static_cast<int>('b')));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->second.cast<char>(), 'a');
ASSERT_EQ(view.find(1.)->second.cast<char>(), 'b');
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
view.find(1.)->second.cast<char &>() = 'f';
ASSERT_EQ(view.find(1.f)->second.cast<char>(), 'f');
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, DenseSet) {
entt::dense_set<int> set{};
auto any = entt::forward_as_meta(set);
auto view = any.as_associative_container();
set.emplace(2);
set.emplace(3);
set.emplace(4);
ASSERT_TRUE(view);
ASSERT_TRUE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::meta_type{});
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->first.cast<int>(), 3);
ASSERT_FALSE(view.insert(invalid_type{}));
ASSERT_TRUE(view.insert(.0));
ASSERT_TRUE(view.insert(1));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->first.cast<int>(), 0);
ASSERT_EQ(view.find(1.)->first.cast<int>(), 1);
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(1.f)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(1.)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(true)->first.cast<const int &>(), 1);
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, ConstSequenceContainer) {
std::vector<int> vec{};
auto any = entt::forward_as_meta(std::as_const(vec));
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_FALSE(view.resize(3u));
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
vec.push_back(42);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view[0].cast<const int &>(), 42);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(it->cast<int>(), 42);
ASSERT_EQ(++it, view.end());
it = view.begin();
ret = view.erase(it);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 1u);
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 1u);
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, ConstSequenceContainer) {
std::vector<int> vec{};
auto any = entt::forward_as_meta(std::as_const(vec));
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view[0].cast<int &>() = 2, "");
}
TEST_F(MetaContainer, ConstKeyValueAssociativeContainer) {
std::map<int, char> map{};
auto any = entt::forward_as_meta(std::as_const(map));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_FALSE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::resolve<char>());
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
map[2] = 'c';
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(2)->second.cast<const char &>(), 'c');
ASSERT_FALSE(view.insert(0, 'a'));
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(2)->second.cast<char>(), 'c');
ASSERT_EQ(view.erase(2), 0u);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.find(2), view.end());
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 1u);
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, ConstKeyValueAssociativeContainer) {
std::map<int, char> map{};
auto any = entt::forward_as_meta(std::as_const(map));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view.find(2)->second.cast<char &>() = 'a', "");
}
TEST_F(MetaContainer, ConstKeyOnlyAssociativeContainer) {
std::set<int> set{};
auto any = entt::forward_as_meta(std::as_const(set));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_TRUE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::meta_type{});
ASSERT_EQ(view.value_type(), (entt::resolve<int>()));
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
set.insert(2);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(2)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(2)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(2)->first.cast<int>(), 2);
ASSERT_EQ(view.find(2)->first.cast<const int &>(), 2);
ASSERT_FALSE(view.insert(0));
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(2)->first.cast<int>(), 2);
ASSERT_EQ(view.erase(2), 0u);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.find(2), view.end());
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 1u);
}
TEST_F(MetaContainer, SequenceContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view[0].cast<const int &>(), 42);
};
std::vector<int> vec{42};
test(vec);
test(entt::forward_as_meta(vec));
test(entt::forward_as_meta(std::as_const(vec)));
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, SequenceContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view[0].cast<int &>() = 2, "");
};
std::vector<int> vec{42};
test(vec);
test(entt::forward_as_meta(vec));
test(entt::forward_as_meta(std::as_const(vec)));
}
TEST_F(MetaContainer, KeyValueAssociativeContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.find(2)->second.cast<const char &>(), 'c');
};
std::map<int, char> map{{2, 'c'}};
test(map);
test(entt::forward_as_meta(map));
test(entt::forward_as_meta(std::as_const(map)));
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, KeyValueAssociativeContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view.find(2)->second.cast<char &>() = 'a', "");
};
std::map<int, char> map{{2, 'c'}};
test(map);
test(entt::forward_as_meta(map));
test(entt::forward_as_meta(std::as_const(map)));
}
TEST_F(MetaContainer, KeyOnlyAssociativeContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), (entt::resolve<int>()));
ASSERT_EQ(view.find(2)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(2)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(2)->first.cast<int>(), 2);
ASSERT_EQ(view.find(2)->first.cast<const int &>(), 2);
};
std::set<int> set{2};
test(set);
test(entt::forward_as_meta(set));
test(entt::forward_as_meta(std::as_const(set)));
}
TEST_F(MetaContainer, StdVectorBool) {
using proxy_type = typename std::vector<bool>::reference;
using const_proxy_type = typename std::vector<bool>::const_reference;
std::vector<bool> vec{};
auto any = entt::forward_as_meta(vec);
auto cany = std::as_const(any).as_ref();
auto view = any.as_sequence_container();
auto cview = cany.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<bool>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<proxy_type>() = true;
view[1].cast<proxy_type>() = true;
view[2].cast<proxy_type>() = false;
ASSERT_EQ(cview[1u].cast<const_proxy_type>(), true);
auto it = view.begin();
auto ret = view.insert(it, true);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, false));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<proxy_type>(), true);
ASSERT_EQ((++cview.begin())->cast<const_proxy_type>(), false);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<proxy_type>(), false);
ASSERT_TRUE(view.clear());
ASSERT_EQ(cview.size(), 0u);
}

View File

@ -0,0 +1,505 @@
#include <unordered_map>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/template.hpp>
struct base {
base() = default;
base(char v)
: value{v} {}
char get() const {
return value;
}
char value;
};
struct clazz: base {
clazz() = default;
clazz(int v)
: base{},
value{v} {}
clazz(char c, int v)
: base{c},
value{v} {}
int func(int v) {
return (value = v);
}
int cfunc(int v) const {
return v;
}
static void move_to_bucket(const clazz &instance) {
bucket = instance.value;
}
int value{};
static inline int bucket{};
};
struct local_only {};
struct argument {
argument(int val)
: value{val} {}
int get() const {
return value;
}
int get_mul() const {
return value * 2;
}
private:
int value{};
};
template<typename...>
struct template_clazz {};
class MetaContext: public ::testing::Test {
void init_global_context() {
using namespace entt::literals;
entt::meta<int>()
.data<global_marker>("marker"_hs);
entt::meta<argument>()
.conv<&argument::get>();
entt::meta<clazz>()
.type("foo"_hs)
.prop("prop"_hs, prop_value)
.ctor<int>()
.data<&clazz::value>("value"_hs)
.data<&clazz::value>("rw"_hs)
.func<&clazz::func>("func"_hs);
entt::meta<template_clazz<int>>()
.type("template"_hs);
}
void init_local_context() {
using namespace entt::literals;
entt::meta<int>(context)
.data<local_marker>("marker"_hs);
entt::meta<local_only>(context)
.type("quux"_hs);
entt::meta<argument>(context)
.conv<&argument::get_mul>();
entt::meta<base>(context)
.data<&base::value>("char"_hs)
.func<&base::get>("get"_hs);
entt::meta<clazz>(context)
.type("bar"_hs)
.prop("prop"_hs, prop_value)
.base<base>()
.ctor<char, int>()
.dtor<&clazz::move_to_bucket>()
.data<nullptr, &clazz::value>("value"_hs)
.data<&clazz::value>("rw"_hs)
.func<&clazz::cfunc>("func"_hs);
entt::meta<template_clazz<int, char>>(context)
.type("template"_hs);
}
public:
void SetUp() override {
init_global_context();
init_local_context();
clazz::bucket = bucket_value;
}
void TearDown() override {
entt::meta_reset(context);
entt::meta_reset();
}
protected:
static constexpr int global_marker = 1;
static constexpr int local_marker = 42;
static constexpr int bucket_value = 99;
static constexpr int prop_value = 3;
entt::meta_ctx context{};
};
TEST_F(MetaContext, Resolve) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve<clazz>());
ASSERT_TRUE(entt::resolve<clazz>(context));
ASSERT_TRUE(entt::resolve<local_only>());
ASSERT_TRUE(entt::resolve<local_only>(context));
ASSERT_TRUE(entt::resolve(entt::type_id<clazz>()));
ASSERT_TRUE(entt::resolve(context, entt::type_id<clazz>()));
ASSERT_FALSE(entt::resolve(entt::type_id<local_only>()));
ASSERT_TRUE(entt::resolve(context, entt::type_id<local_only>()));
ASSERT_TRUE(entt::resolve("foo"_hs));
ASSERT_FALSE(entt::resolve(context, "foo"_hs));
ASSERT_FALSE(entt::resolve("bar"_hs));
ASSERT_TRUE(entt::resolve(context, "bar"_hs));
ASSERT_FALSE(entt::resolve("quux"_hs));
ASSERT_TRUE(entt::resolve(context, "quux"_hs));
ASSERT_EQ((std::distance(entt::resolve().cbegin(), entt::resolve().cend())), 4);
ASSERT_EQ((std::distance(entt::resolve(context).cbegin(), entt::resolve(context).cend())), 6);
}
TEST_F(MetaContext, MetaType) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_NE(global, local);
ASSERT_EQ(global, entt::resolve("foo"_hs));
ASSERT_EQ(local, entt::resolve(context, "bar"_hs));
ASSERT_EQ(global.id(), "foo"_hs);
ASSERT_EQ(local.id(), "bar"_hs);
clazz instance{'c', 99};
const argument value{2};
ASSERT_NE(instance.value, value.get());
ASSERT_EQ(global.invoke("func"_hs, instance, value).cast<int>(), value.get());
ASSERT_EQ(instance.value, value.get());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_EQ(local.invoke("func"_hs, instance, value).cast<int>(), value.get_mul());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_FALSE(global.invoke("get"_hs, instance));
ASSERT_EQ(local.invoke("get"_hs, instance).cast<char>(), 'c');
}
TEST_F(MetaContext, MetaBase) {
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_EQ((std::distance(global.base().cbegin(), global.base().cend())), 0);
ASSERT_EQ((std::distance(local.base().cbegin(), local.base().cend())), 1);
ASSERT_EQ(local.base().cbegin()->second.info(), entt::type_id<base>());
ASSERT_FALSE(entt::resolve(entt::type_id<base>()));
ASSERT_TRUE(entt::resolve(context, entt::type_id<base>()));
}
TEST_F(MetaContext, MetaData) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global.data("value"_hs));
ASSERT_TRUE(local.data("value"_hs));
ASSERT_FALSE(global.data("value"_hs).is_const());
ASSERT_TRUE(local.data("value"_hs).is_const());
ASSERT_EQ(global.data("value"_hs).type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.data("value"_hs).type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(global.data("rw"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.data("rw"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), local_marker);
clazz instance{'c', 99};
const argument value{2};
ASSERT_NE(instance.value, value.get());
ASSERT_TRUE(global.data("rw"_hs).set(instance, value));
ASSERT_EQ(instance.value, value.get());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_TRUE(local.data("rw"_hs).set(instance, value));
ASSERT_EQ(instance.value, value.get_mul());
ASSERT_FALSE(global.data("char"_hs));
ASSERT_EQ(local.data("char"_hs).get(instance).cast<char>(), 'c');
ASSERT_TRUE(local.data("char"_hs).set(instance, 'x'));
ASSERT_EQ(instance.base::value, 'x');
}
TEST_F(MetaContext, MetaFunc) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global.func("func"_hs));
ASSERT_TRUE(local.func("func"_hs));
ASSERT_FALSE(global.func("func"_hs).is_const());
ASSERT_TRUE(local.func("func"_hs).is_const());
ASSERT_EQ(global.func("func"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.func("func"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(global.func("func"_hs).ret().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.func("func"_hs).ret().data("marker"_hs).get({}).cast<int>(), local_marker);
clazz instance{'c', 99};
const argument value{2};
ASSERT_NE(instance.value, value.get());
ASSERT_EQ(global.func("func"_hs).invoke(instance, value).cast<int>(), value.get());
ASSERT_EQ(instance.value, value.get());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_EQ(local.func("func"_hs).invoke(instance, value).cast<int>(), value.get_mul());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_FALSE(global.func("get"_hs));
ASSERT_EQ(local.func("get"_hs).invoke(instance).cast<char>(), 'c');
}
TEST_F(MetaContext, MetaCtor) {
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
auto any = global.construct();
auto other = local.construct();
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.cast<const clazz &>().value, 0);
ASSERT_EQ(other.cast<const clazz &>().value, 0);
argument argument{2};
any = global.construct(argument);
other = local.construct(argument);
ASSERT_TRUE(any);
ASSERT_FALSE(other);
ASSERT_EQ(any.cast<const clazz &>().value, 2);
any = global.construct('c', argument);
other = local.construct('c', argument);
ASSERT_FALSE(any);
ASSERT_TRUE(other);
ASSERT_EQ(other.cast<const clazz &>().value, 4);
}
TEST_F(MetaContext, MetaConv) {
argument value{2};
auto global = entt::forward_as_meta(value);
auto local = entt::forward_as_meta(context, value);
ASSERT_TRUE(global.allow_cast<int>());
ASSERT_TRUE(local.allow_cast<int>());
ASSERT_EQ(global.cast<int>(), value.get());
ASSERT_EQ(local.cast<int>(), value.get_mul());
}
TEST_F(MetaContext, MetaDtor) {
auto global = entt::resolve<clazz>().construct();
auto local = entt::resolve<clazz>(context).construct();
ASSERT_EQ(clazz::bucket, bucket_value);
global.reset();
ASSERT_EQ(clazz::bucket, bucket_value);
local.reset();
ASSERT_NE(clazz::bucket, bucket_value);
}
TEST_F(MetaContext, MetaProp) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global.prop("prop"_hs));
ASSERT_TRUE(local.prop("prop"_hs));
ASSERT_EQ(global.prop("prop"_hs).value().type(), entt::resolve<int>());
ASSERT_EQ(local.prop("prop"_hs).value().type(), entt::resolve<int>(context));
ASSERT_EQ(global.prop("prop"_hs).value().cast<int>(), prop_value);
ASSERT_EQ(local.prop("prop"_hs).value().cast<int>(), prop_value);
ASSERT_EQ(global.prop("prop"_hs).value().type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.prop("prop"_hs).value().type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaTemplate) {
using namespace entt::literals;
const auto global = entt::resolve("template"_hs);
const auto local = entt::resolve(context, "template"_hs);
ASSERT_TRUE(global.is_template_specialization());
ASSERT_TRUE(local.is_template_specialization());
ASSERT_EQ(global.template_arity(), 1u);
ASSERT_EQ(local.template_arity(), 2u);
ASSERT_EQ(global.template_arg(0u), entt::resolve<int>());
ASSERT_EQ(local.template_arg(0u), entt::resolve<int>(context));
ASSERT_EQ(local.template_arg(1u), entt::resolve<char>(context));
ASSERT_EQ(global.template_arg(0u).data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.template_arg(0u).data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaPointer) {
using namespace entt::literals;
int value = 42;
const entt::meta_any global{&value};
const entt::meta_any local{context, &value};
ASSERT_TRUE(global.type().is_pointer());
ASSERT_TRUE(local.type().is_pointer());
ASSERT_TRUE(global.type().is_pointer_like());
ASSERT_TRUE(local.type().is_pointer_like());
ASSERT_EQ((*global).type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local).type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaAssociativeContainer) {
using namespace entt::literals;
std::unordered_map<int, int> map{{{0, 0}}};
auto global = entt::forward_as_meta(map).as_associative_container();
auto local = entt::forward_as_meta(context, map).as_associative_container();
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_EQ(global.size(), 1u);
ASSERT_EQ(local.size(), 1u);
ASSERT_EQ(global.key_type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.key_type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(global.mapped_type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.mapped_type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ((*global.begin()).first.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local.begin()).first.type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ((*global.begin()).second.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local.begin()).second.type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaSequenceContainer) {
using namespace entt::literals;
std::vector<int> vec{0};
auto global = entt::forward_as_meta(vec).as_sequence_container();
auto local = entt::forward_as_meta(context, vec).as_sequence_container();
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_EQ(global.size(), 1u);
ASSERT_EQ(local.size(), 1u);
ASSERT_EQ(global.value_type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.value_type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ((*global.begin()).type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local.begin()).type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaAny) {
using namespace entt::literals;
entt::meta_any global{42};
entt::meta_any ctx_value{context, 42};
entt::meta_any in_place{context, std::in_place_type<int>, 42};
entt::meta_any two_step_local{entt::meta_ctx_arg, context};
ASSERT_TRUE(global);
ASSERT_TRUE(ctx_value);
ASSERT_TRUE(in_place);
ASSERT_FALSE(two_step_local);
two_step_local = 42;
ASSERT_TRUE(two_step_local);
ASSERT_EQ(global.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(ctx_value.type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(in_place.type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(two_step_local.type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaHandle) {
using namespace entt::literals;
int value = 42;
entt::meta_handle global{value};
entt::meta_handle ctx_value{context, value};
entt::meta_handle two_step_local{entt::meta_ctx_arg, context};
ASSERT_TRUE(global);
ASSERT_TRUE(ctx_value);
ASSERT_FALSE(two_step_local);
two_step_local->emplace<int &>(value);
ASSERT_TRUE(two_step_local);
ASSERT_EQ(global->type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(ctx_value->type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(two_step_local->type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, ForwardAsMeta) {
using namespace entt::literals;
const auto global = entt::forward_as_meta(42);
const auto local = entt::forward_as_meta(context, 42);
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_EQ(global.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.type().data("marker"_hs).get({}).cast<int>(), local_marker);
}

View File

@ -0,0 +1,71 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct clazz_t {
clazz_t() = default;
operator int() const {
return value;
}
bool to_bool() const {
return (value != 0);
}
int value{};
};
double conv_to_double(const clazz_t &instance) {
return instance.value * 2.;
}
struct MetaConv: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<clazz_t>()
.type("clazz"_hs)
.conv<int>()
.conv<&clazz_t::to_bool>()
.conv<conv_to_double>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaConv, Functionalities) {
auto any = entt::resolve<clazz_t>().construct();
any.cast<clazz_t &>().value = 42;
const auto as_int = std::as_const(any).allow_cast<int>();
const auto as_bool = std::as_const(any).allow_cast<bool>();
const auto as_double = std::as_const(any).allow_cast<double>();
ASSERT_FALSE(any.allow_cast<char>());
ASSERT_TRUE(as_int);
ASSERT_TRUE(as_bool);
ASSERT_TRUE(as_double);
ASSERT_EQ(as_int.cast<int>(), any.cast<clazz_t &>().operator int());
ASSERT_EQ(as_bool.cast<bool>(), any.cast<clazz_t &>().to_bool());
ASSERT_EQ(as_double.cast<double>(), conv_to_double(any.cast<clazz_t &>()));
}
TEST_F(MetaConv, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<clazz_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->conv.empty());
ASSERT_EQ(node.details->conv.size(), 3u);
}

View File

@ -0,0 +1,215 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/utility.hpp>
#include <entt/entity/registry.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
struct base_t {
base_t()
: value{'c'} {}
char value;
};
struct derived_t: base_t {
derived_t()
: base_t{} {}
};
struct clazz_t {
clazz_t(const base_t &other, int &iv)
: clazz_t{iv, other.value} {}
clazz_t(const int &iv, char cv)
: i{iv}, c{cv} {}
operator int() const {
return i;
}
static clazz_t factory(int value) {
return {value, 'c'};
}
static clazz_t factory(base_t other, int value, int mul) {
return {value * mul, other.value};
}
int i{};
char c{};
};
double double_factory() {
return 42.;
}
struct MetaCtor: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs)
.ctor<double_factory>();
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>();
entt::meta<clazz_t>()
.type("clazz"_hs)
.ctor<&entt::registry::emplace_or_replace<clazz_t, const int &, const char &>, entt::as_ref_t>()
.ctor<const base_t &, int &>()
.ctor<const int &, char>()
.ctor<entt::overload<clazz_t(int)>(clazz_t::factory)>()
.ctor<entt::overload<clazz_t(base_t, int, int)>(clazz_t::factory)>()
.conv<int>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaCtor, Functionalities) {
auto any = entt::resolve<clazz_t>().construct(42, 'c');
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, Func) {
auto any = entt::resolve<clazz_t>().construct(42);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, MetaAnyArgs) {
auto any = entt::resolve<clazz_t>().construct(entt::meta_any{42}, entt::meta_any{'c'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, InvalidArgs) {
ASSERT_FALSE(entt::resolve<clazz_t>().construct(entt::meta_any{}, derived_t{}));
}
TEST_F(MetaCtor, CastAndConvert) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{42, 'd'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, ArithmeticConversion) {
auto any = entt::resolve<clazz_t>().construct(true, 4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 1);
ASSERT_EQ(any.cast<clazz_t>().c, char{4});
}
TEST_F(MetaCtor, ConstNonConstRefArgs) {
int ivalue = 42;
const char cvalue = 'c';
auto any = entt::resolve<clazz_t>().construct(entt::forward_as_meta(ivalue), entt::forward_as_meta(cvalue));
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, WrongConstness) {
int value = 42;
auto any = entt::resolve<clazz_t>().construct(derived_t{}, entt::forward_as_meta(value));
auto other = entt::resolve<clazz_t>().construct(derived_t{}, entt::forward_as_meta(std::as_const(value)));
ASSERT_TRUE(any);
ASSERT_FALSE(other);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncMetaAnyArgs) {
auto any = entt::resolve<clazz_t>().construct(entt::meta_any{42});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncCastAndConvert) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, 3., clazz_t{3, 'd'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 9);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncArithmeticConversion) {
auto any = entt::resolve<clazz_t>().construct(4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 4);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncConstNonConstRefArgs) {
int ivalue = 42;
auto any = entt::resolve<clazz_t>().construct(entt::forward_as_meta(ivalue));
auto other = entt::resolve<clazz_t>().construct(entt::forward_as_meta(std::as_const(ivalue)));
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(other.cast<clazz_t>().i, 42);
}
TEST_F(MetaCtor, ExternalMemberFunction) {
entt::registry registry;
const auto entity = registry.create();
ASSERT_FALSE(registry.all_of<clazz_t>(entity));
const auto any = entt::resolve<clazz_t>().construct(entt::forward_as_meta(registry), entity, 3, 'c');
ASSERT_TRUE(any);
ASSERT_TRUE(registry.all_of<clazz_t>(entity));
ASSERT_EQ(registry.get<clazz_t>(entity).i, 3);
ASSERT_EQ(registry.get<clazz_t>(entity).c, 'c');
}
TEST_F(MetaCtor, OverrideImplicitlyGeneratedDefaultConstructor) {
auto type = entt::resolve<double>();
auto any = type.construct();
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<double>());
ASSERT_EQ(any.cast<double>(), 42.);
}
TEST_F(MetaCtor, NonDefaultConstructibleType) {
auto type = entt::resolve<clazz_t>();
// no implicitly generated default constructor
ASSERT_FALSE(type.construct());
}
TEST_F(MetaCtor, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<double>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->ctor.empty());
// implicitly generated default constructor is not cleared
ASSERT_NE(node.default_constructor, nullptr);
}

View File

@ -0,0 +1,678 @@
#include <cstdlib>
#include <string>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_traits.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
struct base_t {
virtual ~base_t() = default;
static void destroy(base_t &) {
++counter;
}
inline static int counter = 0;
int value{3};
};
struct derived_t: base_t {
derived_t() {}
};
struct clazz_t {
clazz_t()
: i{0},
j{1},
base{} {}
operator int() const {
return h;
}
int i{0};
const int j{1};
base_t base{};
inline static int h{2};
inline static const int k{3};
};
struct setter_getter_t {
setter_getter_t()
: value{0} {}
int setter(double val) {
return value = static_cast<int>(val);
}
int getter() {
return value;
}
int setter_with_ref(const int &val) {
return value = val;
}
const int &getter_with_ref() {
return value;
}
static int static_setter(setter_getter_t &type, int value) {
return type.value = value;
}
static int static_getter(const setter_getter_t &type) {
return type.value;
}
int value;
};
struct multi_setter_t {
multi_setter_t()
: value{0} {}
void from_double(double val) {
value = val;
}
void from_string(const char *val) {
value = std::atoi(val);
}
int value;
};
struct array_t {
static inline int global[3];
int local[5];
};
enum class property_t : entt::id_type {
random,
value
};
struct MetaData: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs);
entt::meta<base_t>()
.type("base"_hs)
.dtor<base_t::destroy>()
.data<&base_t::value>("value"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>()
.dtor<derived_t::destroy>()
.data<&base_t::value>("value_from_base"_hs);
entt::meta<clazz_t>()
.type("clazz"_hs)
.data<&clazz_t::i, entt::as_ref_t>("i"_hs)
.prop(3u, 0)
.data<&clazz_t::i, entt::as_cref_t>("ci"_hs)
.data<&clazz_t::j>("j"_hs)
.prop("true"_hs, 1)
.data<&clazz_t::h>("h"_hs)
.prop(static_cast<entt::id_type>(property_t::random), 2)
.data<&clazz_t::k>("k"_hs)
.prop(static_cast<entt::id_type>(property_t::value), 3)
.data<&clazz_t::base>("base"_hs)
.data<&clazz_t::i, entt::as_void_t>("void"_hs)
.conv<int>();
entt::meta<setter_getter_t>()
.type("setter_getter"_hs)
.data<&setter_getter_t::static_setter, &setter_getter_t::static_getter>("x"_hs)
.data<&setter_getter_t::setter, &setter_getter_t::getter>("y"_hs)
.data<&setter_getter_t::static_setter, &setter_getter_t::getter>("z"_hs)
.data<&setter_getter_t::setter_with_ref, &setter_getter_t::getter_with_ref>("w"_hs)
.data<nullptr, &setter_getter_t::getter>("z_ro"_hs)
.data<nullptr, &setter_getter_t::value>("value"_hs);
entt::meta<multi_setter_t>()
.type("multi_setter"_hs)
.data<entt::value_list<&multi_setter_t::from_double, &multi_setter_t::from_string>, &multi_setter_t::value>("value"_hs);
entt::meta<array_t>()
.type("array"_hs)
.data<&array_t::global>("global"_hs)
.data<&array_t::local>("local"_hs);
base_t::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaDataDeathTest = MetaData;
TEST_F(MetaData, Functionalities) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("i"_hs);
clazz_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, 3u);
ASSERT_EQ(curr.second.value(), 0);
}
ASSERT_FALSE(data.prop(2));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop(3u);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 0);
}
TEST_F(MetaData, Const) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("j"_hs);
clazz_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_TRUE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 1);
ASSERT_FALSE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_EQ(curr.second.value(), 1);
}
ASSERT_FALSE(data.prop(false));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 1);
}
TEST_F(MetaData, Static) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("h"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_EQ(data.get({}).cast<int>(), 2);
ASSERT_TRUE(data.set({}, 42));
ASSERT_EQ(data.get({}).cast<int>(), 42);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::random));
ASSERT_EQ(curr.second.value(), 2);
}
ASSERT_FALSE(data.prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop(static_cast<entt::id_type>(property_t::random));
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 2);
}
TEST_F(MetaData, ConstStatic) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("k"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_TRUE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_EQ(data.get({}).cast<int>(), 3);
ASSERT_FALSE(data.set({}, 42));
ASSERT_EQ(data.get({}).cast<int>(), 3);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::value));
ASSERT_EQ(curr.second.value(), 3);
}
ASSERT_FALSE(data.prop(static_cast<entt::id_type>(property_t::random)));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop(static_cast<entt::id_type>(property_t::value));
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 3);
}
TEST_F(MetaData, GetMetaAnyArg) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
any.cast<clazz_t &>().i = 99;
const auto value = entt::resolve<clazz_t>().data("i"_hs).get(any);
ASSERT_TRUE(value);
ASSERT_TRUE(static_cast<bool>(value.cast<int>()));
ASSERT_EQ(value.cast<int>(), 99);
}
TEST_F(MetaData, GetInvalidArg) {
using namespace entt::literals;
auto instance = 0;
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).get(instance));
}
TEST_F(MetaData, SetMetaAnyArg) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
entt::meta_any value{42};
ASSERT_EQ(any.cast<clazz_t>().i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, value));
ASSERT_EQ(any.cast<clazz_t>().i, 42);
}
TEST_F(MetaData, SetInvalidArg) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).set({}, 'c'));
}
TEST_F(MetaData, SetCast) {
using namespace entt::literals;
clazz_t instance{};
ASSERT_EQ(base_t::counter, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("base"_hs).set(instance, derived_t{}));
ASSERT_EQ(base_t::counter, 1);
}
TEST_F(MetaData, SetConvert) {
using namespace entt::literals;
clazz_t instance{};
instance.h = 42;
ASSERT_EQ(instance.i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(instance, instance));
ASSERT_EQ(instance.i, 42);
}
TEST_F(MetaData, SetByRef) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
int value{42};
ASSERT_EQ(any.cast<clazz_t>().i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, entt::forward_as_meta(value)));
ASSERT_EQ(any.cast<clazz_t>().i, 42);
value = 3;
auto wrapper = entt::forward_as_meta(value);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, wrapper.as_ref()));
ASSERT_EQ(any.cast<clazz_t>().i, 3);
}
TEST_F(MetaData, SetByConstRef) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
int value{42};
ASSERT_EQ(any.cast<clazz_t>().i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, entt::forward_as_meta(std::as_const(value))));
ASSERT_EQ(any.cast<clazz_t>().i, 42);
value = 3;
auto wrapper = entt::forward_as_meta(std::as_const(value));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, wrapper.as_ref()));
ASSERT_EQ(any.cast<clazz_t>().i, 3);
}
TEST_F(MetaData, SetterGetterAsFreeFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("x"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
}
TEST_F(MetaData, SetterGetterAsMemberFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("y"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<double>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42.));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
ASSERT_TRUE(data.set(instance, 3));
ASSERT_EQ(data.get(instance).cast<int>(), 3);
}
TEST_F(MetaData, SetterGetterWithRefAsMemberFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("w"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
}
TEST_F(MetaData, SetterGetterMixed) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("z"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
}
TEST_F(MetaData, SetterGetterReadOnly) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("z_ro"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 0u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::meta_type{});
ASSERT_TRUE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_FALSE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 0);
}
TEST_F(MetaData, SetterGetterReadOnlyDataMember) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("value"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 0u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::meta_type{});
ASSERT_TRUE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_FALSE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 0);
}
TEST_F(MetaData, MultiSetter) {
using namespace entt::literals;
auto data = entt::resolve<multi_setter_t>().data("value"_hs);
multi_setter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 2u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<double>());
ASSERT_EQ(data.arg(1u), entt::resolve<const char *>());
ASSERT_EQ(data.arg(2u), entt::meta_type{});
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
ASSERT_TRUE(data.set(instance, 3.));
ASSERT_EQ(data.get(instance).cast<int>(), 3);
ASSERT_FALSE(data.set(instance, std::string{"99"}));
ASSERT_TRUE(data.set(instance, std::string{"99"}.c_str()));
ASSERT_EQ(data.get(instance).cast<int>(), 99);
}
TEST_F(MetaData, ConstInstance) {
using namespace entt::literals;
clazz_t instance{};
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(instance).try_cast<int>(), nullptr);
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(instance).try_cast<const int>(), nullptr);
ASSERT_EQ(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)).try_cast<int>(), nullptr);
// as_ref_t adapts to the constness of the passed object and returns const references in case
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)).try_cast<const int>(), nullptr);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("ci"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs).get(instance));
ASSERT_FALSE(entt::resolve<clazz_t>().data("j"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("j"_hs).set(std::as_const(instance), 3));
}
TEST_F(MetaData, ArrayStatic) {
using namespace entt::literals;
auto data = entt::resolve<array_t>().data("global"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int[3]>());
ASSERT_EQ(data.arg(0u), entt::resolve<int[3]>());
ASSERT_FALSE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_TRUE(data.type().is_array());
ASSERT_FALSE(data.get({}));
}
TEST_F(MetaData, Array) {
using namespace entt::literals;
auto data = entt::resolve<array_t>().data("local"_hs);
array_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int[5]>());
ASSERT_EQ(data.arg(0u), entt::resolve<int[5]>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_TRUE(data.type().is_array());
ASSERT_FALSE(data.get(instance));
}
TEST_F(MetaData, AsVoid) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("void"_hs);
clazz_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(instance.i, 42);
ASSERT_EQ(data.get(instance), entt::meta_any{std::in_place_type<void>});
}
TEST_F(MetaData, AsRef) {
using namespace entt::literals;
clazz_t instance{};
auto data = entt::resolve<clazz_t>().data("i"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_NE(data.prop().cbegin(), data.prop().cend());
ASSERT_EQ(instance.i, 0);
data.get(instance).cast<int &>() = 3;
ASSERT_EQ(instance.i, 3);
}
TEST_F(MetaData, AsConstRef) {
using namespace entt::literals;
clazz_t instance{};
auto data = entt::resolve<clazz_t>().data("ci"_hs);
ASSERT_EQ(instance.i, 0);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_EQ(data.get(instance).cast<const int &>(), 0);
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_EQ(data.prop().cbegin(), data.prop().cend());
ASSERT_EQ(instance.i, 0);
}
ENTT_DEBUG_TEST_F(MetaDataDeathTest, AsConstRef) {
using namespace entt::literals;
clazz_t instance{};
auto data = entt::resolve<clazz_t>().data("ci"_hs);
ASSERT_DEATH(data.get(instance).cast<int &>() = 3, "");
}
TEST_F(MetaData, SetGetBaseData) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(instance.value, 3);
ASSERT_TRUE(type.data("value"_hs).set(instance, 42));
ASSERT_EQ(type.data("value"_hs).get(instance).cast<int>(), 42);
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaData, SetGetFromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
ASSERT_TRUE(type.data("value_from_base"_hs));
ASSERT_EQ(instance.value, 3);
ASSERT_TRUE(type.data("value_from_base"_hs).set(instance, 42));
ASSERT_EQ(type.data("value_from_base"_hs).get(instance).cast<int>(), 42);
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaData, ReRegistration) {
using namespace entt::literals;
SetUp();
auto &&node = entt::internal::resolve<base_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
auto type = entt::resolve<base_t>();
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->data.empty());
ASSERT_EQ(node.details->data.size(), 1u);
ASSERT_TRUE(type.data("value"_hs));
entt::meta<base_t>().data<&base_t::value>("field"_hs);
ASSERT_TRUE(node.details);
ASSERT_EQ(node.details->data.size(), 2u);
ASSERT_TRUE(type.data("value"_hs));
ASSERT_TRUE(type.data("field"_hs));
}
TEST_F(MetaData, CollisionAndReuse) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs));
ASSERT_FALSE(entt::resolve<clazz_t>().data("cj"_hs));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs).is_const());
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().data<&clazz_t::i>("j"_hs));
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().data<&clazz_t::j>("cj"_hs));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs));
ASSERT_TRUE(entt::resolve<clazz_t>().data("cj"_hs));
ASSERT_FALSE(entt::resolve<clazz_t>().data("j"_hs).is_const());
}

View File

@ -0,0 +1,113 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct clazz_t {
clazz_t() {
++counter;
}
static void destroy_decr(clazz_t &) {
--counter;
}
void destroy_incr() const {
++counter;
}
inline static int counter = 0;
};
struct MetaDtor: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<clazz_t>()
.type("clazz"_hs)
.dtor<clazz_t::destroy_decr>();
clazz_t::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaDtor, Functionalities) {
ASSERT_EQ(clazz_t::counter, 0);
auto any = entt::resolve<clazz_t>().construct();
auto cref = std::as_const(any).as_ref();
auto ref = any.as_ref();
ASSERT_TRUE(any);
ASSERT_TRUE(cref);
ASSERT_TRUE(ref);
ASSERT_EQ(clazz_t::counter, 1);
cref.reset();
ref.reset();
ASSERT_TRUE(any);
ASSERT_FALSE(cref);
ASSERT_FALSE(ref);
ASSERT_EQ(clazz_t::counter, 1);
any.reset();
ASSERT_FALSE(any);
ASSERT_FALSE(cref);
ASSERT_FALSE(ref);
ASSERT_EQ(clazz_t::counter, 0);
}
TEST_F(MetaDtor, AsRefConstruction) {
ASSERT_EQ(clazz_t::counter, 0);
clazz_t instance{};
auto any = entt::forward_as_meta(instance);
auto cany = entt::forward_as_meta(std::as_const(instance));
auto cref = cany.as_ref();
auto ref = any.as_ref();
ASSERT_TRUE(any);
ASSERT_TRUE(cany);
ASSERT_TRUE(cref);
ASSERT_TRUE(ref);
ASSERT_EQ(clazz_t::counter, 1);
any.reset();
cany.reset();
cref.reset();
ref.reset();
ASSERT_FALSE(any);
ASSERT_FALSE(cany);
ASSERT_FALSE(cref);
ASSERT_FALSE(ref);
ASSERT_EQ(clazz_t::counter, 1);
}
TEST_F(MetaDtor, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<clazz_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_NE(node.dtor.dtor, nullptr);
entt::meta<clazz_t>().dtor<&clazz_t::destroy_incr>();
entt::resolve<clazz_t>().construct().reset();
ASSERT_EQ(clazz_t::counter, 2);
}

View File

@ -0,0 +1,608 @@
#include <cstddef>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/utility.hpp>
#include <entt/entity/registry.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
struct base_t {
base_t() {}
virtual ~base_t() = default;
static void destroy(base_t &) {
++counter;
}
void setter(int v) {
value = v;
}
int getter() const {
return value;
}
static void static_setter(base_t &ref, int v) {
ref.value = v;
}
inline static int counter = 0;
int value{3};
};
void fake_member(base_t &instance, int value) {
instance.value = value;
}
int fake_const_member(const base_t &instance) {
return instance.value;
}
struct derived_t: base_t {
derived_t()
: base_t{} {}
};
struct func_t {
int f(const base_t &, int a, int b) {
return f(a, b);
}
int f(int a, int b) {
value = a;
return b * b;
}
int f(int v) const {
return v * v;
}
void g(int v) {
value = v * v;
}
static int h(int &v) {
return (v *= value);
}
static void k(int v) {
value = v;
}
int v(int v) const {
return (value = v);
}
int &a() const {
return value;
}
operator int() const {
return value;
}
inline static int value = 0;
};
struct MetaFunc: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs);
entt::meta<base_t>()
.type("base"_hs)
.dtor<base_t::destroy>()
.func<&base_t::setter>("setter"_hs)
.func<fake_member>("fake_member"_hs)
.func<fake_const_member>("fake_const_member"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>()
.func<&base_t::setter>("setter_from_base"_hs)
.func<&base_t::getter>("getter_from_base"_hs)
.func<&base_t::static_setter>("static_setter_from_base"_hs)
.dtor<derived_t::destroy>();
entt::meta<func_t>()
.type("func"_hs)
.func<&entt::registry::emplace_or_replace<func_t>>("emplace"_hs)
.func<entt::overload<int(const base_t &, int, int)>(&func_t::f)>("f3"_hs)
.func<entt::overload<int(int, int)>(&func_t::f)>("f2"_hs)
.prop("true"_hs, false)
.func<entt::overload<int(int) const>(&func_t::f)>("f1"_hs)
.prop("true"_hs, false)
.func<&func_t::g>("g"_hs)
.prop("true"_hs, false)
.func<func_t::h>("h"_hs)
.prop("true"_hs, false)
.func<func_t::k>("k"_hs)
.prop("true"_hs, false)
.func<&func_t::v, entt::as_void_t>("v"_hs)
.func<&func_t::a, entt::as_ref_t>("a"_hs)
.func<&func_t::a, entt::as_cref_t>("ca"_hs)
.conv<int>();
base_t::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
std::size_t reset_and_check() {
std::size_t count = 0;
for(auto func: entt::resolve<func_t>().func()) {
for(auto curr = func.second; curr; curr = curr.next()) {
++count;
}
}
SetUp();
for(auto func: entt::resolve<func_t>().func()) {
for(auto curr = func.second; curr; curr = curr.next()) {
--count;
}
}
return count;
};
};
using MetaFuncDeathTest = MetaFunc;
TEST_F(MetaFunc, Functionalities) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("f2"_hs);
func_t instance{};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_EQ(func.arg(1u), entt::resolve<int>());
ASSERT_FALSE(func.arg(2u));
auto any = func.invoke(instance, 3, 2);
auto empty = func.invoke(instance);
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 4);
ASSERT_EQ(func_t::value, 3);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, Const) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("f1"_hs);
func_t instance{};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_TRUE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke(instance, 4);
auto empty = func.invoke(instance, derived_t{});
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 16);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, RetVoid) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("g"_hs);
func_t instance{};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke(instance, 5);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(func_t::value, 25);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, Static) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("h"_hs);
func_t::value = 2;
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_TRUE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke({}, 3);
auto empty = func.invoke({}, derived_t{});
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 6);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, StaticRetVoid) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("k"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_TRUE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke({}, 42);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(func_t::value, 42);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, StaticAsMember) {
using namespace entt::literals;
base_t instance{};
auto func = entt::resolve<base_t>().func("fake_member"_hs);
auto any = func.invoke(instance, 42);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
ASSERT_EQ(func.prop().cbegin(), func.prop().cend());
ASSERT_FALSE(func.invoke({}, 42));
ASSERT_FALSE(func.invoke(std::as_const(instance), 42));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaFunc, StaticAsConstMember) {
using namespace entt::literals;
base_t instance{};
auto func = entt::resolve<base_t>().func("fake_const_member"_hs);
auto any = func.invoke(std::as_const(instance));
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 0u);
ASSERT_TRUE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_FALSE(func.arg(0u));
ASSERT_EQ(func.prop().cbegin(), func.prop().cend());
ASSERT_FALSE(func.invoke({}));
ASSERT_TRUE(func.invoke(instance));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 3);
}
TEST_F(MetaFunc, MetaAnyArgs) {
using namespace entt::literals;
func_t instance;
auto any = entt::resolve<func_t>().func("f1"_hs).invoke(instance, 3);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 9);
}
TEST_F(MetaFunc, InvalidArgs) {
using namespace entt::literals;
int value = 3;
ASSERT_FALSE(entt::resolve<func_t>().func("f1"_hs).invoke(value, 'c'));
}
TEST_F(MetaFunc, CastAndConvert) {
using namespace entt::literals;
func_t instance;
instance.value = 3;
auto any = entt::resolve<func_t>().func("f3"_hs).invoke(instance, derived_t{}, 0, instance);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 9);
ASSERT_EQ(instance.value, 0);
}
TEST_F(MetaFunc, ArithmeticConversion) {
using namespace entt::literals;
func_t instance;
auto any = entt::resolve<func_t>().func("f2"_hs).invoke(instance, true, 4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 16);
ASSERT_EQ(instance.value, 1);
}
TEST_F(MetaFunc, ArgsByRef) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("h"_hs);
func_t::value = 2;
entt::meta_any any{3};
int value = 4;
ASSERT_EQ(func.invoke({}, entt::forward_as_meta(value)).cast<int>(), 8);
ASSERT_EQ(func.invoke({}, any.as_ref()).cast<int>(), 6);
ASSERT_EQ(any.cast<int>(), 6);
ASSERT_EQ(value, 8);
}
TEST_F(MetaFunc, ArgsByConstRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("g"_hs);
entt::meta_any any{2};
int value = 3;
ASSERT_TRUE(func.invoke(instance, entt::forward_as_meta(std::as_const(value))));
ASSERT_EQ(func_t::value, 9);
ASSERT_TRUE(func.invoke(instance, std::as_const(any).as_ref()));
ASSERT_EQ(func_t::value, 4);
}
TEST_F(MetaFunc, ConstInstance) {
using namespace entt::literals;
func_t instance{};
auto any = entt::resolve<func_t>().func("f1"_hs).invoke(std::as_const(instance), 2);
ASSERT_FALSE(entt::resolve<func_t>().func("g"_hs).invoke(std::as_const(instance), 42));
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<int>(), 4);
}
TEST_F(MetaFunc, AsVoid) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("v"_hs);
func_t instance{};
ASSERT_EQ(func.invoke(instance, 42), entt::meta_any{std::in_place_type<void>});
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaFunc, AsRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("a"_hs);
func.invoke(instance).cast<int &>() = 3;
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, AsConstRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("ca"_hs);
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.invoke(instance).cast<const int &>(), 3);
ASSERT_EQ(func.invoke(instance).cast<int>(), 3);
}
ENTT_DEBUG_TEST_F(MetaFuncDeathTest, AsConstRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("ca"_hs);
ASSERT_DEATH((func.invoke(instance).cast<int &>() = 3), "");
}
TEST_F(MetaFunc, InvokeBaseFunction) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
ASSERT_TRUE(type.func("setter"_hs));
ASSERT_EQ(instance.value, 3);
type.func("setter"_hs).invoke(instance, 42);
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaFunc, InvokeFromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
auto setter_from_base = type.func("setter_from_base"_hs);
ASSERT_TRUE(setter_from_base);
ASSERT_EQ(instance.value, 3);
setter_from_base.invoke(instance, 42);
ASSERT_EQ(instance.value, 42);
auto getter_from_base = type.func("getter_from_base"_hs);
ASSERT_TRUE(getter_from_base);
ASSERT_EQ(getter_from_base.invoke(instance).cast<int>(), 42);
auto static_setter_from_base = type.func("static_setter_from_base"_hs);
ASSERT_TRUE(static_setter_from_base);
ASSERT_EQ(instance.value, 42);
static_setter_from_base.invoke(instance, 3);
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, ExternalMemberFunction) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("emplace"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_TRUE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<entt::registry>());
ASSERT_EQ(func.arg(1u), entt::resolve<entt::entity>());
ASSERT_FALSE(func.arg(2u));
entt::registry registry;
const auto entity = registry.create();
ASSERT_FALSE(registry.all_of<func_t>(entity));
func.invoke({}, entt::forward_as_meta(registry), entity);
ASSERT_TRUE(registry.all_of<func_t>(entity));
}
TEST_F(MetaFunc, ReRegistration) {
using namespace entt::literals;
ASSERT_EQ(reset_and_check(), 0u);
func_t instance{};
auto type = entt::resolve<func_t>();
ASSERT_TRUE(type.func("f2"_hs));
ASSERT_FALSE(type.invoke("f2"_hs, instance, 0));
ASSERT_TRUE(type.invoke("f2"_hs, instance, 0, 0));
ASSERT_TRUE(type.func("f1"_hs));
ASSERT_TRUE(type.invoke("f1"_hs, instance, 0));
ASSERT_FALSE(type.invoke("f1"_hs, instance, 0, 0));
entt::meta<func_t>()
.func<entt::overload<int(int, int)>(&func_t::f)>("f"_hs)
.func<entt::overload<int(int) const>(&func_t::f)>("f"_hs);
ASSERT_TRUE(type.func("f1"_hs));
ASSERT_TRUE(type.func("f2"_hs));
ASSERT_TRUE(type.func("f"_hs));
ASSERT_TRUE(type.invoke("f"_hs, instance, 0));
ASSERT_TRUE(type.invoke("f"_hs, instance, 0, 0));
ASSERT_EQ(reset_and_check(), 0u);
}

View File

@ -0,0 +1,66 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
struct clazz_t {
clazz_t()
: value{} {}
void incr() {
++value;
}
void decr() {
--value;
}
int value;
};
struct MetaHandle: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<clazz_t>()
.type("clazz"_hs)
.func<&clazz_t::incr>("incr"_hs)
.func<&clazz_t::decr>("decr"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaHandle, Functionalities) {
using namespace entt::literals;
clazz_t instance{};
entt::meta_handle handle{};
entt::meta_handle chandle{};
ASSERT_FALSE(handle);
ASSERT_FALSE(chandle);
handle = entt::meta_handle{instance};
chandle = entt::meta_handle{std::as_const(instance)};
ASSERT_TRUE(handle);
ASSERT_TRUE(chandle);
ASSERT_TRUE(handle->invoke("incr"_hs));
ASSERT_FALSE(chandle->invoke("incr"_hs));
ASSERT_FALSE(std::as_const(handle)->invoke("incr"_hs));
ASSERT_EQ(instance.value, 1);
auto any = entt::forward_as_meta(instance);
handle = entt::meta_handle{any};
chandle = entt::meta_handle{std::as_const(any)};
ASSERT_TRUE(handle->invoke("decr"_hs));
ASSERT_FALSE(chandle->invoke("decr"_hs));
ASSERT_FALSE(std::as_const(handle)->invoke("decr"_hs));
ASSERT_EQ(instance.value, 0);
}

View File

@ -0,0 +1,414 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/meta/meta.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
template<typename Type>
struct wrapped_shared_ptr {
wrapped_shared_ptr(Type init)
: ptr{new Type{init}} {}
Type &deref() const {
return *ptr;
}
private:
std::shared_ptr<Type> ptr;
};
struct self_ptr {
using element_type = self_ptr;
self_ptr(int v)
: value{v} {}
const self_ptr &operator*() const {
return *this;
}
int value;
};
struct proxy_ptr {
using element_type = proxy_ptr;
proxy_ptr(int &v)
: value{&v} {}
proxy_ptr operator*() const {
return *this;
}
int *value;
};
template<typename Type>
struct adl_wrapped_shared_ptr: wrapped_shared_ptr<Type> {};
template<typename Type>
struct spec_wrapped_shared_ptr: wrapped_shared_ptr<Type> {};
template<typename Type>
struct entt::is_meta_pointer_like<adl_wrapped_shared_ptr<Type>>: std::true_type {};
template<typename Type>
struct entt::is_meta_pointer_like<spec_wrapped_shared_ptr<Type>>: std::true_type {};
template<>
struct entt::is_meta_pointer_like<self_ptr>: std::true_type {};
template<>
struct entt::is_meta_pointer_like<proxy_ptr>: std::true_type {};
template<typename Type>
struct entt::adl_meta_pointer_like<spec_wrapped_shared_ptr<Type>> {
static decltype(auto) dereference(const spec_wrapped_shared_ptr<Type> &ptr) {
return ptr.deref();
}
};
template<typename Type>
Type &dereference_meta_pointer_like(const adl_wrapped_shared_ptr<Type> &ptr) {
return ptr.deref();
}
int test_function() {
return 42;
}
struct not_copyable_t {
not_copyable_t() = default;
not_copyable_t(const not_copyable_t &) = delete;
not_copyable_t(not_copyable_t &&) = default;
not_copyable_t &operator=(const not_copyable_t &) = delete;
not_copyable_t &operator=(not_copyable_t &&) = default;
};
TEST(MetaPointerLike, DereferenceOperatorInvalidType) {
int value = 0;
entt::meta_any any{value};
ASSERT_FALSE(any.type().is_pointer());
ASSERT_FALSE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int>());
auto deref = *any;
ASSERT_FALSE(deref);
}
TEST(MetaPointerLike, DereferenceOperatorConstType) {
const int value = 42;
entt::meta_any any{&value};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<const int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.try_cast<int>(), nullptr);
ASSERT_EQ(deref.try_cast<const int>(), &value);
ASSERT_EQ(deref.cast<const int &>(), 42);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstType) {
const int value = 42;
entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 0, "");
}
TEST(MetaPointerLike, DereferenceOperatorConstAnyNonConstType) {
int value = 42;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_NE(deref.try_cast<int>(), nullptr);
ASSERT_NE(deref.try_cast<const int>(), nullptr);
ASSERT_EQ(deref.cast<int &>(), 42);
ASSERT_EQ(deref.cast<const int &>(), 42);
}
TEST(MetaPointerLike, DereferenceOperatorConstAnyConstType) {
const int value = 42;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.try_cast<int>(), nullptr);
ASSERT_NE(deref.try_cast<const int>(), nullptr);
ASSERT_EQ(deref.cast<const int &>(), 42);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstAnyConstType) {
const int value = 42;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 0, "");
}
TEST(MetaPointerLike, DereferenceOperatorRawPointer) {
int value = 0;
entt::meta_any any{&value};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<int *>(), 42);
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, DereferenceOperatorSmartPointer) {
auto value = std::make_shared<int>(0);
entt::meta_any any{value};
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<std::shared_ptr<int>>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<std::shared_ptr<int>>(), 42);
ASSERT_EQ(*value, 42);
}
TEST(MetaPointerLike, PointerToConstMoveOnlyType) {
const not_copyable_t instance;
entt::meta_any any{&instance};
auto deref = *any;
ASSERT_TRUE(any);
ASSERT_TRUE(deref);
ASSERT_EQ(deref.try_cast<not_copyable_t>(), nullptr);
ASSERT_NE(deref.try_cast<const not_copyable_t>(), nullptr);
ASSERT_EQ(&deref.cast<const not_copyable_t &>(), &instance);
}
TEST(MetaPointerLike, AsRef) {
int value = 0;
int *ptr = &value;
entt::meta_any any{entt::forward_as_meta(ptr)};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<int *>(), 42);
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, AsConstRef) {
int value = 42;
int *const ptr = &value;
entt::meta_any any{entt::forward_as_meta(ptr)};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<int *>(), 42);
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, DereferenceOverload) {
auto test = [](entt::meta_any any) {
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.cast<int &>(), 42);
ASSERT_EQ(deref.cast<const int &>(), 42);
};
test(adl_wrapped_shared_ptr<int>{42});
test(spec_wrapped_shared_ptr<int>{42});
}
TEST(MetaPointerLike, DereferencePointerToConstOverload) {
auto test = [](entt::meta_any any) {
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.cast<const int &>(), 42);
};
test(adl_wrapped_shared_ptr<const int>{42});
test(spec_wrapped_shared_ptr<const int>{42});
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferencePointerToConstOverload) {
auto test = [](entt::meta_any any) {
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 42, "");
};
test(adl_wrapped_shared_ptr<const int>{42});
test(spec_wrapped_shared_ptr<const int>{42});
}
TEST(MetaPointerLike, DereferencePointerToVoid) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_FALSE(deref);
};
test(static_cast<void *>(nullptr));
test(static_cast<const void *>(nullptr));
}
TEST(MetaPointerLike, DereferenceSmartPointerToVoid) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any.type().is_class());
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_FALSE(deref);
};
test(std::shared_ptr<void>{});
test(std::unique_ptr<void, void (*)(void *)>{nullptr, nullptr});
}
TEST(MetaPointerLike, DereferencePointerToFunction) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_NE(any.try_cast<int (*)()>(), nullptr);
ASSERT_EQ(any.cast<int (*)()>()(), 42);
};
entt::meta_any func{&test_function};
test(func);
test(*func);
test(**func);
test(*std::as_const(func));
}
TEST(MetaPointerLike, DereferenceSelfPointer) {
self_ptr obj{42};
entt::meta_any any{entt::forward_as_meta(obj)};
entt::meta_any deref = *any;
ASSERT_TRUE(deref);
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(deref.cast<const self_ptr &>().value, obj.value);
ASSERT_FALSE(deref.try_cast<self_ptr>());
}
TEST(MetaPointerLike, DereferenceProxyPointer) {
int value = 3;
proxy_ptr obj{value};
entt::meta_any any{obj};
entt::meta_any deref = *any;
ASSERT_TRUE(deref);
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(*deref.cast<const proxy_ptr &>().value, value);
ASSERT_TRUE(deref.try_cast<proxy_ptr>());
*deref.cast<proxy_ptr &>().value = 42;
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, DereferenceArray) {
entt::meta_any array{std::in_place_type<int[3]>};
entt::meta_any array_of_array{std::in_place_type<int[3][3]>};
ASSERT_EQ(array.type(), entt::resolve<int[3]>());
ASSERT_EQ(array_of_array.type(), entt::resolve<int[3][3]>());
ASSERT_FALSE(*array);
ASSERT_FALSE(*array_of_array);
}
TEST(MetaPointerLike, DereferenceVerifiableNullPointerLike) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any);
ASSERT_FALSE(*any);
};
test(entt::meta_any{static_cast<int *>(nullptr)});
test(entt::meta_any{std::shared_ptr<int>{}});
test(entt::meta_any{std::unique_ptr<int>{}});
}

View File

@ -0,0 +1,111 @@
#include <cstring>
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
struct base_1_t {};
struct base_2_t {};
struct base_3_t {};
struct derived_t: base_1_t, base_2_t, base_3_t {};
struct MetaProp: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<base_1_t>()
.type("base_1"_hs)
.prop("int"_hs, 42);
entt::meta<base_2_t>()
.type("base_2"_hs)
.prop("bool"_hs, false)
.prop("char[]"_hs, "char[]");
entt::meta<base_3_t>()
.type("base_3"_hs)
.prop("key_only"_hs)
.prop("key"_hs, 42);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_1_t>()
.base<base_2_t>()
.base<base_3_t>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaProp, Functionalities) {
using namespace entt::literals;
auto prop = entt::resolve<base_1_t>().prop("int"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 42);
}
TEST_F(MetaProp, FromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
auto prop_bool = type.prop("bool"_hs);
auto prop_int = type.prop("int"_hs);
auto key_only = type.prop("key_only"_hs);
auto key_value = type.prop("key"_hs);
ASSERT_TRUE(prop_bool);
ASSERT_TRUE(prop_int);
ASSERT_TRUE(key_only);
ASSERT_TRUE(key_value);
ASSERT_FALSE(prop_bool.value().cast<bool>());
ASSERT_EQ(prop_int.value().cast<int>(), 42);
ASSERT_FALSE(key_only.value());
ASSERT_EQ(key_value.value().cast<int>(), 42);
}
TEST_F(MetaProp, DeducedArrayType) {
using namespace entt::literals;
auto prop = entt::resolve<base_2_t>().prop("char[]"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value().type(), entt::resolve<const char *>());
ASSERT_EQ(strcmp(prop.value().cast<const char *>(), "char[]"), 0);
}
TEST_F(MetaProp, ReRegistration) {
using namespace entt::literals;
SetUp();
auto &&node = entt::internal::resolve<base_1_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
auto type = entt::resolve<base_1_t>();
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->prop.empty());
ASSERT_EQ(node.details->prop.size(), 1u);
ASSERT_TRUE(type.prop("int"_hs));
ASSERT_EQ(type.prop("int"_hs).value().cast<int>(), 42);
entt::meta<base_1_t>().prop("int"_hs, 0);
entt::meta<base_1_t>().prop("double"_hs, 3.);
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->prop.empty());
ASSERT_EQ(node.details->prop.size(), 2u);
ASSERT_TRUE(type.prop("int"_hs));
ASSERT_TRUE(type.prop("double"_hs));
ASSERT_EQ(type.prop("int"_hs).value().cast<int>(), 0);
ASSERT_EQ(type.prop("double"_hs).value().cast<double>(), 3.);
}

View File

@ -0,0 +1,95 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/range.hpp>
#include <entt/meta/resolve.hpp>
struct MetaRange: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<int>().type("int"_hs).data<42>("answer"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaRange, EmptyRange) {
entt::meta_reset();
auto range = entt::resolve();
ASSERT_EQ(range.begin(), range.end());
}
TEST_F(MetaRange, Iterator) {
using namespace entt::literals;
using iterator = typename decltype(entt::resolve())::iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<entt::id_type, entt::meta_type>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::meta_type>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<entt::id_type, entt::meta_type>>);
auto range = entt::resolve();
iterator end{range.begin()};
iterator begin{};
begin = range.end();
std::swap(begin, end);
ASSERT_EQ(begin, range.begin());
ASSERT_EQ(end, range.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, range.begin());
ASSERT_EQ(begin--, range.end());
ASSERT_EQ(begin + 1, range.end());
ASSERT_EQ(end - 1, range.begin());
ASSERT_EQ(++begin, range.end());
ASSERT_EQ(--begin, range.begin());
ASSERT_EQ(begin += 1, range.end());
ASSERT_EQ(begin -= 1, range.begin());
ASSERT_EQ(begin + (end - begin), range.end());
ASSERT_EQ(begin - (begin - end), range.end());
ASSERT_EQ(end - (end - begin), range.begin());
ASSERT_EQ(end + (begin - end), range.begin());
ASSERT_EQ(begin[0u].first, range.begin()->first);
ASSERT_EQ(begin[0u].second, (*range.begin()).second);
ASSERT_LT(begin, end);
ASSERT_LE(begin, range.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, range.end());
entt::meta<double>().type("double"_hs);
range = entt::resolve();
begin = range.begin();
ASSERT_EQ(begin[0u].first, entt::resolve<int>().info().hash());
ASSERT_EQ(begin[1u].second, entt::resolve("double"_hs));
}
TEST_F(MetaRange, DirectValue) {
using namespace entt::literals;
auto type = entt::resolve<int>();
auto range = type.data();
ASSERT_NE(range.cbegin(), range.cend());
for(auto &&[id, data]: range) {
ASSERT_EQ(id, "answer"_hs);
ASSERT_EQ(data.get({}).cast<int>(), 42);
}
}

View File

@ -0,0 +1,49 @@
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/template.hpp>
template<typename>
struct function_type;
template<typename Ret, typename... Args>
struct function_type<Ret(Args...)> {};
template<typename Ret, typename... Args>
struct entt::meta_template_traits<function_type<Ret(Args...)>> {
using class_type = meta_class_template_tag<function_type>;
using args_type = type_list<Ret, Args...>;
};
TEST(MetaTemplate, Invalid) {
const auto type = entt::resolve<int>();
ASSERT_FALSE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 0u);
ASSERT_EQ(type.template_type(), entt::meta_type{});
ASSERT_EQ(type.template_arg(0u), entt::meta_type{});
}
TEST(MetaTemplate, Valid) {
const auto type = entt::resolve<entt::type_list<int, char>>();
ASSERT_TRUE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 2u);
ASSERT_EQ(type.template_type(), entt::resolve<entt::meta_class_template_tag<entt::type_list>>());
ASSERT_EQ(type.template_arg(0u), entt::resolve<int>());
ASSERT_EQ(type.template_arg(1u), entt::resolve<char>());
ASSERT_EQ(type.template_arg(2u), entt::meta_type{});
}
TEST(MetaTemplate, CustomTraits) {
const auto type = entt::resolve<function_type<void(int, const char &)>>();
ASSERT_TRUE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 3u);
ASSERT_EQ(type.template_type(), entt::resolve<entt::meta_class_template_tag<function_type>>());
ASSERT_EQ(type.template_arg(0u), entt::resolve<void>());
ASSERT_EQ(type.template_arg(1u), entt::resolve<int>());
ASSERT_EQ(type.template_arg(2u), entt::resolve<char>());
ASSERT_EQ(type.template_arg(3u), entt::meta_type{});
}

View File

@ -0,0 +1,765 @@
#include <algorithm>
#include <map>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_info.hpp>
#include <entt/core/utility.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/template.hpp>
#include "../common/config.h"
template<typename Type>
void set(Type &prop, Type value) {
prop = value;
}
template<typename Type>
Type get(Type &prop) {
return prop;
}
struct base_t {
base_t()
: value{'c'} {};
char value;
};
struct derived_t: base_t {
derived_t()
: base_t{} {}
};
struct abstract_t {
virtual ~abstract_t() = default;
virtual void func(int) {}
void base_only(int) {}
};
struct concrete_t: base_t, abstract_t {
void func(int v) override {
abstract_t::func(v);
value = v;
}
int value{3};
};
struct clazz_t {
clazz_t() = default;
clazz_t(const base_t &, int v)
: value{v} {}
void member() {}
static void func() {}
operator int() const {
return value;
}
int value{};
};
struct overloaded_func_t {
int e(int v) const {
return v + v;
}
int f(const base_t &, int a, int b) {
return f(a, b);
}
int f(int a, int b) {
value = a;
return g(b);
}
int f(int v) {
return 2 * std::as_const(*this).f(v);
}
int f(int v) const {
return g(v);
}
float f(int a, float b) {
value = a;
return static_cast<float>(e(static_cast<int>(b)));
}
int g(int v) const {
return v * v;
}
inline static int value = 0;
};
enum class property_t : entt::id_type {
random,
value,
key_only,
list
};
struct MetaType: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs)
.data<set<double>, get<double>>("var"_hs);
entt::meta<unsigned int>()
.type("unsigned int"_hs)
.data<0u>("min"_hs)
.data<100u>("max"_hs);
entt::meta<base_t>()
.type("base"_hs)
.data<&base_t::value>("value"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>();
entt::meta<abstract_t>()
.type("abstract"_hs)
.func<&abstract_t::func>("func"_hs)
.func<&abstract_t::base_only>("base_only"_hs);
entt::meta<concrete_t>()
.type("concrete"_hs)
.base<base_t>()
.base<abstract_t>();
entt::meta<overloaded_func_t>()
.type("overloaded_func"_hs)
.func<&overloaded_func_t::e>("e"_hs)
.func<entt::overload<int(const base_t &, int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int) const>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<float(int, float)>(&overloaded_func_t::f)>("f"_hs)
.func<&overloaded_func_t::g>("g"_hs);
entt::meta<property_t>()
.type("property"_hs)
.data<property_t::random>("random"_hs)
.prop(static_cast<entt::id_type>(property_t::random), 0)
.prop(static_cast<entt::id_type>(property_t::value), 3)
.data<property_t::value>("value"_hs)
.prop(static_cast<entt::id_type>(property_t::random), true)
.prop(static_cast<entt::id_type>(property_t::value), 0)
.prop(static_cast<entt::id_type>(property_t::key_only))
.prop(static_cast<entt::id_type>(property_t::list))
.data<property_t::key_only>("key_only"_hs)
.prop(static_cast<entt::id_type>(property_t::key_only))
.data<property_t::list>("list"_hs)
.prop(static_cast<entt::id_type>(property_t::random), false)
.prop(static_cast<entt::id_type>(property_t::value), 0)
.prop(static_cast<entt::id_type>(property_t::key_only))
.data<set<property_t>, get<property_t>>("var"_hs);
entt::meta<clazz_t>()
.type("clazz"_hs)
.prop(static_cast<entt::id_type>(property_t::value), 42)
.ctor<const base_t &, int>()
.data<&clazz_t::value>("value"_hs)
.func<&clazz_t::member>("member"_hs)
.func<clazz_t::func>("func"_hs)
.conv<int>();
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaTypeDeathTest = MetaType;
TEST_F(MetaType, Resolve) {
using namespace entt::literals;
ASSERT_EQ(entt::resolve<double>(), entt::resolve("double"_hs));
ASSERT_EQ(entt::resolve<double>(), entt::resolve(entt::type_id<double>()));
ASSERT_FALSE(entt::resolve(entt::type_id<void>()));
auto range = entt::resolve();
// it could be "char"_hs rather than entt::hashed_string::value("char") if it weren't for a bug in VS2017
const auto it = std::find_if(range.begin(), range.end(), [](auto curr) { return curr.second.id() == entt::hashed_string::value("clazz"); });
ASSERT_NE(it, range.end());
ASSERT_EQ(it->second, entt::resolve<clazz_t>());
bool found = false;
for(auto curr: entt::resolve()) {
found = found || curr.second == entt::resolve<double>();
}
ASSERT_TRUE(found);
}
TEST_F(MetaType, Functionalities) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
ASSERT_TRUE(type);
ASSERT_NE(type, entt::meta_type{});
ASSERT_EQ(type.id(), "clazz"_hs);
ASSERT_EQ(type.info(), entt::type_id<clazz_t>());
for(auto curr: type.prop()) {
ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::value));
ASSERT_EQ(curr.second.value(), 42);
}
ASSERT_FALSE(type.prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.prop("property"_hs));
auto prop = type.prop(static_cast<entt::id_type>(property_t::value));
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 42);
}
TEST_F(MetaType, SizeOf) {
ASSERT_EQ(entt::resolve<void>().size_of(), 0u);
ASSERT_EQ(entt::resolve<int>().size_of(), sizeof(int));
ASSERT_EQ(entt::resolve<int[]>().size_of(), 0u);
ASSERT_EQ(entt::resolve<int[3]>().size_of(), sizeof(int[3]));
}
TEST_F(MetaType, Traits) {
ASSERT_TRUE(entt::resolve<bool>().is_arithmetic());
ASSERT_TRUE(entt::resolve<double>().is_arithmetic());
ASSERT_FALSE(entt::resolve<clazz_t>().is_arithmetic());
ASSERT_TRUE(entt::resolve<int>().is_integral());
ASSERT_FALSE(entt::resolve<double>().is_integral());
ASSERT_FALSE(entt::resolve<clazz_t>().is_integral());
ASSERT_TRUE(entt::resolve<long>().is_signed());
ASSERT_FALSE(entt::resolve<unsigned int>().is_signed());
ASSERT_FALSE(entt::resolve<clazz_t>().is_signed());
ASSERT_TRUE(entt::resolve<int[5]>().is_array());
ASSERT_TRUE(entt::resolve<int[5][3]>().is_array());
ASSERT_FALSE(entt::resolve<int>().is_array());
ASSERT_TRUE(entt::resolve<property_t>().is_enum());
ASSERT_FALSE(entt::resolve<char>().is_enum());
ASSERT_TRUE(entt::resolve<derived_t>().is_class());
ASSERT_FALSE(entt::resolve<double>().is_class());
ASSERT_TRUE(entt::resolve<int *>().is_pointer());
ASSERT_FALSE(entt::resolve<int>().is_pointer());
ASSERT_TRUE(entt::resolve<int *>().is_pointer_like());
ASSERT_TRUE(entt::resolve<std::shared_ptr<int>>().is_pointer_like());
ASSERT_FALSE(entt::resolve<int>().is_pointer_like());
ASSERT_FALSE((entt::resolve<int>().is_sequence_container()));
ASSERT_TRUE(entt::resolve<std::vector<int>>().is_sequence_container());
ASSERT_FALSE((entt::resolve<std::map<int, char>>().is_sequence_container()));
ASSERT_FALSE((entt::resolve<int>().is_associative_container()));
ASSERT_TRUE((entt::resolve<std::map<int, char>>().is_associative_container()));
ASSERT_FALSE(entt::resolve<std::vector<int>>().is_associative_container());
}
TEST_F(MetaType, RemovePointer) {
ASSERT_EQ(entt::resolve<void *>().remove_pointer(), entt::resolve<void>());
ASSERT_EQ(entt::resolve<char **>().remove_pointer(), entt::resolve<char *>());
ASSERT_EQ(entt::resolve<int (*)(char, double)>().remove_pointer(), entt::resolve<int(char, double)>());
ASSERT_EQ(entt::resolve<derived_t>().remove_pointer(), entt::resolve<derived_t>());
}
TEST_F(MetaType, TemplateInfo) {
ASSERT_FALSE(entt::resolve<int>().is_template_specialization());
ASSERT_EQ(entt::resolve<int>().template_arity(), 0u);
ASSERT_EQ(entt::resolve<int>().template_type(), entt::meta_type{});
ASSERT_EQ(entt::resolve<int>().template_arg(0u), entt::meta_type{});
ASSERT_TRUE(entt::resolve<std::shared_ptr<int>>().is_template_specialization());
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arity(), 1u);
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_type(), entt::resolve<entt::meta_class_template_tag<std::shared_ptr>>());
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(0u), entt::resolve<int>());
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(1u), entt::meta_type{});
}
TEST_F(MetaType, Base) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
ASSERT_NE(type.base().cbegin(), type.base().cend());
for(auto curr: type.base()) {
ASSERT_EQ(curr.first, entt::type_id<base_t>().hash());
ASSERT_EQ(curr.second, entt::resolve<base_t>());
}
}
TEST_F(MetaType, Ctor) {
derived_t derived;
base_t &base = derived;
auto type = entt::resolve<clazz_t>();
ASSERT_TRUE((type.construct(entt::forward_as_meta(derived), 42)));
ASSERT_TRUE((type.construct(entt::forward_as_meta(base), 42)));
// use the implicitly generated default constructor
auto any = type.construct();
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<clazz_t>());
}
TEST_F(MetaType, Data) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
int counter{};
for([[maybe_unused]] auto curr: type.data()) {
++counter;
}
ASSERT_EQ(counter, 1);
ASSERT_TRUE(type.data("value"_hs));
type = entt::resolve<void>();
ASSERT_TRUE(type);
ASSERT_EQ(type.data().cbegin(), type.data().cend());
}
TEST_F(MetaType, Func) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
clazz_t instance{};
int counter{};
for([[maybe_unused]] auto curr: type.func()) {
++counter;
}
ASSERT_EQ(counter, 2);
ASSERT_TRUE(type.func("member"_hs));
ASSERT_TRUE(type.func("func"_hs));
ASSERT_TRUE(type.func("member"_hs).invoke(instance));
ASSERT_TRUE(type.func("func"_hs).invoke({}));
type = entt::resolve<void>();
ASSERT_TRUE(type);
ASSERT_EQ(type.func().cbegin(), type.func().cend());
}
TEST_F(MetaType, Invoke) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
clazz_t instance{};
ASSERT_TRUE(type.invoke("member"_hs, instance));
ASSERT_FALSE(type.invoke("rebmem"_hs, {}));
}
TEST_F(MetaType, InvokeFromBase) {
using namespace entt::literals;
auto type = entt::resolve<concrete_t>();
concrete_t instance{};
ASSERT_TRUE(type.invoke("base_only"_hs, instance, 42));
ASSERT_FALSE(type.invoke("ylno_esab"_hs, {}, 'c'));
}
TEST_F(MetaType, OverloadedFunc) {
using namespace entt::literals;
const auto type = entt::resolve<overloaded_func_t>();
overloaded_func_t instance{};
entt::meta_any res{};
ASSERT_TRUE(type.func("f"_hs));
ASSERT_TRUE(type.func("e"_hs));
ASSERT_TRUE(type.func("g"_hs));
res = type.invoke("f"_hs, instance, base_t{}, 1, 2);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 1);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 4);
res = type.invoke("f"_hs, instance, 3, 4);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 16);
res = type.invoke("f"_hs, instance, 5);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 50);
res = type.invoke("f"_hs, std::as_const(instance), 5);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 25);
res = type.invoke("f"_hs, instance, 6, 7.f);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 6);
ASSERT_NE(res.try_cast<float>(), nullptr);
ASSERT_EQ(res.cast<float>(), 14.f);
res = type.invoke("f"_hs, instance, 8, 9.f);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 8);
ASSERT_NE(res.try_cast<float>(), nullptr);
ASSERT_EQ(res.cast<float>(), 18.f);
// it fails as an ambiguous call
ASSERT_FALSE(type.invoke("f"_hs, instance, 8, 9.));
}
TEST_F(MetaType, Construct) {
auto any = entt::resolve<clazz_t>().construct(base_t{}, 42);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 42);
}
TEST_F(MetaType, ConstructNoArgs) {
// this should work, no other tests required
auto any = entt::resolve<clazz_t>().construct();
ASSERT_TRUE(any);
}
TEST_F(MetaType, ConstructMetaAnyArgs) {
auto any = entt::resolve<clazz_t>().construct(entt::meta_any{base_t{}}, entt::meta_any{42});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 42);
}
TEST_F(MetaType, ConstructInvalidArgs) {
ASSERT_FALSE(entt::resolve<clazz_t>().construct('c', base_t{}));
}
TEST_F(MetaType, LessArgs) {
ASSERT_FALSE(entt::resolve<clazz_t>().construct(base_t{}));
}
TEST_F(MetaType, ConstructCastAndConvert) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{derived_t{}, 42});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 42);
}
TEST_F(MetaType, ConstructArithmeticConversion) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{derived_t{}, true});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 1);
}
TEST_F(MetaType, FromVoid) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve<double>().from_void(static_cast<double *>(nullptr)));
ASSERT_FALSE(entt::resolve<double>().from_void(static_cast<const double *>(nullptr)));
auto type = entt::resolve<double>();
double value = 4.2;
ASSERT_FALSE(entt::resolve<void>().from_void(static_cast<void *>(&value)));
ASSERT_FALSE(entt::resolve<void>().from_void(static_cast<const void *>(&value)));
auto as_void = type.from_void(static_cast<void *>(&value));
auto as_const_void = type.from_void(static_cast<const void *>(&value));
ASSERT_TRUE(as_void);
ASSERT_TRUE(as_const_void);
ASSERT_EQ(as_void.type(), entt::resolve<double>());
ASSERT_NE(as_void.try_cast<double>(), nullptr);
ASSERT_EQ(as_const_void.type(), entt::resolve<double>());
ASSERT_EQ(as_const_void.try_cast<double>(), nullptr);
ASSERT_NE(as_const_void.try_cast<const double>(), nullptr);
value = 1.2;
ASSERT_EQ(as_void.cast<double>(), as_const_void.cast<double>());
ASSERT_EQ(as_void.cast<double>(), 1.2);
}
TEST_F(MetaType, Reset) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve("clazz"_hs));
ASSERT_EQ(entt::resolve<clazz_t>().id(), "clazz"_hs);
ASSERT_TRUE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_TRUE(entt::resolve<clazz_t>().data("value"_hs));
ASSERT_TRUE((entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{})));
// implicitly generated default constructor
ASSERT_TRUE(entt::resolve<clazz_t>().construct());
entt::meta_reset("clazz"_hs);
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_NE(entt::resolve<clazz_t>().id(), "clazz"_hs);
ASSERT_FALSE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
ASSERT_FALSE((entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{})));
// implicitly generated default constructor is not cleared
ASSERT_TRUE(entt::resolve<clazz_t>().construct());
entt::meta<clazz_t>().type("clazz"_hs);
ASSERT_TRUE(entt::resolve("clazz"_hs));
}
TEST_F(MetaType, ResetLast) {
auto id = (entt::resolve().cend() - 1u)->second.id();
ASSERT_TRUE(entt::resolve(id));
entt::meta_reset(id);
ASSERT_FALSE(entt::resolve(id));
}
TEST_F(MetaType, ResetAll) {
using namespace entt::literals;
ASSERT_NE(entt::resolve().begin(), entt::resolve().end());
ASSERT_TRUE(entt::resolve("clazz"_hs));
ASSERT_TRUE(entt::resolve("overloaded_func"_hs));
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta_reset();
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_FALSE(entt::resolve("overloaded_func"_hs));
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_EQ(entt::resolve().begin(), entt::resolve().end());
}
TEST_F(MetaType, AbstractClass) {
using namespace entt::literals;
auto type = entt::resolve<abstract_t>();
concrete_t instance;
ASSERT_EQ(type.info(), entt::type_id<abstract_t>());
ASSERT_EQ(instance.base_t::value, 'c');
ASSERT_EQ(instance.value, 3);
type.func("func"_hs).invoke(instance, 42);
ASSERT_EQ(instance.base_t::value, 'c');
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaType, EnumAndNamedConstants) {
using namespace entt::literals;
auto type = entt::resolve<property_t>();
ASSERT_TRUE(type.data("random"_hs));
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.data("random"_hs).type(), type);
ASSERT_EQ(type.data("value"_hs).type(), type);
ASSERT_FALSE(type.data("random"_hs).set({}, property_t::value));
ASSERT_FALSE(type.data("value"_hs).set({}, property_t::random));
ASSERT_EQ(type.data("random"_hs).get({}).cast<property_t>(), property_t::random);
ASSERT_EQ(type.data("value"_hs).get({}).cast<property_t>(), property_t::value);
}
TEST_F(MetaType, ArithmeticTypeAndNamedConstants) {
using namespace entt::literals;
auto type = entt::resolve<unsigned int>();
ASSERT_TRUE(type.data("min"_hs));
ASSERT_TRUE(type.data("max"_hs));
ASSERT_EQ(type.data("min"_hs).type(), type);
ASSERT_EQ(type.data("max"_hs).type(), type);
ASSERT_FALSE(type.data("min"_hs).set({}, 100u));
ASSERT_FALSE(type.data("max"_hs).set({}, 0u));
ASSERT_EQ(type.data("min"_hs).get({}).cast<unsigned int>(), 0u);
ASSERT_EQ(type.data("max"_hs).get({}).cast<unsigned int>(), 100u);
}
TEST_F(MetaType, Variables) {
using namespace entt::literals;
auto p_data = entt::resolve<property_t>().data("var"_hs);
auto d_data = entt::resolve("double"_hs).data("var"_hs);
property_t prop{property_t::key_only};
double d = 3.;
p_data.set(prop, property_t::random);
d_data.set(d, 42.);
ASSERT_EQ(p_data.get(prop).cast<property_t>(), property_t::random);
ASSERT_EQ(d_data.get(d).cast<double>(), 42.);
ASSERT_EQ(prop, property_t::random);
ASSERT_EQ(d, 42.);
}
TEST_F(MetaType, PropertiesAndCornerCases) {
using namespace entt::literals;
auto type = entt::resolve<property_t>();
ASSERT_EQ(type.prop().cbegin(), type.prop().cend());
ASSERT_EQ(type.data("random"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<int>(), 0);
ASSERT_EQ(type.data("random"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 3);
ASSERT_EQ(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<bool>(), true);
ASSERT_EQ(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 0);
ASSERT_TRUE(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
ASSERT_TRUE(type.data("key_only"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.data("key_only"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
ASSERT_EQ(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<bool>(), false);
ASSERT_EQ(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 0);
ASSERT_TRUE(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
type = entt::resolve<void>();
ASSERT_EQ(type.prop().cbegin(), type.prop().cend());
}
TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
using namespace entt::literals;
ASSERT_FALSE(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()).value.empty());
entt::meta_reset<double>();
entt::meta_reset<unsigned int>();
entt::meta_reset<base_t>();
entt::meta_reset<derived_t>();
entt::meta_reset<abstract_t>();
entt::meta_reset<concrete_t>();
entt::meta_reset<overloaded_func_t>();
entt::meta_reset<property_t>();
entt::meta_reset<clazz_t>();
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_FALSE(entt::resolve("base"_hs));
ASSERT_FALSE(entt::resolve("derived"_hs));
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_TRUE(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()).value.empty());
ASSERT_FALSE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
// implicitly generated default constructor is not cleared
ASSERT_TRUE(entt::resolve<clazz_t>().construct());
ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
ASSERT_FALSE(entt::resolve<clazz_t>().func("member"_hs));
entt::meta<double>().type("double"_hs);
entt::meta_any any{42.};
ASSERT_TRUE(any);
ASSERT_TRUE(any.allow_cast<int>());
ASSERT_TRUE(any.allow_cast<float>());
ASSERT_FALSE(entt::resolve("derived"_hs));
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta<property_t>()
.type("property"_hs)
.data<property_t::random>("rand"_hs)
.prop(static_cast<entt::id_type>(property_t::value), 42)
.prop(static_cast<entt::id_type>(property_t::random), 3);
ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(static_cast<entt::id_type>(property_t::random)));
}
TEST_F(MetaType, ReRegistration) {
using namespace entt::literals;
int count = 0;
for([[maybe_unused]] auto type: entt::resolve()) {
++count;
}
SetUp();
for([[maybe_unused]] auto type: entt::resolve()) {
--count;
}
ASSERT_EQ(count, 0);
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta<double>().type("real"_hs);
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_TRUE(entt::resolve("real"_hs));
ASSERT_TRUE(entt::resolve("real"_hs).data("var"_hs));
}
TEST_F(MetaType, NameCollision) {
using namespace entt::literals;
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().type("clazz"_hs));
ASSERT_TRUE(entt::resolve("clazz"_hs));
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().type("quux"_hs));
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_TRUE(entt::resolve("quux"_hs));
}
ENTT_DEBUG_TEST_F(MetaTypeDeathTest, NameCollision) {
using namespace entt::literals;
ASSERT_DEATH(entt::meta<clazz_t>().type("abstract"_hs), "");
}

View File

@ -0,0 +1,268 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/utility.hpp>
#include "../common/config.h"
struct clazz {
void setter(int v) {
member = v;
}
int getter() const {
return member;
}
static void static_setter(clazz &instance, int v) {
instance.member = v;
}
static int static_getter(const clazz &instance) {
return instance.member;
}
static void reset_value() {
value = 0;
}
static int get_value() {
return value;
}
static clazz factory(int v) {
clazz instance{};
instance.member = v;
return instance;
}
int member{};
const int cmember{};
inline static int value{};
inline static const int cvalue{};
inline static int arr[3u]{};
};
struct dummy {};
struct MetaUtility: ::testing::Test {
void SetUp() override {
clazz::value = 0;
}
};
using MetaUtilityDeathTest = MetaUtility;
TEST_F(MetaUtility, MetaDispatch) {
int value = 42;
auto as_void = entt::meta_dispatch<entt::as_void_t>(value);
auto as_ref = entt::meta_dispatch<entt::as_ref_t>(value);
auto as_cref = entt::meta_dispatch<entt::as_cref_t>(value);
auto as_is = entt::meta_dispatch(value);
ASSERT_EQ(as_void.type(), entt::resolve<void>());
ASSERT_EQ(as_ref.type(), entt::resolve<int>());
ASSERT_EQ(as_cref.type(), entt::resolve<int>());
ASSERT_EQ(as_is.type(), entt::resolve<int>());
ASSERT_NE(as_is.try_cast<int>(), nullptr);
ASSERT_NE(as_ref.try_cast<int>(), nullptr);
ASSERT_EQ(as_cref.try_cast<int>(), nullptr);
ASSERT_NE(as_cref.try_cast<const int>(), nullptr);
ASSERT_EQ(as_is.cast<int>(), 42);
ASSERT_EQ(as_ref.cast<int>(), 42);
ASSERT_EQ(as_cref.cast<int>(), 42);
}
TEST_F(MetaUtility, MetaDispatchMetaAny) {
entt::meta_any any{42};
auto from_any = entt::meta_dispatch(any);
auto from_const_any = entt::meta_dispatch(std::as_const(any));
ASSERT_EQ(from_any.type(), entt::resolve<int>());
ASSERT_EQ(from_const_any.type(), entt::resolve<int>());
ASSERT_NE(from_any.try_cast<int>(), nullptr);
ASSERT_NE(from_const_any.try_cast<int>(), nullptr);
ASSERT_EQ(from_any.cast<int>(), 42);
ASSERT_EQ(from_const_any.cast<int>(), 42);
}
TEST_F(MetaUtility, MetaDispatchMetaAnyAsRef) {
entt::meta_any any{42};
auto from_any = entt::meta_dispatch(any.as_ref());
auto from_const_any = entt::meta_dispatch(std::as_const(any).as_ref());
ASSERT_EQ(from_any.type(), entt::resolve<int>());
ASSERT_EQ(from_const_any.type(), entt::resolve<int>());
ASSERT_NE(from_any.try_cast<int>(), nullptr);
ASSERT_EQ(from_const_any.try_cast<int>(), nullptr);
ASSERT_NE(from_const_any.try_cast<const int>(), nullptr);
ASSERT_EQ(from_any.cast<int>(), 42);
ASSERT_EQ(from_const_any.cast<int>(), 42);
}
TEST_F(MetaUtility, MetaArg) {
ASSERT_EQ((entt::meta_arg<entt::type_list<int, char>>(0u)), entt::resolve<int>());
ASSERT_EQ((entt::meta_arg<entt::type_list<int, char>>(1u)), entt::resolve<char>());
}
ENTT_DEBUG_TEST_F(MetaUtilityDeathTest, MetaArg) {
ASSERT_DEATH([[maybe_unused]] auto type = entt::meta_arg<entt::type_list<>>(0u), "");
ASSERT_DEATH([[maybe_unused]] auto type = entt::meta_arg<entt::type_list<int>>(3u), "");
}
TEST_F(MetaUtility, MetaSetter) {
const int invalid{};
clazz instance{};
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(instance, instance)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(std::as_const(instance), 42)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(invalid, 42)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::static_setter>(instance, 42)));
ASSERT_EQ(instance.member, 42);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::setter>(instance, instance)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::setter>(std::as_const(instance), 3)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::setter>(invalid, 3)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::setter>(instance, 3)));
ASSERT_EQ(instance.member, 3);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::member>(instance, instance)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::member>(invalid, 99)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::member>(instance, 99)));
ASSERT_EQ(instance.member, 99);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cmember>(instance, 99)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cmember>(invalid, 99)));
ASSERT_EQ(instance.cmember, 0);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::value>(instance, instance)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::value>(invalid, 1)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::value>(instance, 2)));
ASSERT_EQ(clazz::value, 2);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cvalue>(instance, 1)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cvalue>(invalid, 1)));
ASSERT_EQ(clazz::cvalue, 0);
}
TEST_F(MetaUtility, MetaGetter) {
const int invalid{};
clazz instance{};
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::static_getter>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::static_getter>(instance)).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::getter>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::getter>(instance)).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::member>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(std::as_const(instance))).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::cmember>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(std::as_const(instance))).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(invalid)));
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(instance)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(invalid)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(invalid)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, 42>(invalid)).cast<int>(), 42);
ASSERT_EQ((entt::meta_getter<clazz, 42>(instance)).cast<int>(), 42);
}
TEST_F(MetaUtility, MetaInvokeWithCandidate) {
entt::meta_any args[2u]{clazz{}, 42};
args[0u].cast<clazz &>().value = 99;
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::setter, nullptr)));
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::getter, nullptr)));
ASSERT_TRUE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, args + 1u)));
ASSERT_FALSE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, args)));
ASSERT_EQ((entt::meta_invoke<clazz>(args[0u], &clazz::getter, nullptr)).cast<int>(), 42);
ASSERT_FALSE((entt::meta_invoke<clazz>(args[1u], &clazz::getter, nullptr)));
ASSERT_EQ((entt::meta_invoke<clazz>({}, &clazz::get_value, nullptr)).cast<int>(), 99);
ASSERT_TRUE((entt::meta_invoke<clazz>({}, &clazz::reset_value, nullptr)));
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
const auto setter = [](int &value) { value = 3; };
const auto getter = [](int value) { return value * 2; };
ASSERT_TRUE(entt::meta_invoke<dummy>({}, setter, args + 1u));
ASSERT_EQ(entt::meta_invoke<dummy>({}, getter, args + 1u).cast<int>(), 6);
}
TEST_F(MetaUtility, MetaInvoke) {
entt::meta_any args[2u]{clazz{}, 42};
args[0u].cast<clazz &>().value = 99;
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>({}, nullptr)));
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>({}, nullptr)));
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], args + 1u)));
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], args)));
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::getter>(args[0u], nullptr)).cast<int>(), 42);
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(args[1u], nullptr)));
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::get_value>({}, nullptr)).cast<int>(), 99);
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::reset_value>({}, nullptr)));
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
}
TEST_F(MetaUtility, MetaConstructArgsOnly) {
entt::meta_any args[2u]{clazz{}, 42};
const auto any = entt::meta_construct<clazz, int>(args + 1u);
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz, int>(args)));
ASSERT_EQ(any.cast<const clazz &>().member, 42);
}
TEST_F(MetaUtility, MetaConstructWithCandidate) {
entt::meta_any args[2u]{clazz{}, 42};
const auto any = entt::meta_construct<clazz>(&clazz::factory, args + 1u);
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz>(&clazz::factory, args)));
ASSERT_EQ(any.cast<const clazz &>().member, 42);
ASSERT_EQ(args[0u].cast<const clazz &>().member, 0);
ASSERT_TRUE((entt::meta_construct<clazz>(&clazz::static_setter, args)));
ASSERT_EQ(args[0u].cast<const clazz &>().member, 42);
const auto setter = [](int &value) { value = 3; };
const auto builder = [](int value) { return value * 2; };
ASSERT_TRUE(entt::meta_construct<dummy>(setter, args + 1u));
ASSERT_EQ(entt::meta_construct<dummy>(builder, args + 1u).cast<int>(), 6);
}
TEST_F(MetaUtility, MetaConstruct) {
entt::meta_any args[2u]{clazz{}, 42};
const auto any = entt::meta_construct<clazz, &clazz::factory>(args + 1u);
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz, &clazz::factory>(args)));
ASSERT_EQ(any.cast<const clazz &>().member, 42);
ASSERT_EQ(args[0u].cast<const clazz &>().member, 0);
ASSERT_TRUE((entt::meta_construct<clazz, &clazz::static_setter>(args)));
ASSERT_EQ(args[0u].cast<const clazz &>().member, 42);
}

417
test/entt/poly/poly.cpp Normal file
View File

@ -0,0 +1,417 @@
#include <cstddef>
#include <cstdint>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/core/type_traits.hpp>
#include <entt/poly/poly.hpp>
#include "../common/config.h"
template<typename Base>
struct common_type: Base {
void incr() {
entt::poly_call<0>(*this);
}
void set(int v) {
entt::poly_call<1>(*this, v);
}
int get() const {
return entt::poly_call<2>(*this);
}
void decr() {
entt::poly_call<3>(*this);
}
int mul(int v) const {
return entt::poly_call<4>(*this, v);
}
int rand() const {
return entt::poly_call<5>(*this);
}
};
template<typename Type>
struct common_members {
static void decr(Type &self) {
self.set(self.get() - 1);
}
static double mul(const Type &self, double v) {
return v * self.get();
}
};
static int absolutely_random() {
return 42;
}
template<typename Type>
using common_impl = entt::value_list<
&Type::incr,
&Type::set,
&Type::get,
&common_members<Type>::decr,
&common_members<Type>::mul,
&absolutely_random>;
struct Deduced
: entt::type_list<> {
template<typename Base>
using type = common_type<Base>;
template<typename Type>
using members = common_members<Type>;
template<typename Type>
using impl = common_impl<Type>;
};
struct Defined
: entt::type_list<
void(),
void(int),
int() const,
void(),
int(int) const,
int() const> {
template<typename Base>
using type = common_type<Base>;
template<typename Type>
using members = common_members<Type>;
template<typename Type>
using impl = common_impl<Type>;
};
struct DeducedEmbedded
: entt::type_list<> {
template<typename Base>
struct type: Base {
int get() const {
return entt::poly_call<0>(*this);
}
};
template<typename Type>
using impl = entt::value_list<&Type::get>;
};
struct DefinedEmbedded
: entt::type_list<int()> {
template<typename Base>
struct type: Base {
// non-const get on purpose
int get() {
return entt::poly_call<0>(*this);
}
};
template<typename Type>
using impl = entt::value_list<&Type::get>;
};
struct impl {
impl() = default;
impl(int v)
: value{v} {}
void incr() {
++value;
}
void set(int v) {
value = v;
}
int get() const {
return value;
}
int value{};
};
struct alignas(64u) over_aligned: impl {};
template<typename Type>
struct Poly: testing::Test {
template<std::size_t... Args>
using type = entt::basic_poly<Type, Args...>;
};
template<typename Type>
using PolyDeathTest = Poly<Type>;
using PolyTypes = ::testing::Types<Deduced, Defined>;
TYPED_TEST_SUITE(Poly, PolyTypes, );
TYPED_TEST_SUITE(PolyDeathTest, PolyTypes, );
template<typename Type>
struct PolyEmbedded: testing::Test {
using type = entt::basic_poly<Type>;
};
using PolyEmbeddedTypes = ::testing::Types<DeducedEmbedded, DefinedEmbedded>;
TYPED_TEST_SUITE(PolyEmbedded, PolyEmbeddedTypes, );
TYPED_TEST(Poly, Functionalities) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type empty{};
poly_type in_place{std::in_place_type<impl>, 3};
poly_type alias{std::in_place_type<impl &>, instance};
poly_type value{impl{}};
ASSERT_FALSE(empty);
ASSERT_TRUE(in_place);
ASSERT_TRUE(alias);
ASSERT_TRUE(value);
ASSERT_EQ(empty.type(), entt::type_id<void>());
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
ASSERT_EQ(alias.type(), entt::type_id<impl>());
ASSERT_EQ(value.type(), entt::type_id<impl>());
ASSERT_EQ(alias.data(), &instance);
ASSERT_EQ(std::as_const(alias).data(), &instance);
ASSERT_EQ(value->rand(), 42);
empty = impl{};
ASSERT_TRUE(empty);
ASSERT_NE(empty.data(), nullptr);
ASSERT_NE(std::as_const(empty).data(), nullptr);
ASSERT_EQ(empty.type(), entt::type_id<impl>());
ASSERT_EQ(empty->get(), 0);
empty.template emplace<impl>(3);
ASSERT_TRUE(empty);
ASSERT_EQ(std::as_const(empty)->get(), 3);
poly_type ref = in_place.as_ref();
ASSERT_TRUE(ref);
ASSERT_NE(ref.data(), nullptr);
ASSERT_EQ(ref.data(), in_place.data());
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
ASSERT_EQ(ref.type(), entt::type_id<impl>());
ASSERT_EQ(ref->get(), 3);
poly_type null{};
std::swap(empty, null);
ASSERT_FALSE(empty);
poly_type copy = in_place;
ASSERT_TRUE(copy);
ASSERT_EQ(copy->get(), 3);
poly_type move = std::move(copy);
ASSERT_TRUE(move);
ASSERT_TRUE(copy);
ASSERT_EQ(move->get(), 3);
move.reset();
ASSERT_FALSE(move);
ASSERT_EQ(move.type(), entt::type_id<void>());
}
TYPED_TEST(PolyEmbedded, EmbeddedVtable) {
using poly_type = typename TestFixture::type;
poly_type poly{impl{}};
auto *ptr = static_cast<impl *>(poly.data());
ASSERT_TRUE(poly);
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(poly->get(), 0);
ptr->value = 2;
ASSERT_EQ(poly->get(), 2);
}
TYPED_TEST(Poly, Owned) {
using poly_type = typename TestFixture::template type<>;
poly_type poly{impl{}};
auto *ptr = static_cast<impl *>(poly.data());
ASSERT_TRUE(poly);
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(ptr->value, 0);
ASSERT_EQ(poly->get(), 0);
poly->set(1);
poly->incr();
ASSERT_EQ(ptr->value, 2);
ASSERT_EQ(std::as_const(poly)->get(), 2);
ASSERT_EQ(poly->mul(3), 6);
poly->decr();
ASSERT_EQ(ptr->value, 1);
ASSERT_EQ(poly->get(), 1);
ASSERT_EQ(poly->mul(3), 3);
}
TYPED_TEST(Poly, Reference) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type poly{std::in_place_type<impl &>, instance};
ASSERT_TRUE(poly);
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(poly->get(), 0);
poly->set(1);
poly->incr();
ASSERT_EQ(instance.value, 2);
ASSERT_EQ(std::as_const(poly)->get(), 2);
ASSERT_EQ(poly->mul(3), 6);
poly->decr();
ASSERT_EQ(instance.value, 1);
ASSERT_EQ(poly->get(), 1);
ASSERT_EQ(poly->mul(3), 3);
}
ENTT_DEBUG_TYPED_TEST(Poly, ConstReference) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type poly{std::in_place_type<const impl &>, instance};
ASSERT_TRUE(poly);
ASSERT_EQ(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(poly->get(), 0);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(std::as_const(poly)->get(), 0);
ASSERT_EQ(poly->mul(3), 0);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(poly->get(), 0);
ASSERT_EQ(poly->mul(3), 0);
}
TYPED_TEST(PolyDeathTest, ConstReference) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type poly{std::in_place_type<const impl &>, instance};
ASSERT_TRUE(poly);
ASSERT_DEATH(poly->set(1), "");
}
TYPED_TEST(Poly, AsRef) {
using poly_type = typename TestFixture::template type<>;
poly_type poly{impl{}};
auto ref = poly.as_ref();
auto cref = std::as_const(poly).as_ref();
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(ref.data(), nullptr);
ASSERT_EQ(cref.data(), nullptr);
ASSERT_NE(std::as_const(cref).data(), nullptr);
std::swap(ref, cref);
ASSERT_EQ(ref.data(), nullptr);
ASSERT_NE(std::as_const(ref).data(), nullptr);
ASSERT_NE(cref.data(), nullptr);
ref = ref.as_ref();
cref = std::as_const(cref).as_ref();
ASSERT_EQ(ref.data(), nullptr);
ASSERT_NE(std::as_const(ref).data(), nullptr);
ASSERT_EQ(cref.data(), nullptr);
ASSERT_NE(std::as_const(cref).data(), nullptr);
ref = impl{};
cref = impl{};
ASSERT_NE(ref.data(), nullptr);
ASSERT_NE(cref.data(), nullptr);
}
TYPED_TEST(Poly, SBOVsZeroedSBOSize) {
using poly_type = typename TestFixture::template type<>;
using zeroed_type = typename TestFixture::template type<0u>;
poly_type poly{impl{}};
const auto broken = poly.data();
poly_type other = std::move(poly);
ASSERT_NE(broken, other.data());
zeroed_type dyn{impl{}};
const auto valid = dyn.data();
zeroed_type same = std::move(dyn);
ASSERT_EQ(valid, same.data());
// everything works as expected
same->incr();
ASSERT_EQ(same->get(), 1);
}
TYPED_TEST(Poly, SboAlignment) {
static constexpr auto alignment = alignof(over_aligned);
typename TestFixture::template type<alignment, alignment> sbo[2]{over_aligned{}, over_aligned{}};
const auto *data = sbo[0].data();
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
std::swap(sbo[0], sbo[1]);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
ASSERT_NE(data, sbo[1].data());
}
TYPED_TEST(Poly, NoSboAlignment) {
static constexpr auto alignment = alignof(over_aligned);
typename TestFixture::template type<alignment> nosbo[2]{over_aligned{}, over_aligned{}};
const auto *data = nosbo[0].data();
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
std::swap(nosbo[0], nosbo[1]);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
ASSERT_EQ(data, nosbo[1].data());
}

View File

@ -0,0 +1,269 @@
#include <cstdint>
#include <gtest/gtest.h>
#include <entt/process/process.hpp>
struct fake_delta {};
template<typename Delta>
struct fake_process: entt::process<fake_process<Delta>, Delta> {
using process_type = entt::process<fake_process<Delta>, Delta>;
using delta_type = typename process_type::delta_type;
fake_process()
: init_invoked{false},
update_invoked{false},
succeeded_invoked{false},
failed_invoked{false},
aborted_invoked{false} {}
void succeed() noexcept {
process_type::succeed();
}
void fail() noexcept {
process_type::fail();
}
void pause() noexcept {
process_type::pause();
}
void unpause() noexcept {
process_type::unpause();
}
void init() {
init_invoked = true;
}
void succeeded() {
succeeded_invoked = true;
}
void failed() {
failed_invoked = true;
}
void aborted() {
aborted_invoked = true;
}
void update(typename entt::process<fake_process<Delta>, Delta>::delta_type, void *data) {
if(data) {
(*static_cast<int *>(data))++;
}
update_invoked = true;
}
bool init_invoked;
bool update_invoked;
bool succeeded_invoked;
bool failed_invoked;
bool aborted_invoked;
};
TEST(Process, Basics) {
fake_process<int> process{};
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.succeed();
process.fail();
process.abort();
process.pause();
process.unpause();
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.tick(0);
ASSERT_TRUE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.pause();
ASSERT_TRUE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_TRUE(process.paused());
ASSERT_FALSE(process.rejected());
process.unpause();
ASSERT_TRUE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.fail();
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.tick(0);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
}
TEST(Process, Succeeded) {
fake_process<fake_delta> process{};
process.tick({});
process.tick({});
process.succeed();
process.tick({});
ASSERT_FALSE(process.alive());
ASSERT_TRUE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_TRUE(process.update_invoked);
ASSERT_TRUE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_FALSE(process.aborted_invoked);
}
TEST(Process, Fail) {
fake_process<int> process{};
process.tick(0);
process.tick(0);
process.fail();
process.tick(0);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_TRUE(process.update_invoked);
ASSERT_FALSE(process.succeeded_invoked);
ASSERT_TRUE(process.failed_invoked);
ASSERT_FALSE(process.aborted_invoked);
}
TEST(Process, Data) {
fake_process<fake_delta> process{};
int value = 0;
process.tick({});
process.tick({}, &value);
process.succeed();
process.tick({}, &value);
ASSERT_FALSE(process.alive());
ASSERT_TRUE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
ASSERT_EQ(value, 1);
ASSERT_TRUE(process.init_invoked);
ASSERT_TRUE(process.update_invoked);
ASSERT_TRUE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_FALSE(process.aborted_invoked);
}
TEST(Process, AbortNextTick) {
fake_process<int> process{};
process.tick(0);
process.abort();
process.tick(0);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_FALSE(process.update_invoked);
ASSERT_FALSE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_TRUE(process.aborted_invoked);
}
TEST(Process, AbortImmediately) {
fake_process<fake_delta> process{};
process.tick({});
process.abort(true);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_FALSE(process.update_invoked);
ASSERT_FALSE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_TRUE(process.aborted_invoked);
}
TEST(ProcessAdaptor, Resolved) {
bool updated = false;
auto lambda = [&updated](std::uint64_t, void *, auto resolve, auto) {
ASSERT_FALSE(updated);
updated = true;
resolve();
};
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
process.tick(0);
process.tick(0);
ASSERT_TRUE(process.finished());
ASSERT_TRUE(updated);
}
TEST(ProcessAdaptor, Rejected) {
bool updated = false;
auto lambda = [&updated](std::uint64_t, void *, auto, auto rejected) {
ASSERT_FALSE(updated);
updated = true;
rejected();
};
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
process.tick(0);
process.tick(0);
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(updated);
}
TEST(ProcessAdaptor, Data) {
int value = 0;
auto lambda = [](std::uint64_t, void *data, auto resolve, auto) {
*static_cast<int *>(data) = 42;
resolve();
};
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
process.tick(0);
process.tick(0, &value);
ASSERT_TRUE(process.finished());
ASSERT_EQ(value, 42);
}

View File

@ -0,0 +1,154 @@
#include <functional>
#include <utility>
#include <gtest/gtest.h>
#include <entt/process/process.hpp>
#include <entt/process/scheduler.hpp>
struct foo_process: entt::process<foo_process, int> {
foo_process(std::function<void()> upd, std::function<void()> abort)
: on_update{upd}, on_aborted{abort} {}
void update(delta_type, void *) {
on_update();
}
void aborted() {
on_aborted();
}
std::function<void()> on_update;
std::function<void()> on_aborted;
};
struct succeeded_process: entt::process<succeeded_process, int> {
void update(delta_type, void *) {
++invoked;
succeed();
}
static inline unsigned int invoked;
};
struct failed_process: entt::process<failed_process, int> {
void update(delta_type, void *) {
++invoked;
fail();
}
static inline unsigned int invoked;
};
struct Scheduler: ::testing::Test {
void SetUp() override {
succeeded_process::invoked = 0u;
failed_process::invoked = 0u;
}
};
TEST_F(Scheduler, Functionalities) {
entt::scheduler<int> scheduler{};
bool updated = false;
bool aborted = false;
ASSERT_EQ(scheduler.size(), 0u);
ASSERT_TRUE(scheduler.empty());
scheduler.attach<foo_process>(
[&updated]() { updated = true; },
[&aborted]() { aborted = true; });
ASSERT_NE(scheduler.size(), 0u);
ASSERT_FALSE(scheduler.empty());
scheduler.update(0);
scheduler.abort(true);
ASSERT_TRUE(updated);
ASSERT_TRUE(aborted);
ASSERT_NE(scheduler.size(), 0u);
ASSERT_FALSE(scheduler.empty());
scheduler.clear();
ASSERT_EQ(scheduler.size(), 0u);
ASSERT_TRUE(scheduler.empty());
}
TEST_F(Scheduler, Then) {
entt::scheduler<int> scheduler;
// failing process with successor
scheduler.attach<succeeded_process>()
.then<succeeded_process>()
.then<failed_process>()
.then<succeeded_process>();
// failing process without successor
scheduler.attach<succeeded_process>()
.then<succeeded_process>()
.then<failed_process>();
// non-failing process
scheduler.attach<succeeded_process>()
.then<succeeded_process>();
ASSERT_EQ(succeeded_process::invoked, 0u);
ASSERT_EQ(failed_process::invoked, 0u);
while(!scheduler.empty()) {
scheduler.update(0);
}
ASSERT_EQ(succeeded_process::invoked, 6u);
ASSERT_EQ(failed_process::invoked, 2u);
}
TEST_F(Scheduler, Functor) {
entt::scheduler<int> scheduler;
bool first_functor = false;
bool second_functor = false;
auto attach = [&first_functor](auto, void *, auto resolve, auto) {
ASSERT_FALSE(first_functor);
first_functor = true;
resolve();
};
auto then = [&second_functor](auto, void *, auto, auto reject) {
ASSERT_FALSE(second_functor);
second_functor = true;
reject();
};
scheduler.attach(std::move(attach)).then(std::move(then)).then([](auto...) { FAIL(); });
while(!scheduler.empty()) {
scheduler.update(0);
}
ASSERT_TRUE(first_functor);
ASSERT_TRUE(second_functor);
ASSERT_TRUE(scheduler.empty());
}
TEST_F(Scheduler, SpawningProcess) {
entt::scheduler<int> scheduler;
scheduler.attach([&scheduler](auto, void *, auto resolve, auto) {
scheduler.attach<succeeded_process>().then<failed_process>();
resolve();
});
ASSERT_EQ(succeeded_process::invoked, 0u);
ASSERT_EQ(failed_process::invoked, 0u);
while(!scheduler.empty()) {
scheduler.update(0);
}
ASSERT_EQ(succeeded_process::invoked, 1u);
ASSERT_EQ(failed_process::invoked, 1u);
}

View File

@ -0,0 +1,144 @@
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/resource/resource.hpp>
struct base {
virtual ~base() = default;
virtual const entt::type_info &type() const noexcept {
return entt::type_id<base>();
}
};
struct derived: base {
const entt::type_info &type() const noexcept override {
return entt::type_id<derived>();
}
};
template<typename Type, typename Other>
entt::resource<Type> dynamic_resource_cast(const entt::resource<Other> &other) {
if(other->type() == entt::type_id<Type>()) {
return entt::resource<Type>{other, static_cast<Type &>(*other)};
}
return {};
}
TEST(Resource, Functionalities) {
entt::resource<derived> resource{};
ASSERT_FALSE(resource);
ASSERT_EQ(resource.operator->(), nullptr);
ASSERT_EQ(resource.handle().use_count(), 0l);
const auto value = std::make_shared<derived>();
entt::resource<derived> other{value};
ASSERT_TRUE(other);
ASSERT_EQ(other.operator->(), value.get());
ASSERT_EQ(&static_cast<derived &>(other), value.get());
ASSERT_EQ(&*other, value.get());
ASSERT_EQ(other.handle().use_count(), 2l);
entt::resource<derived> copy{resource};
entt::resource<derived> move{std::move(other)};
ASSERT_FALSE(copy);
ASSERT_TRUE(move);
copy = std::move(move);
move = copy;
ASSERT_TRUE(copy);
ASSERT_TRUE(move);
ASSERT_EQ(copy, move);
}
TEST(Resource, DerivedToBase) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<base> other{resource};
entt::resource<const base> cother{resource};
ASSERT_TRUE(resource);
ASSERT_TRUE(other);
ASSERT_TRUE(cother);
ASSERT_EQ(resource, other);
ASSERT_EQ(other, cother);
other = resource;
cother = resource;
ASSERT_EQ(resource, other);
ASSERT_EQ(other, cother);
}
TEST(Resource, ConstNonConstAndAllInBetween) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<derived> other{resource};
static_assert(std::is_same_v<decltype(*resource), derived &>);
static_assert(std::is_same_v<decltype(*entt::resource<const derived>{other}), const derived &>);
static_assert(std::is_same_v<decltype(*std::as_const(resource)), derived &>);
entt::resource<const derived> copy{resource};
entt::resource<const derived> move{std::move(other)};
ASSERT_TRUE(resource);
ASSERT_FALSE(other);
ASSERT_TRUE(copy);
ASSERT_EQ(copy, resource);
ASSERT_NE(copy, entt::resource<derived>{});
ASSERT_EQ(copy.handle().use_count(), 3u);
ASSERT_TRUE(move);
ASSERT_EQ(move, resource);
ASSERT_NE(move, entt::resource<derived>{});
ASSERT_EQ(move.handle().use_count(), 3u);
copy = resource;
move = std::move(resource);
ASSERT_FALSE(resource);
ASSERT_FALSE(other);
ASSERT_TRUE(copy);
ASSERT_TRUE(move);
ASSERT_EQ(copy.handle().use_count(), 2u);
}
TEST(Resource, DynamicResourceHandleCast) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<const base> other = resource;
ASSERT_TRUE(other);
ASSERT_EQ(resource.handle().use_count(), 2u);
ASSERT_EQ(resource, other);
entt::resource<const derived> cast = dynamic_resource_cast<const derived>(other);
ASSERT_TRUE(cast);
ASSERT_EQ(resource.handle().use_count(), 3u);
ASSERT_EQ(resource, cast);
other = entt::resource<base>{std::make_shared<base>()};
cast = dynamic_resource_cast<const derived>(other);
ASSERT_FALSE(cast);
ASSERT_EQ(resource.handle().use_count(), 1u);
}
TEST(Resource, Comparison) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<const base> other = resource;
ASSERT_TRUE(resource == other);
ASSERT_FALSE(resource != other);
ASSERT_FALSE(resource < other);
ASSERT_FALSE(resource > other);
ASSERT_TRUE(resource <= other);
ASSERT_TRUE(resource >= other);
}

View File

@ -0,0 +1,428 @@
#include <cstddef>
#include <memory>
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/resource/cache.hpp>
#include <entt/resource/loader.hpp>
#include "../common/throwing_allocator.hpp"
struct broken_tag {};
struct with_callback {};
template<typename Type>
struct loader {
using result_type = std::shared_ptr<Type>;
template<typename... Args>
result_type operator()(Args &&...args) const {
return std::make_shared<Type>(std::forward<Args>(args)...);
}
template<typename Func>
result_type operator()(with_callback, Func &&func) const {
return std::forward<Func>(func)();
}
template<typename... Args>
result_type operator()(broken_tag, Args &&...) const {
return {};
}
};
TEST(ResourceCache, Functionalities) {
using namespace entt::literals;
entt::resource_cache<int> cache;
ASSERT_NO_FATAL_FAILURE([[maybe_unused]] auto alloc = cache.get_allocator());
ASSERT_TRUE(cache.empty());
ASSERT_EQ(cache.size(), 0u);
ASSERT_EQ(cache.begin(), cache.end());
ASSERT_EQ(std::as_const(cache).begin(), std::as_const(cache).end());
ASSERT_EQ(cache.cbegin(), cache.cend());
ASSERT_FALSE(cache.contains("resource"_hs));
cache.load("resource"_hs, 42);
ASSERT_FALSE(cache.empty());
ASSERT_EQ(cache.size(), 1u);
ASSERT_NE(cache.begin(), cache.end());
ASSERT_NE(std::as_const(cache).begin(), std::as_const(cache).end());
ASSERT_NE(cache.cbegin(), cache.cend());
ASSERT_TRUE(cache.contains("resource"_hs));
cache.clear();
ASSERT_TRUE(cache.empty());
ASSERT_EQ(cache.size(), 0u);
ASSERT_EQ(cache.begin(), cache.end());
ASSERT_EQ(std::as_const(cache).begin(), std::as_const(cache).end());
ASSERT_EQ(cache.cbegin(), cache.cend());
ASSERT_FALSE(cache.contains("resource"_hs));
}
TEST(ResourceCache, Constructors) {
using namespace entt::literals;
entt::resource_cache<int> cache;
cache = entt::resource_cache<int>{std::allocator<int>{}};
cache = entt::resource_cache<int>{entt::resource_loader<int>{}, std::allocator<float>{}};
cache.load("resource"_hs, 42u);
entt::resource_cache<int> temp{cache, cache.get_allocator()};
entt::resource_cache<int> other{std::move(temp), cache.get_allocator()};
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(other.size(), 1u);
}
TEST(ResourceCache, Copy) {
using namespace entt::literals;
entt::resource_cache<std::size_t> cache;
cache.load("resource"_hs, 42u);
entt::resource_cache<std::size_t> other{cache};
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_TRUE(other.contains("resource"_hs));
cache.load("foo"_hs, 99u);
cache.load("bar"_hs, 77u);
other.load("quux"_hs, 0u);
other = cache;
ASSERT_TRUE(other.contains("resource"_hs));
ASSERT_TRUE(other.contains("foo"_hs));
ASSERT_TRUE(other.contains("bar"_hs));
ASSERT_FALSE(other.contains("quux"_hs));
ASSERT_EQ(other["resource"_hs], 42u);
ASSERT_EQ(other["foo"_hs], 99u);
ASSERT_EQ(other["bar"_hs], 77u);
}
TEST(ResourceCache, Move) {
using namespace entt::literals;
entt::resource_cache<std::size_t> cache;
cache.load("resource"_hs, 42u);
entt::resource_cache<std::size_t> other{std::move(cache)};
ASSERT_EQ(cache.size(), 0u);
ASSERT_TRUE(other.contains("resource"_hs));
cache = other;
cache.load("foo"_hs, 99u);
cache.load("bar"_hs, 77u);
other.load("quux"_hs, 0u);
other = std::move(cache);
ASSERT_EQ(cache.size(), 0u);
ASSERT_TRUE(other.contains("resource"_hs));
ASSERT_TRUE(other.contains("foo"_hs));
ASSERT_TRUE(other.contains("bar"_hs));
ASSERT_FALSE(other.contains("quux"_hs));
ASSERT_EQ(other["resource"_hs], 42u);
ASSERT_EQ(other["foo"_hs], 99u);
ASSERT_EQ(other["bar"_hs], 77u);
}
TEST(ResourceCache, Iterator) {
using namespace entt::literals;
using iterator = typename entt::resource_cache<int>::iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<entt::id_type, entt::resource<int>>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::resource<int>>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<entt::id_type, entt::resource<int>>>);
entt::resource_cache<int> cache;
cache.load("resource"_hs, 42);
iterator end{cache.begin()};
iterator begin{};
begin = cache.end();
std::swap(begin, end);
ASSERT_EQ(begin, cache.begin());
ASSERT_EQ(end, cache.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, cache.begin());
ASSERT_EQ(begin--, cache.end());
ASSERT_EQ(begin + 1, cache.end());
ASSERT_EQ(end - 1, cache.begin());
ASSERT_EQ(++begin, cache.end());
ASSERT_EQ(--begin, cache.begin());
ASSERT_EQ(begin += 1, cache.end());
ASSERT_EQ(begin -= 1, cache.begin());
ASSERT_EQ(begin + (end - begin), cache.end());
ASSERT_EQ(begin - (begin - end), cache.end());
ASSERT_EQ(end - (end - begin), cache.begin());
ASSERT_EQ(end + (begin - end), cache.begin());
ASSERT_EQ(begin[0u].first, cache.begin()->first);
ASSERT_EQ(begin[0u].second, (*cache.begin()).second);
ASSERT_LT(begin, end);
ASSERT_LE(begin, cache.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, cache.end());
cache.load("other"_hs, 3);
begin = cache.begin();
ASSERT_EQ(begin[0u].first, "resource"_hs);
ASSERT_EQ(begin[1u].second, 3);
}
TEST(ResourceCache, ConstIterator) {
using namespace entt::literals;
using iterator = typename entt::resource_cache<int>::const_iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<entt::id_type, entt::resource<const int>>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::resource<const int>>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<entt::id_type, entt::resource<const int>>>);
entt::resource_cache<int> cache;
cache.load("resource"_hs, 42);
iterator cend{cache.cbegin()};
iterator cbegin{};
cbegin = cache.cend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, cache.cbegin());
ASSERT_EQ(cend, cache.cend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, cache.cbegin());
ASSERT_EQ(cbegin--, cache.cend());
ASSERT_EQ(cbegin + 1, cache.cend());
ASSERT_EQ(cend - 1, cache.cbegin());
ASSERT_EQ(++cbegin, cache.cend());
ASSERT_EQ(--cbegin, cache.cbegin());
ASSERT_EQ(cbegin += 1, cache.cend());
ASSERT_EQ(cbegin -= 1, cache.cbegin());
ASSERT_EQ(cbegin + (cend - cbegin), cache.cend());
ASSERT_EQ(cbegin - (cbegin - cend), cache.cend());
ASSERT_EQ(cend - (cend - cbegin), cache.cbegin());
ASSERT_EQ(cend + (cbegin - cend), cache.cbegin());
ASSERT_EQ(cbegin[0u].first, cache.cbegin()->first);
ASSERT_EQ(cbegin[0u].second, (*cache.cbegin()).second);
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, cache.cbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, cache.cend());
cache.load("other"_hs, 3);
cbegin = cache.cbegin();
ASSERT_EQ(cbegin[0u].first, "resource"_hs);
ASSERT_EQ(cbegin[1u].second, 3);
}
TEST(ResourceCache, IteratorConversion) {
using namespace entt::literals;
entt::resource_cache<int> cache;
cache.load("resource"_hs, 42);
typename entt::resource_cache<int>::iterator it = cache.begin();
typename entt::resource_cache<int>::const_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), std::pair<entt::id_type, entt::resource<int>>>);
static_assert(std::is_same_v<decltype(*cit), std::pair<entt::id_type, entt::resource<const int>>>);
ASSERT_EQ(it->first, "resource"_hs);
ASSERT_EQ((*it).second, 42);
ASSERT_EQ(it->first, cit->first);
ASSERT_EQ((*it).second, (*cit).second);
ASSERT_EQ(it - cit, 0);
ASSERT_EQ(cit - it, 0);
ASSERT_LE(it, cit);
ASSERT_LE(cit, it);
ASSERT_GE(it, cit);
ASSERT_GE(cit, it);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(ResourceCache, Load) {
using namespace entt::literals;
entt::resource_cache<int> cache;
typename entt::resource_cache<int>::iterator it;
bool result;
ASSERT_TRUE(cache.empty());
ASSERT_EQ(cache.size(), 0u);
ASSERT_EQ(cache["resource"_hs], entt::resource<int>{});
ASSERT_EQ(std::as_const(cache)["resource"_hs], entt::resource<const int>{});
ASSERT_FALSE(cache.contains("resource"_hs));
std::tie(it, result) = cache.load("resource"_hs, 1);
ASSERT_TRUE(result);
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(it, --cache.end());
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_NE(cache["resource"_hs], entt::resource<int>{});
ASSERT_NE(std::as_const(cache)["resource"_hs], entt::resource<const int>{});
ASSERT_EQ(it->first, "resource"_hs);
ASSERT_EQ(it->second, 1);
std::tie(it, result) = cache.load("resource"_hs, 2);
ASSERT_FALSE(result);
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(it, --cache.end());
ASSERT_EQ(it->second, 1);
std::tie(it, result) = cache.force_load("resource"_hs, 3);
ASSERT_TRUE(result);
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(it, --cache.end());
ASSERT_EQ(it->second, 3);
}
TEST(ResourceCache, Erase) {
static constexpr std::size_t resource_count = 8u;
entt::resource_cache<std::size_t> cache;
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
cache.load(next, next);
}
ASSERT_EQ(cache.size(), resource_count + 1u);
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
ASSERT_TRUE(cache.contains(next));
}
auto it = cache.erase(++cache.begin());
it = cache.erase(it, it + 1);
ASSERT_EQ((--cache.end())->first, 6u);
ASSERT_EQ(cache.erase(6u), 1u);
ASSERT_EQ(cache.erase(6u), 0u);
ASSERT_EQ(cache.size(), resource_count + 1u - 3u);
ASSERT_EQ(it, ++cache.begin());
ASSERT_EQ(it->first, 7u);
ASSERT_EQ((--cache.end())->first, 5u);
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
if(next == 1u || next == 8u || next == 6u) {
ASSERT_FALSE(cache.contains(next));
} else {
ASSERT_TRUE(cache.contains(next));
}
}
cache.erase(cache.begin(), cache.end());
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
ASSERT_FALSE(cache.contains(next));
}
ASSERT_EQ(cache.size(), 0u);
}
TEST(ResourceCache, Indexing) {
using namespace entt::literals;
entt::resource_cache<int> cache;
ASSERT_FALSE(cache.contains("resource"_hs));
ASSERT_FALSE(cache["resource"_hs]);
ASSERT_FALSE(std::as_const(cache)["resource"_hs]);
cache.load("resource"_hs, 99);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache[std::move("resource"_hs)], 99);
ASSERT_EQ(std::as_const(cache)["resource"_hs], 99);
}
TEST(ResourceCache, LoaderDispatching) {
using namespace entt::literals;
entt::resource_cache<int, loader<int>> cache;
cache.force_load("resource"_hs, 99);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache["resource"_hs], 99);
cache.force_load("resource"_hs, with_callback{}, []() { return std::make_shared<int>(42); });
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache["resource"_hs], 42);
}
TEST(ResourceCache, BrokenLoader) {
using namespace entt::literals;
entt::resource_cache<int, loader<int>> cache;
cache.load("resource"_hs, broken_tag{});
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_FALSE(cache["resource"_hs]);
cache.force_load("resource"_hs, 42);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_TRUE(cache["resource"_hs]);
}
TEST(ResourceCache, ThrowingAllocator) {
using namespace entt::literals;
using allocator = test::throwing_allocator<std::size_t>;
using packed_allocator = test::throwing_allocator<entt::internal::dense_map_node<entt::id_type, std::shared_ptr<std::size_t>>>;
using packed_exception = typename packed_allocator::exception_type;
entt::resource_cache<std::size_t, entt::resource_loader<std::size_t>, allocator> cache{};
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(cache.load("resource"_hs), packed_exception);
ASSERT_FALSE(cache.contains("resource"_hs));
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(cache.force_load("resource"_hs), packed_exception);
ASSERT_FALSE(cache.contains("resource"_hs));
}

View File

@ -0,0 +1,13 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/resource/loader.hpp>
TEST(ResourceLoader, Functionalities) {
using loader_type = entt::resource_loader<int>;
const auto resource = loader_type{}(42);
static_assert(std::is_same_v<typename loader_type::result_type, std::shared_ptr<int>>);
ASSERT_TRUE(resource);
ASSERT_EQ(*resource, 42);
}

View File

@ -0,0 +1,433 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/delegate.hpp>
#include "../common/config.h"
int delegate_function(const int &i) {
return i * i;
}
int curried_by_ref(const int &i, int j) {
return i + j;
}
int curried_by_ptr(const int *i, int j) {
return (*i) * j;
}
int non_const_reference(int &i) {
return i *= i;
}
int move_only_type(std::unique_ptr<int> ptr) {
return *ptr;
}
struct delegate_functor {
int operator()(int i) {
return i + i;
}
int identity(int i) const {
return i;
}
static const int static_value = 3;
const int data_member = 42;
};
struct const_nonconst_noexcept {
void f() {
++cnt;
}
void g() noexcept {
++cnt;
}
void h() const {
++cnt;
}
void i() const noexcept {
++cnt;
}
int u{};
const int v{};
mutable int cnt{0};
};
TEST(Delegate, Functionalities) {
entt::delegate<int(int)> ff_del;
entt::delegate<int(int)> mf_del;
entt::delegate<int(int)> lf_del;
delegate_functor functor;
ASSERT_FALSE(ff_del);
ASSERT_FALSE(mf_del);
ASSERT_EQ(ff_del, mf_del);
ff_del.connect<&delegate_function>();
mf_del.connect<&delegate_functor::operator()>(functor);
lf_del.connect([](const void *ptr, int value) { return static_cast<const delegate_functor *>(ptr)->identity(value); }, &functor);
ASSERT_TRUE(ff_del);
ASSERT_TRUE(mf_del);
ASSERT_TRUE(lf_del);
ASSERT_EQ(ff_del(3), 9);
ASSERT_EQ(mf_del(3), 6);
ASSERT_EQ(lf_del(3), 3);
ff_del.reset();
ASSERT_FALSE(ff_del);
ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
ASSERT_NE(mf_del, entt::delegate<int(int)>{});
ASSERT_NE(lf_del, entt::delegate<int(int)>{});
ASSERT_NE(ff_del, mf_del);
ASSERT_NE(ff_del, lf_del);
ASSERT_NE(mf_del, lf_del);
mf_del.reset();
ASSERT_FALSE(ff_del);
ASSERT_FALSE(mf_del);
ASSERT_TRUE(lf_del);
ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
ASSERT_EQ(mf_del, entt::delegate<int(int)>{});
ASSERT_NE(lf_del, entt::delegate<int(int)>{});
ASSERT_EQ(ff_del, mf_del);
ASSERT_NE(ff_del, lf_del);
ASSERT_NE(mf_del, lf_del);
}
ENTT_DEBUG_TEST(DelegateDeathTest, InvokeEmpty) {
entt::delegate<int(int)> del;
ASSERT_FALSE(del);
ASSERT_DEATH(del(42), "");
ASSERT_DEATH(std::as_const(del)(42), "");
}
TEST(Delegate, DataMembers) {
entt::delegate<double()> delegate;
delegate_functor functor;
delegate.connect<&delegate_functor::data_member>(functor);
ASSERT_EQ(delegate(), 42);
}
TEST(Delegate, Comparison) {
entt::delegate<int(int)> lhs;
entt::delegate<int(int)> rhs;
delegate_functor functor;
delegate_functor other;
const int value = 0;
ASSERT_EQ(lhs, entt::delegate<int(int)>{});
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_function>();
ASSERT_EQ(lhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&delegate_function>();
ASSERT_EQ(rhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&curried_by_ref>(value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ref>, value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&curried_by_ref>(value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ref>, value}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&curried_by_ptr>(&value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ptr>, &value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&curried_by_ptr>(&value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ptr>, &value}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, functor}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_functor::operator()>(other);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, other}));
ASSERT_NE(lhs.data(), rhs.data());
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
lhs.connect([](const void *ptr, int val) { return static_cast<const delegate_functor *>(ptr)->identity(val) * val; }, &functor);
ASSERT_NE(lhs, (entt::delegate<int(int)>{[](const void *, int val) { return val + val; }, &functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect([](const void *ptr, int val) { return static_cast<const delegate_functor *>(ptr)->identity(val) + val; }, &functor);
ASSERT_NE(rhs, (entt::delegate<int(int)>{[](const void *, int val) { return val * val; }, &functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
lhs.reset();
ASSERT_EQ(lhs, (entt::delegate<int(int)>{}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.reset();
ASSERT_EQ(rhs, (entt::delegate<int(int)>{}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
}
TEST(Delegate, ConstNonConstNoExcept) {
entt::delegate<void()> delegate;
const_nonconst_noexcept functor;
delegate.connect<&const_nonconst_noexcept::f>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::g>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::h>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::i>(functor);
delegate();
ASSERT_EQ(functor.cnt, 4);
}
TEST(Delegate, DeductionGuide) {
const_nonconst_noexcept functor;
int value = 0;
entt::delegate func{entt::connect_arg<&delegate_function>};
entt::delegate curried_func_with_ref{entt::connect_arg<&curried_by_ref>, value};
entt::delegate curried_func_with_const_ref{entt::connect_arg<&curried_by_ref>, std::as_const(value)};
entt::delegate curried_func_with_ptr{entt::connect_arg<&curried_by_ptr>, &value};
entt::delegate curried_func_with_const_ptr{entt::connect_arg<&curried_by_ptr>, &std::as_const(value)};
entt::delegate member_func_f{entt::connect_arg<&const_nonconst_noexcept::f>, functor};
entt::delegate member_func_g{entt::connect_arg<&const_nonconst_noexcept::g>, functor};
entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, &std::as_const(functor)};
entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, functor};
entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, std::as_const(functor)};
entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, functor};
entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, &functor};
entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, &std::as_const(functor)};
entt::delegate lambda{+[](const void *, int) { return 0; }};
static_assert(std::is_same_v<typename decltype(func)::type, int(const int &)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_ref)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_const_ref)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_ptr)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_const_ptr)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(member_func_f)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_g)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_h)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_h_const)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_i)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_i_const)::type, void()>);
static_assert(std::is_same_v<typename decltype(data_member_u)::type, int()>);
static_assert(std::is_same_v<typename decltype(data_member_v)::type, const int()>);
static_assert(std::is_same_v<typename decltype(data_member_v_const)::type, const int()>);
static_assert(std::is_same_v<typename decltype(lambda)::type, int(int)>);
ASSERT_TRUE(func);
ASSERT_TRUE(curried_func_with_ref);
ASSERT_TRUE(curried_func_with_const_ref);
ASSERT_TRUE(curried_func_with_ptr);
ASSERT_TRUE(curried_func_with_const_ptr);
ASSERT_TRUE(member_func_f);
ASSERT_TRUE(member_func_g);
ASSERT_TRUE(member_func_h);
ASSERT_TRUE(member_func_h_const);
ASSERT_TRUE(member_func_i);
ASSERT_TRUE(member_func_i_const);
ASSERT_TRUE(data_member_u);
ASSERT_TRUE(data_member_v);
ASSERT_TRUE(data_member_v_const);
ASSERT_TRUE(lambda);
}
TEST(Delegate, ConstInstance) {
entt::delegate<int(int)> delegate;
const delegate_functor functor;
ASSERT_FALSE(delegate);
delegate.connect<&delegate_functor::identity>(functor);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(3), 3);
delegate.reset();
ASSERT_FALSE(delegate);
ASSERT_EQ(delegate, entt::delegate<int(int)>{});
}
TEST(Delegate, NonConstReference) {
entt::delegate<int(int &)> delegate;
delegate.connect<&non_const_reference>();
int value = 3;
ASSERT_EQ(delegate(value), value);
ASSERT_EQ(value, 9);
}
TEST(Delegate, MoveOnlyType) {
entt::delegate<int(std::unique_ptr<int>)> delegate;
auto ptr = std::make_unique<int>(3);
delegate.connect<&move_only_type>();
ASSERT_EQ(delegate(std::move(ptr)), 3);
ASSERT_FALSE(ptr);
}
TEST(Delegate, CurriedFunction) {
entt::delegate<int(int)> delegate;
const auto value = 3;
delegate.connect<&curried_by_ref>(value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(1), 4);
delegate.connect<&curried_by_ptr>(&value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(2), 6);
}
TEST(Delegate, Constructors) {
delegate_functor functor;
const auto value = 2;
entt::delegate<int(int)> empty{};
entt::delegate<int(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<int(int)> ref{entt::connect_arg<&curried_by_ref>, value};
entt::delegate<int(int)> ptr{entt::connect_arg<&curried_by_ptr>, &value};
entt::delegate<int(int)> member{entt::connect_arg<&delegate_functor::operator()>, functor};
ASSERT_FALSE(empty);
ASSERT_TRUE(func);
ASSERT_EQ(9, func(3));
ASSERT_TRUE(ref);
ASSERT_EQ(5, ref(3));
ASSERT_TRUE(ptr);
ASSERT_EQ(6, ptr(3));
ASSERT_TRUE(member);
ASSERT_EQ(6, member(3));
}
TEST(Delegate, VoidVsNonVoidReturnType) {
delegate_functor functor;
entt::delegate<void(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)};
ASSERT_TRUE(func);
ASSERT_TRUE(member);
ASSERT_TRUE(cmember);
}
TEST(Delegate, UnboundDataMember) {
entt::delegate<int(const delegate_functor &)> delegate;
delegate.connect<&delegate_functor::data_member>();
delegate_functor functor;
ASSERT_EQ(delegate(functor), 42);
}
TEST(Delegate, UnboundMemberFunction) {
entt::delegate<int(delegate_functor *, const int &i)> delegate;
delegate.connect<&delegate_functor::operator()>();
delegate_functor functor;
ASSERT_EQ(delegate(&functor, 3), 6);
}
TEST(Delegate, TheLessTheBetter) {
entt::delegate<int(int, char)> bound;
entt::delegate<int(delegate_functor &, int, char)> unbound;
delegate_functor functor;
// int delegate_function(const int &);
bound.connect<&delegate_function>();
ASSERT_EQ(bound(3, 'c'), 9);
// int delegate_functor::operator()(int);
bound.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(bound(3, 'c'), 6);
// int delegate_functor::operator()(int);
bound.connect<&delegate_functor::identity>(&functor);
ASSERT_EQ(bound(3, 'c'), 3);
// int delegate_functor::operator()(int);
unbound.connect<&delegate_functor::operator()>();
ASSERT_EQ(unbound(functor, 3, 'c'), 6);
}

View File

@ -0,0 +1,200 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/signal/dispatcher.hpp>
struct an_event {};
struct another_event {};
// makes the type non-aggregate
struct one_more_event {
one_more_event(int) {}
};
struct receiver {
static void forward(entt::dispatcher &dispatcher, an_event &event) {
dispatcher.enqueue(event);
}
void receive(const an_event &) {
++cnt;
}
void reset() {
cnt = 0;
}
int cnt{0};
};
TEST(Dispatcher, Functionalities) {
entt::dispatcher dispatcher;
entt::dispatcher other;
receiver receiver;
ASSERT_NO_FATAL_FAILURE(entt::dispatcher{std::move(dispatcher)});
ASSERT_NO_FATAL_FAILURE(dispatcher = std::move(other));
ASSERT_EQ(dispatcher.size<an_event>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
dispatcher.trigger(one_more_event{42});
dispatcher.enqueue<one_more_event>(42);
dispatcher.update<one_more_event>();
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
dispatcher.enqueue<an_event>();
ASSERT_EQ(dispatcher.size<one_more_event>(), 0u);
ASSERT_EQ(dispatcher.size<an_event>(), 1u);
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.enqueue(another_event{});
dispatcher.update<another_event>();
ASSERT_EQ(dispatcher.size<another_event>(), 0u);
ASSERT_EQ(dispatcher.size<an_event>(), 1u);
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.update<an_event>();
dispatcher.trigger<an_event>();
ASSERT_EQ(dispatcher.size<an_event>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue<an_event>();
dispatcher.clear<an_event>();
dispatcher.update();
dispatcher.enqueue(an_event{});
dispatcher.clear();
dispatcher.update();
ASSERT_EQ(dispatcher.size<an_event>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(receiver.cnt, 3);
receiver.reset();
an_event event{};
dispatcher.sink<an_event>().disconnect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
dispatcher.enqueue(event);
dispatcher.update();
dispatcher.trigger(std::as_const(event));
ASSERT_EQ(receiver.cnt, 0);
}
TEST(Dispatcher, Swap) {
entt::dispatcher dispatcher;
entt::dispatcher other;
receiver receiver;
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.enqueue<an_event>();
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(receiver.cnt, 0);
dispatcher.swap(other);
dispatcher.update();
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(receiver.cnt, 0);
other.update();
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(receiver.cnt, 1);
}
TEST(Dispatcher, StopAndGo) {
entt::dispatcher dispatcher;
receiver receiver;
dispatcher.sink<an_event>().connect<&receiver::forward>(dispatcher);
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.enqueue<an_event>();
dispatcher.update();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.sink<an_event>().disconnect<&receiver::forward>(dispatcher);
dispatcher.update();
ASSERT_EQ(receiver.cnt, 2);
}
TEST(Dispatcher, OpaqueDisconnect) {
entt::dispatcher dispatcher;
receiver receiver;
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.disconnect(receiver);
dispatcher.trigger<an_event>();
ASSERT_EQ(receiver.cnt, 1);
}
TEST(Dispatcher, NamedQueue) {
using namespace entt::literals;
entt::dispatcher dispatcher;
receiver receiver;
dispatcher.sink<an_event>("named"_hs).connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
ASSERT_EQ(receiver.cnt, 0);
dispatcher.trigger("named"_hs, an_event{});
ASSERT_EQ(receiver.cnt, 1);
dispatcher.enqueue<an_event>();
dispatcher.enqueue(an_event{});
dispatcher.enqueue_hint<an_event>("named"_hs);
dispatcher.enqueue_hint("named"_hs, an_event{});
dispatcher.update<an_event>();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.clear<an_event>();
dispatcher.update<an_event>("named"_hs);
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue_hint<an_event>("named"_hs);
dispatcher.clear<an_event>("named"_hs);
dispatcher.update<an_event>("named"_hs);
ASSERT_EQ(receiver.cnt, 3);
}
TEST(Dispatcher, CustomAllocator) {
std::allocator<void> allocator;
entt::dispatcher dispatcher{allocator};
ASSERT_EQ(dispatcher.get_allocator(), allocator);
ASSERT_FALSE(dispatcher.get_allocator() != allocator);
dispatcher.enqueue<an_event>();
decltype(dispatcher) other{std::move(dispatcher), allocator};
ASSERT_EQ(other.size<an_event>(), 1u);
}

View File

@ -0,0 +1,170 @@
#include <functional>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/emitter.hpp>
struct test_emitter: entt::emitter<test_emitter> {
using entt::emitter<test_emitter>::emitter;
};
struct foo_event {
int i;
};
struct bar_event {};
struct quux_event {};
TEST(Emitter, Move) {
test_emitter emitter;
emitter.on<foo_event>([](auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
test_emitter other{std::move(emitter)};
ASSERT_FALSE(other.empty());
ASSERT_TRUE(other.contains<foo_event>());
ASSERT_TRUE(emitter.empty());
emitter = std::move(other);
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(other.empty());
}
TEST(Emitter, Swap) {
test_emitter emitter;
test_emitter other;
int value{};
emitter.on<foo_event>([&value](auto &event, const auto &) {
value = event.i;
});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(other.empty());
emitter.swap(other);
emitter.publish(foo_event{42});
ASSERT_EQ(value, 0);
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(other.empty());
other.publish(foo_event{42});
ASSERT_EQ(value, 42);
}
TEST(Emitter, Clear) {
test_emitter emitter;
ASSERT_TRUE(emitter.empty());
emitter.on<foo_event>([](auto &, const auto &) {});
emitter.on<quux_event>([](const auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
emitter.erase<bar_event>();
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
emitter.erase<foo_event>();
ASSERT_FALSE(emitter.empty());
ASSERT_FALSE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
emitter.on<foo_event>([](auto &, const auto &) {});
emitter.on<bar_event>([](const auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_TRUE(emitter.contains<bar_event>());
emitter.clear();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.contains<foo_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
}
TEST(Emitter, ClearFromCallback) {
test_emitter emitter;
ASSERT_TRUE(emitter.empty());
emitter.on<foo_event>([](auto &, auto &owner) {
owner.template on<foo_event>([](auto &, auto &) {});
owner.template erase<foo_event>();
});
emitter.on<bar_event>([](const auto &, auto &owner) {
owner.template on<bar_event>([](const auto &, auto &) {});
owner.template erase<bar_event>();
});
ASSERT_FALSE(emitter.empty());
emitter.publish(foo_event{});
emitter.publish(bar_event{});
ASSERT_TRUE(emitter.empty());
}
TEST(Emitter, On) {
test_emitter emitter;
int value{};
emitter.on<foo_event>([&value](auto &event, const auto &) {
value = event.i;
});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_EQ(value, 0);
emitter.publish(foo_event{42});
ASSERT_EQ(value, 42);
}
TEST(Emitter, OnAndErase) {
test_emitter emitter;
std::function<void(bar_event &, test_emitter &)> func{};
emitter.on(func);
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<bar_event>());
emitter.erase<bar_event>();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.contains<bar_event>());
}
TEST(Emitter, CustomAllocator) {
std::allocator<void> allocator;
test_emitter emitter{allocator};
ASSERT_EQ(emitter.get_allocator(), allocator);
ASSERT_FALSE(emitter.get_allocator() != allocator);
emitter.on<foo_event>([](auto &, const auto &) {});
decltype(emitter) other{std::move(emitter), allocator};
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(other.empty());
}

577
test/entt/signal/sigh.cpp Normal file
View File

@ -0,0 +1,577 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/sigh.hpp>
struct sigh_listener {
static void f(int &v) {
v = 42;
}
bool g(int) {
k = !k;
return true;
}
bool h(const int &) {
return k;
}
// useless definition just because msvc does weird things if both are empty
void l() {
k = true && k;
}
bool k{false};
};
struct before_after {
void add(int v) {
value += v;
}
void mul(int v) {
value *= v;
}
static void static_add(int v) {
before_after::value += v;
}
static void static_mul(before_after &instance, int v) {
instance.value *= v;
}
static inline int value{};
};
struct SigH: ::testing::Test {
void SetUp() override {
before_after::value = 0;
}
};
struct const_nonconst_noexcept {
void f() {
++cnt;
}
void g() noexcept {
++cnt;
}
void h() const {
++cnt;
}
void i() const noexcept {
++cnt;
}
mutable int cnt{0};
};
TEST_F(SigH, Lifetime) {
using signal = entt::sigh<void(void)>;
ASSERT_NO_FATAL_FAILURE(signal{});
signal src{}, other{};
ASSERT_NO_FATAL_FAILURE(signal{src});
ASSERT_NO_FATAL_FAILURE(signal{std::move(other)});
ASSERT_NO_FATAL_FAILURE(src = other);
ASSERT_NO_FATAL_FAILURE(src = std::move(other));
ASSERT_NO_FATAL_FAILURE(delete new signal{});
}
TEST_F(SigH, Clear) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::f>();
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect(static_cast<const void *>(nullptr));
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect();
ASSERT_TRUE(sink.empty());
ASSERT_TRUE(sigh.empty());
}
TEST_F(SigH, Swap) {
entt::sigh<void(int &)> sigh1;
entt::sigh<void(int &)> sigh2;
entt::sink sink1{sigh1};
entt::sink sink2{sigh2};
sink1.connect<&sigh_listener::f>();
ASSERT_FALSE(sink1.empty());
ASSERT_TRUE(sink2.empty());
ASSERT_FALSE(sigh1.empty());
ASSERT_TRUE(sigh2.empty());
sigh1.swap(sigh2);
ASSERT_TRUE(sink1.empty());
ASSERT_FALSE(sink2.empty());
ASSERT_TRUE(sigh1.empty());
ASSERT_FALSE(sigh2.empty());
}
TEST_F(SigH, Functions) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int v = 0;
sink.connect<&sigh_listener::f>();
sigh.publish(v);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
ASSERT_EQ(42, v);
v = 0;
sink.disconnect<&sigh_listener::f>();
sigh.publish(v);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
ASSERT_EQ(v, 0);
}
TEST_F(SigH, FunctionsWithPayload) {
entt::sigh<void()> sigh;
entt::sink sink{sigh};
int v = 0;
sink.connect<&sigh_listener::f>(v);
sigh.publish();
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
ASSERT_EQ(42, v);
v = 0;
sink.disconnect<&sigh_listener::f>(v);
sigh.publish();
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
ASSERT_EQ(v, 0);
sink.connect<&sigh_listener::f>(v);
sink.disconnect(v);
sigh.publish();
ASSERT_EQ(v, 0);
}
TEST_F(SigH, Members) {
sigh_listener l1, l2;
entt::sigh<bool(int)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::g>(l1);
sigh.publish(42);
ASSERT_TRUE(l1.k);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
sink.disconnect<&sigh_listener::g>(l1);
sigh.publish(42);
ASSERT_TRUE(l1.k);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
sink.connect<&sigh_listener::g>(&l1);
sink.connect<&sigh_listener::h>(l2);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(2u, sigh.size());
sink.disconnect(static_cast<const void *>(nullptr));
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(2u, sigh.size());
sink.disconnect(&l1);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
}
TEST_F(SigH, Collector) {
sigh_listener listener;
entt::sigh<bool(int)> sigh;
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(listener);
auto no_return = [&listener, &cnt](bool value) {
ASSERT_TRUE(value);
listener.k = true;
++cnt;
};
listener.k = true;
sigh.collect(std::move(no_return), 42);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(cnt, 2);
auto bool_return = [&cnt](bool value) {
// gtest and its macro hell are sometimes really annoying...
[](auto v) { ASSERT_TRUE(v); }(value);
++cnt;
return true;
};
cnt = 0;
sigh.collect(std::move(bool_return), 42);
ASSERT_EQ(cnt, 1);
}
TEST_F(SigH, CollectorVoid) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(listener);
sigh.collect([&cnt]() { ++cnt; }, 42);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(cnt, 2);
auto test = [&cnt]() {
++cnt;
return true;
};
cnt = 0;
sigh.collect(std::move(test), 42);
ASSERT_EQ(cnt, 1);
}
TEST_F(SigH, Connection) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int v = 0;
auto conn = sink.connect<&sigh_listener::f>();
sigh.publish(v);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(conn);
ASSERT_EQ(42, v);
v = 0;
conn.release();
sigh.publish(v);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(conn);
ASSERT_EQ(0, v);
}
TEST_F(SigH, ScopedConnection) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
{
ASSERT_FALSE(listener.k);
entt::scoped_connection conn = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(listener.k);
ASSERT_TRUE(conn);
}
sigh.publish(42);
ASSERT_TRUE(sigh.empty());
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, ScopedConnectionMove) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
entt::scoped_connection outer{sink.connect<&sigh_listener::g>(listener)};
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(outer);
{
entt::scoped_connection inner{std::move(outer)};
ASSERT_FALSE(listener.k);
ASSERT_FALSE(outer);
ASSERT_TRUE(inner);
sigh.publish(42);
ASSERT_TRUE(listener.k);
}
ASSERT_TRUE(sigh.empty());
outer = sink.connect<&sigh_listener::g>(listener);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(outer);
{
entt::scoped_connection inner{};
ASSERT_TRUE(listener.k);
ASSERT_TRUE(outer);
ASSERT_FALSE(inner);
inner = std::move(outer);
ASSERT_FALSE(outer);
ASSERT_TRUE(inner);
sigh.publish(42);
ASSERT_FALSE(listener.k);
}
ASSERT_TRUE(sigh.empty());
}
TEST_F(SigH, ScopedConnectionConstructorsAndOperators) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
{
entt::scoped_connection inner{};
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(listener.k);
ASSERT_FALSE(inner);
inner = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(listener.k);
ASSERT_TRUE(inner);
inner.release();
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(inner);
auto basic = sink.connect<&sigh_listener::g>(listener);
inner = std::as_const(basic);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
ASSERT_FALSE(listener.k);
ASSERT_TRUE(inner);
}
sigh.publish(42);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(listener.k);
}
TEST_F(SigH, ConstNonConstNoExcept) {
entt::sigh<void()> sigh;
entt::sink sink{sigh};
const_nonconst_noexcept functor;
const const_nonconst_noexcept cfunctor;
sink.connect<&const_nonconst_noexcept::f>(functor);
sink.connect<&const_nonconst_noexcept::g>(&functor);
sink.connect<&const_nonconst_noexcept::h>(cfunctor);
sink.connect<&const_nonconst_noexcept::i>(&cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
ASSERT_EQ(cfunctor.cnt, 2);
sink.disconnect<&const_nonconst_noexcept::f>(functor);
sink.disconnect<&const_nonconst_noexcept::g>(&functor);
sink.disconnect<&const_nonconst_noexcept::h>(cfunctor);
sink.disconnect<&const_nonconst_noexcept::i>(&cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
ASSERT_EQ(cfunctor.cnt, 2);
}
TEST_F(SigH, BeforeFunction) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::add>(functor);
sink.connect<&before_after::static_add>();
sink.before<&before_after::static_add>().connect<&before_after::mul>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 6);
}
TEST_F(SigH, BeforeMemberFunction) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::static_add>();
sink.connect<&before_after::add>(functor);
sink.before<&before_after::add>(functor).connect<&before_after::mul>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 6);
}
TEST_F(SigH, BeforeFunctionWithPayload) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::static_add>();
sink.connect<&before_after::static_mul>(functor);
sink.before<&before_after::static_mul>(functor).connect<&before_after::add>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 8);
}
TEST_F(SigH, BeforeInstanceOrPayload) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::static_mul>(functor);
sink.connect<&before_after::add>(functor);
sink.before(functor).connect<&before_after::static_add>();
sigh.publish(2);
ASSERT_EQ(functor.value, 6);
}
TEST_F(SigH, BeforeAnythingElse) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::add>(functor);
sink.before().connect<&before_after::mul>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 2);
}
TEST_F(SigH, BeforeListenerNotPresent) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::mul>(functor);
sink.before<&before_after::add>(&functor).connect<&before_after::add>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 2);
}
TEST_F(SigH, UnboundDataMember) {
sigh_listener listener;
entt::sigh<bool &(sigh_listener &)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.k);
sink.connect<&sigh_listener::k>();
sigh.collect([](bool &value) { value = !value; }, listener);
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, UnboundMemberFunction) {
sigh_listener listener;
entt::sigh<void(sigh_listener *, int)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.k);
sink.connect<&sigh_listener::g>();
sigh.publish(&listener, 42);
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, CustomAllocator) {
std::allocator<void (*)(int)> allocator;
entt::sigh<void(int), decltype(allocator)> sigh{allocator};
ASSERT_EQ(sigh.get_allocator(), allocator);
ASSERT_FALSE(sigh.get_allocator() != allocator);
ASSERT_TRUE(sigh.empty());
entt::sink sink{sigh};
sigh_listener listener;
sink.template connect<&sigh_listener::g>(listener);
decltype(sigh) copy{sigh, allocator};
sink.disconnect(listener);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(copy.empty());
sigh = copy;
ASSERT_FALSE(sigh.empty());
ASSERT_FALSE(copy.empty());
decltype(sigh) move{std::move(copy), allocator};
ASSERT_TRUE(copy.empty());
ASSERT_FALSE(move.empty());
sink = entt::sink{move};
sink.disconnect(&listener);
ASSERT_TRUE(copy.empty());
ASSERT_TRUE(move.empty());
sink.template connect<&sigh_listener::g>(listener);
copy.swap(move);
ASSERT_FALSE(copy.empty());
ASSERT_TRUE(move.empty());
sink = entt::sink{copy};
sink.disconnect();
ASSERT_TRUE(copy.empty());
ASSERT_TRUE(move.empty());
}